本項目是基于兆易創新GD32F527微控制器構建了一款CAN數據記錄儀。
整個工程的源碼下載:https://download.csdn.net/download/u010261063/92196886?spm=1001.2101.3001.9500
該項目實現can總線數據的監視并保存在文件系統的功能,并提供時間戳的功能。
項目的使用環境就是在實際的項目中,可能需要分析can通訊的數據是否滿足要求或存在異常,這時可以使用該設備實現數據監視,使用can_replay_log實現數據回顯。
目錄
基本外設的使用
spi nor flash
RAM文件系統
SDIO驅動
can驅動測試
can數據監視回顯APP
《GD32VW553開發實踐指南》貢獻名單
GD32VW553硬件介紹
1 基本外設的使用
1.1 資源介紹GD32F527IST7
Flash有7M,RAM有512+64K,有8個16bit通用定時器,2個32bit通用定時器;8個串口;6個IIC;8個SPI;1個SDIO;2個CAN;
USBFS全速;USBHS高速;ENET以太網;TLI LCD 接口;DSI攝像頭;SAI音頻接口;

1.2 引腳分布圖

1.3 系統架構
APB1 和 APB2 是連接所有 APB 從機的兩條 APB 總線。APB1 最高可達 50MHz,APB2 可以全速運行(最高可到 100MHz)。

1.4 時鐘樹
系統時鐘200M,APB2最大100M,APB1最大50M,

1.5 GPIO 9組
最多可支持 140 個通用 I/O 引腳(GPIO),分別為 PA0 ~ PA15,PB0 ~ PB15,PC0 ~ PC15,PD0 ~ PD15,PE0 ~ PE15,PF0 ~ PF15,PG0 ~ PG15,PH0 ~ PH15 和 PI0 ~ PI11,各片上設備用其來實現邏輯輸入 / 輸出功能
1.5.1 基本結構

1.5.2 地址
GPIOA 基地址:0x4002 0000
GPIOB 基地址:0x4002 0400
GPIOC 基地址:0x4002 0800
GPIOD 基地址:0x4002 0C00
GPIOE 基地址:0x4002 1000
GPIOF 基地址:0x4002 1400
GPIOG 基地址:0x4002 1800
GPIOH 基地址:0x4002 1C00
GPIOI 基地址:0x4002 2000
RTT gpio驅動框架
libraries\gd32_drivers\drv_gpio.h
核心操作結構
structrt_pin_ops{ void(*pin_mode)(structrt_device *device,rt_base_tpin,rt_uint8_tmode); void(*pin_write)(structrt_device *device,rt_base_tpin,rt_uint8_tvalue); rt_ssize_t (*pin_read)(structrt_device *device,rt_base_tpin); rt_err_t(*pin_attach_irq)(structrt_device *device,rt_base_tpin, rt_uint8_tmode,void(*hdr)(void*args),void*args); rt_err_t(*pin_detach_irq)(structrt_device *device,rt_base_tpin); rt_err_t(*pin_irq_enable)(structrt_device *device,rt_base_tpin,rt_uint8_tenabled); rt_base_t(*pin_get)(constchar *name); rt_err_t(*pin_debounce)(structrt_device *device,rt_base_tpin,rt_uint32_tdebounce);};
gd32驅動提供的驅動實現
pin_get使用宏來實現引腳的獲取#define LED1_PIN GET_PIN(E, 3)
conststaticstructrt_pin_opsgd32_pin_ops ={ .pin_mode = gd32_pin_mode, .pin_write = gd32_pin_write, .pin_read = gd32_pin_read, .pin_attach_irq = gd32_pin_attach_irq, .pin_detach_irq= gd32_pin_detach_irq, .pin_irq_enable = gd32_pin_irq_enable, RT_NULL,};
注冊了pin驅動
libraries\gd32_drivers\drv_gpio.c 761行-770行
intrt_hw_pin_init(void){ int result; result =rt_device_pin_register("pin", &gd32_pin_ops,RT_NULL); returnresult;}INIT_BOARD_EXPORT(rt_hw_pin_init);

1.5.3 GPIO測試

代碼
#include#include#include#include/* defined the LED0 pin: PE2 */#defineLED0_PIN GET_PIN(E, 2)/* defined the LED1 pin: PE3 */#defineLED1_PIN GET_PIN(E, 3)intmain(void){ intcount =1; /* set LED1 pin mode to output */ rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT); rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT); while(count++) { rt_pin_write(LED0_PIN, PIN_HIGH); rt_pin_write(LED1_PIN, PIN_HIGH); rt_thread_mdelay(500); rt_pin_write(LED0_PIN, PIN_LOW); rt_pin_write(LED1_PIN, PIN_LOW); rt_thread_mdelay(500); } returnRT_EOK;}
2 spi nor flash
使用時注意這幾個短路帽是否在spi模式下
2.1 原理圖

這里需要注意hold引腳和wp引腳的上拉控制

2.2 rtt內核配置
內核對象的長度設置。塊設備名稱過長導致注冊塊設備失敗!

使能rtt自帶的調試宏定義
rt-thread\include\rtdbg.h
通過使能這個發現了內核工作流程和日志打印,內核名稱太短導致 norflash掛載是名字太長,無法掛載成功
打開rtt的調試日志


打開文件系統fatfs

配置fatfs文件系統

打開sfud組件
這里的名稱改了,串行flash驅動


根據開發板設計,nor flash掛在spi5上,故首先需要使能spi的驅動

