GD32F450替代STM32F429,详细对比

GD32F450

Updated on July 30, 2021 Posted by elmagnifico on June 16, 2021

Foreword

主要是想用GD32F450替代STM32F429,GD32F4xx号称可以替代STM32F4xx,但是实际上可能有一些细节不同,这里做个详细对比。

网上有一个兼容性说明文档传播比较广泛,我也按图索骥先试了试

GD32F450和STM32F4xx兼容性说明.pdf

粗玩

直接用CubeMX 配置STM32F429,然后无论是SD卡,FATFS,还是FreeRTOS都可以做到烧上就能跑,就能用的程度,但是有一点这里只是普通的替代,核心频率也只用到168MHz。这里工程所有配置都是STM32F429的,一点GD都不带。

原功能级别的替代基本可行,有问题的地方在于如果想要用GD的200MHz,那就不一样了,有些细节不同

硬件引脚对比

引脚编号 ST GD
49 NC VCAP_1
73 NC VCAP_2

对于LQFP100来说,只有49,73是标识明显不同的,都是电源相关的内容,而他们二者本身电源上也有微小的耐压区别。实际上可能由于有些引脚的特殊功能、复用等情况,还有更多的不同,那要等我用过可能才会发现

从表面看,直接替换是没问题的。

内存映射对比

当然这里有可能是名字不同,但是功能上是相同的。GD为了避免侵权之类的,文档里搞了一堆保留什么的,然后外设的索引从0开始,而ST的从1开始,看的非常蛋疼。

EXMC部分跳过,由于没用过,具体地址如下

0xC000 0000 - 0x6FFF FFFF

ST的FSMC在GD这里直接没有,所以这部分地址就保留了

0xA000 1000 - 0xBFFF FFFF

在AHB2中,不同的地方非常多,也可以认为是GD做了阉割,去掉了一些外设。

但总的来说保留了 RNG、DCI和USBFS。

下面是AHB1中的不同点,主要是多2组IO,剩下的应该是名称上的差异

地址 ST GD
0x4002 B000 - 0x4002 BBFF DMA2D IPA
0x4002 3C00 - 0x4002 3FFF Flash interface register FMC
0x4002 2800 - 0x4002 2BFF GPIOK
0x4002 2400 - 0x4002 27FF GPIOJ

下面是APB2中的不同点

地址 ST GD
0x4001 6800 - 0x4001 6BFF LCD-TFT TLI
0x4001 5800 - 0x4001 5BFF SAI1
0x4001 2400 - 0x4001 2BFF  

下面是APB1中的不同点

地址 ST GD
0x4000 C400 - 0x4000 C7FF IREF
0x4000 6C00 - 0x4000 6FFF CTC
0x4000 4000 - 0x4000 43FF I2S3ext I2S2_add
0x4000 3400 - 0x4000 37FF I2S2ext I2S1_add

SRAM对比

地址 ST GD 大小 说明
0x2000 0000 - 0x2001 BFFF SRAM1 SRAM0 112KB  
0x2001 C000 - 0x2001 FFFF SRAM2 SRAM1 16KB  
0x2002 0000 - 0x2002 FFFF SRAM3 SRAM2 64KB  
0x2003 0000 - 0x2006 FFFF ADDSRAM 256KB  
0x2007 0000 - 0x3FFF FFFF   保留    

需要注意GD比ST多了256KB的内存

GD的TCMSRAM在ST中叫CCM,64KB,这块内存只能被DBUS访问。

boot 对比

boot上基本保持了一致

外设对比

外设的属性上不同,主要是外设的寄存器相关配置可能不同

系统配置寄存器

地址偏移 ST GD
0x04 SYSCFG_PMC SYSCFG_CFG1

这个寄存器只有Ethernet的选择bit一样,ST还有一个ADCxDC2,而GD没有

Flash

GD的Flash不太一样,如果只有1M大小那就只有bank0,而没有bank1。就算2M或者3M,他的两个bank的大小还是不对称的。如果用到了升级备份之类的功能,那就要注意一下。

