Aurix英飞凌TC334芯片CAN模块配置
大家好,我是左工,在上一节Aurix英飞凌TC334芯片GPT模块定时器配置中我们已经运用GPT模块实现对板载LED灯珠的亮灭控制。今天我们来聊聊嵌入式控制中非常非常关键的CAN通讯模块。
前言
在汽车电子系统中,CAN 通讯模块(Controller Area Network,控制器局域网模块)是实现车载各电子单元之间数据交互的核心组件。汽车上分布着大量电子控制单元,比如发动机ECU、变速箱TCU、ABS控制器、车身控制模块BCM、仪表盘等。CAN通讯模块为这些电子控制单元提供了统一的通信总线,让它们可以共享数据。调试过程中,我们也会用到CAN通讯外发调试信息。另外,在汽车电子系统中,CCP(CAN Calibration Protocol)协议,XCP(Universal Measurement and Calibration Protocol)协议,UDS(Unified Diagnostic Services)协议都可以通过CAN通讯实现的,因此CAN模块非常重要。
一、代码编写
利用文章“搭建英飞凌Aurix系列TC334芯片开源免费开发工具链”中所描述的方法创建一个名为“3_CanCom”的工程,如下图所示。
工程创建完成后,我们会在导引栏中看见如下文件和文件夹。其中文件“Cpu0_Main.c”就是我们本次需要操作的文件。
我们双击打开该文件,找到主函数“core0_main”,TC334运行后,将首先运行该函数。我们可以看见该函数里面有一个while循环。我们将把代码写在这个while循环附近。
用如下代码替换上图中被红色框标出的while循环。
GPTimer_init();//GPT模块初始化
CAN_init(); // CAN模块初始化
uint8 tx_buf[8]; // 发送缓存
while(1)
{
if(TimeCount>1)
{
TimeCount = 0;
tx_buf[0] = rx_buf[0]; // 接受的报文第0字节发送出来
tx_buf[1] = tx_buf[1] + 1; // 自增1
CAN_send(0x123,tx_buf,8); // 报文发送
}
}
添加完成后,我们本次实验对主函数的所有操作就结束了。下面我们来添加一些必要头文件。
#include "IfxGpt12.h"
#include "IfxSrc.h"
#include "IfxCan_Can.h"
函数“GPTimer_init()”的定义可以从文章Aurix英飞凌TC334芯片GPT模块定时器配置中查看。我们只更改定时器中断回调函数即可。在回调函数中我们设定了一个自增的变量。
IFX_INTERRUPT(GPTimer_isr,0,10); //声明定时中断服务函数
void GPTimer_isr(void)
{
TimeCount++;
}
我们在“GPTimer_init()”的下面添加添加上“CAN_init()”函数的具体定义以及CAN模块设定需要的变量。
/************************CAN模块定义************************/
volatile uint16 rx_id = 0; //存储接收报文ID
volatile uint8 rx_len = 0; //存储接收报文负载数据长度
volatile uint8 rx_buf[8] = {0}; //报文接收数据
/*CAN模块初始化*/
IfxCan_Can can_handle = {0}; //CAN模块的操作句柄
IfxCan_Can_Node node0_handle = {0}; //CAN第0节点的操作句柄
/*CAN模块初始化函数*/
void CAN_init(void)
{
/*模块配置*/
IfxCan_Can_Config can_config = {0}; //模块配置参数结构体变量
IfxCan_Can_initModuleConfig(&can_config,&MODULE_CAN0); //填充CAN模块配置参数结构体
IfxCan_Can_initModule(&can_handle,&can_config); //初始化CAN模块
/*端口配置*/
IfxCan_Node_initTxPin(&IfxCan_TXD00_P20_8_OUT,IfxPort_OutputMode_pushPull,IfxPort_PadDriver_cmosAutomotiveSpeed1);
IfxCan_Node_initRxPin(&MODULE_CAN0.N[IfxCan_NodeId_0],&IfxCan_RXD00B_P20_7_IN,IfxPort_InputMode_noPullDevice,IfxPort_PadDriver_cmosAutomotiveSpeed1);
/*节点0配置*/
IfxCan_Can_NodeConfig node_config = {0};
IfxCan_Can_initNodeConfig(&node_config,&can_handle); //填充节点默认配置
node_config.nodeId = IfxCan_NodeId_0; //要配置的节点为第0号节点
node_config.frame.type = IfxCan_FrameType_transmitAndReceive; //允许节点能接收和发送数据帧
node_config.baudRate.baudrate = 500000;//波特率
/*节点接收中断*/
node_config.interruptConfig.messageStoredToDedicatedRxBufferEnabled = TRUE; //使能接收成功中断
node_config.interruptConfig.reint.interruptLine = IfxCan_InterruptLine_0; //中断信号映射到0号中断线
node_config.interruptConfig.reint.priority = 11; //接收成功中断的优先级设为11
IfxCan_Can_initNode(&node0_handle,&node_config); //完成节点配置
/*节点0过滤配置*/
IfxCan_Filter node0_filter0 = {0};
node0_filter0.number = 0; //配置第0号过滤器
node0_filter0.elementConfiguration = IfxCan_FilterElementConfiguration_storeInRxBuffer; //过滤通过存到指定缓冲区
node0_filter0.id1 = 0x010; //在指定缓冲区模式下,配置id1为过滤id
node0_filter0.rxBufferOffset = IfxCan_RxBufferId_0; //过滤完成后存入0号接收缓冲区
IfxCan_Can_setStandardFilter(&node0_handle,&node0_filter0); //完成过滤器配置
}
在“CAN_init()”函数后面我们添加上CAN接收中断的回调函数“CAN_rx_isr()”的定义。这个中断回调函数发生在接收到报文后。
/*CAN模块的接收中断处理函数*/
IFX_INTERRUPT(CAN_rx_isr,0,11); //声明CAN接收中断服务函数
void CAN_rx_isr(void)
{
IfxCan_Message rx_msg = {0};
uint32 data_buf[2] = {0}; //存数据的临时缓冲区
IfxCan_Node_clearInterruptFlag(node0_handle.node,IfxCan_Interrupt_messageStoredToDedicatedRxBuffer); //清除接收完成中断
rx_msg.bufferNumber = IfxCan_RxBufferId_0; //指定获取第0号接收缓冲区
IfxCan_Can_readMessage(&node0_handle,&rx_msg,data_buf); //读取消息,将数据存入缓冲区便于程序处理
rx_id = (uint16)rx_msg.messageId; //将接收到的报文ID保存下来
rx_len = (uint8)rx_msg.dataLengthCode; //获取接收到的数据长度
/*取出接收到的报文数据*/
for(uint32 cnt = 0; cnt < rx_len; cnt ++)
{
rx_buf[cnt] = ((uint8 *)data_buf)[cnt]; //以字节来读取数据
}
}
在主程序中,还有一个CAN发送函数“CAN_send()”,该函数的作用就是将报文发送出去,定义如下。
/*根据报文ID发出报文,返回已完成发送的数据长度*/
void CAN_send(uint16 id,uint8 *buf,uint32 len)
{
IfxCan_Message tx_msg = {0};
IfxCan_Can_initMessage(&tx_msg); //向消息结构体填充初始参数
tx_msg.bufferNumber = IfxCan_TxBufferId_0; //使用第0号发送缓冲区发送
tx_msg.frameMode = IfxCan_FrameMode_standard; //此为标准CAN协议帧
tx_msg.dataLengthCode = len; //设定要发送的报文的负载数据长度
tx_msg.messageId = id; //设置本次报文的id
tx_msg.messageIdLength = IfxCan_MessageIdLength_standard; //本次报文ID长度为11位的ID
IfxCan_Can_sendMessage(&node0_handle,&tx_msg,(uint32 *)buf); //传入负载数据启动发送
}
现在所有代码编写完毕,整个Cpu0_Main.c的定义如下所示。
#include "Ifx_Types.h"
#include "IfxCpu.h"
#include "IfxScuWdt.h"
#include "IfxGpt12.h"
#include "IfxSrc.h"
#include "IfxCan_Can.h"
IFX_ALIGN(4) IfxCpu_syncEvent g_cpuSyncEvent = 0;
/************************GPT定时模块定义************************/
uint8 TimeCount = 0;
void GPTimer_init(void)
{
/*端口初始化*/
IfxPort_setPinMode(&MODULE_P02,5,IfxPort_Mode_outputPushPullGeneral ); //P02.5配置为输入模式
/*GPT120配置*/
IfxGpt12_enableModule(&MODULE_GPT120); //使能GPT120模块
IfxGpt12_setGpt1BlockPrescaler(&MODULE_GPT120,IfxGpt12_Gpt1BlockPrescaler_8); //GPT模块预分频,使用SPB总线时钟为100MHz
/*配置T3定时器用于产生周期性中断*/
IfxGpt12_T3_setMode(&MODULE_GPT120,IfxGpt12_Mode_timer); //设置T3定时器的模式为定时器模式
IfxGpt12_T3_setDirectionSource(&MODULE_GPT120,IfxGpt12_TimerDirectionSource_internal); //内部控制计数方向
IfxGpt12_T3_setTimerDirection(&MODULE_GPT120,IfxGpt12_TimerDirection_up); //T3的计数方向为向上计数
IfxGpt12_T3_setTimerPrescaler(&MODULE_GPT120,IfxGpt12_TimerInputPrescaler_128); //定时器计数时钟为模块时钟的128分频
IfxGpt12_T3_setTimerValue(&MODULE_GPT120,0xFFFF-50000+1); //计数50000个脉冲即会发生溢出事件
/*配置T3的溢出中断*/
IfxSrc_init(&SRC_GPT120T3,IfxSrc_Tos_cpu0,10); //配置GPT120模块的T3定时器溢出中断服务,中断优先级设为10
IfxSrc_enable(&SRC_GPT120T3); //使能GPT120模块的T3定时器溢出中断服务
/*启动定时器*/
IfxGpt12_T3_run(&MODULE_GPT120,IfxGpt12_TimerRun_start); //启动T3
}
IFX_INTERRUPT(GPTimer_isr,0,10); //声明定时中断服务函数
void GPTimer_isr(void)
{
TimeCount++;
}
/************************CAN模块定义************************/
volatile uint16 rx_id = 0; //存储接收报文ID
volatile uint8 rx_len = 0; //存储接收报文负载数据长度
volatile uint8 rx_buf[8] = {0}; //报文接收数据
/*CAN模块初始化*/
IfxCan_Can can_handle = {0}; //CAN模块的操作句柄
IfxCan_Can_Node node0_handle = {0}; //CAN第0节点的操作句柄
/*CAN模块初始化函数*/
void CAN_init(void)
{
/*模块配置*/
IfxCan_Can_Config can_config = {0}; //模块配置参数结构体变量
IfxCan_Can_initModuleConfig(&can_config,&MODULE_CAN0); //填充CAN模块配置参数结构体
IfxCan_Can_initModule(&can_handle,&can_config); //初始化CAN模块
/*端口配置*/
IfxCan_Node_initTxPin(&IfxCan_TXD00_P20_8_OUT,IfxPort_OutputMode_pushPull,IfxPort_PadDriver_cmosAutomotiveSpeed1);
IfxCan_Node_initRxPin(&MODULE_CAN0.N[IfxCan_NodeId_0],&IfxCan_RXD00B_P20_7_IN,IfxPort_InputMode_noPullDevice,IfxPort_PadDriver_cmosAutomotiveSpeed1);
/*节点0配置*/
IfxCan_Can_NodeConfig node_config = {0};
IfxCan_Can_initNodeConfig(&node_config,&can_handle); //填充节点默认配置
node_config.nodeId = IfxCan_NodeId_0; //要配置的节点为第0号节点
node_config.frame.type = IfxCan_FrameType_transmitAndReceive; //允许节点能接收和发送数据帧
node_config.baudRate.baudrate = 500000;//波特率
/*节点接收中断*/
node_config.interruptConfig.messageStoredToDedicatedRxBufferEnabled = TRUE; //使能接收成功中断
node_config.interruptConfig.reint.interruptLine = IfxCan_InterruptLine_0; //中断信号映射到0号中断线
node_config.interruptConfig.reint.priority = 11; //接收成功中断的优先级设为11
IfxCan_Can_initNode(&node0_handle,&node_config); //完成节点配置
/*节点0过滤配置*/
IfxCan_Filter node0_filter0 = {0};
node0_filter0.number = 0; //配置第0号过滤器
node0_filter0.elementConfiguration = IfxCan_FilterElementConfiguration_storeInRxBuffer; //过滤通过存到指定缓冲区
node0_filter0.id1 = 0x010; //在指定缓冲区模式下,配置id1为过滤id
node0_filter0.rxBufferOffset = IfxCan_RxBufferId_0; //过滤完成后存入0号接收缓冲区
IfxCan_Can_setStandardFilter(&node0_handle,&node0_filter0); //完成过滤器配置
}
/*根据报文ID发出报文,返回已完成发送的数据长度*/
void CAN_send(uint16 id,uint8 *buf,uint32 len)
{
IfxCan_Message tx_msg = {0};
IfxCan_Can_initMessage(&tx_msg); //向消息结构体填充初始参数
tx_msg.bufferNumber = IfxCan_TxBufferId_0; //使用第0号发送缓冲区发送
tx_msg.frameMode = IfxCan_FrameMode_standard; //此为标准CAN协议帧
tx_msg.dataLengthCode = len; //设定要发送的报文的负载数据长度
tx_msg.messageId = id; //设置本次报文的id
tx_msg.messageIdLength = IfxCan_MessageIdLength_standard; //本次报文ID长度为11位的ID
IfxCan_Can_sendMessage(&node0_handle,&tx_msg,(uint32 *)buf); //传入负载数据启动发送
}
/*CAN模块的接收中断处理函数*/
IFX_INTERRUPT(CAN_rx_isr,0,11); //声明CAN接收中断服务函数
void CAN_rx_isr(void)
{
IfxCan_Message rx_msg = {0};
uint32 data_buf[2] = {0}; //存数据的临时缓冲区
IfxCan_Node_clearInterruptFlag(node0_handle.node,IfxCan_Interrupt_messageStoredToDedicatedRxBuffer); //清除接收完成中断
rx_msg.bufferNumber = IfxCan_RxBufferId_0; //指定获取第0号接收缓冲区
IfxCan_Can_readMessage(&node0_handle,&rx_msg,data_buf); //读取消息,将数据存入缓冲区便于程序处理
rx_id = (uint16)rx_msg.messageId; //将接收到的报文ID保存下来
rx_len = (uint8)rx_msg.dataLengthCode; //获取接收到的数据长度
/*取出接收到的报文数据*/
for(uint32 cnt = 0; cnt < rx_len; cnt ++)
{
rx_buf[cnt] = ((uint8 *)data_buf)[cnt]; //以字节来读取数据
}
}
void core0_main(void)
{
IfxCpu_enableInterrupts();
/* !!WATCHDOG0 AND SAFETY WATCHDOG ARE DISABLED HERE!!
* Enable the watchdogs and service them periodically if it is required
*/
IfxScuWdt_disableCpuWatchdog(IfxScuWdt_getCpuWatchdogPassword());
IfxScuWdt_disableSafetyWatchdog(IfxScuWdt_getSafetyWatchdogPassword());
/* Wait for CPU sync event */
IfxCpu_emitEvent(&g_cpuSyncEvent);
IfxCpu_waitEvent(&g_cpuSyncEvent, 1);
GPTimer_init();
CAN_init();
uint8 tx_buf[8]; // 发送缓存
while(1)
{
if(TimeCount>1)
{
TimeCount = 0;
tx_buf[0] = rx_buf[0]; // 接受的报文第0字节发送出来
tx_buf[1] = tx_buf[1] + 1; // 自增1
CAN_send(0x123,tx_buf,8); // 报文发送
}
}
}
二、代码解释
下面我们就对这段代码就行简单的分析和讲解。
IfxCan_Node_initTxPin(&IfxCan_TXD00_P20_8_OUT,IfxPort_OutputMode_pushPull,IfxPort_PadDriver_cmosAutomotiveSpeed1);
IfxCan_Node_initRxPin(&MODULE_CAN0.N[IfxCan_NodeId_0],&IfxCan_RXD00B_P20_7_IN,IfxPort_InputMode_noPullDevice,IfxPort_PadDriver_cmosAutomotiveSpeed1);
上面这两行代码是将CAN的发送端口配置为P20.8端口,将接收端口配置为P20.7这与硬件原理图中的端口对应。
node_config.baudRate.baudrate = 500000;//波特率
这行代码的作用是将CAN的波特率设置为500kbps。波特率是指通讯速率,整个CAN网络上所有相连节点的波特率必须设置成一样。波特率越高通讯速率会越高,但是相应的通讯距离会缩短。
node_config.interruptConfig.reint.priority = 11; //接收成功中断的优先级设为11
这行代码将CAN接收中断的优先级定为11。CAN接收中断表示,当接收到目标ID的CAN报文的时候,芯片会产生一个中断,通知处理器过来处理接收到的信息,这是CAN报文能够顺利实现信息传递的关键。
node0_filter0.id1 = 0x010; //在指定缓冲区模式下,配置id1为过滤id
这行代码就是设定待接收的目标报文的ID。重复这个操作,可以设定多个目标报文ID。
IFX_INTERRUPT(CAN_rx_isr,0,11); //声明CAN接收中断服务函数
这一行代码就是将优先级11的中断也就是CAN报文接收中断与中断回调函数“CAN_rx_isr()”联系起来。
void CAN_rx_isr(void)
{
IfxCan_Message rx_msg = {0};
uint32 data_buf[2] = {0}; //存数据的临时缓冲区
IfxCan_Node_clearInterruptFlag(node0_handle.node,IfxCan_Interrupt_messageStoredToDedicatedRxBuffer); //清除接收完成中断
rx_msg.bufferNumber = IfxCan_RxBufferId_0; //指定获取第0号接收缓冲区
IfxCan_Can_readMessage(&node0_handle,&rx_msg,data_buf); //读取消息,将数据存入缓冲区便于程序处理
rx_id = (uint16)rx_msg.messageId; //将接收到的报文ID保存下来
rx_len = (uint8)rx_msg.dataLengthCode; //获取接收到的数据长度
/*取出接收到的报文数据*/
for(uint32 cnt = 0; cnt < rx_len; cnt ++)
{
rx_buf[cnt] = ((uint8 *)data_buf)[cnt]; //以字节来读取数据
}
}
我们可以看见,CAN中断的回调函数主要功能就是将接收到报文的ID、长度、内容全都放到设定好的全局变量中缓存。CAN通讯配置主要就是配置端口,波特率,报文ID信息。这段代码主要功能是周期性发送ID为0x123的报文,周期是通过GPT定时中断设定的。发送报文的第0字节为接收到ID为0x10的报文的第0个字节,发送报文的第1字节每次都自增1。程序的结构如下图所示。
三、下载调试
程序编写完毕后,我们点击任务栏中的编译按钮。
编译完成后,在输出窗口可以看见“编译完成,无错误,无警告”的字样。