查看spi引腳發現默認的驅動引腳和原理圖一致,無需修改
#ifdefBSP_USING_SPI5 { SPI5, "spi5", RCU_SPI5, RCU_GPIOG, RCU_GPIOG, RCU_GPIOG, &spi_bus5, GPIOG, GPIOG, GPIOG,#ifdefined (SOC_SERIES_GD32F4xx) || defined (SOC_SERIES_GD32H7xx) || (defined SOC_SERIES_GD32F5xx) GPIO_AF_5,#endif GPIO_PIN_13, GPIO_PIN_12, GPIO_PIN_14, }#endif/* BSP_USING_SPI5 */};
注冊spi 設備
注冊塊設備
官方提供了這個文件,已經完成了部分代碼
libraries\gd32_drivers\drv_spi_flash.c
/** Copyright (c) 2006-2022, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2021-12-31 BruceOu first implementation* 2023-06-03 CX fixed sf probe error bug* 2024-05-30 godmial refactor driver for multi-SPI bus auto-mount*/#include#include"drv_spi.h"#include"dev_spi_flash.h"#ifdefRT_USING_SFUD#include"dev_spi_flash_sfud.h"#endif#include#include#ifdefRT_USING_DFS#include#endifstructspi_flash_config{ constchar *bus_name; constchar *device_name; constchar *flash_name; rt_base_tcs_pin;};staticconststructspi_flash_configflash_configs[] = {#ifdefBSP_USING_SPI0 { .bus_name ="spi0", .device_name ="spi00", .flash_name ="gd25q_spi0", .cs_pin =GET_PIN(A,4), },#endif#ifdefBSP_USING_SPI1 { .bus_name ="spi1", .device_name ="spi10", .flash_name ="gd25q_spi1", .cs_pin =GET_PIN(B,9), },#endif#ifdefBSP_USING_SPI2 { .bus_name ="spi2", .device_name ="spi20", .flash_name ="gd25q_spi2", .cs_pin =GET_PIN(B,12), },#endif#ifdefBSP_USING_SPI3 { .bus_name ="spi3", .device_name ="spi30", .flash_name ="gd25q_spi3", .cs_pin =GET_PIN(E,4), },#endif#ifdefBSP_USING_SPI4 { .bus_name ="spi4", .device_name ="spi40", .flash_name ="gd25q_spi4", .cs_pin =GET_PIN(F,6), },#endif// 新增spi5的配置#ifdefBSP_USING_SPI5 { .bus_name ="spi5", .device_name ="spi50", .flash_name = FAL_USING_NOR_FLASH_DEV_NAME,/** norflash0 */ .cs_pin =GET_PIN(I,8), },#endif};// 配置hold和wp引腳的控制#defineNOR_FLASH_HOLD GET_PIN(H, 4)#defineNOR_FLASH_WP GET_PIN(G, 10)staticintspi_flash_init(void){ intresult = RT_EOK; rt_pin_mode(NOR_FLASH_HOLD, PIN_MODE_OUTPUT); rt_pin_mode(NOR_FLASH_WP, PIN_MODE_OUTPUT); rt_pin_write(NOR_FLASH_HOLD, PIN_HIGH); rt_pin_write(NOR_FLASH_WP, PIN_HIGH); for(size_ti =0; i bus_name, cfg->device_name, cfg->cs_pin); if(result != RT_EOK) { rt_kprintf("Failed to attach device %s on bus %s\n", cfg->device_name, cfg->bus_name); continue; }#ifdefRT_USING_SFUD // 注冊一個塊設備 if(RT_NULL ==rt_sfud_flash_probe(cfg->flash_name, cfg->device_name)) { rt_kprintf("SFUD probe failed: %s\n", cfg->flash_name); continue; } else { rt_kprintf("SFUD probe ok: %s\n", cfg->flash_name); }#endif } returnresult;}INIT_COMPONENT_EXPORT(spi_flash_init);
首次使用需要格式化
mkfs -t elm norflash0
掛載
mount norflash0 / elm
創建一個文件測試文件系統
echo 123456abc 123.txt
開機自動掛載
新建文件,開機自動掛載
applications\fs_mount.c
#include#include#includeintmnt_init(void){ //將sd掛載在 / 目錄 if(dfs_mount(FAL_USING_NOR_FLASH_DEV_NAME,"/","elm",0,0) ==0) { LOG_I("%s mount success !\n",FAL_USING_NOR_FLASH_DEV_NAME); } else { LOG_W("%s mount failed!\n",FAL_USING_NOR_FLASH_DEV_NAME); } return0;}INIT_FS_EXPORT(mnt_init);
硬件測試時,注意斷路冒的設置;JP13,JP15;JP17;JP19

3 RAM文件系統
頂一個ram空間用于存放文件系統,經測試不可以創建文件夾,只能創建文件
#include#include#include#includeintmnt_init(void){ uint8_tramfs_buff[4096]; if(dfs_mount(RT_NULL,"/","ram",0,dfs_ramfs_create(ramfs_buff,4096)) ==0) { LOG_I("ram mount success !\n"); } else { LOG_W("ram mount failed!\n"); } return0;}INIT_FS_EXPORT(mnt_init);
4 SDIO驅動
4.1 開發原理圖
注意短路帽的設置

4.2 使能驅動

4.3 修改驅動
修改gpio_config,修改gpio速度
libraries\gd32_drivers\drv_sdio.c

4.4 將sd卡掛載到文件系統
if(dfs_mount(SD_BLK_NAME,"/","elm",0,0) ==0) { LOG_I("%smount success !\n",SD_BLK_NAME); } else { LOG_W("%smount failed!\n",SD_BLK_NAME); }
sd0就是sd卡
---------------------------------------------------------------- -------------------- ----------spi50 SPI Device 0sd0 Block Device 1rtc RTC 0spi5 SPI Bus 0timer10 Timer Device 0pin Pin Device 0can0 CAN Device 1uart0 CharacterDevice 2msh/>dfdiskfree:1.6GB [3408064block,512bytesperblock ]
注意短路冒設置,默認時攝像頭使用的
我是一個4G的sd卡

