本帖最后由 zero949079783 于 2024-7-14 10:27 编辑
基于STM32(串口+DMA+空闲中断+环形缓冲区)实现 YMODEM协议IAP在线烧写程序
BootLoader 扇区: 0x08000000 - 0x08004000 共16K大小
生成BIN: fromelf.exe --bin -o "$L@L.bin" "#L
上位机软件:SecureCRT
Ymodem_STM32F1:
链接:https://pan.baidu.com/s/1SO34uS3flsdE8MdL4HHU0g?pwd=802v
提取码:802v
Ymodem_STM32F4:
链接:https://pan.baidu.com/s/13Kh6QzgNosYnYE5fJ-TB4Q?pwd=u5tj
提取码:u5tj
https://gitee.com/csx949089783/ymodem_-stm32-f4.git
YMODED接口函数:
#include "ymodem.h"
#include "crc.h"
#include "usart.h"
#include "inner_flash.h"
#include "delay.h"
#include "key.h"
#define YMODEM_TIME 10
typedef void (*pFunction)(void);
pFunction Jump_To_Application;
typedef struct{
uint8_t OTA_FLAG : 1;
uint8_t YMODE_FLAG : 1;
uint8_t YMODE_Frist_FLAG :1;
uint8_t YMODE_Frist_Pack_FLAG: 1;
uint8_t YMODE_EOH_STX_DATALEN_FLAG :1;
uint8_t fileName[256]; //文件名
uint8_t UpdataBuffer[STM32_PAGE_SIZE]; //接收数据缓冲区
uint16_t time; //OTA退出时间
uint16_t count;
uint32_t num; //接收数据包数
uint32_t STXnum; //接收数据包数
uint32_t crc; //CRC校验
uint32_t remanider; //数据余数
uint32_t packnum; //数据总包数
uint32_t remaniderpacknum; //数据余数总包数(1024时)
uint32_t fileSize; //文件大小
uint32_t JumpAddress; //APP跳转地址
}YMODEM_T ;
YMODEM_T ymodem_t;
void Ymodem_Init(void)
{
memset(&ymodem_t,0,sizeof(ymodem_t));
ymodem_t.time = YMODEM_TIME;
}
static void BootLoader_To_App(void)
{
Delay_ms(1);
printf("\r\n");
printf("BootLoader_To_App.....\r\n");
Delay_ms(1);
FLASH_Lock();
SysTick->CTRL &= SysTick_CTRL_TICKINT_Msk; //关滴答定时器
ymodem_t.JumpAddress = *(__IO uint32_t*) (APPLICATION_ADDRESS + 4);
/* Jump to user application */
Jump_To_Application = (pFunction) ymodem_t.JumpAddress;
/* Initialize user application's Stack Pointer */
__set_MSP(*(__IO uint32_t*) APPLICATION_ADDRESS);
Jump_To_Application();
}
static void BootLoader_SendByte(uint8_t data)
{
Usartx_SendArray(USART1,&data,1);
}
/*************************************************************************
* 函 数 名: BootLoader_EraseFlash
* 功能说明: Flash扇区擦除
* 形 参:startSectorAddr:起始扇区地址 numSector:扇区个数
* 返 回 值: 无
**************************************************************************/
void BootLoader_EraseFlash(void)
{
STM32_EraseFlash(STM32_A_SADDR,11);
}
/*************************************************************************
* 函 数 名: BootLoader_WriteFlash
* 功能说明: 数据写入
* 形 参:startAddr:起始地址 writeData:写入数据 numByte:写入字节数
* 返 回 值: 无
**************************************************************************/
static void BootLoader_WriteFlash(uint32_t startAddr, uint32_t* writeData, uint32_t numByte)
{
STM32_WriteFlash(startAddr,writeData,numByte);
}
void BootLoader_Meun(void)
{
static uint16_t times = 0;
times ++;
if(ymodem_t.OTA_FLAG == YMODEM_DISABLE && times%1000 == 0)
{
ymodem_t.time --;
printf(" \r\n %ds后退出BootLoader\r\n ",ymodem_t.time);
if(ymodem_t.time <= 0)
{
BootLoader_To_App();
}
}
else if(ymodem_t.YMODE_FLAG == YMODEMD_ENABLE && times %500 == 0)
{
ymodem_t.time = YMODEM_TIME;
BootLoader_SendByte(CA); //发送握手
}
//按键双击进入OTA
if( get_Key_Val() == 0x51 && ymodem_t.YMODE_FLAG == YMODEM_DISABLE && ymodem_t.OTA_FLAG == YMODEM_DISABLE)
{
printf("EraseFlash......\r\n");
BootLoader_EraseFlash();
printf("EraseFlash successfully \r\n");
ymodem_t.OTA_FLAG = 1;
ymodem_t.YMODE_FLAG = 1;
}
}
static void BootLoader_Com(uint8_t *data)
{
if(memcmp(data,"1",1)==0 ) //当接收到上位机或者双击时,进入OTA
{
printf("EraseFlash......\r\n");
BootLoader_EraseFlash();
printf("EraseFlash successfully \r\n");
ymodem_t.OTA_FLAG = 1;
ymodem_t.YMODE_FLAG = 1;
}
}
/***************************************************************************
Ymodem数据帧:
帧头 包号 包号反码 信息块 校验
SOH/STX PN XPN DATA 128/1024 CRC16(CRCH CRCL)
1:对于SOH帧,若余下数据小于128字节,则以0x1A填充,该帧长度仍为133字节。
2:对于STX帧需考虑几种情况
帧长度:
1:以SOH(0x01)开始的数据包,信息块是128字节,该类型帧总长度为133字节
2:以STX(0x02)开始的数据包,信息块是1024字节,该类型帧总长度为1029字节
a:●余下数据等于1024字节,以1029长度帧发送;
b:●余下数据小于1024字节,但大于128字节,以1029字节帧长度发送,无效数据以0x1A填充
c:●余下数据等于128字节,以133字节帧长度发送
d:●余下数据小于128字节,以133字节帧长度发送,无效数据以0x1A填充
包序号:
数据包序号只有1字节,因此计算范围是0~255;对于数据包大于255的,序号归零重复计算。
校验:
Ymodem采用的是CRC16校验算法,校验值为2字节,传输时CRC高八位在前,低八位在后;CRC计算数据为信息块数据,不包含帧头、包号、包号反码
起始帧:
Ymodem起始帧并不直接传输文件内容,而是先将文件名和文件大小置于数据帧中传输;起始帧是以SOH 133字节长度帧传输,格式如下
帧头 包号 包号反码 文名件称 文件大小 填充区 校验
SOH/STX PN XPN fileName+0x00 fileSize+0x00 0x00 CRC16(CRCH CRCL)
Ymodem结束帧
帧头 包号 包号反码 信息块 校验
SOH PN XPN 0x00 CRC16(CRCH CRCL)
文件传输过程
文件的传输过程,以具体的例子说明。把foo.c,大小为4196Byte(16进制为0x1064)的文件作为传输的对象,则它的传输过程如下:
发送端----------------------------------------------------------------接收端
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C
SOH 00 FF “foo.c” "1064’’ NUL[118] CRC CRC >>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C
STX 01 FE data[1024] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
STX 02 FD data[1024] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
STX 03 FC data[1024] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
STX 04 FB data[1024] CRC CRC>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
SOH 05 FA data[100] 1A[28] CRC CRC>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
EOT >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< NAK
EOT>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< C
SOH 00 FF NUL[128] CRC CRC >>>>>>>>>>>>>>>>>>>>>>>
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< ACK
*******************************************************************************************/
void Ymodem_Receive(uint8_t *data,uint16_t datalen)
{
char *fileSizebuff;
char fileSize[50]; //文件大小
uint8_t i = 0,j=0;
if(ymodem_t.OTA_FLAG == YMODEM_DISABLE)
{
BootLoader_Com(data);
}
//当是第0包数据时,和最1后才能进入,
if((data[0] == SOH && data[1] == 0x00 && data[2] == 0xff && ymodem_t.YMODE_Frist_Pack_FLAG == YMODEM_DISABLE && datalen == YMODE_SOH_PACKLEN) || ymodem_t.YMODE_Frist_FLAG == YMODEMD_ENABLE ) //Ymodem起始帧: 113
{
ymodem_t.crc = CRC16_XMODEM(&data[3],YMODE_SOH_DATALEN);
if(ymodem_t.crc == ((data[YMODE_SOH_PACKLEN - 2]<<8) |data[YMODE_SOH_PACKLEN - 1])) //CRC
{
if(ymodem_t.YMODE_Frist_FLAG == YMODEM_DISABLE) //第1次
{
ymodem_t.YMODE_FLAG = YMODEM_DISABLE;
ymodem_t.YMODE_Frist_Pack_FLAG = YMODEMD_ENABLE ; //第1包接收标志位置使能,说时第一包接好
for(i=0;data[3+i] !='\0';i++) //读取文件名
{
ymodem_t.fileName[i] = data[3+i];
}
i++;
for(j=0;data[3+i+j] != '\0';j++) //读取文件大小
{
fileSize[j] = data[3+i+j];
}
ymodem_t.fileSize = strtol(fileSize,&fileSizebuff,10);
ymodem_t.remanider = ymodem_t.fileSize % YMODE_SOH_DATALEN;
ymodem_t.packnum = ymodem_t.fileSize / YMODE_SOH_DATALEN;
if(ymodem_t.remanider > 0)
ymodem_t.packnum = ymodem_t.packnum+1;
BootLoader_SendByte(ACK); //正确发送ACK
BootLoader_SendByte(CA);
}
else if(ymodem_t.YMODE_Frist_FLAG == YMODEMD_ENABLE)
{
BootLoader_SendByte(ACK); //正确发送ACK
BootLoader_SendByte(CA); //正确发送ACK
Delay_ms(20);
printf("\r\n");
printf("fileName :%s \r\n",ymodem_t.fileName);
printf("fileSize :%d Byte\r\n",ymodem_t.fileSize);
printf("File uploaded successfully \r\n");
printf("\r\n");
Ymodem_Init();
BootLoader_To_App();
}
}
else{
BootLoader_SendByte(NAK); //错误发送NAK
}
}
else if((data[0] == STX && data[1] == 0x00 && data[2] == 0xff && ymodem_t.YMODE_Frist_Pack_FLAG == YMODEM_DISABLE && datalen == YMODE_STX_PACKLEN)) ////Ymodem起始帧: 1024
{
ymodem_t.crc = CRC16_XMODEM(&data[3],YMODE_STX_DATALEN);
if(ymodem_t.crc == ((data[YMODE_STX_PACKLEN - 2]<<8) |data[YMODE_STX_PACKLEN - 1])) //CRC
{
if(ymodem_t.YMODE_Frist_FLAG == YMODEM_DISABLE) //第1次
{
ymodem_t.YMODE_FLAG = YMODEM_DISABLE;
ymodem_t.YMODE_Frist_Pack_FLAG = YMODEMD_ENABLE; //第1包接收标志位置使能,说时第一包接好
for(i=0;data[3+i] !='\0';i++) //读取文件名
{
ymodem_t.fileName[i] = data[3+i];
}
i++;
for(j=0;data[3+i+j] != '\0';j++) //读取文件大小
{
fileSize[j] = data[3+i+j];
}
ymodem_t.fileSize = strtol(fileSize,&fileSizebuff,10);
//数据1024时,数据是1024时,按1024接收,当小于1024时,以每包128个字传接收,直到所有数据收完
ymodem_t.remanider = ymodem_t.fileSize % YMODE_STX_DATALEN; //余下字节个数
ymodem_t.remaniderpacknum = ymodem_t.remanider / YMODE_SOH_DATALEN; //余下字节以128接收的包数据
ymodem_t.packnum = (ymodem_t.fileSize / YMODE_STX_DATALEN) ; //1024总包数
if(ymodem_t.remanider > 0)
{
ymodem_t.remaniderpacknum = ymodem_t.remaniderpacknum +1;
ymodem_t.packnum = ymodem_t.packnum+1;
}
ymodem_t.YMODE_EOH_STX_DATALEN_FLAG = YMODEMD_ENABLE;
BootLoader_SendByte(ACK); //正确发送ACK
BootLoader_SendByte(CA);
}
}
else{
BootLoader_SendByte(NAK); //错误发送NAK
}
}
else if(data[0] == SOH && datalen == YMODE_SOH_PACKLEN) //Ymodem数据帧:当数据以大小是128字节时
{
ymodem_t.crc = CRC16_XMODEM(&data[3],YMODE_SOH_DATALEN);
if(ymodem_t.crc == ((data[YMODE_SOH_PACKLEN - 2]<<8) |data[YMODE_SOH_PACKLEN - 1])) //CRC
{
ymodem_t.num ++; //已接收的数据包数量+1
//将本次接收的数据,暂存到ymodem_t.UpdataBuffer缓冲
memcpy(&ymodem_t.UpdataBuffer[((ymodem_t.num - 1) % (STM32_PAGE_SIZE / YMODE_SOH_DATALEN)) * YMODE_SOH_DATALEN],&data[3], YMODE_SOH_DATALEN); //将本次接收的数据,暂存到UpDataA.Updatabuff缓冲区
if(ymodem_t.YMODE_EOH_STX_DATALEN_FLAG == YMODEM_DISABLE) //当是128每包数据时
{
if(((ymodem_t.num % (STM32_PAGE_SIZE/YMODE_SOH_DATALEN)) == 0) &&( ymodem_t.packnum!=ymodem_t.num )) //每满一个扇区写入Flash
{
//写入到单片机A区相应的扇区
BootLoader_WriteFlash(STM32_A_SADDR + (((ymodem_t.num / (STM32_PAGE_SIZE / YMODE_SOH_DATALEN)) -1) * STM32_PAGE_SIZE), (uint32_t *)ymodem_t.UpdataBuffer,STM32_PAGE_SIZE); //写入到单片机A区相应的扇区
//备份FLASG写入
}
else if(((ymodem_t.num % (STM32_PAGE_SIZE/YMODE_SOH_DATALEN)) != 0) &&(ymodem_t.packnum==ymodem_t.num))//最后一包数,判断是否还有不满1扇区1024字节的数据,如果有进入if,把剩余的小尾巴写入
{
//写入到单片机A区相应的扇区
BootLoader_WriteFlash(STM32_A_SADDR + (((ymodem_t.num / (STM32_PAGE_SIZE / YMODE_SOH_DATALEN))) * STM32_PAGE_SIZE), (uint32_t *)ymodem_t.UpdataBuffer,(ymodem_t.num % (STM32_PAGE_SIZE / YMODE_SOH_DATALEN)) * YMODE_SOH_DATALEN); //写入到单片机A区相应的扇区
//备份FLASG写入
}
}
else {
if(((ymodem_t.num % (STM32_PAGE_SIZE/YMODE_SOH_DATALEN)) == 0) &&( ymodem_t.remaniderpacknum!=ymodem_t.num )) //每满一个扇区写入Flash
{
//写入到单片机A区相应的扇区
BootLoader_WriteFlash(STM32_A_SADDR + (ymodem_t.STXnum * STM32_PAGE_SIZE)+(((ymodem_t.num / (STM32_PAGE_SIZE / YMODE_SOH_DATALEN)) -1) * STM32_PAGE_SIZE), (uint32_t *)ymodem_t.UpdataBuffer,STM32_PAGE_SIZE); //写入到单片机A区相应的扇区
//备份FLASG写入
}
else if(((ymodem_t.num % (STM32_PAGE_SIZE/YMODE_SOH_DATALEN)) != 0) &&(ymodem_t.remaniderpacknum==ymodem_t.num))//最后一包数,判断是否还有不满1扇区1024字节的数据,如果有进入if,把剩余的小尾巴写入
{
//写入到单片机A区相应的扇区
BootLoader_WriteFlash(STM32_A_SADDR + (ymodem_t.STXnum * STM32_PAGE_SIZE) + (((ymodem_t.num / (STM32_PAGE_SIZE / YMODE_SOH_DATALEN))) * STM32_PAGE_SIZE), (uint32_t *)ymodem_t.UpdataBuffer,(ymodem_t.num % (STM32_PAGE_SIZE / YMODE_SOH_DATALEN)) * YMODE_SOH_DATALEN); //写入到单片机A区相应的扇区
//备份FLASG写入
}
}
BootLoader_SendByte(ACK); //正确发送ACK
}
else{
BootLoader_SendByte(NAK); //错误发送NAK
}
}
else if(data[0] == STX && datalen == YMODE_STX_PACKLEN) //Ymodem数据帧:当数据是1029字节时
{
ymodem_t.crc = CRC16_XMODEM(&data[3],YMODE_STX_DATALEN);
if(ymodem_t.crc == ((data[YMODE_STX_PACKLEN - 2]<<8) |data[YMODE_STX_PACKLEN - 1])) //CRC
{
ymodem_t.STXnum++;
memcpy(&ymodem_t.UpdataBuffer[((ymodem_t.STXnum- 1) % (STM32_PAGE_SIZE / YMODE_STX_DATALEN)) * YMODE_STX_DATALEN],&data[3], YMODE_STX_DATALEN); //将本次接收的数据,暂存到UpDataA.Updatabuff缓冲区
if(((ymodem_t.STXnum % (STM32_PAGE_SIZE/YMODE_STX_DATALEN)) == 0) &&( ymodem_t.packnum!=ymodem_t.STXnum)) //每满一个扇区写入Flash
{
//写入到单片机A区相应的扇区
BootLoader_WriteFlash(STM32_A_SADDR + (((ymodem_t.STXnum/ (STM32_PAGE_SIZE / YMODE_STX_DATALEN)) -1) * STM32_PAGE_SIZE), (uint32_t *)ymodem_t.UpdataBuffer,STM32_PAGE_SIZE); //写入到单片机A区相应的扇区
//备份FLASG写入
}
BootLoader_SendByte(ACK); //正确发送ACK
}
else{
BootLoader_SendByte(NAK); //错误发送NAK
}
}
else if(data[0] == EOT && datalen == YMODE_EOT_DATALEN) //当接收到EOT
{
if(ymodem_t.count == 0)
{
ymodem_t.count ++;
BootLoader_SendByte(NAK); //正确发送NAK
}
else if(ymodem_t.count == 1)
{
ymodem_t.YMODE_Frist_FLAG = YMODEMD_ENABLE;
ymodem_t.count = 0;
BootLoader_SendByte(ACK); //正确发送ACK
BootLoader_SendByte(CA); //正确发送C
}
}
else if(data[0] == CAN && data[1] == CAN && data[2] == CAN && data[3] == CAN && data[4] == CAN ) //取消传输命令,连续发送5个该命令
{
ymodem_t.OTA_FLAG = YMODEM_DISABLE;
Ymodem_Init();
}
}
#ifndef __YMODEM_H
#define __YMODEM_H
#include "stm32f4xx.h"
#define STM32_FLASH_SADDR 0x08000000 //FLASH扇区起始地址
#define STM32_PAGE_SIZE 1024 //FLASH扇区大小
#define STM32_PAGE_NUM 1024 //FLASH扇区总个数
#define STM32_B_PAGE_NUM 16 //BootLoader 扇区个数
#define STM32_A_PAGE_NUM (STM32_PAGE_NUM - STM32_B_PAGE_NUM) //APP扇区个数
#define STM32_A_START_PAGE (STM32_B_PAGE_NUM) //APP扇区起始数
#define STM32_A_SADDR (STM32_FLASH_SADDR + STM32_A_START_PAGE * STM32_PAGE_SIZE) //APP扇区起始地址
#define APPLICATION_ADDRESS (STM32_A_SADDR) //APP跳转地址
#define YMODE_SOH_PACKLEN 133 //SOH数据包长度
#define YMODE_STX_PACKLEN 1029 //SOH数据包长度
#define YMODE_SOH_DATALEN 128 //SOH有效数据长度
#define YMODE_STX_DATALEN 1024 //SOH有效数据长度
#define YMODE_EOT_DATALEN 1
typedef enum {YMODEM_DISABLE = 0, YMODEMD_ENABLE = !YMODEM_DISABLE} YmodemState;
typedef enum{
SOH = 0X01,//133字节长度帧
STX = 0X02,//1024字节长度帧
EOT = 0X04,//文件传输结束命令
ACK = 0X06,//接收正确应答命令
NAK = 0X15,//重传当前数据包请求命令
CAN = 0X18,//取消传输命令,连续发送5个该命令
CA = 0X43,//字符C ,发送握手
}YMODEM_COM;
void Ymodem_Init(void);
void BootLoader_Meun(void);
void Ymodem_Receive(uint8_t *data,uint16_t datalen);
#endif
usart.c#include "usart.h"
#include "delay.h"
uint8_t Usart1_RxBuffer[UARTx_RX_SIZE] = {0}; //串口1接收缓冲区
uint8_t Usart1_TxBuffer[UARTx_TX_SIZE] = {0}; //串口1发送缓冲区
Usartx_Control_Block usart1; //串口1控制结构体
void (*usartTakeFunCb)(uint8_t *data,uint16_t datalen);
void usart1TakeCb(void(*pFunc)(uint8_t *data,uint16_t datalen))
{
usartTakeFunCb = pFunc;
}
void usart1_take(void)
{
if(usart1.RxOutPtr != usart1.RxInPtr)
{
usartTakeFunCb(usart1.RxOutPtr->start,(usart1.RxOutPtr->end - usart1.RxOutPtr->start+1));
usart1.RxOutPtr++;
if(usart1.RxOutPtr == usart1.RxEndPtr)
{
usart1.RxOutPtr = &usart1.RxLocation[0];
}
}
if((usart1.TxOutPtr != usart1.TxInPtr) &&( usart1.TxState == 0))
{
usart1.TxState = 1;
DMA_Cmd(DMA2_Stream7,ENABLE);
while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE); //确保DMA可以被设置
DMA_SetCurrDataCounter(DMA2_Stream7, UARTx_TX_MAX);
usart1.TxOutPtr++;
if(usart1.TxOutPtr == usart1.TxEndPtr)
{
usart1.TxOutPtr = &usart1.TxLocation[0];
}
}
}
void USART1_Init(uint32_t bandRate)
{
Usart1_PtrInit();
USART1_GPIO_Init();
Delay_ms(1);
USART1_Config_Init(bandRate);
Delay_ms(1);
USART1_DMA_Init();
}
/*************************************************************************
* 函 数 名: Uart1_RX_PtrInit
* 功能说明: 串口1控制结构体各个指针初始化
* 形 参:无
* 返 回 值: 无
**************************************************************************/
void Usart1_PtrInit(void)
{
usart1.RxInPtr = &usart1.RxLocation[0];
usart1.RxOutPtr = &usart1.RxLocation[0];
usart1.RxEndPtr = &usart1.RxLocation[9];
usart1.RxCounter = 0;
usart1.RxInPtr->start = Usart1_RxBuffer;
usart1.TxInPtr = &usart1.TxLocation[0];
usart1.TxOutPtr = &usart1.TxLocation[0];
usart1.TxEndPtr = &usart1.TxLocation[9];
usart1.TxCounter = 0;
usart1.TxInPtr->start = Usart1_TxBuffer;
memset(Usart1_RxBuffer,0,UARTx_RX_SIZE);
memset(Usart1_TxBuffer,0,UARTx_RX_SIZE);
}
/*************************************************************************
* 函 数 名: Usart1_GPIO_Config
* 功能说明: 串口1 GPIO初始化
* 形 参:无
* 返 回 值: 无
**************************************************************************/
void USART1_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOB, ENABLE);
/* 配置Tx引脚为复用功能 */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7 ;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
/*串口上电发送0x00的解决*/
/* 连接 PXx 到 USARTx_Tx*/
GPIO_PinAFConfig(GPIOB,GPIO_PinSource6, GPIO_AF_USART1);
/* 连接 PXx 到 USARTx__Rx*/
GPIO_PinAFConfig(GPIOB,GPIO_PinSource7,GPIO_AF_USART1);
}
/*************************************************************************
* 函 数 名: Usart1_Config
* 功能说明: 串口1初始化
* 形 参:bandRate:波特率
* 返 回 值: 无
**************************************************************************/
void USART1_Config_Init(uint32_t bandRate)
{
/* 使能 UART 时钟 */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
usart1.usart.USART_BaudRate = bandRate;
usart1.usart.USART_WordLength = USART_WordLength_8b; //8bit数据位
usart1.usart.USART_StopBits = USART_StopBits_1; //1bit停止位
usart1.usart.USART_Parity = USART_Parity_No; //无校验
usart1.usart.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
usart1.usart.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART1, &usart1.usart);
// USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //开启空闲中断
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //开启空闲中断
// USART_ITConfig(USART1,USART_IT_TC,ENABLE); //开启发送完成中断
USART_Cmd(USART1, ENABLE); //使能串口
USART_DMACmd(USART1,USART_DMAReq_Rx, ENABLE); //使能RX DMA
USART_DMACmd(USART1,USART_DMAReq_Tx, ENABLE); //使能RX DMA
}
/*************************************************************************
* 函 数 名: Uart1_DMA_Init
* 功能说明: 串口1 DMA初始化
* 形 参:无
* 返 回 值: 无
**************************************************************************/
void USART1_DMA_Init(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2,ENABLE);
/* 复位初始化DMA数据流 */
DMA_DeInit(DMA2_Stream5);
/* 确保DMA数据流复位完成 */
while (DMA_GetCmdStatus(DMA2_Stream5) != DISABLE) {
}
/*usart1 rx对应dma2,通道4,数据流2*/
usart1.dmarx.DMA_Channel = DMA_Channel_4;
/*设置DMA源:串口数据寄存器地址*/
usart1.dmarx.DMA_PeripheralBaseAddr = (USART1_BASE + 0x04);
/*内存地址(要传输的变量的指针)*/
usart1.dmarx.DMA_Memory0BaseAddr = (uint32_t)Usart1_RxBuffer;
/*方向:从内存到外设*/
usart1.dmarx.DMA_DIR = DMA_DIR_PeripheralToMemory;
/*传输大小DMA_BufferSize=RECEIVEBUFF_SIZE*/
usart1.dmarx.DMA_BufferSize = UARTx_RX_MAX+1;
/*外设地址不增*/
usart1.dmarx.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
/*内存地址自增*/
usart1.dmarx.DMA_MemoryInc = DMA_MemoryInc_Enable;
/*外设数据单位*/
usart1.dmarx.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
/*内存数据单位 8bit*/
usart1.dmarx.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
/*DMA模式:不断循环*/
usart1.dmarx.DMA_Mode = DMA_Mode_Circular;
/*优先级:中*/
usart1.dmarx.DMA_Priority = DMA_Priority_Medium;
/*禁用FIFO*/
usart1.dmarx.DMA_FIFOMode = DMA_FIFOMode_Disable;
usart1.dmarx.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
/*存储器突发传输 16个节拍*/
usart1.dmarx.DMA_MemoryBurst = DMA_MemoryBurst_Single;
/*外设突发传输 1个节拍*/
usart1.dmarx.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
/*配置DMA2的数据流2*/
DMA_Init(DMA2_Stream5, &usart1.dmarx);
// /*使能DMA*/
DMA_Cmd(DMA2_Stream5, ENABLE);
/* 等待DMA数据流有效*/
while(DMA_GetCmdStatus(DMA2_Stream5) != ENABLE)
{
}
/* 复位初始化DMA数据流 */
DMA_DeInit(DMA2_Stream7);
/* 确保DMA数据流复位完成 */
while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE) {
}
/*usart1 tx对应dma2,通道4,数据流7*/
usart1.dmatx.DMA_Channel = DMA_Channel_4;
/*设置DMA源:串口数据寄存器地址*/
usart1.dmatx.DMA_PeripheralBaseAddr = (USART1_BASE+0x04);
/*内存地址(要传输的变量的指针)*/
usart1.dmatx.DMA_Memory0BaseAddr = (uint32_t)Usart1_TxBuffer;
/*方向:从内存到外设*/
usart1.dmatx.DMA_DIR = DMA_DIR_MemoryToPeripheral;
/*传输大小DMA_BufferSize=SENDBUFF_SIZE*/
usart1.dmatx.DMA_BufferSize = UARTx_TX_MAX;
/*外设地址不增*/
usart1.dmatx.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
/*内存地址自增*/
usart1.dmatx.DMA_MemoryInc = DMA_MemoryInc_Enable;
/*外设数据单位*/
usart1.dmatx.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
/*内存数据单位 8bit*/
usart1.dmatx.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
/*DMA模式:不断循环*/
usart1.dmatx.DMA_Mode = DMA_Mode_Circular;
/*优先级:中*/
usart1.dmatx.DMA_Priority = DMA_Priority_Medium;
/*禁用FIFO*/
usart1.dmatx.DMA_FIFOMode = DMA_FIFOMode_Disable;
usart1.dmatx.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
/*存储器突发传输 16个节拍*/
usart1.dmatx.DMA_MemoryBurst = DMA_MemoryBurst_Single;
/*外设突发传输 1个节拍*/
usart1.dmatx.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
/*配置DMA2的数据流7*/
DMA_Init(DMA2_Stream7, &usart1.dmatx);
//
// /*使能DMA*/
// DMA_Cmd(DMA2_Stream7, ENABLE);
//
// /* 等待DMA数据流有效*/
// while(DMA_GetCmdStatus(DMA2_Stream7) != ENABLE)
// {
// }
DMA_ITConfig(DMA2_Stream5,DMA_IT_TC,ENABLE);
DMA_ITConfig(DMA2_Stream7,DMA_IT_TC,ENABLE);
// NVIC
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream7_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream5_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void Usart1_Txdata(uint8_t *tdata,uint16_t datalen)
{
if(datalen == 0)
{
DMA_SetCurrDataCounter(DMA2_Stream7, UARTx_TX_MAX);
return;
}
if((UARTx_TX_SIZE - usart1.TxCounter) >= datalen) //发送空间大于等于发数据
{
usart1.TxInPtr ->start = &Usart1_TxBuffer[usart1.TxCounter];
}else
{
usart1.TxCounter = 0; //发送空间小于发送数据,清0
usart1.TxInPtr->start = Usart1_TxBuffer;
}
while (DMA_GetCmdStatus(DMA2_Stream7) != DISABLE); //确保DMA可以被设置
DMA_SetCurrDataCounter(DMA2_Stream7,datalen); //设置数据传输长度
memcpy(usart1.TxInPtr ->start,tdata,datalen); //数据拷贝
usart1.TxCounter += datalen; //统计每次发送量
usart1.TxInPtr ->end = &Usart1_TxBuffer[usart1.TxCounter-1];//标记end位,为下次作准备
DMA2_Stream7->M0AR = (uint32_t)usart1.TxInPtr->start;
usart1.TxInPtr++;
if(usart1.TxInPtr == usart1.TxEndPtr){
usart1.TxInPtr = &usart1.TxLocation[0];
}
}
void Usartx_SendArray( USART_TypeDef * pUSARTx, uint8_t *data, uint16_t datalen)
{
uint16_t i;
for(i=0;i<datalen;i++)
{
/* 发送一个字节数据到USART */
pUSARTx->DR = data[i];
while(!(pUSARTx->SR & USART_FLAG_TXE) );
}
while(!USART_GetFlagStatus(pUSARTx,USART_FLAG_TC));
}
void Usartx_SendStr( USART_TypeDef * pUSARTx, const char *SendStr)
{
while(*SendStr != '\0')
{
/* 发送一个字节数据到USART */
// pUSARTx->DR = *SendStr;
USART_SendData(pUSARTx,*SendStr);
//while(!USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE));
while(!(pUSARTx->SR & USART_FLAG_TXE) );
SendStr++;
}
while(!USART_GetFlagStatus(pUSARTx,USART_FLAG_TC));
}
//uint8_t tempbuff[256];
//void u1_printf(char *fmt, ...)
//{
// uint16_t i=0;
// va_list ap;
// va_start(ap,fmt);
//
// vsprintf((char *)tempbuff,fmt,ap);
//
// va_end(ap);
// DMA_Cmd(DMA2_Stream7,DISABLE);
// for(i=0;i<strlen((char *)tempbuff);i++)
// {
// while(!USART_GetFlagStatus(USART1,USART_FLAG_TXE));
// USART1->DR = tempbuff[i];
// }
// //while(!USART_GetFlagStatus(USART1,USART_FLAG_TC));
//}
void USART1_IRQHandler(void)
{
//发送中断
if(USART_GetITStatus(USART1,USART_IT_TC) != RESET)
{
USART_ClearITPendingBit(USART1,USART_IT_TC);
//usart1.TxState = 0;
}
//空闲中断
if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
{
USART1->SR; //清中断
USART1->DR; //清中断
usart1.RxCounter +=((UARTx_RX_MAX+1)- DMA_GetCurrDataCounter(DMA2_Stream5));
usart1.RxInPtr->end = &Usart1_RxBuffer[usart1.RxCounter - 1];
usart1.RxInPtr++;
if(usart1.RxInPtr == usart1.RxEndPtr)
{
usart1.RxInPtr = &usart1.RxLocation[0];
}
if((UARTx_RX_SIZE - usart1.RxCounter) >= UARTx_RX_MAX){
usart1.RxInPtr->start = &Usart1_RxBuffer[usart1.RxCounter]; //标记接位置
}else{
usart1.RxInPtr->start = Usart1_RxBuffer;
usart1.RxCounter = 0;
}
DMA_Cmd(DMA2_Stream5, DISABLE); //关闭DMA
DMA_SetCurrDataCounter(DMA2_Stream5, UARTx_RX_MAX+1);
DMA2_Stream5->M0AR = (uint32_t)usart1.RxInPtr->start;
DMA_Cmd(DMA2_Stream5, ENABLE); //打开DMA
}
}
void DMA2_Stream7_IRQHandler(void)
{
if(DMA_GetITStatus(DMA2_Stream7,DMA_IT_TCIF7)!=RESET)//等待DMA2_Steam7传输完成
{
DMA_Cmd(DMA2_Stream7,DISABLE); //关闭使能
DMA_ClearITPendingBit(DMA2_Stream7,DMA_FLAG_TCIF7); //清除DMA2_Steam7传输完成标志
DMA_SetCurrDataCounter(DMA2_Stream7, UARTx_TX_MAX);
usart1.TxState = 0;
}
if(DMA_GetITStatus(DMA2_Stream7,DMA_IT_HTIF7)!=RESET)
{
DMA_ClearITPendingBit(DMA2_Stream7,DMA_IT_HTIF7);
}
}
void DMA2_Stream5_IRQHandler(void)
{
if(DMA_GetITStatus(DMA2_Stream5,DMA_IT_TCIF5)!=RESET)//等待DMA2_Steam2传输完成
{
DMA_ClearITPendingBit(DMA2_Stream5,DMA_FLAG_TCIF5); //清除DMA2_Steam2传输完成标志
DMA_Cmd(DMA2_Stream5, ENABLE);
}
if(DMA_GetITStatus(DMA2_Stream5,DMA_IT_HTIF5)!=RESET)
{
DMA_ClearITPendingBit(DMA2_Stream5,DMA_FLAG_TCIF7);
}
}
/* 加入以下代码, 支持printf函数, 而不需要选择use MicroLIB */
#if 1
#if (__ARMCC_VERSION >= 6010050) /* 使用AC6编译器时 */
__asm(".global __use_no_semihosting\n\t"); /* 声明不使用半主机模式 */
__asm(".global __ARM_use_no_argv \n\t"); /* AC6下需要声明main函数为无参数格式,否则部分例程可能出现半主机模式 */
#else
/* 使用AC5编译器时, 要在这里定义__FILE 和 不使用半主机模式 */
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
/* Whatever you require here. If the only file you are using is */
/* standard output using printf() for debugging, no file handling */
/* is required. */
};
#endif
/* 不使用半主机模式,至少需要重定义_ttywrch\_sys_exit\_sys_command_string函数,以同时兼容AC6和AC5模式 */
int _ttywrch(int ch)
{
ch = ch;
return ch;
}
/* 定义_sys_exit()以避免使用半主机模式 */
void _sys_exit(int x)
{
x = x;
}
char *_sys_command_string(char *cmd, int len)
{
return NULL;
}
/* FILE 在 stdio.h里面定义. */
FILE __stdout;
/* 重定义fputc函数, printf函数最终会通过调用fputc输出字符串到串口 */
int fputc(int ch, FILE *f)
{
while ((USART1->SR & 0X40) == 0); /* 等待上一个字符发送完成 */
USART1->DR = (uint8_t)ch; /* 将要发送的字符 ch 写入到DR寄存器 */
return ch;
}
#endif
/***********************************************END*******************************************/
usart.h
#ifndef __USART_H
#define __USART_H
#include "stm32f4xx.h"
#include <stdint.h>
#include "string.h"
#include "signal.h"
#include "stdarg.h"
#include "stdio.h"
#include "stdlib.h"
#include <stdbool.h>
#define UARTx_RX_SIZE 4096 //接收缓冲区长度
#define UARTx_TX_SIZE 4096 //发送缓冲区长度
#define UARTx_RX_MAX 1029 //单次接收最大量
#define UARTx_TX_MAX 256 //单次接收最大量
#define SE_PTR_NUM 10 //se指针对结构体数组长度
typedef struct{
uint8_t *start; //start用于标记起始位置
uint8_t *end; //end用于标记结束位置
}LCB; //se 指针对结构体
typedef struct{
uint16_t RxCounter; //累计接收数据量
uint16_t TxCounter;
uint16_t TxState;
LCB RxLocation[SE_PTR_NUM]; //se指针对结构体数组
LCB *RxInPtr; //IN指针用于标记接收数据
LCB *RxOutPtr; //OUT指针用于提取接收的数据
LCB *RxEndPtr; ////IN和OUT指针的结尾标志
LCB TxLocation[SE_PTR_NUM]; //se指针对结构体数组
LCB *TxInPtr; //IN指针用于标记发送数据
LCB *TxOutPtr; //OUT指针用于提取发送的数据
LCB *TxEndPtr; ////IN和OUT指针的结尾标志
USART_InitTypeDef usart;
DMA_InitTypeDef dmatx;
DMA_InitTypeDef dmarx;
}Usartx_Control_Block;
extern uint8_t Usart1_RxBuffer[UARTx_RX_SIZE]; //串口1接收缓冲区
extern uint8_t Usart1_TxBuffer[UARTx_TX_SIZE]; //串口1发送缓冲区
extern Usartx_Control_Block usart1; //串口控制结构体
void USART1_Init(uint32_t bandRate);
void USART1_GPIO_Init(void);
void USART1_Config_Init(uint32_t bandRate);
void USART1_DMA_Init(void);
void Usart1_PtrInit(void);
void Usart1_Txdata(uint8_t *tdata,uint16_t datalen);
//void u1_printf(char *fmt, ...);
void Usart1_Rx_Data(uint8_t *rdata);
void usart1TakeCb(void(*pFunc)(uint8_t *data,uint16_t datalen));
void usart1_take(void);
void Clearsart3_RxBuffer(void);
void Usartx_SendStr( USART_TypeDef * pUSARTx, const char *SendStr);
void Usartx_SendArray( USART_TypeDef * pUSARTx, uint8_t *data, uint16_t datalen);
#endif
|
|