GD32F303 使用 STM32Cubmex 开发应用 使用GD32官方例程 开发Bootloader 修正错误 见评论

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值:相同了。于是乎他们就可以快乐的跳来跳去了!~!

希望 我写的这些 东东 能够帮助需要的人!再次感谢分享经验和代码的大神!~!
每个人都分享一点点。。。。什么坑都不是事

  • 17
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值