optiboot_w25x20cl.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. //! @file
  2. // Based on the OptiBoot project
  3. // https://github.com/Optiboot/optiboot
  4. // Licence GLP 2 or later.
  5. #include "Marlin.h"
  6. #include "w25x20cl.h"
  7. #include "stk500.h"
  8. #include "bootapp.h"
  9. #include <avr/wdt.h>
  10. #define OPTIBOOT_MAJVER 6
  11. #define OPTIBOOT_CUSTOMVER 0
  12. #define OPTIBOOT_MINVER 2
  13. static unsigned const int __attribute__((section(".version")))
  14. optiboot_version = 256*(OPTIBOOT_MAJVER + OPTIBOOT_CUSTOMVER) + OPTIBOOT_MINVER;
  15. /* Watchdog settings */
  16. #define WATCHDOG_OFF (0)
  17. #define WATCHDOG_16MS (_BV(WDE))
  18. #define WATCHDOG_32MS (_BV(WDP0) | _BV(WDE))
  19. #define WATCHDOG_64MS (_BV(WDP1) | _BV(WDE))
  20. #define WATCHDOG_125MS (_BV(WDP1) | _BV(WDP0) | _BV(WDE))
  21. #define WATCHDOG_250MS (_BV(WDP2) | _BV(WDE))
  22. #define WATCHDOG_500MS (_BV(WDP2) | _BV(WDP0) | _BV(WDE))
  23. #define WATCHDOG_1S (_BV(WDP2) | _BV(WDP1) | _BV(WDE))
  24. #define WATCHDOG_2S (_BV(WDP2) | _BV(WDP1) | _BV(WDP0) | _BV(WDE))
  25. #define WATCHDOG_4S (_BV(WDP3) | _BV(WDE))
  26. #define WATCHDOG_8S (_BV(WDP3) | _BV(WDP0) | _BV(WDE))
  27. #if 0
  28. #define W25X20CL_SIGNATURE_0 9
  29. #define W25X20CL_SIGNATURE_1 8
  30. #define W25X20CL_SIGNATURE_2 7
  31. #else
  32. //FIXME this is a signature of ATmega2560!
  33. #define W25X20CL_SIGNATURE_0 0x1E
  34. #define W25X20CL_SIGNATURE_1 0x98
  35. #define W25X20CL_SIGNATURE_2 0x01
  36. #endif
  37. static void watchdogConfig(uint8_t x) {
  38. CRITICAL_SECTION_START
  39. WDTCSR = _BV(WDCE) | _BV(WDE);
  40. WDTCSR = x;
  41. CRITICAL_SECTION_END
  42. }
  43. #define RECV_READY ((UCSR0A & _BV(RXC0)) != 0)
  44. static uint8_t getch(void) {
  45. uint8_t ch;
  46. while(! RECV_READY) ;
  47. if (!(UCSR0A & _BV(FE0))) {
  48. /*
  49. * A Framing Error indicates (probably) that something is talking
  50. * to us at the wrong bit rate. Assume that this is because it
  51. * expects to be talking to the application, and DON'T reset the
  52. * watchdog. This should cause the bootloader to abort and run
  53. * the application "soon", if it keeps happening. (Note that we
  54. * don't care that an invalid char is returned...)
  55. */
  56. wdt_reset();
  57. }
  58. ch = UDR0;
  59. return ch;
  60. }
  61. static void putch(char ch) {
  62. while (!(UCSR0A & _BV(UDRE0)));
  63. UDR0 = ch;
  64. }
  65. static void verifySpace() {
  66. if (getch() != CRC_EOP) {
  67. putch(STK_FAILED);
  68. watchdogConfig(WATCHDOG_16MS); // shorten WD timeout
  69. while (1) // and busy-loop so that WD causes
  70. ; // a reset and app start.
  71. }
  72. putch(STK_INSYNC);
  73. }
  74. static void getNch(uint8_t count) {
  75. do getch(); while (--count);
  76. verifySpace();
  77. }
  78. typedef uint16_t pagelen_t;
  79. static const char entry_magic_send [] PROGMEM = "start\n";
  80. static const char entry_magic_receive[] PROGMEM = "w25x20cl_enter\n";
  81. static const char entry_magic_cfm [] PROGMEM = "w25x20cl_cfm\n";
  82. struct block_t;
  83. extern struct block_t *block_buffer;
  84. //! @brief Enter an STK500 compatible Optiboot boot loader waiting for flashing the languages to an external flash memory.
  85. //! @return 1 if "start\n" was not sent. Optiboot was skipped
  86. //! @return 0 if "start\n" was sent. Optiboot ran normally. No need to send "start\n" in setup()
  87. uint8_t optiboot_w25x20cl_enter()
  88. {
  89. if (boot_app_flags & BOOT_APP_FLG_USER0) return 1;
  90. uint8_t ch;
  91. uint8_t rampz = 0;
  92. register uint16_t address = 0;
  93. register pagelen_t length;
  94. // Use the planner's queue for the receive / transmit buffers.
  95. // uint8_t *buff = (uint8_t*)block_buffer;
  96. uint8_t buff[260];
  97. // bitmap of pages to be written. Bit is set to 1 if the page has already been erased.
  98. uint8_t pages_erased = 0;
  99. // Handshake sequence: Initialize the serial line, flush serial line, send magic, receive magic.
  100. // If the magic is not received on time, or it is not received correctly, continue to the application.
  101. {
  102. wdt_reset();
  103. unsigned long boot_timeout = 2000000;
  104. unsigned long boot_timer = 0;
  105. const char *ptr = entry_magic_send;
  106. const char *end = strlen_P(entry_magic_send) + ptr;
  107. const uint8_t selectedSerialPort_bak = selectedSerialPort;
  108. // Flush the serial line.
  109. while (RECV_READY) {
  110. wdt_reset();
  111. // Dummy register read (discard)
  112. (void)(*(char *)UDR0);
  113. }
  114. selectedSerialPort = 0; //switch to Serial0
  115. MYSERIAL.flush(); //clear RX buffer
  116. int SerialHead = rx_buffer.head;
  117. // Send the initial magic string.
  118. while (ptr != end)
  119. putch(pgm_read_byte(ptr ++));
  120. wdt_reset();
  121. // Wait for two seconds until a magic string (constant entry_magic) is received
  122. // from the serial line.
  123. ptr = entry_magic_receive;
  124. end = strlen_P(entry_magic_receive) + ptr;
  125. while (ptr != end) {
  126. while (rx_buffer.head == SerialHead) {
  127. wdt_reset();
  128. delayMicroseconds(1);
  129. if (++ boot_timer > boot_timeout)
  130. {
  131. // Timeout expired, continue with the application.
  132. selectedSerialPort = selectedSerialPort_bak; //revert Serial setting
  133. return 0;
  134. }
  135. }
  136. ch = rx_buffer.buffer[SerialHead];
  137. SerialHead = (unsigned int)(SerialHead + 1) % RX_BUFFER_SIZE;
  138. if (pgm_read_byte(ptr ++) != ch)
  139. {
  140. // Magic was not received correctly, continue with the application
  141. selectedSerialPort = selectedSerialPort_bak; //revert Serial setting
  142. return 0;
  143. }
  144. wdt_reset();
  145. }
  146. cbi(UCSR0B, RXCIE0); //disable the MarlinSerial0 interrupt
  147. // Send the cfm magic string.
  148. ptr = entry_magic_cfm;
  149. while (ptr != end)
  150. putch(pgm_read_byte(ptr ++));
  151. }
  152. spi_init();
  153. w25x20cl_init();
  154. watchdogConfig(WATCHDOG_OFF);
  155. /* Forever loop: exits by causing WDT reset */
  156. for (;;) {
  157. /* get character from UART */
  158. ch = getch();
  159. if(ch == STK_GET_PARAMETER) {
  160. unsigned char which = getch();
  161. verifySpace();
  162. /*
  163. * Send optiboot version as "SW version"
  164. * Note that the references to memory are optimized away.
  165. */
  166. if (which == STK_SW_MINOR) {
  167. putch(optiboot_version & 0xFF);
  168. } else if (which == STK_SW_MAJOR) {
  169. putch(optiboot_version >> 8);
  170. } else {
  171. /*
  172. * GET PARAMETER returns a generic 0x03 reply for
  173. * other parameters - enough to keep Avrdude happy
  174. */
  175. putch(0x03);
  176. }
  177. }
  178. else if(ch == STK_SET_DEVICE) {
  179. // SET DEVICE is ignored
  180. getNch(20);
  181. }
  182. else if(ch == STK_SET_DEVICE_EXT) {
  183. // SET DEVICE EXT is ignored
  184. getNch(5);
  185. }
  186. else if(ch == STK_LOAD_ADDRESS) {
  187. // LOAD ADDRESS
  188. uint16_t newAddress;
  189. // Workaround for the infamous ';' bug in the Prusa3D usb to serial converter.
  190. // Send the binary data by nibbles to avoid transmitting the ';' character.
  191. newAddress = getch();
  192. newAddress |= getch();
  193. newAddress |= (((uint16_t)getch()) << 8);
  194. newAddress |= (((uint16_t)getch()) << 8);
  195. // Transfer top bit to LSB in rampz
  196. if (newAddress & 0x8000)
  197. rampz |= 0x01;
  198. else
  199. rampz &= 0xFE;
  200. newAddress += newAddress; // Convert from word address to byte address
  201. address = newAddress;
  202. verifySpace();
  203. }
  204. else if(ch == STK_UNIVERSAL) {
  205. // LOAD_EXTENDED_ADDRESS is needed in STK_UNIVERSAL for addressing more than 128kB
  206. if ( AVR_OP_LOAD_EXT_ADDR == getch() ) {
  207. // get address
  208. getch(); // get '0'
  209. rampz = (rampz & 0x01) | ((getch() << 1) & 0xff); // get address and put it in rampz
  210. getNch(1); // get last '0'
  211. // response
  212. putch(0x00);
  213. }
  214. else {
  215. // everything else is ignored
  216. getNch(3);
  217. putch(0x00);
  218. }
  219. }
  220. /* Write memory, length is big endian and is in bytes */
  221. else if(ch == STK_PROG_PAGE) {
  222. // PROGRAM PAGE - we support flash programming only, not EEPROM
  223. uint8_t desttype;
  224. uint8_t *bufPtr;
  225. pagelen_t savelength;
  226. // Read the page length, with the length transferred each nibble separately to work around
  227. // the Prusa's USB to serial infamous semicolon issue.
  228. length = ((pagelen_t)getch()) << 8;
  229. length |= ((pagelen_t)getch()) << 8;
  230. length |= getch();
  231. length |= getch();
  232. savelength = length;
  233. // Read the destination type. It should always be 'F' as flash.
  234. desttype = getch();
  235. // read a page worth of contents
  236. bufPtr = buff;
  237. do *bufPtr++ = getch();
  238. while (--length);
  239. // Read command terminator, start reply
  240. verifySpace();
  241. if (desttype == 'E') {
  242. while (1) ; // Error: wait for WDT
  243. } else {
  244. uint32_t addr = (((uint32_t)rampz) << 16) | address;
  245. // During a single bootloader run, only erase a 64kB block once.
  246. // An 8bit bitmask 'pages_erased' covers 512kB of FLASH memory.
  247. if ((address == 0) && (pages_erased & (1 << (addr >> 16))) == 0) {
  248. w25x20cl_wait_busy();
  249. w25x20cl_enable_wr();
  250. w25x20cl_block64_erase(addr);
  251. pages_erased |= (1 << (addr >> 16));
  252. }
  253. w25x20cl_wait_busy();
  254. w25x20cl_enable_wr();
  255. w25x20cl_page_program(addr, buff, savelength);
  256. w25x20cl_wait_busy();
  257. w25x20cl_disable_wr();
  258. }
  259. }
  260. /* Read memory block mode, length is big endian. */
  261. else if(ch == STK_READ_PAGE) {
  262. uint32_t addr = (((uint32_t)rampz) << 16) | address;
  263. register pagelen_t i;
  264. // Read the page length, with the length transferred each nibble separately to work around
  265. // the Prusa's USB to serial infamous semicolon issue.
  266. length = ((pagelen_t)getch()) << 8;
  267. length |= ((pagelen_t)getch()) << 8;
  268. length |= getch();
  269. length |= getch();
  270. // Read the destination type. It should always be 'F' as flash. It is not checked.
  271. (void)getch();
  272. verifySpace();
  273. w25x20cl_wait_busy();
  274. w25x20cl_rd_data(addr, buff, length);
  275. for (i = 0; i < length; ++ i)
  276. putch(buff[i]);
  277. }
  278. /* Get device signature bytes */
  279. else if(ch == STK_READ_SIGN) {
  280. // READ SIGN - return what Avrdude wants to hear
  281. verifySpace();
  282. putch(W25X20CL_SIGNATURE_0);
  283. putch(W25X20CL_SIGNATURE_1);
  284. putch(W25X20CL_SIGNATURE_2);
  285. }
  286. else if (ch == STK_LEAVE_PROGMODE) { /* 'Q' */
  287. // Adaboot no-wait mod
  288. watchdogConfig(WATCHDOG_16MS);
  289. verifySpace();
  290. }
  291. else {
  292. // This covers the response to commands like STK_ENTER_PROGMODE
  293. verifySpace();
  294. }
  295. putch(STK_OK);
  296. }
  297. }