STM32外设地图-CAN

一、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.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值