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.

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