然后我们通过Type-C将测试板与你的电脑相连。
点击任务栏中的下载按钮。
下载完成后,在输出窗口可以看见“下载成功”的字样。
将测试板的CAN_H(红色)和CAN_L(蓝色)接出来通过DB9接头与CAN卡相连。
将CAN卡通过USB接口与电脑相连。
个人电脑的上位机我们使用TSMaster。TSMaster 是上海同星智能推出的国产汽车电子虚拟仪器软件平台,核心用于汽车总线监控仿真、诊断、标定、ECU刷写与自动化测试,兼容主流第三方硬件与工具链,且基础版免费、适合车载嵌入式与电驱动系统开发。可在其官网免费下载使用,使用方法在其官方公众号和B站账号均有详细介绍。我们这里仅使用其最基本的CAN报文收发功能。
配置好TSMaster后,开发板已经在周期性的发送ID为0x123的报文了,且第0字节一直在自增。我们用TSMaster发送一帧ID为0x10的报文后,我们发现0x123报文的第0字节就将接收到的0x10报文第0位0xAA转发出来了,至此我们学会了使用TC334完成了CAN的报文收发配置。
总结
今天我们介绍了如何运用TC334芯片CAN通讯模块实现了报文的收发。后续我们将继续介绍如何实现该芯片ADC采样模块的配置。敬请收藏关注,不迷路。
如需测试板,请点击。
https://store.weixin.qq.com/shop/b/feHVuMfOAmRCoHk?entrance_id=h5
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)