STM32Ymodem协议 串口接收数据的代码
时间: 2023-07-15 20:09:57 浏览: 119
STM32F1升级方案采用ymodem协议,串口的 提供bootloader源码,用户app代码,都是keil工程 提供上
以下是一个基于STM32的Ymodem协议串口接收数据的示例代码:
```c
#include "stm32f10x.h"
#include "stdio.h"
#include "stdbool.h"
#define SOH 0x01
#define STX 0x02
#define EOT 0x04
#define ACK 0x06
#define NAK 0x15
#define CAN 0x18
#define CRC16 0x43
#define PACKET_SIZE 128 // 数据包大小
#define PACKET_1K_SIZE 1024 // 1K数据包大小
#define HEAD_SIZE 3 // 数据包头大小
#define TAIL_SIZE 2 // 数据包尾大小
#define PACKET_DATA_SIZE 128-3 // 每个数据包的数据大小
#define FLASH_APP_ADDRESS 0x08008000 // APP程序的存储器地址
bool Ymodem_Receive(uint8_t *);
uint8_t Ymodem_WaitACK(void);
uint8_t Ymodem_SendPacket(uint8_t *, uint16_t, uint8_t);
uint16_t Ymodem_CalcCRC16(uint8_t *, uint16_t);
void Ymodem_EraseAppArea(void);
void Ymodem_WriteToFlash(uint32_t, uint8_t *, uint16_t);
uint8_t PacketBuffer[PACKET_1K_SIZE];
uint8_t ReceiveBuffer[PACKET_1K_SIZE];
uint8_t TxBuf[PACKET_SIZE+TAIL_SIZE];
uint8_t RxBuf[PACKET_SIZE+HEAD_SIZE+TAIL_SIZE];
int main(void)
{
USART_InitTypeDef USART_InitStructure;
/* 使能DMA时钟 */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
/* 使能USART1时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_AFIO, ENABLE);
/* 将USART1 Tx的GPIO配置为推挽复用模式 */
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* 将USART1 Rx的GPIO配置为浮空输入模式 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
/* USART1初始化设置 */
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &USART_InitStructure);
/* 使能USART1 */
USART_Cmd(USART1, ENABLE);
/* 等待串口稳定 */
int i;
for (i = 0; i < 1000000; i++);
/* 清空Flash */
Ymodem_EraseAppArea();
/* 接收数据 */
Ymodem_Receive(ReceiveBuffer);
while (1);
}
/**
* @brief Ymodem协议接收函数
* @param[in] pBuffer 存储接收数据的缓冲区
* @retval true: 接收成功 false: 接收失败
*/
bool Ymodem_Receive(uint8_t *pBuffer)
{
uint8_t ch;
uint8_t packet_number = 1;
uint32_t packet_length = 0;
uint32_t received_packet_count = 0;
uint16_t crc16 = 0;
uint32_t i;
/* 等待发送方发送数据 */
while (1)
{
ch = Ymodem_WaitACK();
if (ch == 'C')
{
break;
}
else if (ch == NAK)
{
continue;
}
else
{
return false;
}
}
/* 开始接收数据 */
while (1)
{
/* 发送一个ACK,以示准备接收数据 */
USART_SendData(USART1, ACK);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
/* 接收数据包头 */
for (i = 0; i < (PACKET_SIZE+HEAD_SIZE+TAIL_SIZE); i++)
{
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
RxBuf[i] = USART_ReceiveData(USART1);
}
/* 判断数据包类型 */
if (RxBuf[0] == SOH) // 数据包大小为128字节
{
packet_length = PACKET_SIZE;
}
else if (RxBuf[0] == STX) // 数据包大小为1024字节
{
packet_length = PACKET_1K_SIZE;
}
else if (RxBuf[0] == EOT) // 数据接收完成
{
/* 发送一个ACK,表示数据接收完成 */
USART_SendData(USART1, ACK);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
/* 等待发送方发送下一批数据 */
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
ch = USART_ReceiveData(USART1);
if (ch == EOT)
{
/* 发送最后一个ACK */
USART_SendData(USART1, ACK);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return true;
}
else
{
return false;
}
}
else // 其他情况
{
return false;
}
/* 判断数据包序号 */
if (RxBuf[1] == packet_number && RxBuf[2] == (255 - packet_number))
{
packet_number++;
/* 计算CRC校验值 */
crc16 = Ymodem_CalcCRC16(&RxBuf[HEAD_SIZE], packet_length);
/* 校验CRC校验值 */
if (crc16 == ((RxBuf[PACKET_SIZE+HEAD_SIZE]<<8)|RxBuf[PACKET_SIZE+HEAD_SIZE+1]))
{
/* 将数据保存到缓冲区中 */
memcpy(&pBuffer[received_packet_count * packet_length], &RxBuf[HEAD_SIZE], packet_length);
received_packet_count++;
/* 发送ACK,以示数据接收成功 */
USART_SendData(USART1, ACK);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
else
{
/* 发送NAK,表示数据接收失败 */
USART_SendData(USART1, NAK);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
}
else
{
/* 发送NAK,表示数据接收失败 */
USART_SendData(USART1, NAK);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
}
}
/**
* @brief 等待发送方发送ACK或C
* @retval 发送方发送的字符
*/
uint8_t Ymodem_WaitACK(void)
{
uint8_t ch;
while (1)
{
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
ch = USART_ReceiveData(USART1);
if (ch == ACK || ch == 'C')
{
return ch;
}
}
}
/**
* @brief 发送数据包
* @param[in] pBuffer 存储数据的缓冲区
* @param[in] packet_length 数据包大小
* @param[in] packet_number 数据包序号
* @retval 发送结果
*/
uint8_t Ymodem_SendPacket(uint8_t *pBuffer, uint16_t packet_length, uint8_t packet_number)
{
uint16_t crc16 = 0;
uint16_t i;
/* 填充数据包头 */
TxBuf[0] = SOH;
TxBuf[1] = packet_number;
TxBuf[2] = (255 - packet_number);
/* 填充数据 */
memcpy(&TxBuf[HEAD_SIZE], pBuffer, packet_length);
/* 计算CRC校验值 */
crc16 = Ymodem_CalcCRC16(&TxBuf[HEAD_SIZE], packet_length);
/* 填充CRC校验值 */
TxBuf[PACKET_SIZE+HEAD_SIZE] = (crc16 >> 8) & 0xFF;
TxBuf[PACKET_SIZE+HEAD_SIZE+1] = crc16 & 0xFF;
/* 发送数据包 */
for (i = 0; i < (PACKET_SIZE+TAIL_SIZE); i++)
{
USART_SendData(USART1, TxBuf[i]);
while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}
/* 等待接收ACK */
while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
return USART_ReceiveData(USART1);
}
/**
* @brief 计算CRC16校验值
* @param[in] pBuffer 存储数据的缓冲区
* @param[in] length 数据长度
* @retval CRC16校验值
*/
uint16_t Ymodem_CalcCRC16(uint8_t *pBuffer, uint16_t length)
{
uint16_t crc16 = 0;
uint16_t i, j;
for (i = 0; i < length; i++)
{
crc16 ^= (uint16_t)pBuffer[i] << 8;
for (j = 0; j < 8; j++)
{
if (crc16 & 0x8000)
{
crc16 = (crc16 << 1) ^ CRC16;
}
else
{
crc16 = crc16 << 1;
}
}
}
return crc16;
}
/**
* @brief 擦除APP程序区域
*/
void Ymodem_EraseAppArea(void)
{
FLASH_Unlock(); // 解锁Flash
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); // 清除错误标志
FLASH_ErasePage(FLASH_APP_ADDRESS); // 擦除整个APP程序区域
FLASH_Lock(); // 上锁Flash
}
/**
* @brief 将数据写入Flash
* @param[in] addr Flash地址
* @param[in] pBuffer 存储数据的缓冲区
* @param[in] length 数据长度
*/
void Ymodem_WriteToFlash(uint32_t addr, uint8_t *pBuffer, uint16_t length)
{
uint32_t i;
uint16_t *pDst = (uint16_t *)addr;
uint16_t *pSrc = (uint16_t *)pBuffer;
FLASH_Unlock(); // 解锁Flash
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR); // 清除错误标志
for (i = 0; i < length/2; i++)
{
FLASH_ProgramHalfWord((uint32_t)pDst, *pSrc); // 写入数据
pDst++;
pSrc++;
}
FLASH_Lock(); // 上锁Flash
}
```
注意:以上代码仅供参考,实际使用时需要根据具体情况进行修改。
阅读全文