avr/nano: move avcc as adc reference
[ros_wild_thumper.git] / avr / nano / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <limits.h>
4 #include <avr/io.h>
5 #include <avr/interrupt.h>
6 #include <avr/sleep.h>
7 #include <util/delay.h>
8 #include "uart.h"
9
10 /*
11  * I2C Register Map (8 Bit)
12  * 0x00 Register select
13  * 0x01 Distance left MSB
14  * 0x02 Distance left LSB
15  * 0x03 Distance right MSB
16  * 0x04 Distance right LSB
17  * 0x05 Distance forward MSB
18  * 0x06 Distance forward LSB
19  * 0x07 Distance backward MSB
20  * 0x08 Distance backward LSB
21  * 0x09 Voltage MSB
22  * 0x0A Voltage LSB
23  *
24  * 0xff Bootloader
25  */
26
27
28 #define TWI_ACK         TWCR = (1<<TWEA) | (1<<TWINT) | (1<<TWEN) | (1<<TWIE)
29 #define TWI_RESET       TWCR &= ~((1 << TWSTO) | (1 << TWEN)); TWI_ACK
30 #define TWI_NAK         TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWIE)
31
32 static volatile uint8_t ireg=0;
33 static volatile uint8_t bootloader=0;
34 static volatile uint16_t dist_left=0;
35 static volatile uint16_t dist_right=0;
36 static volatile uint16_t dist_forward=0;
37 static volatile uint16_t dist_backward=0;
38 static volatile uint8_t start_dist_fwd=0;
39 static volatile uint8_t start_dist_bwd=0;
40 static volatile uint16_t voltage=0;
41
42 ISR(TWI_vect)
43 {
44         static int16_t tmp16=0;
45
46         switch (TWSR & 0xF8)
47         {  
48                 case 0x60: // start write
49                         TWI_ACK;
50                         ireg = 0;
51                         break;
52                 case 0x80: // write
53                         switch(ireg) {
54                                 case 0x00: // register select
55                                         ireg = TWDR;
56
57                                         if (ireg == 0x05) start_dist_fwd=1;
58                                         if (ireg == 0x07) start_dist_bwd=1;
59
60                                         ireg--; // because we do ireg++ below
61                                         TWI_ACK;
62                                         break;
63                                 case 0xff: // bootloader
64                                         bootloader = TWDR;
65                                 default:
66                                         TWI_NAK;
67                         }
68                         ireg++;
69                         break;
70                 case 0xA8: // start read
71                 case 0xB8: // read
72                         switch(ireg) {
73                                 case 0x01: // Distance left MSB
74                                         tmp16 = dist_left;
75                                         TWDR = tmp16>>8;
76                                         TWI_ACK;
77                                         break;
78                                 case 0x02: // Distance right LSB
79                                         TWDR = tmp16;
80                                         TWI_ACK;
81                                         break;
82                                 case 0x03: // Distance right MSB
83                                         tmp16 = dist_right;
84                                         TWDR = tmp16>>8;
85                                         TWI_ACK;
86                                         break;
87                                 case 0x04: // Distance right LSB
88                                         TWDR = tmp16;
89                                         TWI_ACK;
90                                         break;
91                                 case 0x05: // Distance forward MSB
92                                         tmp16 = dist_forward;
93                                         TWDR = tmp16>>8;
94                                         TWI_ACK;
95                                         break;
96                                 case 0x06: // Distance forward LSB
97                                         TWDR = tmp16;
98                                         TWI_ACK;
99                                         break;
100                                 case 0x07: // Distance backward MSB
101                                         tmp16 = dist_backward;
102                                         TWDR = tmp16>>8;
103                                         TWI_ACK;
104                                         break;
105                                 case 0x08: // Distance backward LSB
106                                         TWDR = tmp16;
107                                         TWI_ACK;
108                                         break;
109                                 case 0x09: // Voltage MSB
110                                         tmp16 = voltage;
111                                         TWDR = tmp16>>8;
112                                         TWI_ACK;
113                                         break;
114                                 case 0x0A: // Voltage LSB
115                                         TWDR = tmp16;
116                                         TWI_ACK;
117                                         break;
118                                 default:
119                                         TWDR = 0;
120                                         TWI_NAK;
121                         }
122                         ireg++;
123                         break;
124                 default:
125                         TWI_RESET;
126         }
127 }
128
129
130 uint16_t ReadChannel(uint8_t mux) {
131         uint8_t i;
132         uint16_t result;
133
134         ADCSRA = (1<<ADEN) | (1<<ADPS1) | (1<<ADPS0);    // Frequenzvorteiler 
135         // setzen auf 8 (1) und ADC aktivieren (1)
136
137         ADMUX = mux;                      // Kanal waehlen
138         ADMUX |= (1<<REFS0);
139
140         /* nach Aktivieren des ADC wird ein "Dummy-Readout" empfohlen, man liest
141            also einen Wert und verwirft diesen, um den ADC "warmlaufen zu lassen" */
142         ADCSRA |= (1<<ADSC);              // eine ADC-Wandlung 
143         while ( ADCSRA & (1<<ADSC)  ) {
144                      // auf Abschluss der Konvertierung warten 
145         }
146         result = ADCW;  // ADCW muss einmal gelesen werden,
147         // sonst wird Ergebnis der nächsten Wandlung
148         // nicht übernommen.
149
150         /* Eigentliche Messung - Mittelwert aus 5 aufeinanderfolgenden Wandlungen */
151         result = 0;
152         for( i=0; i<5; i++ )
153         {
154                 ADCSRA |= (1<<ADSC);            // eine Wandlung "single conversion"
155                 while ( ADCSRA & (1<<ADSC) ) {
156                            // auf Abschluss der Konvertierung warten
157                 }
158                 result += ADCW;             // Wandlungsergebnisse aufaddieren
159         }
160
161         ADCSRA &= ~(1<<ADEN);             // ADC deaktivieren (2)
162
163         result /= 5;                     // Summe durch 5 teilen = arithm. Mittelwert
164         
165         return result;
166 }
167
168
169 static unsigned short get_distance(uint8_t i) {
170         return ReadChannel(i);
171 }
172
173
174 static unsigned short get_voltage(void) {
175         return ReadChannel(2)*1.46;
176 }
177
178
179 ISR(INT0_vect) {
180         static uint16_t t_start=0;
181         uint16_t t_now = TCNT1;
182         uint16_t t_diff;
183
184         if (bit_is_set(PIND, 2)) { // high level
185                 // start timer
186                 t_start = t_now;
187         } else {
188                 t_diff = t_now - t_start;
189                 dist_forward = t_diff*2.7586 + 0.5; // t [µs] / 580 = mm
190                 // disable this interrupt
191                 EIMSK |= (1 << INT0);
192         }
193 }
194
195
196 ISR(INT1_vect) {
197         static uint16_t t_start=0;
198         uint16_t t_now = TCNT1;
199         uint16_t t_diff;
200
201         if (bit_is_set(PIND, 3)) { // high level
202                 // start timer
203                 t_start = t_now;
204         } else {
205                 t_diff = t_now - t_start;
206                 dist_backward = t_diff*2.7586 + 0.5; // t [µs] / 580 = mm
207                 // disable this interrupt
208                 EIMSK |= (1 << INT1);
209         }
210 }
211
212
213 int main(void) {
214         bootloader = 0x00;
215         setup_uart(9600);
216         uart_setup_stdout();
217
218         // I2C
219         TWAR = 0x52;
220         TWI_RESET;
221
222         // Timer 1: Normal mode, Top: 0xffff, Prescaler: F_CPU/256=62500Hz
223         TCCR1A = 0x0;
224         TCCR1B = (1 << CS12);
225
226         // External Interrupts
227         EICRA = (1 << ISC10) | (1 << ISC00);
228
229         printf("\r\nStart\r\n");
230
231         set_sleep_mode(SLEEP_MODE_IDLE);
232         sei();
233         while(1) {
234                 switch(ireg) {
235                         case 0x01: // ir left
236                                 dist_left = get_distance(0);
237                                 break;
238                         case 0x03: // ir right
239                                 dist_right = get_distance(1);
240                                 break;
241                         case 0x09: // voltage
242                                 voltage = get_voltage();
243                                 break;
244                         case 0xff: // Magic reg that starts the bootloader
245                                 if (bootloader == 0xa5) {
246                                         cli();
247                                         {
248                                                 void (*start)(void) = (void*)0x1800;
249                                                 start();
250                                         }
251                                 }
252                                 break;
253                 }
254
255                 if (start_dist_fwd) {
256                         start_dist_fwd = 0;
257                         dist_forward = 0;
258
259                         DDRD |= (1 << 2);
260                         PORTD |= (1 << 2);
261                         _delay_us(10);
262                         PORTD &= ~(1 << 2);
263                         DDRD &= ~(1 << 2);
264                         // wait for interrupt
265                         EIMSK |= (1 << INT0);
266                 }
267                 if (start_dist_bwd) {
268                         start_dist_bwd = 0;
269                         dist_backward = 0;
270
271                         DDRD |= (1 << 3);
272                         PORTD |= (1 << 3);
273                         _delay_us(10);
274                         PORTD &= ~(1 << 3);
275                         DDRD &= ~(1 << 3);
276                         // wait for interrupt
277                         EIMSK |= (1 << INT1);
278                 }
279
280                 sleep_mode();
281         }
282
283         return 0;
284 }