CAN通讯中 BUS off 是怎么产生的
时间: 2024-04-11 21:31:11 浏览: 208
在CAN通信中,当一个CAN节点发生了一定数量的错误时,可能会导致该节点进入"BUS OFF"(总线关闭)状态。"BUS OFF"状态表示该节点无法正常参与总线通信。
以下是引起CAN节点进入"BUS OFF"状态的条件:
1. 错误计数器达到上限:CAN节点在发送或接收数据时,会对传输过程中出现的错误进行计数。当发送或接收错误达到一定数量(通常是128个)时,错误计数器会超过设定的阈值。这个阈值通常称为"错误报警限制"(Error Warning Limit)。达到错误报警限制后,节点进入"错误报警"状态。
2. 错误报警状态持续时间:当节点进入错误报警状态后,如果在持续时间内没有发生任何错误,则节点会恢复正常。但如果在持续时间内继续发生错误,则节点进入"BUS OFF"状态。
3. 错误恢复机制:当节点进入"BUS OFF"状态后,它会尝试自动恢复到正常状态。节点会等待一段时间(通常是128个时间单位)后,重新尝试参与总线通信。如果在这个时间内没有发生任何错误,则节点会恢复正常。
总结起来,当一个CAN节点连续发生一定数量的错误时,超过了错误报警限制,并且在错误报警状态持续时间内继续发生错误,该节点会进入"BUS OFF"状态。在"BUS OFF"状态下,节点无法正常参与总线通信,但会尝试自动恢复到正常状态。这样的设计可以保护整个CAN网络免受异常节点的影响,并确保通信的可靠性和稳定性。
相关问题
GD32F103VET6的CAN通讯代码
以下是GD32F103VET6的CAN通讯代码示例,包括CAN初始化、CAN发送和接收数据:
```c
#include "gd32f10x.h"
/* CAN初始化函数 */
void CAN_Config(void)
{
/* 使能CAN时钟 */
rcu_periph_clock_enable(RCU_CAN0);
/* 配置CAN GPIO */
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);
gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);
/* CAN单元初始化 */
can_deinit(CAN0);
can_struct_init(&can_initpara);
can_initpara.time_triggered_mode = DISABLE;
can_initpara.auto_bus_off_recovery = ENABLE;
can_initpara.auto_wake_up = DISABLE;
can_initpara.auto_retrans = ENABLE;
can_init(CAN0, &can_initpara);
/* 配置CAN过滤器 */
can_filter_initpara.filter_number = 0;
can_filter_initpara.filter_mode = CAN_FILTERMODE_MASK;
can_filter_initpara.filter_scale = CAN_FILTERSCALE_32BIT;
can_filter_initpara.filter_mask_id_high = 0x0000;
can_filter_initpara.filter_mask_id_low = 0x0000;
can_filter_initpara.filter_id_high = 0x0000;
can_filter_initpara.filter_id_low = 0x0000;
can_filter_initpara.filter_fifo_number = CAN_FIFO0;
can_filter_initpara.filter_enable = ENABLE;
can_filter_init(&can_filter_initpara);
/* 使能CAN中断 */
nvic_irq_enable(CAN0_RX0_IRQn, 0, 0);
can_interrupt_enable(CAN0, CAN_INTEN_RFNEIE0);
}
/* CAN发送数据函数 */
void CAN_SendData(uint8_t* data, uint8_t len)
{
/* 等待发送缓冲区为空 */
while(CAN0->TSR & CAN_TSR_TME0);
/* 设置发送数据长度 */
can_message_struct.length = len;
/* 填充发送数据 */
for(uint8_t i = 0; i < len; i++)
{
can_message_struct.data[i] = data[i];
}
/* 设置发送数据ID */
can_message_struct.format_type = CAN_STDID;
can_message_struct.id = 0x123;
/* 发送数据 */
can_message_transmit(CAN0, &can_message_struct);
/* 等待发送完成 */
while(!(CAN0->TSR & CAN_TSR_TME0));
}
/* CAN接收数据函数 */
void CAN_ReceiveData(uint8_t* data, uint8_t* len)
{
if(CAN0->RFIF & CAN_RFIF_FMP0)
{
/* 获取接收数据长度 */
*len = (uint8_t)(CAN0->sFIFOMailBox[0].RDTR & CAN_RDT0R_DLC);
/* 获取接收数据 */
for(uint8_t i = 0; i < *len; i++)
{
data[i] = (uint8_t)(CAN0->sFIFOMailBox[0].RDLR >> (i * 8));
}
/* 清除接收数据标志 */
CAN0->RFIF &= ~(CAN_RFIF_FMP0);
}
}
/* CAN接收中断服务函数 */
void CAN0_RX0_IRQHandler(void)
{
CAN_ReceiveData(recv_data, &recv_len);
}
```
以上代码仅供参考,具体实现需要根据实际情况进行修改。
STM32HAL库can通断通讯
CAN总线断电检测可以通过在CAN硬件上使用断电检测电路实现。在STM32上,可以使用HAL库函数来检测CAN总线的状态。以下是一个简单的示例代码,演示了如何使用STM32HAL库进行CAN通断通讯:
```c
#include "main.h"
#include "stm32f4xx_hal.h"
CAN_HandleTypeDef hcan1;
uint8_t can_tx_data[8] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF};
uint8_t can_rx_data[8] = {0};
int main(void) {
HAL_Init();
SystemClock_Config();
__HAL_RCC_CAN1_CLK_ENABLE();
hcan1.Instance = CAN1;
hcan1.Init.Mode = CAN_MODE_NORMAL;
hcan1.Init.AutoRetransmission = ENABLE;
hcan1.Init.AutoBusOff = ENABLE;
hcan1.Init.ReceiveFifoLocked = DISABLE;
hcan1.Init.TimeTriggeredMode = DISABLE;
hcan1.Init.TransmitFifoPriority = DISABLE;
hcan1.Init.SyncJumpWidth = CAN_SJW_1TQ;
hcan1.Init.TimeSeg1 = CAN_BS1_13TQ;
hcan1.Init.TimeSeg2 = CAN_BS2_2TQ;
hcan1.Init.Prescaler = 2;
HAL_CAN_Init(&hcan1);
CAN_FilterTypeDef can_filter;
can_filter.FilterMode = CAN_FILTERMODE_IDMASK;
can_filter.FilterIdHigh = 0x0000;
can_filter.FilterIdLow = 0x0000;
can_filter.FilterMaskIdHigh = 0x0000;
can_filter.FilterMaskIdLow = 0x0000;
can_filter.FilterFIFOAssignment = CAN_FILTER_FIFO0;
can_filter.FilterBank = 0;
can_filter.FilterScale = CAN_FILTERSCALE_32BIT;
can_filter.FilterActivation = ENABLE;
HAL_CAN_ConfigFilter(&hcan1, &can_filter);
HAL_CAN_Start(&hcan1);
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);
while (1) {
if (HAL_CAN_GetState(&hcan1) == HAL_CAN_STATE_BUS_OFF) {
// CAN bus is off
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
HAL_Delay(500);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
HAL_Delay(500);
} else {
// CAN bus is on
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
HAL_Delay(100);
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
HAL_Delay(100);
}
if (HAL_CAN_GetState(&hcan1) == HAL_CAN_STATE_READY) {
CAN_TxHeaderTypeDef can_tx_header;
can_tx_header.StdId = 0x123;
can_tx_header.RTR = CAN_RTR_DATA;
can_tx_header.IDE = CAN_ID_STD;
can_tx_header.DLC = 8;
uint32_t tx_mailbox;
HAL_CAN_AddTxMessage(&hcan1, &can_tx_header, can_tx_data, &tx_mailbox);
}
HAL_Delay(1000);
}
}
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan) {
CAN_RxHeaderTypeDef can_rx_header;
HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &can_rx_header, can_rx_data);
}
```
在这个示例代码中,我们使用了CAN1总线,并在主循环中轮询了CAN总线的状态。如果CAN总线处于“BUS_OFF”状态,我们将LED灯设置为常亮,否则将LED灯闪烁。如果CAN总线处于“READY”状态,我们将发送一个长度为8个字节的CAN消息。我们还注册了一个CAN接收中断回调函数,在接收到CAN消息时,将CAN接收缓冲区中的数据复制到can_rx_data数组中。
需要注意的是,实际应用中需要根据具体的需求进行修改和优化。
阅读全文