4.5 官方的sdio驅動可能的問題
我對比了現在官方提供的sdio驅動和我在之前gd32f303使用的幾乎一樣,gd32f303平臺會快速出現sd卡讀寫異常,需要重新初始化才可以使用,但是在GD32F527平臺上同樣的驅動幾乎沒有出現。
修改了驅動主要做的修改是,在各種地方重置sdio的狀態。
修改后的驅動問題
根據我在GD32F303上的經驗,發現sdio的驅動會大量寫入數據時,驅動報錯的問題,于是修改了GD32F527的sdio驅動,但是引發了ls指令列出文件時越來越慢的問題;經排查這里出現問題。、
5 can驅動測試
can框架分析:
https://blog.csdn.net/u010261063/article/details/148741206?spm=1011.2415.3001.5331
rt thread 基于GD32F303的can驅動編寫
https://blog.csdn.net/u010261063/article/details/148784016?spm=1011.2124.3001.6209
5.1 驅動使能

5.2 硬件
開發版的引腳和驅動引腳一致

5.3 drv_can.c驅動問題1
測試代碼
#include#include"rtdevice.h"#defineCAN_DEV_NAME"can0"/* CAN 設備名稱 */staticstructrt_semaphorerx_sem;/* 用于接收消息的信號量 */staticrt_device_tcan_dev; /* CAN 設備句柄 *//* 接收數據回調函數 */staticrt_err_tcan_rx_call(rt_device_tdev,rt_size_tsize){ /* CAN 接收到數據后產生中斷,調用此回調函數,然后發送接收信號量 */ rt_sem_release(&rx_sem); returnRT_EOK;}staticvoidcan_rx_thread(void*parameter){ inti; rt_err_tres; structrt_can_msgrxmsg = {0}; /* 設置接收回調函數 */ rt_device_set_rx_indicate(can_dev, can_rx_call);#ifdefRT_CAN_USING_HDR structrt_can_filter_itemitems[5] = { RT_CAN_FILTER_ITEM_INIT(0x100,0,0,0,0x700, RT_NULL, RT_NULL),/* std,match ID:0x100~0x1ff,hdr 為 - 1,設置默認過濾表 */ RT_CAN_FILTER_ITEM_INIT(0x300,0,0,0,0x700, RT_NULL, RT_NULL),/* std,match ID:0x300~0x3ff,hdr 為 - 1 */ RT_CAN_FILTER_ITEM_INIT(0x211,0,0,0,0x7ff, RT_NULL, RT_NULL),/* std,match ID:0x211,hdr 為 - 1 */ RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL), /* std,match ID:0x486,hdr 為 - 1 */ { 0x555, 0, 0, 0, 0x7ff, 7, }/* std,match ID:0x555,hdr 為 7,指定設置 7 號過濾表 */ }; structrt_can_filter_configcfg = {5,1, items};/* 一共有 5 個過濾表 */ /* 設置硬件過濾表 */ res =rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg); RT_ASSERT(res == RT_EOK);#endif while(1) { /* hdr 值為 - 1,表示直接從 uselist 鏈表讀取數據 */ rxmsg.hdr_index =-1; /* 阻塞等待接收信號量 */ rt_sem_take(&rx_sem, RT_WAITING_FOREVER); /* 從 CAN 讀取一幀數據 */ rt_device_read(can_dev,0, &rxmsg,sizeof(rxmsg)); /* 打印數據 ID 及內容 */ rt_kprintf("ID:%08x [ ", rxmsg.id); for(i =0; i < rxmsg.len; i++)? ? ? ? {? ? ? ? ? ? rt_kprintf("%02x ", rxmsg.data[i]);? ? ? ? }? ? ? ? rt_kprintf(" ]\n");? ? }}intcan_sample(int?argc,?char?*argv[]){? ? structrt_can_msgmsg = {0};? ? rt_err_t?res;? ? rt_size_t?size;? ? rt_thread_t?thread;? ? char?can_name[RT_NAME_MAX];? ? if?(argc ==?2)? ? {? ? ? ? rt_strncpy(can_name, argv[1], RT_NAME_MAX);? ? }? ? else? ? {? ? ? ? rt_strncpy(can_name, CAN_DEV_NAME, RT_NAME_MAX);? ? }? ? /* 查找 CAN 設備 */? ? can_dev =?rt_device_find(can_name);? ? if?(!can_dev)? ? {? ? ? ? rt_kprintf("find %s failed!\n", can_name);? ? ? ? return?RT_ERROR;? ? }? ? /* 初始化 CAN 接收信號量 */? ? rt_sem_init(&rx_sem,?"rx_sem",?0, RT_IPC_FLAG_FIFO);? ? /* 以中斷接收及發送方式打開 CAN 設備 */? ? res =?rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);? ? RT_ASSERT(res == RT_EOK);? ? /* 設置 CAN 通信的波特率為 500kbit/s*/? ? // res = rt_device_control(can_dev, RT_CAN_CMD_SET_BAUD, (void *)CAN500kBaud);? ? // RT_ASSERT(res == RT_EOK);? ? /* 創建數據接收線程 */? ? thread =?rt_thread_create("can_rx", can_rx_thread, RT_NULL,?1024,?25,?10);? ? if?(thread != RT_NULL)? ? {? ? ? ? rt_thread_startup(thread);? ? }? ? else? ? {? ? ? ? rt_kprintf("create can_rx thread failed!\n");? ? }? ? msg.id =?0x78; ? ? ? ? ?/* ID 為 0x78 */? ? msg.ide = RT_CAN_STDID;?/* 標準格式 */? ? msg.rtr = RT_CAN_DTR; ? /* 數據幀 */? ? msg.len =?8; ? ? ? ? ? ?/* 數據長度為 8 */? ? /* 待發送的 8 字節數據 */? ? msg.data[0] =?0x00;? ? msg.data[1] =?0x11;? ? msg.data[2] =?0x22;? ? msg.data[3] =?0x33;? ? msg.data[4] =?0x44;? ? msg.data[5] =?0x55;? ? msg.data[6] =?0x66;? ? msg.data[7] =?0x77;? ? /* 發送一幀 CAN 數據 */? ? size =?rt_device_write(can_dev,?0, &msg,?sizeof(msg));? ? if?(size ==?0)? ? {? ? ? ? rt_kprintf("can dev write data failed!\n");? ? }? ? return?res;}/* 導出到 msh 命令列表中 */MSH_CMD_EXPORT(can_sample, can device sample);
在通訊頻率時1M時發送ok

