A TWI / I2C bootloader for AVR MCUs
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

326 lines
7.0 KiB

14 years ago
14 years ago
13 years ago
14 years ago
14 years ago
14 years ago
13 years ago
14 years ago
14 years ago
13 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
14 years ago
13 years ago
14 years ago
13 years ago
14 years ago
14 years ago
13 years ago
14 years ago
13 years ago
14 years ago
  1. /***************************************************************************
  2. * Copyright (C) 09/2007 by Olaf Rempel *
  3. * razzor@kopf-tisch.de *
  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; version 2 of the License, *
  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, write to the *
  16. * Free Software Foundation, Inc., *
  17. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  18. ***************************************************************************/
  19. #include <avr/io.h>
  20. #include <avr/interrupt.h>
  21. #include <avr/boot.h>
  22. #include <avr/pgmspace.h>
  23. #define F_CPU 8000000
  24. #include <util/delay.h>
  25. #define LED_RT (1<<PORTB4)
  26. #define LED_GN (1<<PORTB5)
  27. #define TWI_ADDRESS 0x21
  28. #define COOKIE 0x4711
  29. #define APP_END 0x1C00
  30. #define CMD_WAIT 0x00
  31. #define CMD_GET_INFO 0x10
  32. #define CMD_GET_SIGNATURE 0x11
  33. #define CMD_WRITE_FLASH 0x12
  34. #define CMD_READ_FLASH 0x13
  35. #define CMD_WRITE_EEPROM 0x14
  36. #define CMD_READ_EEPROM 0x15
  37. #define CMD_BOOT_APPLICATION 0x1F
  38. /*
  39. * LED on PORTB4 blinks with 20Hz (while bootloader is running)
  40. * LED on PORTB5 blinks on TWI activity
  41. *
  42. * Fuse E: 0xfa (512 words bootloader)
  43. * Fuse H: 0xdd (2.7V BOD)
  44. * Fuse L: 0xc2 (8Mhz internal RC-Osz.)
  45. *
  46. * bootloader twi-protocol:
  47. * - get info about bootloader:
  48. * SLA+W, 0x10, SLA+R, {16 bytes}, STO
  49. *
  50. * - get signature bytes:
  51. * SLA+W, 0x11, SLA+R, {4 bytes}, STO
  52. *
  53. * - write one flash page (64bytes on mega8)
  54. * SLA+W, 0x12, addrh, addrl, 0x47, 0x11, {64 bytes}, STO
  55. *
  56. * - read one (or more) flash bytes
  57. * SLA+W, 0x13, addrh, addrl, SLA+R, {* bytes}, STO
  58. *
  59. * - write one (or more) eeprom bytes
  60. * SLA+W, 0x14, addrh, addrl, 0x47, 0x11, {* bytes}, STO
  61. *
  62. * - read one (or more) eeprom bytes
  63. * SLA+W, 0x15, addrh, addrl, SLA+R, {* bytes}, STO
  64. *
  65. * - boot application
  66. * SLA+W, 0x1F, STO
  67. */
  68. const static uint8_t info[16] = "TWIBOOT m88-v12";
  69. const static uint8_t signature[4] = { 0x1E, 0x93, 0x0A, 0x00 };
  70. /* wait 40 * 25ms = 1s */
  71. static uint8_t boot_timeout = 40;
  72. volatile static uint8_t cmd = CMD_WAIT;
  73. /* flash buffer */
  74. static uint8_t buf[SPM_PAGESIZE];
  75. static uint16_t addr;
  76. void write_flash_page(void)
  77. {
  78. uint16_t pagestart = addr;
  79. uint8_t size = SPM_PAGESIZE;
  80. uint8_t *p = buf;
  81. if (pagestart >= APP_END)
  82. return;
  83. boot_page_erase(pagestart);
  84. boot_spm_busy_wait();
  85. do {
  86. uint16_t data = *p++;
  87. data |= *p++ << 8;
  88. boot_page_fill(addr, data);
  89. addr += 2;
  90. size -= 2;
  91. } while (size);
  92. boot_page_write(pagestart);
  93. boot_spm_busy_wait();
  94. boot_rww_enable();
  95. }
  96. void write_eeprom_byte(uint8_t val)
  97. {
  98. EEARL = addr;
  99. EEARH = (addr >> 8);
  100. EEDR = val;
  101. addr++;
  102. EECR |= (1<<EEMPE);
  103. EECR |= (1<<EEPE);
  104. eeprom_busy_wait();
  105. }
  106. ISR(TWI_vect)
  107. {
  108. static uint8_t bcnt = 0;
  109. switch (TWSR & 0xF8) {
  110. /* SLA+W received, ACK returned -> receive data and ACK */
  111. case 0x60:
  112. bcnt = 0;
  113. PORTB |= LED_RT;
  114. TWCR |= (1<<TWINT) | (1<<TWEA);
  115. break;
  116. /* prev. SLA+W, data received, ACK returned -> receive data and ACK */
  117. case 0x80:
  118. if (bcnt == 0) {
  119. cmd = TWDR;
  120. switch (cmd) {
  121. case CMD_GET_INFO:
  122. case CMD_GET_SIGNATURE:
  123. case CMD_WRITE_FLASH:
  124. case CMD_READ_FLASH:
  125. case CMD_WRITE_EEPROM:
  126. case CMD_READ_EEPROM:
  127. /* abort countdown */
  128. boot_timeout = 0;
  129. case CMD_BOOT_APPLICATION:
  130. bcnt++;
  131. break;
  132. default:
  133. cmd = CMD_WAIT;
  134. bcnt = 0;
  135. break;
  136. }
  137. } else if (bcnt == 1) {
  138. addr = (TWDR << 8);
  139. bcnt++;
  140. } else if (bcnt == 2) {
  141. addr |= TWDR;
  142. bcnt++;
  143. } else if (bcnt == 3) {
  144. if (TWDR == (COOKIE >> 8))
  145. bcnt++;
  146. else
  147. bcnt = 0;
  148. } else if (bcnt == 4) {
  149. if (TWDR == (COOKIE & 0xFF))
  150. bcnt++;
  151. else
  152. bcnt = 0;
  153. } else if (bcnt >= 5 && cmd == CMD_WRITE_FLASH) {
  154. buf[bcnt -5] = TWDR;
  155. if (bcnt < sizeof(buf) +4) {
  156. bcnt++;
  157. } else {
  158. write_flash_page();
  159. bcnt = 0;
  160. }
  161. } else if (bcnt >= 5 && cmd == CMD_WRITE_EEPROM) {
  162. write_eeprom_byte(TWDR);
  163. bcnt++;
  164. }
  165. TWCR |= (1<<TWINT) | (1<<TWEA);
  166. break;
  167. /* SLA+R received, ACK returned -> send data */
  168. case 0xA8:
  169. bcnt = 0;
  170. PORTB |= LED_RT;
  171. /* prev. SLA+R, data sent, ACK returned -> send data */
  172. case 0xB8:
  173. switch (cmd) {
  174. case CMD_GET_INFO:
  175. TWDR = info[bcnt++];
  176. bcnt %= sizeof(info);
  177. break;
  178. case CMD_GET_SIGNATURE:
  179. TWDR = signature[bcnt++];
  180. bcnt %= sizeof(signature);
  181. break;
  182. case CMD_READ_FLASH:
  183. TWDR = pgm_read_byte_near(addr++);
  184. break;
  185. case CMD_READ_EEPROM:
  186. EEARL = addr;
  187. EEARH = (addr >> 8);
  188. EECR |= (1<<EERE);
  189. addr++;
  190. TWDR = EEDR;
  191. break;
  192. default:
  193. TWDR = 0xFF;
  194. break;
  195. }
  196. TWCR |= (1<<TWINT) | (1<<TWEA);
  197. break;
  198. /* STOP or repeated START */
  199. case 0xA0:
  200. /* data sent, NACK returned */
  201. case 0xC0:
  202. PORTB &= ~LED_RT;
  203. TWCR |= (1<<TWINT) | (1<<TWEA);
  204. break;
  205. /* illegal state -> reset hardware */
  206. case 0xF8:
  207. TWCR |= (1<<TWINT) | (1<<TWSTO) | (1<<TWEA);
  208. break;
  209. }
  210. }
  211. ISR(TIMER0_OVF_vect)
  212. {
  213. /* come back in 25ms (@8MHz) */
  214. TCNT0 = 0xFF - 195;
  215. /* blink LED while running */
  216. PORTB ^= LED_GN;
  217. /* count down for app-boot */
  218. if (boot_timeout > 1)
  219. boot_timeout--;
  220. /* trigger app-boot */
  221. else if (boot_timeout == 1)
  222. cmd = CMD_BOOT_APPLICATION;
  223. }
  224. static void (*jump_to_app)(void) = 0x0000;
  225. /*
  226. * For newer devices (mega88) the watchdog timer remains active even after a
  227. * system reset. So disable it as soon as possible.
  228. * automagically called on startup
  229. */
  230. void disable_wdt_timer(void) __attribute__((naked, section(".init3")));
  231. void disable_wdt_timer(void)
  232. {
  233. MCUSR = 0;
  234. WDTCSR = (1<<WDCE) | (1<<WDE);
  235. WDTCSR = (0<<WDE);
  236. }
  237. int main(void)
  238. {
  239. DDRB = LED_GN | LED_RT;
  240. PORTB = LED_GN;
  241. /* move interrupt-vectors to bootloader */
  242. MCUCR = (1<<IVCE);
  243. MCUCR = (1<<IVSEL);
  244. /* timer0: running with F_CPU/1024 */
  245. TCCR0B = (1<<CS02) | (1<<CS00);
  246. /* enable timer0 OVF interrupt */
  247. TIMSK0 = (1<<TOIE0);
  248. /* TWI init: set address, auto ACKs with interrupts */
  249. TWAR = (TWI_ADDRESS<<1);
  250. TWCR = (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
  251. sei();
  252. while (cmd != CMD_BOOT_APPLICATION);
  253. cli();
  254. /* Disable TWI but keep address! */
  255. TWCR = 0x00;
  256. /* disable timer0 */
  257. TIMSK0 = 0x00;
  258. TCCR0B = 0x00;
  259. /* move interrupt vectors back to application */
  260. MCUCR = (1<<IVCE);
  261. MCUCR = (0<<IVSEL);
  262. PORTB = 0x00;
  263. _delay_ms(25);
  264. _delay_ms(25);
  265. _delay_ms(25);
  266. _delay_ms(25);
  267. jump_to_app();
  268. return 0;
  269. }