Flash中的额外区域,比如opt或者系统内置的boot块,这些大小也都有所不同

地址偏移 ST GD
0x00 FLASH_ACR FMC_WS

延迟状态寄存器,ST由这里多了好几个关于cache的控制位,而GD直接没有

GD和ST解锁Flash的key 都是0x45670123和0xCDEF89AB,只是GD为了规避写在了前面的说明里,而寄存器说明里啥都不写,真有你的

地址偏移 ST GD
0x0C FLASH_SR FMC_STAT

状态位中主要是ST的bit5不同,GD是保留,ST是写入没对齐的错误标志

地址偏移 ST GD
0xFC   FMC_WSEN

GD多一个FMC的状态寄存器,没啥大用

地址偏移 ST GD
0x100   FMC_PID

GD多一个产品保留ID的寄存器

电源管理

电源这里有很大不同,ST有PVD,作为低电处理的模块,对应GD叫做LVD。

地址偏移 ST GD
0x00 PWR_CR PMU_CTL

bit13不同,GD没有这一位,ST是和ADC相关的配置

bit9不同,ST是用于设置flash进入断电模式,而GD没有

地址偏移 ST GD
0x04 PWR_CSR1 PMU_CS

bit8,ST保留了,GD是关于唤醒引脚的使能

复位与时钟管理

时钟是二者最大的不同。GD450可以跑到200Mhz,而且根据其兼容性说明文件,可以知道他专门为了跑200Mhz,给USB等设备的48Mhz专门给了一个设置,而对应的ST这里只有跑168的时候才能用48Mhz,跑满180的时候则是无法使用USB等设备

仔细看时钟配置发现,GD这里多给了一个内部时钟,这个IRC是48MHz的,但是和其他内部时钟一样,他的精度不高,1%,用在USB或者SD卡的时候这个误差太大了。所以需要校准一下,而这个校准简单说就是通过和外部晶振一起计数比较,然后算出来内部时钟应该慢多少或者快多少,所以GD比ST多了一个控制器,用于校准时钟。

CTC 时钟校准控制器

这个校准还好有一个一键硬件校准的,不然真的麻烦,所以如果要用最好是先进行一次时钟校准,然后再进行USB等通信操作。

对应的CTC的校准寄存器是GD独有的,ST没有这部分功能.

PLLSAI

PLLSAI是时钟这里最大的不同,他专门用来给USB和SDIO提供48M的时钟

    //如果这里PLLQ是48M直接用下面这句就行了
	rcu_pll48m_clock_config(RCU_PLL48MSRC_PLLQ);
	// 但是一般情况下都会选择跑满,那么PLLQ就不能用了,这里就切换成PLLSAIP
    rcu_pll48m_clock_config(RCU_PLL48MSRC_PLLSAIP);

    rcu_ck48m_clock_config(RCU_CK48MSRC_PLL48M);

    rcu_periph_clock_enable(RCU_USBFS);
地址偏移 ST GD
0x08 RCC_CFGR RCU_CFG0

这个寄存器是一样的,但是中文版中有写错的地方,我对比看了下英文版这里是正确的

image-20210616162653632

APB2PSC应该是有3位,也就是15:13的分频位

地址偏移 ST GD
0x10 RCC_AHB1RSTR RCU_AHB1RST

这里略有不同,因为ST多了两组IO,所以这里就多了对应的复位BIT10和9

地址偏移 ST GD
0x14 RCC_AHB2RSTR RCU_AHB2RST

这里也是一样,ST多了HASH,CRYP的功能,对应多了BIT5和4

地址偏移 ST GD
0x30 RCC_AHB1ENR RCU_AHB1EN

GPIO的问题,同上

地址偏移 ST GD
0x34 RCC_AHB2ENR RCU_AHB2EN

同上

后面还有一些GPIO和多出来的功能的不同,就不列出来了,都是对应的寄存器

