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 |
这个寄存器是一样的,但是中文版中有写错的地方,我对比看了下英文版这里是正确的
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
下面接着出错了
地址偏移 | 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的分频
这里可以看到,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的问题
- SD时钟开启响应延迟较高
- USB和SDIO的48M时钟源配置有错
- 中文文档中存在错误,据说中文是从英文翻译过来的(我之前不确定他们优先中文还是英文,一起看的)
- 固件库例程真的不行(用个I2C还是GPIO模拟的,丢人不丢人?各种你想用的驱动例程里都是最菜的那种,就不能发挥一下外设的作用嘛)
- 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