GD32或STM32(GD32F103RB)Ymodem串口升级源码分析

先看GD32或STM32(GD32F103RB)用Ymodem升级-CSDN博客

结合下代码:(代码进行过删减,主要用来理解每个步骤的目的使用)

串口:

初始化串口,这里没有开启任何关于串口的中断(串口的idle空闲标记也只是用来查询,并不是中断);

DMA:

使能串口的DMA接收同样也没有开DMA中断;

着重介绍一下UART_RecvData函数的使用,该函数是用了DMA接收串口数据,接收完数据就会触发空闲的标记位USART_FLAG_IDLE(否则一直while等待,除非超时)。主要步骤如下:(每次需要调用该函数时,都会执行以下,请结合下面代码看以下文字)

1 设置DMA的帧数据长度dma_transfer_number_config:

如果调用函数传递的len=-1,则USART1_RX_BUF_SIZE为1029,否则设置的长度就是len;(可能会有疑问?每次都有1029数据传递么(3帧头+1024+2CRC),Ymodem不是还有128的帧么);我理解这里帧长度,只是说收完这么长的一帧之后,一定会触发相应的标记或者中断;但是,如果实际上收到的帧长度没有那么长,中断空闲了,也同样会有相应的标记或中断被触发。例如DMA的DMA_FLAG_FTF,传输完成标记;

2 while等待串口的USART_FLAG_IDLE空闲标记:该标记置位的条件是,收完一帧数据,或者收完一段数据后停止了一段时间(根据代码现象,我理解是这样,但是我没有仔细去查阅);UART_RecvData函数超时后,根据调试现象(容易接收失败),UART_SendChar(YMODEM_COM, 0x15, timeout);添加这条NAK 0x15的应答,我理解上位机收到后这条会重发该帧

这里再说下USART_FLAG_IDLE这个标记怎么清除:

软件先读USART_STAT,再读USART_DATA可清除该位。(没有找到有清除它的函数);

3 拷贝DMA的buf的内容。

static void HardwareInit(void) {
    UART_UserInit(YMODEM_COM, 115200);
    UART1_UserDMAInit();
}


