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.

456 lines
10 KiB

  1. /***************************************************************************
  2. * Copyright (C) 08/2010 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. /*
  24. * atmega8:
  25. * Fuse H: 0xda (512 words bootloader)
  26. * Fuse L: 0x84 (8Mhz internal RC-Osz., 2.7V BOD)
  27. *
  28. * atmega88:
  29. * Fuse E: 0xfa (512 words bootloader)
  30. * Fuse H: 0xdd (2.7V BOD)
  31. * Fuse L: 0xc2 (8Mhz internal RC-Osz.)
  32. *
  33. * atmega168:
  34. * Fuse E: 0xfa (512 words bootloader)
  35. * Fuse H: 0xdd (2.7V BOD)
  36. * Fuse L: 0xc2 (8Mhz internal RC-Osz.)
  37. */
  38. #if defined (__AVR_ATmega8__)
  39. #define VERSION_STRING "TWIBOOT m8v2.0"
  40. #define SIGNATURE_BYTES 0x1E, 0x93, 0x07
  41. #elif defined (__AVR_ATmega88__)
  42. #define VERSION_STRING "TWIBOOT m88v2.0"
  43. #define SIGNATURE_BYTES 0x1E, 0x93, 0x0A
  44. #elif defined (__AVR_ATmega168__)
  45. #define VERSION_STRING "TWIBOOT m168v2.0"
  46. #define SIGNATURE_BYTES 0x1E, 0x94, 0x06
  47. #else
  48. #error MCU not supported
  49. #endif
  50. #define EEPROM_SUPPORT 1
  51. /* 25ms @8MHz */
  52. #define TIMER_RELOAD (0xFF - 195)
  53. /* 40 * 25ms */
  54. #define TIMEOUT 40
  55. #define LED_RT (1<<PORTB4)
  56. #define LED_GN (1<<PORTB5)
  57. #define TWI_ADDRESS 0x21
  58. /* SLA+R */
  59. #define CMD_WAIT 0x00
  60. #define CMD_READ_VERSION 0x01
  61. #define CMD_READ_MEMORY 0x02
  62. /* internal mappings */
  63. #define CMD_READ_CHIPINFO (0x10 | CMD_READ_MEMORY)
  64. #define CMD_READ_FLASH (0x20 | CMD_READ_MEMORY)
  65. #define CMD_READ_EEPROM (0x30 | CMD_READ_MEMORY)
  66. #define CMD_READ_PARAMETERS (0x40 | CMD_READ_MEMORY) /* only in APP */
  67. /* SLA+W */
  68. #define CMD_SWITCH_APPLICATION CMD_READ_VERSION
  69. #define CMD_WRITE_MEMORY CMD_READ_MEMORY
  70. /* internal mappings */
  71. #define CMD_BOOT_BOOTLOADER (0x10 | CMD_SWITCH_APPLICATION) /* only in APP */
  72. #define CMD_BOOT_APPLICATION (0x20 | CMD_SWITCH_APPLICATION)
  73. #define CMD_WRITE_CHIPINFO (0x10 | CMD_WRITE_MEMORY) /* invalid */
  74. #define CMD_WRITE_FLASH (0x20 | CMD_WRITE_MEMORY)
  75. #define CMD_WRITE_EEPROM (0x30 | CMD_WRITE_MEMORY)
  76. #define CMD_WRITE_PARAMETERS (0x40 | CMD_WRITE_MEMORY) /* only in APP */
  77. /* CMD_SWITCH_APPLICATION parameter */
  78. #define BOOTTYPE_BOOTLOADER 0x00 /* only in APP */
  79. #define BOOTTYPE_APPLICATION 0x80
  80. /* CMD_{READ|WRITE}_* parameter */
  81. #define MEMTYPE_CHIPINFO 0x00
  82. #define MEMTYPE_FLASH 0x01
  83. #define MEMTYPE_EEPROM 0x02
  84. #define MEMTYPE_PARAMETERS 0x03 /* only in APP */
  85. /*
  86. * LED_GN blinks with 20Hz (while bootloader is running)
  87. * LED_RT blinks on TWI activity
  88. *
  89. * bootloader twi-protocol:
  90. * - abort boot timeout:
  91. * SLA+W, 0x00, STO
  92. *
  93. * - show bootloader version
  94. * SLA+W, 0x01, SLA+R, {16 bytes}, STO
  95. *
  96. * - start application
  97. * SLA+W, 0x01, 0x80, STO
  98. *
  99. * - read chip info: 3byte signature, 1byte page size, 2byte flash size, 2byte eeprom size
  100. * SLA+W, 0x02, 0x00, 0x00, 0x00, SLA+R, {4 bytes}, STO
  101. *
  102. * - read one (or more) flash bytes
  103. * SLA+W, 0x02, 0x01, addrh, addrl, SLA+R, {* bytes}, STO
  104. *
  105. * - read one (or more) eeprom bytes
  106. * SLA+W, 0x02, 0x02, addrh, addrl, SLA+R, {* bytes}, STO
  107. *
  108. * - write one flash page (64bytes on mega8)
  109. * SLA+W, 0x02, 0x01, addrh, addrl, {64 bytes}, STO
  110. *
  111. * - write one (or more) eeprom bytes
  112. * SLA+W, 0x02, 0x02, addrh, addrl, {* bytes}, STO
  113. */
  114. const static uint8_t info[16] = VERSION_STRING;
  115. const static uint8_t chipinfo[8] = {
  116. SIGNATURE_BYTES,
  117. SPM_PAGESIZE,
  118. (APP_END >> 8) & 0xFF,
  119. APP_END & 0xFF,
  120. #if (EEPROM_SUPPORT)
  121. ((E2END +1) >> 8 & 0xFF),
  122. (E2END +1) & 0xFF
  123. #else
  124. 0x00, 0x00
  125. #endif
  126. };
  127. /* wait 40 * 25ms = 1s */
  128. static uint8_t boot_timeout = TIMEOUT;
  129. volatile static uint8_t cmd = CMD_WAIT;
  130. /* flash buffer */
  131. static uint8_t buf[SPM_PAGESIZE];
  132. static uint16_t addr;
  133. static void write_flash_page(void)
  134. {
  135. uint16_t pagestart = addr;
  136. uint8_t size = SPM_PAGESIZE;
  137. uint8_t *p = buf;
  138. if (pagestart >= APP_END)
  139. return;
  140. boot_page_erase(pagestart);
  141. boot_spm_busy_wait();
  142. do {
  143. uint16_t data = *p++;
  144. data |= *p++ << 8;
  145. boot_page_fill(addr, data);
  146. addr += 2;
  147. size -= 2;
  148. } while (size);
  149. boot_page_write(pagestart);
  150. boot_spm_busy_wait();
  151. boot_rww_enable();
  152. }
  153. #if (EEPROM_SUPPORT)
  154. static uint8_t read_eeprom_byte(void)
  155. {
  156. EEARL = addr;
  157. EEARH = (addr >> 8);
  158. EECR |= (1<<EERE);
  159. addr++;
  160. return EEDR;
  161. }
  162. static void write_eeprom_byte(uint8_t val)
  163. {
  164. EEARL = addr;
  165. EEARH = (addr >> 8);
  166. EEDR = val;
  167. addr++;
  168. #if defined (__AVR_ATmega8__)
  169. EECR |= (1<<EEMWE);
  170. EECR |= (1<<EEWE);
  171. #elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega168__)
  172. EECR |= (1<<EEMPE);
  173. EECR |= (1<<EEPE);
  174. #endif
  175. eeprom_busy_wait();
  176. }
  177. #endif /* EEPROM_SUPPORT */
  178. ISR(TWI_vect)
  179. {
  180. static uint8_t bcnt;
  181. uint8_t data;
  182. uint8_t ack = (1<<TWEA);
  183. switch (TWSR & 0xF8) {
  184. /* SLA+W received, ACK returned -> receive data and ACK */
  185. case 0x60:
  186. bcnt = 0;
  187. PORTB |= LED_RT;
  188. TWCR |= (1<<TWINT) | (1<<TWEA);
  189. break;
  190. /* prev. SLA+W, data received, ACK returned -> receive data and ACK */
  191. case 0x80:
  192. data = TWDR;
  193. switch (bcnt) {
  194. case 0:
  195. switch (data) {
  196. case CMD_SWITCH_APPLICATION:
  197. case CMD_WRITE_MEMORY:
  198. bcnt++;
  199. /* no break */
  200. case CMD_WAIT:
  201. /* abort countdown */
  202. boot_timeout = 0;
  203. break;
  204. default:
  205. /* boot app now */
  206. cmd = CMD_BOOT_APPLICATION;
  207. ack = (0<<TWEA);
  208. break;
  209. }
  210. cmd = data;
  211. break;
  212. case 1:
  213. switch (cmd) {
  214. case CMD_SWITCH_APPLICATION:
  215. if (data == BOOTTYPE_APPLICATION) {
  216. cmd = CMD_BOOT_APPLICATION;
  217. }
  218. ack = (0<<TWEA);
  219. break;
  220. case CMD_WRITE_MEMORY:
  221. bcnt++;
  222. if (data == MEMTYPE_CHIPINFO) {
  223. cmd = CMD_WRITE_CHIPINFO;
  224. } else if (data == MEMTYPE_FLASH) {
  225. cmd = CMD_WRITE_FLASH;
  226. #if (EEPROM_SUPPORT)
  227. } else if (data == MEMTYPE_EEPROM) {
  228. cmd = CMD_WRITE_EEPROM;
  229. #endif
  230. } else {
  231. ack = (0<<TWEA);
  232. }
  233. break;
  234. default:
  235. ack = (0<<TWEA);
  236. break;
  237. }
  238. break;
  239. case 2:
  240. case 3:
  241. addr <<= 8;
  242. addr |= data;
  243. bcnt++;
  244. break;
  245. default:
  246. switch (cmd) {
  247. case CMD_WRITE_FLASH:
  248. buf[bcnt -4] = data;
  249. if (bcnt < sizeof(buf) +3) {
  250. bcnt++;
  251. } else {
  252. write_flash_page();
  253. ack = (0<<TWEA);
  254. }
  255. break;
  256. #if (EEPROM_SUPPORT)
  257. case CMD_WRITE_EEPROM:
  258. write_eeprom_byte(data);
  259. bcnt++;
  260. break;
  261. #endif
  262. default:
  263. ack = (0<<TWEA);
  264. break;
  265. }
  266. break;
  267. }
  268. if (ack == 0x00)
  269. bcnt = 0;
  270. TWCR |= (1<<TWINT) | ack;
  271. break;
  272. /* SLA+R received, ACK returned -> send data */
  273. case 0xA8:
  274. bcnt = 0;
  275. PORTB |= LED_RT;
  276. /* prev. SLA+R, data sent, ACK returned -> send data */
  277. case 0xB8:
  278. switch (cmd) {
  279. case CMD_READ_VERSION:
  280. data = info[bcnt++];
  281. bcnt %= sizeof(info);
  282. break;
  283. case CMD_READ_CHIPINFO:
  284. data = chipinfo[bcnt++];
  285. bcnt %= sizeof(chipinfo);
  286. break;
  287. case CMD_READ_FLASH:
  288. data = pgm_read_byte_near(addr++);
  289. break;
  290. #if (EEPROM_SUPPORT)
  291. case CMD_READ_EEPROM:
  292. data = read_eeprom_byte();
  293. break;
  294. #endif
  295. default:
  296. data = 0xFF;
  297. break;
  298. }
  299. TWDR = data;
  300. TWCR |= (1<<TWINT) | (1<<TWEA);
  301. break;
  302. /* STOP or repeated START */
  303. case 0xA0:
  304. /* data sent, NACK returned */
  305. case 0xC0:
  306. PORTB &= ~LED_RT;
  307. TWCR |= (1<<TWINT) | (1<<TWEA);
  308. break;
  309. /* illegal state -> reset hardware */
  310. case 0xF8:
  311. TWCR |= (1<<TWINT) | (1<<TWSTO) | (1<<TWEA);
  312. break;
  313. }
  314. }
  315. ISR(TIMER0_OVF_vect)
  316. {
  317. /* restart timer */
  318. TCNT0 = TIMER_RELOAD;
  319. /* blink LED while running */
  320. PORTB ^= LED_GN;
  321. /* count down for app-boot */
  322. if (boot_timeout > 1)
  323. boot_timeout--;
  324. /* trigger app-boot */
  325. else if (boot_timeout == 1)
  326. cmd = CMD_BOOT_APPLICATION;
  327. }
  328. static void (*jump_to_app)(void) __attribute__ ((noreturn)) = 0x0000;
  329. /*
  330. * For newer devices (mega88) the watchdog timer remains active even after a
  331. * system reset. So disable it as soon as possible.
  332. * automagically called on startup
  333. */
  334. #if defined (__AVR_ATmega88__) || defined (__AVR_ATmega168__)
  335. void disable_wdt_timer(void) __attribute__((naked, section(".init3")));
  336. void disable_wdt_timer(void)
  337. {
  338. MCUSR = 0;
  339. WDTCSR = (1<<WDCE) | (1<<WDE);
  340. WDTCSR = (0<<WDE);
  341. }
  342. #endif
  343. int main(void) __attribute__ ((noreturn));
  344. int main(void)
  345. {
  346. DDRB = LED_GN | LED_RT;
  347. PORTB = LED_GN;
  348. /* move interrupt-vectors to bootloader */
  349. /* timer0: running with F_CPU/1024, OVF interrupt */
  350. #if defined (__AVR_ATmega8__)
  351. GICR = (1<<IVCE);
  352. GICR = (1<<IVSEL);
  353. TCCR0 = (1<<CS02) | (1<<CS00);
  354. TIMSK = (1<<TOIE0);
  355. #elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega168__)
  356. MCUCR = (1<<IVCE);
  357. MCUCR = (1<<IVSEL);
  358. TCCR0B = (1<<CS02) | (1<<CS00);
  359. TIMSK0 = (1<<TOIE0);
  360. #endif
  361. /* TWI init: set address, auto ACKs with interrupts */
  362. TWAR = (TWI_ADDRESS<<1);
  363. TWCR = (1<<TWEA) | (1<<TWEN) | (1<<TWIE);
  364. sei();
  365. while (cmd != CMD_BOOT_APPLICATION);
  366. cli();
  367. /* Disable TWI but keep address! */
  368. TWCR = 0x00;
  369. /* disable timer0 */
  370. /* move interrupt vectors back to application */
  371. #if defined (__AVR_ATmega8__)
  372. TCCR0 = 0x00;
  373. TIMSK = 0x00;
  374. GICR = (1<<IVCE);
  375. GICR = (0<<IVSEL);
  376. #elif defined (__AVR_ATmega88__) || defined (__AVR_ATmega168__)
  377. TIMSK0 = 0x00;
  378. TCCR0B = 0x00;
  379. MCUCR = (1<<IVCE);
  380. MCUCR = (0<<IVSEL);
  381. #endif
  382. PORTB = 0x00;
  383. uint16_t wait = 0x0000;
  384. do {
  385. __asm volatile ("nop");
  386. } while (--wait);
  387. jump_to_app();
  388. }