地址偏移 ST GD
0x70 RCC_BDCR RCU_BDCTL

这里GD多了一位BIT3,是对晶振驱动力高低的备份

中文还有一处错误这里保留的应该是28和29

image-20210616164044673

下面接着出错了

image-20210616164203725

地址偏移 ST GD
0x84 RCC_PLLI2SCFGR RCU_PLLI2S

ST的BIT27:24是PLLI2SQ的,而GD由于没有SAI1直接保留了。同理造成下面的RCC_PLLSAICFGR (RCU_PLLSAI),RCC_DCKCFGR (RCU_CFG1)不同

地址偏移 ST GD
0x88 RCC_PLLSAICFGR RCU_PLLSAI

但是这里GD多了BIT16和17,用来处理PPSAIP的分频

image-20210616170121386

这里可以看到,CK_PPLSAIVCO是来自于CK_ PLLSAIVCOSRC × PLLSAIN

CK_PLLSAIVCOSRC = CK_PLLSRC / PLLPSC
CK_PLLSAIVCO = CK_ PLLSAIVCOSRC × PLLSAIN
CK_PLLSAIP = CK_ PLLSAIVCO / PLLSAIP
CK_PLLSAIR = CK_ PLLSAIVCO / PLLSAIR

其中CK_ PLLSAIVCOSRC我们一般都配置成了1MHz,要配置48的话,就全靠PLLSAIN了。

这里需要注意一下PLLSAIN是不能设置为50MHz以下的,所以一般PPLSAIN设置为96

然后CK_PLLSAIVCO就是96MHz,PPLSAIP设置为00,对应2分频,那么得到的CK_PLLSAIP就是48MHz了

地址偏移 ST GD
0x8C RCC_DCKCFGR RCU_CFG1

这里不同的主要是ST多了SAI1,PLLSAIDIVQ和PLLS2DIVQ

附加寄存器

再往下ST已经没有寄存器,剩下就是GD单独附加的寄存器了

地址偏移 ST GD
0xC0   RCU_ADDCTL

RCU_ADDCTL主要是用来设置多出来的一个48M的时钟源,并且多出来的一个外部时钟IRC48M的也在这里控制

地址偏移 ST GD
0xCC   RCU_ADDINT

对应ICR48M的中断寄存器

地址偏移 ST GD
0xE0   RCU_ADDAPB1RST

时钟校准和编程电流复位寄存器

地址偏移 ST GD
0xE4   RCU_ADDAPB1EN

上面的使能寄存器

地址偏移 ST GD
0xE8   RCU_ADDAPB1SPEN

上面的睡眠寄存器

地址偏移 ST GD
0x100   RCU_VKEY

电源解锁寄存器

地址偏移 ST GD
0x134   RCU_DSV

深度睡眠的寄存器,主要是设置深度睡眠下的内核电压值

总结一下独立设置USB和SDIO的48M时钟
// 配置SAI分频 构造48M时钟
rcu_pllsai_config(96, 2, ???, 2);
// 切换到SAIP
rcu_pll48m_clock_config(RCU_PLL48MSRC_PLLSAIP);
// 选择时钟源
rcu_ck48m_clock_config(RCU_CK48MSRC_PLL48M);
// 使能USB时钟
rcu_periph_clock_enable(RCU_USBFS);

额 我配置的时候发现一个错,实际手册里根本没有SAIQ的位,但是代码里却配置了,所以有错

