本帖最后由 聚沃科技 于 2023-6-20 14:38 编辑
#技术资源# #申请原创#
1. System 模块使用注意事项
1.1 HXTAL 时钟丢失后,程序如何运行
程序使用外部晶振正常运行之后,如果将外部晶振去掉或者短路,GD32F30x 的程序不会取指执行。若将晶振重新恢复正常,程序会接着之前的地址运行,如果程序对丢时钟这种场景有特殊要求,应用中需要手动使能时钟安全系统,在其中做相关的保护措施。
1.2 如何区分 standby 复位和上电复位
仅通过 RCU 的 RCU_RSTSCK 寄存器无法区分是 standby 复位还是上电复位,需要结合查询 PMU 的 PMU_CS 寄存器解决问题。
1.3 HSE 注意事项
GD32F30x 外部晶体起振时间会比 STM32F10x 系列要长,所以原有的晶体超时时间需要加大:
#define HSE_STARTUP_TIMEOUT ((uint16_t)0x0500);
/*!< Time out for HSE start up */
调整前:
#define HSE_STARTUP_TIMEOUT ((uint16_t)0xFFFF);
/*!< Time out for HSE start up */
调整后:
#define HSE_STARTUP_TIMEOUT ((uint16_t)0xFFFF);
/*!< Time out for HSE start up */
1.4 如何通过软件区分 GD32 和 STM32
GD32F30x 在设计阶段,已经预留了相关寄存器,用户只需要软件读取寄存器,即可获取到相关的型号信息,GD32F30x 每个型号此处值都为固定值。
Code_Num=*( uint32_t *)( 0x40022100 );
1.5 GD32F30x Flash 取值零等待,软件方面注意事项
GD32F30x 系列 Flash 为零等待设计,在同主频下,带来了更高的性能体验。如果用户代码有用到 for 循环或者是 while 循环语句来做延时,延时时间在GD32F30x系列上会变短,需要适当的加大延时参数或改用Timer来做延时函数。
1.6 GD32F30x 上电启动异常常见原因
1.6.1 检查板子上 Boot 0 引脚是否悬空,GD32F30x 运行用户程序必须要求 Boot0经 10K 电阻接 GND;
1.6.2 如果板子上有大功率器件(Wifi、GSM、GPS 等),检查大功率器件开启瞬间VDD 是否存在跌落情况,如存在跌落可以适当加大电源输出端的负载电容;
1.6.3 观察芯片的复位管脚,复位管脚是否一直处于拉低状态,检查是否供电异常或者是芯片硬件看门狗使能了,芯片处于反复复位状态。
1.7 MCU 无法正常使用 SWD 下载程序
1.7.1 接线异常,SWD 相关的调试口未正常接好;
1.7.2 芯片是否被读保护或者处于反复复位状态;
1.7.3 SWD 的调试线过长或者是通信速率过高,适当减短 SWD 数据线,同时降低SWD 速率;
1.7.4 按照硬件指南给 SWD 添加相应的上下拉电阻,提高通信抗干扰能力。
1.8 MCU 上电时间较长
由于设计架构差异,GD32F30x 比 STM32F10x 的上电时间和 Standby 唤醒时间较长,时间约 144ms,若对时间没有要求,可忽略。
1.9 代码超过 256K 后执行速度慢
GD32F30x 系列的 Flash 分为 Code 区(前 256K)和 Data 区(256K 以后的区域),二者在擦写操作上没有区别,但是读操作时间上存在较大差别,Code 区代码取值零等待,Data 区执行代码会有较长延时。应用中如果涉及该架构影响到使用可以通过分散加载来改善,具体做法参考分散加载应用文档。
2. CAN 模块使用注意事项
2.1 CAN 发送出现 ACK 错误
假如出现类似 ACK 出错的问题,可以修改采样点,一般做法是将 BS1 值增大,BS2 减小。
2.2 CAN 接收异常,接收两帧数据会丢一包数据
STM32 固件库会调用 CAN_FIFORelease 函数,如果手动多调用一次清缓存的动作会导致 CAN 接收丢包,也就是软件中无需主动调用 CAN_FIFORelease 函数,CAN FIFO 会被自动释放。
3. 低功耗注意事项
3.1 在 Stop 模式下,少数芯片功耗偏高
为了使功耗恢复正常,需要将没用到的 IO 口全部配置成模拟输入(AN)模式,芯片内部未引出来的也要配置。
3.2 在 Stop 模式下,概率性不能唤醒
在不断进出 Stop,并且不断有 timer 或者 systick 中断时,运行很长时间后,在 Stop模式下有概率性的无法唤醒。
解决方案:
当进入 Stop 时(WFI/WFE),屏蔽掉除唤醒源中断外的所有中断,出 Stop 之后再打开其他中断,代码修改如下。
void PWR_EnterSTOPMode(uint32_t PWR_Regulator, uint8_t PWR_STOPEntry)
{
uint32_t tmpreg = 0;
static uint32_t reg_snap[ 4 ];
/* Check the parameters */
assert_param(IS_PWR_REGULATOR(PWR_Regulator));
assert_param(IS_PWR_STOP_ENTRY(PWR_STOPEntry));
/* Select the regulator state in STOP mode ---------------------------------*/
tmpreg = PWR->CR;
/* Clear PDDS and LPDS bits */
tmpreg &= CR_DS_MASK;
/* Set LPDS bit according to PWR_Regulator value */
tmpreg |= PWR_Regulator;
/* Store the new value */
PWR->CR = tmpreg;
/* Set SLEEPDEEP bit of Cortex System Control Register */
SCB->SCR |= SCB_SCR_SLEEPDEEP;
reg_snap[ 0 ] = (*(volatile uint32_t *)(0xE000E010));//REG32( 0xE000E010 );
reg_snap[ 1 ] = (*(volatile uint32_t *)(0xE000E100));//REG32( 0xE000E100 );
reg_snap[ 2 ] = (*(volatile uint32_t *)(0xE000E104));//REG32( 0xE000E104 );
reg_snap[ 3 ] = (*(volatile uint32_t *)(0xE000E108));//REG32( 0xE000E108 );
(*(volatile uint32_t *)(0xE000E010)) &= 0x00010004; //( 0xE000E010 )
(*(volatile uint32_t *)(0xE000E180)) = 0XFF7FF83D; //( 0xE000E180 )
(*(volatile uint32_t *)(0xE000E184)) = 0XBFFFF8FF; //( 0xE000E184 )
(*(volatile uint32_t *)(0xE000E188)) = 0xFFFFFFFF; //( 0xE000E188 )
/* Select STOP mode entry --------------------------------------------------*/
if(PWR_STOPEntry == PWR_STOPEntry_WFI)
{
/* Request Wait For Interrupt */
WFI();
}
else
{
/* Request Wait For Event */
SEV();
WFE();
WFE();
}
(*(volatile uint32_t *)(0xE000E010)) = reg_snap[ 0 ] ;
(*(volatile uint32_t *)(0xE000E100)) = reg_snap[ 1 ] ;
(*(volatile uint32_t *)(0xE000E104)) = reg_snap[ 2 ] ;
(*(volatile uint32_t *)(0xE000E108)) = reg_snap[ 3 ] ;
/* Reset SLEEPDEEP bit of Cortex System Control Register */
SCB->SCR &= (uint32_t)~((uint32_t)SCB_SCR_SLEEPDEEP);
}
4. ADC 模块使用注意事项
4.1 ADC 采集数据异常问题分析
4.1.1. ADC 通道的采集引脚未配置为模拟输入,GD32 要求通道 IO 口必须配置为模拟输入;
4.1.2. ADC 时钟过高,ADC 采样时钟高于 40M 获取到的数据不具有参考意义;
4.1.3. ADC 不耐 5V 的 IO 口被接入超过 VDDA 的电平信号;
4.1.4. ADC 采样值偏小或不稳定,应该适当的降低 ADC 时钟,加大采样周期的值。
4.2 ADC1 和 ADC2 同步模式下,注意事项
如果 ADC1 和 ADC2 同步采集,ADC2 是跟着 ADC1 同步触发,此时 ADC2 的注入组的触发方式需要手动配置成软件触发(默认是 TIMER1_TRGO),否则可能导致 ADC 注入组无数据。
4.3 ADC_CR2 中的 ADCON 使用注意事项
ADC 使能以后需要在代码里面插入 1 个通道的 ADC 转换周期以上;
/* Enable ADC1 */
ADC_Cmd(ADC1, ENABLE);
Delay_us(20);
4.4 ADC 查询法采集数据,出现通道错乱的情况
ADC 使用查询法采集数据时,如果使能了 ADC 的 SCAN 模式,就有可能会出现ADC 数据错乱的情况;ADC 采集通道 SCAN 功能只适用于多通道注入采样和DMA 模式。
4.5 ADC 在每次复位之后采样值有偏差
由于 MCU 上电或者复位阶段,ADC 校准时,电源不稳定,导致校准时出现偏差,ADC 值采样均出现偏大或者偏小,可以关闭校准,或者等电源稳定后再校准解决该问题。
/* ADC calibration and reset calibration */
adc_calibration_enable(ADCx);
5 RCU 模块使用注意事项
当 IAP 时,使用 GD32 的固件作为 boot,跳转到 STM32 固件库的 APP 后,可能造成系统时钟不对,例如 uart 打印异常,timer 异常。
GD32F30x 系列 MCU 最高主频可以运行 120MHz,PLL 倍频参数比 STM32 的多几个 bit,如下,出现异常时,需要跳转前将相关的 bit 复位。
GD32 的 CFG0 寄存器的 bit30 bit28 bit27 为有效位。
STM32 的 RCC_CFGR 寄存器的 bit30 bit28 bit27 为保留位。
6 SPI 模块使用注意事项
6.1 SPI 通信 BSY 标志位
在 SPI 程序编写的过程中,轮询使用 BSY 作为通信标志位,导致传送数据丢失或者是错误。
这主要是因为 GD32 的 BSY 标志位不是在写入 DR 后就置位的,而是发送完第一个 bit 才被置位,传输过程中不要使用 BSY 作为每次传输的判断,使用 TXE 和
RXNE 来进行判断。
/*send data*/
while((SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY) ==SET))
while((SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) !=SET))
/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();
/*receive data*/
while((SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_BSY) ==SET))
while((SPI_I2S_GetFlagStatus(SPI1,SPI_I2S_FLAG_TXE) !=SET))//send byte
/* Deselect the FLASH: Chip Select high */
SPI_FLASH_CS_HIGH();
6.2 SPI 使能之后配置参数不生效
当需要修改 SPI 模式或者速度的时候,需要将 SPI 先 disable,再修改参数,最后再 enable.
6.3 SPI 从机模式管脚模式
从机模式下 CLK、MISO、NSS 需要将 IO 配置成 Input_floating,才能正常工作。
7 UART DMA 注意事项
使用 UART DMA 发送数据的时候,可能丢掉一帧中的第一个 byte 数据,注意尽量不要在发送的时候频繁的开关 UART 发送,若测试无问题,可以忽略。
usart_transmit_config(USARTx,USART_TRANSMIT_ENABLE)假如要关闭 UART 发送,需要按照以下流程:先开启 usart_transmit_ enable 再去打开 dma_channel_enable,如下图。
8 LSE 驱动模式
GD32 MCU 的 RTC 驱动能力分四档,默认是强驱动能力,若不使用 GD32 本身的固件库,可能会强驱动改成弱驱模式,造成部分机器不起振,则需要将寄存器RCU_BDCTL 的 LXTALDRI[1:0]配置成 11。
9 USB 模块使用注意事项
USBD 通讯假如出现 PMOUIF 和 ERRIF 错误,可以通过屏蔽 CNTR_ESOFM 标 志 来解决该问题。
/* IMR_MSK */
/* mask defining which events has to be handled */
/* by the device application software */
#define IMR_MSK (CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM
| CNTR_SOFM| CNTR_ESOFM | CNTR_RESETM )//before fix
#define IMR_MSK (CNTR_CTRM | CNTR_WKUPM | CNTR_SUSPM | CNTR_ERRM
| CNTR_SOFM| CNTR_RESETM )// after fix
10 Flash 模块使用注意事项
10.1 Flash 擦写差异
GD32 MCU Flash 执行速度快,但是写操作慢,所以在对 Flash 操作的时候需要修改下面几个函数:
修改 Flash 擦除和编程超时宏定义:
#define EraseTimeout ((uint32_t)0xFFFFFFFF)//0x000B0000
#define ProgramTimeout ((uint32_t)0xFFFFFFFF)//00002000
修改选项字节操作函数:
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;)
后添加两个__nop()语句或者是增加 如下语句:
while((FLASH->CR&CR_OPTWRE_Set)!=CR_OPTWRE_Set )
{}
#define CR_OPTWRE_Set ((uint32_t) 0x00000200) //Fix
FLASH_Status FLASH_EraseOptionBytes(void)
{
uint16_t rdptmp = RDP_Key;
FLASH_Status status = FLASH_COMPLETE;
/* Get the actual read protection Option Byte value */
if(FLASH_GetReadOutProtectionStatus() != RESET)
{
rdptmp = 0x00;
}
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation(EraseTimeout);
if(status == FLASH_COMPLETE)
{
/* Authorize the small information block programming */
FLASH->OPTKEYR = FLASH_KEY1;
FLASH->OPTKEYR = FLASH_KEY2;
/* if the previous operation is completed, proceed to erase the option bytes */
FLASH->CR |= CR_OPTER_Set;
FLASH->CR |= CR_STRT_Set;
while(((FLASH->CR&CR_OPTWRE_Set)!=CR_OPTWRE_Set)){};
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation(EraseTimeout);}
10.2 Flash 操作地址差异
写 Flash,必须采用绝对地址,也就是 0x08000000 为首地址。而对于读操作,既可以使用绝对地址,也可以用相对地址 0x00000000。
获取更多移植文档:https://www.gd32bbs.com/docs/GD32MCU/yizhi.html
PDF下载:
|