CAN通信基础:Tx Comfirmation、Rx Indication以及Ack

嵌入式开发中,知识细碎,工程实际中,如果某些细碎的问题理解不到位,就可能成为Bug的拦路虎。本文,聊一聊CAN通信中的Tx Comfirmation、Rx Indication以及Acknowledgement(Ack)。

************************************************************************************

关注微信公众号“开心果 Need Car”,一起讨论Autosar开发中遇到的那些“坑”!

************************************************************************************

1、Tx Comfirmation 与 Acknowledgement(Ack)

了解Tx Comfirmation之前,我们需要先清楚“发送请求(Transmit Request)”,只有先发送请求,才有对请求结果的确认(Comfirmation)。可以参考前文Autosar通信栈:发送返回OK和发送确认是一回事吗

先说发送请求,当用户请求发送CAN报文时,最终会调用Can_Write()接口,完成报文发送的请求动作,该接口有一个返回值,表示发送请求成功与否。如果发送请求成功,返回E_OK;如果发送请求不成功,返回E_NOT_OK。E_OK表示什么意思呢?意思是说:CAN驱动有可用的缓存空间(RAM),请求发送的报文信息已经被成功地放入CAN驱动缓存区(注意,还没有发送到CAN BUS),这只是完成了发送请求,如下绿色框图:

当发送的报文信息被成功添加到驱动缓存区以后,这些报文就时刻准备发送到总线。我们知道,一个网段内可以有多个节点,每个节点都等着发送各自缓存区中的报文,谁先发送呢?答:通过仲裁决定哪个节点可以使用CAN BUS,仲裁赢的节点,获取总线使用权,进而发送报文。谁发送的报文优先级高(CAN ID越小,优先级越高)谁就可以抢占总线使用权。可以参考前文CAN总线仲裁原理

当网段内的某个节点获取总线使用权以后,就开始发送缓存区中最高优先级的报文,发送节点为了获悉自己所发报文信息是否正确,在使用Tx Pin发送的同时,使用Rx Pin接收(CAN总线是串行总线),当然,总线上的其他节点(处于监听状态的节点)也会通过CAN BUS接收总线上的报文。发送节点边发送边接收过程如下所示:

动图封面

发送节点按照CAN报文格式发送各个位域,发送节点发送ACK Field(ACL Slot、ACK Delimiter)时,2 Bit均发送隐性位(Recessive,1),当接收节点接收到ACK Field的第一个Bit(ACK Slot,ACK应答槽)时,接收节点将其置为显性位(Dominant,0),由于"线与"原则(显性位覆盖隐性位),所以,CAN BUS上,ACK Slot = 0,这个动作称为应答(Acknowledgement),即:接收节点应答发送节点,告知发送节点,其发送的报文已被成功接收,如下所示:

接收节点收到隐性("1")的Ack Slot时,将其置为显性("0"),这个动作由接收节点的Tx Pin触发。提示

  • Ack动作由Controller完成,不是Transceiver;
  • 总线上可能有多个接收节点,只要其中一个应答(Ack),发送节点就认为数据发送成功;
  • 当总线上没有节点应答发送节点时(即:总线上只有一个节点),发送节点检测到Ack Slot为隐性("1"),会发出错误帧(Acknowledgment Error),同时,发送节点的TEC累加,之后发送节点尝试重传。所以,TEC很快会达到128,进入Error Passive模式。注意,如果一直是Acknowledgment Error,TEC不会再累加,不会进入Bus Off。

当发送节点的发送报文被其他节点成功接收以后,发送节点就会将这个信息反馈给对应的User,告诉请求发送的User,这个过程就是Tx Comfirmation。这个反馈途径有两种:

  1. 中断方式,成功将一帧报文发送到总线,且被其他节点有效接收以后,发送节点进入发送完成中断例程,调用User注册的回调接口,比如:CanIf_TxConfirmation(),CanIf在进一步调用目标上层Xx_TxComfirmation 回调接口告知User发送结果。
  2. Polling方式,CAN驱动的Main函数周期性(eg:5ms)的检测是否有报文被成功发送,如果有,则调用目标User的Xx_TxConfirmation(),告知其请求发送报文被接收的结果,即:已被其他节点成功接收,且被应答。

2、Rx Indication

