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.

314 lines
6.7 KiB

  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. volatile 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. int main(void)
  226. {
  227. DDRB = LED_GN | LED_RT;
  228. PORTB = LED_GN;
  229. /* move interrupt-vectors to bootloader */
  230. MCUCR = (1<<IVCE);
  231. MCUCR = (1<<IVSEL);
  232. /* timer0: running with F_CPU/1024 */
  233. TCCR0B = (1<<CS02) | (1<<CS00);
  234. /* enable timer0 OVF interrupt */
  235. TIMSK0 |= (1<<TOIE0);
  236. /* TWI init: set address, auto ACKs with interrupts */
  237. TWAR = (TWI_ADDRESS<<1);
  238. TWCR = (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
  239. sei();
  240. while (cmd != CMD_BOOT_APPLICATION);
  241. cli();
  242. /* Disable TWI but keep address! */
  243. TWCR = 0x00;
  244. /* disable timer0 */
  245. TIMSK0 = 0x00;
  246. TCCR0B = 0x00;
  247. /* move interrupt vectors back to application */
  248. MCUCR = (1<<IVCE);
  249. MCUCR = (0<<IVSEL);
  250. PORTB = 0x00;
  251. _delay_ms(25);
  252. _delay_ms(25);
  253. _delay_ms(25);
  254. _delay_ms(25);
  255. jump_to_app();
  256. return 0;
  257. }