STM32 Ymodem 协议及代码解析
·
介绍了STM32中Ymodem协议的使用,包括协议的传输效果、协议介绍、最低要求、帧详解以及文件传输过程。通过SecureCRT发送端和接收端的实现,解析了Ymodem协议的帧结构和命令
STM32 Ymodem 协议及代码解析
文章导图:(出处STM32 Ymodem 协议及代码解析-CSDN博客)

1. Ymodem 协议传输效果
1.1 发送端软件效果图
- SecureCRT 软件:SecureCRT 支持 Ymodem 协议传输,可以作为接收端也可以作为发送端,这里我们作为发送端发送升级固件给 STM32 接收端。
- SecureCRT 软件配置如下:




1.2 STM32接收端效果图

STM32 作为 Ymodem 接收端接收升级固件进行升级。

2. Ymodem 协议介绍
YModem 协议是由 XModem 协议演变而来的,每包数据可以达到 1024 字节,是一个非常高效的文件传输协议。我们平常所说的 Ymodem 协议是指的 Ymodem-1K,除此还有 Ymodem-g(没有 CRC 校验,不常用)。YModem-1K 协议用 1024 字节数据帧传输取代了标准的 128 字节数据帧传输,发送的数据会使用 CRC 校验,保证数据传输的正确性。它每传输一个信息块时,就会等待接收端返回 ACK 信号,接收到响应信号后,才会继续传输下一个信息块,从而保证能够接收到全部数据。
3. Ymodem 协议的最低要求
所有声称支持 Ymodem 协议的项目必须满足以下最低要求:
- 发送端应在第一个数据包中发送路径名(文件名)。
- 路径名应为以 “NULL” 结尾的 ASCII 字符串,如下所述:
- 1)除非有特别要求,否则只发送文件名部分。
- 2)不发送驱动器符号。
- 3)不区分文件名中大小写字母的系统应仅以小写形式发送路径名。
- 接收端应使用第一个数据包中发送的路径名作为接收文件名。
- 当接收端接收到第一个数据包并成功打开输出文件后,应该返回一个 “ACK” 字符给发送端用来确认这个数据包,然后接收端再发送 “C” 或 “NAK” 来开始进行正常的 Ymodem 传输。
- 接收端在接收每个文件时要能接受 128 字节和 1024 字节两种数据包的任意混合发送。发送端可以在 1024 字节和 128 字节两种数据包之间任意切换发送。
- 发送端不能更改未确认的数据包的长度。
- 在每次文件传输的末尾,发送端只能发送最多 10 次 “EOT” 字符,直到它收到一个 “ACK” 字符。(这是 Xmodem 规范的一部分。)
- 发送路径名为空的路径名数据包来表示传输会话的结束,该路径名数据包应与其它路径名数据包一样被确认。
没有满足以上要求的程序不兼容 Ymodem 协议,不应被描述为支持 Ymodem 协议。
4. Ymodem 协议帧详解
4.1 帧格式
Ymodem 有两种帧格式,主要区别是数据长度的不同。
| 名称 | 结构 | 长度(byte) | 标识符 | 备注 |
|---|---|---|---|---|
| 帧头 | SOH/STX | 1 | 0x01/0x02 | 表示帧格式类型; SOH 为 128 字节帧,STX 为 1024 字节帧; |
| 帧序号 | FN | 1 | 帧序号为 1 个字节,取值范围为 0~255; 当帧序号大于 255 后,序号重新从 0 开始累加; | |
| 帧序号反码 | XFN | 1 | 帧序号的反码,数值为 0xFF-FN; | |
| 数据 | DATA | 128/1024 | 帧头是 SOH 的数据帧,数据为 128 字节,该类型帧的总长度为 133 字节; 帧头是 STX 的数据帧,数据是 1024 字节,该类型帧总长度为 1029 字节; | |
| 校验 | CRC | 2 | Ymodem 采用的是 CRC16 校验算法,校验值为 2 字节,传输时 CRC 高八位在前,低八位在后; CRC 计算的是数据部分,不包含帧头、帧序号和帧序号反码; |
4.2 帧命令
| 命令 | 命令码 | 备注 |
|---|---|---|
| SOH | 0x01 | start of 128-byte data packet |
| STX | 0x02 | start of 1024-byte data packet |
| EOT | 0x04 | end of transmission |
| ACK | 0x06 | acknowledge |
| NAK | 0x15 | negative acknowledge |
| CA | 0x18 | two of these in succession aborts transfer |
| C | 0x43 | ‘C’ == 0x43, request command |
| ABORT1 | 0x41 | ‘A’ == 0x41, abort by user |
| ABORT2 | 0x61 | ‘a’ == 0x61, abort by user |
4.3 起始帧
Ymodem 的起始帧并不直接传输文件的数据,而是将文件名和文件大小放在数据段中进行传输。
它的帧结构如下所示:
| 名称 | 结构 | 长度(byte) | 标识符 | 备注 |
|---|---|---|---|---|
| 帧头 | SOH/STX | 1 | 0x01 | 起始帧为128字节帧; |
| 帧序号 | FN | 1 | 0x00 | 起始帧的帧序号固定为 0x00; |
| 帧序号反码 | XFN | 1 | 0xFF | 起始帧的帧序号反码固定为 0xFF; |
| 数据 | FILENAME | 要传输的文件名; | ||
| 数据 | FILESIZE | 要传输的文件大小; | ||
| 数据 | NULL | 若 FILENAME 和 FILESIZE 加起来不满 128 字节则以 0x00 填充剩余字节; | ||
| 校验 | CRC | 2 | 数据部分的 CRC 校验; |
- 帧头为 SOH,表示起始帧中包含着128个字节的数据。
- 起始帧帧序号固定为 0x00,帧序号反码为 0xFF。
- FILENAME 是要传输的文件名,如 Template.bin,它在数据帧中的格式为:0x54,0x65,0x6D,0x70,0x6C,0x61,0x74,0x65,0x2E,0x62,0x69,0x6E,0x00,也就是把 ASCII 码转成十六进制,但是最后一定要在文件名后加上 0x00,表示文件名的结束。
- FILESIZE 是要传输的文件大小,如上面的 Template.bin 大小 107544 byte,转换成十六进制就是 0x1A418。它在数据帧中的格式可以是十进制数的 0x31,0x30,0x37,0x35,0x34,0x34,也可以是十六进制数的 0x30,0x78,0x31,0x41,0x34,0x31,0x38,同样最后要加上 0x00 表示结束。
- NULL 是数据部分若 FILENAME 和 FILESIZE 加起来不满 128 字节则以 0x00 填充剩余字节。
- CRC 表示的是数据部分的 CRC 校验,不包含帧头、帧序号和帧序号反码。校验值为 2 字节,传输时 CRC 高八位在前,低八位在后。
4.4 数据帧
YModem 数据帧会将数据段用来传输文件。
它的帧结构如下所示:
| 名称 | 结构 | 长度(byte) | 标识符 | 备注 |
|---|---|---|---|---|
| 帧头 | SOH/STX | 1 | 0x01/0x02 | 数据帧可以是 128 字节帧或 1024 字节帧; |
| 帧序号 | FN | 1 | 数据帧帧序号为 0~255; | |
| 帧序号反码 | XFN | 1 | 帧序号的反码,数值为0xFF-FN; | |
| 数据 | DATA | 要传输的数据; | ||
| 数据 | FILL | 若 DATA 不满 128/1024 字节则以 0x1A 填充剩余字节; | ||
| 校验 | CRC | 2 | 数据部分的 CRC 校验; |
- 帧头为 SOH 时,表示数据帧的数据段为 128 个字节;帧头为 STX 时,表示数据帧的数据段为 1024 个字节。
- 数据帧帧序号的取值范围为 0~255,帧序号反码是它的取反;
- DATA 表示要传输的文件数据;
- FILL 是数据部分若 DATA 不满 128/1024 字节则以 0x1A 填充剩余字节。
- CRC 表示的是数据部分的 CRC 校验,不包含帧头、帧序号和帧序号反码。校验值为 2 字节,传输时 CRC 高八位在前,低八位在后。
4.5 结束帧
YModem 的结束帧也采用帧头为 SOH 的 128 字节帧,它的结构如下:
| 名称 | 结构 | 长度(byte) | 标识符 | 备注 |
|---|---|---|---|---|
| 帧头 | SOH | 1 | 0x01 | 结束帧为 128 字节帧; |
| 帧序号 | FN | 1 | 0x00 | 结束帧的帧序号固定为 0x00; |
| 帧序号反码 | XFN | 1 | 0xFF | 结束帧的帧序号反码固定为 0xFF; |
| 数据 | DATA | 128 | 结束帧的数据部分不存放任何信息,全部用 0x00 填充; | |
| 校验 | CRC | 2 | 数据部分的 CRC 校验; |
- 帧头为 SOH,表示结束帧中包含着 128 个字节的数据。
- 结束帧帧序号固定为 0x00,帧序号反码为 0xFF。
- 结束帧的 128 字节的数据部分不存放任何信息,即全部用 0x00 填充。
- CRC 表示的是数据部分的 CRC 校验,不包含帧头、帧序号和帧序号反码。校验值为 2 字节,传输时 CRC 高八位在前,低八位在后。
5. 文件传输过程
文件的传输过程我们用具体的例子来说明。将大小为 107544 byte 的文件 “Template.bin” 作为传输对象,则它的传输过程如下:

