目录

  • 1. 硬件芯片移植
  • 2. 软件配置
  • 2.1 修改外部晶振起振超时时间
  • 2.2 增加DMA 功能
  • 2.3 异常记录


1. 硬件芯片移植


下面以 GD32F103RET6 替换 STM32F103RET6为例

  1. 硬件上使用对应的GD32芯片替换STM32,如使用 GD32F103RET6 替换 STM32F103RET6,其引脚数与引脚定义都是一样的
  2. 到兆易创新官网 - 资料下载 - 对应系列芯片 - 应用软件 - 找到GD32F1x0_Addon_V3.1.0.rar,解压安装
    3. 更换算法文件(.FLM)到Keil 的Flash文件夹中(Keil_v5\ARM\Flash)

算法文件资源下载:GD32F10xxx Keil IDE Config.rar

2. 软件配置

下图取自STM32 参考手册,配置DMA 通道时须根据此表对应功能配置,如使用DMA 作为串口1的接收功能,就必须使用DMA1 的通道5 .

stm32可以移植深度学习算法吗_GD32

2.1 修改外部晶振起振超时时间

⚠ 不用外部晶振可跳过此步

由于GD芯片与ST芯片的启动时间存在差异,需要对下面参数进行修改

搜索以下代码
#define HSE_STARTUP_TIMEOUT ((uint16_t)0x0500)
修改为:
#define HSE_STARTUP_TIMEOUT ((uint16_t)0xFFFF)

2.2 增加DMA 功能

下面以串口3为例

GD32替换STM32的最大问题就是串口收发问题,经常出现丢数据异常,使用DMA处理串口数据,可有效解决该问题。

  • 首先引入STM32 官方头文件 stm32f10x_dma.h
#include "stm32f10x_dma.h"
  • 串口3与DMA初始化函数
#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);  		//使能串口 
}
  • 串口3中断服务函数
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);//等待发送结束
	}
}
  • main函数
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;
        }
    }
}

2.3 异常记录

  1. 如下图

stm32可以移植深度学习算法吗_stm32可以移植深度学习算法吗_02

  • 异常原因:Receice_DataPack函数中,参数USART_RX_BUFF_SIZE 的长度与 Usart3data 定义的长度不同
  • 解决方法:根据实际接收缓冲区长度,修改USART_RX_BUFF_SIZE 的长度

参考:GD32如何替换STM32?