软件移植注意事项
1.1系统方面的注意事项
1.1.1 HSE相关内容修改
在V3.x的库,启动时间宏定义在xxx32f10x.h头文件中;
在 V3.0 以 前 的 库 , 其 启 动 时 间 宏 定 义 在 xxx32f10x_rcc.c 中
(HSEStartUp_TimeOut);
修改前:
//#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 */
修改原因:
GD和STM32的晶振部分电路设计有一定的差异,两者对外部高速晶振的参数要求
也不一样,
修改HSE_STARTUP_TIMEOUT宏定义可以保证晶振正常起振。当然你会在应用
中发现有一些应用不修改也能照常跑,这是由于晶振的参数差异造成了,为了保
证程序的正常运行还是修改该宏定义。
1.1.2 代码执行速度方面的修改
GD32采用专利技术,提高了相同工作频率下的代码执行速度,带来了高性能的使
用体验。这样一些在ST下面编写的程序如While或者是For循环的延时,移植到GD
上面来肯定相应的延时会变短。所以如果客户的应用有用到这种延时方法的得根
据实际情况进行一定的调整。GD的代码执行速度比ST更快,那么在客户的应用
中如果有一些判断的结构不够严谨也可能会导致问题。
案例1:
客户在软件中编写了一个延时函数如下:
void delay(void)
{
U8 I;
For(i=0;i<75;i++);
}
通过实测相同的这一段代码:
ST执行该函数的延时时间是7.4us
GD执行该函数的延时时间是5.4us。
如果客户的应用对时间要求比较严格请不要忽略GD代码执行速度的问题,参数需
要做一定的调整。
案例2:
客户采用IO模拟I2C他的查应答函数的编写如下
#define SDA_Status() GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1)
Void CheckACK(void)
{
cAcknowledge=TRUE
if(SDA_Status())
{
cAcknowledge=FalSE;
}
}
客户反馈这段代码在ST上面执行OK,但是在GD上面运行不正常,其实这是由于
GD的执行速度更快,ACK信号还出来,语句就已经执行完成了。建议客户修改代
码:
#define SDA_Status() GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1)
Void CheckACK(void)
{
U8 ErrTimer=0;
cAcknowledge=TRUE
while(SDA_Status())
{
ucErrTime++;
if(ucErrTime>250)
{
cAcknowledge=FalSE;
}
}
}
代码修改后客户的问题得以解决,使用ST没有问题是因为ST的代码执行速度没
有GD的快,刚好客户的代码能满足他的应用,换了GD以后由于代码执行速度更
快,只能通过更严谨的逻辑来进行代码的移植。
1.1.3 BOOT管脚注意事项
BOOT0及BOOT1管脚在芯片复位时的电平状态决定了复位后从哪个区域开始执行程序。典型情况下,BOOT0需
外接10K下拉电阻,就可以从Flash启动。如BOOT0悬空,则不能从Flash启动。
1.2GPIO方面说明
1.2.1 EXTI中断相应异常
如果EXTI配置好后关闭EXTI,IO有沿跳变,再次打开EXTI时,系统会响应EXTI关闭过
程中的外部触发,(小容量存在该问题,大容量没有)。
软件可以参考以下例程设置IMR 屏蔽寄存器来使能中断 (这里以 EXTI 7 为例):
使用上升沿触发:
{ /* Clear Rising Falling edge configuration */
EXTI->RTSR &= ~EXTI_Line7; /* Clear EXTI line configuration */
EXTI->IMR |= EXTI_Line7 ;
EXTI->EMR |= EXTI_Line7 ; /* Rising Falling edge */
EXTI->RTSR |= EXTI_Line7; }
使用下降沿触发:
{ /* Clear Rising Falling edge configuration */
EXTI->FTSR &= ~EXTI_Line7;
/* Clear EXTI line configuration */
EXTI->IMR |= EXTI_Line7 ;
EXTI->EMR |= EXTI_Line7 ; /* Rising Falling edge */
EXTI->FTSR |= EXTI_Line7; }
使用上升沿和下降沿同时触发:
{
/* Clear Rising Falling edge configuration */
EXTI->RTSR &= ~EXTI_Line7;
EXTI->FTSR &= ~EXTI_Line7;
/* Clear EXTI line configuration */
EXTI->IMR |= EXTI_Line7 ;
EXTI->EMR |= EXTI_Line7 ;
/* Rising Falling edge */
EXTI->RTSR |= EXTI_Line7;
EXTI->FTSR |= EXTI_Line7;
}
1.2.2 PA8和低功耗关系
PA8管脚如果外接上拉电阻,会导致在standby模式下有较大的漏电流,会增加
standby模式下的功耗,但是对stop模式下的功耗无影响。因此如果要获得比较低的功耗。
PA8 管脚要保持悬空或者是下拉,或者在进入standby模式之前,断开PA8 管脚与外部的
连接。
1.2.3 PB1和PB2关系
PB1设置成IPU IPD AF_PP,AF_OD四种模式的时候,PB2不受控。为了能正常使用
PB2,PB1必须配制成Out_PP, AIN, FLOATING;
1.2.4 PA8上电状态
当PA8设置为输出高电平时,开机的第一次翻转会比ST的慢2.5ms左右,
1.3定时器方面的说明
1.3.1 TIM 中断标志位清除问题
当TIM的Channel 配置成Input Capture mode时,GD32F103 reading the TIMx_CCRx
寄存器不会自动清除TIMx_SR寄存器的CcxIF标志,该中断标志位需要软件清除。
1.3.2 TIM 输入捕获/正交编码配置问题
有客户反馈使用GD的MCU,用定时器工作在正交编码或者是输入捕获不能工作,
同一程序ST的工作正常。检查客户的程序,发现客户编程的时候没有配置定时器的周期,
而ST的定时器不配置定时器的周期的话会给出一个默认值,GD不会。在程序中加上配置定
时器周期的语句就OK了。
TIM_TimeBaseStructure.TIM_Period = 0xffff;
TIM_TimeBaseStructure.TIM_Prescaler =0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIMx, &TIM_TimeBaseStructure);
1.4 ADC方面的说明
1.4.1 ADC输入通道模式配置
使用GD的MCU的时候,输入通道必须配制成模拟输入的模式GPIO_Mode_AIN,配
制成其他模式不能正常工作。
1.4.2 ADC时钟配置问题
使用GD的MCU的时候,需要ADC的采样配置时钟(特别是小容量,一定要配置采样时
钟),
而且不能大于14M Hz。采样周期配置如下:
RCC_ADCCLKConfig(RCC_PCLK2_Divx);
1.4.3 ADC输入阻抗和采样周期选择
从ST移植到GD,使用到ADC的话需要根据具体情况相应的修改采样周期。具体参数见下表:
GD32 STM32
1.4.4 ADC_CR2中的ADON使用方法
当ADON=0时写入1后,需要等待一段时间t_WAIT,才能进行后续操作。
t_WAIT的计算公式如下:t_WAIT≥14×t_ADCClk。参见AN003文档 Section 2.2。
小容量的芯片需要注意这个问题,大容量的这个问题当前已经修改过
1.4.5 ADC_SR中的EOC标志位使用
GD32F103/101系列Flash 128KB 及以下的型号,在ADC的Regular group或Injected
group转换完成时,由硬件自动置1;由软件写入0清除该位。参见AN003文档 Section 3。
1.6 RTC方面的说明
1.6.1 RTC硬件方面问题
GD的RTC正常使用起来和ST的没有差别,但是客户使用的过程中经常会在设计硬件的
时候增加一个电阻或者是不加电容等问题,所以关于RTC方面的设计请严格参照下面的原
理图:
1.7 SPI方面的说明
1.7.1 CLK线重复配置
有客户反馈当SPI处于主模式下,重复配置SPI的CLK线会引起内部计数器紊乱:
问题代码:GPIO->CRL&=0x000fffff;
GPIO->CRL|=0xBBB00000;
上面这段代码是执行SPI口的IO初始化,调用两次后会使CLK上面产生一个脉冲。引起内部
计数器出错,ST的在SPI再次初始化的时候会将内部计数器清零,GD不会将内部计数器清
零。
修改办法: vu32 temp;
temp=GPIOA->CFL;
temp&=0x000fffff;
temp|=0xbbb00000;
GPIOA->CRL=temp;
先进行运算再去进行赋值,内部的SPI 计数器就不会因为CLK数据的变化引起误操作。
1.7.2 SPI 通信BSY标志位
在SPI程序编写的过程中,轮询使用BSY作为检测标志位,传送数据丢失或者是错误。
这是因为GD的BSY标志位不是在写入DR后就置位的,而是有很小的一个延时后才置起的。
传输过程中不要使用BSY作为每次传输的判断,使用TXE和 RXNE来进行判断。
1.7.3 SPI 控制寄存器1(SPI_CR1)配置流程
客户如果从低到高一位一位配置该寄存器会出现MODF的错误,经过排查是由于GD的
MCU在SPE使能之前就开始检测是否有模式错误,而ST是在SPE使能之后,所以在MSTR
写1之前要保证模式正确。SSM为0时保证外接高电平或者SSM和SSI都写1或者是把SSOE
打开。130\150没有该问题.
案例:BIDIMODE=0; RXONLY=0;MSTR=1;DFF=0;CPOL=0;CPHA=0;SSM=1;
SSOE=1;TXEIE=1;BR=2;LSBFIRST=0;CRCPOLY=7;SPE=1;
这个顺序在ST上正常,在GD上出现MODF错误,解决的话可以将整个寄存器一次写入,
也可调整初始化顺序,同时加上SSI的配置。
1.7.4 SPI CLK时钟线配置
小容量的产品,CK线需要将IO配置成input_floating,才能正常工作,大容量不需要这样配置。
1.8 I2C方面的说明
1.8.1 查询法I2C寻址判断
在使用I2C编程的时候需要修改固件库stm32f10x_i2c.h中的两个宏定义
#define I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED
((uint32_t)0x00060082)
#define I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED
((uint32_t)0x00070082)
修改为:
#define I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED
((uint32_t)0x00060002)
#define I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED
((uint32_t)0x00070002)
1.8.2 函数I2C_CheckEvent修改
使用V3.5.0之前的固件库需要修改I2C_CheckEvent函数。
修改前:
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t lastevent = 0;
uint32_t flag1 = 0, flag2 = 0;
ErrorStatus status = ERROR;
/* Check the parameters */
assert_param(IS_I2C_ALL_PERIPH(I2Cx));
assert_param(IS_I2C_EVENT(I2C_EVENT));
/* Read the I2Cx status register */
flag1 = I2Cx->SR1;
flag2 = I2Cx->SR2;
flag2 = flag2 << 16;
/* Get the last event value from I2C status register */
lastevent = (flag1 | flag2) & FLAG_Mask;
/* Check whether the last event contains the I2C_EVENT */
if (lastevent == I2C_EVENT)
{
/* SUCCESS: last event is equal to I2C_EVENT */
status = SUCCESS;
}
else
{
/* ERROR: last event is different from I2C_EVENT */
status = ERROR;
}
/* Return status */
return status;
}
修改后:
ErrorStatus I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
{
uint32_t lastevent = 0;
uint32_t flag1 = 0, flag2 = 0;
ErrorStatus status = ERROR;
/* Check the parameters */
assert_param(IS_I2C_ALL_PERIPH(I2Cx));
assert_param(IS_I2C_EVENT(I2C_EVENT));
/* Read the I2Cx status register */