锐单电子商城 , 一站式电子元器件采购平台!
  • 电话:400-990-0325

STM32到GD32移植攻略

时间:2023-07-26 09:07:00 gd8216电容器

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,电平不能保持在高电平。  描述:部分有源晶振动时间过快,复位信号尚未完成  解决方案:在有源晶振输入端和地面之前pf电容。  3) GD32 MCU 主频支持108MHz 高性能,代码移植需要注意  描述:GD32 在同一工作频率下,通过芯片内部增加缓存,提高代码执行速度,带来高性能的使用体验。  因此,如果代码有用for 循环或while 由于代码执行速度时,由于代码执行速度加快,定时时间会缩短循环时间。使用Timer 定时器没有影响。  4) GD32F105/107 系列MCU 配置为108MHz 有何不同  描述:通过Clock configuration register (RCC_CFGR) 中, 第21 : 18 位为PLLMUL[3:0],结合第299 位PLLMUL[4]组成5 确定位置的位置PLL 倍频系数,即通过软件配置定义PLL 的倍频系数,且PLL 输出频率不得超过最高主频(108)MHz)。 
  1. 内部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)

  1. 用IAR 下载配置

    解决方案:批量生产时,会先烧写一个USB 的boot,这个boot 上位机软件烧写应用程序自动运行后。如果boot 如果程序不能自动运行,则需要重新插拔电源。给生产带来一些麻烦。不能自动操作程序的原因是,如果程序设置了读取保护,则需要等待FLASH_CR 的第9[OPTWRE]1.如果没有位置,继续执行会出错。由于ST 执行速度慢,程序执行到阅读FLASH_CR 该位置在寄存器时已放置1,GD 执行速度相对较快,当程序运行到这个位置时,该位置尚未设置1,因此需要在FLASH_ReadOutProtection 在函数中添加一些轮询,位于1 或者添加一些延迟。

  1. ISP 烧写软件

    1. ISP 建议使用官方烧写软件

    描述:GD32 芯片内部flash 同STM32 有区别。

    解决方案:建议到www.mcuisp.com 下载最新版本MCUISP。另外GD32 还有专门的烧写软件(GigaDevice MCU ISP Programmer)可以到http://bbs.21ic.com/gd32 论坛下载。若使用自制的ISP 实现软件或脱机编程器ST 和GD 建议修改以下参数:

    1、 页擦等待时间增加到3000ms,整个擦除等待时间增加到3s 左右。

    2、 字编程等待时间增加到2ms,超时时间增加到3000ms。

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)时钟关闭,不管是否有端口。 
  1. 脉冲群冲击管脚时

    描述:中断后需要关闭中断。

  1. 定时器

    1. 定时器输入捕获模式需要软件清中断

    描述:STM 默认情况下,定时器输入捕获模式可以中断硬件清,GD 为了对配置有更严格的要求,进行软件清中断 。

    解决方案:软件清除标志位。

    1. 设置定时器向上脉冲计数模式

    描述:定时器的用法差异。

    解决方案:在脉冲计数模式下,装载值必须设置为大于预期值,否则不计数ST 如果不设置重载值(初始为0),CNT 能正常计数。 在GD 如果不设置重载值,保持初始0 当重载值为零时,即使是脉冲也会导致所有寄存器复位,无法正常计数。

    1. TIM、ADC 模块

    描述:Timer、ADC 触发触发信号宽度要求。

    解决方法:由于内部有两条外围总线:高速和低速,Timer、ADC 这两条总线由模块和其他外设共同使用。GD32F103/101 系列Flash 128KB 以下型号,Timer、ADC 模块识别触发信号的条件是触发信号宽度大于模块所在总线的时钟宽度。

  2. 串口USART

    1. USART 连续发送数据字节有空闲位置

    描述:字节间有空闲位置。

    解决方案:对于一般通信,不会有影响,只有对于一般通信有特殊协议,会产生数据不准确,因此,具体情况,修改程序。

  3. I2C 总线

    1. 硬件I2C 特殊配置

    描述:GD 的I2C 相对STM 对于少一个标志位

    解决方法

    1.宏地址定义变更

#define I2C_EVENT_SLAVE_TRASMITTER_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。
  1. ADC 采集

    1. ADC 采样设置

    描述:ADC 启动

    解决方法:

    1. 当ADON=0 时写入1 后,需要等待一段时间t_WAIT,如果用ST 库的话就在ADC_CMD 后面加20us 左右的延时。

    2. 如果采用中断获得采样数据后,需要软件清除中断。

  2. 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 的端口配置成复用输出即可解决问题。

    1. 程序在刚烧完后能正常读写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 --);

}

  1. USB

    A. USB_OTG

    1. 客户使用STM32 的DFU 原工程时需要注意几点

    解决方法:

    1、 在usb_istr.c 中,增加如下语句

for (i=0;i<8;i++) EP[i] = _GetENDPOINT(i);

for (i=0;i<8;i++)

_SetENDPOINT(i, EP[i] & 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,可以正常工作。
  1. 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 总线数据是否接收或发送完成。
  1. 看门狗

    1. 进入STOP 模式前打开看门狗,通过RTC 的ALR 唤醒后,程序会不断被复位的现像

    描述:IWDG 内部有个Reload 信号,KEY 寄存器写AAAA 会使其拉高,过一段时间自动拉低。在拉底之前进入STOP 状态会使Reload 信号一直为高,等到退出STOP 后也保持为高,之后再写AAAA 没有办法让Reload 产生上升沿,也就没办法更新计数器了。

    解决方法:进STOP 之前不要Reload,也可以调整下程序的顺序,把IWDG 的配置放到RTC配置之前,效果是一样的。

锐单商城拥有海量元器件数据手册IC替代型号,打造电子元器件IC百科大全!

相关文章