×
单片机 > 单片机程序设计 > 详情

基于STM32的CAN总线通信程序

发布时间:2020-09-03 发布时间:
|
#include

#include "sys.h"
#include "delay.h"
#include "CAN.h"
 CAN_msg     CAN_TxMsg;    //发送邮箱
 CAN_msg     CAN_RxMsg;    //接收邮箱
 u8 CAN_TxRdy=0;     //发送就绪标志
 u8 CAN_RxRdy=0;     //接收就绪标志
 u8 CAN_TDTxR_DLC;     //数据长度,最高为1111
/////////////////CAN设置/////////////////////////////////////
void CAN_setup(void)
{
  u8 brp=20;           //获取主时钟频率  APB1ENR时钟频率为36M
  RCC->APB1ENR|=1<<25;              //RCC_APB1ENR_CANEN; //开启CAN使能时钟
  RCC->APB2ENR|=1<<0;               //RCC_APB2ENR_AFIOEN; //开启辅助时钟
  AFIO->MAPR&=0XFFFF9FFF;   //清除复用重映射配置寄存器13,14位,
  AFIO->MAPR|=0X00004000;   //配置为10;IO口重映射至PB8.PB9
  RCC->APB2ENR|=1<<3;               //RCC_APB2ENR_IOPBEN; //开启IO口B时钟
  GPIOB->CRH&=~(0X0F<<0);   //清除PB8状态寄存器~(1111<<0)
  GPIOB->CRH|=(0X08<<0);   //设定pb8上下拉输入1000
  GPIOB->CRH&=~(0XF<<4);   //清空pb9状态寄存器
  GPIOB->CRH|=(0X0B<<4);   //设定PB9推挽输出
  MY_NVIC_Init(1,1,USB_HP_CAN_TX_IRQChannel,2);//发送中断使能
  MY_NVIC_Init(1,1,USB_LP_CAN_RX0_IRQChannel,2);//接收中断使能
  CAN->MCR|=1<<4;     //置NART位为1,禁止自动重传  403
  CAN->MCR|=1<<0;                   //置INRQ位为1,请求初始化    403 (CAN_MCR_NART|CAN_MCR_INRQ);//初始化模式,禁止重传
  CAN->IER|=1<<0;     //发送邮箱空中断允许   409
  CAN->IER|=1<<1;                   //FIFO0消息挂号中断允许      409 (CAN_IER_FMPIE0|CAN_IER_TMEIE);
  //brp=(brp/18)/500000;    //波特率设置500Kb/s,计算波特率分频器值
  CAN->BTR&=~(((0X03)<<24)|((0x07)<<20)|((0x0f)<<16)|(0x1ff)); //清空BTR寄存器相关位 410  400
  CAN->BTR|=(((1&0X03)<<24)|((7&0X07)<<20)|((8&0X0F)<<16)|(brp-1));//设置BTR寄存器  410 400
  
  
  //////////////从初始化模式进入正常工作模式/////////////////////////////////////////////
  void CAN_start(void)
  {
   CAN->MCR&=0xfffffffe;
   while(CAN->MSR&(0x01));
   CAN->MCR&=0xfffffffd;               //清零INRQ位,进入正常模式
   //while(~(CAN->MSR&0x01));               //等待硬件对INAK位清零,确认退出初始化
   }
   //////////////设置工作模式///////////////////////////////////////////
  void CAN_testmode(void)
  {
   CAN->BTR&=0x3fffffff;   //清空BTR 的SILM LBKM位 410
   CAN->BTR|=0XC0000000;//设置BTR 的LBKM位为高,环回模式  410
  
   }
  ///////////////检测发送邮箱为空////////////////////////////////////////////////
  void CAN_waitReady(void)
  {
   while((CAN->TSR&(1<<26))==0);    //检测发送邮箱0标志位,为1则为空,为0不为空
   CAN_TxRdy=1;         //置发送就绪标志位为1
  }
  ///////////////发送数据//////////////////////////////////////
  void CAN_wrMsg(CAN_msg *msg)
  {
   CAN->sTxMailBox[0].TIR=(u8)0;  //发送邮箱标示符寄存器复位 清零
   if(msg->format ==STANDARD_FORMAT)//如果是标准11位标示符帧
      {msg->id=33 ;
    CAN->sTxMailBox[0].TIR|=0x4200000;//(msg->id<<21)|CAN_ID_STD; //则标示符左移21位,高11位为标准标示符,标示符选择位置0  411
    }
   else          //如果是29位扩展标示符帧
      {
    CAN->sTxMailBox[0].TIR|=(u8)(msg->id<<3)|CAN_ID_EXT;  //标示符左移3位,高29位为标示符(扩展标示符) 标示符选择位置1  411
    }
   if(msg->type==DATA_FRAME)    //如果消息为数据帧
       {
     CAN->sTxMailBox[0].TIR|=CAN_RTR_DATA;//标示符寄存器RTR位置0,数据帧
       }
   else          //如果为远程帧
       {
     CAN->sTxMailBox[0].TIR|=CAN_RTR_REMOTE; //标示符寄存器RTR置1,远程帧
  }
   CAN->sTxMailBox[0].TDLR=(((u8)msg->data[3]<<24)|((u8)msg->data[2]<<16)|((u8)msg->data[1]<<8)|((u8)msg->data[0]));    //数据位低4字节写入发送邮箱0
                            
         
         
   CAN->sTxMailBox[0].TDHR=(((u8)msg->data[7]<<24)|((u8)msg->data[6]<<16)|((u8)msg->data[5]<<8)|((u8)msg->data[4]));    //数据位高4字节写入发送邮箱0
                            
         
         
   CAN->sTxMailBox[0].TDTR&=0xfffffff0;  //设置消息长度DLC清0
   CAN->sTxMailBox[0].TDTR|=0x00000004;  //设置消息长度为4个字节
   CAN->IER|=1<<0;                  //发送邮箱空中断使能 409
   CAN->sTxMailBox[0].TIR|=1<<0;                //发送消息 411
 
   }
   ///////////////读取邮箱数据并释放//////////////////////////////////////////////////////////////
  void CAN_rdMsg(CAN_msg *msg)
    {
  if((CAN->sFIFOMailBox[0].RIR&CAN_ID_EXT)==0) //如果是11位标准标识符
     {
   msg->format=STANDARD_FORMAT;     //消息为标准格式
   msg->id=(u32)0x000007ff&(CAN->sFIFOMailBox[0].RIR>>21); //标示符等于标示符位右移21位与0x7ff
         }
  else   //如果是29位扩展标识符
      {
   msg->format=EXTENDED_FORMAT;     //消息格式为扩展标示符格式
   msg->id=(u32)0x0003ffff&(CAN->sFIFOMailBox[0].RIR>>3); //标示符等于标示符位右移3位与上0x3ffff
         }
     if((CAN->sFIFOMailBox[0].RIR&CAN_RTR_REMOTE)==0) //如果消息为数据帧  
      {
    msg->type=DATA_FRAME;
   }
  else                                          //数据位远程帧
      {
    msg->type=REMOTE_FRAME;
   }
   msg->len = (unsigned char)0x0000000F & CAN->sFIFOMailBox[0].RDTR;  //读取数据长度
 
   msg->data[0] = (unsigned int)0x000000FF & (CAN->sFIFOMailBox[0].RDLR);    //读取数据低4字节
   msg->data[1] = (unsigned int)0x000000FF & (CAN->sFIFOMailBox[0].RDLR >> 8);
   msg->data[2] = (unsigned int)0x000000FF & (CAN->sFIFOMailBox[0].RDLR >> 16);
   msg->data[3] = (unsigned int)0x000000FF & (CAN->sFIFOMailBox[0].RDLR >> 24);
 
   msg->data[4] = (unsigned int)0x000000FF & (CAN->sFIFOMailBox[0].RDHR);   //读取数据高4字节
   msg->data[5] = (unsigned int)0x000000FF & (CAN->sFIFOMailBox[0].RDHR >> 8);
   msg->data[6] = (unsigned int)0x000000FF & (CAN->sFIFOMailBox[0].RDHR >> 16);
   msg->data[7] = (unsigned int)0x000000FF & (CAN->sFIFOMailBox[0].RDHR >> 24);

   CAN->RF0R |= 1<<5;                                   //释放接收邮箱0 407
 }
////////////////////////设置标示符过滤器组/////////////////////////////////////////////////////////////////////////////

  void CAN_wrFilter (unsigned int id, unsigned char format)  
{
  static unsigned short CAN_filterIdx = 0;   //标示符设置数量索引
         unsigned int   CAN_msgId     = 0;   //变量用于存储需要设定的标示符
  
   if (CAN_filterIdx > 13)        //检查CAN过滤器是否已满,最高到13
                          
      return;          //如果已满-返回
     }
   if (format == STANDARD_FORMAT)       //如果是标准帧,标准帧是11位ID(报文识别码)
                                           // Standard ID
      CAN_msgId  |= (unsigned int)(id << 21) | CAN_ID_STD;//标示符左移21位并添加帧识别码,得到32位的标示符字
     
  else                                        //如果是扩展帧,扩展帧是29位ID(报文识别码)
                                           // Extended ID
      CAN_msgId  |= (unsigned int)(id < 3) | CAN_ID_EXT;//标示符左移3位并添加帧识别码,得到32位的标示符字
  }
                                              // set Initialisation mode for filter banks
                                              //CAN过滤器组工作在初始化模式
  CAN->FMR  |=  1<<0;  
                                              // deactivate filter                  
  CAN->FA1R &=  ~(unsigned int)(1 << CAN_filterIdx);

                                              // initialize filter
                                              // set 32-bit scale configuration 
                                              //初始化过滤器
                                              //过滤器位宽为单个32位
  CAN->FS1R |= (unsigned int)(1 << CAN_filterIdx);
  // set 2 32-bit identifier list mode
  //过滤器组x的2个32位寄存器工作在标识符列表模式
  CAN->FM1R |= (unsigned int)(1 << CAN_filterIdx);
  //  32-bit identifier
  //32位标识符
  CAN->sFilterRegister[CAN_filterIdx].FR1 = CAN_msgId; 
  //  32-bit identifier
  //32位标识符
  CAN->sFilterRegister[CAN_filterIdx].FR2 = CAN_msgId; 
  // assign filter to FIFO 0
  //过滤器被关联到FIFO0
  CAN->FFA1R &= ~(unsigned int)(1 << CAN_filterIdx);  
  // activate filter
  //过滤器被激活
  CAN->FA1R  |=  (unsigned int)(1 << CAN_filterIdx);  
  // reset Initialisation mode for filter banks
  //CAN过滤器组工作在正常模式
  CAN->FMR &= ~(1<<0);                        // 该位置0为正常模式  415
  // increase filter index
  //设置的过滤器数量增加
  CAN_filterIdx += 1;
}

