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存放在剩下的高位中,低位还有IDE\RTR位标明了这一帧报文的属性
   // 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)
   {
      CAN1->RF0R |= 0x20; // 释放FIFO0邮箱
   }
   else if(mbox == 1)
   {
      CAN1->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);
}

猜你喜欢

转载自www.cnblogs.com/icode-wzc/p/12910703.html