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.

603 lines
16 KiB

  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 */