void UART_UserInit(uint32_t usartPeriph, uint32_t baud) {
	uint32_t com_id = 0U;

	switch(usartPeriph) {
	case USER_COM0:
		com_id = 0U;
		break;

	case USER_COM1:
		com_id = 1U;
		break;

	case USER_COM2:
		com_id &#
  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是STM32F103的Ymodem升级程序示例代码: ``` #include "stm32f10x.h" #include "stdio.h" #define FLASH_APP1_ADDR 0x08010000 // APP1地址 #define FLASH_APP2_ADDR 0x08020000 // APP2地址 #define FLASH_APP_SIZE 0x00010000 // 每个APP的大小为64KB #define FLASH_PAGE_SIZE 0x400 // Flash每页大小为1KB #define YMODEM_SOH 0x01 #define YMODEM_STX 0x02 #define YMODEM_EOT 0x04 #define YMODEM_ACK 0x06 #define YMODEM_NAK 0x15 #define YMODEM_CAN 0x18 #define YMODEM_C 0x43 #define YMODEM_TIMEOUT 1000 // 超时时间 #define YMODEM_MAX_SIZE 64 // Ymodem每个数据包大小为128字节,实际使用时使用64字节即可 static uint8_t ymodem_get_char(uint8_t *data); static uint8_t ymodem_wait_ack(void); static uint8_t ymodem_calc_checksum(uint8_t *data, uint32_t size); static uint8_t ymodem_write_packet(uint8_t *data, uint32_t size); static void ymodem_write_flash(uint8_t *data, uint32_t size); static uint8_t ymodem_file_receive(uint32_t addr); int main(void) { if (ymodem_file_receive(FLASH_APP1_ADDR) == YMODEM_ACK) { // APP1接收成功,重启执行APP1 NVIC_SystemReset(); } else if (ymodem_file_receive(FLASH_APP2_ADDR) == YMODEM_ACK) { // APP2接收成功,重启执行APP2 NVIC_SystemReset(); } while (1); } static uint8_t ymodem_get_char(uint8_t *data) { uint32_t timer = YMODEM_TIMEOUT; while (timer--) { if (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) != RESET) { *data = USART_ReceiveData(USART1); return YMODEM_ACK; } } return YMODEM_NAK; } static uint8_t ymodem_wait_ack(void) { uint8_t ret = 0; uint8_t data = YMODEM_NAK; uint32_t timer = YMODEM_TIMEOUT; while (timer--) { ret = ymodem_get_char(&data); if (ret == YMODEM_ACK) { return YMODEM_ACK; } } return YMODEM_NAK; } static uint8_t ymodem_calc_checksum(uint8_t *data, uint32_t size) { uint8_t checksum = 0; uint32_t i; for (i = 0; i < size; i++) { checksum += data[i]; } return checksum; } static uint8_t ymodem_write_packet(uint8_t *data, uint32_t size) { uint8_t ret = 0; uint8_t packet[YMODEM_MAX_SIZE + 6]; // 数据包大小为64字节,加上头部3字节和尾部3字节 uint32_t i; packet[0] = size == 128 ? YMODEM_SOH : YMODEM_STX; // 根据数据包大小确定头类型 packet[1] = 0xFF - size; // 数据包大小的补码 packet[2] = size == 128 ? 0x00 : 0x01; // 序号,128字节为0,64字节为1 for (i = 0; i < size; i++) { packet[i + 3] = data[i]; } for (i = size + 3; i < YMODEM_MAX_SIZE + 3; i++) { packet[i] = 0x00; // 填充0 } uint8_t checksum = ymodem_calc_checksum(&packet[3], size); packet[YMODEM_MAX_SIZE + 3] = checksum; packet[YMODEM_MAX_SIZE + 4] = 0x00; // 结束标志 packet[YMODEM_MAX_SIZE + 5] = 0x00; // 结束标志 for (i = 0; i < 3; i++) { ret = USART_SendData(USART1, packet[i]); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } for (i = 3; i < YMODEM_MAX_SIZE + 6; i++) { ret = USART_SendData(USART1, packet[i]); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } return ymodem_wait_ack(); } static void ymodem_write_flash(uint8_t *data, uint32_t size) { uint32_t i; uint32_t flash_addr = FLASH_APP1_ADDR; // 默认写入APP1 uint8_t flash_erase = 0; // 是否需要擦除 // 判断写入目标地址 if (*((uint32_t *)data) == 0x08020000) { flash_addr = FLASH_APP2_ADDR; } // 判断是否需要擦除 if (flash_addr == FLASH_APP1_ADDR) { if (*((uint32_t *)FLASH_APP1_ADDR) != 0xFFFFFFFF) { flash_erase = 1; } } else { if (*((uint32_t *)FLASH_APP2_ADDR) != 0xFFFFFFFF) { flash_erase = 1; } } // 擦除Flash if (flash_erase) { FLASH_Unlock(); FLASH_ErasePage(flash_addr); FLASH_Lock(); } // 写入数据到Flash FLASH_Unlock(); for (i = 0; i < size; i += 4) { FLASH_ProgramWord(flash_addr + i, *((uint32_t *)(data + i))); } FLASH_Lock(); } static uint8_t ymodem_file_receive(uint32_t addr) { uint8_t ret = 0; uint8_t data[YMODEM_MAX_SIZE]; uint32_t size = 0; uint32_t i; // 发送开始传输命令 uint8_t cmd = YMODEM_C; for (i = 0; i < 3; i++) { ret = USART_SendData(USART1, cmd); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } while (1) { // 等待数据包 ret = ymodem_wait_ack(); if (ret != YMODEM_SOH && ret != YMODEM_STX) { return YMODEM_NAK; } // 接收数据包 for (i = 0; i < YMODEM_MAX_SIZE; i++) { ret = ymodem_get_char(&data[i]); if (ret != YMODEM_ACK) { return YMODEM_NAK; } } // 校验数据包 if (data[YMODEM_MAX_SIZE - 2] != ymodem_calc_checksum(data, YMODEM_MAX_SIZE - 2)) { return YMODEM_NAK; } // 处理数据包 if (data[0] == YMODEM_SOH) { size = 128; } else { size = 64; } // 数据包序号为0时表示结束 if (data[1] == 0x00) { // 发送确认结束命令 for (i = 0; i < 3; i++) { ret = USART_SendData(USART1, YMODEM_ACK); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } break; } // 写入Flash ymodem_write_flash(&data[3], size); // 发送确认命令 for (i = 0; i < 3; i++) { ret = USART_SendData(USART1, YMODEM_ACK); while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); } } return YMODEM_ACK; } ``` 该示例代码实现了一个Ymodem协议的文件接收程序,可以将接收到的数据包写入Flash中。在主函数中,先尝试接收APP1,如果接收成功则重启执行APP1;如果接收失败则尝试接收APP2,如果接收成功则重启执行APP2。如果两个APP都接收失败,则程序将被卡在while(1)循环中,等待重新启动。 在使用该示例代码时,需要在USART1的RX和TX引脚上接上一个串口转USB模块,并将该模块连接到PC上。然后,在PC上使用Ymodem协议的传输工具发送.bin文件即可。 需要注意的是,该示例代码只是一个简单的示例,实际使用时还需要根据具体的应用场景进行修改和完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值