• STM32F407VET6 底层驱动之CAN总线寄存器封装


    注:在CAN驱动模块中仅CAN1调试完成,CAN2未调试

    CAN 通信距离和通信速度的关系如下:

    波特率/kbps    1000    500    250    125    100    50      20      10

    距 离/m              40     130    270    530    620   1300  3300  6700

    1、CAN总线封装接口如下:

      a、CAN端口初始化:unsigned int can_init(eCanType_t can, eGpioType_t txgpio, ePinType_t txpin, eGpioType_t rxgpio, ePinType_t rxpin, eCanBuadRateType_t baudrate, unsigned char prio)

      b、CAN端口过滤器ID设置:unsigned int can_filter_id_set(eCanType_t can, unsigned char filter_id)

      c、CAN1端口发送函数:unsigned int can1_write(const unsigned char *pbuf, unsigned int length, unsigned int extId)

      d、CAN2端口发送函数:unsigned int can2_write(const unsigned char *pbuf, unsigned int length, unsigned int extId)

      e、注册CAN接收回调函数:unsigned int can_isr_callback_add(eCanType_t can, unsigned int (*pfunc)(const unsigned char *pbuf, unsigned char length, unsigned int extId))

    2、CAN总线模块对外开放的枚举类型如下:

    typedef enum _eCanType
    {
       eCAN1,
       eCAN2,
       eCAN_COUNT, // 注:这不是CAN端口类型,不可删除,仅用做数量统计
    }eCanType_t;

    typedef enum _eCanBuadRateType
    {
       eCAN_BR_20K, // CAN建议最长通信距离3300米
       eCAN_BR_50K, // CAN建议最长通信距离1300米
       eCAN_BR_100K, // CAN建议最长通信距离620米
       eCAN_BR_125K, // CAN建议最长通信距离530米
       eCAN_BR_250K, // CAN建议最长通信距离270米
       eCAN_BR_500K, // CAN建议最长通信距离130米
       eCAN_BR_800K, // CAN建议最长通信距离70米
       eCAN_BR_1000K, // CAN建议最长通信距离40米
    }eCanBuadRateType_t;

    3、CAN总线模块代码实现如下:

    typedef unsigned int (*pCallBack_t)(const unsigned char *pbuf, unsigned char length, unsigned int extId);
    static pCallBack_t pCallBack[eCAN_COUNT] = {NULL}; // CAN中断回调函数
    /********************************************************
     * 函数功能:CAN中断优先级设置(组4),注意优先级不能超过设
                定的组的范围否则会有意想不到的错误。组划分如下:
           组0:0位抢占优先级,4位响应优先级
           组1:1位抢占优先级,3位响应优先级
           组2:2位抢占优先级,2位响应优先级
           组3:3位抢占优先级,1位响应优先级
           组4:4位抢占优先级,0位响应优先级
       抢占优先级和响应优先级的数值越小,优先级越高,处理器优
       先比较抢占优先级然后再看响应优先级。
     * 形    参:prio:抢占优先级(分组4的响应优先级固定为0)
        channel:中断通道
     * 返 回 值:无
     ********************************************************/
    static void can_nvic_set(unsigned char prio, unsigned char channel)
    {
    // unsigned int temp, temp1;
     
       // 设置分组
       unsigned char nvic_group = 4; // 0 - 4
    // temp1 = (~nvic_group) & 0x07; // 取后三位
    // temp1 = temp1 << 8;
    // temp = SCB->AIRCR;  // 读取先前的设置
    // temp &= 0x0000F8FF; // 清空先前分组
    // temp |= 0x05FA0000; // 写入钥匙
    // temp |= temp1;
    // SCB->AIRCR = temp; // 设置分组
       // 设置NVIC
       unsigned int temp;
       unsigned char sub_prio = 0; // 分组4的响应优先级固定为0
     
       // 注:优先级的设置必须要大于configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY
       // 否则不能在中断服务函数中调用FreeRTOS相关的API函数
       if(prio <= 5)
       {
          prio = 6;
       }
       else if(prio > 15)
       {
          prio = 15;
       }
     
       temp = prio << (4 - nvic_group);
       temp |= sub_prio & (0x0F >> nvic_group);
       temp = temp & 0xF; // 取低四位
       NVIC->ISER[channel >> 5] |= 1 << (channel % 32); // 使能中断位(要清除的话,设置ICER对应位为1即可)
       NVIC->IP[channel] |= temp << 4; // 设置响应优先级和抢断优先级
    }

    /********************************************************
     * 函数功能:CAN初始化
     * 形    参:can:CAN端口
                 txgpio:CAN TX 端口
                 txpin:CAN TX 引脚
                 rxgpio:CAN RX 端口
                 rxpin:CAN RX 引脚
                 baudrate:CAN波特率
                 prio:CAN中断优先级(数值越小优先级越高,范围:6 - 15)
     * 返 回 值:0=成功
                 1=无法识别的CAN端口
                 2=CAN进入硬件初始化模式失败
                 3=波特率错误
                 4=CAN退出硬件初始化模式失败
     ********************************************************/
    unsigned int can_init(eCanType_t can, eGpioType_t txgpio, ePinType_t txpin, eGpioType_t rxgpio, ePinType_t rxpin, eCanBuadRateType_t baudrate, unsigned char prio)
    {
       CAN_TypeDef *CANx = NULL;
       unsigned short timeout = 0;
       if(can != eCAN1 && can != eCAN2)
       {
          return 1; // 无法识别的CAN端口
       }
     
       // 找到CAN句柄
       CANx = (can == eCAN1)? CAN1 : CAN2;
       // CAN GPIO配置
       gpio_config(rxgpio, rxpin, eGPIO_AF_UP, 0);
       gpio_config(txgpio, txpin, eGPIO_AF_UP, 0);
     
       // 引脚复用映射配置
       gpio_af_config(txgpio, txpin, eGPIO_AF_CAN1); // CAN1/CAN2的复用是一样的,所以这里只用CAN1的就行
       gpio_af_config(rxgpio, rxpin, eGPIO_AF_CAN1); // CAN1/CAN2的复用是一样的,所以这里只用CAN1的就行
     
       // CAN时钟配置(42MHz)
       RCC->APB1ENR |= 0x1UL << (can + 25); // 初始化CAN时钟(使用CAN2时CAN1时钟也必须要初始化)
       RCC->APB1RSTR |= 0x1UL << (can + 25); // Enable CANx reset state
       RCC->APB1RSTR &= ~(0x1UL << (can + 25)); // Release CANx from reset state
     
       CANx->MCR &= ~(0x1UL << 1); // 退出睡眠模式
       CANx->MCR |= CAN_MCR_INRQ; // 请求进入硬件自动初始化模式
       while((CANx->MSR & CAN_MSR_INAK) != CAN_MSR_INAK)
       {
          timeout++;
          if(timeout > 0xFFF0)
          {
             return 2; // CAN进入硬件初始化模式失败
          }
       }
     
       // 配置CAN寄存器
       CANx->MCR |= 0x0UL << 7; // 关闭时间触发通信使能
       CANx->MCR |= 0x0UL << 6; // 自动离线管理,也就是如果检测到总线上出错,本节点自动离线
       CANx->MCR |= 0x0UL << 5; // 设置需要时自动唤醒,如果DISABLE,则需要软件唤醒
       CANx->MCR |= 0x1UL << 4; // 禁止报文自动传送
       CANx->MCR |= 0x0UL << 3; // 在接收的FIFO中如果溢出,自动覆盖原有报文
       CANx->MCR |= 0x0UL << 2; // 优先级由请求顺序确定(优先级由报文标识符决定)
     
       CANx->BTR = 0x00UL; // 清除原来的设置.
       CANx->BTR |= 0x0UL << 30; // 正常工作模式设置:0=普通模式,1=回环模式;
     
       // 配置CAN波特率
       switch(baudrate)
       {
          case eCAN_BR_20K:
             CANx->BTR |= 0x00UL << 24; // 重新同步跳跃宽度:0x00 - 0x03
             CANx->BTR |= 0x07UL << 16; // TBS1范围:0x00 - 0x0F
             CANx->BTR |= 0x00UL << 20; // TBS2范围:0x00 - 0x07
             CANx->BTR |= 208 - 1; // 分频系数
             break;
          case eCAN_BR_50K:
             CANx->BTR |= 0x00UL << 24; // 重新同步跳跃宽度:0x00 - 0x03
             CANx->BTR |= 0x07UL << 16; // TBS1范围:0x00 - 0x0F
             CANx->BTR |= 0x00UL << 20; // TBS2范围:0x00 - 0x07
             CANx->BTR |= 84 - 1; // 分频系数
             break;
          case eCAN_BR_100K:
             CANx->BTR |= 0x00UL << 24; // 重新同步跳跃宽度:0x00 - 0x03
             CANx->BTR |= 0x07UL << 16; // TBS1范围:0x00 - 0x0F
             CANx->BTR |= 0x00UL << 20; // TBS2范围:0x00 - 0x07
             CANx->BTR |= 42 - 1; // 分频系数
             break;
          case eCAN_BR_125K:
             CANx->BTR |= 0x00UL << 24; // 重新同步跳跃宽度:0x00 - 0x03
             CANx->BTR |= 0x0DUL << 16; // TBS1范围:0x00 - 0x0F
             CANx->BTR |= 0x00UL << 20; // TBS2范围:0x00 - 0x07
             CANx->BTR |= 21 - 1; // 分频系数
             break;
          case eCAN_BR_250K:
             CANx->BTR |= 0x00UL << 24; // 重新同步跳跃宽度:0x00 - 0x03
             CANx->BTR |= 0x0BUL << 16; // TBS1范围:0x00 - 0x0F
             CANx->BTR |= 0x00UL << 20; // TBS2范围:0x00 - 0x07
             CANx->BTR |= 12 - 1; // 分频系数
             break;
          case eCAN_BR_500K:
             CANx->BTR |= 0x00UL << 24; // 重新同步跳跃宽度:0x00 - 0x03
             CANx->BTR |= 0x04UL << 16; // TBS1范围:0x00 - 0x0F
             CANx->BTR |= 0x07UL << 20; // TBS2范围:0x00 - 0x07
             CANx->BTR |= 6 - 1; // 分频系数
             break;
          case eCAN_BR_800K:
             CANx->BTR |= 0x00UL << 24; // 重新同步跳跃宽度:0x00 - 0x03
             CANx->BTR |= 0x0AUL << 16; // TBS1范围:0x00 - 0x0F
             CANx->BTR |= 0x00UL << 20; // TBS2范围:0x00 - 0x07
             CANx->BTR |= 4 - 1; // 分频系数
             break;
          case eCAN_BR_1000K:
             CANx->BTR |= 0x00UL << 24; // 重新同步跳跃宽度:0x00 - 0x03
             CANx->BTR |= 0x04UL << 16; // TBS1范围:0x00 - 0x0F
             CANx->BTR |= 0x07UL << 20; // TBS2范围:0x00 - 0x07
             CANx->BTR |= 3 - 1; // 分频系数
             break;
          default: return 3; // 波特率错误
       }
     
       CANx->MCR &= ~(unsigned int)CAN_MCR_INRQ; // 请求退出初始化模式
       while((CANx->MSR & CAN_MSR_INAK) == CAN_MSR_INAK)
       {
          timeout++;
          if(timeout > 0xFFF0)
          {
             return 4; // CAN退出硬件初始化模式失败
          }
       }
     
       // 配置CAN中断优先级
       can_nvic_set(prio, (can == eCAN1)? CAN1_RX0_IRQn : CAN2_RX0_IRQn);
       return 0;
    }

    /********************************************************
     * 函数功能:CAN过滤器ID设置,与此ID相同的数据帧才会被接收
     * 形    参:can:CAN端口
                 filter_id:过滤ID
     * 返 回 值:0=成功
                 1=失败,28组过滤器使用完了
                 2=无法识别的CAN端口
     ********************************************************/
    unsigned int can_filter_id_set(eCanType_t can, unsigned char filter_id)
    {
       CAN_TypeDef *CANx = NULL;
       unsigned int filter_bit = 0;
       unsigned char can_filter_id = 0;
       static unsigned char can1_filter_id = 0;
       static unsigned char can2_filter_id = 0;
     
       // 找到CAN句柄
       if(can == eCAN1)
       {
          CANx = CAN1;
          can_filter_id = can1_filter_id++; // CAN1的滤波器:0 - 13
          if(can1_filter_id > 14)
          {
             return 1; // 14组过滤器使用完了
          }
       }
       else if(can == eCAN2)
       {
          CANx = CAN2;
          can_filter_id = 14 + can2_filter_id++; // CAN2的滤波器,:14 - 27
          if(can2_filter_id > 14)
          {
             return 1; // 14组过滤器使用完了
          }
       }
       else
       {
          return 2;
       }
     
       filter_bit = 0x1UL << can_filter_id;
       CANx->FMR |= 0x00000001UL; // 过滤器组工作在初始化模式
       CANx->FA1R &= ~filter_bit; // 过滤器暂不激活
       CANx->FM1R &= ~filter_bit; // 过滤器工作在标识符屏蔽位模式
       CANx->FS1R |= filter_bit; // 过滤器位宽为32位(16位bit需要清除对应位)
     
       // CAN过滤器有2个32位存储器,第一个用于存放32bit标识符,第二个用于存放掩码
       // 若掩码的某一bit为1,则收到数据的相应bit必须与标识符对应bit相同才会被接收
       // 扩展ID存放在3 - 17bit,标准ID存放在剩下的高位中,低位还有IDERTR位标明了这一帧报文的属性
       // IDE位标明是标准帧还是扩展帧,RTR位标明这一帧报文是数据帧还是远程遥控帧(用于请求数据)
       CANx->sFilterRegister[can_filter_id].FR1 = 0x00000000UL | (filter_id << 24); // 32位ID
     
       // ID左移三位后(左对齐),desID处于最高八个bit,所以此处只需最高八个bit一致就允许接收
       CANx->sFilterRegister[can_filter_id].FR2 = 0xFF000000UL; // 32位MASK
     
       CANx->FFA1R &= ~filter_bit; // 过滤器关联到FIFO0
      // CANx->FFA1R |= filter_bit; // 过滤器关联到FIFO1
       CANx->FA1R |= filter_bit; // 激活过滤器
       CANx->FMR &= ~0x0000001UL; // 过滤器组进入正常模式
     
       CANx->IER |= 0x02UL; // enable FIFO 0 message pending Interrupt
       can_filter_id++;
       return 0;
    }

    /********************************************************
     * 函数功能:CAN发送数据帧
     * 形    参:can:CAN端口
                 pbuf:数据指针
                 length:数据字节数
                 extId:扩展ID
     * 返 回 值:0=成功
                 1=发送失败(物理连接或初始化问题导致)
                 2=无空邮箱,发送失败
     ********************************************************/
    static unsigned int can_write_msg(eCanType_t can, const unsigned char *pbuf, unsigned int length, unsigned int extId)
    {
       unsigned int data = 0;
       unsigned int state = 0;
       unsigned char mbox = 0;
       unsigned short timeout = 0;
       CAN_TypeDef *CANx = NULL;
     
       // 找到CAN句柄
       CANx = (can == eCAN1)? CAN1 : CAN2;
     
       // 查找空邮箱
       if((CANx->TSR & (0x1UL << 26)) != 0)
       {
          mbox = 0; // 邮箱0为空
          state = CAN_TSR_RQCP0 | CAN_TSR_TXOK0 | CAN_TSR_TME0;
       }
       else if((CANx->TSR & (0x1UL << 27)) != 0)
       {
          mbox = 1; // 邮箱1为空
          state = CAN_TSR_RQCP1 | CAN_TSR_TXOK1 | CAN_TSR_TME1;
       }
       else if((CANx->TSR & (0x1UL << 28)) != 0)
       {
          mbox = 2; // 邮箱2为空
          state = CAN_TSR_RQCP2 | CAN_TSR_TXOK2 | CAN_TSR_TME2;
       }
       else
       {
          return 2; // 无空邮箱,发送失败
       }
     
       CANx->sTxMailBox[mbox].TIR = 0; // 清除之前的设置
      // extId = extId << 21; // 标准帧
       extId = extId << 3; // 扩展帧
     
       CANx->sTxMailBox[mbox].TIR |= extId;
       CANx->sTxMailBox[mbox].TIR |= 0x1UL << 2; // 0=标准帧,1=扩展帧
       CANx->sTxMailBox[mbox].TIR |= 0x0UL << 1; // 0=数据帧,1=远程帧
     
       CANx->sTxMailBox[mbox].TDTR &= ~(0x0000000F); // 先清除数据长度
       CANx->sTxMailBox[mbox].TDTR |= length & 0x0FUL; // 设置数据长度
     
       // 数据存入邮箱(小端模式)
       data = (data << 8) + pbuf[7];
       data = (data << 8) + pbuf[6];
       data = (data << 8) + pbuf[5];
       data = (data << 8) + pbuf[4];
       CANx->sTxMailBox[mbox].TDHR = data;
     
       data = (data << 8) + pbuf[3];
       data = (data << 8) + pbuf[2];
       data = (data << 8) + pbuf[1];
       data = (data << 8) + pbuf[0];
       CANx->sTxMailBox[mbox].TDLR = data;
     
       CANx->sTxMailBox[mbox].TIR |= 0x1UL << 0; //请求发送邮箱数据
       timeout = 0; // 超时变量清零
       do
       {
          // 获取CAN发送状态
          switch (CANx->TSR & state)
          {
             case 0: // CAN transmission pending
                break;
             case CAN_TSR_RQCP0 | CAN_TSR_TME0:
             case CAN_TSR_RQCP1 | CAN_TSR_TME1:
             case CAN_TSR_RQCP2 | CAN_TSR_TME2:
                break; // CAN transmission failed
             case CAN_TSR_RQCP0 | CAN_TSR_TXOK0 | CAN_TSR_TME0:
             case CAN_TSR_RQCP1 | CAN_TSR_TXOK1 | CAN_TSR_TME1:
             case CAN_TSR_RQCP2 | CAN_TSR_TXOK2 | CAN_TSR_TME2:
                return 0; // CAN transmission succeeded
             default: break; // CAN transmission failed
          }
       }while(timeout++ < 0xFFF);
       return 1; // 发送失败
    }

    /********************************************************
     * 函数功能:CAN1端口发送数据帧
     * 形    参:pbuf:数据指针
                 length:数据字节数
                 extId:扩展ID
     * 返 回 值:0=成功
                 1=发送失败(物理连接或初始化问题导致)
                 2=无空邮箱,发送失败
     ********************************************************/
    unsigned int can1_write(const unsigned char *pbuf, unsigned int length, unsigned int extId)
    {
       return can_write_msg(eCAN1, pbuf, length, extId);
    }
    /********************************************************
     * 函数功能:CAN2端口发送数据帧
     * 形    参:pbuf:数据指针
                 length:数据字节数
                 extId:扩展ID
     * 返 回 值:0=成功
                 1=发送失败(物理连接或初始化问题导致)
                 2=无空邮箱,发送失败
     ********************************************************/
    unsigned int can2_write(const unsigned char *pbuf, unsigned int length, unsigned int extId)
    {
       return can_write_msg(eCAN2, pbuf, length, extId);
    }

    /********************************************************
     * 函数功能:注册CAN中断接收回调函数
     * 形    参:can:指定CAN端口
                 pfunc:回调函数指针
     * 返 回 值:0=成功
                 1=CAN端口错误
                 2=函数指针为NULL
     * 开 发 者:王志超
     * 维护日期:2020年5月7日
     * 修订日志:开发
     ********************************************************/
    unsigned int can_isr_callback_add(eCanType_t can, unsigned int (*pfunc)(const unsigned char *pbuf, unsigned char length, unsigned int extId))
    {
       if(can >= eCAN_COUNT)
       {
          return 1;
       }
       if(pfunc == NULL)
       {
          return 2;
       }
       pCallBack[can] = pfunc;
       return 0;
    }


    /********************************************************
     * 函数功能:CAN中断回调函数
     * 形    参:can:指定CAN端口
     * 返 回 值:无
     ********************************************************/
    static void can_isr_callback(eCanType_t can)
    {
       unsigned int extId = 0;
       unsigned char mbox = 0; // 0=CAN_FIFO0, 1=CAN_FIFO1
       CAN_TypeDef *CANx = NULL;
     
       // 找到CAN句柄
       CANx = (can == eCAN1)? CAN1 : CAN2;
     
       // 获取标识符选择位的值
       extId = CANx->sFIFOMailBox[mbox].RIR & 0x04;
       if(extId == 0)
       {
          extId = CANx->sFIFOMailBox[mbox].RIR >> 21; // 标准标识符
       }
       else
       {
          extId = CANx->sFIFOMailBox[mbox].RIR >> 3; // 扩展标识符
       }
     
      // unsigned char rtr = CANx->sFIFOMailBox[mbox].RIR & 0x02; // 获取远程发送请求值
       unsigned char len = CANx->sFIFOMailBox[mbox].RDTR & 0x0F; // 获取数据长度
       unsigned char data[8] = {0};
     
       // 获取数据
       data[0] = (unsigned char)(CANx->sFIFOMailBox[mbox].RDLR >> 0);
       data[1] = (unsigned char)(CANx->sFIFOMailBox[mbox].RDLR >> 8);
       data[2] = (unsigned char)(CANx->sFIFOMailBox[mbox].RDLR >> 16);
       data[3] = (unsigned char)(CANx->sFIFOMailBox[mbox].RDLR >> 24);   
       data[4] = (unsigned char)(CANx->sFIFOMailBox[mbox].RDHR >> 0);
       data[5] = (unsigned char)(CANx->sFIFOMailBox[mbox].RDHR >> 8);
       data[6] = (unsigned char)(CANx->sFIFOMailBox[mbox].RDHR >> 16);
       data[7] = (unsigned char)(CANx->sFIFOMailBox[mbox].RDHR >> 24);
     
       // 释放邮箱
       if(mbox == 0)
       {
          CANx->RF0R |= 0x20; // 释放FIFO0邮箱
       }
       else if(mbox == 1)
       {
          CANx->RF1R |= 0x20; // 释放FIFO1邮箱
       }
     
       // 数据处理
       if(pCallBack[can] != NULL)
       {
          pCallBack[can](data, len, extId); // 执行用户数据处理函数
       }
    }

    /********************************************************
     * 函数功能:CAN1中断服务函数
     * 形    参:无
     * 返 回 值:无
     ********************************************************/
    void CAN1_RX0_IRQHandler(void)
    {
       can_isr_callback(eCAN1);
    }

    /********************************************************
     * 函数功能:CAN1中断服务函数
     * 形    参:无
     * 返 回 值:无
     ********************************************************/
    void CAN2_RX0_IRQHandler(void)
    {
       can_isr_callback(eCAN2);
    }

  • 相关阅读:
    rails的字符编码
    rails中ActionController::InvalidAuthenticityToken解决办法
    ruby on rails 实战(二)
    ruby on rails 实战(一)
    朴素贝叶斯趣味挑战项目
    python re的findall和finditer
    使用Naive Bayes从个人广告中获取区域倾向
    第五周(2.5~2.11)
    第四周(1.29~2.4)
    第三周(1.22~1.28)
  • 原文地址:https://www.cnblogs.com/icode-wzc/p/12910703.html
Copyright © 2020-2023  润新知