项目中有两个产品进行了芯片替换,一个是GDF450ZGT6 “pin to pin” 替换STM32F429VGT6,另一个是GDF450VGT6替换STM32F429ZGT6(硬件开发时做了MCU转接板的设计,方便后续更换MCU)。
总体上,在GD芯片上跑基于CubeMx+HAL开发的驱动和应用程序是没问题的,如CAN、UART、SDIO、ADC、EXTI等模块的使用基本没什么大问题。
但是,有以下几处需要注意的地方:
1、注意GD_FLASH的Data area区
阅读GD芯片的datasheet可以看到它的flash会有两个分区:Code area和Data area。Code area即存放代码的高速运行区,Data area为存放数据的低速运行区。
对比ST,ST的FLASH区域可以认为全是高速运行区,且GD32的高速运行区的运行速度大于STM32的高速运行区(即GD宣传的“GD的Flash执行速度快”),但是GD的Data area区执行速度非常慢,只适合于处理不重要的代码和存储FLASH数据。
芯片选型时一定要注意该Code area分区的大小,如果你的程序大小超过了Code area区的上限,就需要做分散加载(参考GD的“GD32F10x_分散加载使用方法”)。
例如,替换VG芯片时,APP程序的起始位置比较靠后,部分代码被编译到Data area区,就发现一个串口中断的单字节接收老是丢字节,最后发现是Data area执行速度慢的原因,修改keil工程的sct文件-将处理函数的文件编译到Code area区位置就好了。
2、注意ST-HAL库版本的兼容性
这个问题是我自己遇到的,不知道其他人有没有这个问题,网上的资料也比较少。
在“pin to pin”替换STM32F429VGT6时,V1.7.13版本的HAL库发现两个问题:1)CAN初始化失败,2)UART发送和接收脚(K线)短接时会通讯失败-只能发送不能接收,再替换回没问题的V1.17.2版本就没问题了。对比代码,发现最新的HAL库在CAN休眠退出的寄存器操作和串口设置的寄存器做了几处改动,GD芯片的串口寄存器操作与ST存在差异。
3、USB问题
GD芯片的USB使用绝对是个大坑。
有两种解决方案(USB虚拟串口功能):
1)基于ST的HAL库进行使用
这里面又分两个问题:一是USB识别不了,二是USB只能接收和发送前64字节。
a.只能发送和接收64字节的问题,驱动层难以兼容,所以在应用层做了处理,下位机发送时每64字节发一包,上位机或对端发送给下位机时也每64字节发一包。
b.USB识别不了的问题,可以通过添加USB重置来解决,代码如下:
void USB_Reset(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11 | GPIO_PIN_12, GPIO_PIN_RESET);
GPIO_InitStruct.Pin = GPIO_PIN_11 | GPIO_PIN_12;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}
uint8_t GetUsbFSConnectState(void)
{
if(hUsbDeviceFS.dev_state==USBD_STATE_CONFIGURED)
{
return 1;
}
else
{
USBD_Stop(&hUsbDeviceFS);
USBD_DeInit(&hUsbDeviceFS);
USB_Reset();
MX_USB_DEVICE_Init();
}
}
2)基于GD的USB驱动进行使用
由于GetUsbFSConnectState在USB重置时会有2ms的耗时,且USB分包发送无法让每个上位机和对端都这样做,所以决定直接在HAL库的基础上移植GD的USB协议栈。
步骤如下:
1、配置HAL库的USB接口驱动
注意:这里只需要打开USB接口“Device_Only”,不需要再配置USB虚拟串口。
2、移植GD的USB协议栈
添加如下“GD32F4xx_Firmware_Library_V2.0.1\Firmware\GD32F4xx_usb_driver”中的相关源文件到keil目录下。
找到GD-demo工程的“usb_conf.h”、“usbd_conf.h”文件,并修改“usb_conf.h”文件如下。
//1、修改包含的头文件
//#include "gd32f4xx.h"
//#include "gd32f450i_eval.h"
#include "stm32f4xx.h"
//2、添加几个USB协议栈需要用到的定义
#define REG32(addr) (*(volatile uint32_t *)(uint32_t)(addr))
#define REG16(addr) (*(volatile uint16_t *)(uint32_t)(addr))
#define REG8(addr) (*(volatile uint8_t *)(uint32_t)(addr))
#define BIT(x) ((uint32_t)((uint32_t)0x01U<<(x)))
#define BITS(start, end) ((0xFFFFFFFFUL << (start)) & (0xFFFFFFFFUL >> (31U - (uint32_t)(end))))
#define GET_BITS(regval, start, end) (((regval) & BITS((start),(end))) >> (start))
//3、文件末尾__ALIGN_BEGIN 定义后面加上__align(4)
#else
#define __ALIGN_BEGIN __align(4)
#define __ALIGN_END
#endif /* USBHS_INTERNAL_DMA_ENABLED */
#endif /* USB_CONF_H */
修改其他冲突的地方,可直接进行程序编译,逐个消除warning和error,基本都是缺少定义或存在重复定义的问题。
最后一步,在中断中调用GD的中断处理函数入口,mian函数中进行初始化和轮询,进行调用和测试,如下。
usb_core_handle_struct cdc_acm =
{
.dev =
{
.dev_desc = (uint8_t *)&device_descriptor,
.config_desc = (uint8_t *)&configuration_descriptor,
.strings = usbd_strings,
.class_init = cdc_acm_init,
.class_deinit = cdc_acm_deinit,
.class_req_handler = cdc_acm_req_handler,
.class_data_handler = cdc_acm_data_handler
},
.udelay = NULL,
.mdelay = HAL_Delay
};
/**
* @brief This function handles USB On The Go FS global interrupt.
*/
void OTG_FS_IRQHandler(void)
{
/* USER CODE BEGIN OTG_FS_IRQn 0 */
/* USER CODE END OTG_FS_IRQn 0 */
usbd_isr(&cdc_acm);
/* USER CODE BEGIN OTG_FS_IRQn 1 */
/* USER CODE END OTG_FS_IRQn 1 */
}
/**
* @brief The application entry point.
* @retval int
*/
void mian(void)
{
……
MX_USB_OTG_FS_PCD_Init();
usbd_init(&cdc_acm,USB_FS_CORE_ID);
while (usbhs_core_dev.dev.status != USB_STATUS_CONFIGURED) {}
while (1) {
if ((1 == packet_receive) && (1 == packet_sent)) {
packet_sent = 0;
/* receive datas from the host when the last packet datas have sent to the host */
cdc_acm_data_receive(&usbhs_core_dev);
} else {
if (0 != receive_length) {
/* send receive data */
cdc_acm_data_send(&usbhs_core_dev, receive_length);
receive_length = 0;
}
}
}
}
移植好之后,可直接复用packet_receive和packet_sent两个变量,并在对应的处理函数中添加自己的callback。
4、GD串口丢字节、丢数据或乱码问题
丢字节问题除了前面所说的代码编译到data区外,还有可能是芯片兼容性问题。
1) GD移植手册中有说明“GD的MCU和ST的相比在连续发送的时候会多一个IDLE bit。对于客户的应用基本没有影响,只是会影响连续发送的时候的发送时间”。
正巧我们项目中,一个串口的对端通讯(K线)对时序要求比较严格,结果就是串口发出去对方无响应,最后解决方案就是串口字符串发送都改为单字节发送且字节之间添加100微秒左右延时。
2)“GDF4系列的UART判断起始位的时候,16倍过采样,需要16个点全部为0才能确认”。
对于正常串口通讯,像我的项目中用到的蓝牙、4G模组、log等的串口通讯都没有什么问题,但是在K线串口低波特率正常、到高波特率单字节接收方式就是丢字节和乱码。调试了快半个星期才怀疑是这个原因,最后改成DMA接收方式+并且改成8倍过采样后能通过,但是稳定性还需要测试。
具体可参考知乎的一篇文章“GD32F450的USART接收数据错误bug”。
总结,对于时序和协议要求比较严格的串口一定要注意验证和测试通讯速度、稳定性和通讯质量,一般串口则不必(因为在另外一个产品没遇到过异常问题)。