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.

537 lines
14 KiB

13 years ago
13 years ago
10 years ago
10 years ago
6 years ago
6 years ago
13 years ago
6 years ago
6 years ago
13 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
13 years ago
13 years ago
10 years ago
13 years ago
10 years ago
13 years ago
10 years ago
13 years ago
10 years ago
13 years ago
10 years ago
13 years ago
10 years ago
13 years ago
10 years ago
13 years ago
10 years ago
10 years ago
10 years ago
10 years ago
13 years ago
13 years ago
10 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
10 years ago
10 years ago
10 years ago
13 years ago
10 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
10 years ago
13 years ago
10 years ago
13 years ago
13 years ago
10 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
  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 */