i2c rework
[aversive.git] / projects / microb2010 / bootloader / main.c
1 /*
2  *  Copyright Droids Corporation
3  *  Olivier Matz <zer0@droids-corp.org>
4  *
5  *  This program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  *
19  *  Revision : $Id: main.c,v 1.4 2009-05-27 20:04:06 zer0 Exp $
20  *
21  */
22
23 /*
24  * A simple bootloader example.
25  */
26
27 #include <aversive.h>
28 #include <aversive/wait.h>
29 #include <aversive/pgmspace.h>
30
31 #include <stdlib.h>
32 #include <string.h>
33 #include <util/crc16.h>
34 #include <avr/boot.h>
35
36 #define BRAKE_DDR()     do { DDRJ |= 0xF0; } while(0)
37 #define BRAKE_ON()      do { PORTJ |= 0xF0; } while(0)
38 #define BRAKE_OFF()     do { PORTJ &= 0x0F; } while(0)
39
40 #define LED1_ON()       sbi(PORTJ, 2)
41 #define LED1_OFF()      cbi(PORTJ, 2)
42
43 #define LED2_ON()       sbi(PORTJ, 3)
44 #define LED2_OFF()      cbi(PORTJ, 3)
45
46
47 #define NOECHO
48
49 #ifdef NOECHO
50 #define echo(c) do {} while(0)
51 #else
52 #define echo(c) uart_send(c)
53 #endif
54
55 #if UART_NUM == 0
56
57 #define UCSRxA UCSR0A
58 #define UCSRxB UCSR0B
59 #define UCSRxC UCSR0C
60 #define RXCx RXC0
61 #define UDRx UDR0
62 #define UDREx UDRE0
63 #define U2Xx U2X0
64 #define RXENx RXEN0
65 #define TXENx TXEN0
66 #define UCSZx0 UCSZ00
67 #define UCSZx1 UCSZ01
68 #define UBRRx UBRR0
69
70 #elif  UART_NUM == 1
71
72 #define UCSRxA UCSR1A
73 #define UCSRxB UCSR1B
74 #define UCSRxC UCSR1C
75 #define RXCx RXC1
76 #define UDRx UDR1
77 #define UDREx UDRE1
78 #define U2Xx U2X1
79 #define RXENx RXEN1
80 #define TXENx TXEN1
81 #define UCSZx0 UCSZ10
82 #define UCSZx1 UCSZ11
83 #define UBRRx UBRR1
84
85 #elif  UART_NUM == 2
86
87 #define UCSRxA UCSR2A
88 #define UCSRxB UCSR2B
89 #define UCSRxC UCSR2C
90 #define RXCx RXC2
91 #define UDRx UDR2
92 #define UDREx UDRE2
93 #define U2Xx U2X2
94 #define RXENx RXEN2
95 #define TXENx TXEN2
96 #define UCSZx0 UCSZ20
97 #define UCSZx1 UCSZ21
98 #define UBRRx UBRR2
99
100 #elif  UART_NUM == 3
101
102 #define UCSRxA UCSR3A
103 #define UCSRxB UCSR3B
104 #define UCSRxC UCSR3C
105 #define RXCx RXC3
106 #define UDRx UDR3
107 #define UDREx UDRE3
108 #define U2Xx U2X3
109 #define RXENx RXEN3
110 #define TXENx TXEN3
111 #define UCSZx0 UCSZ30
112 #define UCSZx1 UCSZ31
113 #define UBRRx UBRR3
114
115 #endif
116
117
118 static char uart_recv(void)
119 {
120         while ( !(UCSRxA & (1<<RXCx)) ) ;
121         return UDRx;
122 }
123
124 static void uart_send(char c)
125 {
126         while ( !( UCSRxA & (1<<UDREx)) ) ;
127         UDRx = c;
128 }
129
130 static void uart_puts(const char *buf)
131 {
132         while (*buf)
133                 uart_send(*(buf++));
134 }
135
136 static int8_t bootloader_query_hex(uint32_t *val)
137 {
138         uint32_t tmp = 0;
139         int c;
140
141         while (1) {
142                 c = uart_recv();
143                 echo(c);
144
145                 if (c == '\n' || c == '\r') {
146                         *val = tmp;
147                         return 0;
148                 }
149                 else if (c >= '0' && c <= '9') {
150                         tmp <<= 4;
151                         tmp += (c - '0');
152                 }
153                 else if (c >= 'a' && c <= 'f') {
154                         tmp <<= 4;
155                         tmp += (c - 'a' + 10);
156                 }
157                 else if (c >= 'A' && c <= 'F') {
158                         tmp <<= 4;
159                         tmp += (c - 'A' + 10);
160                 }
161                 else
162                         return -1;
163         }
164         return 0;
165 }
166
167 /* launch application */
168 static void launch_app(void)
169 {
170         uart_puts("Boot...");
171         MCUCR = (1 << IVCE);
172         MCUCR = (0 << IVSEL);
173         reset();
174 }
175
176 static void disp_digit(uint8_t x)
177 {
178         if (x < 10)
179                 x += '0';
180         else
181                 x += 'a' - 10 ;
182         uart_send(x);
183 }
184
185 static void disp_hex8(uint8_t x)
186 {
187         disp_digit(x>>4);
188         disp_digit(x&0xf);
189 }
190
191 static void disp_hex16(uint16_t x)
192 {
193         disp_hex8(x>>8);
194         disp_hex8(x);
195 }
196
197 static void disp_hex32(uint32_t x)
198 {
199         disp_hex16(x>>16);
200         disp_hex16(x);
201 }
202
203 static void crc_app(void)
204 {
205         uint32_t start_addr, addr, size;
206         uint8_t c;
207         uint16_t crc = 0xffff;
208         uint16_t sum = 0;
209
210         uart_puts("addr?\r\n");
211         if (bootloader_query_hex(&start_addr))
212                 goto fail;
213         if (start_addr > FLASHEND)
214                 goto fail;
215         uart_puts("size?\r\n");
216         if (bootloader_query_hex(&size))
217                 goto fail;
218         if (start_addr + size > FLASHEND)
219                 goto fail;
220         for (addr=start_addr; addr<start_addr+size; addr++) {
221 #if 0
222                 /* ignore the 2nd page, it contains microb infos */
223                 if (addr >= 256 && addr < 512)
224                         continue;
225 #endif
226                 c = pgm_read_byte_far(addr);
227                 crc = _crc_ccitt_update(crc, c);
228                 sum += c;
229         }
230         disp_hex16(crc);
231         disp_hex16(sum);
232         return;
233  fail:
234         uart_puts("KO");
235 }
236
237 static void read32(void)
238 {
239         uint32_t start_addr, val = 0;
240         uint8_t c, i;
241
242         uart_puts("addr?\r\n");
243         if (bootloader_query_hex(&start_addr))
244                 goto fail;
245         if (start_addr > FLASHEND)
246                 goto fail;
247         for (i=0; i<4; i++) {
248                 c = pgm_read_byte_far(start_addr+i);
249                 val <<= 8;
250                 val |= c;
251         }
252         disp_hex32(val);
253         return;
254  fail:
255         uart_puts("KO");
256 }
257
258 static void prog_page(void)
259 {
260         int c;
261         uint32_t addr;
262         uint16_t i;
263         uint16_t crc = 0xffff;
264         uint16_t sum = 0;
265         uint8_t buf[SPM_PAGESIZE];
266
267 #define SPM_PAGEMASK ((uint32_t)SPM_PAGESIZE-1)
268         uart_puts("addr?\r\n");
269         if (bootloader_query_hex(&addr))
270                 goto fail;
271         if (addr > FLASHEND)
272                 goto fail;
273         /* start_addr must be page aligned */
274         if (addr & SPM_PAGEMASK)
275                 goto fail;
276
277         uart_puts("ok\r\n");
278
279         PORTJ = 0xF0;
280
281         /* data is received like the .bin format (which is already in
282          * little endian) */
283         for (i=0; i<SPM_PAGESIZE; i++) {
284                 c = uart_recv();
285                 crc = _crc_ccitt_update(crc, c);
286                 sum += c;
287                 buf[i] = c;
288         }
289         disp_hex16(crc);
290         disp_hex16(sum);
291         uart_puts(" (y?)\r\n");
292         c = uart_recv();
293         if (c != 'y')
294                 goto fail;
295
296         /* erase page */
297         eeprom_busy_wait();
298         boot_page_erase(addr);
299         boot_spm_busy_wait();
300
301         /* Set up little-endian word and fill tmp buf. */
302         for (i=0; i<SPM_PAGESIZE; i+=2) {
303                 uint16_t w = buf[i] + ((uint16_t)(buf[i+1]) << 8);
304                 boot_page_fill(addr + i, w);
305         }
306
307         PORTJ = 0xFC;
308
309         boot_page_write(addr);
310         boot_spm_busy_wait();
311
312         /* Reenable RWW-section again. We need this if we want to jump
313          * back to the application after bootloading. */
314         boot_rww_enable();
315
316         uart_puts("OK");
317         return;
318  fail:
319         uart_puts("KO");
320 }
321
322 int main(void)
323 {
324         int c;
325         uint32_t i=0;
326
327         /* disable all motors and switch on leds */
328         DDRJ = 0xFC;
329         PORTJ = 0xFC;
330
331         UCSRxA = _BV(U2Xx);
332         UCSRxB = _BV(RXENx) | _BV(TXENx);
333         UCSRxC = _BV(UCSZx1) | _BV(UCSZx0); /* 8 bits no parity 1 stop */
334         UBRRx = 34; /* 57600 at 16 Mhz */
335
336         /* move interrupt vector in bootloader section */
337         MCUCR = (1 << IVCE);
338         MCUCR = (1 << IVSEL);
339
340         sei();
341
342         uart_puts("\r\ncmd> ");
343
344         /* timeout */
345         while ( !(UCSRxA & (1<<RXCx)) ) {
346                 i++;
347                 if (i>1000000) /* wait about 1 sec */
348                         launch_app();
349         }
350
351         while (1) {
352                 uart_puts("\r\ncmd> ");
353                 c = uart_recv();
354                 if (c == 'x')
355                         launch_app();
356                 else if (c == 'c')
357                         crc_app();
358                 else if (c == 'p')
359                         prog_page();
360                 else if (c == 'd')
361                         read32();
362                 else
363                         uart_puts("p:prog_page c:crc x:exec d:dump32");
364         }
365
366         return 0;
367 }