基于STM32F407 MCU,写一段通过串口和Ymodem协议接收bin文件进行在线升级的代码,使用C语言
时间: 2023-12-10 18:41:02 浏览: 157
以下是一个基于STM32F407 MCU的串口Ymodem协议在线升级代码示例:
```c
#include "stm32f4xx.h"
#include <string.h>
#define FLASH_START_ADDRESS 0x08000000
#define FLASH_END_ADDRESS 0x08080000 // 512KB Flash
#define UART_TIMEOUT_MS 1000 // UART接收超时时间,单位毫秒
#define FLASH_PAGE_SIZE 2048 // Flash页大小
#define BUFFER_SIZE 2048 // 缓存区大小
#define SOH 0x01 // Ymodem协议Start of Header标志
#define STX 0x02 // Ymodem协议Start of Text标志
#define EOT 0x04 // Ymodem协议End of Transmission标志
#define ACK 0x06 // Ymodem协议Acknowledge标志
#define NAK 0x15 // Ymodem协议Negative Acknowledge标志
#define CAN 0x18 // Ymodem协议Cancel标志
#define CRC16 0x43 // Ymodem协议CRC16标志
uint8_t buffer[BUFFER_SIZE]; // 缓存区
uint8_t filename[128]; // 文件名
uint32_t filesize; // 文件大小
void erase_flash(uint32_t start_addr, uint32_t end_addr)
{
FLASH_EraseInitTypeDef erase_init;
uint32_t page_error;
HAL_FLASH_Unlock();
erase_init.TypeErase = FLASH_TYPEERASE_SECTORS;
erase_init.Sector = FLASH_SECTOR_0;
erase_init.NbSectors = (end_addr - start_addr) / FLASH_PAGE_SIZE;
erase_init.VoltageRange = FLASH_VOLTAGE_RANGE_3;
HAL_FLASHEx_Erase(&erase_init, &page_error);
HAL_FLASH_Lock();
}
void write_flash(uint32_t addr, uint8_t *data, uint32_t size)
{
uint32_t i;
uint32_t remainder = addr % FLASH_PAGE_SIZE;
uint32_t start_address = addr - remainder;
uint32_t page_count = (size + remainder + FLASH_PAGE_SIZE - 1) / FLASH_PAGE_SIZE;
if (HAL_FLASH_Unlock() != HAL_OK) {
return;
}
for (i = 0; i < page_count; i++) {
uint32_t page_address = start_address + i * FLASH_PAGE_SIZE;
uint32_t offset = i == 0 ? remainder : 0;
uint32_t write_size = i == page_count - 1 ? (size - i * FLASH_PAGE_SIZE + remainder) : FLASH_PAGE_SIZE;
uint32_t j;
for (j = 0; j < write_size; j++) {
buffer[j + offset] = data[i * FLASH_PAGE_SIZE + j];
}
HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, page_address, (uint64_t*)buffer);
uint32_t k;
for (k = 0; k < write_size; k += 4) {
if (*(uint32_t*)(page_address + k) != *(uint32_t*)(buffer + k)) {
return;
}
}
}
HAL_FLASH_Lock();
}
uint32_t get_crc16(uint8_t *data, uint32_t size)
{
uint32_t i, j;
uint32_t crc = 0;
for (i = 0; i < size; i++) {
crc ^= ((uint32_t)data[i] << 8);
for (j = 0; j < 8; j++) {
if ((crc & 0x8000) != 0) {
crc = (crc << 1) ^ 0x1021;
} else {
crc = (crc << 1);
}
}
}
return crc;
}
int receive_ymodem(int fd)
{
uint8_t packet_number = 0;
uint32_t bytes_received = 0;
uint32_t crc = 0;
uint32_t expected_crc = 0;
uint16_t block_size = 128;
uint8_t packet[1024];
uint8_t packet_type;
erase_flash(FLASH_START_ADDRESS, FLASH_END_ADDRESS);
while (1) {
HAL_StatusTypeDef status;
uint32_t start_time = HAL_GetTick();
do {
status = HAL_UART_Receive(fd, packet, 1, UART_TIMEOUT_MS);
if (status == HAL_OK) {
break;
}
} while (HAL_GetTick() - start_time < UART_TIMEOUT_MS);
if (status != HAL_OK) {
HAL_UART_Transmit(fd, &NAK, 1, UART_TIMEOUT_MS);
continue;
}
switch (packet[0]) {
case SOH:
block_size = 128;
packet_type = SOH;
break;
case STX:
block_size = 1024;
packet_type = STX;
break;
case EOT:
HAL_UART_Transmit(fd, &ACK, 1, UART_TIMEOUT_MS);
return 1;
case CAN:
if (HAL_UART_Receive(fd, packet, 1, UART_TIMEOUT_MS) == HAL_OK && packet[0] == CAN) {
HAL_UART_Transmit(fd, &ACK, 1, UART_TIMEOUT_MS);
return 0;
}
break;
default:
break;
}
if (packet_type == SOH || packet_type == STX) {
uint8_t packet[1024];
uint8_t packet_number_complement;
uint16_t i;
packet_number++;
packet_number_complement = ~packet_number;
if (HAL_UART_Receive(fd, packet, block_size + 4, UART_TIMEOUT_MS) != HAL_OK) {
HAL_UART_Transmit(fd, &NAK, 1, UART_TIMEOUT_MS);
continue;
}
if (packet[0] != packet_number || packet[1] != packet_number_complement) {
HAL_UART_Transmit(fd, &NAK, 1, UART_TIMEOUT_MS);
continue;
}
crc = get_crc16(packet + 2, block_size);
expected_crc = (packet[block_size + 2] << 8) | packet[block_size + 3];
if (crc != expected_crc) {
HAL_UART_Transmit(fd, &NAK, 1, UART_TIMEOUT_MS);
continue;
}
for (i = 0; i < block_size; i++) {
buffer[bytes_received + i] = packet[i + 2];
}
bytes_received += block_size;
HAL_UART_Transmit(fd, &ACK, 1, UART_TIMEOUT_MS);
}
}
}
int main(void)
{
/* 初始化串口 */
/* 初始化 Ymodem 协议 */
/* 等待接收文件 */
/* 升级完成后,跳转到新固件 */
void (*new_app)(void) = (void*)FLASH_START_ADDRESS;
new_app();
while (1);
}
```
注意,以上代码仅是示例,实际使用时需要根据具体的硬件和软件环境进行适当修改和调试。
阅读全文