/*!
    \brief      configure the PLLSAI clock 
    \param[in]  pllsai_n: the PLLSAI VCO clock multi factor
      \arg        this parameter should be selected between 50 and 500
    \param[in]  pllsai_p: the PLLSAI P output frequency division factor from PLL VCO clock
      \arg        this parameter should be selected 2,4,6,8
    \param[in]  pllsai_q: the PLLSAI Q output frequency division factor from PLL VCO clock
      \arg        this parameter should be selected between 2 and 15
    \param[in]  pllsai_r: the PLLSAI R output frequency division factor from PLL VCO clock
      \arg        this parameter should be selected between 2 and 7
    \param[out] none
    \retval     ErrStatus: SUCCESS or ERROR
*/
ErrStatus rcu_pllsai_config(uint32_t pllsai_n, uint32_t pllsai_p, uint32_t pllsai_q, uint32_t pllsai_r)
{
    /* check the function parameter */

这部分配置我看了官方的固件库或者例程,都找不到任何一个在200MHz下使用的情况,全都是直接配好了PLL48M,不存在这种单独配置的情况。

USB

由于USB一直出错,所以这里先对比一下看看是否有不同,USB太复杂了,里面这么多不同,最终影响库的使用到底有多少谁也不知道。

地址偏移 ST GD
0x0C OTG_FS_GUSBCFG USBFS_GUSBCS

BIT31 在ST这边是用来debug,GD则是保留

BIT6 在ST这里是总是1,用来,USB全速发送,但是GD保留

地址偏移 ST GD
0x010 OTG_FS_GRSTCTL USBFS_GRSTCTL

BIT31 在ST这边是AHB的状态,GD是保留

地址偏移 ST GD
0x01C OTG_FS_GRSTCTL USBFS_GRSTATR/USBFS_GRSTATP

这个寄存器分为Host模式和Device模式,在Device模式中有所不同

BIT24-21在ST中是帧个数,而GD直接没有,这个位是只有同步输出才有用

地址偏移 ST GD
0x024 OTG_FS_GRXFSIZ USBFS_GRFLEN
0x028 OTG_FS_DIEPTXF0 USBFS_HNPTFLEN _DIEP0TFLEN

这个里面Rx的FIFO最大深度在ST中是256,而GD中则是1024

同理,发送深度也是这种情况。不过在Device模式中,GD发送最大是140,而ST是256

地址偏移 ST GD
0x038 OTG_FS_GCCFG USBFS_GCCFG

BIT19和18 是Vbus比较使能的位,在ST这边是如果使用了就用,没用就没用。但是GD这里是不管你用没用都得置位,否则直接无法正常工作,代码如下

    USBx->GCCFG |= USB_OTG_GCCFG_VBUSBSEN;
    USBx->GCCFG |= USB_OTG_GCCFG_VBUSASEN;
地址偏移 ST GD
0x804 OTG_FS_DCTL USBFS_DCTL

BIT6-4里ST是测试模式,而GD保留

地址偏移 ST GD
0x808 OTG_FS_DSTS USBFS_DSTAT

BIT3在ST中不稳定误差的显示,而GD中保留

BIT0 在ST中没有明确说到底0是挂起还是非挂起,但是从代码里看到,1是挂起,0是没挂起

GD这里是相反的,GD明确说了0是挂起状态,1是非挂起状态 一定要注意,库里这个地方设置的是相反的。

地址偏移 ST GD
0x810 OTG_FS_DIEPMSK USBFS_DIEPINTEN

BIT5 ST中是EP不匹配错误,GD保留

BIT13 ST中是NAK中断,而GD直接没有

地址偏移 ST GD
0x814 OTG_FS_DOEPMSK USBFS_DOEPINTEN

BIT5 ST中状态解析,而GD保留

BIT6 GD是连续setup包的中断,而ST中没有

BIT8-13 ST有而GD保留

地址偏移 ST GD
0x818 OTG_FS_DAINT USBFS_DAEPINT
0x81C OTG_FS_DAINTMSK USBFS_DAEPINTEN
0x0834 OTG_FS_DIEPEMPMSK USBFS_DIEPFEINTEN

这里ST无保留,而GD保留了一部分端点的中断位

地址偏移 ST GD
0x908+x*0x20 OTG_FS_DIEPINTx USBFS_DIEPxINTF

这里BIT13在ST是NAK,11是丢包状态,而GD则没有这几位

地址偏移 ST GD
0xB08+x*0x20 OTG_FS_DOEPINTx USBFS_DOEPxINTF

BIT5在ST是状态解析,而GD保留

BIT6在ST保留,GD中式连续setup包

BIT13在ST是NAK,而GD则没有

BIT11是丢包状态,而GD则没有

地址偏移 ST GD
0x910+x*0x20 OTG_FS_DIEPTSIZx USBFS_DIEPxLEN

BIT30-29在GD中是每帧多包数目,而ST没有

地址偏移 ST GD
0xE00 OTG_FS_PCGCCTL USBFS_PWRCLKCTL

BIT4 ST中多了物理挂起的选项,但是GD中没有

问题总结

目前已经发现的GD的问题

  1. SD时钟开启响应延迟较高
  2. USB和SDIO的48M时钟源配置有错
  3. 中文文档中存在错误,据说中文是从英文翻译过来的(我之前不确定他们优先中文还是英文,一起看的)
  4. 固件库例程真的不行(用个I2C还是GPIO模拟的,丢人不丢人?各种你想用的驱动例程里都是最菜的那种,就不能发挥一下硬件的作用嘛)
  5. USB中的DSTAT和GCCFG寄存器和预期的不相同,直接影响了usb设备能否正常工作。

其他

之前有说过F7中的Timer DMA PWM中有bug,只能同时启动一路。

http://elmagnifico.tech/2020/06/03/Dshot-STM32-PWM-HAL/

这次看F4的库,发现这里这个判断修改了,之前F7这里判定的是Timer本身的状态,而这里则是判定每个channel的状态。

HAL_StatusTypeDef HAL_TIM_PWM_Start_DMA(TIM_HandleTypeDef *htim, uint32_t Channel, uint32_t *pData, uint16_t Length)
{
  uint32_t tmpsmcr;

  /* Check the parameters */
  assert_param(IS_TIM_CCX_INSTANCE(htim->Instance, Channel));

  /* Set the TIM channel state */
  if (TIM_CHANNEL_STATE_GET(htim, Channel) == HAL_TIM_CHANNEL_STATE_BUSY)
  {
    return HAL_BUSY;
  }
  else if (TIM_CHANNEL_STATE_GET(htim, Channel) == HAL_TIM_CHANNEL_STATE_READY)
  {

GD可以高达200MHz,但是实际使用的时候就会发现有些情况这个200MHz有点坑爹。比如DSHOT的频率和GD的时钟频率就匹配不上(3和10互质),怎么用都会出现时钟频率不理想。这种情况下只好降频,降到192MHz,这样各种时钟频率都处于一个比较ok的状态。而192还有一个好处,就是usb的频率都不需要切换到PLLSAI了,直接分频就能得到合适的48MHz。

有时候想想,发现这些板子的总时钟频率还真不能瞎选,可能你选的不合适,一味追求高,导致实际上有些地方分频怎么分也得不到合适的值,这时候就只能降频了。

Summary

目前只总结了这么多,后续随着使用继续更新。

还是想吐槽一下,GD还是不行,F4的固件库上次更新是2019年3月,2年了没有任何更新,从某种程度上说这个系列的产品也快淘汰了。

反观ST,F4最近一次更新是20年9月,F7最近一次更新是20年12月,人一年至少更新一次,虽然不是啥大更新,但是说明这玩意还有人维护啊。

F4和F7差异

最后GD的450还是放弃掉了,因为本来是想替代F7系列的,虽然CoreMark跑分上相差不是很大,但是实际应用的时候不只是跑分,中断响应时间也非常重要,而这种指标在跑分上看不出来,F4系列中断响应比F7慢很多,当然也有一些其他因素,比如cache这种高级货只有F7才有,比如我们系统开销比较高,F4跑不过来。最终导致,450不能满足我们的使用吧。

此文的更新大抵也就到这里了

Quote

https://www.cirmall.com/bbs/thread-95530-1-1.html

https://www.cnblogs.com/zwj412/p/10026393.html