中学网站建设工作实施方案,自媒体 wordpress,网站跳出率如何计算,百度双站和响应式网站的区别本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写#xff0c;需要的同学可以在这里获取#xff1a; https://item.taobao.com/item.htm?id728461040949
配套资料获取#xff1a;https://renesas-docs.100ask.net
瑞萨MCU零基础入门系列教程汇总#xff1a; ht…本教程基于韦东山百问网出的 DShanMCU-RA6M5开发板 进行编写需要的同学可以在这里获取 https://item.taobao.com/item.htm?id728461040949
配套资料获取https://renesas-docs.100ask.net
瑞萨MCU零基础入门系列教程汇总 https://blog.csdn.net/qq_35181236/article/details/132779862 第17章 CAN FD 模块
本章目标
使用 RASC 快速配置 CAN FD 模块学会使用 CAN FD 的 API 进行通信
17.1 RA6M5 的 CAN FD
17.1.1 CAN FD 模块简介
对于瑞萨的 RA6M5 处理器它的 CAN FD 有如下特征
兼容性CAN2.0和CAN FD通信速率在1Mbps~8Mbps模块时钟50MHz有两个通信通道每通道 4-16 个传输消息缓冲区TX MB支持标准帧11bit的ID和扩展帧29bit的ID个全局接收消息缓冲区RX MB2-8个全局接收 FIFORX FIFO可以单独将每个筛选规则配置为根据以下条件接受邮件
编号标准或扩展IDIDE 位数据或远程帧RTR 位ID/IDE/RTR掩码最小DLC数据长度值
对于CAN FD的中断分为可配置的全局RX FIFO中断、信道TX中断、全局错误中断和通道错误中断其中又进行了如下细分
可配置的全局 RX FIFO 中断
可单独配置每个 FIFO在接收到特定深度或每收到一条消息触发中断
全局错误中断
数据链路校验邮件丢失FD 有效负载溢出
通道错误中断
总线错误警告错误被动错误总线断开总线断开恢复超载总线锁仲裁损失传输中止
17.1.2 CAN FD系统框图
RA6M5的CAN FD外设的系统框图如下图所示 CAN的通道和时钟
RA6M5的CAN有两个通道CAN0和CAN1其通信引脚对应框图中的CTX0/1和CRX0/1这两个通道的CAN使用各自独立的通信速率。
CANFDCLK和CANMCLK为CAN-FD 模块的输入时钟可以选择其一经过波特率预分频器Baud rate prescaler进行分频后输入到CAN协议控制器Protoco- controller。 该输入时钟对于计算CAN波特率非常重要。
CAN的相关寄存器
RA6M5的CAN具有很多个寄存器大致分为控制寄存器、状态寄存器、过滤器寄存器、错误寄存器、FIFO寄存器和发送消息队列寄存器。这些寄存器总数特别多且本书是基于FSP库进行的面向对象的编程而直接控制寄存器驱动外设十分的复杂是面向过程的编程思想与本书的中心主旨相悖所以请读者自行阅读手册查阅寄存器信息。
过滤器
接收过滤器Acceptance filter和接收过滤器列表AFLAcceptance filter list用于实现CAN的接收过滤功能。而对于使用CAN来说接收过滤功能至关重要使用CAN就绕不过CAN外设模块中的接收过滤器后面在代码中需要手动配置接收过滤器列表AFLAcceptance filter list。
CAN的中断
中断生成器Interrupt generator用于生成CAN相关的中断信号包含如下信号
成功接收并存到RX FIFO中断全局错误中断通道相关的传输中断通道错误中断从COM FIFO成功接收中断
另外两个CAN通道的CRX引脚可用于产生通道唤醒中断信号通道唤醒中断CRX0、CRX1。
17.1.3 通信速率
CAN FD的波特率基础计算公式如下 通过手册中关于DLL_CLOCK和对Bit Timing的说明和计算上述公式可以换算成为下面这个公式 各参数取值范围见下表 用户需要根据应用场景需求计算表中的参数值在FSP中填入这些值后生成对应代码使得CAN FD的通信速率满足需求。
但是实际上在RASC中配置的时候用户在Bitrate|Automatic中填入预期的波特率时生成代码自动计算这些数值。
17.1.4CAN FD的测试模式
监听模式
SO11898-1推荐一种可选的总线监控模式。在此模式下CAN通道能够接收有效的数据帧和有效的远程帧。但是它只在CAN总线上发送隐性位不允许发送显性位。如果CAN引擎需要发送显性位ACK位、过载标志、活动错误标志则该位在内部路由以便CAN引擎将其监控为显性。外部TX引脚保持隐性状态。
该模式可用于波特率检测。在此模式下如果发生总线错误并启用了中断则会产生错误中断。在此模式下不允许从该通道的任何正常TX消息缓冲区或TX/GW FIFO请求传输。
注意如果消息存储在GWFIFO或路由TXQ中请确保发送通道不处于监听模式以便不会从GW FIFO或路由TXQ请求此通道的传输。 外部环回测试
在自检模式0中CAN引擎将自己发送的消息视为通过CAN收发器接收到的消息并将它们存储到其接收消息缓冲区中。为了独立于外部激励引擎会生成自己的确认位。此测试可用于CAN收发器测试并且RX/TX引脚应连接到收发器。 内部环回测试
在自检模式1中CAN引擎将自己发送的消息视为接收的消息并将它们存储到接收缓冲区中。此模式用于自检功能。为了独立于外部刺激CAN引擎生成自己的确认位。在这种模式下CAN引擎执行从TX内部到RX内部的内部反馈。CAN引擎忽略外部RX输入的实际值。外部TX引脚仅输出隐性位。RX/TX引脚不需要连接到CAN总线或任何外部设备。 17.2CAN FD模块的使用
17.2.1配置CAN FD模块
配置CAN FD模块步骤
使能时钟配置引脚配置模块的Stack
对于CAN FD的Stack分为两项Common和Module。
Common是针对于CAN FD模块而言的通用参数Module是针对于CAN FD的某个通道而言的特定的参数。CAN FD有两个通道CAN FD0和CAN FD1Common配置的就是CAN FD0和CAN FD1共用的参数而如果两个通道都使用了则会有两个Module用户需要去CAN FD0和CAN FD1各自的Module中定制配置这两个通道的通信参数。
使能CAN FD的时钟
CAN FD模块的时钟默认是没有使能的用户需要在FSP的“Clocks”里面选择PLL2的时钟源和CAN FD的时钟源以及设置预分频系数得到CAN FD的时钟频率如下图所示 本书选择的PLL2时钟源是内部晶振经过2分频和24分频后得到240MHz的PLL2总线时钟而CAN FD的时钟源选择的是PLL2的时钟也就是240Mhz然后经6分频后得到40MHz的CAN FD时钟。
配置Pins
在FSP的“Pins”中选择“Peripherals”里的“Connectivity:CANFD”根据硬件设计选择使用的通道本书使用的是CANFD0然后再去使能、选择引脚配置如下图所示 引脚选择的是P401和P402本书配套的开发板使用的就是这两个引脚原理图如下图所示 添加CAN FD的Stack模块
配置CAN FD的Stack前需要先去RASC配置界面的“Stacks”中添加CAN FD的Stack步骤如下图所示 添加完成后可以看到CAN FD的Stack属性配置界面如下 配置Stack Common
CAN FD模块的通用参数类型如下图所示 其中Global Error Interupt、Reception和Flexible Data还会细分设置具体的参数这些参数的作用见下表 配置Stack Module
CAN FD的通道配置参数如下图所示 参数较多本书将会挑选其中需要重点关注的几个参数进行讲解其它的参数读者可以在RASC的CAN FD的Stack中点击蓝色感叹号进入查看阅读如下图所示 本书主要介绍以下几个参数
General|Name:CAN FD模块在代码中的名称需要满足C语言字符串的定义要求默认为g_canfd0General|ChannelCAN FD模块的通道范围0~1Bitrate|Automatic|Nominal Rate (bps)波特率标称值默认500000bpsBitrate|Automatic|FD Data Rate (bps)数据波特率值默认2000000bpsBitrate|Manual|Nominal|Prescaler (divisor)标称波特率时钟分频系数默认1Bitrate|Manual|Nominal|Time Segment 1 (Tq)标称波特率的Segment1默认29Bitrate|Manual|Nominal|Time Segment 2 (Tq)标称波特率的Segment2默认10Bitrate|Manual|Nominal|Sync Jump Width (Tq)标称波特率的Sync Jump Width,默认4
这样计算出来的标称波特率值就是 Bitrate|Manual|Data|Prescaler (divisor)数据波特率时钟分频系数默认1 Bitrate|Manual|Data|Time Segment 1 (Tq)标称波特率的Segment1默认5 Bitrate|Manual|Data|Time Segment 2 (Tq)标称波特率的Segment2默认2 Bitrate|Manual|Data|Sync Jump Width (Tq)标称波特率的Sync Jump Width默认1
这样计算出来的数据波特率值就是 Interrupts|CallbackCAN FD通信的中断回调函数本书取名为canfd0_callback适配于CAN FD的通道0 Interrupts|Channel Interrupt Priority LevelCAN FD通信的中断优先级默认为Priority 12 Transmit Interrupts|TXMB xCAN FD发送使用的消息缓存中断使能本书选择的是使能TXMB 0
配置好CAN FD的时钟、Pins和Stacks后在RASC中点击“Generate Project Content”生成工程代码随后去代码中了解CAN FD的配置及其API。
17.2.2 中断回调函数
在RASC中设置的中断回调函数后生成代码在hal_data.h中它只是一个声明
#ifndef canfd0_callback
void canfd0_callback(can_callback_args_t * p_args);
#endif中断回调函数的形参can_callback_args_t的原型如下
typedef struct st_can_callback_args
{uint32_t channel; /// Device channel number.can_event_t event; /// Event code.uint32_t error; /// Error code.union{uint32_t mailbox; /// Mailbox number of interrupt source.uint32_t buffer; /// Buffer number of interrupt source.};void const * p_context; /// Context provided to user during callback.can_frame_t frame; /// Received frame data.
} can_callback_args_t;可能用户更关心的是event成员和frame成员。event帮助用户了解触发中断的原因是什么frame帮助用户获得接收到的数据帧信息。
CAN FD支持的触发中断的事件有这些
typedef enum e_can_event
{CAN_EVENT_ERR_WARNING 0x0002, /// Error Warning event.CAN_EVENT_ERR_PASSIVE 0x0004, /// Error Passive event.CAN_EVENT_ERR_BUS_OFF 0x0008, /// Bus Off event.CAN_EVENT_BUS_RECOVERY 0x0010, /// Bus Off Recovery event.CAN_EVENT_MAILBOX_MESSAGE_LOST 0x0020, /// Mailbox has been overrun.CAN_EVENT_ERR_BUS_LOCK 0x0080, /// Bus lock detected (32 consecutive dominant bits).CAN_EVENT_ERR_CHANNEL 0x0100, /// Channel error has occurred.CAN_EVENT_TX_ABORTED 0x0200, /// Transmit abort event.CAN_EVENT_RX_COMPLETE 0x0400, /// Receive complete event.CAN_EVENT_TX_COMPLETE 0x0800, /// Transmit complete event.CAN_EVENT_ERR_GLOBAL 0x1000, /// Global error has occurred.CAN_EVENT_TX_FIFO_EMPTY 0x2000, /// Transmit FIFO is empty.CAN_EVENT_FIFO_MESSAGE_LOST 0x4000, /// Receive FIFO overrun.
} can_event_t;用户可以参考以下代码设计CAN FD通信的中断回调函数
void canfd0_callback(can_callback_args_t *p_args)
{/* TODO: add your own code here */switch(p_args-event){case CAN_EVENT_TX_COMPLETE:{gCANFDTxCplt true;break;}case CAN_EVENT_RX_COMPLETE:{memcpy(gRxFrame, p_args-frame, sizeof(can_frame_t));gCANFDRxCplt true;break;}default:break;}
}第06、11行判断触发中断是发送完成事件还是接收完成事件第08行如果是发送完成则将标志位置true第13、14行如果是接收完成则将接收到的数据copy到自定义的变量中并且将接收完成标志置true
17.2.3 配置信息解读
RASC生成的CAN配置信息有3种时钟配置信息、引脚配置信息、CANFD模块本身的配置信息。
时钟配置参数
所有的时钟参数配置都在bsp_clock_cfg.h中定义包括本章CAN FD模块的时钟此文件代码如下
/* generated configuration header file - do not edit */
#ifndef BSP_CLOCK_CFG_H_
#define BSP_CLOCK_CFG_H_
......(省略内容)
#define BSP_CFG_XTAL_HZ (24000000) /* XTAL 24000000Hz */
#define BSP_CFG_HOCO_FREQUENCY (2) /* HOCO 20MHz */
#define BSP_CFG_PLL_SOURCE (BSP_CLOCKS_SOURCE_CLOCK_HOCO) /* PLL Src: HOCO */
......(省略内容)
#define BSP_CFG_PLL2_SOURCE (BSP_CLOCKS_SOURCE_CLOCK_HOCO) /* PLL2 Src: HOCO */
#define BSP_CFG_PLL2_DIV (BSP_CLOCKS_PLL_DIV_2) /* PLL2 Div /2 */
#define BSP_CFG_PLL2_MUL BSP_CLOCKS_PLL_MUL(24U,0U) /* PLL2 Mul x24.0 */
#define BSP_CFG_CLOCK_SOURCE (BSP_CLOCKS_SOURCE_CLOCK_PLL) /* Clock Src: PLL */
......(省略内容)
#define BSP_CFG_CANFDCLK_SOURCE (BSP_CLOCKS_SOURCE_CLOCK_PLL2) /* CANFDCLK Src: PLL2 */
......(省略内容)
#define BSP_CFG_CANFDCLK_DIV (BSP_CLOCKS_CANFD_CLOCK_DIV_6) /* CANFDCLK Div /6 */
......
#endif /* BSP_CLOCK_CFG_H_ */第07行定义PLL的时钟源为HOCO第09行定义PLL2的时钟源为HOCO第10~11行定义PLL2的分频系数为2倍频系数为24第12行定义系统时钟源为PLL第14行定义CAN FD的时钟源是PLL2第16行定义CAN FD的分频系数为6
引脚配置信息
CANFD涉及的引脚它们的配置信息在工程的pin_data.c中生成。在RASC里配置的每一个引脚都会在pin_data.c生成一个ioport_pin_cfg_t数组项里面的内容跟配置时选择的参数一致。代码如下
const ioport_pin_cfg_t g_bsp_pin_cfg_data[] {......(省略内容){.pin BSP_IO_PORT_04_PIN_01,.pin_cfg ((uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_CAN)},{.pin BSP_IO_PORT_04_PIN_02,.pin_cfg ((uint32_t) IOPORT_CFG_PERIPHERAL_PIN | (uint32_t) IOPORT_PERIPHERAL_CAN)},......(省略内容)
};模块配置信息
CAN FD的模块配置信息在hal_data.c中它是一个can_cfg_t类型的全局结构体名为g_canfd0_cfg如下
const can_cfg_t g_canfd0_cfg
{.channel 0,.p_bit_timing g_canfd0_bit_timing_cfg,.p_callback canfd0_callback,.p_extend g_canfd0_extended_cfg,.p_context NULL,.ipl (12),
#if defined(VECTOR_NUMBER_CAN0_TX).tx_irq VECTOR_NUMBER_CAN0_TX,
#else.tx_irq FSP_INVALID_VECTOR,
#endif
#if defined(VECTOR_NUMBER_CAN0_CHERR).error_irq VECTOR_NUMBER_CAN0_CHERR,
#else.error_irq FSP_INVALID_VECTOR,
#endif
};此结构体囊括了CAN FD的通道值、中断回调函数、时间特性参数配置和扩展参数配置其中时间特性和扩展参数同样在hal_data.c中用结构体can_bit_timing_cfg_t和结构体canfd_extended_cfg_t定义的全局变量表示。
在hal_data.c中定义了两种时间特性标称时间特性和数据时间特性对应于前文的标称波特率和数据波特率这两个时间特性在代码中是这样表示的
can_bit_timing_cfg_t g_canfd0_bit_timing_cfg
{/* Actual bitrate: 500000 Hz. Actual sample point: 75 %. */.baud_rate_prescaler 1,.time_segment_1 59,.time_segment_2 20,.synchronization_jump_width 4
};#if BSP_FEATURE_CANFD_FD_SUPPORT
can_bit_timing_cfg_t g_canfd0_data_timing_cfg
{/* Actual bitrate: 2000000 Hz. Actual sample point: 75 %. */.baud_rate_prescaler 1,.time_segment_1 14,.time_segment_2 5,.synchronization_jump_width 1
};
#endif从这段代码中可以看到在FSP的CAN FD Stack模块中配置的
l Bitrate|Manual|Nominal|Prescaler (divisor)标称波特率时钟分频系数默认1l Bitrate|Manual|Nominal|Time Segment 1 (Tq)标称波特率的Segment1默认29l Bitrate|Manual|Nominal|Time Segment 2 (Tq)标称波特率的Segment2默认10l Bitrate|Manual|Nominal|Sync Jump Width (Tq)标称波特率的Sync Jump Width,默认4l Bitrate|Manual|Data|Prescaler (divisor)数据波特率时钟分频系数默认1l Bitrate|Manual|Data|Time Segment 1 (Tq)标称波特率的Segment1默认5l Bitrate|Manual|Data|Time Segment 2 (Tq)标称波特率的Segment2默认2l Bitrate|Manual|Data|Sync Jump Width (Tq)标称波特率的Sync Jump Width默认1这些参数并没有实际生成到代码中而是根据一下参数自动计算了baud_rate_prescaler、time_segment_1和time_segment_2以及synchronization_jump_width的值;l Bitrate|Automatic|Nominal Rate (bps)波特率标称值默认500000bpsl Bitrate|Automatic|FD Data Rate (bps)数据波特率值默认2000000bps
所以用户要配置波特率最方便的方法就是去Bitrate|Automati中设置波特率。
扩展参数中配置的就是CAN FD的数据波特率配置和全局配置代码如下
canfd_extended_cfg_t g_canfd0_extended_cfg
{.p_afl p_canfd0_afl,.txmb_txi_enable ((1ULL 0) | 0ULL),.error_interrupts ( 0U),
#if BSP_FEATURE_CANFD_FD_SUPPORT.p_data_timing g_canfd0_data_timing_cfg,
#else.p_data_timing NULL,
#endif.delay_compensation (1),.p_global_cfg g_canfd_global_cfg,
};全局配置参数设置的就是CAN FD通信的中断优先级、错误帧格式、接收FIFO等的配置代码如下
canfd_global_cfg_t g_canfd_global_cfg
{.global_interrupts CANFD_CFG_GLOBAL_ERR_SOURCES,.global_config (CANFD_CFG_TX_PRIORITY | CANFD_CFG_DLC_CHECK | (BSP_CFG_CANFDCLK_SOURCE BSP_CLOCKS_SOURCE_CLOCK_MAIN_OSC ? R_CANFD_CFDGCFG_DCS_Msk : 0U) | CANFD_CFG_FD_OVERFLOW),.rx_mb_config (CANFD_CFG_RXMB_NUMBER | (CANFD_CFG_RXMB_SIZE R_CANFD_CFDRMNB_RMPLS_Pos)),.global_err_ipl CANFD_CFG_GLOBAL_ERR_IPL,.rx_fifo_ipl CANFD_CFG_RX_FIFO_IPL,.rx_fifo_config {
......省略内容
#if !BSP_FEATURE_CANFD_LITE
......省略内容
#endif},
};
#endif最后在hal_data.c中定义了一个can_instance_t类型的全局结构体g_canfd0它容纳了控制结构体、配置结构体、接口结构体如下c
const can_instance_t g_canfd0
{.p_ctrl g_canfd0_ctrl,.p_cfg g_canfd0_cfg,.p_api g_canfd_on_canfd
};17.2.4 API接口及其用法
CAN FD设备的操作方法函数在r_can_api.h中定义
typedef struct st_can_api
{fsp_err_t (* open)(can_ctrl_t * const p_ctrl, can_cfg_t const * const p_cfg);fsp_err_t (* write)(can_ctrl_t * const p_ctrl, uint32_t buffer_number, can_frame_t * const p_frame);fsp_err_t (* read)(can_ctrl_t * const p_ctrl, uint32_t buffer_number, can_frame_t * const p_frame);fsp_err_t (* close)(can_ctrl_t * const p_ctrl);fsp_err_t (* modeTransition)(can_ctrl_t * const p_api_ctrl, can_operation_mode_t operation_mode,can_test_mode_t test_mode);fsp_err_t (* infoGet)(can_ctrl_t * const p_ctrl, can_info_t * const p_info);fsp_err_t (* callbackSet)(can_ctrl_t * const p_api_ctrl, void (* p_callback)(can_callback_args_t *),void const * const p_context, can_callback_args_t * const p_callback_memory);
} can_api_t;这些函数会在r_canfd.c中实现它们都被放入一个体can_api_t结构体里
const can_api_t g_canfd_on_canfd
{.open R_CANFD_Open,.close R_CANFD_Close,.write R_CANFD_Write,.read R_CANFD_Read,.modeTransition R_CANFD_ModeTransition,.infoGet R_CANFD_InfoGet,.callbackSet R_CANFD_CallbackSet,
};下面就来看下这些操作函数的用法。
打开CAN FD设备
fsp_err_t (* open)(can_ctrl_t * const p_ctrl, can_cfg_t const * const p_cfg);此函数用于初始化CAN FD设备
fsp_err_t err g_canfd0.p_api-open(g_canfd0.p_ctrl, g_canfd0.p_cfg);
if(FSP_SUCCESS ! err)
{printf(Function:%s\tLine:%d\r\n, __FUNCTION__, __LINE__);return false;
}关闭CAN FD设备
fsp_err_t (* close)(can_ctrl_t * const p_ctrl);关闭CAN FD设备函数就很简单调用此函数的时候传入参数g_canfd0_ctrl即可。如果关闭设备成功会返回FSP_SUCCESS(0)。
CAN FD发送数据帧
fsp_err_t (* write)(can_ctrl_t * const p_ctrl, uint32_t buffer_number, can_frame_t * const p_frame);发送CAN FD数据帧的write函数其第二个参数指的是发送的buffer序号而不是数据个数取值范围在CANFD_TX_MB_0CANFD_TX_MB_7CANFD_TX_MB_32CANFD_TX_MB_39之间第三个参数表示数据帧的结构包括ID、ID类型、数据等结构体原型如下
typedef struct st_can_frame
{uint32_t id; /// CAN ID.can_id_mode_t id_mode; /// Standard or Extended ID (IDE).can_frame_type_t type; /// Frame type (RTR).uint8_t data_length_code; /// CAN Data Length Code (DLC).uint32_t options; /// Implementation-specific options.uint8_t data[CAN_DATA_BUFFER_LENGTH]; /// CAN data.
} can_frame_t;CAN Id的类型有标准帧和扩展帧在代码中的宏定义值是
typedef enum e_can_id_mode
{CAN_ID_MODE_STANDARD, /// Standard IDs of 11 bits used.CAN_ID_MODE_EXTENDED, /// Extended IDs of 29 bits used.
} can_id_mode_t;帧类型有数据帧和远程帧之分
typedef enum e_can_frame_type
{CAN_FRAME_TYPE_DATA, /// Data frame.CAN_FRAME_TYPE_REMOTE, /// Remote frame.
} can_frame_type_t;数据个数则根据当前发送的是传统CAN数据帧还是CAN FD数据帧决定传统数据帧个数不能超过8CAN FD数据帧个数据不能超过64。CAN数据帧的特殊操作有三种ESI、BRS和FDF
typedef enum e_canfd_frame_option
{CANFD_FRAME_OPTION_ERROR 0x01, /// Error state set (ESI).CANFD_FRAME_OPTION_BRS 0x02, /// Bit Rate Switching (BRS) enabled.CANFD_FRAME_OPTION_FD 0x04, /// Flexible Data frame (FDF).
} canfd_frame_options_t;CAN数据帧的数据数组此数组的大小由宏定义值CAN_DATA_BUFFER_LENGTH确定
#if BSP_FEATURE_CANFD_NUM_CHANNELS#define CAN_DATA_BUFFER_LENGTH (64)
#else#define CAN_DATA_BUFFER_LENGTH (8)
#endif用户可以参考以下代码使用write函数发送CAN数据
fsp_err_t err g_canfd0.p_api-write(g_canfd0.p_ctrl, CANFD_TX_MB_0, (can_frame_t*)frame);
if(FSP_SUCCESS ! err)
{printf(Function:%s\tLine:%d\r\n, __FUNCTION__, __LINE__);return false;
}CAN FD读取数据帧
fsp_err_t (* read)(can_ctrl_t * const p_ctrl, uint32_t buffer_number, can_frame_t * const p_frame);此函数和发送函数十分类似第二个参数是读取buffer的序号取值范围是CANFD_RX_BUFFER_MB_0~CANFD_RX_BUFFER_MB_31和CANFD_RX_BUFFER_FIFO_0、CANFD_RX_BUFFER_FIFO_1第二个参数同样是数据帧结构体。
用户可以参考发送数据的方法来使用此函数。
CAN FD的通信模式
fsp_err_t (* modeTransition)(can_ctrl_t * const p_api_ctrl, can_operation_mode_t operation_mode,can_test_mode_t test_mode);此函数用于设置CAN FD的通讯方式由参数e_can_operation_mode和e_can_test_mode共同决定。
e_can_operation_mode定义了CAN支持的所有操作模式
typedef enum e_can_operation_mode
{CAN_OPERATION_MODE_NORMAL 0, /// CAN Normal Operation ModeCAN_OPERATION_MODE_RESET, /// CAN Reset Operation ModeCAN_OPERATION_MODE_HALT, /// CAN Halt Operation ModeCAN_OPERATION_MODE_SLEEP 5, /// CAN Sleep Operation ModeCAN_OPERATION_MODE_GLOBAL_OPERATION 0x80, // CANFD Global Operation ModeCAN_OPERATION_MODE_GLOBAL_RESET, // CANFD Global Reset ModeCAN_OPERATION_MODE_GLOBAL_HALT, // CANFD Global Halt ModeCAN_OPERATION_MODE_GLOBAL_SLEEP 0x85 // CANFD Global Sleep Mode
} can_operation_mode_t;在正常通信中一般选择CAN_OPERATION_MODE_NORMAL模式。 e_can_test_mode定义了CAN支持的所有测试模式
typedef enum e_can_test_mode
{CAN_TEST_MODE_DISABLED 0, /// CAN Test Mode Disabled.CAN_TEST_MODE_LISTEN 3, /// CAN Test Listen Mode.CAN_TEST_MODE_LOOPBACK_EXTERNAL 5, /// CAN Test External Loopback Mode.CAN_TEST_MODE_LOOPBACK_INTERNAL 7, /// CAN Test Internal Loopback Mode.CAN_TEST_MODE_INTERNAL_BUS 0x80 /// CANFD Internal CAN Bus Communication Test Mode.
} can_test_mode_t;在测试阶段可以根据硬件设计选择某种测试模式在正常的双端CAN通信中不要使用测试模式选择CAN_TEST_MODE_DISABLED。
在开始通信前用户需要在代码中指定CAN的通信模式可以参考以下代码来设置通信模式
fsp_err_t err g_canfd0.p_api-modeTransition(g_canfd0.p_ctrl,CAN_OPERATION_MODE_NORMAL,CAN_TEST_MODE_LOOPBACK_INTERNAL);
if(FSP_SUCCESS ! err)
{printf(Function:%s\tLine:%d\r\n, __FUNCTION__, __LINE__);return false;
}获取CAN FD的配置参数
fsp_err_t (* infoGet)(can_ctrl_t * const p_ctrl, can_info_t * const p_info);用户可以获取到的CAN信息有这些
typedef struct st_can_info
{uint32_t status; /// Useful information from the CAN status register.uint32_t rx_mb_status; /// RX Message Buffer New Data flags.uint32_t rx_fifo_status; /// RX FIFO Empty flags.uint8_t error_count_transmit; /// Transmit error count.uint8_t error_count_receive; /// Receive error count.uint32_t error_code; /// Error code, cleared after reading.
} can_info_t;17.3 CAN FD回环实验
此实验会使用到按键中断、printf功能和滴答定时器请读者将前文的外部中断实验的驱动文件、drv_uart.c/.h文件和滴答定时器的驱动文件移植到本工程。
17.3.1 设计目的
本节实验是利用CAN FD的环回测试模式体验CAN FD的过滤器配置和数据收发。本实验通过按键控制CAN FD的数据帧发送按一次发送一次。
17.3.2 硬件连接
本书配套的开发板板载了CAN收发器因而既可以使用外部环回测试也可以使用内部换回测试。 17.3.3 驱动程序
接收过滤器列表
使用RASC配置CAN FD并生成代码后接受过滤器列表会在hal_data.c中声明
extern const canfd_afl_entry_t p_canfd0_afl[CANFD_CFG_AFL_CH0_RULE_NUM];因而需要用户在代码中按照声明的格式定义此接收过滤器数组例如本书在drv_canfd.c中就定义了此数组
const canfd_afl_entry_t p_canfd0_afl[CANFD_CFG_AFL_CH0_RULE_NUM]
{{.id {/* Specify the ID, ID type and frame type to accept. */.id 0x000,.frame_type CAN_FRAME_TYPE_DATA,.id_mode CAN_ID_MODE_STANDARD},.mask {/* These values mask which ID/mode bits to compare when filtering messages. */.mask_id 0x000,.mask_frame_type 1,.mask_id_mode 1},.destination {/* If DLC checking is enabled any messages shorter than the below setting will be rejected. */.minimum_dlc CANFD_MINIMUM_DLC_0,/* Optionally specify a Receive Message Buffer (RX MB) to store accepted frames. RX MBs do not have an* interrupt or overwrite protection and must be checked with R_CANFD_InfoGet and R_CANFD_Read. */.rx_buffer CANFD_RX_MB_NONE,/* Specify which FIFO(s) to send filtered messages to. Multiple FIFOs can be ORd together. */.fifo_select_flags CANFD_RX_FIFO_0}},
};canfd_afl_entry_t结构体各成员含义见下表 以本书的过滤器为例
id0frame_type CAN_FRAME_TYPE_DATAid_mode CAN_ID_MODE_STANDARDmask_id0
表示所有的标准ID数据帧都可以接收不做任何过滤而如果
id0x40frame_type CAN_FRAME_TYPE_DATAid_mode CAN_ID_MODE_STANDARDmask_id0x7F0
那么就表示ID在0x40~0x4F的标准ID数据帧不进行过滤其它的都过滤不接收。计算方式如下 断回调函数
本书的中断回调函数仅对收发完成事件做处理用户可以根据需求添加对错误事件的处理。
void canfd0_callback(can_callback_args_t *p_args)
{/* TODO: add your own code here */switch(p_args-event){case CAN_EVENT_TX_COMPLETE:{gCANFDTxCplt true;break;}case CAN_EVENT_RX_COMPLETE:{memcpy(gRxFrame, p_args-frame, sizeof(can_frame_t));gCANFDRxCplt true;break;}default:break;}
}本书没有使用CAN FD的read函数读取数据而是在中断回调函数中将接收到的数据帧复制给一个静态全局变量gRxFrame。
收发完成等待函数
在通信过程中通常需要知道读写操作何时完成然后再进行下一步的处理本书以下代码等待读写完成并加入了超时机制
static bool CANFDDrvWaitTxCplt(void)
{uint16_t uwTimeout 100;while(!gCANFDTxCplt uwTimeout){uwTimeout--;HAL_Delay(1);}bool ret gCANFDTxCplt;gCANFDTxCplt false;return ret;
}static bool CANFDDrvWaitRxCplt(void)
{uint16_t uwTimeout 100;while(!gCANFDRxCplt uwTimeout){uwTimeout--;HAL_Delay(1);}bool ret gCANFDRxCplt;gCANFDRxCplt false;return ret;
}这两个函数的逻辑比较简单不再讲解。
数据发送函数
本书通过帧ID的大小来分辨标准帧还是扩展帧将这一判断封装到发送函数中然后调用CAN FD的write函数将数据帧发送出去
static int (CANFDDrvWrite)(struct CANDev *ptDev, void *frame, unsigned char length)
{if(NULL ptDev) return false;can_frame_t *tFrame (can_frame_t*)frame;tFrame-data_length_code length;tFrame-type CAN_FRAME_TYPE_DATA;tFrame-options 0;if(tFrame-id 0x7FF)tFrame-id_mode CAN_ID_MODE_EXTENDED;elsetFrame-id_mode CAN_ID_MODE_STANDARD;if(1 ptDev-channel){fsp_err_t err g_canfd0.p_api-write(g_canfd0.p_ctrl, CANFD_TX_MB_0, (can_frame_t*)frame);if(FSP_SUCCESS ! err){printf(Function:%s\tLine:%d\r\n, __FUNCTION__, __LINE__);return false;}return CANFDDrvWaitTxCplt();}return true;
}数据接收函数
在介绍中断回调函数的时候已经说明在中断回调函数中已经把接收到的数据复制到gRxFrame里因此接收函数的实现就比较简单了——从gRxFrame中复制数据即可
static int (CANFDDrvRead)(struct CANDev *ptDev, void *frame)
{if(NULL ptDev) return false;if(1 ptDev-channel){if(true CANFDDrvWaitRxCplt()){memcpy((can_frame_t*)frame, (can_frame_t*)gRxFrame, sizeof(can_frame_t));return true;}}return false;
}按键驱动的修改
为了适配本实验将按键中断实验的代码进行了修改
按键消抖处理不再点灯而是设置一个按键标志位
void KeyProcessJitter(uint32_t tick)
{if(tick uwPressTick){gKeyStatus true;}
}要记得将按键消抖处理函数放到滴答定时器的中断服务函数中
void SysTick_Handler(void)
{dwTick 1;KeyProcessJitter(dwTick);
}
- IODrvRead函数可以获取按键IO的电平值了c
static IODevState_t IODrvRead(struct IODev *ptdev)
{if(ptdev-name NULL) return false;IODevState_t state LowLevel;if(strcmp(ptdev-name, UserKey) 0){state (IODevState_t)gKeyStatus;gKeyStatus false;}return state;
}注册按键IO设备的时候需要将Read函数注册
static IODev gKeyDev {.name UserKey,.Init IODrvInit,.Write NULL,.Read IODrvRead
};17.3.4 测试程序
对滴答定时器、UART、按键设备和CAN FD设备初始化之后读取IO按键发现按键被按下后就发送一个数据帧。程序还会尝试读取数据如果收到数据就把它打印出来
uint16_t count 0;
while(1)
{can_frame_t txFrame;if(pKeyDev-Read(pKeyDev) true){txFrame.id count;for(uint8_t i0; i8; i){txFrame.data[i] 0x10 i count;}if(pCANDev-Write(pCANDev, (can_frame_t*)txFrame, 8) ! true){printf(Failed to transmit ID 0x%.3x frame\r\n, txFrame.id);continue;}printf(\r\nSuccess to transmit!\r\n\tID 0x%.3x frame\r\n, txFrame.id);}can_frame_t rxFrame;if(pCANDev-Read(pCANDev, (can_frame_t*)rxFrame) ! true){continue;}count;printf(\r\nLoopback --- %d\r\n, count);printf(\tSource ID 0x%.3x \t Destination ID 0x%.3x\r\n, txFrame.id, rxFrame.id);for(uint8_t i0; i8; i){printf(\tSource Data[%d] 0x%.2x, i, txFrame.data[i]);printf(\t Destination[%d] 0x%.2x\r\n, i, rxFrame.data[i]);}
}第07~11行发送的数据帧每次都设置不同的ID和数据第15行如果发送失败则不去判断是否接收到数据第23行如果没有接收到数据则不进行信息打印从头再来
17.3.5 测试结果
将工程编译后把得到的二进制可执行文件烧录到处理器中执行可以看到如下的打印信息 17.4 CAN FD双板通信实验
此实验会使用到按键中断、printf功能和滴答定时器请读者将前文的外部中断实验的驱动文件、drv_uart.c/.h文件和滴答定时器的驱动文件移植到本工程。
本节实验使用的CAN FD驱动程序和上一节回环实验的代码基本一致读者在配置好FSP生成工程后可以将上一节的启动代码整体移植到本工程。
17.4.1 设计目的
让用户体验真实的CAN FD双端通信。本实验需要两块支持CAN FD接口的开发板除了本书配套的一块RA6M5处理器的开发板外用户还需准备一块有CAN FD接口和控制器的开发板可以再购买一块本书配套的开发板。
17.4.2 硬件连接
本节实验使用两块开发板连接CAN FD控制器的CAN_H和CAN_L如下图所示 17.4.3 驱动程序
本节实验的驱动程序大体和上一小节的实验一致差别仅在于初始化CAN FD的时候不再是测试模式
static int CANFDDrvInit(struct CANDev *ptDev)
{if(NULL ptDev) return false;if(1 ptDev-channel){{fsp_err_t err g_canfd0.p_api-open(g_canfd0.p_ctrl, g_canfd0.p_cfg);if(FSP_SUCCESS ! err){printf(Function:%s\tLine:%d\r\n, __FUNCTION__, __LINE__);return false;}}{fsp_err_t err g_canfd0.p_api-modeTransition(g_canfd0.p_ctrl,CAN_OPERATION_MODE_NORMAL,CAN_TEST_MODE_DISABLED);if(FSP_SUCCESS ! err){printf(Function:%s\tLine:%d\r\n, __FUNCTION__, __LINE__);return false;}}}return true;
}第15~17行设置的CAN FD通信模式不使能测试模式
17.4.4测试程序
测试程序和上一小节的基本也一样只是去掉了数据的比较只是把接收到的数据打印出来
uint16_t count 0;
while(1)
{if(pKeyDev-Read(pKeyDev) true){can_frame_t txFrame;txFrame.id count;for(uint8_t i0; i8; i){txFrame.data[i] 0x10 i count;}if(pCANDev-Write(pCANDev, (can_frame_t*)txFrame, 8) ! true){printf(\r\nFailed to transmit!\r\n\tID 0x%.3x frame\r\n, txFrame.id);continue;}count;printf(\r\nSuccess to transmit!\r\n\tID 0x%.3x frame\r\n, txFrame.id);}can_frame_t rxFrame;if(pCANDev-Read(pCANDev, (can_frame_t*)rxFrame) ! true){continue;}printf(\r\nSuccess to Receive!\r\n\tID 0x%.3x\r\n, rxFrame.id);for(uint8_t i0; i8; i){printf(\tReceive[%d] 0x%.2x\r\n, i, rxFrame.data[i]);}
}17.4.5测试结果
本实验需要使用2个RA6M5开发板连接它们的CAN接口后两个板子烧入同样的程序。然后按下按键以给对方发送数据 本章完