[page]
void USB_HP_CAN_TX_IRQHandler (void)
 {
                                    // request completed mbx 0
                                    //邮箱0请求完成  当发送完成后硬件对该位置1 407
 if (CAN->TSR & (1<<0))    //检测是否置1来判断发送已完成
 
                                   // reset request complete mbx 0
                                   //复位邮箱0请求,对该位写1可以清0该位    407
    CAN->TSR |= 1<<0;
                                    // disable  TME interrupt       
                                                       
    CAN->IER &= ~(1<<0);     //禁止发送邮箱空中断              
 
   CAN_TxRdy = 1; 
  }
      


void USB_LP_CAN_RX0_IRQHandler (void) {
                                      // message pending ?
                                      //接收到CAN报文,通过判断报文数目判断是否有报文
  if (CAN->RF0R & (1<<0)) 
        
                                   // read the message
                                   //接收报文
   CAN_rdMsg (&CAN_RxMsg);

    CAN_RxRdy = 1;                    // set receive flag
  }
}


////////////////////////CAN初始化////////////////////////////////////////////////////////////////////////
void can_Init (void) 
{

  CAN_setup ();                                   // setup CAN interface
  CAN_wrFilter (33, STANDARD_FORMAT);             // Enable reception of messages,filter set

 
  //CAN_testmode(CAN_BTR_SILM | CAN_BTR_LBKM);      // Loopback, Silent Mode (self-test)
  
  //软件仿真使用环回模式
  //CAN_testmode(CAN_BTR_SILM | CAN_BTR_LBKM);
  //硬件仿真使用正常模式
   CAN_testmode(); //正常模式
  // leave init mode
  //进入正常模式
   CAN_start ();
  // wait til mbx is empty
  //等待CAN就绪
   CAN_waitReady ();
}