500K報錯

解決辦法
經過調試發現,官方提供的can驅動存在問題;修改波特率導致發送數據失敗,經過分析時在配置配置波特率是重新初始化了can外設,導致了open階段的配置就丟失了,例如中斷發送接收功能沒有了
libraries\gd32_drivers\drv_can.c

修改can的反初始化can_deinit位置 放在這里
函數:int rt_hw_can_init(void)

5.4 優化等級問題2?
本來就想測試下can的自發自收測試,沒想到問題出現了
5.4.1 測試代碼
/** Copyright (c) 2006-2023, RT-Thread Development Team** SPDX-License-Identifier: Apache-2.0** Change Logs:* Date Author Notes* 2021-08-20 BruceOu first implementation* 2023-03-05 yuanzihao change the LED pins*/#include#include#include#include/* defined the LED1 pin: PE3 */#defineLED1_PIN GET_PIN(E, 3)#defineLED0_PIN GET_PIN(E, 2)intmain(void){ intcount =1; /* set LED1 pin mode to output */ rt_pin_mode(LED1_PIN, PIN_MODE_OUTPUT); rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT); while(count++) { rt_pin_write(LED0_PIN, PIN_HIGH); rt_pin_write(LED1_PIN, PIN_HIGH); rt_thread_mdelay(500); rt_pin_write(LED0_PIN, PIN_LOW); rt_pin_write(LED1_PIN, PIN_LOW); rt_thread_mdelay(500); } returnRT_EOK;}#include#include"rtdevice.h"#defineCAN_DEV_NAME"can0"/* CAN 設備名稱 */staticstructrt_semaphorerx_sem;/* 用于接收消息的信號量 */staticrt_device_tcan_dev; /* CAN 設備句柄 *//* 接收數據回調函數 */staticrt_err_tcan_rx_call(rt_device_tdev,rt_size_tsize){ /* CAN 接收到數據后產生中斷,調用此回調函數,然后發送接收信號量 */ rt_sem_release(&rx_sem); returnRT_EOK;}staticstructrt_can_msgrxmsg = {0};staticstructrt_can_msgmsg = {0};staticvoidcan_rx_thread(void*parameter){ inti; rt_err_tres; /* 設置接收回調函數 */ rt_device_set_rx_indicate(can_dev, can_rx_call);#ifdefRT_CAN_USING_HDR structrt_can_filter_itemitems[5] = { RT_CAN_FILTER_ITEM_INIT(0x100,0,0,0,0x700, RT_NULL, RT_NULL),/* std,match ID:0x100~0x1ff,hdr 為 - 1,設置默認過濾表 */ RT_CAN_FILTER_ITEM_INIT(0x300,0,0,0,0x700, RT_NULL, RT_NULL),/* std,match ID:0x300~0x3ff,hdr 為 - 1 */ RT_CAN_FILTER_ITEM_INIT(0x211,0,0,0,0x7ff, RT_NULL, RT_NULL),/* std,match ID:0x211,hdr 為 - 1 */ RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL), /* std,match ID:0x486,hdr 為 - 1 */ { 0x555, 0, 0, 0, 0x7ff, 7, }/* std,match ID:0x555,hdr 為 7,指定設置 7 號過濾表 */ }; structrt_can_filter_configcfg = {5,1, items};/* 一共有 5 個過濾表 */ /* 設置硬件過濾表 */ res =rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg); RT_ASSERT(res == RT_EOK);#endif while(1) { /* hdr 值為 - 1,表示直接從 uselist 鏈表讀取數據 */ rxmsg.hdr_index =-1; /* 阻塞等待接收信號量 */ rt_sem_take(&rx_sem, RT_WAITING_FOREVER); /* 從 CAN 讀取一幀數據 */ rt_device_read(can_dev,0, &rxmsg,sizeof(rxmsg)); /* 打印數據 ID 及內容 */ rt_kprintf("ID:%08x [ ", rxmsg.id); for(i =0; i < rxmsg.len; i++)? ? ? ? {? ? ? ? ? ? rt_kprintf("%02x ", rxmsg.data[i]);? ? ? ? }? ? ? ? rt_kprintf(" ]\n");? ? ? ? //直接結構體對象賦值實現數據回環,竟然不得行?修改了優化等級就可以? ? ? ? msg = rxmsg;? ? ? ? /* 發送一幀 CAN 數據 */? ? ? ? int?size =?rt_device_write(can_dev,?0, &msg,?sizeof(msg));? ? ? ? if?(size ==?0)? ? ? ? {? ? ? ? ? ? rt_kprintf("can dev write data failed!\n");? ? ? ? }? ? }}intcan_sample(int?argc,?char?*argv[]){? ? rt_err_t?res;? ? rt_size_t?size;? ? rt_thread_t?thread;? ? char?can_name[RT_NAME_MAX];? ? if?(argc ==?2)? ? {? ? ? ? rt_strncpy(can_name, argv[1], RT_NAME_MAX);? ? }? ? else? ? {? ? ? ? rt_strncpy(can_name, CAN_DEV_NAME, RT_NAME_MAX);? ? }? ? /* 查找 CAN 設備 */? ? can_dev =?rt_device_find(can_name);? ? if?(!can_dev)? ? {? ? ? ? rt_kprintf("find %s failed!\n", can_name);? ? ? ? return?RT_ERROR;? ? }? ? /* 初始化 CAN 接收信號量 */? ? rt_sem_init(&rx_sem,?"rx_sem",?0, RT_IPC_FLAG_FIFO);? ? /* 以中斷接收及發送方式打開 CAN 設備 */? ? res =?rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);? ? RT_ASSERT(res == RT_EOK);? ? /* 設置 CAN 通信的波特率為 500kbit/s*/? ? res =?rt_device_control(can_dev, RT_CAN_CMD_SET_BAUD, (void?*)CAN500kBaud);? ? RT_ASSERT(res == RT_EOK);? ? /* 創建數據接收線程 */? ? thread =?rt_thread_create("can_rx", can_rx_thread, RT_NULL,?2048,?25,?10);? ? if?(thread != RT_NULL)? ? {? ? ? ? rt_thread_startup(thread);? ? }? ? else? ? {? ? ? ? rt_kprintf("create can_rx thread failed!\n");? ? }? ? //這樣手動賦值也可以實現收一條發一條的功能? ? structrt_can_msgmsg = {0};? ? msg.id =?0x78; ? ? ? ? ?/* ID 為 0x78 */? ? msg.ide = RT_CAN_STDID;?/* 標準格式 */? ? msg.rtr = RT_CAN_DTR; ? /* 數據幀 */? ? msg.len =?8; ? ? ? ? ? ?/* 數據長度為 8 */? ? /* 待發送的 8 字節數據 */? ? msg.data[0] =?0x00;? ? msg.data[1] =?0x11;? ? msg.data[2] =?0x22;? ? msg.data[3] =?0x33;? ? msg.data[4] =?0x44;? ? msg.data[5] =?0x55;? ? msg.data[6] =?0x66;? ? msg.data[7] =?0x77;? ? /* 發送一幀 CAN 數據 */? ? size =?rt_device_write(can_dev,?0, &msg,?sizeof(msg));? ? if?(size ==?0)? ? {? ? ? ? rt_kprintf("can dev write data failed!\n");? ? }? ? return?res;}/* 導出到 msh 命令列表中 */MSH_CMD_EXPORT(can_sample, can device sample);
5.4.2 問題現象




