Added common target header file.
[betaflight.git] / src / main / io / serial_4way_avrootloader.c
blobacc05f9225c6d3c7171afe3704846552b5d9c4d9
1 /*
2 * This file is part of Cleanflight.
4 * Cleanflight is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
9 * Cleanflight 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.
14 * You should have received a copy of the GNU General Public License
15 * along with Cleanflight. If not, see <http://www.gnu.org/licenses/>.
16 * Author: 4712
17 * for info about Hagens AVRootloader:
18 * http://www.mikrocontroller.net/topic/avr-bootloader-mit-verschluesselung
21 #include <stdbool.h>
22 #include <stdint.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <platform.h>
27 #ifdef USE_SERIAL_4WAY_BLHELI_INTERFACE
29 #include "common/utils.h"
30 #include "drivers/system.h"
31 #include "drivers/serial.h"
32 #include "drivers/buf_writer.h"
33 #include "drivers/pwm_mapping.h"
34 #include "drivers/gpio.h"
35 #include "io/serial.h"
36 #include "io/serial_msp.h"
37 #include "io/serial_4way.h"
38 #include "io/serial_4way_impl.h"
39 #include "io/serial_4way_avrootloader.h"
41 // Bootloader commands
42 // RunCmd
43 #define RestartBootloader 0
44 #define ExitBootloader 1
46 #define CMD_RUN 0x00
47 #define CMD_PROG_FLASH 0x01
48 #define CMD_ERASE_FLASH 0x02
49 #define CMD_READ_FLASH_SIL 0x03
50 #define CMD_VERIFY_FLASH 0x03
51 #define CMD_READ_EEPROM 0x04
52 #define CMD_PROG_EEPROM 0x05
53 #define CMD_READ_SRAM 0x06
54 #define CMD_READ_FLASH_ATM 0x07
55 #define CMD_KEEP_ALIVE 0xFD
56 #define CMD_SET_ADDRESS 0xFF
57 #define CMD_SET_BUFFER 0xFE
59 #define CMD_BOOTINIT 0x07
60 #define CMD_BOOTSIGN 0x08
62 // Bootloader result codes
64 #define BR_SUCCESS 0x30
65 #define BR_ERRORCOMMAND 0xC1
66 #define BR_ERRORCRC 0xC2
67 #define BR_NONE 0xFF
69 #define START_BIT_TIMEOUT 2000 // 2ms
71 #define BIT_TIME 52 // 52uS
72 #define BIT_TIME_HALVE (BIT_TIME >> 1) // 26uS
73 #define START_BIT_TIME (BIT_TIME_HALVE + 1)
75 static int suart_getc(void)
77 uint32_t btime;
78 uint32_t start_time;
80 uint32_t wait_time = micros() + START_BIT_TIMEOUT;
81 while (ESC_IS_HI) {
82 // check for startbit begin
83 if (micros() >= wait_time) {
84 return -1;
87 // start bit
88 start_time = micros();
89 btime = start_time + START_BIT_TIME;
90 uint16_t bitmask = 0;
91 for(int bit = 0; bit < 10; bit++) {
92 while (cmp32(micros(), btime) < 0);
93 if (ESC_IS_HI)
94 bitmask |= (1 << bit);
95 btime = btime + BIT_TIME;
97 // check start bit and stop bit
98 if ((bitmask & (1 << 0)) || (!(bitmask & (1 << 9)))) {
99 return -1;
101 return bitmask >> 1;
104 static void suart_putc(uint8_t byte)
106 // send one idle bit first (stopbit from previous byte)
107 uint16_t bitmask = (byte << 2) | (1 << 0) | (1 << 10);
108 uint32_t btime = micros();
109 while(1) {
110 if(bitmask & 1)
111 ESC_SET_HI; // 1
112 else
113 ESC_SET_LO; // 0
114 btime = btime + BIT_TIME;
115 bitmask >>= 1;
116 if (bitmask == 0)
117 break; // stopbit shifted out - but don't wait
118 while (cmp32(micros(), btime) < 0);
122 static uint16_t crc16Byte(uint16_t from, uint8_t byte)
124 uint16_t crc16 = from;
125 for (int i = 0; i < 8; i++) {
126 if (((byte & 0x01) ^ (crc16 & 0x0001)) != 0) {
127 crc16 >>= 1;
128 crc16 ^= 0xA001;
129 } else {
130 crc16 >>= 1;
132 byte >>= 1;
134 return crc16;
137 static uint8_t BL_ReadBuf(uint8_t *pstring, int len, bool checkCrc)
139 int crc = 0;
140 int c;
142 uint8_t lastACK = BR_NONE;
143 for(int i = 0; i < len; i++) {
144 int c;
145 if ((c = suart_getc()) < 0) goto timeout;
146 crc = crc16Byte(crc, c);
147 pstring[i] = c;
150 if(checkCrc) {
151 // With CRC read 3 more
152 for(int i = 0; i < 2; i++) { // checksum 2 CRC bytes
153 if ((c = suart_getc()) < 0) goto timeout;
154 crc = crc16Byte(crc, c);
156 if((c = suart_getc()) < 0) goto timeout;
157 lastACK = c;
158 if (crc != 0) // CRC of correct message is 0
159 lastACK = BR_ERRORCRC;
160 } else {
161 if((c = suart_getc()) < 0) goto timeout;
162 lastACK = c;
164 timeout:
165 return (lastACK == BR_SUCCESS);
168 static void BL_SendBuf(uint8_t *pstring, int len, bool appendCrc)
170 ESC_OUTPUT;
171 uint16_t crc = 0;
172 for(int i = 0; i < len; i++) {
173 suart_putc(pstring[i]);
174 crc = crc16Byte(crc, pstring[i]);
176 if (appendCrc) {
177 suart_putc(crc & 0xff);
178 suart_putc(crc >> 8);
180 ESC_INPUT;
183 uint8_t BL_ConnectEx(escDeviceInfo_t *pDeviceInfo)
185 #define BOOT_MSG_LEN 4
186 #define DevSignHi (BOOT_MSG_LEN)
187 #define DevSignLo (BOOT_MSG_LEN + 1)
189 memset(pDeviceInfo, 0, sizeof(*pDeviceInfo));
190 uint8_t bootInfo[BOOT_MSG_LEN + 4];
191 static const uint8_t bootMsgCheck[BOOT_MSG_LEN - 1] = "471";
192 // x * 0 + 9
193 #if defined(USE_SERIAL_4WAY_SK_BOOTLOADER)
194 // SK message was sent during autodetection, use longer preamble
195 uint8_t bootInit[] = {0,0,0,0,0,0,0,0,0,0,0,0,0x0D,'B','L','H','e','l','i',0xF4,0x7D};
196 #else
197 uint8_t bootInit[] = { 0,0,0,0,0,0,0,0,0x0D,'B','L','H','e','l','i',0xF4,0x7D};
198 #endif
199 BL_SendBuf(bootInit, sizeof(bootInit), false);
200 if (!BL_ReadBuf(bootInfo, sizeof(bootInfo), false))
201 return 0;
202 // BootInfo has no CRC (ACK byte already analyzed... )
203 // Format = BootMsg("471c") SIGNATURE_001, SIGNATURE_002, BootVersion (always 6), BootPages (,ACK)
204 if(memcmp(bootInfo, bootMsgCheck, sizeof(bootMsgCheck)) != 0) // Check only the first 3 letters -> 471x OK
205 return 0;
207 pDeviceInfo->signature2 = bootInfo[BOOT_MSG_LEN - 1]; // taken from bootloaderMsg part, ascii 'c' now
208 pDeviceInfo->signature = (bootInfo[DevSignHi] << 8) | bootInfo[DevSignLo]; // SIGNATURE_001, SIGNATURE_002
209 return 1;
212 static uint8_t BL_GetACK(int timeout)
214 int c;
215 while ((c = suart_getc()) < 0)
216 if(--timeout < 0) // timeout=1 -> 1 retry
217 return BR_NONE;
218 return c;
221 uint8_t BL_SendCMDKeepAlive(void)
223 uint8_t sCMD[] = {CMD_KEEP_ALIVE, 0};
224 BL_SendBuf(sCMD, sizeof(sCMD), true);
225 if (BL_GetACK(1) != BR_ERRORCOMMAND)
226 return 0;
227 return 1;
230 void BL_SendCMDRunRestartBootloader(void)
232 uint8_t sCMD[] = {RestartBootloader, 0};
233 BL_SendBuf(sCMD, sizeof(sCMD), true); // sends simply 4 x 0x00 (CRC = 00)
234 return;
237 static uint8_t BL_SendCMDSetAddress(ioMem_t *pMem) //supports only 16 bit Adr
239 // skip if adr == 0xFFFF
240 if((pMem->addr == 0xffff))
241 return 1;
242 uint8_t sCMD[] = {CMD_SET_ADDRESS, 0, pMem->addr >> 8, pMem->addr & 0xff };
243 BL_SendBuf(sCMD, sizeof(sCMD), true);
244 return BL_GetACK(2) == BR_SUCCESS;
247 static uint8_t BL_SendCMDSetBuffer(ioMem_t *pMem)
249 uint16_t len = pMem->len;
250 uint8_t sCMD[] = {CMD_SET_BUFFER, 0, len >> 8, len & 0xff};
251 BL_SendBuf(sCMD, sizeof(sCMD), true);
252 if (BL_GetACK(2) != BR_NONE)
253 return 0;
254 BL_SendBuf(pMem->data, len, true);
255 return BL_GetACK(40) == BR_SUCCESS;
258 static uint8_t BL_ReadA(uint8_t cmd, ioMem_t *pMem)
260 if(!BL_SendCMDSetAddress(pMem))
261 return 0;
262 unsigned len = pMem->len;
263 uint8_t sCMD[] = {cmd, len & 0xff}; // 0x100 is sent a 0x00 here
264 BL_SendBuf(sCMD, sizeof(sCMD), true);
265 return BL_ReadBuf(pMem->data, len, true);
268 static uint8_t BL_WriteA(uint8_t cmd, ioMem_t *pMem, uint32_t timeout)
270 if(!BL_SendCMDSetAddress(pMem))
271 return 0;
272 if (!BL_SendCMDSetBuffer(pMem))
273 return 0;
274 uint8_t sCMD[] = {cmd, 0x01};
275 BL_SendBuf(sCMD, sizeof(sCMD), true);
276 return BL_GetACK(timeout) == BR_SUCCESS;
280 uint8_t BL_ReadFlashATM(ioMem_t *pMem)
282 return BL_ReadA(CMD_READ_FLASH_ATM, pMem);
285 uint8_t BL_ReadFlashSIL(ioMem_t *pMem)
287 return BL_ReadA(CMD_READ_FLASH_SIL, pMem);
291 uint8_t BL_ReadEEprom(ioMem_t *pMem)
293 return BL_ReadA(CMD_READ_EEPROM, pMem);
296 uint8_t BL_PageErase(ioMem_t *pMem)
298 if(!BL_SendCMDSetAddress(pMem))
299 return 0;
301 uint8_t sCMD[] = {CMD_ERASE_FLASH, 0x01};
302 BL_SendBuf(sCMD, sizeof(sCMD), true);
303 return BL_GetACK(40 * 1000 / START_BIT_TIMEOUT) == BR_SUCCESS;
306 uint8_t BL_WriteEEprom(ioMem_t *pMem)
308 return BL_WriteA(CMD_PROG_EEPROM, pMem, 3000 * 1000 / START_BIT_TIMEOUT);
311 uint8_t BL_WriteFlash(ioMem_t *pMem)
313 return BL_WriteA(CMD_PROG_FLASH, pMem, 40 * 1000 / START_BIT_TIMEOUT);
316 #endif