CAN总线开发一本全(4) - FlexCAN的驱动程序
CAN总线开发一本全(4) - FlexCAN的驱动程序
苏勇,2023年2月
文章目录
- CAN总线开发一本全(4) - FlexCAN的驱动程序
- 引言
- 从MindSDK获取FlexCAN驱动程序
- 数据结构
- 配置通信引擎的结构体类型
- 访问MB的结构体类型
- 配置ID过滤器的结构体类型
- FIFO相关的结构体类型
- API清单
- 配置通信引擎的API
- 访问MB的API
- 配置ID过滤器的API
- 中断和状态的API
- FIFO相关的API
- 样例工程
- 回环通信 flexcan_loopback
- 板对板直接通信 flexcan_b2b_tx & flexcan_b2b_rx
- 板对板请求远程帧通信 flexcan_b2b_req & flexcan_b2b_ack
- 总结
- 参考文献
引言
前文介绍了FlexCAN外设模块,一种典型的CAN总线引擎子系统的工作机制。那么,用户在软件开发平台对CAN总线引擎进行编程,需要根据硬件外设模块的功能进行建模,将对CAN总线通信引擎的操作封装起来,让开发者通过软件开发平台的数据结构和用户可编程应用接口(API)函数使用FlexCAN模块。基于灵动微电子微控制器的软件开发平台MindSDK,包含了集成FlexCAN外设的MM32F5270和MM32F0140微控制器,其中就有FlexCAN外设模块的驱动程序以及样例工程,以及对CAN总线通信协议CANopen的适配工程。本文将介绍MindSDK中FlexCAN驱动程序及样例工程,展现一种典型的CAN总线驱动程序的实现及应用场景。
从MindSDK获取FlexCAN驱动程序
通过MindSDK在线发布网站 https://mindsdk.mindmotion.com.cn 选择搭载了MM32F0140微控制器的POKT-F0140
开发板,就可以得到MM32F0140微控制器的软件开发包。如图x所示。
下载的软件开发包后pokt-f0140_mdk.zip
,其中就包含了FlexCAN的驱动程序源码,具体就是hal_flexcan.h
和hal_flexcan.c
两个源文件。如图x所示。
数据结构
这里列写FlexCAN驱动程序中的主要数据结构。
配置通信引擎的结构体类型
FLEXCAN_Init_Type
类型的结构体的变量,用于在初始化FlexCAN总线引擎时传入配置参数。
/*!* @brief This type of structure instance is used to keep the settings when calling the @ref FLEXCAN_Init() to initialize the general setting of FLEXCAN.*/
typedef struct
{uint8_t MaxXferNum; /*!< Max number of message buffer to be used. */uint32_t BitRate; /*!< Data bit per second when using FLEXCAN for transmision and reception. */uint32_t ClockFreqHz; /*!< Clock source frequency. */FLEXCAN_ClockSource_Type ClockSource; /*!< Clock source selection. */FLEXCAN_SelfWakeUp_Type SelfWakeUp; /*!< Stop mode self wake up source. */FLEXCAN_WorkMode_Type WorkMode; /*!< FLEXCAN function mode. */FLEXCAN_Mask_Type Mask; /*!< Filter work range for filtering the received frames. */FLEXCAN_TimConf_Type * TimConf; /*!< FLEXCAN timer and time synchronization setup. */bool EnableSelfReception; /*!< Whether to receive frames sent by FLEXCAN itself. */bool EnableTimerSync; /*!< Refresh the timer every frame reception. */
} FLEXCAN_Init_Type;
其中,FLEXCAN_TimConf_Type
类型用于指定FlexCAN总线位时间的配置参数。关于如何配置CAN总线的位时间,后续将有专门文章详解。
/*!* @brief This type of structure instance is used to keep the settings when calling the @ref FLEXCAN_SetTimingConf() to initialize the time configuration.*/
typedef struct
{bool EnableExtendedTime; /*!< If enable, the setting time range can be broader. */uint32_t PhaSegLen1; /*!< Phase segment 1. */uint32_t PhaSegLen2; /*!< Phase segment 2. */uint32_t PropSegLen; /*!< Propagation segment. Compensate for signal delays across the network.*/uint32_t JumpWidth; /*!< Resynchronize jump width. */uint32_t PreDiv; /*!< The divider for FLEXCAN clock source. */
} FLEXCAN_TimConf_Type;
访问MB的结构体类型
FLEXCAN_Mb_Type
类型的结构体,建立了对MB在物理存储空间的映射结构,软件使用该类型的结构体整体传入或读出硬件MB存储区中的内容。
/*!* @brief This type of structure instance is used to keep the settings when calling the @ref FLEXCAN_WriteTxMb() to set the mask for buffer.*/
typedef struct
{struct{uint32_t TIMESTAMP : 16; /*!< Free-running counter time stamp. */uint32_t LENGTH : 4; /*!< Length of Data in Bytes. */uint32_t TYPE : 1; /*!< Frame data type or remote type. */uint32_t FORMAT : 1; /*!< Frame extended format or standard format. */uint32_t RESERVED_0 : 1; /*!< Reservation. */uint32_t IDHIT : 9; /*!< Id filter number hit by fifo. */};struct{uint32_t ID :29; /*!< Frame Identifier. */uint32_t PRIORITY: 3; /*!< Local priority. */};union{struct{uint32_t WORD0; /*!< CAN Frame payload word0. */uint32_t WORD1; /*!< CAN Frame payload word1. */};struct{/* The sequence refers to the little-endian-storage and big-endian transfer. */uint8_t BYTE3; /*!< CAN Frame payload byte3. */uint8_t BYTE2; /*!< CAN Frame payload byte2. */uint8_t BYTE1; /*!< CAN Frame payload byte1. */uint8_t BYTE0; /*!< CAN Frame payload byte0. */uint8_t BYTE7; /*!< CAN Frame payload byte7. */uint8_t BYTE6; /*!< CAN Frame payload byte6. */uint8_t BYTE5; /*!< CAN Frame payload byte5. */uint8_t BYTE4; /*!< CAN Frame payload byte4. */};};
} FLEXCAN_Mb_Type;
FLEXCAN_RxMbConf_Type
类型的结构体,用于配置在MB中配置接收帧的部分属性,这相当于是FLEXCAN_Mb_Type
的轻量级版本,但不需要数据负载、数据长度、本地优先级等配置信息。
/*!* @brief This type of structure instance is used to keep the settings when calling the @ref FLEXCAN_SetRxMb() to set the mask for buffer.*/
typedef struct
{FLEXCAN_MbType_Type MbType; /*!< Data frame or Remote frame switcher. */FLEXCAN_MbFormat_Type MbFormat; /*!< Extended or standard id switcher. */uint32_t Id; /*!< Id value. */
} FLEXCAN_RxMbConf_Type;
FLEXCAN_MbCode_Type
枚举类型指定了可用的CODE命令。
/*!* @brief FLEXCAN Xfer MB frame code switcher.*/
typedef enum
{/* rx. */FLEXCAN_MbCode_RxInactive = 0u, /*!< Code for MB being not active. */FLEXCAN_MbCode_RxFull = 2u, /*!< Code for MB being full. */FLEXCAN_MbCode_RxEmpty = 4u, /*!< Code for MB being active and empty. */FLEXCAN_MbCode_RxOverrun = 6u, /*!< Code for MB being over written without accessing the received frame. */FLEXCAN_MbCode_RxRanswer = 10u, /*!< Code for Rx waiting for remote frame. */FLEXCAN_MbCode_RxBusy = 15u, /*!< Code for Rx updating MB. *//* rx. */FLEXCAN_MbCode_TxInactive = 8u, /*!< Code for data response for Tx inactive. */FLEXCAN_MbCode_TxAbort = 9u, /*!< Code for Tx abort after transmission. */FLEXCAN_MbCode_TxDataOrRemote = 12u, /*!< Code for data frame or remote frame transmission. */FLEXCAN_MbCode_TxTanswer = 14u, /*!< Code for data response for remote frame. */
} FLEXCAN_MbCode_Type;
配置ID过滤器的结构体类型
FLEXCAN_RxMbMaskConf_Type
可以用于配置全局ID过滤器和MB专属ID过滤器。
/*!* @brief This type of structure instance is used to keep the settings when calling the @ref FLEXCAN_SetGlobalMbMaskConf() to set the mask for buffer.*/
typedef struct
{FLEXCAN_MbType_Type MbType; /*!< Data frame or Remote frame switcher. */FLEXCAN_MbFormat_Type MbFormat; /*!< Extended or standard id switcher. */uint32_t IdMask; /*!< Id mask. */
} FLEXCAN_RxMbMaskConf_Type;
FIFO相关的结构体类型
FLEXCAN_RxFifoConf_Type
类型用于在启用FIFO功能时,配置FIFO的属性。
/*!* @brief This type of structure instance is used to keep the settings when calling the @ref FLEXCAN_EnableRxFifo() to initialize the fifo setting.*/
typedef struct
{FLEXCAN_FifoIdFilterFormat_Type FilterFormat; /*!< fifo filter format which will decide how to filter the fifo reception. */uint32_t IdFilterNum; /*!< The fifo filter element num. */uint32_t * IdFilterTable; /*!< Filter array to be set for Rx fifo. */FLEXCAN_FifoPriority_Type priority; /*!< Enable matching process start with fifo. */
} FLEXCAN_RxFifoConf_Type;
当使用FIFO时,需要使用FLEXCAN_RxFifoMaskConf_Type
结构体类型的变量配置ID过滤器。
/*!* @brief This type of structure instance is used to keep the settings when calling the @ref FLEXCAN_SetRxFifoGlobalMaskConf() to set the conf for fifo mask filter.*/
typedef struct
{FLEXCAN_MbType_Type MbType; /*!< Data frame or Remote frame switcher. */FLEXCAN_MbFormat_Type MbFormat; /*!< Extended or standard id switcher. */FLEXCAN_FifoIdFilterFormat_Type FilterFormat; /*!< fifo filter format. */union{uint32_t RxIdA; /*!< The fifo Id setting for filter format A. */uint16_t RxIdB[2]; /*!< The fifo Id setting for filter format B. */uint8_t RxIdC[4]; /*!< The fifo Id setting for filter format C. */};
} FLEXCAN_RxFifoMaskConf_Type;
还有更多枚举类型和宏常量,可继续查阅hal_flexcan.h
源文件。
API清单
FlexCAN驱动的API相对较多,这里做了个分类,便于快速索引。更详细的内容可见源码。
配置通信引擎的API
使用FlexCAN外设之前需要初始化驱动引擎。
void FLEXCAN_Enable(FLEXCAN_Type * FLEXCANx, bool enable);
void FLEXCAN_DoSoftReset(FLEXCAN_Type * FLEXCANx);
bool FLEXCAN_Init(FLEXCAN_Type * FLEXCANx, FLEXCAN_Init_Type * init);
void FLEXCAN_SetTimingConf(FLEXCAN_Type * FLEXCANx, FLEXCAN_TimConf_Type * conf);
void FLEXCAN_EnableFreezeMode(FLEXCAN_Type * FLEXCANx, bool enable);
访问MB的API
这一组API中,第二个参数channel
,对应的是MB列表中的索引,FlexCAN中有16个MB,对应channel
的取值可以是0-15。FlexCAN外设中MB的内存区是ECC的,在使用之前必须通过FLEXCAN_ResetMb()函数复位选定的MB。
void FLEXCAN_ResetMb(FLEXCAN_Type * FLEXCANx, uint32_t channel);
void FLEXCAN_SetMbCode(FLEXCAN_Type * FLEXCANx, uint32_t channel, FLEXCAN_MbCode_Type code);
void FLEXCAN_SetRxMb(FLEXCAN_Type * FLEXCANx, uint32_t channel, FLEXCAN_RxMbConf_Type * conf);
bool FLEXCAN_WriteTxMb(FLEXCAN_Type * FLEXCANx, uint32_t channel, FLEXCAN_Mb_Type * mb);
bool FLEXCAN_ReadRxMb(FLEXCAN_Type * FLEXCANx, uint32_t channel, FLEXCAN_Mb_Type * mb);
配置ID过滤器的API
ID过滤器分为全局过滤器和MB专属的过滤器,对应有各自的过滤码(掩码)。
void FLEXCAN_SetGlobalMbMaskConf(FLEXCAN_Type * FLEXCANx, FLEXCAN_RxMbMaskConf_Type * conf);
void FLEXCAN_SetRxMbIndividualMaskConf(FLEXCAN_Type * FLEXCANx, uint32_t channel, FLEXCAN_RxMbMaskConf_Type * conf);
中断和状态的API
FlexCAN的中断和状态标志位分别面向FlexCAN引擎和MB,另外还有一些属性状态,例如计数器、CRC值等。
void FLEXCAN_EnableInterrupts(FLEXCAN_Type * FLEXCANx, uint32_t interrupts, bool enable);
uint32_t FLEXCAN_GetStatus(FLEXCAN_Type * FLEXCANx);
void FLEXCAN_ClearStatus(FLEXCAN_Type * FLEXCANx, uint32_t flags);
void FLEXCAN_EnableMbInterrupts(FLEXCAN_Type * FLEXCANx, uint32_t interrupts, bool enable);
uint32_t FLEXCAN_GetMbStatus(FLEXCAN_Type * FLEXCANx);
void FLEXCAN_ClearMbStatus(FLEXCAN_Type * FLEXCANx, uint32_t mbs);
uint32_t FLEXCAN_GetTxError(FLEXCAN_Type * FLEXCANx);
uint32_t FLEXCAN_GetRxError(FLEXCAN_Type * FLEXCANx);
uint32_t FLEXCAN_GetMatchCrcValue(FLEXCAN_Type * FLEXCANx, uint32_t * channel);
FIFO相关的API
在配置好FlexCAN引擎后,通过FLEXCAN_EnableRxFifo()函数启用FIFO功能,之后就可以以FIFO的方式访问MB。
bool FLEXCAN_EnableRxFifo(FLEXCAN_Type * FLEXCANx, FLEXCAN_RxFifoConf_Type * conf);
void FLEXCAN_SetRxFifoGlobalMaskConf(FLEXCAN_Type * FLEXCANx, FLEXCAN_RxFifoMaskConf_Type * conf);
bool FLEXCAN_ReadRxFifo(FLEXCAN_Type * FLEXCANx, FLEXCAN_Mb_Type * mb);
void FLEXCAN_EnableFifoDMA(FLEXCAN_Type * FLEXCANx, bool enable);
uint32_t FLEXCAN_GetFifoAddr(FLEXCAN_Type * FLEXCANx);
样例工程
MindSDK为FlexCAN驱动设计了一些样例工程,用于演示在一些典型场景中使用FlexCAN的方法。这些样例工程也可以运行在搭载MM32F0140
微控制器的POKT-F0140
开发板上。见表x。
工程 | 说明 |
---|---|
driver_examples\flexcan\flexcan_loopback | 回环通信实验 |
driver_examples\flexcan\flexcan_b2b_tx | 板对板直接通信,发送方,发送数据帧 |
driver_examples\flexcan\flexcan_b2b_rx | 板对板直接通信,接收方,接收数据帧 |
driver_examples\flexcan\flexcan_b2b_req | 板对板请求远程帧通信,请求方,发送远程帧,捕获数据帧 |
driver_examples\flexcan\flexcan_b2b_ack | 板对板请求远程帧通信,响应方,捕获远程帧,发送数据帧 |
driver_examples\flexcan\flexcan_b2b_tx_self | 板对板直接通信,发送方,发送数据帧,但同时监听自己发送的数据帧 |
回环通信 flexcan_loopback
FlexCAN外设的回环通信功能,就是将FlexCAN外设模块的Tx信号和Rx信号在模块内部,由软件配置电路连通,可用于验证在未接入CAN总线网络时,节点软件本身能否正常收发通信帧。当完成验证后,仅需要通过软件关闭回环通信功能,即可用已经验证过的收发过程同外部CAN总线网络对接。
flexcan_loopback
样例工程中的源码,展示了使用FlexCAN模块回环通信的方法。除了启用了回环通信的功能之外,其余对CAN通信帧的发送操作和接收操作同正常对外通信无异,因此,本工程也是使用FlexCAN驱动收发CAN通信帧的最基础的用例。另外,由于使用了回环通信,不需要专门准备发送和接收两块运行不同程序的电路板,仅用一块开发板即可完成实验。
在flxcan_loopback
样例工程的main()
函数中,除了必要的初始化微控制器的时钟、引脚和后台人机交互端口外,先初始化了FlexCAN模块,然后在while(1)
循环中,先准备一组数据作为CAN通信帧的数据负载发送出去,等待发送完成后,再接收一个CAN通信帧,等待接收完成后打印到终端显示接收帧中的数据负载,周而复始。发送帧和接收帧的ID使用同一个 APP_FLEXCAN_XFER_ID
,因此可以实现收发。
/** Variables.*/
volatile bool app_flexcan_rx_flag = false; /* Flag the message buffer reception state. */
FLEXCAN_Mb_Type app_flexcan_rx_mb; /* For message buffer rx frame storage. */
uint8_t app_flexcan_tx_buf[APP_FLEXCAN_XFER_BUF_LEN]; /* The flexcan tx buffer for tx mb frame preparation. */
uint8_t app_flexcan_rx_buf[APP_FLEXCAN_XFER_BUF_LEN]; /* The flexcan rx buffer for rx mb frame storage. *//** Declerations.*/
void app_flexcan_init(void); /* Setup flexcan. */
void app_flexcan_tx(uint8_t *tx_buf); /* Send frame. */
void app_flexcan_read(uint8_t *rx_buf); /* Receive frame. *//** Functions.*/
int main(void)
{BOARD_Init();printf("\r\nflexcan_loopback example.\r\n");/* Setup the flexcan module.*/app_flexcan_init();printf("press any key to send loop back frame with id 0x%x.\r\n", (unsigned)APP_FLEXCAN_XFER_ID);while (1){getchar();/* Send a message through flexcan. */for (uint8_t i = 0u; i < APP_FLEXCAN_XFER_BUF_LEN; i++){app_flexcan_tx_buf[i] = ( app_flexcan_tx_buf[i] + i) % 256u;}app_flexcan_tx(app_flexcan_tx_buf);printf("app_flexcan_tx() done.\r\n");/* Wait for reception. */while (!app_flexcan_rx_flag) /* This flag will be on when the Rx interrupt is asserted. */{}app_flexcan_rx_flag = false;printf("app_flexcan_read(): ");for (uint8_t i = 0u; i < APP_FLEXCAN_XFER_BUF_LEN; i++){printf("%u ", (unsigned)app_flexcan_rx_buf[i]);}printf("\r\n\r\n");}
}
其中,初始化FlexCAN模块的函数 app_flexcan_init()
,初始化了FlexCAN通信引擎,包括配置CAN总线通信的位时钟,配置好了发送MB和接收MB,分别使用两个不同的MB索引BOARD_FLEXCAN_TX_MB_CH
和BOARD_FLEXCAN_RX_MB_CH
,并分别设定它们为有效的非激活状态。最后还启用了发送完成和接收到数据帧的中断。虽然这里也可以使用纯粹的轮询标志位实现流控制,但使用中断方式便于向后续用例中过渡。在中断服务程序中,当检测到有接收帧时,从接收帧的MB中搬运接收到的数据负载到内存变量app_flexcan_rx_buf
中,然后清接收标志位。有实现代码如下:
/* Setup the flexcan module. */
void app_flexcan_init(void)
{/* Set bit timing. */FLEXCAN_TimConf_Type flexcan_tim_conf;flexcan_tim_conf.EnableExtendedTime = true;flexcan_tim_conf.PhaSegLen1 = 5u;flexcan_tim_conf.PhaSegLen2 = 1u;flexcan_tim_conf.PropSegLen = 2u;flexcan_tim_conf.JumpWidth = 1u;/* Setup flexcan. */FLEXCAN_Init_Type flexcan_init;flexcan_init.MaxXferNum = APP_FLEXCAN_XFER_MaxNum; /* The max mb number to be used. */flexcan_init.ClockSource = FLEXCAN_ClockSource_Periph; /* Use peripheral clock. */flexcan_init.BitRate = APP_FLEXCAN_XFER_BITRATE; /* Set bitrate. */flexcan_init.ClockFreqHz = BOARD_FLEXCAN_CLOCK_FREQ; /* Set clock frequency. */flexcan_init.SelfWakeUp = FLEXCAN_SelfWakeUp_BypassFilter; /* Use unfiltered signal to wake up flexcan. */flexcan_init.WorkMode = FLEXCAN_WorkMode_LoopBack; /* Normal workmode, can receive and transport. */flexcan_init.Mask = FLEXCAN_Mask_Global; /* Use global mask for filtering. */flexcan_init.EnableSelfReception = true; /* Must receive mb frame sent by self. */flexcan_init.EnableTimerSync = true; /* Every tx or rx done, refresh the timer to start from zero. */flexcan_init.TimConf = &flexcan_tim_conf; /* Set timing sychronization. */FLEXCAN_Init(BOARD_FLEXCAN_PORT, &flexcan_init);/* Set tx mb. */FLEXCAN_ResetMb(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_TX_MB_CH);FLEXCAN_SetMbCode(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_TX_MB_CH, FLEXCAN_MbCode_TxInactive);/* Set rx mb. */FLEXCAN_RxMbConf_Type flexcan_mb_conf;flexcan_mb_conf.Id = APP_FLEXCAN_XFER_ID; /* Id for filtering with mask and receiving. */flexcan_mb_conf.MbType = FLEXCAN_MbType_Data; /* Only receive standard data frame. */flexcan_mb_conf.MbFormat = FLEXCAN_MbFormat_Standard;FLEXCAN_SetRxMb(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_RX_MB_CH, &flexcan_mb_conf);FLEXCAN_SetMbCode(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_RX_MB_CH, FLEXCAN_MbCode_RxEmpty); /* Set for receiving. *//* Enable intterupts for rx mb. */FLEXCAN_EnableMbInterrupts(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_RX_MB_INT, true);NVIC_EnableIRQ(BOARD_FLEXCAN_IRQn);
}/* Interrupt request handler. */
void BOARD_FLEXCAN_IRQHandler(void)
{if (0u!= (FLEXCAN_GetMbStatus(BOARD_FLEXCAN_PORT) & BOARD_FLEXCAN_RX_MB_STATUS) ){/* Read the message. */app_flexcan_read(app_flexcan_rx_buf);/* Clear flexcan mb interrupt flag. */FLEXCAN_ClearMbStatus(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_RX_MB_STATUS);/* Update the flag. */app_flexcan_rx_flag = true;}
}
其中,flexcan_init.WorkMode = FLEXCAN_WorkMode_LoopBack;
即指定启用了回环模式。
发送数据帧的操作被封装成app_flexcan_tx()
函数。在该函数中,将即将发送的数据填充到MB结构体的数据负载中,再将整个帧结构写入到FlexCAN硬件的MB内存区,最后过向发送MB的内存区写命令码,启动发送过程。注意,这里使用预分配的帧ID(而不是作为函数传参可配置),发送数据帧(而不是远程帧)。有源代码如下:
/* Send a message frame. */
void app_flexcan_tx(uint8_t * tx_buf)
{/* Prepare the mb to be sent. */FLEXCAN_Mb_Type mb;mb.ID = APP_FLEXCAN_XFER_ID; /* Indicated ID number. */mb.TYPE = FLEXCAN_MbType_Data; /* Data frame type. */mb.FORMAT = FLEXCAN_MbFormat_Standard; /* STD frame format. */mb.PRIORITY = APP_FLEXCAN_XFER_PRIORITY; /* The priority of the frame mb. */mb.BYTE0 = tx_buf[0]; /* Set the data payload. */mb.BYTE1 = tx_buf[1];mb.BYTE2 = tx_buf[2];mb.BYTE3 = tx_buf[3];mb.BYTE4 = tx_buf[4];mb.BYTE5 = tx_buf[5];mb.BYTE6 = tx_buf[6];mb.BYTE7 = tx_buf[7];mb.LENGTH = APP_FLEXCAN_XFER_BUF_LEN; /* Set the size of data payload. */FLEXCAN_WriteTxMb(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_TX_MB_CH, &mb);/* Write code to send. */FLEXCAN_SetMbCode(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_TX_MB_CH, FLEXCAN_MbCode_TxDataOrRemote);
}
接收数据帧的操作被封装成app_flexcan_read()
函数。这里执行的操作,仅仅是从预定的FlexCAN硬件的接收MB内存区中把整个MB读出来,然后从MB结构类型中提取数据负载,作为传参返回给函数调用者。有源代码如下:
/* Receive a message frame. */
void app_flexcan_read(uint8_t *rx_buf)
{/* Read the info from mb and reconstruct for understanding. */FLEXCAN_ReadRxMb(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_RX_MB_CH, &app_flexcan_rx_mb);rx_buf[0] = app_flexcan_rx_mb.BYTE0;rx_buf[1] = app_flexcan_rx_mb.BYTE1;rx_buf[2] = app_flexcan_rx_mb.BYTE2;rx_buf[3] = app_flexcan_rx_mb.BYTE3;rx_buf[4] = app_flexcan_rx_mb.BYTE4;rx_buf[5] = app_flexcan_rx_mb.BYTE5;rx_buf[6] = app_flexcan_rx_mb.BYTE6;rx_buf[7] = app_flexcan_rx_mb.BYTE7;
}
板对板直接通信 flexcan_b2b_tx & flexcan_b2b_rx
板对板直接通信的用例,需要两块开发板,一个作为接收方,另一个作为发送方,由发送方发送CAN通信帧到接收方,从而实现两块电路板通过CAN总线传输数据的过程。这个实验的样例工程同基本的flexcan_loopback
工程非常相近,只是收发过程拆分成两个独立的工程。注意,收发两个工程中使用CAN通信帧的ID也是约定一致的。
相对于flexcan_loop
工程,独立的flexcan_b2b_rx
和flexcan_b2b_tx
工程中对FlexCAN模块的初始化过程,不再启用回环模式,而是常规模式。见源代码如下:
/* Set up the flexCAN module. */
void app_flexcan_init(void)
{.../* Setup FlexCAN. */FLEXCAN_Init_Type flexcan_init;...flexcan_init.WorkMode = FLEXCAN_WorkMode_Normal; /* Normal workmode, can receive and transport. */...FLEXCAN_Init(BOARD_FLEXCAN_PORT, &flexcan_init);...
}
接收方电路板运行flexcan_b2b_rx
工程的程序:初始化FlexCAN通信引擎后,配置接收MB,开中断等待接收帧完成。在后台的FlexCAN中断服务程序中,一旦捕获到约定ID的通信帧,就将接收帧中的数据负载转存到内存中的app_flexcan_rx_buf
变量中,并且通过标志变量app_flexcan_rx_flag
告知前台程序,然后清除硬件标志位。在前台的while(1)
循环中,一旦接收到约定ID的通信帧,就在终端界面打印出接收到帧的数据内容。
发送方电路板运行flexcan_b2b_tx
工程的程序:初始化FlexCAN通信引擎后,配置发送MB,开中断等待发送帧完成。前台的while(1)
循环中,由用户触发向发送MB填充数据负载,并发送预定帧数据的操作。在后台的FlexCAN中断服务程序中,一旦发送完成约定ID的通信帧,就通过标志变量app_flexcan_tx_flag
告知前台程序,然后清除硬件标志位。
运行实验时,电脑同时接入发送方和接收方的两个终端界面,先在发送方的终端界面中输入任意字符启动发送帧过程,将有CAN通信帧从发送方上传CAN总线,接收方亦会从总线上捕获到约定同一ID的数据帧,解析出其中的数据负载再显示到接收方的终端界面上。
板对板请求远程帧通信 flexcan_b2b_req & flexcan_b2b_ack
板对板请求远程帧通信的用例,需要两块开发板,一个作为请求方,另一个作为响应方,由请求方发送CAN通信远程帧到应答方,应答方收到远程帧后,准备好数据帧,再将响应的数据帧发送至CAN总线,由请求方捕获,从而实现两块电路板通过CAN总线读数据的过程。相对于板对板直接通信的写数据过程,板对板请求远程帧通信过程实现的是读数据过程。这个实验的样例工程同基本的flexcan_loopback
工程,以及板对板直接通信的两个工程非常相近,只是收发过程拆分成两个独立的工程,并且原来的发送方先发出远程帧再接收数据帧,而原来的接收方将先等待远程帧再发送数据帧。注意,收发两个工程中使用CAN通信帧的ID也是约定一致的。
请求方和响应方的两个工程,同flexcan_loopback
工程对FlexCAN引擎的初始化过程完全相同,并且在各自的工程中,也需要处理发送和接收过程。不同之处仅在于其中的一个通信帧从数据帧变成的远程帧。
响应方电路板运行flexcan_b2b_ack
工程的程序:初始化FlexCAN通信引擎后,配置接收MB,配置发送MB,开中断等待发送和接收帧完成。其中接收MB的帧类型为远程帧FLEXCAN_MbType_Remote
。
void app_flexcan_init(void)
{.../* Set rx mb. */FLEXCAN_RxMbConf_Type flexcan_mb_conf;flexcan_mb_conf.Id = APP_FLEXCAN_XFER_ID; /* Id for filtering with mask and receiving. */flexcan_mb_conf.MbType = FLEXCAN_MbType_Remote; /* Only receive remote data frame. */flexcan_mb_conf.MbFormat = FLEXCAN_MbFormat_Standard;FLEXCAN_SetRxMb(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_RX_MB_CH, &flexcan_mb_conf);FLEXCAN_SetMbCode(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_RX_MB_CH, FLEXCAN_MbCode_RxEmpty); /* Set for receiving. */...
}
请求方电路板运行flexcan_b2b_req
工程的程序:初始化FlexCAN通信引擎后,配置接收MB,配置发送MB,开中断等待发送和接收帧完成。在发送请求帧时,设定MB的帧类型为远程帧FLEXCAN_MbType_Remote
。
/* Send a message frame. */
void app_flexcan_req()
{FLEXCAN_Mb_Type mb;mb.ID = APP_FLEXCAN_XFER_ID; /* Indicated ID number. */mb.TYPE = FLEXCAN_MbType_Remote; /* Setup remote frame. */mb.FORMAT = FLEXCAN_MbFormat_Standard; /* STD frame format. */mb.PRIORITY = APP_FLEXCAN_XFER_PRIORITY; /* The priority of the frame mb. */mb.LENGTH = APP_FLEXCAN_REQ_BUF_LEN; /* Set the workload size. */FLEXCAN_WriteTxMb(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_TX_MB_CH, &mb); /* Send. *//* Write code to send. */FLEXCAN_SetMbCode(BOARD_FLEXCAN_PORT, BOARD_FLEXCAN_TX_MB_CH, FLEXCAN_MbCode_TxDataOrRemote);
}
当运行通信过程时:先启动应答方的程序,准备好响应过程;再请求方的程序,发出请求的远程帧到CAN总线上,应答方从CAN总线上捕获到请求的远程帧后,在本机生成数据负载组,使用同远程帧相同的ID,装成数据帧,再上传至CAN总线;请求方此时可以捕获到CAN总线上的同ID的数据帧,显示到终端界面。周而复始。
总结
MindSDK中设计的FlexCAN驱动程序,对FlexCAN外设进行了建模,创建了一系列数据结构和API,能够为软件开发者提供初始化FlexCAN通信引擎,通过MB的结构类型发送数据帧、远程帧等功能。MindSDK为FlexCAN驱动设计的一些样例工程,演示了在一些典型应用场景中(回环通信、板对板直接通信、板对板请求远程帧通信)使用FlexCAN驱动的方法。
参考文献
- MindSDK在线发布网站,https://mindsdk.mindmotion.com.cn
本文中使用的MindSDK样例工程,可以从MindSDK在线网站上获取最新版,亦可从从此处下载当前版本的软件包: https://download.csdn.net/download/suyong_yq/87501465
相关文章:

