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.

483 lines
11KB

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