什么是Rx Indication呢?CAN BUS上有节点发送报文,就有目标节点接收报文,当接收节点接收到所需的报文以后,需要将接收的信息给到目标User,如下所示:

动图封面

如何通知目标节点呢?两种方式:

  1. 中断方式,当接收节点成功接收到一帧CAN报文以后,程序进入接收完成中断例程,在接收完成中断例程中,会通过上层注册好的Call Back(eg:CanIf_RxIndication())将接收的报文信息送给目标上层。
  2. Polling方式,CAN驱动的Main主函数不断地查询是否有报文接收,如果有,则调用Xx_RxIndication()将接收信息送给目标User处理。

如上,将接收报文信息通知到目标User的过程就是Rx Indication。

************************************************************************************

关注微信公众号“开心果 Need Car”,一起讨论Autosar开发中遇到的那些“坑”!

************************************************************************************

CAN通信基础:Tx Comfirmation、Rx Indication以及Ack - 知乎 (zhihu.com)

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是代码实现: ``` #include "stm32f10x.h" #include "stm32f10x_can.h" #define CAN_RX_PIN GPIO_Pin_11 #define CAN_TX_PIN GPIO_Pin_12 #define CAN_PORT GPIOA void GPIO_Config(void); void CAN_Config(void); int main(void) { GPIO_Config(); // 配置GPIO CAN_Config(); // 配置CAN while(1) { } } void GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟 // 将PA0配置为输出模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOA, &GPIO_InitStructure); // 将PA11和PA12配置为复用推挽输出模式 GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN | CAN_TX_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_Init(CAN_PORT, &GPIO_InitStructure); // 将PA11配置为上拉输入模式 GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; GPIO_Init(CAN_PORT, &GPIO_InitStructure); } void CAN_Config(void) { CAN_InitTypeDef CAN_InitStructure; CAN_FilterInitTypeDef CAN_FilterInitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE); // 使能CAN1时钟 CAN_DeInit(CAN1); // 复位CAN1 // CAN1初始化 CAN_InitStructure.CAN_TTCM = DISABLE; CAN_InitStructure.CAN_ABOM = DISABLE; CAN_InitStructure.CAN_AWUM = DISABLE; CAN_InitStructure.CAN_NART = ENABLE; CAN_InitStructure.CAN_RFLM = DISABLE; CAN_InitStructure.CAN_TXFP = DISABLE; CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; CAN_InitStructure.CAN_BS1 = CAN_BS1_6tq; CAN_InitStructure.CAN_BS2 = CAN_BS2_8tq; CAN_InitStructure.CAN_Prescaler = 4; CAN_Init(CAN1, &CAN_InitStructure); // CAN过滤器初始化 CAN_FilterInitStructure.CAN_FilterNumber = 0; CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000; CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000; CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000; CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000; CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0; CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; CAN_FilterInit(&CAN_FilterInitStructure); // CAN接收中断使能 CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); NVIC_EnableIRQ(CAN1_RX0_IRQn); } void CAN1_RX0_IRQHandler(void) { CanRxMsg RxMessage; // 接收到CAN数据 if(CAN_GetITStatus(CAN1, CAN_IT_FMP0) != RESET) { CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0); // 获取接收到的CAN数据 CAN_Receive(CAN1, CAN_FIFO0, &RxMessage); // 判断波特率和帧ID是否符合要求 if((RxMessage.StdId == 0xB4) && (RxMessage.Data[0] == 0x00) && (RxMessage.Data[1] == 0x00) && (RxMessage.Data[2] == 0x00) && (RxMessage.Data[3] == 0x01) && (RxMessage.Data[4] == 0x00) && (RxMessage.Data[5] == 0x00) && (RxMessage.Data[6] == 0x00) && (RxMessage.Data[7] == 0x00)) { // 将PA0输出低电平 GPIO_SetBits(GPIOA, GPIO_Pin_0); } } } ``` 以上代码实现了PA0的输出低电平功能,当CAN RX接收到波特率为500K,帧ID为0XB4,数据为00 00 00 01 00 00 00 00时触发。需要注意的是,由于PA11和PA12被配置为复用推挽输出模式,因此需要在初始化CAN之前将它们配置为该模式。同时,还需要在CAN接收中断中判断接收到的数据是否符合要求,符合要求才触发输出低电平的功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值