匯編分析
staticstructrt_can_msgrxmsg = {0};staticstructrt_can_msgmsg = {0};structrt_can_msg{ rt_uint32_tid :29; /**< CAN ID (Standard or Extended). */? ? rt_uint32_t?ide :?1; ? ? ? ? ? ?/**< Identifier type: 0=Standard ID, 1=Extended ID. */? ? rt_uint32_t?rtr :?1; ? ? ? ? ? ?/**< Frame type: 0=Data Frame, 1=Remote Frame. */? ? rt_uint32_t?rsv :?1; ? ? ? ? ? ?/**< Reserved bit. */? ? rt_uint32_t?len :?8; ? ? ? ? ? ?/**< Data Length Code (DLC) from 0 to 8. */? ? rt_uint32_t?priv :?8; ? ? ? ? ? /**< Private data, used to specify the hardware mailbox in private mode. */? ? rt_int32_t?hdr_index :?8; ? ? ? /**< For received messages, the index of the hardware filter that matched the message. */? ? rt_uint32_t?rxfifo :?2; ? ? ? ? /**< The RX FIFO where the message was received. */? ? rt_uint32_t?reserved :?5;? ? rt_uint32_t?nonblocking :?1; ? ?/**< Send mode: 0=Blocking (default), 1=Non-blocking. */? ? rt_uint8_t?data[8]; ? ? ? ? ? ? /**< CAN message payload (up to 8 bytes). */};
可行
106: msg = rxmsg;0x08006CFAF242326C MOV r2,#0x236c0x08006CFEF2C20200 MOVT r2,#0x20000x08006D026810 LDR r0,[r2,#0]0x08006D046851 LDR r1,[r2,#4]0x08006D066893 LDR r3,[r2,#8]0x08006D08F8D2C00C LDR r12,[r2,#0xc]0x08006D0CF64172E8 MOV r2,#0x1fe80x08006D10F2C20200 MOVT r2,#0x20000x08006D14F8C2C00C STR r12,[r2,#0xc]0x08006D186093 STR r3,[r2,#8]0x08006D1A6051 STR r1,[r2,#4]0x08006D1C6010 STR r0,[r2,#0]
不可行匯編
106: msg = rxmsg;0x080048D8 E894000F LDM r4,{r0-r3}0x080048DC60EB STR r3,[r5,#0xc]0x080048DE E8850007 STM r5,{r0-r2}
使用AI分析了匯編,他說出現這個問題可能是結構struct rt_can_msg的位定義問題,導致了內存拷貝的異常,經過測試其實不是真實原因
5.4.3 解決辦法:又是can驅動的問題
發現了一個問題,優化等級太高,導致輸出之收不發,不優化就可以自收發。本以為是優化等級問題,結果不是。
結論:不是優化等級的問題,又是can驅動的問題,在收到can的驅動時需要memset一下struct rt_can_msg消息結構。
在接收函數內部將結構體設置為零后,就可以實現在優化等級3下,實現數據回環收發
libraries\gd32_drivers\drv_can.c

再次分析看看有無memset的數據有啥不一樣
測試代碼
uint8_t*u8 = (uint8_t*)&rxmsg;for(i =0; i
在優化等級3的情況下;這里就說明了struct rt_can_msg填充了其他值
有置零:4b 01 00 20 03 00 00 00 01 02 03 00 00 00 00 00
無置零:4b 01 00 20 03 be ad dc 01 02 03 20 3c 2c 00 20
優化等級1:
有置零:4b 01 00 20 03 00 00 00 01 02 03 00 00 00 00 00
無置零:4b 01 00 20 03 0a 00 20 01 02 03 00 01 00 00 00
由此可以知道不是優化等級的問題;在未置零memset的情況下僅僅是rxmsg的某些成員是未定義的,而在發送時這個成員又是需要的。
libraries\gd32_drivers\drv_can.c

5.5 時間戳的實現
使能RTC驅動程序

測試驅動
注意時間存在一個時區問題
查看時間:date
設置時間:date 2025 10 24 9 21 00
msh/>datelocaltime: Mon Jan 108:00:002024timestamps:1704067200timezone: UTC+08:00:00msh/>date2025102492100old: Mon Jan 108:00:132024now: Fri Oct2409:21:002025msh/>datelocaltime: Fri Oct2409:21:042025timestamps:1761268864timezone: UTC+08:00:00msh/>
6 can數據監視回顯APP
6.1 can數據結構定義
#ifndefCAN_LOGGER_H_#defineCAN_LOGGER_H_#include#include/* CAN 消息結構體 */structcan_message{ rt_uint32_tid :29;/* CAN ID, 標志格式 11 位,擴展格式 29 位 */ rt_uint32_tide :1;/* 擴展幀標識位 */ rt_uint32_trtr :1;/* 遠程幀標識位 */ rt_uint32_trsv :1;/* 保留位 */ uint8_tdata[8]; // 數據域 uint8_tlen; // 數據長度 time_ttimestamp; // 時間戳(秒)};/* 發送消息接口(用于測試) */voidsend_can_message(structcan_message *msg);#endif
6.2 數據監視并記錄為文件
applications\can_log_monitor.c
#include#include#include#include#include#include#include#include#include#include#include#include#defineCAN_DEV_NAME"can0"/* CAN 設備名稱 */#defineLED0_PIN GET_PIN(E, 2)#defineLED0_ERR GET_PIN(E, 7)staticstructrt_semaphorerx_sem;/* 用于接收消息的信號量 */staticrt_device_tcan_dev; /* CAN 設備句柄 */staticvoidcan_led_blink(){ staticrt_uint8_tblink =0; if(blink) { rt_pin_write(LED0_PIN, PIN_HIGH); } else { rt_pin_write(LED0_PIN, PIN_LOW); } blink = !blink;}/* 接收數據回調函數 */staticrt_err_tcan_rx_call(rt_device_tdev,rt_size_tsize){ /* CAN 接收到數據后產生中斷,調用此回調函數,然后發送接收信號量 */ rt_sem_release(&rx_sem); returnRT_EOK;}staticvoidcan_rx_thread(void*parameter){ inti; rt_err_tres; staticstructrt_can_msgrxmsg = {0}; staticstructcan_messagecan_rx_msg; /* 設置接收回調函數 */ rt_device_set_rx_indicate(can_dev, can_rx_call);#ifdefRT_CAN_USING_HDR structrt_can_filter_itemitems[5] = { RT_CAN_FILTER_ITEM_INIT(0x100,0,0,0,0x700, RT_NULL, RT_NULL),/* std,match ID:0x100~0x1ff,hdr 為 - 1,設置默認過濾表 */ RT_CAN_FILTER_ITEM_INIT(0x300,0,0,0,0x700, RT_NULL, RT_NULL),/* std,match ID:0x300~0x3ff,hdr 為 - 1 */ RT_CAN_FILTER_ITEM_INIT(0x211,0,0,0,0x7ff, RT_NULL, RT_NULL),/* std,match ID:0x211,hdr 為 - 1 */ RT_CAN_FILTER_STD_INIT(0x486, RT_NULL, RT_NULL), /* std,match ID:0x486,hdr 為 - 1 */ { 0x555, 0, 0, 0, 0x7ff, 7, }/* std,match ID:0x555,hdr 為 7,指定設置 7 號過濾表 */ }; structrt_can_filter_configcfg = {5,1, items};/* 一共有 5 個過濾表 */ /* 設置硬件過濾表 */ // res = rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg); // RT_ASSERT(res == RT_EOK);#endif while(1) { /* hdr 值為 - 1,表示直接從 uselist 鏈表讀取數據 */ rxmsg.hdr_index =-1; /* 阻塞等待接收信號量 */ rt_sem_take(&rx_sem, RT_WAITING_FOREVER); /* 從 CAN 讀取一幀數據 */ rt_device_read(can_dev,0, &rxmsg,sizeof(rxmsg)); can_rx_msg.id = rxmsg.id; can_rx_msg.ide = rxmsg.ide; can_rx_msg.rtr = rxmsg.rtr; can_rx_msg.rsv = rxmsg.rsv; can_rx_msg.len = rxmsg.len; can_rx_msg.timestamp =time(NULL); rt_memcpy(can_rx_msg.data, rxmsg.data, rxmsg.len); send_can_message(&can_rx_msg); can_led_blink(); /* 打印數據 ID 及內容 */ // rt_kprintf("ID:%x [", rxmsg.id); // for (i = 0; i < rxmsg.len; i++)? ? ? ? // ? ? ? ?{? ? ? ? // ? ? ? ? ? ?rt_kprintf("%02x ", rxmsg.data[i]);? ? ? ? // ? ? ? ?}? ? ? ? // ? ? ? ?rt_kprintf("]\n");? ? ? ? // rt_device_write(can_dev, 0, &rxmsg, sizeof(rxmsg));? ? }}intcan_app_main(void){? ? structrt_can_msgmsg = {0};? ? rt_err_t?res;? ? rt_size_t?size;? ? rt_thread_t?thread;? ? /* 查找 CAN 設備 */? ? can_dev =?rt_device_find(CAN_DEV_NAME);? ? if?(!can_dev)? ? {? ? ? ? LOG_E("find %s failed!", CAN_DEV_NAME);? ? ? ? return?RT_ERROR;? ? }? ? /* 初始化 CAN 接收信號量 */? ? rt_sem_init(&rx_sem,?"rx_sem",?0, RT_IPC_FLAG_FIFO);? ? /* 以中斷接收及發送方式打開 CAN 設備 */? ? res =?rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);? ? RT_ASSERT(res == RT_EOK);? ? LOG_I("set bate 500K");? ? res =?rt_device_control(can_dev, RT_CAN_CMD_SET_BAUD, (void?*)CAN500kBaud);? ? RT_ASSERT(res == RT_EOK);? ? /* 創建數據接收線程 */? ? thread =?rt_thread_create("can_rx", can_rx_thread, RT_NULL,?1024,?25,?10);? ? if?(thread != RT_NULL)? ? {? ? ? ? rt_thread_startup(thread);? ? }? ? else? ? {? ? ? ? LOG_W("create can_rx thread failed!");? ? }? ? msg.id =?0x78; ? ? ? ? ?/* ID 為 0x78 */? ? msg.ide = RT_CAN_STDID;?/* 標準格式 */? ? msg.rtr = RT_CAN_DTR; ? /* 數據幀 */? ? msg.len =?8; ? ? ? ? ? ?/* 數據長度為 8 */? ? /* 待發送的 8 字節數據 */? ? msg.data[0] =?0x00;? ? msg.data[1] =?0x11;? ? msg.data[2] =?0x22;? ? msg.data[3] =?0x33;? ? msg.data[4] =?0x44;? ? msg.data[5] =?0x55;? ? msg.data[6] =?0x66;? ? msg.data[7] =?0x77;? ? /* 發送一幀 CAN 數據 */? ? size =?rt_device_write(can_dev,?0, &msg,?sizeof(msg));? ? if?(size ==?0)? ? {? ? ? ? LOG_W("can dev write data failed!");? ? }? ? return?res;}/* 導出到 msh 命令列表中 */INIT_APP_EXPORT(can_app_main);#define?QUEUE_SIZE 10#define?AUTO_SYNC_TIME_MS (5 * 1000)/* 全局變量 */staticrt_mq_t?can_queue = RT_NULL;staticrt_thread_t?log_thread = RT_NULL;staticrt_timer_t?timer_auto_save = RT_NULL;staticint fd =?-1;/* 當前文件名緩存 */staticchar current_filename[64];/* 生成當前時間的文件夾名(按小時) */voidgenerate_hourly_dirname(char?*dirname,?size_t?len){? ? time_t?now;? ? structtm *today;? ? time(&now);? ? today =?localtime(&now);? ? rt_snprintf(dirname, len,?"/can/%04d%02d%02d_%02d",? ? ? ? ? ? ? ? today->tm_year +1900, today->tm_mon +1, today->tm_mday, today->tm_hour);}/* 生成當前時間的文件名(每分鐘一個文件) */voidgenerate_minute_filename(char*filename,size_tlen){ time_tnow; structtm *today; time(&now); today =localtime(&now); rt_snprintf(filename, len,"%02d.data", today->tm_min);}/* 遞歸創建目錄 */intensure_directory_exists(constchar *path){ chartemp[256]; inti, len =rt_strlen(path); if(len >=sizeof(temp)) return-1; for(i =0; i < len; i++)? ? {? ? ? ? temp[i] = path[i];? ? ? ? if?(temp[i] ==?'/'?&& i >0) { temp[i] ='\0'; mkdir(temp,0755); temp[i] ='/'; } } temp[len] ='\0'; returnmkdir(temp,0755);}staticvoidfun_auto_sync_file(void*parameter){ LOG_D("timeout sync file"); structcan_messagemsg; msg.rsv =0; rt_mq_send(can_queue, &msg,sizeof(structcan_message));}/* 發送消息接口(用于測試) */voidsend_can_message(structcan_message *msg){ if(can_queue != RT_NULL) { msg->rsv =1; rt_mq_send(can_queue, msg,sizeof(structcan_message)); // rt_uint32_t time=AUTO_SYNC_TIME_MS; // rt_timer_control(timer_auto_save,RT_TIMER_CTRL_SET_TIME,&time); rt_timer_start(timer_auto_save); }}/* 線程入口函數 */staticvoidlog_thread_entry(void*parameter){ staticstructcan_messagemsg; staticchar dirname[64] =""; staticchar filename[32] =""; staticchar fullpath[128] =""; staticchar last_dirname[64] =""; staticchar last_filename[32] =""; staticint fd =-1; // 自動創建目錄 LOG_I("crea path="/can" dir %d",mkdir("can",0x777)); while(1) { if(sizeof(structcan_message) ==rt_mq_recv(can_queue, &msg,sizeof(structcan_message), RT_WAITING_FOREVER) && msg.rsv) { generate_hourly_dirname(dirname,sizeof(dirname)); generate_minute_filename(filename,sizeof(filename)); rt_snprintf(fullpath,sizeof(fullpath),"%s/%s", dirname, filename); // 新目錄,檢查是否需要切換目錄或文件 if(rt_strcmp(last_dirname, dirname) !=0) { // 先關閉舊文件 if(fd >=0) { fsync(fd); close(fd); fd =-1; } // 目錄不存在則創建 interr =mkdir(dirname,0x777); if(err) { LOG_E("create dir error=%d path =[%s]", err, dirname); } rt_strncpy(last_dirname, dirname,sizeof(last_dirname)); } // 新文件 if(fd 0?||?rt_strcmp(last_filename, filename) !=?0)? ? ? ? ? ? {? ? ? ? ? ? ? ?// 先關閉舊文件? ? ? ? ? ? ? ? if?(fd >=0) { fsync(fd); close(fd); fd =-1; } // 打開新文件 fd =open(fullpath, O_WRONLY | O_APPEND | O_CREAT,0); if(fd 0)? ? ? ? ? ? ? ? {? ? ? ? ? ? ? ? ? ? LOG_W("Failed to open file: %s", fullpath);? ? ? ? ? ? ? ? ? ? rt_pin_write(LED0_ERR, PIN_HIGH);? ? ? ? ? ? ? ? ? ? continue;? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? rt_strncpy(last_filename, filename,?sizeof(last_filename));? ? ? ? ? ? }? ? ? ? ? ? // 寫入數據? ? ? ? ? ? if?(fd >=0) { write(fd, &msg,sizeof(msg)); } rt_pin_write(LED0_ERR, PIN_LOW); } } // 線程退出前關閉文件 if(fd >=0) { fsync(fd); close(fd); }}/* 初始化函數:創建隊列和線程 */intcan_logger_init(void){ /* 創建消息隊列 */ can_queue =rt_mq_create("can_mq",sizeof(structcan_message), QUEUE_SIZE, RT_IPC_FLAG_FIFO); if(can_queue == RT_NULL) { rt_kprintf("Failed to create message queue.\n"); return-1; } /* 創建線程 */ log_thread =rt_thread_create("can_log", log_thread_entry, RT_NULL, 1024, 10, 10); if(log_thread != RT_NULL) { rt_thread_startup(log_thread); } else { rt_kprintf("Failed to create logging thread.\n"); rt_mq_delete(can_queue); return-1; } /**創建一個定時器實現自動保存文件 */ timer_auto_save =rt_timer_create("auto_save", fun_auto_sync_file,0, AUTO_SYNC_TIME_MS, RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER); RT_ASSERT(timer_auto_save); // 創建文件夾 mkdir("can",0x777); rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT); rt_pin_mode(LED0_ERR, PIN_MODE_OUTPUT); return0;}INIT_APP_EXPORT(can_logger_init);// 自動初始化
6.3 數據查看指令can_replay_log
applications\can_log_show.c
#include#include#include#include#include#include#include// #include voidcan_replay_log(intargc,char**argv){ if(argc !=2) { rt_kprintf("Usage: can_replay_log \n"); } intfd = open(argv[1], O_RDONLY,0); if(fd 0)? ? {? ? ? ? rt_kprintf("Failed to open log file: %s\n", argv[1]);? ? ? ? return;? ? }? ? structcan_messagemsg;? ? ssize_t bytes_read;? ? rt_kprintf("Replaying CAN log from: %s\n", argv[1]);? ? while?((bytes_read = read(fd, &msg,?sizeof(msg))) ==?sizeof(msg))? ? {? ? ? ? char?ts_str[32];? ? ? ? structtm *tm_info = localtime(&msg.timestamp);? ? ? ? strftime(ts_str,?sizeof(ts_str),?"%Y-%m-%d %H:%M:%S", tm_info);? ? ? ? /* 判斷是標準幀還是擴展幀 */? ? ? ? constchar *frame_type = msg.ide ??"EXT"?:?"STD";? ? ? ? constchar *rtr_type = msg.rtr ??"RTR"?:?"DATA";? ? ? ? /* 打印幀信息 */? ? ? ? rt_kprintf("[%s] ID: 0x%0*X (%s), Type: %s, LEN: %u, DATA: ",? ? ? ? ? ? ? ? ? ?ts_str,? ? ? ? ? ? ? ? ? ?msg.ide ??8?:?3, msg.id, frame_type, rtr_type, msg.len);? ? ? ? /* 如果是 RTR 幀,則不打印數據 */? ? ? ? if?(!msg.rtr)? ? ? ? {? ? ? ? ? ? for?(int?i =?0; i < msg.len; i++)? ? ? ? ? ? {? ? ? ? ? ? ? ? rt_kprintf("%02X ", msg.data[i]);? ? ? ? ? ? }? ? ? ? }? ? ? ? else? ? ? ? {? ? ? ? ? ? rt_kprintf("(no data)");? ? ? ? }? ? ? ? rt_kprintf("\n");? ? }? ? close(fd);}MSH_CMD_EXPORT(can_replay_log, Replay?CAN?log from current hour file.);
6.4 效果展示
數據含有時間,ID,幀類型,數據長度,數據內容

7 《GD32VW553開發實踐指南》貢獻名單
RT-Thread社區攜手兆易創新聯合發起兆易創新GD32VW553 無線MCU評測活動,《GD32VW553開發實踐指南》詳細列出了各個內容板塊及其貢獻者。在此,衷心感謝所有小伙伴的支持與貢獻!

8 GD32VW553硬件介紹
GD32VW553系列是兆易創新推出的高集成度無線微控制器,專為物聯網應用設計。該芯片采用RISC-V內核,主頻高達160MHz,并配備4MB Flash與320KB SRAM,為復雜應用提供了充裕的運算與存儲空間。其核心亮點在于集成了Wi-Fi 6 (802.11ax) 與Bluetooth LE 5.2雙模無線模塊,在提供高速、高容量、低延遲無線連接的同時,也具備優異的射頻性能和低功耗特性。此外,芯片還集成了UART、SPI、I2C、USB、以太網等豐富外設接口及硬件安全引擎,全面滿足智能家居、工業物聯網等場景對連接性、功能性和安全性的需求。
-
CAN
+關注
關注
59文章
3066瀏覽量
472732 -
監視器
+關注
關注
1文章
805瀏覽量
35091 -
RT-Thread
+關注
關注
32文章
1613瀏覽量
44818
發布評論請先 登錄
基于兆易創新GD32F527系列MCU的多媒體門禁系統解決方案
rt-thread studio新建gd32f450工程
兆易創新加入RT-Thread高級會員合作伙伴計劃 | 戰略新篇
基于RT-Thread與兆易創新GD32F527的工業級網絡-CAN透傳網關設計與實現 | 技術集結
基于GD32F310開發板在rt-thread上添加finsh及其shell功能
如何將RT-Thread系統移植到GD32F427上呢
Fibocom MCU之兆易創新 技術資料
RT-Thread上的CAN總線介紹以及驅動編寫
【直播預告】GD32F527高性能MCU全方位解析,與RT-Thread的全棧開發實戰 | 博觀講堂
基于 RT-Thread 和兆易創新GD32F527的CAN總線監視器 | 技術集結
評論