GD32F303 使用 STM32Cubmex 开发应用程序 使用GD32官方例程 开发Bootloader程序
最近使用GD32F303开发项目,为了偷懒使用 STM32Cubmex CPU选STM32F103,生成代码开发应用。应用到的功能有ADC(DMA)、USART(DMA)、UART、TIM、CAN、Freertos等。
将遇到的坑总结如下(有表达不对的地方请多多指教):
一、GD32F303主频可到120M,STM32F103主频72M。所以用STM32Cubmex生成的代码不更改的情况下主频工作在72M。
二、GD32F303 CAN初始化,STM32Cubmex生成的CAN代码烧录后,直接死机。之前的发的帖子有说明。
三、GD32F303 ADC(DMA),在最初配置STM32Cubmex时,设置了DMA功能生成代码没有问题。
如果开始没用DMA,并生成了代码测试没有问题。后来想提高下程序效率上DMA,那么小心了,有可能会让 你郁闷一下下。。。
最初配置STM32Cubmex时设置了DMA生成的main代码段如下:
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init(); //DMA初始化 我在这里 ~_~ ~_~
MX_ADC1_Init();
MX_SPI2_Init();
MX_TIM3_Init();
MX_TIM4_Init();
MX_CAN_Init();
MX_UART5_Init();
MX_USART3_UART_Init();
MX_IWDG_Init();
MX_RTC_Init();
/* USER CODE BEGIN 2 */
后续配置STM32Cubmex时设置了DMA生成的main代码段如下:
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_ADC1_Init();
MX_DMA_Init(); //DMA初始化 我在这里 ^_^ ^_^ ^_^
MX_SPI2_Init();
MX_TIM3_Init();
MX_TIM4_Init();
MX_CAN_Init();
MX_UART5_Init();
MX_USART3_UART_Init();
MX_IWDG_Init();
MX_RTC_Init();
/* USER CODE BEGIN 2 */
对比代码发现两次生成rMX_DMA_Init();函数的位置不一样。MX_DMA_Init() 在 MX_ADC1_Init()之前是正确的,否则DMA就是不对。这的地方让我郁闷了小半天。。。。
四、项目测试的差不多了,想做个Bootloader实现远程更新程序(高大尚走起)。GD32和STM32 flash不一样,STM32Cubmex生成的flash库用不了。于是从GD官网下了例程,这里的flash库是好用的,应用程序中没有用到flash操作。第一次搞Bootloader没经验,在网上拜读了各大神的贴子。真的很感激分享开发经验与代码的大神们。我也来做点贡献。
花了三天的时间,调试了编程板(SD卡存储应用程序Bin文件)、目标板(带Bootloader的应用电路板)。
1、编程板(CPU STM32F103),带有SD卡,USB,CAN接口。
2、目标板(CPU GD32F303),项目最终使用的电路板。
(1)、GD32F303 的 flash 名为 FMC具体查看
GD32F303 闪存512M之前每页2K。512M以后每页4K。我用到的303为256M所以每次擦除2K。
GD32F303 FMC 代码如下(借鉴了个别大神陪分代码):
/************************************************************************************************
** 函数名称: uint32_t FLASH_PagesCount(__IO uint32_t Size)
** 功能描述:
** 输 入:
** 输 出:
*************************************************************************************************/
uint32_t FLASH_PagesCount(__IO uint32_t Size)
{
uint32_t pagenumber = 0x0;
uint32_t size = Size;
if ((size % FLASH_PAGE_SIZE) != 0) //大于1页小于2页 + 1
{
pagenumber = (size / FLASH_PAGE_SIZE) + 1;
}
else
{
pagenumber = size / FLASH_PAGE_SIZE;
}
return pagenumber;
}
/************************************************************************************************
** 函数名称: int Flash_WritePorgramData(uint32_t AppAddress,uint8_t *AppBuff,uint32_t Lenth)
** 功能描述: 将接收到的程序数据写入flash,每次写入2K
** 输 入:
** 输 出:
*************************************************************************************************/
int Flash_WritePorgramData(uint32_t AppAddress0,uint8_t *AppBuff,uint32_t Lenth)
{
uint8_t nPage;
uint32_t j;
uint32_t EraseCounter = 0x00; //记录要擦除多少页
fmc_state_enum FLASHStatus = FMC_READY; //记录每次擦除的结果
WriteFlashStatus MemoryProgramStatus = PASSED;//记录整个测试结果
nPage = FLASH_PagesCount(Lenth); //计算擦除页数
fmc_unlock(); /* 解锁flash编写擦除控制器 */
//擦除Flash
for (EraseCounter = 0; (EraseCounter < nPage) && (FLASHStatus == FMC_READY); EraseCounter++)
{
FLASHStatus = fmc_page_erase(FlashDestination);
}
//把接收到的程序数据编写到Flash中
for (j = 0; (j < Lenth) ; j += 4)
{
//把接收到的数据编写到Flash中
fmc_word_program(FlashDestination, *(uint32_t*)AppBuff);
//校验写入数据
if (*(uint32_t*)FlashDestination != *(uint32_t*)AppBuff)
{
// printf("Write error!!\r\n");
MemoryProgramStatus = FAILED; //结束
}
FlashDestination += 4;
AppBuff += 4;
}
fmc_lock(); /* 锁定flash编写擦除控制器 */
return MemoryProgramStatus;
}
(3)、CAN接收数据存储到FMC代码(借鉴了个别大神陪分代码):
#define FLASH_PAGE_SIZE ((uint16_t)0x800) //2048
#define WRITE_START_ADDR ((uint32_t)0x08010000)
if(ProgramStep == BeginForProgram)
{
if((Rx_message.rx_data[0] == 'S')
&& (Rx_message.rx_data[1] == 'T')
&& (Rx_message.rx_data[2] == 'O')
&& (Rx_message.rx_data[3] == 'P'))
{
ProgramStep = FinishForProgram;
Flash_WritePorgramData(WRITE_START_ADDR + off,Can_Rx_Buffer,Position);
User_CANTransmit(0x18200000,"FINISH",6,1,2,11);
off = 0;
Position = 0;
}
for(i=0; i<Rx_message.rx_dlen; i++,TotalLen++)
{
Can_Rx_Buffer[Position++] = Rx_message.rx_data[i];
}
if(Position%1024 == 0)
{
if(Position/1024 == 2)
{
User_CANTransmit(0x18200000,"WAIT",4,1,2,11);
Flash_WritePorgramData(WRITE_START_ADDR + off,Can_Rx_Buffer,FLASH_PAGE_SIZE);
Position -= 2048;
for(i=0; i<=Position; i++)
{
Can_Rx_Buffer[i] = Can_Rx_Buffer[i+2048];
}
User_CANTransmit(0x18200000,"CONTINUE",8,1,2,11);
off += 2048;
}
else
{
User_CANTransmit(0x18200000,"CONTINUE",8,1,2,11);
}
}
}
***(4)、GD32F303 Bootloader部分
不知道为什么GD32F303RC Bootloader 划分小于 64K程序就不好用。
FMC中 Bootloader存储位置为 0x8000000 ~ 0x8010000***
FMC中 应用App 存储位置为 0x8010000 ~ 0x8030000
跳转部分代码如下:
/* 类型声明 -----------------------------------------------------------------*/
typedef void (*pFunction)(void);
//GD32F303RCT6,
//其flash大小为256K,共128页,每页大小2K,对应的地址为0x0800 0000--0x0804 0000
#define FLASH_PAGE_SIZE ((uint16_t)0x800) //2048
#define WRITE_START_ADDR ((uint32_t)0x08010000)
#define WRITE_END_ADDR ((uint32_t)0x08040000)
/************************************************************************************************
** 函数名称: void Jump_App(void)
** 功能描述:
** 输 入:
** 输 出:
*************************************************************************************************/
void Jump_App(void)
{
rcu_deinit();//此句代码可以让跳转后的应用代码运行起来,即使主频不对 至少可以让你知道程序跳转成功。
__set_FAULTMASK(1); //关闭中断,确保跳转过程中 不会进入中断,导致跳转失败 此句必须加
if (((*(__IO uint32_t*)WRITE_START_ADDR) & 0x2FFE0000) == 0x20000000)
{
AppAddress = *(__IO uint32_t*) (WRITE_START_ADDR + 4);
App_Run = (pFunction) AppAddress; //跳转到用户程序
__set_MSP(*(__IO uint32_t*) WRITE_START_ADDR); //初始化用户程序的堆栈指针
__set_CONTROL(0); //在RTOS工程,这条语句很重要,设置为特权级模式,SP使用MSP指针
App_Run();
}
}
注意 坑来了。。。
万事具备。。。Boot测试走起,我了个XX,跳转后程序怎么跑飞了。。。没有任何反应。十万个为什么???纳尼???
由于 应用程序 是用STM32cubemx生成的,Bootloader是用GD32官方库做的。
于是 用GD32官方库做了个小应用程序,发现Boot是正常的。耶耶Boot是好用的,感动!!
来 拜读 大神之贴 先_
https://blog.csdn.net/qq_23852045/article/details/104560672
看完大神的贴子,明白了。知道了为什么?和纳尼?
GD32 RCU_CFG0寄存器如下图:
STM32F103 RCC_CFGR寄存器 如下图:
来镜头拉近看这里。。。寄存器的27~31位。ST没用,GD为了使主频可以到120M,第27位和第30位用做 PLL 时钟倍频因子 的扩展位。
GD32的 PLL倍频因子最大可以做到PLL时钟源63。
STM32的PLL倍频因子最大可以做到PLL时钟源16
明白了所以然,那我们来把两个寄存器位一样的部分用起来就可以了。
STM32cubemx 应用程序 主频用72MHz
GD32官方库的主频也改成72MHz
这样就可以了是吧。。。开心搞起,纳尼还是跑飞,欲哭无泪。冷静。。。。
通过仿真跟踪发现
STM32cbuemx生成的代码72MHz是这样获得的
外部晶振8M PLL 倍频因子 为9 8* 9=72 来和我说 八九七十二 乘法口决是这么说的。
GD32F303库中的72MHz是怎样获得的呢??
外部晶振8M (8M/2)* PLL 倍频因子为18 (8/2)*18 = 72 为啥要这样弄呢?
有可能是GD想测试RCU_CFG0 第27位或第30位吧。
那好吧更改GD32F303代码和STM32获得72MHz一样PLL 倍频因子也为9 代码如下:
static void system_clock_72m_hxtal(void)函数中
/******************** 原函数库中 72M 是这么来的 ***************************/
#if (defined(GD32F30X_HD) || defined(GD32F30X_XD))
// /* select HXTAL/2 as clock source */
// RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0);
// RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M | RCU_CFG0_PREDV0);/* select HXTAL/2 as clock source */
// /* CK_PLL = (CK_HXTAL/2) * 18 = 72 MHz 这里是 四乘十八得七十二 ^_^ */
// RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5);
// RCU_CFG0 |= RCU_PLL_MUL18;
/******************** 更改后的 72M 是这么来的 ***************************/
/* select HXTAL as clock source */
RCU_CFG0 &= ~(RCU_CFG0_PLLSEL | RCU_CFG0_PREDV0);
RCU_CFG0 |= (RCU_PLLSRC_HXTAL_IRC48M ); /* select HXTAL as clock source */
/* CK_PLL = (CK_HXTAL/1) * 9 = 72 MHz 这里是 八九七十二 ^_^ */
RCU_CFG0 &= ~(RCU_CFG0_PLLMF | RCU_CFG0_PLLMF_4 | RCU_CFG0_PLLMF_5);
RCU_CFG0 |= RCU_PLL_MUL9;
就这样
GD32 RCU_CFG0寄存器PLL值:STM32F103 RCC_CFGR寄存器 PLL值:相同了。于是乎他们就可以快乐的跳来跳去了!!!!~!
希望 我写的这些 东东 能够帮助需要的人!再次感谢分享经验和代码的大神!~!
每个人都分享一点点。。。。什么坑都不是事!!