特点:
1.传输方式:广播式和请求式
2.高速CAN 125-1Mbps 40m 低速CAN 10k-125kbps 1km
3.11/29帧ID 区分消息功能 同时决定优先级
4.半双工 异步 差分 多对多
5.高速和低速,主要区别是配置波特率不同,一般推荐250K,保证距离和传输速度
6.CAN最多挂载110个节点,但一般控制在30-50个
7.CAN使用位同步的方式来抗干扰、吸收误差,提高通讯能力
8.不支持使用DMA进行数据收发
9、三个发送邮箱,最多同时缓存三个报文
区别:
硬件电路:
CAN收发器作用:输出电平转换 输出驱动 输入采样 与单片机相连 无需交叉
高速CAN使用闭环网络,CAN_H 和 CAN_L两端并上120欧电阻
低速CAN使用开环网络,CAN_H和CAN_L其中一端加2.2k电阻
实际电路测试:
H和L两端电阻是否是60欧姆,L对地,2.4V左右,H对地在2.6V左右
CAN电平标准:
显性0电压差v | 隐性1电压差v | |
高速CAN | 2 | 0 |
低速CAN | 3 | -1.5 |
CAN的数据帧格式:
数据帧RTR位必须为显性电平,远程帧RTR位必须为隐形电平
CAN通讯相关配置
CAN_InitStructure.TTCM = DISABLE; //时间触发通信,无时间触发功能
CAN_InitStructure.ABOM = ENABLE; //自动总线管理,自动尝试连接总线,用于总线故障后的恢复
CAN_InitStructure.AWKUM = ENABLE;//总线唤醒,总线由活动时会从休眠中唤醒总线
CAN_InitStructure.NART = ENABLE;//启用禁用自动重发,enable表示禁止重新发送未成功发送的消息,避免总线阻塞
CAN_InitStructure.RFLM = DISABLE;//新的报文是否会覆盖旧的报文消息,适合实时更新数据的系统
CAN_InitStructure.TXFP = ENABLE;//优先级由发送请求的顺序决定 禁用表示按照标识符确定优先级
CAN_InitStructure.OperatingMode = OPERATINGMODE;//工作模式,正确
CAN_InitStructure.RSJW = CAN_BIT_RSJW;//时间再同步,调整收发产生的时间偏差,维持CAN同步
CAN_InitStructure.TBS1 = CAN_BIT_BS1;//
CAN_InitStructure.TBS2 = CAN_BIT_BS2;
CAN_InitStructure.BaudRatePrescaler = CAN_BAUDRATEPRESCALER;//波特率预分频器
/*Initializes the CAN */
CAN_Init(CAN1, &CAN_InitStructure);
CAN_Filter_Init();
CAN数据协议格式
软件代码中如何修改CAN采样率,提高通讯稳定性,抗干扰能力
裸机: CAN代码相关配置
#include <stdio.h>
#include "n32g45x.h"
#include "User_Can_Config.h"
rt_mailbox_t dynamic_mailbox;
#define POOL_SIZE 5
#define BLOCK_SIZE 64
/* 定义内存池控制块 用于CAN接收 */
static rt_mp_t mp = RT_NULL;
void init_memory_pool()
{
/* 创建一个静态内存池 */
mp = rt_mp_create("test_mp", POOL_SIZE, BLOCK_SIZE);
if (mp != RT_NULL)
{
rt_kprintf("finsh\n");
}
else
{
rt_kprintf("error\n");
}
}
/**
* @brief Configures CAN GPIOs
CAN过滤器相关配置
*/
void CAN_Filter_Init(void)
{
CAN_FilterInitType CAN_FilterInitStructure;
/* CAN filter init */
CAN_FilterInitStructure.Filter_Num = CAN_FILTERNUM0;
CAN_FilterInitStructure.Filter_Mode = CAN_Filter_IdMaskMode;
CAN_FilterInitStructure.Filter_Scale = CAN_Filter_32bitScale;
CAN_FilterInitStructure.Filter_HighId = 0;
CAN_FilterInitStructure.Filter_LowId = 0;
CAN_FilterInitStructure.FilterMask_HighId = CAN_STD_ID_H_MASK_DONT_CARE;
CAN_FilterInitStructure.FilterMask_LowId = CAN_STD_ID_H_MASK_DONT_CARE;
CAN_FilterInitStructure.Filter_FIFOAssignment = CAN_FIFO0;
CAN_FilterInitStructure.Filter_Act = ENABLE;
CAN1_InitFilter(&CAN_FilterInitStructure);
CAN_INTConfig(CAN1, CAN_INT_FMP0, ENABLE);
}
//CAN引脚等配置
void CAN1_Config(void)
{
dynamic_mailbox = rt_mb_create("mbx", 130, RT_IPC_FLAG_FIFO);
if (dynamic_mailbox == RT_NULL)
{
/* 邮箱创建失败处理 */
rt_kprintf("Failed to create dynamic mailbox.\n");
return;
}
init_memory_pool();
//
GPIO_InitType GPIO_InitStructure;
GPIO_InitStruct(&GPIO_InitStructure);
/* Configures CAN1 IOs */
RCC_EnableAPB2PeriphClk(RCC_APB2_PERIPH_AFIO | RCC_APB2_PERIPH_GPIOA, ENABLE);
/* Configure CAN1 RX pin */
GPIO_InitStructure.Pin = GPIO_PIN_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);
/* Configure CAN1 TX pin */
GPIO_InitStructure.Pin = GPIO_PIN_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitPeripheral(GPIOA, &GPIO_InitStructure);
// /* Remap CAN1 GPIOs */
// GPIO_ConfigPinRemap(GPIO_RMP1_CAN1, ENABLE);
CAN_InitType CAN_InitStructure;
/* Configure CAN */
RCC_EnableAPB1PeriphClk(RCC_APB1_PERIPH_CAN1, ENABLE);
/* CAN register init */
CAN_DeInit(CAN1);
/* Struct init*/
CAN_InitStruct(&CAN_InitStructure);
/* CAN cell init */
CAN_InitStructure.TTCM = DISABLE;
CAN_InitStructure.ABOM = ENABLE;
CAN_InitStructure.AWKUM = ENABLE;
CAN_InitStructure.NART = ENABLE;
CAN_InitStructure.RFLM = DISABLE;
CAN_InitStructure.TXFP = ENABLE;
CAN_InitStructure.OperatingMode = OPERATINGMODE;
CAN_InitStructure.RSJW = CAN_BIT_RSJW;
CAN_InitStructure.TBS1 = CAN_BIT_BS1;
CAN_InitStructure.TBS2 = CAN_BIT_BS2;
CAN_InitStructure.BaudRatePrescaler = CAN_BAUDRATEPRESCALER;
/*Initializes the CAN */
CAN_Init(CAN1, &CAN_InitStructure);
CAN_Filter_Init();
NVIC_InitType NVIC_InitStructure;
/* Configure the NVIC Preemption Priority Bits */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
/**
CAN发送函数
* @brief CAN Transmit Message.
* @param CAN
* @param TxMessage CAN_TxMessage
* @return The number of the mailbox that is used for transmission or CAN_TxSTS_NoMailBox if there is no empty mailbox.
*/
void CANTxMessage(CAN_Module *CANx, uint32_t id, uint8_t *data, uint8_t len)
{
int result = 0;
CanTxMessage CAN_TxMessage ;
result = (len + 8 - 1) / 8;
for (int J = 1; J <= result; J++)
{
CAN_TxMessage.IDE = CAN_Extended_Id;
CAN_TxMessage.RTR = RT_CAN_DTR;
CAN_TxMessage.DLC = (len > 8) ? 8 : len;;
CAN_TxMessage.ExtId = id;
if (len > 8)
len = len - 8;
for (uint8_t i = 0; i < CAN_TxMessage.DLC; i++)
{
CAN_TxMessage.Data[i] = data[i + (8 * (J - 1))];
}
}
uint8_t dealy = 0;
while (CAN_TransmitMessage(CANx, &CAN_TxMessage) == CAN_TxSTS_NoMailBox && dealy < 10)
{
dealy++;
};
}
//CAN中断处理函数
void USB_LP_CAN1_RX0_IRQHandler()
{
CanRxMessage *can_rxmessage = rt_mp_alloc(mp, RT_WAITING_NO);;
CAN_ReceiveMessage(CAN1, CAN_FIFO0, can_rxmessage);
if (can_rxmessage->IDE == CAN_Extended_Id)
{
if (can_rxmessage->RTR == RT_CAN_DTR)
{
rt_err_t result = rt_mb_send(dynamic_mailbox, (rt_uint32_t)can_rxmessage);
if (result != RT_EOK)
{
rt_mp_free(can_rxmessage);
rt_kprintf("error");
}
}
}else
{
rt_mp_free(can_rxmessage);
rt_kprintf("error");
}
}
应用部分:
void ProcessCAN1_RXTX()
{
CanRxMessage *canrxmes; //初始化CAN接收结构对象
struct rt_can_msg rxmsg = {0};
while(1)
{
CANTxMessage(CAN1, 0, slave_ctx->send_buf, send_len_can);//集中器先发送能够一个ID为0 的广播 设备地址为1 寄存器地址1 数量1
rt_thread_mdelay(25);
while (rt_mb_recv(dynamic_mailbox, (rt_ubase_t *) &canrxmes, 0) == RT_EOK)//接收CAN1 探测器发来 shdata数据包
{
if (canrxmes->DLC == 7)
{
rt_memcpy(slave_ctx->read_buf, canrxmes->Data, 7);
uint16_t save[2] = {0};
}
}
RT-thread中CAN代码配置:
设置CAN模式,波特率等信息
can_dev = rt_device_find("bxcan2");//上行
rt_device_open(can_dev, RT_DEVICE_FLAG_INT_TX | RT_DEVICE_FLAG_INT_RX);
rt_device_control(can_dev, RT_CAN_CMD_SET_BAUD, (void *)CAN250kBaud);//波特率250k
rt_device_control(can_dev, RT_CAN_CMD_SET_MODE, (void *) RT_CAN_MODE_NORMAL); //正常模式
void CAN2_RX_Process(void *parameter)
{
struct rt_can_msg rxmsg = {0};
agile_modbus_rtu_t rtuc;
agile_modbus_t *slave_ctxc = &rtuc._ctx;
uint8_t ctx_sdbufc[200];
uint8_t ctx_rxbufc[10];
agile_modbus_rtu_init(&rtuc, ctx_sdbufc, sizeof(ctx_sdbufc), ctx_rxbufc, sizeof(ctx_rxbufc));
struct rt_can_filter_item items[] =
{
RT_CAN_FILTER_EXT_INIT(0, RT_NULL, RT_NULL),//接收标准帧扩展帧所有数据
RT_CAN_FILTER_STD_INIT(0, RT_NULL, RT_NULL),
};
struct rt_can_filter_config cfg = {sizeof(items) / sizeof(struct rt_can_filter_item), 1, items}; /* 一共有 5 个过滤表 一个激活器 */
rt_device_control(can_dev, RT_CAN_CMD_SET_FILTER, &cfg);/* 设置硬件过滤表 */
while (1)
{
rxmsg.hdr = -1;
int read_can_len = rt_device_read(can_dev, 0, &rxmsg, sizeof(rxmsg));
if (read_can_len > 0)
{
if (rxmsg.ide == RT_CAN_STDID)
{
if (rxmsg.rtr == RT_CAN_DTR) //数据帧
{
if (rxmsg.id ==0x00)
{
uint8_t can_senbuf[2]= {addr_in << 8, addr_in&0xff};
sendCan_485Message(can_dev, 0x38, can_senbuf, sizeof(can_senbuf));//发送地址信息到CAN总线上
}
}
}
else if (rxmsg.ide == RT_CAN_EXTID) //扩展帧
{
if (rxmsg.rtr == RT_CAN_DTR) //数据帧
{
memcpy(slave_ctxc->read_buf, rxmsg.data, sizeof(rxmsg.data));
slave_ctxc->read_bufsz = 8;
agile_modbus_set_slave(slave_ctxc, addr_in);//设置地址
int rc = agile_modbus_slave_handle(slave_ctxc, 8, 1, slave_callback, NULL); //CAN2接收的数据 打包给4853去处理 然后通过CAN2回复相应 8字节的数据 1为单次处理
if (rc > 0)
{
//modbus指令得到正确处理
sendCan_485Message(can_dev, 0, slave_ctxc->send_buf, rc);
}
}
}
}
rt_thread_mdelay(5);
}
}
CAN相关问题及排查:
优先控制变量法,使用万用表,示波器,CAN盒
1、如果通讯不了,收发不到数据,先排查物理接线,再排查软件配置,如果和CAN盒通讯没问题,和设备通讯有问题,排查帧ID是否正确,过滤信息是否正确
2、如果CAN收发器不对,软件上CAN也发送不出去数据,不像USART串口 ,硬件问题
3、错误帧频繁出现:
4、如果出现使用CAN盒可以发送数据成功,但是软件仿真调试接上设备,CAN数据就是发送不出去,原因可能是设备没有接120欧电阻。
更多CAN问题及排查参考: