CAN学习笔记

特点:

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
高速CAN20
低速CAN3-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问题及排查参考:

https://blog.youkuaiyun.com/qq_29788741/article/details/133967474?ops_request_misc=&request_id=&biz_id=102&utm_term=CAN%E9%80%9A%E8%AE%AF%E5%9C%A8%E5%AE%9E%E9%99%85%E4%BD%BF%E7%94%A8%E5%87%BA%E7%8E%B0%E7%9A%84%E9%97%AE%E9%A2%98%E5%92%8C%E6%8E%92%E6%9F%A5&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-2-133967474.142^v100^pc_search_result_base8&spm=1018.2226.3001.4187

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值