|
和大部分同志一样,楼主也是初次接触GD32的MCU,不过看名字就很亲切,因为他和STM32有着诸多的相似之处,那么就让我们一起来领略一下二者的兼容性吧:
1、 系统
1) 晶振起振区别
描述:启动时间,GD32 与STM32 启动时间都是2ms,实际上GD 的执行效率快,所以ST 的HSE_STARTUP_TIMEOUT ((uint16_t)0x0500)是2ms,但是这个宏定义值在GD 上时间就更加短了,所以要加大这个值的设置。
解决方法:将宏定义:
#define HSE_STARTUP_TIMEOUT ((uint16_t)0x0500)
修改为:
#define HSE_STARTUP_TIMEOUT ((uint16_t)0xFFFF)
备注:启动时间宏定义所在位置:
1、在V3.X 的库,其启动时间宏定义在stm32f10x.h 头文件中(路径:\..\Libraries\CMSIS\CM3)。(库版本的不同,所在目录也有所不同)
2、在V3.0 以前的库,其启动时间宏定义在stm32f10x_rcc.c 源文件中(HSEStartUp_TimeOut)(路径:\..\Libraries\STM32F10x_StdPeriph_Driver\src)。
2) 部分客户使用有源晶振出现问题,在GD32F103 小容量产品,发现会在MCU 的复位管脚一直把电平拉到0.89V,电平不能保持在高电平。
描述:是由于部分有源晶振起振时间太快,复位信号还没有完成导致的
解决方法:就是在有源晶振的输入端与地之前并上一个30pf电容。
3) GD32 MCU 主频支持108MHz 高性能,在代码移植方面需要注意事项
描述:GD32 通过芯片内部加大缓存,提高了相同工作频率下的代码执行速度,带来了高性能的使用体验。
解决方法:因此如果代码有用到for 循环或while 循环语句做精确定时的,定时时间会由于代码执行速度加快而使循环的时间变短。使用Timer 定时器则没有影响。
4) GD32F105/107 系列MCU 配置为108MHz 有何不同
描述:通过Clock configuration register (RCC_CFGR) 中, 第21 : 18 位为PLLMUL[3:0],再结合第29 位PLLMUL[4]组成5 位的位域来确定PLL 倍频系数,即通过软件配置来定义PLL 的倍频系数,且PLL 输出频率绝对不得超过最高主频(108MHz)。
2. 内部Flash
1) 芯片设置读保护用法
描述:由于GD 的Flash 是自己的专利技术,STM 的Flash 是第三方提供的,所以GD 的Flash 和STM 的Flash 有些许差异。GD 的擦除时间会长一点.
解决方法:在写完KEY 序列以后,需要读该位,确认key 已生效。 所以,这里应该插入
While( ! (FLASH->CR & 0x200 ) ); // Wait OPTWRE 或可简单插入两个NOP。
__NOP();
__NOP();
在ST 库中,只有
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)
四个函数需要修改。
2) IAP 在应用中编程
描述:GD32 由于自有flash 的0 访问时序,同STM32 在Flash 的Erase 和Program 上存在差别,GD32 的Erase 和Program 时间比STM32 的稍微长些,建议对Erase 和Program 时间进行修改。
解决方法:将宏定义
#define EraseTimeout ((uint32_t)0x000B0000)
#define ProgramTimeout ((uint32_t)0x00002000)
修改为:
#define EraseTimeout ((uint32_t)0x000FFFFF)
#define ProgramTimeout ((uint32_t)0x0000FFFF)
备注: Erase 和Program 时间宏定义在stm32f10x_flash.c 源文件中
(路径:\..\Libraries\STM32F10x_StdPeriph_Driver\src)
3) 用IAR 下载配置
解决方法:在批量生产的时候首先会烧写一个USB 的boot,这个boot 自动运行后在由上位机软件进行烧写应用程序。如果boot 程序不能自动运行则需要重新插拔一次电源。给生产造成一些麻烦。产生不能自动运行程序的原因是如果程序设置读保护的话需要等待FLASH_CR 的第9[OPTWRE]位为1.如果没有置位的话继续执行就会出错。由于ST 的执行速度慢,程序执行到读FLASH_CR 寄存器的时候该位已经置1,GD 的执行速度比较快,程序运行到这的时候该位还没置1,因此需要在FLASH_ReadOutProtection 函数里面添加一些轮询该位为1 或者加一些延时。
3. ISP 烧写软件
1) ISP 烧写,建议使用官方烧写软件
描述:GD32 芯片内部flash 同STM32 有区别。
解决方法:建议到www.mcuisp.com 下载最新版本的MCUISP。另外GD32 也有专门的烧写软件(GigaDevice MCU ISP Programmer)可以到http://bbs.21ic.com/gd32 论坛下载。如果使用自制的ISP 软件或脱机编程器,实现ST 和GD 完全兼容,建议修改以下参数:
1、 页擦除等待超时时间增加至300ms,整片擦除等待超时时间增加至3s 左右。
2、 字编程等待超时时间增加至2ms,页编程等等超时时间增加至300ms。
4.I/O 口
1) IO 口外部中断使用方法
描述:在关闭期间,如果外部引脚有电平的变化,在使用IMR 打开中断后会马上进入中断服务程序。理论是打开中断前,不管管脚是否有电平的变化,都不会影响到打开后的中断响应。
解决方法:所以解决方法就是通过禁用上升沿或者下降沿检测寄存器来开关中断,不能使用IMR 屏蔽寄存器。程序如下:
EXTI->FTSR &= ~EXTI_Line3; //关闭沿检测,以达到关闭中断的目的,下降沿使用FTSR 寄存器,上升沿使用RTSR 寄存器
EXTI->PR = EXTI_Line3;
EXTI->FTSR |= EXTI_Line3;
2) 在待机模式,PA8 引脚特殊设置
描述:在使用低功耗的情况下,PA8 会被MCU 在内部被设置为地PA8 复用为MCU 内部频率输出,超低功耗设置时需要悬空。
解决方法:在待机模式,PA8 悬空不用。
3) 低功耗下必须注意
描述:在使用低功耗情况下,把软件全部端口(A-F)时钟关掉,无论是否有该端口。
4) 当有脉冲群冲击管脚
描述:需要在在进入中断后关闭中断。
5. 定时器
1) 定时器输入捕获模式需要软件清中断
描述:STM 定时器输入捕获模式默认能硬件清中断,GD 为了更加严格要求配置,需要做软件清中断 。
解决方法:软件清除标志位。
2) 定时器向上脉冲计数模式设置
描述:定时器的用法差异。
解决方法:脉冲计数模式下,装载值必须设置为比预期值大,否则不计数在ST 上如果重载值不设置(初始为0)的时候,CNT 可以正常计数。 在GD 上如果重载值不设置保持初始为0 的时候,会因为重载值为零,即便是来一个脉冲也会导致所有的寄存器复位从而不能正常计数。
3) TIM、ADC 模块
描述:Timer、ADC 模块的触发信号宽度要求。
解决方法:由于内部有高速和低速两条外围总线,Timer、ADC 模块和其他外设共同使用这两个总线。GD32F103/101 系列Flash 128KB 及以下的型号,Timer、ADC 等模块识别触发信号的条件是触发信号宽度大于模块所在总线的时钟宽度。
6. 串口USART
1) USART 连续发送数据字节有空闲位
描述:字节间有空闲位
解决方法:对于一般的通讯来说,不会有影响,只对于一般在通讯上有特殊协议的,才会产生数据不准确的情况所以,特定情况,修改程序。
7. I2C 总线
1) 硬件I2C 特殊配置
描述:GD 的I2C 相对STM 的来说要少一个标志位
解决方法
1、宏地址定义改变
#define I2C_EVENT_SLAVE_TRANSMITTER_ADDRESS_MATCHED
((uint32_t)0x00060002)
#define I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED
((uint32_t)0x00070002)
2、硬件I2C 在会在向从机发送7bits 地址完成后,从机还没来得及识别。(看客户应用)我们可以在发送完7bits 后加个延时,让从机完全识别:
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);
{
int i = 0xfff;
while(i --);
}
3、检测ADDR 不能使用I2C_CheckEvent 函数,因为他会清除ADDR,可以使用I2C_GetFlagStatus 函数。
就是把while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
改为while(!I2C_GetFlagStatus(I2C1, I2C_FLAG_ADDR));
4、还有个关于编程步骤的严谨性,跟STM 想比,我们是先Clear_ACK,再Clear_ARRD。
8. ADC 采集
1) ADC 采样设置
描述:ADC 启动
解决方法:
1. 当ADON=0 时写入1 后,需要等待一段时间t_WAIT,如果用ST 库的话就在ADC_CMD 后面加20us 左右的延时。
2. 如果采用中断获得采样数据后,需要软件清除中断。
9. SDIO
1) SDIO DAT 3 pin 的在 1 bit bus mode 和4 bit bus mode 下的配置
描述:
1、 SDIO 在 1 bit bus mode 下,DAT 3 pin 是低电平,这样会导致 SD Card 进入SPI 模式。
原因:初始化失败的原因主要是因为GD32 的芯片SDIO 的DAT3 口存在BUG。
2、 在4 位模式下,通过上面的方法,程序能正常初始化,但不能正常读写SD卡。
原因:因为DAT3 口在前面已经配置成推挽输出,所以在4 位模式下,不能正常读下。在调用4 位模式前,把DAT3 的端口配置成复用推挽输入即可解决问题。
解决方法 1、 1 bit bus mode 的解决方法:建议在SDIO 使能之前,先把 SDIO DAT 3 pin 配置成推挽输出,并且要置成高电平,使 SDIO DAT 3 pin 保持高电平即可.
2、 4 bit bus mode 的解决方法:在调用4 位模式前,把DAT3 的端口配置成复用输出即可解决问题。
2) 程序在刚烧完后能正常读写SD 卡,断电再上电后,SD 卡初始化失败,需要手动复位一次后才正常
描述 在某些SD 卡中,GD32 断电再上电,会引起SD 卡上的时钟信号不正常,导致SD 卡发送命令失败。
解决方法:在程序中,打开SD 卡时钟后,增加一小段延时,以保证SD 卡时钟信号稳定。这个延时添加的地方:在sdcard.c(即SDIO 的配置文件中),然后在SD_Error SD_Init(void)这个函数中找到SDIO_DeInit();就在这个后面加个延时。
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA2, ENABLE);
SDIO_DeInit();
{
int i = 0xffff;
while(i --);
}
10. USB
A. USB_OTG
1) 客户使用STM32 的DFU 原工程时需要注意几点
解决方法:
1、 在usb_istr.c 中,增加如下语句
for (i=0;i<8;i++) EP = _GetENDPOINT(i);
for (i=0;i<8;i++)
_SetENDPOINT(i, EP & 0x7070);
2、 在usb_conf.h 中,按照下图红色字体语句进行修改
#if defined(STM32L1XX_MD) || defined(STM32L1XX_HD)||
defined(STM32L1XX_MD_PLUS)
#define INTERN_FLASH_SECTOR_ERASE_TIME 100
#define INTERN_FLASH_SECTOR_WRITE_TIME 104
#else
#define INTERN_FLASH_SECTOR_ERASE_TIME 100
#define INTERN_FLASH_SECTOR_WRITE_TIME 100
3、 把固件库中的stm32f10x_flash.c 使用附件的进行替换。
4、 软件进行读保护位时需要选使用FLASH_Unlock();函数
int main(void)
{
#if defined (USE_STM32L152D_EVAL)
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_OPTVERRUSR);
#endif
FLASH_Unlock();
FLASH_ReadOutProtection(ENABLE);
FLASH_Lock();
2) 部分USB 兼容性
解决方法:
1. 部分U 盘有3 个端点,数组越界导致Itf_Desc 被清空,所以主机不能识别设备类型。USBH_conf.h 文件的USBH_MAX_NUM_ENDPOINTS 的定义由2 改成3 就可以了将:#define USBH_MAX_NUM_ENDPOINTS 2
改为:#define USBH_MAX_NUM_ENDPOINTS 3
2. 在In 端点中断处理程序USB_OTG_USBH_handle_hc_n_In_ISR 中,对于NAK 中断,V1.0.0 版本的处理如下:
else if (hcint.b.nak)
{
if(hcchar.b.eptype == EP_TYPE_INTR)
{
UNMASK_HOST_INT_CHH (num);
USB_OTG_HC_Halt(pdev, num);
CLEAR_HC_INT(hcreg , nak);
}
else if ((hcchar.b.eptype == EP_TYPE_CTRL)||
(hcchar.b.eptype == EP_TYPE_BULK))
{
/* re-activate the channel */
hcchar.b.chen = 1;
hcchar.b.chdis = 0;
USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[num]->HCCHAR, hcchar.d32);
}
pdev->host.HC_Status = HC_NAK;
}
而V2.1.0 版本的NAK 处理过程如下:
else if (hcint.b.nak)
{
if(hcchar.b.eptype == EP_TYPE_INTR)
{
UNMASK_HOST_INT_CHH (num);
USB_OTG_HC_Halt(pdev, num);
}
else if ((hcchar.b.eptype == EP_TYPE_CTRL)||
(hcchar.b.eptype == EP_TYPE_BULK))
{
/* re-activate the channel */
hcchar.b.chen = 1;
hcchar.b.chdis = 0;
USB_OTG_WRITE_REG32(&pdev->regs.HC_REGS[num]->HCCHAR, hcchar.d32);
}
pdev->host.HC_Status[num] = HC_NAK;
CLEAR_HC_INT(hcreg , nak);
}
唯一的区别就是CLEAR_HC_INT(hcreg , nak)的位置,在V1.0.0 版本中对于CTRL 和BULK 端点的NAK 中断没有清除NAK,我们的芯片会因此产生多次IN 传输的请求,导致数据传输错 误。改为V2.1.1 的写法后传输正常。(注意HC_Status 在V2.1.0 是数组,在V1.0.0 是单个数据,直接拷贝的话要去掉后面的[num])
B. USB 外设的工作频率有限制 描述:有最低工作频率的要求,也就是APB1 分频后的时钟必须大于12MHz,比如HCLK 为56MHz,APB1 的最大分频系数为4,56/4 = 14MHz,可以正常工作。
11. SPI
1) 输入与输出配置要求(STM32 不需要如此要求)
解决方法 :
GD32 在使用SPI 时,IO 的配置必须严格遵守主从模式下的输入与输出配置,而STM32 无此要求,相关代码如下:主机模式下IO 配置(主机以SPI 为例):
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5|GPIO_Pin_7;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_Init(GPIOA,&GPIO_InitStructure);
从机模式下IO 配置(从机以SPI2 为例):
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_15;
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;
GPIO_Init(GPIOB,&GPIO_InitStructure);
3) 在GD32 的SPI 的时钟信号,空闲状态需要配置成高电平,以保证数据的稳定性,具体代码如下
解决方法
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
4) 当作为从机时,在GD32 中,时钟信号必须为8 的整数倍。
解决方法
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(SPI1, &SPI_InitStructure);
5) 在GD32 中,不能使用SPI_I2S_FLAG_BSY 该位来判断SPI 总线数据是否接收或发送完成。
12. 看门狗
1) 进入STOP 模式前打开看门狗,通过RTC 的ALR 唤醒后,程序会不断被复位的现像
描述:IWDG 内部有个Reload 信号,KEY 寄存器写AAAA 会使其拉高,过一段时间自动拉低。在拉底之前进入STOP 状态会使Reload 信号一直为高,等到退出STOP 后也保持为高,之后再写AAAA 没有办法让Reload 产生上升沿,也就没办法更新计数器了。
解决方法:进STOP 之前不要Reload,也可以调整下程序的顺序,把IWDG 的配置放到RTC配置之前,效果是一样的。
|
|