一、CAN(Controller Area Network)
1、CAN1发送一帧报文
软件把一帧CAN报文的内容(identifier,IDE标志,RTR标志,数据长度及数据内容),写到CAN1的空闲发送邮箱mailbox_X(X取值范围 0~2,CAN1共有3个发送邮箱,每个邮箱都有自己的空闲标志TME bit),并请求CAN1发送mailbox_X(置位mailbox_X对应的TXRQ标志,置位后邮箱mailbox_X变成非空闲状态),CAN1收到请求后根据mailbox_X的优先级来决定是将其挂起还是立刻响应(By identifier or By transmit request order),如果是立刻响应,则当CAN1检测到总线空闲时会立刻发送mailbox_X,发送结束后:标志TXOK_X、ALST_X和TERR_X表示 mailbox_X的发送结果(TXOK_X置位表示发送成功,ALST_X置位表示仲裁失败,TERR_X置位表示发送错误),寄存器LEC[2:0] 表示发送错误的具体原因,若发送错误(LEC[2:0] > 0)则触发CAN1_SCE中断(LEC中断使能 且 ERR中断使能时);若没用自动重传功能(NART = 1),则mailbox_X变为空闲,并且标志RQCP_X置位并触发CAN1_TX中断(TME中断使能时); 若使用自动重传功能,发送失败则重新发送,发送成功则mailbox_X变为空闲 且 标志RQCP_X置位并触发CAN1_TX中断(TME中断使能时)。需要注意的是,使用自动重传功能时,因发送错误而触发CAN1_SCE中断时,可在中断函数中判断错误原因是否为ACK错误(接收节点不存在),如果是,则可手动取消 mailbox_X 的发送。
2、CAN1接收一帧报文
CAN1从总线上收到一帧报文,此报文通过了过滤器X(X取值范围 0 ~ 13, CAN1最多14个Filter bank,每个 Filter bank 可设置为 四个16位或两个32位的列表模式 或 掩码模式)的过滤后,被保存到与 过滤器X 绑定的FIFO_Y中(Y取值范围 0 ~ 1, CAN1有两个FIFO,过滤器X 可以绑定其中任意一个,每个FIFO能缓存3帧报文,FIFO_Y没满或虽然满了但没上锁,都会保存新报文),然后CAN1更新FIFO_Y的状态(缓存的报文数量 FMP_Y[1:0],已满标志FULL_Y,溢出标志FOVR_Y),并触发CAN1_FIFO_Y中断(FMP_Y中断、FULL_Y中断、FOVR_Y中断使能时),软件在CAN1_FIFO_Y中断函数中,读出FIFO_Y中的报文(FIFO_Y能缓存3帧报文,但这3帧报文只通过一个 FIFO_Y output mailbox对软件开放,FIFO_Y output mailbox存放的总是FIFO_Y中最老的那帧报文,软件读完FIFO_Y output mailbox后,需手动置位 RFOM_Y标志,这样FIFO_Y的报文数量 FMP_Y[1:0]就会自动减1,并让FIFO_Y output mailbox存放下一帧报文(同时CAN会自动清零RFOM_Y标志),这样软件再读FIFO_Y output mailbox读到的就是FIFO_Y的下一帧报文)。
二、程序开发流程
1、发送多帧CAN报文(使用CAN自动重发功能,禁能bus-off自动管理)
先把第一帧报文写入空闲的 TX Mailbox_X(发送邮箱有3个) 并请求CAN1发送此邮箱(置位 邮箱对应的TXRQ标志),剩下的报文缓存到buff中, TX Mailbox_X 发送完成后(发送成功 or 因没有接收节点ACK而发送失败),RQCP_X标志置位并触发CAN1_TX中断,在CAN1_TX中断函数中,从缓存buff中取出一帧报文 并写入 TX Mailbox_X,然后请求CAN1发送此邮箱。(注意:当CAN bus-off 时,禁止发送任何报文)
PS:
①、TX Mailbox_X 发送失败的原因有两个:1、仲裁失败; 2、发送出错了。
如果 TX Mailbox_X 是发送出错了,且是因为没有接收节点ACK而出错,此时为了避免缓存中的报文被阻塞,软件可手动取消 TX Mailbox_X 的重传,然后从缓存中取出下一帧报文写入TX Mailbox_X 并请求CAN1发送此邮箱。具体操作过程如下:
因为使能了自动重发功能,TX Mailbox_X 发送失败后会重发,标志RQCP_X不置位(不触发CAN1_TX中断),但 TXOK_X标志会清零 且 TERR_X标志会置位,同时 LEC[2:0]指示错误原因并触发 CAN1_SCE中断,在CAN1_SCE中断函数中,根据“LEC[2:0]为3”知道是发送出错 且出错原因是没有接收节点ACK, 根据 “TERR_X置位” 可知道发送失败的具体邮箱是 TX Mailbox_X,此时,即可判断出 TX Mailbox_X 发送错误 且原因是没有接收节点ACK,然后软件手动取消 TX Mailbox_X 的发送(置位 ABRQ_X标志),取消后,标志 RQCP_X置位并触发CAN1_TX中断,在CAN1_TX中断函数中,从缓存buff中取出一帧报文 并写入 TX Mailbox_X,然后请求CAN1发送此邮箱。
②、当CAN多次发送失败后会进入bus-off 状态,此状态下CAN不再参与总线通信,此时为了避免CAN恢复太快而影响其他节点通信,使用软件定时恢复,当软件定时超时后,使能bus-off 恢复机制(软件控制CAN进入Initialization,然后再控制CAN进入Normal),后面当CAN连续128次检测到11位隐性电平后,自动退出bus-off 进入 error active,此时CAN可正常参与总线通信。
③、在CAN中使能 TME中断、LEC中断、BOFF中断、ERRI中断;在NVIC中使能 CAN1_TX中断、CAN1_SCE中断,且两个中断的优先级要一样。
④、发送异常情况分析:
A、总线没有问题,但只挂了一个节点A:因为没有接收节点ACK,节点A每次发送都失败,节点A最终会进入 error passive 状态(在error passive 状态时,如果是ACK错误导致发送失败,则Transmit Error Counter不会增加,因此节点A最终停留在error passive,不会进入bus-off)。节点A在发送TX Mailbox_X 时,因为是ACK错误导致发送失败,软件会取消 TX Mailbox_X 的发送,因此TX Mailbox_X 自动重发功能无效,发送缓存中的报文不会被阻塞。
B、总线有问题:节点A每次发送都失败 且失败原因是随机的,最终会进入bus-off 状态。节点A进入bus-off 后,3个TX Mailbox 全部手动取消发送,同时清空发送缓存,并且禁止发送任何报文。
struct can_msg
{
uint32_t id : 29; //can idenifier
uint32_t ide : 1; //标准帧 or 扩展帧
uint32_t rtr: 1; //数据帧 or 远程帧
uint32_t rsv0 : 1;
uint8_t data_len : 4; //数据长度
uint8_t rsv1 : 4;
uint8_t data[8]; //数据内容
};
struct can_msg_list
{
struct list_head list;
struct can_msg data;
};
/* 定义缓存来保存待发送的CAN报文, 缓存使用双向环形链表,
空闲的缓存放在空闲链表中, 待发送的缓存放在工作链表中 */
static struct list_head g_freelist; //空闲链表
static struct list_head g_uselist; //工作链表
static struct can_msg_list g_can1_tx_node[100]; //静态开辟缓存空间
static struct timer g_busoff_timer; //bus-off恢复倒计时定时器(定时时间到后, 软件使能bus-off恢复机制)
/* 函数功能:用 CAN1 发送多帧CAN报文, 此接口供外部模块调用
返回值:实际发送报文帧数
*/
int can1_send_message(struct can_msg *msg, int msg_num)
{
int send_num = 0;
ASSERT(msg != NULL && msg_num > 0);
if(CAN1当前不处于Normal模式, 或 CAN1当前为 bus-off状态)
return 0;
if(BOFF中断 没有使能)
{
使能BOFF中断;
}
for(int i = 0; i < msg_num; i++)
{
if(链表g_freelist为空)
break;
interrupt_disable();
从链表g_freelist取出一个节点X, 把 msg[i]的内容保存到节点X中, 然后将节点X插入链表g_uselist;
interrupt_enable();
send_num ++;
}
if(send_num > 0, 且TX Mailbox0、TX Mailbox1、TX Mailbox2当中有空闲的邮箱)
{
从链表g_uselist取出一个节点X, 把节点X的内容写到空闲的发送邮箱中,
并请求CAN发送此邮箱, 然后将节点X插入链表g_freelist;
}
return send_num; //返回实际发送报文帧数
}
void can1_tx_irq_handler(void)
{
if(RQCP0标志置位)
{
清零RQCP0标志、TXOK0标志、TERR0标志、ALST0标志;
if(链表g_uselist 不为空)
{
从链表g_uselist取出一个节点X, 把节点X的内容写到 TX Mailbox0,
并置位 寄存器CAN_TI0R 的TXRQ bit(请求发送TX Mailbox0), 然后将节点X插入链表g_freelist;
}
}
if(RQCP1标志置位)
{
清零RQCP1标志、TXOK1标志、TERR1标志、ALST1标志;
if(链表g_uselist 不为空)
{
从链表g_uselist取出一个节点X, 把节点X的内容写到 TX Mailbox1,
并置位 寄存器CAN_TI1R 的TXRQ bit(请求发送TX Mailbox1), 然后将节点X插入链表g_freelist;
}
}
if(RQCP2标志置位)
{
清零RQCP2标志、TXOK2标志、TERR2标志、ALST2标志;
if(链表g_uselist 不为空)
{
从链表g_uselist取出一个节点X, 把节点X的内容写到 TX Mailbox2,
并置位 寄存器CAN_TI2R 的TXRQ bit(请求发送TX Mailbox2), 然后将节点X插入链表g_freelist;
}
}
}
void can1_sce_irq_handler(void)
{
if(BOFF标志置位 && BOFF中断使能) //can进入bus-off
{
禁能BOFF中断; //BOFF标志是只读标志, BOFF置位且BOFF中断使能时, 会自动置位ERRI标志进而触发CAN1_SCE中断
把链表g_uselist的所有节点全部移到链表g_freelist中;
手动取消TX Mailbox0、TX Mailbox1、TX Mailbox2发送; //取消后, RQCP0、RQCP1、RQCP2会置位(当对应邮箱不为空时)
timer_start(&g_busoff_timer); //bus-off恢复倒计时
}
if(LEC[2:0] 为3) //发送错误 且原因是没有收到节点ACK
{
if(RQCP0清零 && TERR0置位) //TX Mailbox0发送失败 且使能了自动重传
{
置位ABRQ0标志; //手动取消TX Mailbox0发送, 取消后, RQCP0会置位
}
if(RQCP1清零 && TERR1置位) //TX Mailbox1发送失败 且使能了自动重传
{
置位ABRQ1标志; //手动取消TX Mailbox1发送, 取消后, RQCP1会置位
}
if(RQCP2清零 && TERR2置位) //TX Mailbox2发送失败 且使能了自动重传
{
置位ABRQ2标志; //手动取消TX Mailbox2发送, 取消后, RQCP2会置位
}
}
清零LEC[2:0]; // 1 <= LEC[2:0] <= 6, 且LEC中断使能时, 会自动置位ERRI标志
清零ERRI标志; //此标志置位时可触发 CAN1_SCE中断
}
/* 定时超时后, 软件使能CAN的bus-off恢复机制: 让CAN进入Initialization, 然后再让CAN进入Normal
CAN的bus-off恢复机制:连续128次检测到11个隐性位
*/
void bus_off_recover_timeout(void)
{
#define CAN_TIMEOUT_VALUE 10
置位INRQ标志; //请求CAN进入Initialization
/* 等待CAN进入Initialization */
uint32_t tickstart = get_tick();
while (INAK标志 不为1)
{
if ((get_tick() - tickstart) > CAN_TIMEOUT_VALUE)
{
重新初始化CAN; //CAN进入Initialization失败, 重新初始化CAN
return;
}
}
清零INRQ标志; //请求CAN进入Normal
/* 等待CAN进入Normal */
uint32_t tickstart = get_tick();
while (INAK标志 不为0)
{
if ((get_tick() - tickstart) > CAN_TIMEOUT_VALUE)
{
重新初始化CAN; //CAN进入Normal失败, 重新初始化CAN
return;
}
}
}
/* 仅针对发送初始化 */
void can1_init(void)
{
清零SLEEP标志并置位INRQ标志, 然后等待 SLAK标志清零、INAK标志置位; //CAN复位后进入Sleep, 软件需先让CAN进入 Initialization后, 才能对CAN进行配置
初始化can1:
can1引脚:TX引脚;
不使用TEST功能:清零 SILM标志、LBKM标志;
禁能自动唤醒:清零 AWUM标志;
禁能bus-off自动管理:清零 ABOM标志;
设置波特率500K:APB1为36Mhz, 预分频系数为6, TS1_8TQ, TS2_3TQ,因此BRP[9:0] = 5, TS1[3:0] = 7, TS2[2:0] = 2;
配置重新同步的节拍个数2TQ, 故SJW[1:0] = 1; //设置波特率时, 相关参数的理论计算值需减1后再写入对应寄存器
使用自动重发:清零 NART标志
TX Mailbox优先级 by request order:置位 TXFP标志
清零INRQ标志, 然后等待INAK标志清零; //让CAN进入Normal,CAN连续检测到11个隐性电平后(总线空闲)清零INAK标志并进入Normal
在CAN中使能 TME中断、LEC中断、BOFF中断、ERRI中断;在NVIC中使能 CAN1_TX中断、CAN1_SCE中断,且两个中断的优先级相同
timer_set_timeout(&g_busoff_timer, TIMEOUT_100_MS); //设置bus-off定时器超时时间
timer_set_call_back(&g_busoff_timer, bus_off_recover_timeout); //设置定时器回调函数
初始化发送相关链表;
}
2、接收多帧CAN报文
根据业务需要,选择使用若干个Filter bank。对这些Filter bank进行配置后,从CAN总线上收到的报文会经过这些 Filter bank的过滤,过滤通过后报文会保存到对应的FIFO,同时FIFO状态会自动更新(已缓存的报文数量、已满标志、溢出标志),FIFO0中已缓存的报文数量 > 0 触发CAN1_RX0中断,FIFO1中已缓存的报文数量 > 0 触发CAN1_RX1中断。在中断函数中读出FIFO中的报文并保存到接收缓存中。
PS:
①、CAN1有FIFO0、FIFO1,每个FIFO最多缓存3个报文,FIFO0在NVIC的中断为 CAN1_RX0,FIFO1在NVIC的中断为CAN1_RX1,每个Filter bank都可以由软件指定 是绑定到 FIFO0 还是 FIFO1。
②、FIFO0虽然可缓存3个报文,但是只有一个RX Mailbox0,且它存放的是FIFO0中最老的那个报文,软件只能通过读这个RX Mailbox0来访问FIFO0缓存的那3个报文,方法是:软件读完一次RX Mailbox0后,置位 RFOM0标志让FIFO0的RX Mailbox 自动切换到下一个报文,然后软件再读RX Mailbox读到的就是FIFO0的下一个报文。FIFO1的情况和FIFO0类似。
struct can_msg
{
uint32_t id : 29; //can idenifier
uint32_t ide : 1; //标准帧 or 扩展帧
uint32_t rtr: 1; //数据帧 or 远程帧
uint32_t rsv0 : 1;
uint8_t data_len : 4; //数据长度
uint8_t rsv1 : 4;
uint8_t data[8]; //数据内容
uint8_t filter_num; //报文是通过哪个filter收到的
};
struct can_msg_list
{
struct list_head list;
struct can_msg data;
};
/* 定义缓存来保存接收的CAN报文, 缓存使用双向环形链表,
空闲的缓存放在空闲链表中, 已保存报文的缓存放在工作链表中 */
static struct list_head g_freelist; //空闲链表
static struct list_head g_uselist; //工作链表
static struct can_msg_list g_can1_rx_node[100]; //静态开辟缓存空间
/* 函数功能:读 CAN1 接收的报文, 此接口供外部模块调用
返回值:TRUE:读成功, FALSE:读失败
*/
bool_t can1_read_message(struct can_msg *msg)
{
bool_t result = FALSE;
ASSERT(msg != NULL);
if(链表g_uselist 不为空)
{
interrupt_disable();
从链表g_uselist取出一个节点X, 把节点X的内容保存到msg指向的内存中, 然后将节点X插入链表g_freelist;
interrupt_enable();
result = TRUE;
}
return result;
}
void can1_rx0_irq_handler(void)
{
if(寄存器FMP0[1:0] > 0) //FIFO0中已缓存的报文数量 > 0
{
struct can_msg msg;
读RX Mailbox0的报文, 并将报文保存到 msg;
置位RFOM0标志; //让 RX Mailbox0 切换到 FIFO0缓存的下一个报文
if(链表g_freelist 不为空)
{
从链表g_freelist取出一个节点X, 把msg的报文保存到节点X, 然后将节点X插入链表g_uselist;
}
}
}
void can1_rx1_irq_handler(void)
{
if(寄存器FMP1[1:0] > 0) //FIFO1中已缓存的报文数量 > 0
{
struct can_msg msg;
读RX Mailbox1的报文, 并将报文保存到 msg;
置位RFOM1标志; //让 RX Mailbox1 切换到 FIFO1缓存的下一个报文
if(链表g_freelist 不为空)
{
从链表g_freelist取出一个节点X, 把msg的报文保存到节点X, 然后将节点X插入链表g_uselist;
}
}
}
/* 发送和接收完整的初始化 */
void can1_init(void)
{
清零SLEEP标志并置位INRQ标志, 然后等待 SLAK标志清零、INAK标志置位; //CAN复位后进入Sleep, 软件需先让CAN进入 Initialization后, 才能对CAN进行配置
初始化can1:
can1引脚:TX引脚, RX引脚;
不使用TEST功能:清零 SILM标志、LBKM标志;
禁能自动唤醒:清零 AWUM标志;
禁能bus-off自动管理:清零 ABOM标志;
设置波特率500K:APB1为36Mhz, 预分频系数为6, TS1_8TQ, TS2_3TQ,因此BRP[9:0] = 5, TS1[3:0] = 7, TS2[2:0] = 2;
配置重新同步的节拍个数2TQ, 故SJW[1:0] = 1; //设置波特率时, 相关参数的理论计算值需减1后再写入对应寄存器
使用自动重发:清零 NART标志
TX Mailbox优先级 by request order:置位 TXFP标志
设置CAN2的起始Filter bank:14 (复位值)
接收FIFO满后需上锁:置位 RFLM标志
配置Filter bank0:
进入Filter init状态:置位 FINIT标志
失活Filter bank0:清零 FACT0标志
Filter bank0 绑定到FIFO0:清零 FFA0标志
Filter bank0 为dual 16-bit scale:清零 FSC0标志
Filter bank0为 mask mode:清零 FBM0标志
第一个filter过滤的报文为:数据帧(RTR位须为0), 扩展帧(IDE位须为1), ID的 bit15须为0、bit28须为1
CAN_F0R1[31:24] = 0b1000 0000, CAN_F0R1[ 7: 0] = 0b0000 1000
CAN_F0R1[31:24] = 0b1000 0000, CAN_F0R1[23:16] = 0b0001 1001
第二个filter过滤的报文为:数据帧(RTR位须为0), 标准帧(IDE位须为0), ID的 bit0须为1、bit10须为1
CAN_F0R2[31:24] = 0b1000 0000, CAN_F0R2[ 7: 0] = 0b0010 0000
CAN_F0R2[31:24] = 0b1000 0000, CAN_F0R2[23:16] = 0b0011 1000
退出Filter init状态:清零 FINIT标志
激活Filter bank0:置位 FACT0标志
清零INRQ标志, 然后等待INAK标志清零; //让CAN进入Normal,CAN连续检测到11个隐性电平后(总线空闲)清零INAK标志并进入Normal
在CAN中使能 TME中断、LEC中断、BOFF中断、ERRI中断;在NVIC中使能 CAN1_TX中断、CAN1_SCE中断,且两个中断的优先级相同
在CAN中使能 FMP0、FMP1中断; 在NVIC中使能 CAN1_RX0中断、CAN1_RX1中断,且两个中断的优先级相同
timer_set_timeout(&g_busoff_timer, TIMEOUT_100_MS); //设置bus-off定时器超时时间
timer_set_call_back(&g_busoff_timer, bus_off_recover_timeout); //设置定时器回调函数
初始化发送、接收的相关链表;
}
三、注意事项
1、发送失败时的两种情况:
① 禁能自动重发:TX Mailbox_X 发送失败时,CAN1_SCE中断、CAN1_TX中断会同时触发,若在CAN1_SCE中断函数中清零了 RQCP_X标志,则在CAN1_TX中断函数中发现 RQCP_X不为1,不会做任何动作。
② 使能自动重发:TX Mailbox_X 发送失败时,不触发CAN1_TX中断(因为RQCP_X标志不置位),只触发CAN1_SCE中断。
2、当CAN未进入 Normal模式时,无法发送报文;当CAN处于 bus-off 状态时,无法发送报文。
3、当CAN进入bus-off后,有两种恢复方式:
A、使能bus-off自动管理,此时CAN会立刻检测恢复条件是否满足(连续128次检测到11位隐性电平)。这种方式的优点是无需软件操心,缺点是恢复时间太快容易对总线造成干扰。
B、禁能bus-off自动管理,此时软件需设置一个定时器,当定时器超时后,再让CAN去检测恢复条件是否满足(让CAN从Nomal进入Initialization,然后再从Initialization进入Normal)。这种方式的优点是软件可以控制恢复时间,避免恢复太快的问题,缺点是需要软件做额外的工作。
4、使用CAN1_SCE中断时,CAN1_SCE中断、CAN1_TX中断的优先级要相同,因为这两个中断可能同时触发,需要避免抢占的情况出现。CAN1_SCE中断的作用:
① CAN休眠、唤醒时通知软件
① 帮助软件统计CAN各种错误出现的次数,评估CAN通信的稳定性
③ 及时通知软件接收报文失败:LEC[2:0]为6时表示 CRC错误,而CRC错误必定是接收错误
④ 帮助软件优化发送策略:
A、当使能了自动重发功能后,如果因为总线上没有挂 接收节点 而导致 TX Mailbox_X 发送失败,则软件可手动取消 TX Mailbox_X 的发送,这样 TX Mailbox_X 就可以腾出来给后面需要发送的报文使用。
B、当CAN进入 bus-off 状态后,CAN退出 bus-off 的时间是不确定的(使用bus-off自动管理时会立刻检测恢复条件,使用软件定时器时当软件定时超时后才检测恢复条件, 恢复条件为连续128次检测到11个隐性位),如果有发送缓存且缓存中还有待发送的报文,则软件可清空 缓存中待发送的报文,并手动取消状态不为空闲的 TX Mailbox。
5、发送TX Mailbox_X失败的原因,可能是数据传输过程中发生了问题(此时可通过 LEC[2:0]查看具体原因),但也有可能根本就没传输而是软件取消了TX Mailbox_X的发送(软件置位了 ABRQ_X)
6、当两节点同时发送ID相同数据不同的报文时,将发生数据场填充错误;当两节点同时发送ID相同数据也相同的报文时,若有其他节点应答则不发生错误,若无其他节点应答则发生应答错误。因此,在设计CAN应用层协议时应避免ID段相同的情况出现。
7、对于有两个CAN的STM32芯片,从整体功能上讲,两个CAN是独立的,但是因为CAN1、CAN2使用的SRAM由CAN1统一进行管理,所以即使只是单独使用CAN2进行收发,也须开启CAN1的时钟。
8、软件清零INRQ标志请求CAN从Initialization 进入 Normal时,CAN需要检测到总线空闲(连续检测到11个隐性位),才会正式进入Normal(INAK标志清零),此时要注意三点:一是软件等待CAN清零INAK标志的超时时间要设置好;二是确认RX引脚是否连接了CAN收发器,如果没连就需要把RX引脚设为上拉输入;三是总线需要留有一定空闲时间。
9、CAN 2.0B协议定义了五种通信错误:Stuff error(位填充错误),Bit error(隐性位 or 显性位错误),Form error(格式错误),ACK error(应答错误),CRC error。这些错误并不排斥,可能多个同时出现。针对这些错误有以下约定:
①、当发送节点或接收节点在检测到错误后,会主动发送 ERROR FLAG(错误帧):
“ACTIVE EEROR FLAG”:当节点状态为 error active 时(此时节点可信度较高), 发送6个连续的显性位(故意违反位填充约定,来破坏报文),可通知其他接收节点、发送节点,数据错了
“PASSIVE EEROR FLAG”:当节点状态为 error passive 时(此时节点可信度较低),发送6个连续的隐性位,此时不影响其他节点的正常收发
② 每个节点都有 Transmiter Error Counter(TEC)和 Receive Error Counter(REC)。节点发送失败则TEC增加,发送成功则TEC减小;节点接收失败则REC增加,接收成功则REC减小。当节点的 TEC > 255时,节点进入 bus-off 状态,此时不再参与总线的任何通信(即不能收发)。当节点的 TEC or REC > 127时,CAN进入 error passive状态,在此状态时,若CAN发送时检测到 ACK错误,则 TEC 不会增加(即当总线上只有一个节点A,则节点A在发送时会不断检测到发送失败,但是不会进入bus-off状态)。当节点处于 bus-off 状态时,若在总线上连续128次检测到11位隐性电平,则允许节点退出 bus-off 状态 进入 error active 状态,并清零 TEC 和 REC。
③ 发送节点检测的错误:
Stuff error:判断是否有其他节点发送了ACTIVE ERROR FLAG。(接收端发现错误后,会发送6个连续显性位(ACTIVE ERROR FLAG)通知发送端数据错了)
Bit error:判断发出电平的和读回的电平是否一致,仲裁期间不判断 Bit error
ACK error:判断是否有接收节点正确收到报文
Form error:判断在必须发送预定值的区域内是否有非法值:其他节点发的 错误帧界定符,过载帧界定符
④ 接收节点检测的错误:
Stuff error:判断发送端是否位填充失误,导致产生了6个连续的相同位;判断是否有其他节点发送了ACTIVE ERROR FLAG。
Bit error:接收端在接收时发现错误,主动发送6个连续显性位(ACTIVE ERROR FLAG)时,此时需判断6个连续显性位是否正确发出去了。
Form error:判断在必须发送预定值的区域内是否有非法值:数据帧和遥控帧的CRC界定符、ACK界定符、EOF,错误帧界定符,过载帧界定符。
CRC error:判断发送端的CRC是否正确。
10、CAN数据帧有标准帧和扩展帧
11、在配置CAN波特率时,建议把 bit sample point 控制在 75%左右
参考资料
[1] BOSCH CAN Specification Version 2.0:PART B
[2] STM32F10xxx reference manual.