概述
今天来调一下nxp S32K146的CAN通讯,硬件部分使用的是NXP TJA1043 CAN通讯芯片先翻译一下数据手册。
一、TJA1043 有这么几个特点:
①几种保护和诊断功能,包括母线短路检测和电池连接检测
②CANFD快速阶段以高达5Mbit/s的数据速率进行可靠的通信。
③用于节点诊断和故障控制的仅监听模式
④传输数据(TXD)主要超时功能与诊断(如何实现?)
⑤TJA1043支持五种操作模式。控制引脚STB_N和EN用于选择操作模式。 在模式之间切换允许通过引脚ERR_N访问许多诊断标志。
其他都略过了,详细看一下7.1 Operating modes
正常模式Normal mode
在正常模式下,收发器可以通过总线CANH和CANL传输和接收数据。差分接收器将总线上的模拟数据转换为数字数据,然后输出到引脚RXD。引脚INH是运行的,因此由引脚INH控制的电压调节器也将是运行的。
只听模式 Listen-only mode
在只听模式下,收发器的发射器被禁用,有效地提供了收发器的只听”功能。接收器仍将将引脚CANH和CANL上的模拟总线信号转换为数字数据,可通过引脚RXD输出,引脚INH保持运行的。
待机模式Standby mode
待机模式是TJA1043的一级节电模式,提供减少电流消耗。在备用模式下,收发器无法传输或接收数据,并激活低功率接收机以监控总线活动。引脚INH仍然是运行的,所以由这个引脚控制的电压调节器也将是运行的。Pins RXD和ERR_N将反映任何活动的唤醒请求。
进入睡眠模式Go-to-Sleep mode
进入睡眠模式是进入睡眠模式的控制路径。在进入睡眠模式下,收发器表现为待机模式,并附加了一个向收发器发出进入睡眠的命令。在进入睡眠模式之前,收发器将保持最短保持时间。如果pin STB_N或pin EN的状态发生改变,或者在结束(分钟)之前设置了唤醒标志,收发器将不会进入休眠模式。
睡眠模式 Sleep mode
睡眠模式是TJA1043的二级节电模式。睡眠模式通过进入睡眠模式进入,当VCC或VIO的欠压检测时间在相关电压水平恢复时进入。在睡眠模式下,收发器按照待机模式的描述行为,除了引脚INH设置为浮动。由此引脚控制的电压调节器将关闭,进入引脚VBAT的电流将减少到最小。pin STB_N、EN和Wake标志可以用来从睡眠模式中唤醒。
注:INH拉出控制供电芯片的使能脚,也就是通过该引脚控制电源芯片是否使能。
再翻译一下 7.2内部标志
TJA1043使用7个内部标志为其故障安全回退模式控制和系统诊断支持。控制器可以通过引脚ERR_N轮询其中五个标志。在任何时候,引脚ERR_N上可用的标志取决于活动操作模式和许多其他条件。
UVNOM标志
UVNOM是VCC和VIO欠压检测标志。当引脚VCC上的电压低于VCC欠压检测电压Vuvd(VCC),且持续时间超过欠压检测时间tdet(uv),或者当引脚VIO上的电压低于Vuvd(VIO)且持续时间超过tdet(uv)时,设置该标志。设置UVNOM标志后,收发器进入Sleep模式,节省电能,保证总线不受干扰。在睡眠模式下,连接到引脚INH的稳压器是禁用的,避免任何额外的功耗,可能产生的短路条件。任何唤醒请求,设置Pwon标志或STB_N上的LOW-to-HIGH转换将清除UVNOM和定时器,允许稳压器重新激活(至少直到UVNOM再次被设置)。如果VCC和VIO恢复时间超过欠压恢复时间trec(uv), UVNOM也将被清除。然后,收发器将切换到由引脚STB_N和EN上的逻辑电平指示的工作模式。
UVBAT 标志
UVBAT是VBAT欠压检测标志。当引脚VBAT上的电压低于vvd (VBAT)时设置该标志。当设置UVBAT时,收发器将尝试进入待机模式以节省电力,并将从总线断开(零负载)。当引脚VBAT电压恢复时,UVBAT被清除。然后,收发器将切换到由引脚STB_N和EN上的逻辑电平指示的工作模式。
Pwon 标志
Pwon是VBAT开机标志。当引脚VBAT上的电压在之前下降到vvd (VBAT)以下后恢复时,设置该标志(通常是因为电池断开)。设置Pwon标志可以清除UVNOM标志和定时器。唤醒和唤醒源标志的设置,以确保在所有供应条件下的一致系统上电。在Listen-only模式下,Pwon标志可以通过引脚ERR_N进行轮询。当收发器进入Normal模式时,该标志被清除。
wake 标志
当收发器检测到本地或远程唤醒请求时设置唤醒标志。当引脚WAKE上的逻辑电平发生变化时,检测到本地唤醒请求,并且新电平至少在唤醒后保持稳定。可在待机模式、转睡眠模式或睡眠模式下设置唤醒标志。设置唤醒标志可以清除UVNOM标志和定时器。一旦设置,唤醒标志状态立即在引脚ERR_N和RXD上可用(提供VIO和VBAT)。该标志也在上电时设置,当设置UVNOM标志或收发器进入正常模式时清除。
Remote wake-up (via the CAN bus)
当总线上检测到专用的唤醒模式(在ISO 11898- 2:16中指定)时,TJA1043从待机或睡眠模式中唤醒。这种过滤有助于避免虚假的唤醒事件。一个虚假的唤醒序列可以被触发,例如,一个主导箝位总线或主导相位由噪声或尖峰总线上。唤醒模式包括:
•至少唤醒(busdom)的主导阶段紧随其后
•随后是至少苏醒(busrec)的隐性阶段
•至少唤醒(busdom)的主导阶段
在上述阶段之间的显性或隐性位,分别比twake(busdom) twake(busrec)短被忽略。在tto(wake)总线内必须接收到完全的显性-隐性-显性模式,才能被识别为有效的唤醒模式(见图5)。否则,内部唤醒逻辑将被重置。然后需要重新传输完整的唤醒模式来触发唤醒事件。引脚RXD保持高,直到唤醒事件被触发。当收到一个有效的唤醒模式时,如果以下任何一个事件发生,RXD上的唤醒事件不会被标记:
•TJA1043切换到普通模式
•在to(wake)总线中没有接收到完整的唤醒模式
•检测到VCC或VIO欠压
剩下的标志也不翻译了也不大能用上。。。。
二、S32K146 FLEXCAN SDK解读:
FlexCAN 模块是一个通信控制器,该模块实现了 CAN 协议即CAN2.0B 协议规范。 FlexCAN 模块的字模块,包括了用来存储消息缓冲的相关联的内存区域,Rx 全局掩码寄存器、Rx 私有掩码寄存器、Rx 先进先出队列以及 Rx 队列标识过滤器。消息缓冲区存储在一个专用于 FlexCAN 模块的 RAM 区,请求存取 RAM 接收和传输消息帧,验证接收到的消息以及进行错误处理。控制器主机接口子模块用来选择接收和传输的消息缓冲区,使用仲裁与 ID 匹配算法,以建立同 CPU 或者其他模块的连接。
模块特征:
- 0 到 8
字节长度报文缓冲区,每个报文缓冲区都可以配置成发送缓冲区或者接受缓冲区,支持标准和扩展帧格式,每个消息缓冲区都有自己的接受掩码控制寄存器 - 全功能的接受队列,该队列可以存储最多 6 个帧,并且自动进行内部指针处理,传输中止能力;可编程的 CAN 协议接口的时钟源,可以是总线时钟也可以是外部晶振;没有使用的结构空间可以当成普通的 RAM 空间使用;等其他特征。
操作模式
- 正常模式(用户与管理员) 在正常模式下,CAN 模块收发数据帧、处理错误,CAN协议的所有功能全部开启。对于一些控制比较严格的寄存器,在用户模式和管理员模式下访问时又区别的。
- 冻结模式
如果 MCR 寄存器的 FRZ 位被置位,那么将开启 CAN 模块的冻结模式。当 MCR 的 HALT 位置位时或者在 MCU 级请求调试模式(Debug Mode)并且 MCR 寄存器的FRZ_ACK 位被置位时,CAN
模块将进入到冻结模式。 - 监听模式
当控制 1 寄存器的 LOM 位置位时,模块将进入到监听模式。在该模式下,CAN 模块禁止数据收发,所有的错误计数器都被冻结,且该模块工作在 CAN 被动错误模式。只有被其他 CAN节点应答了的报文才可以被监听的节点接受。如果 FlexCAN 检测到一个还没有被应答的报文,则标记为一个 BIT0 错误(不会改变 REC),将如同它试图应答报文一些样。 - 监听模式
如果控制 1 寄存器的 LPB 位被置位,模块将进入到回环模式。在该模式下,FlexCAN 工作在内部闭环模式用于自测。从发送器发送出的比特流输出回内部的接收器输入。输入引脚将会被忽略,并且输出引脚将处于逻辑 1 状态。发送报文时,FlexCAN 模块和正常模式一样,而接收器则认为接受它自己的报文与接受远程节点的报文相同。FlexCAN 为保证能正确接受到自己发送的报文,将忽略应答间隙内的应答字段。报文接受发送时,如果中断使能则 FlexCAN 将向CPU 产生中断。 - 模块禁止模式
当 MCR 寄存器的 MDIS 位被 CPU 置位并且 LPM_ACK 位被FlexCAN 模块置位时,模块将会进入该低功耗模式。如果模块被禁止,那么模块将会请求停止 CAN 协议引擎的时钟并且请求禁止控制器主机接口子模块。通过忽略 MCR 寄存器 MDIS 位可以退出该模式。有关该模块的更多信息参考“模块禁止模式”节。 - 睡眠模式
当 MCR 寄存器的 DOZE 位被置位、在 MCU 级请求睡眠模式并且由 FlexCAN 置位 MCR 寄存器的 LPM_ACK 位时模块将进入到该低功耗模式。当模块处于睡眠模式时,FlexCAN 将会请求禁止CAN 协议引擎的时钟并且请求禁止 CAN 控制器主机接口子系统。当 MCR 寄存器的 DOZE 位被忽略(negated)、当 MCU 离开睡眠模式时或者当检测到 CAN 总线上有活动并且自醒机制开启时模块将退出睡眠模式。 - 停止模式
在 MCU 级请求模式并且由 FlexCAN 模块置位 MCR 寄存器的 LPM_ACK 位时,模块将会进入到该低功耗模式。在停止模式中,模块将会将自己置于不活动状态,然后通知 CPU 可以关闭所有的时钟。当请求离开停止模式或者当检测到 CAN 总线上有或者并且开启了自醒即使时,FlexCAN将会退出该模式。
SDK函数分析(一)
//这个函数将为经典帧设置所有的时间段值,
//或者为FD帧的仲裁阶段设置扩展的时间段。
//这些时间段值由用户传入,并基于所需的波特率。
void FLEXCAN_DRV_SetBitrate(uint8_t instance, const flexcan_time_segment_t *bitrate)
{
DEV_ASSERT(instance < CAN_INSTANCE_COUNT);
DEV_ASSERT(bitrate != NULL);
CAN_Type * base = g_flexcanBase[instance];
#if FEATURE_CAN_HAS_FD
bool fdEnabled = FLEXCAN_IsFDEnabled(base);
#endif
//只有在冻结模式下才能被写入,在其他模式下该位被硬件锁定
FLEXCAN_EnterFreezeMode(base);
#if FEATURE_CAN_HAS_FD
if (fdEnabled)
{
/* Set extended time segments*/
FLEXCAN_SetExtendedTimeSegments(base, bitrate);
}
else
#endif
{
/* Set time segments*/
FLEXCAN_SetTimeSegments(base, bitrate);
}
FLEXCAN_ExitFreezeMode(base);
}
//
static inline void FLEXCAN_SetTimeSegments(CAN_Type * base, const flexcan_time_segment_t *timeSeg)
{
DEV_ASSERT(timeSeg != NULL);
//0bit 传播时间段 16-18bit 相位段2位时间长度 19-21 相位段1位时间长度
//31-24 bit 预分频器分频系数
(base->CTRL1) = ((base->CTRL1) & ~((CAN_CTRL1_PROPSEG_MASK | CAN_CTRL1_PSEG2_MASK |
CAN_CTRL1_PSEG1_MASK | CAN_CTRL1_PRESDIV_MASK) |
CAN_CTRL1_RJW_MASK));
(base->CTRL1) = ((base->CTRL1) | (CAN_CTRL1_PROPSEG(timeSeg->propSeg) |
CAN_CTRL1_PSEG2(timeSeg->phaseSeg2) |
CAN_CTRL1_PSEG1(timeSeg->phaseSeg1) |
CAN_CTRL1_PRESDIV(timeSeg->preDivider) |
CAN_CTRL1_RJW(timeSeg->rJumpwidth)));
}
//FLEXCAN进入冻结模式
void FLEXCAN_EnterFreezeMode(CAN_Type * base)
{
bool enabled = false;
//使能进入冻结模式
base->MCR = (base->MCR & ~CAN_MCR_FRZ_MASK) | CAN_MCR_FRZ(1U);
//暂停FlexCAN模块
base->MCR = (base->MCR & ~CAN_MCR_HALT_MASK) | CAN_MCR_HALT(1U);
//判断是否关FLEXCAN模块
if (((base->MCR & CAN_MCR_MDIS_MASK) >> CAN_MCR_MDIS_SHIFT) == 0U)
{
enabled = true;
}
else
{
base->MCR &= ~CAN_MCR_MDIS_MASK;
}
//冻结模式确认位
while (((base->MCR & CAN_MCR_FRZACK_MASK) >> CAN_MCR_FRZACK_SHIFT) == 0U) {}
if (false == enabled)
{
base->MCR |= CAN_MCR_MDIS_MASK;
//是否进入低功耗
while (((base->MCR & CAN_MCR_LPMACK_MASK) >> CAN_MCR_LPMACK_SHIFT) == 0U) {}
}
}
//这个函数将返回经典帧或FD帧仲裁阶段的当前比特率设置。
void FLEXCAN_DRV_GetBitrate(uint8_t instance, flexcan_time_segment_t *bitrate)
{
DEV_ASSERT(instance < CAN_INSTANCE_COUNT);
DEV_ASSERT(bitrate != NULL);
const CAN_Type * base = g_flexcanBase[instance];
#if FEATURE_CAN_HAS_FD
if (true == FLEXCAN_IsFDEnabled(base))
{
/* Get the Extended time segments*/
FLEXCAN_GetExtendedTimeSegments(base, bitrate);
}
else
#endif
{
/* Get the time segments*/
FLEXCAN_GetTimeSegments(base, bitrate);
}
}
//这个函数将返回经典帧或FD帧仲裁阶段的当前比特率设置
static inline void FLEXCAN_GetTimeSegments(const CAN_Type * base, flexcan_time_segment_t *timeSeg)
{
DEV_ASSERT(timeSeg != NULL);
//得到刚才寄存器里面配置的值
timeSeg->preDivider = ((base->CTRL1) & CAN_CTRL1_PRESDIV_MASK) >> CAN_CTRL1_PRESDIV_SHIFT;
timeSeg->propSeg = ((base->CTRL1) & CAN_CTRL1_PROPSEG_MASK) >> CAN_CTRL1_PROPSEG_SHIFT;
timeSeg->phaseSeg1 = ((base->CTRL1) & CAN_CTRL1_PSEG1_MASK) >> CAN_CTRL1_PSEG1_SHIFT;
timeSeg->phaseSeg2 = ((base->CTRL1) & CAN_CTRL1_PSEG2_MASK) >> CAN_CTRL1_PSEG2_SHIFT;
timeSeg->rJumpwidth = ((base->CTRL1) & CAN_CTRL1_RJW_MASK) >> CAN_CTRL1_RJW_SHIFT;
}
//关于FLEXCAN的SDK分成两层 上层:flexcan_driver.c 底层flexcan_access.c
SDK函数分析(二)
//Set RX masking type
//flexcan_rx_mask_type_t 分为全局/个人屏蔽
void FLEXCAN_DRV_SetRxMaskType(uint8_t instance, flexcan_rx_mask_type_t type)
{
DEV_ASSERT(instance < CAN_INSTANCE_COUNT);
CAN_Type * base = g_flexcanBase[instance];
FLEXCAN_EnterFreezeMode(base);//上边分析了
FLEXCAN_SetRxMaskType(base, type);
FLEXCAN_ExitFreezeMode(base);//上边分析了
}
//判断是全局还是个人屏蔽
static inline void FLEXCAN_SetRxMaskType(CAN_Type * base, flexcan_rx_mask_type_t type)
{
//关闭个别接收掩码和队列功能。
if (type == FLEXCAN_RX_MASK_GLOBAL)
{
base->MCR = (base->MCR & ~CAN_MCR_IRMQ_MASK) | CAN_MCR_IRMQ(0U);
}
else//开启个别接收掩码和队列功能。
{
base->MCR = (base->MCR & ~CAN_MCR_IRMQ_MASK) | CAN_MCR_IRMQ(1U);
}
}
//设置Rx FIFO全局掩码为11位标准掩码或29位扩展掩码
void FLEXCAN_DRV_SetRxFifoGlobalMask(
uint8_t instance,
flexcan_msgbuff_id_type_t id_type,
uint32_t mask)
{
DEV_ASSERT(instance < CAN_INSTANCE_COUNT);
//FLEXCAN_GetRxFifoIdFormat中两位用来定义接受队列过滤器表元素的格式
// 格式A:每一个ID过滤表元素有一个全ID(标准的以及扩展的);
//格式B:每一个ID过滤表元素都有两个全标准IDs或者两个14比特(标准的及扩展的)的IDs;
//格式C:每一个过滤表元素有四个8比特标准IDs;
//格式D:拒绝所有的帧
flexcan_rx_fifo_id_element_format_t formatType;
CAN_Type * base = g_flexcanBase[instance];
uint32_t calcMask = 0U;
FLEXCAN_EnterFreezeMode(base);
//判断是否开启接收队列。该位用来控制是否开启接收队列
if (true == FLEXCAN_IsRxFifoEnabled(base))
{ //返回过滤器元素的格式
formatType = FLEXCAN_GetRxFifoIdFormat(base);
calcMask = FLEXCAN_GetRxFifoMask(id_type, formatType, mask);
//FLEXCAN_SetRxFifoGlobalMask设置接收队列 FIFO 全局掩码寄存器(CANx_RXFGMASK)该寄存器位于 RAM 区。
//如果接收 FIFO 队列被使能,RXFGMASK 被用来屏蔽接收队列 FIFO 过滤器表元素,
//这些表元素由于 CTRL2[RFEN]字段的设定并没有相应的 RXIMR。该寄存器只能在冻结模式下被写入,在其他模式下该字段被硬件锁定。
switch (formatType)
{
//FLEXCAN_SetRxFifoGlobalMask
//设置接收队列FIFO全局掩码位。这些比特位以完美对齐的方式用来屏蔽ID过滤器表元素
case FLEXCAN_RX_FIFO_ID_FORMAT_A :
FLEXCAN_SetRxFifoGlobalMask(base, calcMask);
break;
case FLEXCAN_RX_FIFO_ID_FORMAT_B :
FLEXCAN_SetRxFifoGlobalMask(base, (calcMask | (calcMask >> FLEXCAN_RX_FIFO_ID_FILTER_FORMATB_EXT_SHIFT1)));
break;
case FLEXCAN_RX_FIFO_ID_FORMAT_C :
FLEXCAN_SetRxFifoGlobalMask(base, (calcMask | (calcMask >> FLEXCAN_RX_FIFO_ID_FILTER_FORMATC_SHIFT1) |
(calcMask >> FLEXCAN_RX_FIFO_ID_FILTER_FORMATC_SHIFT2) |
(calcMask >> FLEXCAN_RX_FIFO_ID_FILTER_FORMATC_SHIFT3)));
break;
default :
FLEXCAN_SetRxFifoGlobalMask(base, 0xFFFFFFFFU);
break;
}
}
FLEXCAN_ExitFreezeMode(base);
}
//设置邮箱全局掩码
void FLEXCAN_DRV_SetRxMbGlobalMask(
uint8_t instance,
flexcan_msgbuff_id_type_t id_type,
uint32_t mask)
{
DEV_ASSERT(instance < CAN_INSTANCE_COUNT);
CAN_Type * base = g_flexcanBase[instance];
FLEXCAN_EnterFreezeMode(base);
if (id_type == FLEXCAN_MSG_ID_STD)
{
//设置接受邮箱全局掩码寄存器(CANx_RXMGMASK)
FLEXCAN_SetRxMsgBuffGlobalStdMask(base, mask);
}
else if (id_type == FLEXCAN_MSG_ID_EXT)
{
FLEXCAN_SetRxMsgBuffGlobalExtMask(base, mask);
}
else {
}
FLEXCAN_ExitFreezeMode(base);
}
//缓冲区14过滤字段提供掩码
void FLEXCAN_DRV_SetRxMb14Mask(
uint8_t instance,
flexcan_msgbuff_id_type_t id_type,
uint32_t mask)
{
DEV_ASSERT(instance < CAN_INSTANCE_COUNT);
CAN_Type * base = g_flexcanBase[instance];
FLEXCAN_EnterFreezeMode(base);
if (id_type == FLEXCAN_MSG_ID_STD)
{
//提供该寄存器是为了对传统的支持。当 MCR[IRMQ]位被置位时,RX14MASK 无效。
//RX14MASK 用来给报文缓冲区 14 的过滤字段提供掩码。
FLEXCAN_SetRxMsgBuff14StdMask(base, mask);
}
else if (id_type == FLEXCAN_MSG_ID_EXT)
{
FLEXCAN_SetRxMsgBuff14ExtMask(base, mask);
}
else {
}
FLEXCAN_ExitFreezeMode(base);
}
//RX15MASK 用来给报文缓冲区 15 的过滤字段提供掩码。
void FLEXCAN_DRV_SetRxMb15Mask(
uint8_t instance,
flexcan_msgbuff_id_type_t id_type,
uint32_t mask)
{
//略同上
}
//设置个别掩码
status_t FLEXCAN_DRV_SetRxIndividualMask(
uint8_t instance,
flexcan_msgbuff_id_type_t id_type,
uint8_t mb_idx,
uint32_t mask)
{
DEV_ASSERT(instance < CAN_INSTANCE_COUNT);
CAN_Type * base = g_flexcanBase[instance];
FLEXCAN_EnterFreezeMode(base);
//MAXMB最大报文缓冲区个数
if ((mb_idx > FLEXCAN_GetMaxMsgBuffNum(base)) || (mb_idx >= CAN_RXIMR_COUNT))
{
FLEXCAN_ExitFreezeMode(base);
return STATUS_CAN_BUFF_OUT_OF_RANGE;
}
//开启接收队列
if (false == FLEXCAN_IsRxFifoEnabled(base))
{//标准帧
if (id_type == FLEXCAN_MSG_ID_STD)
{
//设置接收私有掩码寄存器
FLEXCAN_SetRxIndividualStdMask(base, mb_idx, mask);
}
else if (id_type == FLEXCAN_MSG_ID_EXT)
{
//设置接收私有掩码寄存器
FLEXCAN_SetRxIndividualExtMask(base, mb_idx, mask);
}
else
{
}
}
else
{//单个掩码配置的最大过滤器是(7 + RFFN * 2)
if (mb_idx <= FLEXCAN_GetNoOfIndividualMBsRxFIFO(base))
{
//返回过滤器元素的格式
flexcan_rx_fifo_id_element_format_t formatType = FLEXCAN_GetRxFifoIdFormat(base);
//以FIFO模式的格式ID类型计算全局掩码
uint32_t calcMask = FLEXCAN_GetRxFifoMask(id_type, formatType, mask);
switch (formatType)
{
case FLEXCAN_RX_FIFO_ID_FORMAT_A :
FLEXCAN_SetRxIndividualMask(base, mb_idx, calcMask);//设置接收私有掩码寄存器,以FIFO模式的格式ID类型计算全局掩码
break;
case FLEXCAN_RX_FIFO_ID_FORMAT_B :
FLEXCAN_SetRxIndividualMask(base, mb_idx, (calcMask | (calcMask >> FLEXCAN_RX_FIFO_ID_FILTER_FORMATB_EXT_SHIFT1)));
break;
case FLEXCAN_RX_FIFO_ID_FORMAT_C :
FLEXCAN_SetRxIndividualMask(base, mb_idx, (calcMask | (calcMask >> FLEXCAN_RX_FIFO_ID_FILTER_FORMATC_SHIFT1) |
(calcMask >> FLEXCAN_RX_FIFO_ID_FILTER_FORMATC_SHIFT2) |
(calcMask >> FLEXCAN_RX_FIFO_ID_FILTER_FORMATC_SHIFT3)));
break;
default :
FLEXCAN_SetRxIndividualMask(base, mb_idx, 0xFFFFFFFFU);
break;
}
}
else
{
if (id_type == FLEXCAN_MSG_ID_STD)
{
FLEXCAN_SetRxIndividualStdMask(base, mb_idx, mask);
}
else if (id_type == FLEXCAN_MSG_ID_EXT)
{
FLEXCAN_SetRxIndividualExtMask(base, mb_idx, mask);
}
else
{
}
}
}
FLEXCAN_ExitFreezeMode(base);
return STATUS_SUCCESS;
}
SDK函数分析(三)
//flexcan初始化
status_t FLEXCAN_DRV_Init(
uint8_t instance,
flexcan_state_t *state,
const flexcan_user_config_t *data)
{
DEV_ASSERT(instance < CAN_INSTANCE_COUNT);
DEV_ASSERT(state != NULL);
DEV_ASSERT(g_flexcanStatePtr[instance] == NULL);
status_t result;
CAN_Type * base = g_flexcanBase[instance];
flexcan_time_segment_t bitrate;
status_t osifStat;
uint32_t i, j;
if(FLEXCAN_IsEnabled(base))
{
FLEXCAN_EnterFreezeMode(base);
FLEXCAN_Disable(base);
}
//CAN引擎时钟源。CTRL1 13BIT 该位用来为CAN协议引擎(PE)选择时钟源,
//可以是设备外设时钟(有PLL驱动)也可以是外部晶体振荡器时钟。
#if FEATURE_CAN_HAS_PE_CLKSRC_SELECT
FLEXCAN_SelectClock(base, data->pe_clock);
#endif
/* Enable the CAN clock */
FLEXCAN_Enable(base);
FLEXCAN_EnterFreezeMode(base);
//初始化 1.复位 2.中止传输 3.清空RAM 4.寄存器重置
//5.状态与错误寄存器init
FLEXCAN_Init(base);
#if FEATURE_CAN_HAS_FD
FLEXCAN_SetFDEnabled(base, data->fd_enable);
if (FLEXCAN_IsFDEnabled(base) != data->fd_enable)
{
return STATUS_ERROR;
}//flexcan_user_config_t配置FD是否使能
FLEXCAN_SetStuffBitCount(base, data->fd_enable);
#endif
//不是回环模式
if (data->flexcanMode != FLEXCAN_LOOPBACK_MODE)
{//MCR->SRXDIS使能自接受
FLEXCAN_SetSelfReception(base, false);
}
if (data->is_rx_fifo_needed)
{//使能循环队列 1.mcr->rfen 2.CTRL->RFEN接收队列FIFO过滤器的数目
//3.设置接受队列全局掩码寄存器(RXFGMASK)
//4.接收私有掩码寄存器RXIMR
result = FLEXCAN_EnableRxFifo(base, (uint32_t)data->num_id_filters);
if (result != STATUS_SUCCESS)
{
return result;
}
}
#if FEATURE_CAN_HAS_DMA_ENABLE
/* Enable DMA support for RxFIFO transfer, if requested. */
if (data->transfer_type == FLEXCAN_RXFIFO_USING_DMA)
{
if (FLEXCAN_IsRxFifoEnabled(base))
{
FLEXCAN_SetRxFifoDMA(base, true);
}
else
{
return STATUS_ERROR;
}
}
if (data->transfer_type == FLEXCAN_RXFIFO_USING_INTERRUPTS)
{
FLEXCAN_SetRxFifoDMA(base, false);
}
#endif
#if FEATURE_CAN_HAS_FD
//这个寄存器包含CAN FD操作的控制位。
// 它还定义了分配在RAM(内存块)的不同分区中的消息缓冲区的数据大小,如下表所示。
FLEXCAN_SetPayloadSize(base, data->payload);
#endif
//设置缓冲区数量
result = FLEXCAN_SetMaxMsgBuffNum(base, data->max_num_mb);
if (result != STATUS_SUCCESS)
{
return result;
}
#if FEATURE_CAN_HAS_FD
if (FLEXCAN_IsFDEnabled(base))
{
bitrate = data->bitrate;
FLEXCAN_SetExtendedTimeSegments(base, &bitrate);
bitrate = data->bitrate_cbt;
FLEXCAN_SetFDTimeSegments(base, &bitrate);
}
else
#endif
{
bitrate = data->bitrate;
FLEXCAN_SetTimeSegments(base, &bitrate);
}
FLEXCAN_SetOperationMode(base, data->flexcanMode);
if (data->flexcanMode != FLEXCAN_FREEZE_MODE)
{
FLEXCAN_ExitFreezeMode(base);
}
FLEXCAN_EnableIRQs(instance);
for (i = 0; i < FEATURE_CAN_MAX_MB_NUM; i++)
{
osifStat = OSIF_SemaCreate(&state->mbs[i].mbSema, 0U);
if (osifStat != STATUS_SUCCESS)
{
for (j = 0; j < i; j++)
{
(void)OSIF_SemaDestroy(&state->mbs[j].mbSema);
}
return STATUS_ERROR;
}
state->mbs[i].isBlocking = false;
state->mbs[i].mb_message = NULL;
state->mbs[i].state = FLEXCAN_MB_IDLE;
}
#if FEATURE_CAN_HAS_MEM_ERR_DET
FLEXCAN_DisableMemErrorDetection(base);
#endif
state->transferType = data->transfer_type;
#if FEATURE_CAN_HAS_DMA_ENABLE
state->rxFifoDMAChannel = data->rxFifoDMAChannel;
#endif
state->callback = NULL;
state->callbackParam = NULL;
state->error_callback = NULL;
state->errorCallbackParam = NULL;
g_flexcanStatePtr[instance] = state;
return (STATUS_SUCCESS);
}
SDK函数分析(四)
//发送mb cofig
status_t FLEXCAN_DRV_ConfigTxMb(
uint8_t instance,
uint8_t mb_idx,
const flexcan_data_info_t *tx_info,
uint32_t msg_id)
{
DEV_ASSERT(instance < CAN_INSTANCE_COUNT);
DEV_ASSERT(tx_info != NULL);
flexcan_msgbuff_code_status_t cs;
CAN_Type * base = g_flexcanBase[instance];
//这个没啥说的就是赋值
cs.dataLen = tx_info->data_length;
cs.msgIdType = tx_info->msg_id_type;
#if FEATURE_CAN_HAS_FD
cs.enable_brs = tx_info->enable_brs;
cs.fd_enable = tx_info->fd_enable;
cs.fd_padding = tx_info->fd_padding;
#endif
cs.code = (uint32_t)FLEXCAN_TX_INACTIVE;
//下边解释
return FLEXCAN_SetTxMsgBuff(base, mb_idx, &cs, msg_id, NULL);
}
//设置flexcan 发送msg buff
status_t FLEXCAN_SetTxMsgBuff(
CAN_Type * base,
uint32_t msgBuffIdx,
const flexcan_msgbuff_code_status_t *cs,
uint32_t msgId,
const uint8_t *msgData)
{
DEV_ASSERT(cs != NULL);
uint32_t val1, val2 = 1;
uint32_t flexcan_mb_config = 0;
uint32_t databyte;
uint8_t dlc_value;
status_t stat = STATUS_SUCCESS;
//这个函数多墨迹几句在四的结尾处
volatile uint32_t *flexcan_mb = FLEXCAN_GetMsgBuffRegion(base, msgBuffIdx);
//拿到刚才给的地址 指针指向
volatile uint32_t *flexcan_mb_id = &flexcan_mb[1];
volatile uint8_t *flexcan_mb_data = (volatile uint8_t *)(&flexcan_mb[2]);
volatile uint32_t *flexcan_mb_data_32 = &flexcan_mb[2];
const uint32_t *msgData_32 = (const uint32_t *)msgData;
//最大缓冲区的个数
if (msgBuffIdx > (((base->MCR) & CAN_MCR_MAXMB_MASK) >> CAN_MCR_MAXMB_SHIFT) )
{
stat = STATUS_CAN_BUFF_OUT_OF_RANGE;
}
if (((base->MCR & CAN_MCR_RFEN_MASK) >> CAN_MCR_RFEN_SHIFT) != 0U)
{
//这几句解释起来真烦
//这个CTRL2寄存器的27-24(RFEN)
//这4位用来定义了下表中所示的接收队列过滤器的数目
//过滤器使用的越多,满足条件的邮箱越少
//eg:RFEN=01 消息邮箱 MB8-63 val1=0x01
val1 = (((base->CTRL2) & CAN_CTRL2_RFFN_MASK) >> CAN_CTRL2_RFFN_SHIFT);
//val2 = 9
val2 = RxFifoOcuppiedLastMsgBuff(val1);
//为啥现在我也不知道。。。脑子不够用了,搞不动了
if (msgBuffIdx <= val2) {
stat = STATUS_CAN_BUFF_OUT_OF_RANGE;
}
}
if (stat == STATUS_SUCCESS)
{
#if FEATURE_CAN_HAS_FD
if (FLEXCAN_IsFDEnabled(base) && cs->enable_brs)
{
base->FDCTRL = (base->FDCTRL & ~CAN_FDCTRL_FDRATE_MASK) | CAN_FDCTRL_FDRATE(1U);
}
DEV_ASSERT((uint8_t)cs->dataLen <= FLEXCAN_GetPayloadSize(base));
#else
DEV_ASSERT((uint8_t)cs->dataLen <= 8U);
#endif
dlc_value = FLEXCAN_ComputeDLCValue((uint8_t)cs->dataLen);
if (msgData != NULL)
{
uint8_t payload_size = FLEXCAN_ComputePayloadSize(dlc_value);
#if (defined(CPU_S32K116) || defined(CPU_S32K118))
(void) msgData_32;
databyte = FLEXCAN_DataTransferTxMsgBuff( flexcan_mb_data_32, cs, msgData);
#else
for (databyte = 0; databyte < (cs->dataLen & ~3U); databyte += 4U)
{
FlexcanSwapBytesInWord(msgData_32[databyte >> 2U], flexcan_mb_data_32[databyte >> 2U]);
}
#endif
for ( ; databyte < cs->dataLen; databyte++)
{
flexcan_mb_data[FlexcanSwapBytesInWordIndex(databyte)] = msgData[databyte];
}
/* Add padding, if needed */
for (databyte = cs->dataLen; databyte < payload_size; databyte++)
{
flexcan_mb_data[FlexcanSwapBytesInWordIndex(databyte)] = cs->fd_padding;
}
}
/* Clean up the arbitration field area */
*flexcan_mb = 0;
*flexcan_mb_id = 0;
/* Set the ID according the format structure */
if (cs->msgIdType == FLEXCAN_MSG_ID_EXT)
{
/* ID [28-0] */
*flexcan_mb_id &= ~(CAN_ID_STD_MASK | CAN_ID_EXT_MASK);
*flexcan_mb_id |= (msgId & (CAN_ID_STD_MASK | CAN_ID_EXT_MASK));
/* Set IDE */
flexcan_mb_config |= CAN_CS_IDE_MASK;
/* Clear SRR bit */
flexcan_mb_config &= ~CAN_CS_SRR_MASK;
}
if(cs->msgIdType == FLEXCAN_MSG_ID_STD)
{
/* ID[28-18] */
*flexcan_mb_id &= ~CAN_ID_STD_MASK;
*flexcan_mb_id |= (msgId << CAN_ID_STD_SHIFT) & CAN_ID_STD_MASK;
/* make sure IDE and SRR are not set */
flexcan_mb_config &= ~(CAN_CS_IDE_MASK | CAN_CS_SRR_MASK);
}
/* Set the length of data in bytes */
flexcan_mb_config &= ~CAN_CS_DLC_MASK;
flexcan_mb_config |= ((uint32_t)dlc_value << CAN_CS_DLC_SHIFT) & CAN_CS_DLC_MASK;
/* Set MB CODE */
if (cs->code != (uint32_t)FLEXCAN_TX_NOT_USED)
{
if (cs->code == (uint32_t)FLEXCAN_TX_REMOTE)
{
/* Set RTR bit */
flexcan_mb_config |= CAN_CS_RTR_MASK;
}
/* Reset the code */
flexcan_mb_config &= ~CAN_CS_CODE_MASK;
/* Set the code */
if (cs->fd_enable)
{
flexcan_mb_config |= ((cs->code << CAN_CS_CODE_SHIFT) & CAN_CS_CODE_MASK) | CAN_MB_EDL_MASK;
}
else
{
flexcan_mb_config |= (cs->code << CAN_CS_CODE_SHIFT) & CAN_CS_CODE_MASK;
}
if (cs->enable_brs)
{
flexcan_mb_config |= CAN_MB_BRS_MASK;
}
*flexcan_mb |= flexcan_mb_config;
}
}
return stat;
}
//根据msgBuffIdx返回地址
volatile uint32_t* FLEXCAN_GetMsgBuffRegion(
CAN_Type * base,
uint32_t msgBuffIdx)
{
#if FEATURE_CAN_HAS_FD
uint8_t payload_size = FLEXCAN_GetPayloadSize(base);
#else
uint8_t payload_size = 8U;
#endif
uint8_t arbitration_field_size = 8U;
//#define CAN_RAMn_COUNT 128u
//typedef struct
//{
// __IO uint32_t RAMn[CAN_RAMn_COUNT]; array offset: 0x80,
//} CAN_Type, *CAN_MemMapPtr;
//根据上边这几句所以uint32_t ramBlockSize = 512U;
uint32_t ramBlockSize = 512U;
uint32_t ramBlockOffset;
//一个MB的大小
uint8_t mb_size = (uint8_t)(payload_size + arbitration_field_size);
//按这个算最大的mb个数
uint8_t maxMbNum = (uint8_t)(ramBlockSize / mb_size);
//偏移 eg msgBuffIdx =1 ramBlockOffset =0
ramBlockOffset = 128U * (msgBuffIdx / (uint32_t)maxMbNum);
//在这块ram中索引=0+1*16/4=4
uint32_t mb_index = ramBlockOffset + ((msgBuffIdx % (uint32_t)maxMbNum) * ((uint32_t)mb_size >> 2U));
return &(base->RAMn[mb_index]);
}
//最大512个字节 根据mb_size算一共有几个偏移多少返回地址
初始化需要的SDK函数
static void Can0Init(void)
{
//TJA1043 EN STB 全部使能高电平进入正常收发状态
hal_mcu_can_modeset();
FLEXCAN_DRV_Init(INST_CANCOM0, &canCom0_State, &canCom0_InitConfig0);
FLEXCAN_DRV_ConfigRxMb(INST_CANCOM0, RX_MAILBOX_0, &data_std_info, 0x87);
FLEXCAN_DRV_ConfigTxMb(INST_CANCOM0, 1, &data_std_info, 0x228);
FLEXCAN_DRV_InstallEventCallback(INST_CANCOM0, canRxCallback, NULL);
FLEXCAN_DRV_Receive(INST_CANCOM0, RX_MAILBOX_0, &recvMsg0);
}
//CALLBACK 发什么回什么
void canRxCallback(uint8_t instance, flexcan_event_type_t eventType,
uint32_t buffIdx, flexcan_state_t *flexcanState) {
if(eventType == FLEXCAN_EVENT_RX_COMPLETE)
{
FLEXCAN_DRV_Receive(INST_CANCOM0, RX_MAILBOX_0, &recvMsg0);
FLEXCAN_DRV_Send(INST_CANCOM0, TX_MAILBOX_0 , &data_std_info , recvMsg0.msgId, recvMsg0.data);
}
}
接下来搞一下掩码FIFO
NXP S32K146 CAN通讯 TJA1043(二)
最后
以上就是开心钢铁侠为你收集整理的NXP S32K146 CAN通讯 TJA1043(一)的全部内容,希望文章能够帮你解决NXP S32K146 CAN通讯 TJA1043(一)所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复