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)
   {
      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);
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM