zoukankan      html  css  js  c++  java
  • 重学STM32---(十)之CAN通信(二)

     
     
     

    前言

      CAN协议是非常难的,但是在stm32中却是简单的,只需要我们配置寄存器即可,,,即使这样,我在学习的时候也遇到了许多困难
     

    程序编写

    1、开时钟,不用说

    2、设置GPIO口,,CAN_TX复用推挽输出,CAN_RX上拉或浮空输入

    3、CAN初始化了,分三步

    • 一般设置
    	/*对CAN初始化*/
        CAN1->MCR |= 1 << 15;          //软复位 
        CAN1->MCR &= ~(1 << 1);    //退出睡眠模式  (郁闷,忘退出睡眠模式了,折腾了半天)
        CAN1->MCR |= 1 << 0;           //设置初始化模式 必须
        while(!(CAN1->MSR & 1 << 0));   //等待确认初始化
        
        CAN1->MCR |= 0 << 7;           //禁止时间触发模式
        CAN1->MCR |= 0 << 6;           //禁止自动离线
        CAN1->MCR |= 0 << 5;            //禁止自动唤醒
        CAN1->MCR |= 1 << 4;            //报文只传送一次,禁止自动重传
        CAN1->MCR |= 0 << 2;            //优先级由报文的标识符决定
    
    
    • 时序设置(时序也在初始化模式中)
    /* 设置CAN位时序*/
        CAN1->BTR &= 0x00000000;        //
        CAN1->BTR |= mode << 30;        //正常模式(0)或环回模式(1)可见CAN_BTR寄存器
        CAN1->BTR |= sjw << 24;            //设置sjw
        CAN1->BTR |= ts2 << 20;            //
        CAN1->BTR |= ts1 << 16;            //
        CAN1->BTR |= brp << 0;
    
        CAN1->MCR &= ~(1 << 0);            //退出初始化
        while(CAN1->MSR & 1 << 0);        //确认退出初始化
    
    • 滤波器设置
    /* 设置过滤器组 */
        
        CAN1->FMR |= 1 << 0;            //过滤器组设置在初始化模式(记得必须设置初始化模式)
        CAN1->FA1R &= ~(1 << 0);        //过滤器禁用
        CAN1->FM1R &= ~(1<< 0);   //过滤器组工作在标识符屏蔽位模式
        CAN1->FS1R |= 1<<0;         //过滤器组位宽为32位
        CAN1->FFA1R &= ~(1 << 0);  //关联到fifo0
        CAN1->sFilterRegister[0].FR1 = 0X00000000;     //全都为0 ,什么数据都接受
        CAN1->sFilterRegister[0].FR2 = 0X00000000;
        CAN1->FA1R |= 1 << 0;        //激活过滤器
        //退出滤波器初始化模式  (记得要退出,否则发送成功却接受不到,不要问我为什么这么清楚。。。)
        CAN1->FMR &= 0 << 0;         
    

    4、发送消息函数和得到发送状态函数
     见下写程序
      u8 CAN_TX_Msg(u8 ide, u32 id, u8 rtr, u8 len, u8 *data)
      u8 CAN_TX_Statt(u8 mbox)

    5、接收函数

    // 参数要用指针,开始我ide没用指针,在其他函数调用这个函数ide的值是不确定的
    void CAN_RX_Msg(u8 *ide, u32* id, u8* rtr, u8* len, u8 *data, u8 fifox)   
    {
        *ide = (CAN1->sFIFOMailBox[fifox].RIR & 1<<2)>>2;
        if(*ide == 0)
        {
            *id = CAN1->sFIFOMailBox[fifox].RIR >> 21;
        }
        else 
        {
            *id = CAN1->sFIFOMailBox[fifox].RIR >> 3;
        }
        *rtr = (CAN1->sFIFOMailBox[fifox].RIR & 1<<1)>>1;
        
        *len = CAN1->sFIFOMailBox[fifox].RDTR & 0x0f;
        
            //接收数据
        data[0]=CAN1->sFIFOMailBox[fifox].RDLR&0XFF;
        data[1]=(CAN1->sFIFOMailBox[fifox].RDLR>>8)&0XFF;
        data[2]=(CAN1->sFIFOMailBox[fifox].RDLR>>16)&0XFF;
        data[3]=(CAN1->sFIFOMailBox[fifox].RDLR>>24)&0XFF;    
        data[4]=CAN1->sFIFOMailBox[fifox].RDHR&0XFF;
        data[5]=(CAN1->sFIFOMailBox[fifox].RDHR>>8)&0XFF;
        data[6]=(CAN1->sFIFOMailBox[fifox].RDHR>>16)&0XFF;
        data[7]=(CAN1->sFIFOMailBox[fifox].RDHR>>24)&0XFF;
            
        if(fifox==0)CAN1->RF0R|=0X20;//释放FIFO0邮箱
        else if(fifox==1)CAN1->RF1R|=0X20;//释放FIFO1邮箱     
    
    }
    

    主代码

    只有一块开发板,只能测试回环模式

    main.c

    主函数
    u8 txBuff[8] ;
    int main()
    {
        u8 key,time = 0;
        u8 i;
        u8 data = 0;
        u8 reBuff[8];
        Stm32_Clock_Init(9);    //系统时钟设置
        uart_init(72,115200);    //串口初始化为115200
        delay_init(72);    //延时初始化 
        LED_Init();    //初始化与LED连接的硬件接口
        LCD_Init();
        KEY_Init();
        LCD_ShowString(10,10,100,16,16,"CAN回环模式实验");
        LCD_ShowString(10,30,100,16,16,"KEY1:发送数据");
        CAN_Init(1,8,9,4,1);
        while(1)
        {
            key = KEY_Scan();     //查询按键
            if(key == KEY1)    //按键1发送数据
            {
                for(i = 0; i < 8; i++)
                txBuff[i] = data++;
                CAN_Send_Msg((u8*)txBuff, 8);
                // delay_ms(10);
                if(CAN_Receive_Msg(reBuff))
                {
                    for(i = 0; i < 8; i++)
                    {
                        printf ("CAN发送数据为:%d
    
    ", reBuff[i]);
                        txBuff[i] = 0;
                        // LCD_ShowxNum(10 + i*10, 100,reBuff[i], 3, 16, 0x80);
                    }
                }
            }
    
           time++;
           delay_ms(100);
           if(time>10)
           {
               LED0 = !LED0 ;
               time = 0;
           }
        }    
    }
    

    can.c

    #include "can.h"
    //#include "stm32f10x.h"
    #include "sys.h"
    
    
    #define CAN_INT_ENABLE    0
    
    void CAN_Init(u8 sjw, u8 ts2, u8 ts1,u16 brp, u8 mode)
    {
        sjw -= 1;
        ts1 -= 1;
        ts2 -= 1;
        brp -= 1;
        
        /*开启CAN_TX与CAN_RX的时钟PB*/
        RCC->APB2ENR |= 1 << 3;                                               
        /*开启CAN时钟*/
        RCC->APB1ENR |= 1 << 25;
    
        /*设置PB11上拉输入,PB12推挽输出*/
        GPIOB->CRH &= ~(0xff << 12);
        GPIOB->CRH |= 0xB3 << 12;
        GPIOB->ODR |= 1<<11;
        
        /*对CAN初始化*/
        CAN1->MCR |= 1 << 15;          //软复位 
        CAN1->MCR &= ~(1 << 1);
        CAN1->MCR |= 1 << 0;           //设置初始化模式
        while(!(CAN1->MSR & 1 << 0));   //等待确认初始化
        
        CAN1->MCR |= 0 << 7;           //禁止时间触发模式
        CAN1->MCR |= 0 << 6;           //禁止自动离线
        CAN1->MCR |= 0 << 5;            //禁止自动唤醒
        CAN1->MCR |= 1 << 4;            //报文只传送一次,禁止自动重传
        CAN1->MCR |= 0 << 2;            //优先级由报文的标识符决定
        /* 设置CAN位时序*/
        CAN1->BTR &= 0x00000000;        //
        CAN1->BTR |= mode << 30;        //正常模式或环回模式
        CAN1->BTR |= sjw << 24;            //设置sjw
        CAN1->BTR |= ts2 << 20;            //
        CAN1->BTR |= ts1 << 16;            //
        CAN1->BTR |= brp << 0;
    
        CAN1->MCR &= ~(1 << 0);            //退出初始化
        while(CAN1->MSR & 1 << 0);        //确认退出初始化
        
        /* 设置过滤器组 */
        
        CAN1->FMR |= 1 << 0;            //过滤器组设置在初始化模式
        CAN1->FA1R &= ~(1 << 0);        //过滤器禁用
        CAN1->FM1R &= ~(1<< 0);   //过滤器组工作在标识符屏蔽位模式
        CAN1->FS1R |= 1<<0;         //过滤器组位宽为32位
        CAN1->FFA1R &= ~(1 << 0);  //关联到fifo0
        CAN1->sFilterRegister[0].FR1 = 0X00000000;     //全都为0 ,什么数据都接受
        CAN1->sFilterRegister[0].FR2 = 0X00000000;
        CAN1->FA1R |= 1 << 0;        //激活过滤器
        CAN1->FMR &= 0 << 0;         //退出滤波器初始化模式
        
    #if CAN_INT_ENABLE
        CAN1->IER |= 1 << 1;   //FIFO0消息挂号中断使能
        MY_NVIC_Init(2,2,USB_LP_CAN1_RX0_IRQn,2);
    #endif
    
    }
    
    // ide 0:标准帧  1:扩展帧
    // id  标识id
    // rtr  0:数据帧  1:远程帧
    // len 数据长度
    // *data 指向要发送数据的指针
    u8 CAN_TX_Msg(u8 ide, u32 id, u8 rtr, u8 len, u8 *data)
    {
        u8 mbox;
        if(CAN1->TSR & 1<<26) mbox = 0;   //邮箱0为空
        if(CAN1->TSR & 1<<27) mbox = 1;
        if(CAN1->TSR & 1<<28) mbox = 2;
        else return 0xff;
        
        /*为标准id时*/
        if(ide == 0)
        {
            id = id & 0x07ff;          //取11位
            id <<= 21;
        }
        /*为扩展ID时*/
        if(ide == 1)
        {
            id <<= 3;
        }
        CAN1->sTxMailBox[mbox].TIR = 0;
        CAN1->sTxMailBox[mbox].TIR |= ide << 2;        //  
        CAN1->sTxMailBox[mbox].TIR |= id ;             //
        CAN1->sTxMailBox[mbox].TIR &= ~(1<<1);         //
        CAN1->sTxMailBox[mbox].TDTR |= len&0xf;        //
        
        CAN1->sTxMailBox[mbox].TDLR =((u32)data[0]) | ((u32)data[1]<<8) | ((u32)data[2]<<16) | ((u32)data[3]<<24);
        CAN1->sTxMailBox[mbox].TDHR =((u32)data[4]) | ((u32)data[5]<<8) | ((u32)data[6]<<16) | ((u32)data[7]<<24);
        
        CAN1->sTxMailBox[mbox].TIR |= 1 << 0;
        return     mbox ;
    }
     
    //判断发送是成功
    //state = 7成功
    u8 CAN_TX_Statt(u8 mbox)
    {
        u8 state;
        switch(mbox)
        {
            case 0:
                state |= (CAN1->TSR & 1<<0) | (CAN1->TSR & 1<<1) |((CAN1->TSR & 1<<26)>>24);
                break ;
            case 1:
                state |= (CAN1->TSR & 1<<8)>>8;
                state |= (CAN1->TSR & 1<<9)>>9;
                state |= (CAN1->TSR & 1<<27)>>25;
                break ;
            case 2:
                state |= (CAN1->TSR & 1<<16)>>16;
                state |= (CAN1->TSR & 1<<17)>>17;
                state |= (CAN1->TSR & 1<<28)>>26;
                break ;
            default :
                state = 0x05;
        }
        return state ;
    
    }
    
    //得到在FIFO0/FIFO1中接收到的报文个数.
    //fifox:0/1.FIFO编号;
    //返回值:FIFO0/FIFO1中的报文个数.
    u8 CAN_Msg_Pend(u8 fifox)
    {
        if(fifox==0)return CAN1->RF0R&0x03; 
        else if(fifox==1)return CAN1->RF1R&0x03; 
        else return 0;
    }
    
    // 参数同上
    void CAN_RX_Msg(u8 *ide, u32* id, u8* rtr, u8* len, u8 *data, u8 fifox)
    {
        *ide = (CAN1->sFIFOMailBox[fifox].RIR & 1<<2)>>2;
        if(*ide == 0)
        {
            *id = CAN1->sFIFOMailBox[fifox].RIR >> 21;
        }
        else 
        {
            *id = CAN1->sFIFOMailBox[fifox].RIR >> 3;
        }
        *rtr = (CAN1->sFIFOMailBox[fifox].RIR & 1<<1)>>1;
        
        *len = CAN1->sFIFOMailBox[fifox].RDTR & 0x0f;
        
            //接收数据
        data[0]=CAN1->sFIFOMailBox[fifox].RDLR&0XFF;
        data[1]=(CAN1->sFIFOMailBox[fifox].RDLR>>8)&0XFF;
        data[2]=(CAN1->sFIFOMailBox[fifox].RDLR>>16)&0XFF;
        data[3]=(CAN1->sFIFOMailBox[fifox].RDLR>>24)&0XFF;    
        data[4]=CAN1->sFIFOMailBox[fifox].RDHR&0XFF;
        data[5]=(CAN1->sFIFOMailBox[fifox].RDHR>>8)&0XFF;
        data[6]=(CAN1->sFIFOMailBox[fifox].RDHR>>16)&0XFF;
        data[7]=(CAN1->sFIFOMailBox[fifox].RDHR>>24)&0XFF;    
          if(fifox==0)CAN1->RF0R|=0X20;//释放FIFO0邮箱
        else if(fifox==1)CAN1->RF1R|=0X20;//释放FIFO1邮箱     
    
    }
    
    //
    u8 CAN_Send_Msg(u8* data, u8 len)
    {
        u8 mbox;
        u16 i = 0;
        mbox = CAN_TX_Msg(0,0X12,0,len,data);
        while((CAN_TX_Statt(mbox) != 7) && (i < 0xfff)) i++;
        return 0;
    }
    
    //返回0 接收失败
    u8 CAN_Receive_Msg(u8* data)
    {
        u32 id;
        u8 ide = 0,rtr = 0,len = 0;
        while(!CAN_Msg_Pend(0));
        CAN_RX_Msg(&ide,&id,&rtr,&len,data,0);
        if( id!=0x12 || ide!=0 || rtr!=0) return 0;
        else return len;
    }
    

    测试

      已在本地测试OK。

     
     
     
     
     
     
     
    关注公众号"小败日记",搬砖过程遇到的问题,大家一起探讨,资源共享

    小败日记公众号

  • 相关阅读:
    运算符的一些运用规则
    “?:”练习(24小时计时转换12小时计时)
    if条件语句练习(相亲)
    练习
    理解PHP 依赖注入|Laravel IoC容器
    yiibooster+bsie
    PHP dirname() 函数 __FILE__ __DIR__
    per-project basis
    Setting composer minimum stability for your application
    修改mysql的root密码
  • 原文地址:https://www.cnblogs.com/qigaohua/p/11738303.html
Copyright © 2011-2022 走看看