以下是.H文件
#ifndef _CAN_H_
#define _CAN_H_

#define STANDARD_FORMAT  0
#define EXTENDED_FORMAT  1

#define DATA_FRAME       0
#define REMOTE_FRAME     1

#define CAN_ID_STD                 ((u32)0x00000000) 
#define CAN_ID_EXT                 ((u32)0x00000004) 


typedef struct  {
  unsigned int   id;                 // 29 bit identifier
  unsigned char  data[8];            // Data field
  unsigned char  len;                // Length of data field in bytes
  unsigned char  format;             // 0 - STANDARD, 1- EXTENDED IDENTIFIER
  unsigned char  type;               // 0 - DATA FRAME, 1 - REMOTE FRAME
} CAN_msg;


void CAN_setup         (void);
//void CAN_init          (void);
void CAN_start         (void);
void CAN_waitReady     (void);
void CAN_wrMsg         (CAN_msg *msg);
void CAN_rdMsg         (CAN_msg *msg);
void CAN_wrFilter      (unsigned int id, unsigned char filter_type);
void can_Init (void);
void CAN_testmode      (void);

extern CAN_msg       CAN_TxMsg;      // CAN messge for sending
extern CAN_msg       CAN_RxMsg;      // CAN message for receiving                                
extern u8  CAN_TxRdy;      // CAN HW ready to transmit a message
extern u8   CAN_RxRdy;      // CAN HW received a message

