今天研究了二進制加載功能,但當我試圖把hello從romfs裏拷貝到smartfs中時,出現了IO錯誤的ERROR。關閉二進制加載後任然不能恢復正常。鬱悶。重新打開DEBUG再找找原因。但是這之後就好了。又沒BUG了。我想了想,關閉了DEBUG後再試,果然又出錯了。原因居然在DEBUG上?分析了一下,應該是LOG的問題,打印LOG需要消耗CPU時間,所以每次flash操作間隔都會比邊長。而不打印日誌間隔就會非常短。抓取一下波形。
結果顯示時間差據非常大。開啓LOG後,間隔爲2.3ms。關閉LOG後,間隔只有15us了。。。而且我還發現這個波形不是很漂亮。時鐘信號的脈寬有長有短(這是我的強迫症)。
總之,原因是找到了。但不能就這樣讓我帶着log來操作把。。。每次打印log都是很費時間的。看着桌面上的113M的日誌文件夾,我無話可說。根據經驗,finfo打開的時候不會出現BUG。那就打開它把。這意味着要被強行看一大堆日誌了。。。暫時也沒有什麼更好的辦法,原因發給作者,等他回覆。
早上就看到回覆了,作者給了幾個文件的patch。但是我這邊的文件已經不是原檔了。直接patch是不可能的,只能靠手動修改才能勉強應用這個patch這樣子。。
作者增加了可以用小於4096的扇區大小讀寫的模式。
受不了eclipse了。老是崩潰,一崩潰就內存泄漏。內存佔用經常是94%。而且還找不到是誰佔用的。活見鬼。
今天試了試MSYS2
,打算把代碼移到windows
上來操作。也不用經常忍受權限問題。多好。
1,安裝MSYS2,然後按照README裏的說明把缺少的東西都裝個遍。
2,下載GUN-ARM-Toolchain
的壓縮包,拷貝到MSYS的根目錄下面的usr裏。
3,再下個stlink的windows版,拷貝到MSYS的根目錄下面的usr裏就能make download
了
4,下個picocom的源碼,編譯一下,就能開一個小窗口調試串口了,但是貌似沒有日誌功能,這點不好,但是可以使用tab了,這個好啊。
完成。試着編譯一下,看看時間,clean後加time前綴再編譯。time make -j 32
可以明顯感覺到,clean的速度完全不如在linux上的快。最後結果:
real 0m49.311s
user 0m21.357s
sys 0m49.660s
現在用什麼編輯器呢。。。。。不知道。。。先用sublime湊活一下。
試一下作者發的補丁好了。
感覺很煩躁,用這個MSYS。因爲很卡。換回Linux把。
Linux 用的也很煩躁,各種卡頓。聽說VM挺好用的,換VM試一試。去官網看了半天,下了VM station play
這個免費版。
試了一下效果,簡直是,,,,太流暢了。完全沒有VBOX裏的卡頓的感覺。我想我可以在ubuntu裏裝sublime了。
果然可以。
使用起來很流暢,甚至比win上還要快。
重新git源代碼,修改
#define MX25R6435F_SECTOR_COUNT (16384)
爲
#define MX25R6435F_SECTOR_COUNT (2048)
打開finfo級日誌。
編譯下載。一點問題也沒有。
重新寫接收段的邏輯。我想我可以不寫線程了,因爲實在是難控制。發送和接受同時有一個在運行就可以了。
將發送和接收封裝起來。
static void esp_8266_wchar(int fd, uint8_t *ch)
{
uint8_t temp[2];
temp[0] = ESP_PACK_ESC;
switch (*ch)
{
case ESP_PACK_END:
{
temp[1] = ESP_PACK_ESC_END;
write(fd, temp, 2);
break;
}
case ESP_PACK_ESC:
{
temp[1] = ESP_PACK_ESC_ESC;
write(fd, temp, 2);
break;
}
default:
{
write(fd, ch, 1);
}
}
}
static FAR int esp_8266_rchar(int fd, uint8_t *ch)
{
if(read(fd, ch, 1))
{
/* Check wether a Escape Character */
if (*ch == ESP_PACK_ESC)
{
read(fd, ch, 1);
switch (*ch)
{
case ESP_PACK_ESC_END:
{
*ch = ESP_PACK_END;
break;
}
case ESP_PACK_ESC_ESC:
{
*ch = ESP_PACK_ESC;
break;
}
default:
{
*ch = NULL;
return 0;
}
}
}
return 1;
}
else
{
/* Read nothing */
return 0;
}
}
假設8266死掉了,不能迴應,那麼read也不應該就卡死在這裏。需要一個延時。
借鑑《【轉】Linux串口IO模式的一些心得(阻塞、非阻塞、超時)》這篇文章,可以寫出:
1.開啓阻塞
int flag;
flag = fcntl(rx_fd,F_GETFL);
flag &= ~O_NONBLOCK;
fcntl(fd,F_SETFL,flag);
2.設置超時時間
struct termios tio;
ret = tcgetattr(fd, &tio);
if (ret)
{
fprintf(stderr, "esp_sub_tool: OPEN_SERIAL: ERROR during tcgetattr(): %d\n", errno);
rc = -1;
}
tio.c_cc[VMIN] = 0;
tio.c_cc[VTIME] = 1;
ret = tcsetattr(fd, TCSANOW, &tio);
if (ret)
{
fprintf(stderr, "esp_sub_tool: OPEN_SERIAL: ERROR during tcsetattr(): %d\n", errno);
rc = -1;
}
姑且設置爲1s把。
接下來就可以無憂無慮的寫數據轉換了。
首先,我定義了兩個結構體:
typedef struct
{
uint8_t Type;
uint8_t Command;
uint16_t Data_Size;
uint32_t Checksum;
uint8_t *Body;
} TX_FRAME;
typedef struct
{
uint8_t Type;
uint8_t Command;
uint16_t Data_Size;
uint32_t Response;
uint8_t *Body;
uint8_t Status;
uint8_t Error;
} RX_FRAME;
鑑於body內容長度不定,所以,這裏用指針來指。暫時性還應不上Body。先不管他。
再往上一層就是這樣的,在這一層處理轉義字符。發送一個“字符”:
static void esp_8266_wchar(int fd, uint8_t *ch)
{
uint8_t temp[2];
temp[0] = ESP_PACK_ESC;
switch (*ch)
{
case ESP_PACK_END:
{
temp[1] = ESP_PACK_ESC_END;
write(fd, temp, 2);
break;
}
case ESP_PACK_ESC:
{
temp[1] = ESP_PACK_ESC_ESC;
write(fd, temp, 2);
break;
}
default:
{
write(fd, ch, 1);
}
}
}
接收一個“字符”:
static FAR int esp_8266_rchar(int fd, uint8_t *ch)
{
if(read(fd, ch, 1))
{
if (*ch == ESP_PACK_ESC)
{
read(fd, ch, 1);
switch (*ch)
{
case ESP_PACK_ESC_END:
{
*ch = ESP_PACK_END;
break;
}
case ESP_PACK_ESC_ESC:
{
*ch = ESP_PACK_ESC;
break;
}
default:
{
*ch = NULL;
return 0;
}
}
}
return 1;
}
else
{
return 0; /* Read nothing */
}
}
再上一層。處理結構體,發送:
static void ESP_8266_Sed(int fd, TX_FRAME * tx_buff)
{
uint8_t *tx_ptr;
uint16_t i;
tx_ptr = (uint8_t *) tx_buff;
// printf("ESP_8266_Sed: Command: 0x%02X\n", tx_buff -> Command);
if ((tx_buff -> Command == ESP_COMMAND_SYNC) ||
(tx_buff -> Command == ESP_COMMAND_RREG) || (false))
{
Send_HEAD_END:
{
// printf("ESP_8266_Sed: Send_HEAD_END\n");
uint8_t temp;
temp = ESP_PACK_END;
write(fd, &temp, 1);
}
Send_Type_Command_Data_Size_Checksum:
{
// printf("ESP_8266_Sed: Send_Type_Command_Data_Size_Checksum\n");
for (i = 0; i < 8; ++i)
{
esp_8266_wchar(fd, tx_ptr);
tx_ptr++;
}
}
Send_Body:
{
// printf("ESP_8266_Sed: Send_Body\n");
tx_ptr = (*tx_buff).Body;
for (i = 0; i < (*tx_buff).Data_Size; ++i)
{
esp_8266_wchar(fd, tx_ptr);
tx_ptr++;
}
}
Send_END_END:
{
// printf("ESP_8266_Sed: Send_END_END\n");
uint8_t temp;
temp = ESP_PACK_END;
write(fd, &temp, 1);
}
}
}
接收:
static FAR int ESP_8266_Rec(int fd, RX_FRAME * rx_buff)
{
uint8_t *rx_ptr;
uint16_t i;
uint8_t err;
rx_ptr = (uint8_t *) rx_buff;
/* Read for HEAD */
if(!esp_8266_rchar(fd, rx_ptr))
{
err = ENODATA;
goto ESP_8266_Rec_err;
}
if (*rx_ptr == ESP_PACK_END)
{
Read_Type_Command_Data_Size_Response:
{
for (i = 0; i < 8; ++i)
{
if(esp_8266_rchar(fd, rx_ptr))
{
rx_ptr++;
}
else
{
err = ENODATA;
goto ESP_8266_Rec_err;
}
}
}
Read_Body:
{
rx_ptr = (*rx_buff).Body;
switch((*rx_buff).Command)
{
default:
{
goto Read_Status_Error;
}
}
}
Read_Status_Error:
{
if(!esp_8266_rchar(fd, &((*rx_buff).Status)))
{
err = ENODATA;
goto ESP_8266_Rec_err;
}
if(!esp_8266_rchar(fd, &((*rx_buff).Error)))
{
err = ENODATA;
goto ESP_8266_Rec_err;
}
}
Read_END_END:
{
uint8_t temp;
esp_8266_rchar(fd, &temp);
if(temp != ESP_PACK_END)
{
err = EPROTO;
}
}
return OK;
}
else
{
err = EPROTO;
}
ESP_8266_Rec_err:
{
(*rx_buff).Error = err;
return ERROR;
}
}
這裏爲了方便我看,我就將整段代碼分成了很多段。每一段附上標號。
所以,再上層就是這樣的了:
同步:
SYNC:
{
uint8_t sync_count = 0;
/* waiting for 8266 startup */
usleep(60 * 1000);
/* The 8266 NOW should started and printfed "ets .....""
* those data under baudrate 74880 and useless. So,
* droped it.
*/
tcflush(rx_fd, TCIFLUSH);
/* Send two Sync data */
tx_fram.Command = ESP_COMMAND_SYNC;
tx_fram.Data_Size = sizeof(SYNC_PACK);
tx_fram.Body = (uint8_t *)SYNC_PACK;
ESP_8266_Sed(tx_fd, &tx_fram);
usleep(1000);
sync_count = 0;
int i;
ESP_8266_Sed(tx_fd, &tx_fram);
for (i = 0; i < 8; ++i)
{
ESP_8266_Rec(rx_fd, &rx_fram);
if (rx_fram.Type == 0x01)
{
if (rx_fram.Command == ESP_COMMAND_SYNC)
{
if (rx_fram.Response == SYNC_ACK)
{
rx_fram.Response = (uint32_t) 0;
sync_count++;
}
}
}
}
if (sync_count == 8)
{
printf("esp_sub_tool: SYNC: Sync Successful\n");
}
else
{
printf("esp_sub_tool: SYNC: Sync Failed\n");
goto SYNC;
}
}
讀取MAC段:
Read_MAC:
{
uint32_t ESP_OTP_MAC[4] =
{
0x3FF00050, 0x3FF00054, 0x3FF00058, 0x3FF0005C
};
uint32_t REG_OTP_MAC[4];
int i;
uint8_t mac[8];
// printf("esp_sub_tool: Read_MAC: Entry\n");
tx_fram.Command = ESP_COMMAND_RREG;
tx_fram.Data_Size = (uint16_t) sizeof(uint32_t);
for (i = 0; i < 4; ++i)
{
// printf("esp_sub_tool: Read_MAC: Required REG 0x%08X\n", ESP_OTP_MAC[i]);
tx_fram.Body = (uint8_t *)(&(ESP_OTP_MAC[i]));
ESP_8266_Sed(tx_fd, &tx_fram);
ESP_8266_Rec(rx_fd, &rx_fram);
if (rx_fram.Type == 0x01)
{
if (rx_fram.Command == ESP_COMMAND_RREG)
{
REG_OTP_MAC[i] = rx_fram.Response;
rx_fram.Response = (uint32_t) 0;
printf("esp_sub_tool: Read_MAC: 0x%08X: 0x%08X\n", ESP_OTP_MAC[i], REG_OTP_MAC[i]);
}
}
}
if (REG_OTP_MAC[3] != 0)
{
current_MAC[0] = (REG_OTP_MAC[3] >> 16) & 0xFF;
current_MAC[1] = (REG_OTP_MAC[3] >> 8) & 0xFF;
current_MAC[2] = (REG_OTP_MAC[3] >> 0) & 0xFF;
}
else if (((REG_OTP_MAC[1] >> 16) & 0xFF) == 0)
{
current_MAC[0] = 0x18;
current_MAC[1] = 0xFE;
current_MAC[2] = 0x34;
}
else if (((REG_OTP_MAC[1] >> 16) & 0xFF) == 1)
{
current_MAC[0] = 0xAC;
current_MAC[1] = 0xD0;
current_MAC[2] = 0x74;
}
current_MAC[3] = (REG_OTP_MAC[1] >> 8) & 0xFF;
current_MAC[4] = (REG_OTP_MAC[1] >> 0) & 0xFF;
current_MAC[5] = (REG_OTP_MAC[0] >> 24) & 0xFF;
printf("esp_sub_tool: Read_MAC: MAC: %02X:%02X:%02X:%02X:%02X:%02X\n",
current_MAC[0],
current_MAC[1],
current_MAC[2],
current_MAC[3],
current_MAC[4],
current_MAC[5]);
}
結果:
nsh> esptool
esptool v1.0
esp_sub_tool: SYNC: Sync Successful
esp_sub_tool: Read_MAC: 0x3FF00050: 0xFF6A0000
esp_sub_tool: Read_MAC: 0x3FF00054: 0x02001C1A
esp_sub_tool: Read_MAC: 0x3FF00058: 0x4900B000
esp_sub_tool: Read_MAC: 0x3FF0005C: 0x00A020A6
esp_sub_tool: Read_MAC: MAC: A0:20:A6:1C:1A:FF
esptool_main: com0 exit success
最近有點忙,沒時間做。明天開始研究如何寫整個FLASH。
根據python的內容和下載手冊。燒錄流程分爲三個階段:
1,擦除目標段數據
2,發送數據
3,結束
先從擦除段開始。根據手冊內容可以寫出:
erase_page:
{
uint32_t temp[4];
temp[0] = 256; // Erase sector size: 256 x 4096 = 1Mb
temp[1] = 1024; // Package size
temp[2] = 1024; // Package size
temp[3] = 0; // Address offset
tx_fram.Command = ESP_FLASH_BEGIN;
tx_fram.Data_Size = (uint16_t) sizeof(uint32_t) * 4;
tx_fram.Body = temp;
ESP_8266_Sed(tx_fd, &tx_fram);
ESP_8266_Rec(rx_fd, &rx_fram);
if((rx_fram.Type == 0x01) &&(rx_fram.Command == ESP_FLASH_BEGIN))
{
printf("esp_sub_tool: WRITE_FLASH: erase_page: Response = 0x%04X\n", rx_fram.Response);
}
else
{
printf("esp_sub_tool: WRITE_FLASH: erase_page: ERROR package\n");
goto ESPTOOL_END;
}
}
這段也可以從邏輯分析儀上得出。
接下來寫入數據。先寫個1kb吧。
發送文件的時候,會有一個開頭,共16 byts。那麼可以先開闢1024+16的內存單元,這樣文件頭就能發送出去了,所以:
flash_block:
{
int BIN_fd;
BIN_fd = open(argv[4], O_RDONLY);
if (BIN_fd < 0)
{
printf("esp_sub_tool: flash_block: open file %s failed: %d", argv[4], errno);
return ERROR;
}
FAR uint8_t *buffer;
buffer = (FAR uint8_t *)malloc(sizeof(uint8_t) * 1024 + sizeof(uint32_t) * 4);
if(buffer == NULL)
{
printf("esp_sub_tool: flash_block: ERROR: can not alloc mem: %d", errno);
return ERROR;
}
FAR uint8_t *buffer_ptr;
buffer_ptr = buffer;
((uint32_t *) buffer_ptr)[0] = (uint32_t) 1024;
((uint32_t *) buffer_ptr)[1] = (uint32_t) 1;
((uint32_t *) buffer_ptr)[2] = (uint32_t) 0;
((uint32_t *) buffer_ptr)[3] = (uint32_t) 0;
int nbytesread = read(BIN_fd, &(((uint32_t *) buffer_ptr)[4]), 1024);
printf("esp_sub_tool: flash_block: Read %d bytes\n", nbytesread);
tx_fram.Command = ESP_FLASH_DATA;
tx_fram.Data_Size = 1024 + 16;
tx_fram.Body = buffer_ptr;
ESP_8266_Sed(tx_fd, &tx_fram);
ESP_8266_Rec(rx_fd, &rx_fram);
if((rx_fram.Type == 0x01) &&
(rx_fram.Command == ESP_FLASH_DATA) &&
(rx_fram.Response == ((current_MAC[0] << 16) |
(current_MAC[1] << 8) |
(current_MAC[2] ))))
{
printf("esp_sub_tool: WRITE_FLASH: flash_block: Response = 0x%04X\n", rx_fram.Response);
free(buffer);
}
else
{
printf("esp_sub_tool: WRITE_FLASH: flash_block: ERROR package\n");
goto ESPTOOL_END;
}
free(buffer_ptr);
}
同時我還發現一個有趣的地方。燒錄操作的返回值就是MAC的前幾位。至於是不是就是這樣的,還有待進一步考證。
運行是正常的,改成寫1M。
flash_block:
{
int BIN_fd;
BIN_fd = open(argv[4], O_RDONLY);
if (BIN_fd < 0)
{
printf("esp_sub_tool: flash_block: open file %s failed: %d", argv[4], errno);
return ERROR;
}
FAR uint8_t *buffer;
buffer = (FAR uint8_t *)malloc(sizeof(uint8_t) * 1024 + sizeof(uint32_t) * 4);
if(buffer == NULL)
{
printf("esp_sub_tool: flash_block: ERROR: can not alloc mem: %d", errno);
return ERROR;
}
FAR uint8_t *buffer_ptr;
buffer_ptr = buffer;
((uint32_t *) buffer_ptr)[0] = (uint32_t) 1024;
((uint32_t *) buffer_ptr)[2] = (uint32_t) 0;
((uint32_t *) buffer_ptr)[3] = (uint32_t) 0;
tx_fram.Command = ESP_FLASH_DATA;
tx_fram.Data_Size = 1024 + 16;
tx_fram.Body = buffer_ptr;
int i;
for (i = 0; i < 1024; ++i)
{
((uint32_t *) buffer_ptr)[1] = (uint32_t)(i + 1);
int nbytesread = read(BIN_fd, &(((uint32_t *) buffer_ptr)[4]), 1024);
if (nbytesread > 0)
{
// printf("esp_sub_tool: flash_block: Read %d bytes %d times\n", nbytesread, i + 1);
}
else
{
printf("esp_sub_tool: flash_block: ERROR: Read nothing\n");
free(buffer);
goto ESPTOOL_END;
}
ESP_8266_Sed(tx_fd, &tx_fram);
ESP_8266_Rec(rx_fd, &rx_fram);
if((rx_fram.Type == 0x01) &&
(rx_fram.Command == ESP_FLASH_DATA) &&
(rx_fram.Response == ((current_MAC[0] << 16) |
(current_MAC[1] << 8) |
(current_MAC[2] ))))
{
printf("esp_sub_tool: flash_block: write %d bytes %d times\n", nbytesread, i + 1);
}
else
{
printf("esp_sub_tool: WRITE_FLASH: flash_block: ERROR package\n");
free(buffer);
goto ESPTOOL_END;
}
}
free(buffer);
}
改改細節,運行:
nsh> esptool
esptool v1.0
esp_sub_tool: SYNC: Sync Successful
esp_sub_tool: Read_MAC: 0x3FF00050: 0xFF6A0000
esp_sub_tool: Read_MAC: 0x3FF00054: 0x02001C1A
esp_sub_tool: Read_MAC: 0x3FF00058: 0x4900B000
esp_sub_tool: Read_MAC: 0x3FF0005C: 0x00A020A6
esp_sub_tool: Read_MAC: MAC: A0:20:A6:1C:1A:FF
esp_sub_tool: WRITE_FLASH: erase_page: Response = 0x00A020A6
esp_sub_tool: flash_block: write 1024 bytes 1 times
esp_sub_tool: flash_block: write 1024 bytes 2 times
esp_sub_tool: flash_block: write 1024 bytes 3 times
esp_sub_tool: flash_block: write 1024 bytes 4 times
esp_sub_tool: flash_block: write 1024 bytes 5 times
esp_sub_tool: flash_block: write 1024 bytes 6 times
esp_sub_tool: flash_block: write 1024 bytes 7 times
esp_sub_tool: flash_block: write 1024 bytes 8 times
esp_sub_tool: flash_block: write 1024 bytes 9 times
esp_sub_tool: flash_block: write 1024 bytes 10 times
...
esp_sub_tool: flash_block: write 1024 bytes 1018 times
esp_sub_tool: flash_block: write 1024 bytes 1019 times
esp_sub_tool: flash_block: write 1024 bytes 1020 times
esp_sub_tool: flash_block: write 1024 bytes 1021 times
esp_sub_tool: flash_block: write 1024 bytes 1022 times
esp_sub_tool: flash_block: write 1024 bytes 1023 times
esp_sub_tool: flash_block: write 1024 bytes 1024 times
esp_sub_tool: WRITE_FLASH: Write successful
esptool_main: com0 exit success
邏輯分析儀的結果與hexdump的結果一致。下來就是校驗和高速下載了。
OK。新開一篇。