GD32F4移植STM32F4


前言

近期在项目中采用了GD32F407VET6替换原项目中的STM32F407VET6,网传GD的兼容性很好,之前也用F1系统的替换了一下,按照CSND各位大佬的经验一步步改进了代码,测试直接通过,现在也一直在项目中实际应用了,一直没有出问题。
所以这SMT时,嘉立创没有STM的货果断换成了GD,可换时简单,到手之后,搜索了一大圈,并没有发现F4像F1一样有大把的经验可以抄。可能是我的搜索水平有限;也有可能是F4相较于F1,其成本优势并没有比F1强很多,替换的人就少了。
还好经过几天的摸索还是搞定了F4的程序替换,下面就个人经验分享给大家,提前声明一下,个人经验有限,本文只阐述本人的开发过程,目前程序运行正常,如果有其他不兼容的地方,还请各位网友自行解决。
另外,如果你是新开发程序,还是建议采用GD的库,毕竟原厂的库更有保障,我这里是因为程序都开发完成了,再移植到GD的库中,很多地方都有修改。

一、开发环境

本人使用Keil5,编写的程序,其他开发环境如:IAR等,可参考修改。工程模板及大部分驱动代码借鉴正点原子F4系列。

二、移植步骤

1.安装GD的支持包。

GD的程序芯片可以选择STM32的替代,但是烧录的时候Flash必须选择GD的,所以安装支持包是必须的。
官网pack包下载链接:https://www.keil.com/dd2/pack/
1.进入KEIL官网下载pack包(链接 **https://www.keil.com/dd2/pack/)

keil MDK5官网
2.找到GigaDevice -> GD32F4xx pack包 -> 点击下载,等待下载完成。
GD32F4Pack包
3. 下载完成后,点击安装包,默认安装就好了。
4. 安装完成之后可以打开工程文件,点击魔术棒\Device ,选择对应的GD的芯片就可以了。
GD32芯片选择
注意:重选芯片,会让工程的代码起始位置和大小变成默认值,如果你的程序有OTA或者其他更改程序起始位置和大小的设计,请自行在Target中修改。
修改程序起始位置和大小
5.然后 点击魔术棒\Debug\Settings\Flash Download\Add,如果看到下图这个GD的Flash就ok了。
GD的Flash下载代码
还有,代码的起始位置和大小,按照自己的程序来改,没有修改就默认即可,我这里是BootLoader程序,所以程序大小就比默认的小很多。
程序起始位置和大小修改
6. 完成上面的操作之后就重新编译一下工程即可。

2.修改32固件库的时钟

在V3.x的库,启动时间宏定义在xxx32f10x.h头文件中;在V3.0以前的库,其启动时间宏定义在xxx32f10x_rcc.c中(HSEStartUp_TimeOut)。搜索一下就找到了。

//修改前:
#define HSE_STARTUP_TIMEOUT    ((uint16_t)0x05000)   /*!< Time out for HSE start up */
//修改后:
#define HSE_STARTUP_TIMEOUT    ((uint16_t)0xFFFF)   /*!< Time out for HSE start up */

GD和STM32的晶振部分电路设计存在一定的差异,修改HSE_STARTUP_TIMEOUT宏定义可以保证晶振能够正常起振。

3.FLASH修改

由于GD的Flash采用了其专利技术,所以GD 的Flash 和STM 的Flash 有些许差异。GD 的擦除时间会一些。对于Falsh的移植要重视,如果程序中有对FLASH的操作(主要是写Flash),则应该进行修改。
这里我们参考F1系列的修改。

3.1 F1的处理

在F1的移植中需要对4个函数进行修改

FLASH_Status FLASH_EraseOptionBytes(void)
FLASH_Status FLASH_ProgramOptionByteData(uint32_t Address, uint8_t Data)
FLASH_Status FLASH_EnableWriteProtection(uint32_t FLASH_Pages)
FLASH_Status FLASH_ReadOutProtection(FunctionalState NewState)

上面这个这四个函数里面都有下面这一句代码:

key( FLASH->OPTKEYR = FLASH_KEY1;FLASH->OPTKEYR = FLASH_KEY2;

在以上4个函数中在写完KEY 序列以后,需要读该位,确认key 已生效。所以我们在这里应该插入
While( ! (FLASH->CR & 0x200 ) ); // Wait OPTWRE 或可简单插入两个NOP。
比如,改成以下这样。

/* Authorize the small information block programming */
FLASH->OPTKEYR = FLASH_KEY1;
FLASH->OPTKEYR = FLASH_KEY2;
while( ! (FLASH->CR & 0x200 ) );// Wait OPTWRE

或者

/* Authorize the small information block programming */
FLASH->OPTKEYR = FLASH_KEY1;
FLASH->OPTKEYR = FLASH_KEY2;
__NOP();
__NOP();

3.2 F4的Flash修改

由于我代码里对Flash的操作使用的是正点原子的驱动文件,并未使用ST官方库函数stm32f4xx_flash.c,尝试搜索key( FLASH->OPTKEYR = FLASH_KEY1;FLASH->OPTKEYR = FLASH_KEY2;),并无匹配项,于是尝试搜索FLASH_KEY1,这时匹配到下面那个函数:

//解锁STM32的FLASH
void STMFLASH_Unlock(void)
{
	FLASH->KEYR=FLASH_KEY1;	//写入解锁序列.
	FLASH->KEYR=FLASH_KEY2; 
}

于是便在其后面加了一个延时查询指令:while( ! (FLASH->CR & 0x200 ) );// Wait OPTWRE
但在后面的测试中,发现有较大概率会卡死在这个while循环中,于是只能采取加两个空延时的方式实现,具体是什么问题暂时还没有查出来。目前该函数改成以下这样:

//解锁STM32的FLASH
void STMFLASH_Unlock(void)
{
	FLASH->KEYR=FLASH_KEY1;	//写入解锁序列.
	FLASH->KEYR=FLASH_KEY2; 
	__NOP();
	__NOP();
//	while(!(FLASH->CR & 0x200));// Wait OPTWRE
}

此外在F1的帖子里,提到GD的延时时间比ST的短,出于保险起见,我在flash的操作中也延长了其等待时间,其函数的等待因为是可以break循环而提前退出,所以我也不知道这部分代码是否起到作用,但是加上应该不会有问题。我是将STMFLASH_WaitDone这个函数的参数扩大为原来的2倍,修改的函数如下:

//擦除扇区
//sectoraddr:扇区地址,范围是:0~11.
//0~3,16K扇区;4,64K扇区;5~11,128K扇区.
//返回值:执行情况
u8 STMFLASH_EraseSector(u32 sectoraddr)
{
	u8 res=0;
	//res=STMFLASH_WaitDone(200000);//等待上次操作结束,最大2s
	res=STMFLASH_WaitDone(400000);//等待上次操作结束,最大2s
	if(res==0)
	{ 
		FLASH->CR&=~(3<<8);	//清除PSIZE原来的设置
		FLASH->CR|=2<<8;	//设置为32bit宽,确保VCC=2.7~3.6V之间!!
		FLASH->CR&=~(0X1F<<3);//清除原来的设置
		FLASH->CR|=sectoraddr<<3;//设置要擦除的扇区 
		FLASH->CR|=1<<1;	//扇区擦除 
		FLASH->CR|=1<<16;	//开始擦除		  
		//res=STMFLASH_WaitDone(200000);//等待上次操作结束,最大2s
		res=STMFLASH_WaitDone(400000);//等待操作结束,最大2s  
		if(res!=1)			//非忙
		{
			FLASH->CR&=~(1<<1);//清除扇区擦除标志.
		}
	}
	return res;
}
//在FLASH指定地址写一个字
//faddr:指定地址(此地址必须为4的倍数!!)
//dat:要写入的数据
//返回值:0,写入成功
//    其他,写入失败
u8 STMFLASH_WriteWord(u32 faddr, u32 dat)
{
	u8 res;	   	  
	//res=STMFLASH_WaitDone(0xFF);
	res=STMFLASH_WaitDone(0X200);
	if(res==0)//OK
	{
		FLASH->CR&=~(3<<8);	//清除PSIZE原来的设置
		FLASH->CR|=2<<8;	//设置为32bit宽,确保VCC=2.7~3.6V之间!!
 		FLASH->CR|=1<<0;	//编程使能
		*(vu32*)faddr=dat;	//写入数据
		//res=STMFLASH_WaitDone(0XFF);//等待操作完成,一个字编程,最多100us.
		res=STMFLASH_WaitDone(0X200);//等待操作完成,一个字编程,最多100us.//0xff
		if(res!=1)//操作成功
		{
			FLASH->CR&=~(1<<0);//清除PG位.
		}
	} 
	return res;
} 

4.其他修改

本来我以为修改以上几点就可以完美兼容代码,可现实给我一个大大的教训,现在想来可能是GD的芯片代码要更加规范,所以如果移植出问题了,可能要在代码的规范上重视。
还有就是GD的MCU和ST的相比在连续串口发送的时候会多一个IDLE bit,但是在F1中,这个差异对于大多数程序并不影响执行,所以就没有重视这个问题。

4.1 函数中的变量定义

在函数中的变量必须先定义,再进行其他操作,当我的芯片改为GD之后,keil编译一遍程序之后,出现了几个报错,百度了一下,发现就是有一个变量的声明在函数的中间,将其声明改到函数前面之后就正常了。

4.2 串口发送

在串口发送数据时,要先发送再等待相应的标志位变化,如果先查询标志位那么程序就会一直在这个while循环中运行,直到我的看门狗将程序复位。
比如,我修改前的串口3发送代码如下:

/*-------------------------------------------------*/
/*函数名:串口3发送缓冲区中的数据                  */
/*参  数:data:数据                               */
/*返回值:无                                       */
/*-------------------------------------------------*/
void u3_TxData(unsigned char *data)
{
	int	i;	
	while((USART3->SR&0X40)==0);
	for(i = 1;i <= data[0];i ++){			
		USART3->DR = data[i];
		while((USART3->SR&0X40)==0);	
	}
}

修改之后为,就正常了。

/*-------------------------------------------------*/
/*函数名:串口3发送缓冲区中的数据                  */
/*参  数:data:数据                               */
/*返回值:无                                       */
/*-------------------------------------------------*/
void u3_TxData(unsigned char *data)
{
	int	i;	
//	while((USART3->SR&0X40)==0);
	for(i = 1;i <= data[0];i ++){			
//		USART3->DR = data[i];
//		while((USART3->SR&0X40)==0);	
		USART_SendData(USART3,data[i]);
    while( USART_GetFlagStatus(USART3, USART_FLAG_TC) == RESET );
	}
}

4.3 中断服务函数中不能有printf

其实这个问题,是大部分做单片机开发的一个常识,这个是我之前在ST调试时留下的一条调试信息,只输出了一个数字,(因为我也知道中断中最好不能printf),在ST中正常,后面程序就忘了删,就埋了一个雷,浪费了许多时间。
这也是我说GD的代码要规范的原因,可能他的容错性还是低于ST的,还有其他的例子,但是我一时想不起来了,只能把我记得的bug写出来,以后调试出现问题,要着重查查代码规范性。


总结

以上就是我在移植中的一个小小记录,也可以给大家一个参考。

  • 3
    点赞
  • 58
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值