CAN总线开发一本全(4) - FlexCAN的驱动程序
CAN总线开发一本全(4) - FlexCAN的驱动程序 苏勇,2023年2月 文章目录CAN总线开发一本全(4) - FlexCAN的驱动程序引言从MindSDK获取FlexCAN驱动程序数据结构配置通信引擎的结构体类型访问MB的结构体类型配置ID过滤器的…...

如何分析linux tcp/ip 丢包问题
引用手把手教你用Dropwatch诊断问题通过dropwatch定位系统内核丢包Finding out if/why a server is dropping packetsgithub source coed: pavel-odintsov/drop_watchHow to drop a packet in Linux in more ways than one试试Linux下的ip命令,ifconfig已经过时了Ho…...

旅游规划(树型dp)
W 市的交通规划出现了重大问题,市政府下定决心在全市各大交通路口安排疏导员来疏导密集的车流。 但由于人员不足,W 市市长决定只在最需要安排人员的路口安排人员。 具体来说,W 市的交通网络十分简单,由 n 个交叉路口和 n−1 条街道…...

【C++】string类的模拟实现
当你将放弃作为一种习惯,你一辈子也不会有出息… 文章目录一、Default member functions1.Constructor2.Copy constructor(代码重构:传统写法和现代写法)3.operator(代码重构:传统写法和现代写法ÿ…...

笔记(一)——STL容器
容器分类:序列式容器:每个元素都有固定位置,取决于插入的时机和地点,和元素无关,如vector、deque、list、stack、queue。关联式容器:元素位置取决于特定的排序准则,和插入顺序无关,如…...

红黑树
红黑树是一个相对的平衡,减少了旋转的消耗 一个节点不是红的就是黑的根节点是黑的一个节点是红的,孩子是黑的(没有连续的红色节点)对于每个节点,从该节点到后代节点的简单路径,都包含相同的黑色࿰…...

RIP路由协议的更新(电子科技大学TCP/IP第二次实验)
一.实验目的 1、掌握 RIP 协议在路由更新时的发送信息和发送方式 2、掌握 RIP 协议的路由更新算法 二.预备知识 1、静态路由选择和动态路由选择 2、内部网关协议和外部网关协议 3、距离向量路由选择 三.实验原理 RIP 协议(…...

基于JWT实现用户身份认证
常见场景 账号/密码登录、手机号验证码登录、微信扫码登录 解决方案 基于Session认证方案 什么是session认证方案 服务端生成httpsession认证(内存-sessionId)sessionId写到浏览器cookie浏览器请求的header中自动带sessionId到服务端服务端校验sessionId是否合法 优点 .…...

SaltStack 远程命令执行漏洞(CVE-2020-16846)
目录 (一)漏洞描述 (二)漏洞复现 1、在vulhub上启动docker 2、访问docker靶机 https /ip:8000...

SAP 详细解析成本收集器
成本收集器作为成本对象,主要应用于按期间进行成本核算的情况,在这种情况下会把产品创建为成本收集器,实际成本的收集和差异的结算全部按照成本收集器进行处理,财务的成本分析也针对成本收集器进行。 成本收集器是按期间核算&am…...

Vision Transformer学习了什么-WHAT DO VISION TRANSFORMERS LEARN? A VISUAL EXPLORATION
WHAT DO VISION TRANSFORMERS LEARN? A VISUAL EXPLORATION 文章地址 代码地址 摘要 视觉转换器( Vision Transformers,ViTs )正在迅速成为计算机视觉的事实上的架构,但我们对它们为什么工作和学习什么知之甚少。虽然现有研究对卷积神经网络的机制进…...

一种全新的图像滤波理论的实验(三)
一、前言 2023年02月22日,我发布了滤波后,为针对异常的白色和黑色像素进行处理的实验,本次发布基于上下文处理的方案的实验,目的是通过基于加权概率模型滤波后,在逆滤波时直接修复大量的白色和黑色的异常像素…...

CV——day79 读论文:基于小目标检测的扩展特征金字塔网络
Extended Feature Pyramid Network for Small Object DetectionI. INTRODUCTIONII. RELATED WORKA. 深层物体探测器B. 跨尺度特征C. 目标检测中的超分辨率III. OUR APPROACHA. 扩展特征金字塔网络B. 特征纹理传输C. 交叉分辨蒸馏IV. EXPERIMENTSA. Experimental Settings1&…...

智能家居项目(五)测试串口功能
目录 一、写一个单独测试串口的demo 二、直接运行上一篇智能家居的代码 一、写一个单独测试串口的demo 1、TTL串口与树莓派的连接方式 (1)TTL的RXD和TXD针脚连接到树莓的TXD和RXD上(T–>R R–>T),交叉连&…...

2023年全国最新道路运输从业人员精选真题及答案7
百分百题库提供道路运输安全员考试试题、道路运输从业人员考试预测题、道路安全员考试真题、道路运输从业人员证考试题库等,提供在线做题刷题,在线模拟考试,助你考试轻松过关。 71.根据《中华人民共和国安全生产法》,生产经营单位…...

python的所有知识点(含讲解),不看就亏死了
目录 简介 特点 搭建开发环境 版本 hello world 注释 文件类型 变量 常量 数据类型 运算符和表达式 控制语句 数组相关 函数相关 字符串相关 文件处理 对象和类,注:不是那个对象!!!!&…...

【Servlet篇】Response对象详细解读
文章目录Response 继承体系Response 设置响应数据设置响应行数据设置响应头数据设置响应体数据Response 重定向Response 响应字符数据Response 响应字节数据Response 继承体系 前面说到,我们使用 Request 对象来获取请求数据,使用 Response 对象来设置响…...

SAP FICO期初开账存货导入尾差
一、问题 1.AFS物料网格级别库存导入先除再乘有尾差: 旧系统数据迁移自两个系统:一个管理数量账(网格级别),一个管理金额账(物料级别) 2.MB52分工厂与MB5L分工厂统计差异: M…...

微信商城小程序怎么做_分享实体店做微信商城小程序制作步骤
各行各业都在用微商城小程序开店,不管是餐饮店还是便利店,还是五金店。都是可以利用微信小程序开一个线上店铺。实现线上跟线下店铺更加全面的结合。维护好自己的老客户。让您的客户给您拉新,带来新客户。小程序经过这几年的快速发展和不断升…...

【moment.js】时间格式化插件
Moment.js 用于在JavaScript中解析,验证,操作和显示日期和时间。是一款在项目中使用频率极高的时间格式化工具,Ant Design Vue 组件中就是使用它来处理时间的。 安装 npm install moment --save # npm yarn add moment # Ya…...

微信小程序开发【壹】
随手拍拍💁♂️📷 日期: 2023.02.24 地点: 杭州 介绍: 2023.02.24上午十点,路过学院的教学楼时🏢,突然看见了一团粉红色。走进一看是一排梅花🌸,赶在它们凋零前,将它们定格在我的相…...

2 k-近邻算法
0 问题引入 想一想:下面图片中有三种豆,其中三颗豆品种未知,如何判断他们类型? 1 KNN概述 1.1 KNN场景 电影可以按照题材分类,那么如何区分 动作片 和 爱情片 呢? 动作片:打斗次数更多爱情…...

深入探究文件I/O
目录Linux 系统如何管理文件静态文件与inode文件打开时的状态返回错误处理与errnostrerror 函数perror 函数exit、_exit、_Exit_exit()和_Exit()函数exit()函数空洞文件概念实验测试O_APPEND 和O_TRUNC 标志O_TRUNC 标志O_APPEND 标志多次打开同一个文件验证一些现象多次打开同…...

【LeetCode】剑指 Offer(9)
目录 题目:剑指 Offer 25. 合并两个排序的链表 - 力扣(Leetcode) 题目的接口: 解题思路: 代码: 过啦!!! 题目:剑指 Offer 26. 树的子结构 - 力扣&#…...

python 遍历可迭代对象的方法
python 遍历可迭代对象的方法 可迭代(iterable) 迭代(遍历)就是按照某种顺序逐个访问对象中的每一项。 Python中有很多对象都是可以通过for语句来直接遍历的,例如list、string、dict等,这些对象都是可迭代的,被称为可迭代对象。 可以将可迭…...

【数据库】 第11章 并发控制
第11章 并发控制 事务 事务:(从微观角度,或者从DBMS角度)是数据库管理系统提供的控制数 据操作的一种手段,通过这一手段,应用程序员将一系列的数据库操作组合 在一起作为一个整体进行操作和控制,以便数据库管理系统能…...

Python3-数字
Python3 数字(Number) Python 数字数据类型用于存储数值。 数据类型是不允许改变的,这就意味着如果改变数字数据类型的值,将重新分配内存空间。 Python 支持三种不同的数值类型: 整型(int) - 通常被称为是整型或整数,是正或负整数&#x…...

(四十一)Read Committed隔离级别是如何基于ReadView机制实现的?
今天我们来给大家讲一下,基于之前我们说的ReadView机制是如何实现Read Committed隔离级别的,那么当然了,首先就是要先做一些简单的回顾。所谓的Read Committed隔离级别,我们可以用骚气一点的名字,就是简称为 RC 隔离级…...

React echarts封装
做大屏的时候经常会遇到 echarts 展示,下面展示在 React (^18.2.0) 中对 echarts (^5.4.0) 的简单封装。 文章首发于https://blog.fxss.work/react/echarts封装.html,样例查看 echarts 封装使用 props 说…...

【C语言进阶】了解计算机的程序环境和预处理过程 掌握计算机预处理操作
📝个人主页:Sherry的成长之路 🏠学习社区:Sherry的成长之路(个人社区) 📖专栏链接:C语言进阶 🎯长路漫漫浩浩,万事皆有期待 文章目录1.编译与链接1.1 程…...