undump: Allow for trailing backslash
[wk-misc.git] / ebus / hardware.c
1 /* hardware.c - Hardware related code for an Elektor Bus Node
2  * Copyright (C) 2011 g10 Code GmbH
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include "hardware.h"
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <util/delay.h>
23 #include <avr/io.h>
24 #include <avr/pgmspace.h>
25 #include <avr/interrupt.h>
26 #include <avr/eeprom.h>
27 #include <avr/sleep.h>
28 #include <util/crc16.h>
29
30 #include "ebus.h"
31
32 /* UART defs.  */
33 #define UBRR_VAL   ((F_CPU+8*BAUD)/(16*BAUD)-1)
34 #define BAUD_REAL  (F_CPU/(16*(UBRR_VAL+1)))
35 #define BAUD_ERROR ((1000*BAUD_REAL)/BAUD)
36 #if (BAUD_ERROR < 990 || BAUD_ERROR > 1010)
37 # error computed baud rate out of range
38 #endif
39
40
41
42 \f
43 /* EEPROM layout.  We keep configuration data at the start of the
44    eeprom but copy it on startup into the RAM for easier access.  */
45 struct __attribute__ ((packed)) ee_config_s
46 {
47   uint16_t  reserved;
48   uint8_t   nodeid_hi;
49   uint8_t   nodeid_lo;
50   uint8_t   reserved1;
51   uint8_t   debug_flags;
52   uint8_t   reserved2;
53   uint8_t   reserved3;
54   uint8_t   name[8];  /* The human readable name of the node.  */
55 };
56
57 struct ee_config_s ee_config EEMEM = {0};
58 struct ee_data_s ee_data EEMEM = {NODETYPE_UNDEFINED, 0, {{0}}};
59
60 /* End EEPROM.  */
61
62
63 /* The current time measured in quantities of 10 seconds with
64    explicit roll over after 7 days.  */
65 static volatile uint16_t current_time;
66
67 /* 10 milliseconds in the 10 second period.  */
68 static volatile uint16_t current_clock;
69
70
71 \f
72 /* Read key S2.  Return true once at the first debounced leading edge
73    of the depressed key.  For correct operation this function needs to
74    be called at fixed intervals.  If this is done using the 10ms
75    system tick the user is required to press the key for at least
76    100ms.  This should be sufficient to avoid faulty triggers due to
77    sparks and other noisy sources.  My tests using the usual PCB micro
78    keys showed that I was not able to push them for less then 30 ms,
79    50ms were achievable but a common value for a quick push was about
80    150ms to 200ms range.  With the old 1ms system tick we had a
81    threshold of 10ms which was too low to avoid all faulty trigger
82    sources; lightning and other noises triggered an unwanted event at
83    least 2 or 3 times a week.*/
84 byte
85 read_key_s2 (void)
86 {
87   static uint16_t state;
88
89   state <<= 1;
90   state |= !KEY_S2;
91   state |= 0xf800; /* To work on 10 continuous depressed readings.  */
92   return (state == 0xfc00); /* Only the transition shall return true.  */
93 }
94
95 byte
96 read_key_s3 (void)
97 {
98   static uint16_t state;
99
100   state <<= 1;
101   state |= !KEY_S3;
102   state |= 0xf800; /* To work on 10 continuous depressed readings.  */
103   return (state == 0xfc00); /* Only the transition shall return true.  */
104 }
105
106
107 /* Return the current time.  Time is measured as the count of 10
108    second periods passed in a 7 day period.  */
109 uint16_t
110 get_current_time (void)
111 {
112   return current_time;
113 }
114
115
116 uint16_t
117 get_current_fulltime (byte *r_deci)
118 {
119   uint16_t hi, lo;
120
121   cli ();
122   hi = current_time;
123   lo = current_clock;
124   sei ();
125   *r_deci = lo/10;
126   return hi;
127 }
128
129
130 void
131 set_current_fulltime (uint16_t tim, byte deci)
132 {
133   cli ();
134   current_time = tim;
135   current_clock = deci * 10;
136   sei ();
137   time_has_been_set = 1;
138 }
139
140
141 void
142 set_debug_flags (uint8_t value)
143 {
144
145   eeprom_update_byte (&ee_config.debug_flags, value);
146   config.debug_flags = value;
147 }
148
149
150 \f
151 /*
152    Interrupt service routines
153  */
154
155
156 /* 2ms ticker interrupt service routine. */
157 ISR (TIMER2_COMPA_vect)
158 {
159   static uint8_t two_ms_counter;
160
161   if (++two_ms_counter != 5)
162     return;
163   two_ms_counter = 0;
164
165   current_clock++;
166   if (current_clock >= 1000)
167     {
168       /* 10 seconds passed.  Bump the current time.  */
169       current_time++;
170       if (current_time == (uint16_t)1440 * 6 * 7 )
171         current_time = 0; /* Weekly roll-over.  */
172       current_clock = 0;
173     }
174
175   ticker_bottom (current_clock);
176 }
177
178
179 /* Setup for some parts of the hardware.  The caller needs to pass the
180    node type so that the EEPROM will be erased if it does not match
181    the node type. */
182 void
183 hardware_setup (byte nodetype)
184 {
185   byte value;
186
187   /* Port D configuration:
188      PIND.7 = Inp: KEY_S3
189      PIND.6 = Out: LED_Transmit
190      PIND.5 = Inp: KEY_S2
191      PIND.4 = Out: LED_Collision
192      PIND.3 = Out: LT1785-pin2 = !RE
193      PIND.2 = Out: LT1785-pin3 = DE
194      PIND.1 = TXD: LT1785-pin4 = DI
195      PIND.0 = RXD: LT1785-pin1 = RO
196   */
197   PORTD = _BV(7) | _BV(5);  /* Enable pull-ups.  */
198   DDRD  = (_BV(6) | _BV(4) | _BV(3) | _BV(2) | _BV(1));
199
200 #if (KEY_S3_BIT != 7 || KEY_S2_BIT != 5                 \
201      || LED_Transmit_BIT != 6 || LED_Collision_BIT != 4)
202 # error mismatch with hardware.h
203 #endif
204
205   /* Set the UART: 8n1, async, rx and tx on, rx int enabled.
206      Baud rate set to the value computed from BAUD.  */
207   UCSR0A = 0x00;
208   UCSR0B = _BV(RXCIE0) | _BV(RXEN0) | _BV(TXEN0);
209   UCSR0C = _BV(UCSZ01) | _BV(UCSZ00);
210   UBRR0H = (UBRR_VAL >> 8) & 0x0f;
211   UBRR0L = (UBRR_VAL & 0xff);
212
213   /* Timer 2: 10ms ticker.
214
215      F_ocr = F_cpu / (PRE * (OCR+1))
216      |    F_cpu |  PRE | OCR |        F_ocr |
217      |----------+------+-----+--------------|
218      | 16000000 |   64 |  24 | 10000.000000 |
219      | 16000000 |   64 | 249 |  1000.000000 |
220      | 16000000 |  128 | 249 |   500.000000 |
221      | 16000000 | 1024 | 155 |   100.160260 |
222      #+TBLFM: $4=$1 / ($2 ($3 + 1));%f
223
224      The table indicates that there is no way to get an exact 10ms
225      timer.  It is possible to have an exact 1ms and 2ms timer.  We
226      use a 2ms timer internally along with a counter to turn it into
227      the 10ms timer.
228   */
229   TCCR2A = 0x02;   /* Select CTC mode.  */
230   TCCR2B = 0x05;   /* Set prescaler to 128.  */
231   TCNT2 = 0x00;    /* Clear timer/counter register.  */
232   OCR2A  = 249;    /* Compare value for 2ms.  */
233   TIMSK2 = 0x02;   /* Set OCIE2A.  */
234
235   /* Copy some configuration data into the RAM.  */
236   config.nodeid_hi = eeprom_read_byte (&ee_config.nodeid_hi);
237   config.nodeid_lo = eeprom_read_byte (&ee_config.nodeid_lo);
238   config.debug_flags = eeprom_read_byte (&ee_config.debug_flags);
239   /* Take a copy the reset flags and clear them.  */
240   config.reset_flags = MCUSR;
241   MCUSR &= ~(_BV(WDRF) | _BV(BORF) | _BV(EXTRF) | _BV(PORF));
242
243   /* Lacking any better way to seed rand we use the node id.  A better
244      way would be to use the low bit of an ADC to gather some entropy.
245      However the node id is supposed to be unique and should thus be
246      sufficient.  */
247   srand (config.nodeid_lo);
248
249   /* Clear the node specific eeprom if the node type changed.  */
250   value = eeprom_read_byte (&ee_data.nodetype);
251   if (value != nodetype)
252     {
253       int i;
254       uint32_t dw = 0;
255
256       for (i=0; i < sizeof ee_data; i += sizeof dw)
257         eeprom_write_dword ((uint32_t*)(&ee_data.nodetype+i), dw);
258       eeprom_write_byte (&ee_data.nodetype, nodetype);
259     }
260
261
262 }