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.

12 years ago
12 years ago
10 years ago
10 years ago
5 years ago
5 years ago
12 years ago
5 years ago
5 years ago
12 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
12 years ago
12 years ago
10 years ago
12 years ago
10 years ago
12 years ago
10 years ago
12 years ago
10 years ago
12 years ago
10 years ago
12 years ago
10 years ago
12 years ago
10 years ago
12 years ago
10 years ago
10 years ago
10 years ago
10 years ago
12 years ago
12 years ago
10 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
10 years ago
10 years ago
10 years ago
12 years ago
10 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
10 years ago
12 years ago
10 years ago
12 years ago
12 years ago
10 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /***************************************************************************
  2. * Copyright (C) 11/2019 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 VERSION_STRING "TWIBOOT v3.0"
  24. #define EEPROM_SUPPORT 1
  25. #define LED_SUPPORT 1
  26. #define USE_CLOCKSTRETCH 0
  27. #define F_CPU 8000000ULL
  28. #define TIMER_DIVISOR 1024
  29. #define TIMER_IRQFREQ_MS 25
  30. #define TIMEOUT_MS 1000
  31. #define TIMER_MSEC2TICKS(x) ((x * F_CPU) / (TIMER_DIVISOR * 1000ULL))
  32. #define TIMER_MSEC2IRQCNT(x) (x / TIMER_IRQFREQ_MS)
  33. #if LED_SUPPORT
  34. #define LED_INIT() DDRB = ((1<<PORTB4) | (1<<PORTB5))
  35. #define LED_RT_ON() PORTB |= (1<<PORTB4)
  36. #define LED_RT_OFF() PORTB &= ~(1<<PORTB4)
  37. #define LED_GN_ON() PORTB |= (1<<PORTB5)
  38. #define LED_GN_OFF() PORTB &= ~(1<<PORTB5)
  39. #define LED_GN_TOGGLE() PORTB ^= (1<<PORTB5)
  40. #define LED_OFF() PORTB = 0x00
  41. #else
  42. #define LED_INIT()
  43. #define LED_RT_ON()
  44. #define LED_RT_OFF()
  45. #define LED_GN_ON()
  46. #define LED_GN_OFF()
  47. #define LED_GN_TOGGLE()
  48. #define LED_OFF()
  49. #endif
  50. #ifndef TWI_ADDRESS
  51. #define TWI_ADDRESS 0x29
  52. #endif
  53. /* SLA+R */
  54. #define CMD_WAIT 0x00
  55. #define CMD_READ_VERSION 0x01
  56. #define CMD_ACCESS_MEMORY 0x02
  57. /* internal mappings */
  58. #define CMD_ACCESS_CHIPINFO (0x10 | CMD_ACCESS_MEMORY)
  59. #define CMD_ACCESS_FLASH (0x20 | CMD_ACCESS_MEMORY)
  60. #define CMD_ACCESS_EEPROM (0x30 | CMD_ACCESS_MEMORY)
  61. #define CMD_WRITE_FLASH_PAGE (0x40 | CMD_ACCESS_MEMORY)
  62. #define CMD_WRITE_EEPROM_PAGE (0x50 | CMD_ACCESS_MEMORY)
  63. /* SLA+W */
  64. #define CMD_SWITCH_APPLICATION CMD_READ_VERSION
  65. /* internal mappings */
  66. #define CMD_BOOT_BOOTLOADER (0x10 | CMD_SWITCH_APPLICATION) /* only in APP */
  67. #define CMD_BOOT_APPLICATION (0x20 | CMD_SWITCH_APPLICATION)
  68. /* CMD_SWITCH_APPLICATION parameter */
  69. #define BOOTTYPE_BOOTLOADER 0x00 /* only in APP */
  70. #define BOOTTYPE_APPLICATION 0x80
  71. /* CMD_{READ|WRITE}_* parameter */
  72. #define MEMTYPE_CHIPINFO 0x00
  73. #define MEMTYPE_FLASH 0x01
  74. #define MEMTYPE_EEPROM 0x02
  75. /*
  76. * LED_GN flashes with 20Hz (while bootloader is running)
  77. * LED_RT flashes on TWI activity
  78. *
  79. * bootloader twi-protocol:
  80. * - abort boot timeout:
  81. * SLA+W, 0x00, STO
  82. *
  83. * - show bootloader version
  84. * SLA+W, 0x01, SLA+R, {16 bytes}, STO
  85. *
  86. * - start application
  87. * SLA+W, 0x01, 0x80, STO
  88. *
  89. * - read chip info: 3byte signature, 1byte page size, 2byte flash size, 2byte eeprom size
  90. * SLA+W, 0x02, 0x00, 0x00, 0x00, SLA+R, {8 bytes}, STO
  91. *
  92. * - read one (or more) flash bytes
  93. * SLA+W, 0x02, 0x01, addrh, addrl, SLA+R, {* bytes}, STO
  94. *
  95. * - read one (or more) eeprom bytes
  96. * SLA+W, 0x02, 0x02, addrh, addrl, SLA+R, {* bytes}, STO
  97. *
  98. * - write one flash page
  99. * SLA+W, 0x02, 0x01, addrh, addrl, {* bytes}, STO
  100. *
  101. * - write one (or more) eeprom bytes
  102. * SLA+W, 0x02, 0x02, addrh, addrl, {* bytes}, STO
  103. */
  104. const static uint8_t info[16] = VERSION_STRING;
  105. const static uint8_t chipinfo[8] = {
  106. SIGNATURE_0, SIGNATURE_1, SIGNATURE_2,
  107. SPM_PAGESIZE,
  108. (BOOTLOADER_START >> 8) & 0xFF,
  109. BOOTLOADER_START & 0xFF,
  110. #if (EEPROM_SUPPORT)
  111. ((E2END +1) >> 8 & 0xFF),
  112. (E2END +1) & 0xFF
  113. #else
  114. 0x00, 0x00
  115. #endif
  116. };
  117. static uint8_t boot_timeout = TIMER_MSEC2IRQCNT(TIMEOUT_MS);
  118. static uint8_t cmd = CMD_WAIT;
  119. /* flash buffer */
  120. static uint8_t buf[SPM_PAGESIZE];
  121. static uint16_t addr;
  122. /* *************************************************************************
  123. * write_flash_page
  124. * ************************************************************************* */
  125. static void write_flash_page(void)
  126. {
  127. uint16_t pagestart = addr;
  128. uint8_t size = SPM_PAGESIZE;
  129. uint8_t *p = buf;
  130. if (pagestart < BOOTLOADER_START)
  131. {
  132. boot_page_erase(pagestart);
  133. boot_spm_busy_wait();
  134. do {
  135. uint16_t data = *p++;
  136. data |= *p++ << 8;
  137. boot_page_fill(addr, data);
  138. addr += 2;
  139. size -= 2;
  140. } while (size);
  141. boot_page_write(pagestart);
  142. boot_spm_busy_wait();
  143. boot_rww_enable();
  144. }
  145. } /* write_flash_page */
  146. #if (EEPROM_SUPPORT)
  147. /* *************************************************************************
  148. * read_eeprom_byte
  149. * ************************************************************************* */
  150. static uint8_t read_eeprom_byte(uint16_t address)
  151. {
  152. EEARL = address;
  153. EEARH = (address >> 8);
  154. EECR |= (1<<EERE);
  155. return EEDR;
  156. } /* read_eeprom_byte */
  157. /* *************************************************************************
  158. * write_eeprom_byte
  159. * ************************************************************************* */
  160. static void write_eeprom_byte(uint8_t val)
  161. {
  162. EEARL = addr;
  163. EEARH = (addr >> 8);
  164. EEDR = val;
  165. addr++;
  166. #if defined (EEWE)
  167. EECR |= (1<<EEMWE);
  168. EECR |= (1<<EEWE);
  169. #elif defined (EEPE)
  170. EECR |= (1<<EEMPE);
  171. EECR |= (1<<EEPE);
  172. #else
  173. #error "EEWE/EEPE not defined"
  174. #endif
  175. eeprom_busy_wait();
  176. } /* write_eeprom_byte */
  177. #if (USE_CLOCKSTRETCH == 0)
  178. /* *************************************************************************
  179. * write_eeprom_buffer
  180. * ************************************************************************* */
  181. static void write_eeprom_buffer(uint8_t size)
  182. {
  183. uint8_t *p = buf;
  184. while (size--)
  185. {
  186. write_eeprom_byte(*p++);
  187. }
  188. } /* write_eeprom_buffer */
  189. #endif /* (USE_CLOCKSTRETCH == 0) */
  190. #endif /* EEPROM_SUPPORT */
  191. /* *************************************************************************
  192. * TWI_data_write
  193. * ************************************************************************* */
  194. static uint8_t TWI_data_write(uint8_t bcnt, uint8_t data)
  195. {
  196. uint8_t ack = 0x01;
  197. switch (bcnt)
  198. {
  199. case 0:
  200. switch (data)
  201. {
  202. case CMD_SWITCH_APPLICATION:
  203. case CMD_ACCESS_MEMORY:
  204. /* no break */
  205. case CMD_WAIT:
  206. /* abort countdown */
  207. boot_timeout = 0;
  208. cmd = data;
  209. break;
  210. default:
  211. /* boot app now */
  212. cmd = CMD_BOOT_APPLICATION;
  213. ack = 0x00;
  214. break;
  215. }
  216. break;
  217. case 1:
  218. switch (cmd)
  219. {
  220. case CMD_SWITCH_APPLICATION:
  221. if (data == BOOTTYPE_APPLICATION)
  222. {
  223. cmd = CMD_BOOT_APPLICATION;
  224. }
  225. ack = 0x00;
  226. break;
  227. case CMD_ACCESS_MEMORY:
  228. if (data == MEMTYPE_CHIPINFO)
  229. {
  230. cmd = CMD_ACCESS_CHIPINFO;
  231. }
  232. else if (data == MEMTYPE_FLASH)
  233. {
  234. cmd = CMD_ACCESS_FLASH;
  235. }
  236. #if (EEPROM_SUPPORT)
  237. else if (data == MEMTYPE_EEPROM)
  238. {
  239. cmd = CMD_ACCESS_EEPROM;
  240. }
  241. #endif /* (EEPROM_SUPPORT) */
  242. else
  243. {
  244. ack = 0x00;
  245. }
  246. break;
  247. default:
  248. ack = 0x00;
  249. break;
  250. }
  251. break;
  252. case 2:
  253. case 3:
  254. addr <<= 8;
  255. addr |= data;
  256. break;
  257. default:
  258. switch (cmd)
  259. {
  260. #if (EEPROM_SUPPORT)
  261. #if (USE_CLOCKSTRETCH)
  262. case CMD_ACCESS_EEPROM:
  263. write_eeprom_byte(data);
  264. break;
  265. #else
  266. case CMD_ACCESS_EEPROM:
  267. cmd = CMD_WRITE_EEPROM_PAGE;
  268. /* fall through */
  269. case CMD_WRITE_EEPROM_PAGE:
  270. #endif /* (USE_CLOCKSTRETCH) */
  271. #endif /* (EEPROM_SUPPORT) */
  272. case CMD_ACCESS_FLASH:
  273. {
  274. uint8_t pos = bcnt -4;
  275. buf[pos] = data;
  276. if (pos >= (sizeof(buf) -2))
  277. {
  278. ack = 0x00;
  279. }
  280. if ((cmd == CMD_ACCESS_FLASH) &&
  281. (pos >= (sizeof(buf) -1))
  282. )
  283. {
  284. #if (USE_CLOCKSTRETCH)
  285. write_flash_page();
  286. #else
  287. cmd = CMD_WRITE_FLASH_PAGE;
  288. #endif
  289. }
  290. break;
  291. }
  292. default:
  293. ack = 0x00;
  294. break;
  295. }
  296. break;
  297. }
  298. return ack;
  299. } /* TWI_data_write */
  300. /* *************************************************************************
  301. * TWI_data_read
  302. * ************************************************************************* */
  303. static uint8_t TWI_data_read(uint8_t bcnt)
  304. {
  305. uint8_t data;
  306. switch (cmd)
  307. {
  308. case CMD_READ_VERSION:
  309. bcnt %= sizeof(info);
  310. data = info[bcnt];
  311. break;
  312. case CMD_ACCESS_CHIPINFO:
  313. bcnt %= sizeof(chipinfo);
  314. data = chipinfo[bcnt];
  315. break;
  316. case CMD_ACCESS_FLASH:
  317. data = pgm_read_byte_near(addr++);
  318. break;
  319. #if (EEPROM_SUPPORT)
  320. case CMD_ACCESS_EEPROM:
  321. data = read_eeprom_byte(addr++);
  322. break;
  323. #endif /* (EEPROM_SUPPORT) */
  324. default:
  325. data = 0xFF;
  326. break;
  327. }
  328. return data;
  329. } /* TWI_data_read */
  330. /* *************************************************************************
  331. * TWI_vect
  332. * ************************************************************************* */
  333. static void TWI_vect(void)
  334. {
  335. static uint8_t bcnt;
  336. uint8_t control = TWCR;
  337. switch (TWSR & 0xF8)
  338. {
  339. /* SLA+W received, ACK returned -> receive data and ACK */
  340. case 0x60:
  341. bcnt = 0;
  342. LED_RT_ON();
  343. break;
  344. /* prev. SLA+W, data received, ACK returned -> receive data and ACK */
  345. case 0x80:
  346. if (TWI_data_write(bcnt++, TWDR) == 0x00)
  347. {
  348. control &= ~(1<<TWEA);
  349. }
  350. break;
  351. /* SLA+R received, ACK returned -> send data */
  352. case 0xA8:
  353. bcnt = 0;
  354. LED_RT_ON();
  355. /* prev. SLA+R, data sent, ACK returned -> send data */
  356. case 0xB8:
  357. TWDR = TWI_data_read(bcnt++);
  358. break;
  359. /* prev. SLA+W, data received, NACK returned -> IDLE */
  360. case 0x88:
  361. TWI_data_write(bcnt++, TWDR);
  362. /* fall through */
  363. /* STOP or repeated START -> IDLE */
  364. case 0xA0:
  365. #if (USE_CLOCKSTRETCH == 0)
  366. if ((cmd == CMD_WRITE_FLASH_PAGE)
  367. #if (EEPROM_SUPPORT)
  368. || (cmd == CMD_WRITE_EEPROM_PAGE)
  369. #endif
  370. )
  371. {
  372. /* disable ACK for now, re-enable after page write */
  373. control &= ~(1<<TWEA);
  374. TWCR = (1<<TWINT) | control;
  375. #if (EEPROM_SUPPORT)
  376. if (cmd == CMD_WRITE_EEPROM_PAGE)
  377. {
  378. write_eeprom_buffer(bcnt -4);
  379. }
  380. else
  381. #endif /* (EEPROM_SUPPORT) */
  382. {
  383. write_flash_page();
  384. }
  385. }
  386. #endif /* (USE_CLOCKSTRETCH) */
  387. bcnt = 0;
  388. /* fall through */
  389. /* prev. SLA+R, data sent, NACK returned -> IDLE */
  390. case 0xC0:
  391. LED_RT_OFF();
  392. control |= (1<<TWEA);
  393. break;
  394. /* illegal state(s) -> reset hardware */
  395. default:
  396. control |= (1<<TWSTO);
  397. break;
  398. }
  399. TWCR = (1<<TWINT) | control;
  400. } /* TWI_vect */
  401. /* *************************************************************************
  402. * TIMER0_OVF_vect
  403. * ************************************************************************* */
  404. static void TIMER0_OVF_vect(void)
  405. {
  406. /* restart timer */
  407. TCNT0 = 0xFF - TIMER_MSEC2TICKS(TIMER_IRQFREQ_MS);
  408. /* blink LED while running */
  409. LED_GN_TOGGLE();
  410. /* count down for app-boot */
  411. if (boot_timeout > 1)
  412. {
  413. boot_timeout--;
  414. }
  415. else if (boot_timeout == 1)
  416. {
  417. /* trigger app-boot */
  418. cmd = CMD_BOOT_APPLICATION;
  419. }
  420. } /* TIMER0_OVF_vect */
  421. static void (*jump_to_app)(void) __attribute__ ((noreturn)) = 0x0000;
  422. /* *************************************************************************
  423. * init1
  424. * ************************************************************************* */
  425. void init1(void) __attribute__((naked, section(".init1")));
  426. void init1(void)
  427. {
  428. /* make sure r1 is 0x00 */
  429. asm volatile ("clr __zero_reg__");
  430. /* on some MCUs the stack pointer defaults NOT to RAMEND */
  431. #if defined(__AVR_ATmega8__) || defined(__AVR_ATmega8515__) || \
  432. defined(__AVR_ATmega8535__) || defined (__AVR_ATmega16__) || \
  433. defined (__AVR_ATmega32__) || defined (__AVR_ATmega64__) || \
  434. defined (__AVR_ATmega128__) || defined (__AVR_ATmega162__)
  435. SP = RAMEND;
  436. #endif
  437. } /* init1 */
  438. /*
  439. * For newer devices the watchdog timer remains active even after a
  440. * system reset. So disable it as soon as possible.
  441. * automagically called on startup
  442. */
  443. #if defined (__AVR_ATmega88__) || defined (__AVR_ATmega168__) || \
  444. defined (__AVR_ATmega328P__)
  445. /* *************************************************************************
  446. * disable_wdt_timer
  447. * ************************************************************************* */
  448. void disable_wdt_timer(void) __attribute__((naked, section(".init3")));
  449. void disable_wdt_timer(void)
  450. {
  451. MCUSR = 0;
  452. WDTCSR = (1<<WDCE) | (1<<WDE);
  453. WDTCSR = (0<<WDE);
  454. } /* disable_wdt_timer */
  455. #endif
  456. /* *************************************************************************
  457. * main
  458. * ************************************************************************* */
  459. int main(void) __attribute__ ((OS_main, section (".init9")));
  460. int main(void)
  461. {
  462. LED_INIT();
  463. LED_GN_ON();
  464. /* timer0: running with F_CPU/1024 */
  465. #if defined (TCCR0)
  466. TCCR0 = (1<<CS02) | (1<<CS00);
  467. #elif defined (TCCR0B)
  468. TCCR0B = (1<<CS02) | (1<<CS00);
  469. #else
  470. #error "TCCR0(B) not defined"
  471. #endif
  472. /* TWI init: set address, auto ACKs */
  473. TWAR = (TWI_ADDRESS<<1);
  474. TWCR = (1<<TWEA) | (1<<TWEN);
  475. while (cmd != CMD_BOOT_APPLICATION)
  476. {
  477. if (TWCR & (1<<TWINT))
  478. {
  479. TWI_vect();
  480. }
  481. #if defined (TIFR)
  482. if (TIFR & (1<<TOV0))
  483. {
  484. TIMER0_OVF_vect();
  485. TIFR = (1<<TOV0);
  486. }
  487. #elif defined (TIFR0)
  488. if (TIFR0 & (1<<TOV0))
  489. {
  490. TIMER0_OVF_vect();
  491. TIFR0 = (1<<TOV0);
  492. }
  493. #else
  494. #error "TIFR(0) not defined"
  495. #endif
  496. }
  497. /* Disable TWI but keep address! */
  498. TWCR = 0x00;
  499. /* disable timer0 */
  500. #if defined (TCCR0)
  501. TCCR0 = 0x00;
  502. #elif defined (TCCR0B)
  503. TCCR0B = 0x00;
  504. #else
  505. #error "TCCR0(B) not defined"
  506. #endif
  507. LED_OFF();
  508. uint16_t wait = 0x0000;
  509. do {
  510. __asm volatile ("nop");
  511. } while (--wait);
  512. jump_to_app();
  513. } /* main */