- 当发送端选择完发送文件后则进入准备发送文件状态。
- 当接收端想要开始接收文件时发送命令 ‘C’ 请求发送端开始传输文件,然后开始等待发送端的起始帧。如果发送端没有发送起始帧,则接收端可选择超时退出或者继续发送命令 ‘C’ 请求发送端开始传输文件。
- 发送端在开始时处于准备发送文件状态,等待接收端发送命令 ‘C’。在发送端收到命令 ‘C’ 后发送起始帧给接收端,内容如下(文件名为 Template.bin,文件大小为 107544 byte):
SOH 00 FF FILENAME[54 65…6E 00] FILESIZE[31 30…34 00] NULL[108] CRC
然后发送端开始等待接收端发送 ‘ACK’。 - 当接收端收到起始帧,CRC 校验通过且确定要接收此文件后,则发送 ‘ACK’ 给发送端。
- 发送端收到 ‘ACK’ 后则开始等待接收端的“请求开始传输数据”信号,即重新进入等待接收端发送命令 “C” 的状态。
- 接收端发送命令 ‘C’,请求发送端开始传输数据。
- 发送端在收到命令 ‘C’ 后,开始发送数据帧。内容如下:
STX 01 FE DATA[1024] CRC
然后发送端开始等待接收端发送 ‘ACK’。 - 接收端收到数据帧后,返回一个 ‘ACK’,然后等待接收下一个数据帧,再继续返回 ‘ACK’。直到所有数据传输完毕。
- 数据传输完毕后,发送端发送 ‘EOT’ 命令给接收端,接收端第一次收到 ‘EOT’ 命令后返回 ‘NAK’,要求进行二次确认。发送端收到 ‘NAK’ 后,重发 ‘EOT’ 命令。接收端第二次收到 ‘EOT’ 命令,返回 ‘ACK’ 应答,确认文件传输完毕。
- 最后接收端发送命令 ‘C’ 请求发送端开启下一个文件传输,发送端在没有第二个文件要传输的情况下,发送结束帧给接收端。内容如下:
SOH 00 FF NULL[128] CRC
在接收端返回 ‘ACK’ 后,正式结束数据传输。
6. STM32 代码解析
BOOT程序
/********************************************************************
* 文件:ymodem.c
* 描述:Ymodem协议实现文件,包含协议处理、数据接收、进度显示等功能
* 版本:v1.0
* 作者:weiwenjie
********************************************************************/
#include "ymodem.h"
// 全局变量定义
file_info_t current_file = {0}; // 当前传输文件信息结构体
// Ymodem协议处理结构体初始化
ymodem_t ymodem = {
.process = WAIT_START_PROGRAM, // 初始状态:等待开始编程
.status = 0, // 协议状态机初始状态
.addr = 0x08004000, // Flash编程起始地址
.app_sector_addr = APP_SECTOR_ADDR, // 应用程序扇区地址
.file_size = 0, // 文件大小初始为0
.transferred_size = 0, // 已传输数据大小初始为0
.file_info = {0}, // 文件信息结构体清零
.cb = NULL // 回调函数指针初始化为空
};
download_buf_t recvBuf; // 数据接收缓冲区
seq_queue_t rx_queue; // 串口接收队列
/********************************************************************
* name : queue_initiate
* description : 初始化循环队列
* Input : Q - 队列结构体指针
* Output : 无
* Return : 无
********************************************************************/
void queue_initiate(seq_queue_t *Q)
{
Q->rear = 0; // 队尾指针初始化为0
Q->front = 0; // 队首指针初始化为0
Q->count = 0; // 队列元素计数初始化为0
}
/********************************************************************
* name : queue_not_empty
* description : 判断循环队列是否非空
* Input : Q - 队列结构体指针
* Output : 无
* Return : 1-队列非空,0-队列为空
********************************************************************/
int queue_not_empty(seq_queue_t *Q)
{
if(Q->count != 0)
return 1; // 队列中有元素,返回1
else
return 0; // 队列为空,返回0
}
/********************************************************************
* name : queue_append
* description : 数据元素入队列操作
* Input : Q - 队列结构体指针
* x - 要插入的数据元素
* Output : 无
* Return : 1-插入成功,0-插入失败(队列已满)
********************************************************************/
int queue_append(seq_queue_t *Q, uint8_t x)
{
if(Q->count > 0 && Q->rear == Q->front)
{
uart_log("queue is full! \n"); // 队列已满,输出提示信息
return 0; // 插入失败
}
else
{
Q->queue[Q->rear] = x; // 数据存入队尾
Q->rear = (Q->rear + 1) % MAX_QUEUE_SIZE; // 队尾指针循环递增
Q->count ++; // 队列元素计数加1
return 1; // 插入成功
}
}
/********************************************************************
* name : queue_delete
* description : 数据元素出队列操作
* Input : Q - 队列结构体指针
* d - 用于存储出队数据的指针
* Output : d - 存储出队的数据
* Return : 1-删除成功,0-删除失败(队列为空)
********************************************************************/
int queue_delete(seq_queue_t *Q, uint8_t *d)
{
if(Q->count == 0)
{
// uart_log("queue is empty! \n"); // 队列为空,可取消注释用于调试
return 0; // 删除失败
}
else
{
*d = Q->queue[Q->front]; // 获取队首数据
Q->front = (Q->front + 1) % MAX_QUEUE_SIZE; // 队首指针循环递增
Q->count--; // 队列元素计数减1
return 1; // 删除成功
}
}
/********************************************************************
* name : queue_get
* description : 获取队头数据元素(不删除)
* Input : Q - 队列结构体
* d - 用于存储队头数据的指针
* Output : d - 存储队头数据
* Return : 1-获取成功,0-获取失败(队列为空)
********************************************************************/
int queue_get(seq_queue_t Q, uint8_t *d)
{
if(Q.count == 0)
{
// uart_log("queue is empty! \n"); // 队列为空,可取消注释用于调试
return 0; // 获取失败
}
else
{
*d = Q.queue[Q.front]; // 获取队首数据但不移动指针
return 1; // 获取成功
}
}
/********************************************************************
* name : ymodem_ack
* description : 发送Ymodem ACK确认信号
* Input : 无
* Output : 无
* Return : 无
********************************************************************/
void ymodem_ack(void)
{
uint8_t buf;
buf = YMODEM_ACK; // 设置ACK字节
RS485_Send_Data(&buf, 1); // 通过RS485发送ACK
}
/********************************************************************
* name : ymodem_nack
* description : 发送Ymodem NACK否定确认信号
* Input : 无
* Output : 无
* Return : 无
********************************************************************/
void ymodem_nack(void)
{
uint8_t buf;
buf = YMODEM_NAK; // 设置NAK字节
RS485_Send_Data(&buf, 1); // 通过RS485发送NAK
}
/********************************************************************
* name : ymodem_c
* description : 发送Ymodem 'C'字符,请求使用CRC校验
* Input : 无
* Output : 无
* Return : 无
********************************************************************/
void ymodem_c(void)
{
uint8_t buf;
buf = YMODEM_C; // 设置'C'字节
RS485_Send_Data(&buf, 1); // 通过RS485发送'C'
}
/********************************************************************
* name : set_ymodem_status
* description : 设置Ymodem处理状态
* Input : process - 要设置的处理状态
* Output : 无
* Return : 无
********************************************************************/
void set_ymodem_status(process_status process)
{
ymodem.process = process; // 更新Ymodem处理状态
}
/********************************************************************
* name : get_ymodem_status
* description : 获取当前Ymodem处理状态
* Input : 无
* Output : 无
* Return : 当前处理状态
********************************************************************/
process_status get_ymodem_status(void)
{
process_status process = ymodem.process;
return process; // 返回当前处理状态
}
/********************************************************************
* name : ymodem_start
* description : 启动Ymodem传输
* Input : cb - 回调函数指针
* Output : 无
* Return : 无
********************************************************************/
void ymodem_start(ymodem_callback cb)
{
if (ymodem.status == 0) // 确保在初始状态下启动
{
ymodem.cb = cb; // 设置回调函数
}
}
/********************************************************************
* name : parse_file_header
* description : 解析Ymodem文件头信息(文件名和文件大小)
* Input : data - 包含文件头数据的缓冲区
* file_info - 文件信息结构体指针
* Output : file_info - 解析后的文件信息
* Return : 1-解析成功,0-解析失败
********************************************************************/
int parse_file_header(uint8_t *data, file_info_t *file_info)
{
int i = 3; // 跳过SOH(0x01), 包序列号, 包序列号取反
// 解析文件名
int filename_idx = 0;
while (data[i] != 0 && i < 128 && filename_idx < sizeof(file_info->filename) - 1) {
file_info->filename[filename_idx++] = data[i++]; // 逐个字符复制文件名
}
file_info->filename[filename_idx] = '\0'; // 添加字符串结束符
i++; // 跳过文件名结束的0字节
// 解析文件大小
char size_str[16] = {0}; // 文件大小字符串缓冲区
int size_idx = 0;
while (data[i] != 0 && i < 128 && size_idx < sizeof(size_str) - 1) {
size_str[size_idx++] = data[i++]; // 逐个字符复制文件大小字符串
}
if (size_idx > 0) {
file_info->filesize = atoi(size_str); // 字符串转整数
file_info->file_found = 1; // 标记文件头已找到
file_info->transferred_size = 0; // 初始化已传输大小为0
uart_log("File: %s, Size: %lu bytes\r\n", file_info->filename, file_info->filesize);
return 1; // 解析成功
}
return 0; // 解析失败
}
/********************************************************************
* name : show_update_progress
* description : 显示带进度条的升级进度信息
* Input : file_info - 文件信息结构体指针
* current_transferred - 当前已传输字节数
* Output : 无
* Return : 无
********************************************************************/
void show_update_progress(file_info_t *file_info, uint32_t current_transferred)
{
if (file_info->file_found && file_info->filesize > 0) {
file_info->transferred_size = current_transferred; // 更新已传输大小
// 计算百分比(限制在0-100%)
uint32_t percent = (file_info->transferred_size * 100) / file_info->filesize;
if (percent > 100) percent = 100;
// 显示进度条
uart_log("[");
for (int i = 0; i < 50; i++) {
if (i < percent / 2) {
uart_log("="); // 已完成部分
} else if (i == percent / 2) {
uart_log(">"); // 当前进度位置
} else {
uart_log(" "); // 未完成部分
}
}
uart_log("] %3lu%% (%lu/%lu bytes)\r",
percent, file_info->transferred_size, file_info->filesize);
// 如果完成,换行
if (percent == 100) {
uart_log("\n");
}
}
}
/********************************************************************
* name : show_update_percent
* description : 显示简化的升级百分比信息
* Input : file_info - 文件信息结构体指针
* current_transferred - 当前已传输字节数
* Output : 无
* Return : 无
********************************************************************/
void show_update_percent(file_info_t *file_info, uint32_t current_transferred)
{
if (file_info->file_found && file_info->filesize > 0) {
file_info->transferred_size = current_transferred; // 更新已传输大小
uint32_t percent = (file_info->transferred_size * 100) / file_info->filesize;
if (percent > 100) percent = 100; // 限制百分比不超过100%
uart_log("Progress: %3lu%% (%lu/%lu bytes)\r",
percent, file_info->transferred_size, file_info->filesize);
if (percent == 100) {
uart_log("\nUpdate Complete!\r\n"); // 传输完成提示
}
}
}
/********************************************************************
* name : ymodem_recv
* description : Ymodem数据接收处理函数
* Input : p - 接收数据缓冲区指针
* Output : 无
* Return : 无
********************************************************************/
void ymodem_recv(download_buf_t *p)
{
uint8_t type = p->data[0]; // 获取数据包类型
static uint32_t total_transferred = 0; // 累计传输字节数
switch (ymodem.status)
{
case 0: // 状态0:等待文件头开始包
if (type == YMODEM_SOH) // 接收到文件头开始包
{
ymodem.process = BUSY; // 设置为忙状态
ymodem.addr = APP_SECTOR_ADDR; // 设置Flash编程地址
uart_log("erase flash: 0x%08X\r\n", APP_SECTOR_ADDR);
mcu_flash_erase(ymodem.addr, APP_ERASE_SECTORS_NUM); // 擦除Flash
uart_log("erase flash success\r\n");
// 解析文件头信息
if (parse_file_header(p->data, ¤t_file))
{
// 显示开始升级信息
uart_log("Starting update: %s (%lu bytes)\r\n",
current_file.filename, current_file.filesize);
}
ymodem_ack(); // 发送ACK确认
ymodem_c(); // 发送'C'请求CRC校验
ymodem.status++; // 状态转移到下一阶段
}
else if (type == '1') // 接收到开始编程指令
{
uart_log("start program now\r\n");
ymodem.process = START_PROGRAM; // 设置为开始编程状态
}
else if (type == '2') // 接收到开始更新指令
{
uart_log("enter update mode\r\n");
ymodem.process = UPDATE_PROGRAM; // 设置为更新编程状态
}
break;
case 1: // 状态1:接收数据包阶段
if (type == YMODEM_SOH || type == YMODEM_STX) // 数据包开始
{
uint32_t block_size = 0;
if (type == YMODEM_SOH) // 128字节数据包
{
block_size = 128;
mcu_flash_write(ymodem.addr, &p->data[3], 128); // 写入Flash
ymodem.addr += block_size; // 地址递增
}
else // 1024字节数据包
{
block_size = 1024;
mcu_flash_write(ymodem.addr, &p->data[3], 1024); // 写入Flash
ymodem.addr += block_size; // 地址递增
}
total_transferred += block_size; // 累计传输字节数
// 显示升级进度(每10个包显示一次,避免过于频繁)
static uint16_t packet_count = 0;
packet_count++;
if (packet_count % 10 == 0) {
show_update_progress(¤t_file, total_transferred); // 显示进度
// 或者使用简化版本:show_update_percent(¤t_file, total_transferred);
}
ymodem_ack(); // 发送ACK确认
}
else if (type == YMODEM_EOT) // 传输结束标志
{
// 显示最终进度
ymodem_nack(); // 发送NACK要求重传EOT
ymodem.status++; // 状态转移到下一阶段
}
else
{
ymodem.status = 0; // 错误情况,重置状态
}
break;
case 2: // 状态2:确认传输结束
if (type == YMODEM_EOT) // 再次收到传输结束标志
{
uart_log("END \r\n");
ymodem_ack(); // 发送ACK确认
ymodem_c(); // 发送'C'准备接收下一个文件
ymodem.status++; // 状态转移到下一阶段
}
break;
case 3: // 状态3:接收空包,完全结束传输
// 空包,表示传输完全结束
if (type == YMODEM_SOH)
{
ymodem_ack(); // 发送最终ACK
ymodem.status = 0; // 重置状态机
ymodem.process = UPDATE_SUCCESS; // 设置为更新成功状态
uart_log("Update successful! File: %s, Total: %lu bytes\r\n",
current_file.filename, total_transferred);
// 重置文件信息
memset(¤t_file, 0, sizeof(current_file));
}
}
p->len = 0; // 重置接收缓冲区长度
}
/********************************************************************
* name : ymodem_init
* description : Ymodem协议初始化函数
* Input : 无
* Output : 无
* Return : 无
********************************************************************/
void ymodem_init(void)
{
RS485_Init(115200); // 初始化RS485通信,波特率115200
timer_init(); // 初始化定时器
queue_initiate(&rx_queue); // 初始化接收队列
}
/********************************************************************
* name : USART2_IRQHandler
* description : USART2串口中断服务函数
* Input : 无
* Output : 无
* Return : 无
********************************************************************/
void USART2_IRQHandler(void)
{
uint8_t res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) // 检查接收中断
{
res = USART_ReceiveData(USART2); // 读取接收到的数据
queue_append(&rx_queue, res); // 数据入队列
// uart_log("%c",res); // 可取消注释用于调试
}
TIM3->CNT = 0; // 重置定时器计数值
TIM_Cmd(TIM3, ENABLE); // 使能定时器
}
/********************************************************************
* name : timer_init
* description : 定时器3初始化函数
* Input : 无
* Output : 无
* Return : 无
********************************************************************/
void timer_init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); // 使能TIM3时钟
// 定时器TIM3初始化
TIM_TimeBaseStructure.TIM_Period = 999; // 自动重装载值 (999+1)*1us=1ms
TIM_TimeBaseStructure.TIM_Prescaler = 71; // 预分频值 72M/(71+1)=1MHz 1us
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 初始化TIM3
TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE); // 使能TIM3更新中断
// 中断优先级NVIC设置
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; // TIM3中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 响应优先级1级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能IRQ通道
NVIC_Init(&NVIC_InitStructure); // 初始化NVIC寄存器
TIM_Cmd(TIM3, ENABLE); // 使能TIM3定时器
}
/********************************************************************
* name : TIM3_IRQHandler
* description : 定时器3中断服务函数,处理接收队列数据
* Input : 无
* Output : 无
* Return : 无
********************************************************************/
void TIM3_IRQHandler(void)
{
if(TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) // 检查更新中断
{
int result = 1;
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); // 清除中断标志
TIM_Cmd(TIM3, DISABLE); // 禁用定时器
result = queue_not_empty(&rx_queue); // 检查队列是否非空
if(result == 1)
{
recvBuf.len = 0; // 重置接收缓冲区长度
// 循环读取队列中的所有数据
do
{
result = queue_delete(&rx_queue, &recvBuf.data[recvBuf.len]);
if(result == 1)
{
recvBuf.len ++; // 递增缓冲区长度
}
}
while(result); // 直到队列为空
ymodem_recv(&recvBuf); // 处理接收到的数据
}
}
}
串口1打印输出
[14:19:14.298]收←◆erase flash: 0x08008000
[14:19:18.037]收←◆erase flash success
File: APP1.bin, Size: 32676 bytes
Starting update: APP1.bin (32676 bytes)
[14:19:18.661]收←◆[=> ] 3% (1280/32676 bytes)
[14:19:19.295]收←◆[===> ] 7% (2560/32676 bytes)
[14:19:19.916]收←◆[=====> ] 11% (3840/32676 bytes)
[14:19:20.539]收←◆[=======> ] 15% (5120/32676 bytes)
[14:19:21.161]收←◆[=========> ] 19% (6400/32676 bytes)
[14:19:21.788]收←◆[===========> ] 23% (7680/32676 bytes)
[14:19:22.411]收←◆[=============> ] 27% (8960/32676 bytes)
[14:19:23.035]收←◆[===============> ] 31% (10240/32676 bytes)
[14:19:23.656]收←◆[=================> ] 35% (11520/32676 bytes)
[14:19:24.287]收←◆[===================> ] 39% (12800/32676 bytes)
[14:19:24.916]收←◆[=====================> ] 43% (14080/32676 bytes)
[14:19:25.529]收←◆[=======================> ] 47% (15360/32676 bytes)
[14:19:26.160]收←◆[=========================> ] 50% (16640/32676 bytes)
[14:19:26.787]收←◆[===========================> ] 54% (17920/32676 bytes)
[14:19:27.415]收←◆[=============================> ] 58% (19200/32676 bytes)
[14:19:28.033]收←◆[===============================> ] 62% (20480/32676 bytes)
[14:19:28.661]收←◆[=================================> ] 66% (21760/32676 bytes)
[14:19:29.287]收←◆[===================================> ] 70% (23040/32676 bytes)
[14:19:29.913]收←◆[=====================================> ] 74% (24320/32676 bytes)
[14:19:30.532]收←◆[=======================================> ] 78% (25600/32676 bytes)
[14:19:31.164]收←◆[=========================================> ] 82% (26880/32676 bytes)
[14:19:31.785]收←◆[===========================================> ] 86% (28160/32676 bytes)
[14:19:32.415]收←◆[=============================================> ] 90% (29440/32676 bytes)
[14:19:33.037]收←◆[===============================================> ] 94% (30720/32676 bytes)
[14:19:33.657]收←◆[================================================> ] 97% (32000/32676 bytes)
[14:19:34.157]收←◆END
[14:19:34.225]收←◆Update successful! File: APP1.bin, Total: 32768 bytes
update success
system reboot...
[14:19:35.234]收←◆---------- Enter BootLoader ----------
======== flash pration table =========
| name | offset | size |
--------------------------------------
| boot | 0x08000000 | 0x00004000 |
| setting | 0x08004000 | 0x00004000 |
| app | 0x08008000 | 0x00078000 |
======================================
------- Please enter parameter -------
USArt2 Send:'1'-->Start program
USArt2 Send:'2'-->Update program
--------------------------------------
wait start app...(5s)
[14:19:36.281]收←◆wait start app...(4s)
[14:19:37.283]收←◆wait start app...(3s)
[14:19:38.287]收←◆wait start app...(2s)
[13:51:26.920]收←◆---------- Enter BootLoader ----------
======== flash pration table =========
| name | offset | size |
--------------------------------------
| boot | 0x08000000 | 0x00004000 |
| setting | 0x08004000 | 0x00004000 |
| app | 0x08008000 | 0x00078000 |
======================================
------- Please enter parameter -------
USArt2 Send:'1'-->Start program
串口1 显示打印信息
串口2 接受升级指令 和APP.bin数据
APP程序 其重点设置便偏移地址


代码链接下载:https://download.csdn.net/download/qq_36958104/92287760
更多推荐



所有评论(0)