下面以 GD32F103RET6 替换 STM32F103RET6为例
GD32F1x0_Addon_V3.1.0.rar
,解压安装算法文件资源下载:GD32F10xxx Keil IDE Config.rar
下图取自STM32 参考手册,配置DMA 通道时须根据此表对应功能配置,如使用DMA 作为串口1的接收功能,就必须使用DMA1 的通道5 .
⚠ 不用外部晶振可跳过此步
由于GD芯片与ST芯片的启动时间存在差异,需要对下面参数进行修改
搜索以下代码
#define HSE_STARTUP_TIMEOUT ((uint16_t)0x0500)
修改为:
#define HSE_STARTUP_TIMEOUT ((uint16_t)0xFFFF)
下面以串口3为例
GD32替换STM32的最大问题就是串口收发问题,经常出现丢数据异常,使用DMA处理串口数据,可有效解决该问题。
stm32f10x_dma.h
#include "stm32f10x_dma.h"
#define USE_USART3_DMA_RX 1 // 宏定义一个条件变量
char Usart3data[200]; // 串口数据接收数组
u8 Usart3flag =0; // 标志位
u8 usart3Count=0; // 串口数据计数
void USART3_Configuration(u32 BaudRate)
{
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStruct; // 定义dma 结构体
//打开usart3要使用的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB | RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3,ENABLE);
//配置串口TX作为推挽复用端口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//配置串口RX作为浮空输入
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//配置NVIC
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //一个抢占优先级,3个从优先级
NVIC_InitStructure.NVIC_IRQChannel =USART3_IRQn; //和库函数手册不一样
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //主优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道
NVIC_Init(&NVIC_InitStructure);
//配置USART
USART_InitStructure.USART_BaudRate = BaudRate; //波特率
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_Tx | USART_Mode_Rx; //模式
USART_Init(USART3, &USART_InitStructure); //写入结构体
// USART_Cmd(USART3, ENABLE); //使能usart3
USART_ITConfig(USART3,USART_IT_PE,ENABLE);
USART_ITConfig(USART3, USART_IT_RXNE,ENABLE); //打开usart3的接收中断
USART_ClearFlag(USART3,USART_IT_RXNE); //清除中断标志
// USART_ClearFlag(USART3, USART_FLAG_TC); // 清标志
/********************************以下是对DMA的参数初始化**********************************/
#if USE_USART3_DMA_RX
USART_ITConfig(USART3, USART_IT_IDLE, ENABLE); // 开启串口3空闲中断
DMA_DeInit(DMA1_Channel3); // 查上表可知,串口3 接收功能对应DMA1 的通道3
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // DMA1时钟初始化
// RX DMA1 ??5
DMA_InitStruct.DMA_BufferSize = sizeof(Usart3data); // 传输的数据大小,其中Usart3data 就是串口数据接收变量
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; // 外设作为数据的来源
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; // 不使能M TO M传输
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)Usart3data; // 设置DMA源地址:串口数据寄存器
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; // 内存数据单元
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; // 外设地址不增
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; // DMA模式一次或者循环模式
//DMA_InitStruct.DMA_Mode = DMA_Mode_Circular; // DMA模式一次或者循环模式
DMA_InitStruct.DMA_PeripheralBaseAddr = USART3_BASE + 0x04; // 设置DMA源地址:串口数据寄存器地址
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设数据单元
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 外设地址不增加
DMA_InitStruct.DMA_Priority = DMA_Priority_High; // 优先级为中
// 配置DMA通道
DMA_Init(DMA1_Channel3, &DMA_InitStruct);
// 清除DMA所有标志
DMA_ClearFlag(DMA1_FLAG_TC3);
DMA_ITConfig(DMA1_Channel3, DMA_IT_TE,ENABLE);
USART_DMACmd(USART3, USART_DMAReq_Rx, ENABLE);//开启串口DMA接收
DMA_Cmd(DMA1_Channel3, ENABLE); // 使能DMA通道
#else
USART_ITConfig(USART3, USART_IT_RXNE, ENABLE); // 开启串口接收中断
#endif
// USART_ClearFlag(USART3, USART_FLAG_TC); /* 清除标志避免第一个字符丢失 */ // 使用GD32时,发送第一个数据前不要清除 USART_FLAG_TC(发送完成标志位)
USART_Cmd(USART3, ENABLE); //使能串口
}
void USART3_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
#if USE_USART3_DMA_RX
if(USART_GetITStatus(USART3, USART_IT_IDLE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾) 读取接收中断标志位后自动清零接收中断标志位
{
Receice_DataPack(3);
Res = USART3->SR;
Res = USART3->DR;
}
#else
#endif
}
#define USART_RX_BUFF_SIZE 200 // 注意这里定义的串口接收缓冲区大小应根据实际定义为准
void Receice_DataPack(uint8_t channel)
{
//接收到的数据长度
uint32_t buff_length;
u32 i;
static uint8_t byteindex = 0;
switch(channel)
{
case 3:
{
//获取这一帧数据个数,此处注意 USART_RX_BUFF_SIZE 的长度要跟 Usart3data 定义的长度相同,否则会发生异常
buff_length = USART_RX_BUFF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel3);
for(;byteindex < buff_length;byteindex++)
{
Putch(USART3,Usart3data[byteindex]);
}//回显处理
byteindex = buff_length;
if(Usart3data[buff_length-1] == '\r' ||Usart3data[buff_length-1] == '\n') //接收到了一帧完整的命令
{
Usart3flag = 1;//接收到空行的命令
Putch(USART3,'\n');//MAC下不发送新行会覆盖输入
byteindex = 0;
if(Usart3data[buff_length-1] == '\n' || Usart3data[buff_length-1] == '\r')//去掉接受数据中尾部的回车换行符
{
Usart3data[buff_length-1] = 0;
}
if(Usart3data[buff_length-2] == '\r')
{
Usart3data[buff_length-2] = 0;
}
return;
}
if(buff_length >= 50)
{
byteindex = 0;
DMA1_Channel3->CCR&=~(1<<0); //关闭DMA接受中断
DMA1_Channel3->CNDTR=USART_RX_BUFF_SIZE; //清空DMA数据计数器,重新计数下一帧数据
DMA1_Channel3->CCR|=1<<0;
}
break;
}
default: break;
}
}
void Putch(USART_TypeDef* USARTx,u8 k) // 回显函数
{
if(k == '\n')
{
USART_SendData(USARTx,'\r');
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)!=SET);//等待发送结束
USART_SendData(USARTx,'\n');
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)!=SET);//等待发送结束
}
else if(k == '\b')//接收到
{
USART_SendData(USARTx,'\b');
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)!=SET);//等待发送结束
USART_SendData(USARTx,' ');
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)!=SET);//等待发送结束
USART_SendData(USARTx,'\b');
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)!=SET);//等待发送结束
}
else
{
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)!=SET);//等待发送结束
USART_SendData(USARTx,k);
//while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET);//等待发送结束
}
}
int main()
{
... //省略代码
while(1)
{
... //省略代码
if(Usart3flag==1) //接收完成
{
port_Index=3;
CheckUartCmd(Usart3data);
Usart3flag=0;
memset(Usart3data,'\0',sizeof(Usart3data));//清空接收缓冲区
DMA1_Channel3->CCR&=~(1<<0); //关闭DMA接受中断
DMA1_Channel3->CNDTR=sizeof(Usart3data); //清空DMA数据计数器,重新计数下一帧数据
DMA1_Channel3->CCR|=1<<0;
usart3Count=0;
}
}
}
Receice_DataPack
函数中,参数USART_RX_BUFF_SIZE
的长度与 Usart3data 定义的长度不同USART_RX_BUFF_SIZE
的长度参考:GD32如何替换STM32?