STM32入门学习笔记之CAN通信实验4

电子说

1.2w人已加入

描述

18.3 实验例程

实验内容:利用CAN的回环模式进行数据收发测试,通信速率500Kbps。

(1)创建can.h文件,并输入以下代码。

/*********************************************************************************************************
            CAN    通    信    驱    动    文    件
*********************************************************************************************************/
#ifndef _CAN_H_
#define _CAN_H_


#include "sys.h"                
/*********************************************************************************************************
                  函    数    列    表
*********************************************************************************************************/
u8 CAN_Init( u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode ) ;                      //CAN初始化
u8 CAN_Tx_Msg( u32 id, u8 ide, u8 rtr, u8 len, u8 *dat ) ;                        //CAN发送数据
void CAN_Rx_Msg( u8 fifox, u32 *id, u8 *ide, u8 *rtr, u8 *len, u8 *dat ) ;                //CAN接收数据


#endif

(2)创建can.c文件,并输入以下代码。

/*********************************************************************************************************
            CAN    通    信    驱    动    程    序
*********************************************************************************************************/
#include "can.h"
/*******************************************************
Name    :CAN_Mode_Init
Function  :CAN初始化
Paramater  :
      tsjw:重新同步跳跃时间单元.范围:1~3
      tbs2:时间段2的时间单元.范围:1~8
      tbs1:时间段1的时间单元.范围:1~16
      brp:波特率分频器.范围:1~1024
      mode:工作模式
        0:普通模式
        1:回环模式
Return    :
      0:成功
      其他:失败
*******************************************************/
u8 CAN_Init( u8 tsjw, u8 tbs2, u8 tbs1, u16 brp, u8 mode )
{
  u16 i=0;
   if( ( tsjw==0 )||( tbs2==0 )||( tbs1==0 )||( brp==0 ) )
    return 1 ;
  //先减去1.再用于设置
  tsjw -= 1 ;
  tbs2 -= 1 ;
  tbs1 -= 1 ;
  brp -= 1 ;


  RCC->APB2ENR |= 1<<2 ;                                        //使能PA时钟
  GPIOA->CRH &= 0xFFF00FFF ;
  GPIOA->CRH |= 0x000B8000 ;
    GPIOA->ODR |= 3<<11 ;
  RCC->APB1ENR |= 1<<25 ;                                        //使能CAN时钟
  CAN1->MCR = 0x0000 ;                                        //退出睡眠模式
  CAN1->MCR |= 1<<0 ;                                          //请求CAN进入初始化模式
  while( ( CAN1->MSR&0x01 )==0 )
  {
    i ++ ;
    if( i>100 )
      return 2 ;                                          //进入初始化模式失败
  }
  CAN1->MCR |= 0<<7 ;                                          //非时间触发通信模式
  CAN1->MCR |= 0<<6 ;                                          //软件自动离线管理
  CAN1->MCR |= 0<<5 ;                                          //睡眠模式通过软件唤醒
  CAN1->MCR |= 1<<4 ;                                          //禁止报文自动传送
  CAN1->MCR |= 0<<3 ;                                          //报文不锁定,新的覆盖旧的
  CAN1->MCR |= 0<<2 ;                                          //优先级由报文标识符决定
  CAN1->BTR = 0x00000000 ;                                      //清除原来的设置
  CAN1->BTR |= mode<<30 ;                                        //模式设置
  CAN1->BTR |= tsjw<<24 ;                                        //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位
  CAN1->BTR |= tbs2<<20 ;                                        //Tbs2=tbs2+1个时间单位
  CAN1->BTR |= tbs1<<16 ;                                        //Tbs1=tbs1+1个时间单位
  CAN1->BTR |= brp<<0 ;                                        //分频系数(Fdiv)为brp+1
  CAN1->MCR &= ~( 1<<0 ) ;                                      //请求CAN退出初始化模式
  while( ( CAN1->MSR&0x01 )!=0 )
  {
    i ++ ;
    if( i>0xFFF0 )
      return 3 ;                                          //退出初始化模式失败
  }
  //过滤器初始化
  CAN1->FMR |= 1<<0 ;                                          //过滤器组工作在初始化模式
  CAN1->FA1R &= ~( 1<<0 ) ;                                      //过滤器0不激活
  CAN1->FS1R |= 1<<0 ;                                        //过滤器位宽为32位
  CAN1->FM1R |= 0<<0 ;                                        //过滤器0工作在标识符屏蔽位模式
  CAN1->FFA1R |= 0<<0 ;                                        //过滤器0关联到FIFO0
  CAN1->sFilterRegister[ 0 ].FR1 = 0x00000000 ;                            //32位ID
  CAN1->sFilterRegister[ 0 ].FR2 = 0x00000000 ;                            //32位MASK
  CAN1->FA1R |= 1<<0 ;                                        //激活过滤器0
  CAN1->FMR &= 0<<0 ;                                          //过滤器组进入正常模式
  return 0 ;
}
/*******************************************************
Name    :CAN_Tx_Msg
Function  :CAN发送数据
Paramater  :
      id:标准ID(11位)/扩展ID(11位+18位)
      ide:ID类型
        0:标准帧
        1:扩展帧
      rtr:数据类型
        0:数据帧
        1:远程帧
      len:要发送的数据长度
      *dat:数据指针
Return    :
      0~3:邮箱编号
      0xFF:无有效邮箱
*******************************************************/
u8 CAN_Tx_Msg( u32 id, u8 ide, u8 rtr, u8 len, u8 *dat )
{
  u16 i;
  u8 mbox, sta=0 ;
  //邮箱0为空
  if( CAN1->TSR&( 1<<26 ) )
    mbox = 0 ;
  //邮箱1为空
  else if( CAN1->TSR&( 1<<27 ) )
    mbox = 1 ;
  //邮箱2为空
  else if( CAN1->TSR&( 1<<28 ) )
    mbox = 2 ;
  //无空邮箱,无法发送
  else
    return 0xFF ;
  CAN1->sTxMailBox[ mbox ].TIR = 0 ;                                  //清除之前的设置
  //标准帧
  if( ide==0 )
  {
    id &= 0x7FF ;                                          //取低11位stdid
    id <<= 21 ;
  }
  //扩展帧
  else
  {
    id &= 0x1FFFFFFF ;                                        //取低32位extid
    id <<= 3 ;
  }
  CAN1->sTxMailBox[ mbox ].TIR |= id ;
  CAN1->sTxMailBox[ mbox ].TIR |= ide<<2 ;
  CAN1->sTxMailBox[ mbox ].TIR |= rtr<<1 ;
  len &= 0x0F ;                                            //得到低四位
  CAN1->sTxMailBox[ mbox ].TDTR &= 0xFFFFFFF0 ;
  CAN1->sTxMailBox[ mbox ].TDTR |= len ;                                //设置DLC
  //待发送数据存入邮箱
  CAN1->sTxMailBox[ mbox ].TDHR = ( ( (u32)dat[7]<<24 )|( (u32)dat[6]<<16 )|( (u32)dat[5]<<8 )|( (u32)dat[4] ) ) ;
  CAN1->sTxMailBox[ mbox ].TDLR = ( ( (u32)dat[3]<<24 )|( (u32)dat[2]<<16 )|( (u32)dat[1]<<8 )|( (u32)dat[0] ) ) ;
  CAN1->sTxMailBox[ mbox ].TIR |= 1<<0 ;                                //请求发送邮箱数据
  //获取发送状态
  while( ( sta!=0x07 )&&( i<0xFFF ) )
  {
    i ++ ;
    switch( mbox )
    {
      //邮箱0
      case 0:
        sta |= CAN1->TSR&( 1<<0 ) ;                                //RQCP0
        sta |= CAN1->TSR&( 1<<1 ) ;                                //TXOK0
        sta |= CAN1->TSR&( 1<<26 )>>24 ;                            //TME0
        break;
      //邮箱1
      case 1:
        sta |= CAN1->TSR&( 1<<8 )>>8 ;                              //RQCP1
        sta |= CAN1->TSR&( 1<<9 )>>8 ;                              //TXOK1
        sta |= CAN1->TSR&( 1<<27 )>>25 ;                            //TME1
        break;
      //邮箱2
      case 2:
        sta |= CAN1->TSR&( 1<<16 )>>16 ;                            //RQCP2
        sta |= CAN1->TSR&( 1<<17 )>>16 ;                            //TXOK2
        sta |= CAN1->TSR&( 1<<28 )>>26 ;                            //TME2
        break;
      //邮箱号不对
      default:
        sta = 0x05 ;
      break ;
    }
  }
  if( i==0xFFF )
    mbox = 0xFF ;
  return mbox ;
}
/*******************************************************
Name    :CAN_Rx_Msg
Function  :CAN接收数据
Paramater  :
      fifox:邮箱号
      id:标准ID(11位)/扩展ID(11位+18位)
      ide:ID类型
        0:标准帧
        1:扩展帧
      rtr:数据类型
        0:数据帧
        1:远程帧
      len:要发送的数据长度
      *dat:数据指针
Return    :None
*******************************************************/
void CAN_Rx_Msg( u8 fifox, u32 *id, u8 *ide, u8 *rtr, u8 *len, u8 *dat )
{
  if( ( fifox==0 )&&( ( CAN1->RF0R&0x03 )==0 ) )
    len = 0 ;
  else if( ( fifox==1 )&&( ( CAN1->RF1R&0x03 )==0 ) )
    len = 0 ;
  else
  {
    //接收数据
    *ide = CAN1->sFIFOMailBox[ fifox ].RIR&0x04 ;                          //得到标识符选择位的值
    if( *ide==0 )
      *id = CAN1->sFIFOMailBox[ fifox ].RIR>>21 ;                          //标准标识符
    else
      *id = CAN1->sFIFOMailBox[ fifox ].RIR>>3 ;                          //扩展标识符
    *rtr = CAN1->sFIFOMailBox[ fifox ].RIR&0x02 ;                          //得到远程发送请求值
    *len = CAN1->sFIFOMailBox[ fifox ].RDTR&0x0F ;                          //得到DLC
    dat[ 0 ] = CAN1->sFIFOMailBox[ fifox ].RDLR&0xFF ;
    dat[ 1 ] = ( CAN1->sFIFOMailBox[ fifox ].RDLR>>8 )&0xFF ;
    dat[ 2 ] = ( CAN1->sFIFOMailBox[ fifox ].RDLR>>16 )&0xFF ;
    dat[ 3 ] = ( CAN1->sFIFOMailBox[ fifox ].RDLR>>24 )&0xFF ;
    dat[ 4 ] = CAN1->sFIFOMailBox[ fifox ].RDHR&0xFF ;
    dat[ 5 ] = ( CAN1->sFIFOMailBox[ fifox ].RDHR>>8 )&0xFF ;
    dat[ 6 ] = ( CAN1->sFIFOMailBox[ fifox ].RDHR>>16 )&0xFF ;
    dat[ 7 ] = ( CAN1->sFIFOMailBox[ fifox ].RDHR>>24 )&0xFF ;
    if( fifox==0 )
      CAN1->RF0R |= 0x20 ;                                    //释放FIFO0邮箱
    else if( fifox==1 )
      CAN1->RF1R |= 0x20 ;                                    //释放FIFO1邮箱
  }
}

(3)创建1.c文件,并输入以下代码。

#include "sys.h"
#include "delay.h"
#include "usart1.h"
#include "can.h"


int main()
{
  u32 id;
  u8 ide, rtr, len, canbuf[ 8 ] ;
  STM32_Clock_Init( 9 ) ;                                        //STM32时钟初始化
  SysTick_Init( 72 ) ;                                        //SysTick初始化
  USART1_Init( 72, 115200 ) ;                                      //初始化串口1波特率115200
  CAN_Init( 1, 8, 9, 4, 1 ) ;                                      //CAN初始化
  CAN_Tx_Msg( 0x12, 0, 0, 8, "CAN Test" ) ;                              //发送8个字节
  while( 1 )
  {
    CAN_Rx_Msg( 0, &id, &ide, &rtr, &len, canbuf ) ;                        //读取数据
  }
}
打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分