#endif // _CAN_H_

以下是主程序部分:
int main(void)      // 主函数,int类型,无参数
{
 u32 i=0;
 Stm32_Clock_Init(9);  //  调用系统时钟初始化子程序
 delay_init(72);   //  调用延时初始化子程序
 IOSET_init();    //  调用io口初始化子程序
 PWM_Init(9000,0);   //   定时器4初始化,设定PWM频率=72MHz/9000=8KH
 
  can_Init ();                                    // initialise CAN interface
 
                               // initialise message to send
  for (i = 0; i < 8; i++) CAN_TxMsg.data[i] = 0;
  
 
 
 
 
 
    
 while(1)
 {
       delay_us(160);


 if (CAN_TxRdy)
    {
       if(++i==600)
       {

   CAN_TxMsg.id = 33; 
   CAN_TxMsg.len = 4;
         CAN_TxMsg.format = STANDARD_FORMAT;//使用标准帧
         CAN_TxMsg.type = DATA_FRAME;//数据帧

 


          //i=0;
          //下面是数据报文
          CAN_TxMsg.data[0] = 1;
          CAN_TxMsg.data[1] = 2;
          CAN_TxMsg.data[2] =3;
          CAN_TxMsg.data[3] =4;
          
          //发送CAN报文
          CAN_TxRdy = 0;
          CAN_wrMsg (&CAN_TxMsg);
       }
       else if(i==1200)
       {
          i=0;
          //下面是数据报文
          CAN_TxMsg.data[0] = 0;
          CAN_TxMsg.data[1] = 1;
          CAN_TxMsg.data[2] = 1;
          CAN_TxMsg.data[3] = 1;
          
          //发送CAN报文
          CAN_TxRdy = 0;
          CAN_wrMsg (&CAN_TxMsg);
       }
       
    }
   

    if (CAN_RxRdy) 
    {
       CAN_RxRdy = 0;
       
       if(CAN_RxMsg.data[0]==1)////PC8状态
            LED1=0;
       else
          LED1=1;
       
       if(CAN_RxMsg.data[1]==2)////PC9状态
          LED2=0;
       else
          LED2=1;
       
       if(CAN_RxMsg.data[2]==3)////PC10状态
          LED3=0;
       else
          LED3=1;
       
       if(CAN_RxMsg.data[3]==4)////PC11状态
          LED4=0;
       else
          LED4=1;
       
    }

 }
 }

关键字:STM32 CAN总线 通信程序 

『本文转载自网络,版权归原作者所有,如有侵权请联系删除』

热门文章 更多
MSP430F5529 上手小例程2