利用CAN进行简单的数据发送

发布者:lambda21最新更新时间:2021-08-19 来源: eefocus关键字:CAN  数据发送  控制器 手机看文章 扫描二维码
随时随地手机看文章

CAN是控制器局域网络(Controller Area Network, CAN)的简称,是国际上应用最广泛的现场总线之一。 在北美和西欧,CAN总线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线,并且拥有以CAN为底层协议专为大型货车和重工机械车辆设计的J1939协议。

在这里插入图片描述

本例通过用MC9S12XS128MAA来实现CAN标准帧的发送。


首先需要对CAN进行初始化配置,以下为本例所用到的寄存器介绍:

CANCTL0寄存器

在这里插入图片描述

RXFRM = 1 时,收到有效消息

= 0 时,未收到有效消息

RXACT = 1时,MSCAN正在接收消息

= 0时,MSCAN正在发送或空闲

CSWAI = 1 时,在等待模式下,模块停止计时

= 0时,等待模式期间模块不受影响

SYNCH = 1 时,MSCAN与CAN同步

= 0 时,MSCAN与CAN不同步

TIME = 1 时,启用内部 MSCAN计时器

= 0 时,禁用内部 MSCAN计时器

WUPE = 1 时,唤醒启用

= 0 时,唤醒禁用

SLPRQ = 1 时,CAN总线空闲时MSCAN进入休眠模式

= 0 时,MSCAN运行在正常模式

INITRQ = 1 时,MSCAN进入初始化模式

= 0 时,MSCAN运行在正常模式


CANCTL1寄存器

在这里插入图片描述

CANE = 1 时,MSCAN使能

= 0 时,MSCAN禁止

CLKSRC = 1 时,MSCAN时钟来源于总线

= 0 时,MSCAN时钟来源于晶振

LOOPB = 1 时,启用环回自检

= 0 时,禁用环回自检

LISTEN = 1 时,帧听模式开启

= 0 时,运行在正常模式

BORM = 1 时,根据用户请求总线断开恢复

= 0 时,自动总线断开恢复

MUPM = 1 时,只有当CAN总线上的主脉冲长度为t wup时,MSCAN才会唤醒

= 0 时,MSCAN任何时候都可唤醒

SLPAK = 1 时,睡眠模式激活

= 0 时,运行在正常模式

INITAK = 1 时,初始化模式激活

= 0 时,运行在正常模式


CANBTR0寄存器

在这里插入图片描述

SJW[1:0] :同步跳跃宽度

在这里插入图片描述

BRP[5:0] :波特率预分频

在这里插入图片描述

CANBTR1寄存器

在这里插入图片描述

SAMP = 1 时,每bit采样3次

= 0 时,每bit采样1次

TSEG2[2:0] :时间段2

在这里插入图片描述

TSEG1[3:0] :时间段1

在这里插入图片描述

通过以下公式计算bit time

在这里插入图片描述

CANIDMR0–CANIDMR7寄存器

在这里插入图片描述

AM[7:0]:接收屏蔽位

= 0 时,匹配接收码寄存器和标识符位

= 1 时,忽略相应接收码寄存器位


CANRIER寄存器

在这里插入图片描述

WUPIE = 1 时,唤醒事件导致唤醒中断请求

= 0 时,唤醒事件不生成中断请求

CSCIE = 1 时,CAN状态改变事件导致错误中断请求

= 0 时,CAN状态改变事件不生成中断请求

RSTATE[1:0] = 00 时,不生成由接收器状态变化引起的任何CSCIF中断

= 01 时,仅当接收器进入或离开“总线关闭”状态时生成CSCIF中断。丢弃其他接收器状态改变生成的CSCIF中断

= 10 时,只有当接收器进入或离开“RXERR”或“总线关闭”(2)状态时,才生成CSCIF中断。丢弃其他接收器状态改变生成的CSCIF中断

= 11 时,任何状态改变均生成CSCIF中断

TSTATE[1:0] = 00 时,不生成由发送器状态变化引起的任何CSCIF中断

= 01 时,仅当发送器进入或离开“总线关闭”状态时生成CSCIF中断。丢弃其他发送器状态改变生成的CSCIF中断

= 10 时,仅当发送器进入或离开“TXERR”或“总线关闭”状态时生成CSCIF中断。丢弃其他发送器状态改变生成的CSCIF中断

= 11 时,任何状态改变均生成CSCIF中断

OVRIE = 1 时,溢出事件导致错误中断请求

= 0 时,溢出事件不生成中断请求

RXFIE = 1 时,接收缓冲区满事件导致接收中断请求

= 0 时,接收缓冲区满事件不生成中断请求

CANTFLG寄存器

在这里插入图片描述

TXE[2:0] = 1 时,关联的消息缓冲区为空

= 0 时,关联的消息缓冲区已满

CANTBSEL寄存器

在这里插入图片描述

TX[2:0] = 1 时,如果最低编号置位,则选择相关的消息缓冲区

= 0 时,忽略选择关联的消息缓冲区

CANTXIDR:用来存放CAN报文的ID、远程帧或数据帧状态、标准帧或扩展帧

在这里插入图片描述
在这里插入图片描述

CANTXDSR:用来存放数据

在这里插入图片描述

CANTXDLR:用来存放数据长度

在这里插入图片描述
在这里插入图片描述

CANTXTBPR:优先级设置,具有最低本地优先级字段的传输缓冲区获得优先级


在这里插入图片描述

以下为完整的CAN发送报文程序(用到的寄存器意思见上)


#include            

#include "derivative.h"                

#define LED PORTB_PB0         //定义连接发光二级管的PORTB_PB0口数据寄存

                              //器为LED,写'0'亮,写'1' 灭

#define LED_dir DDRB_DDRB0    //定义连接发光二级管的PORTB_PB0口方向寄存器

                              //为LED_dir,写'0'做输入口,写'1'做输出口



#define ID                  0x0001   

#define ID1                  0x0002    //发送标识符ID号

#define data_len_TX          8           //发送数据长度


unsigned char a;


unsigned char senddata[8] = {'A','d','o','k','e','n','T','o'};

unsigned char senddata1[8] = {'r','o','t','h','y','N','B','!'};     //发送的数据 AdokenTorothyNB!


struct can_msg     //定义发送报文的结构体

{

    unsigned int id;   //ID号

    Bool RTR;         //远程帧或数据帧标志位

    unsigned char data[8];  //存放数据

    unsigned char len;      //数据长度

    unsigned char prty;     //优先级

};


struct can_msg msg_send, msg_send1;   //定义结构体变量




void INIT_PLL(void)       //初始化锁相环

{   

   

   CLKSEL_PLLSEL=0;     //内部总线时钟来源于晶振

           

   PLLCTL_PLLON=0;  //关闭PLL


 

   SYNR=0x40 | 0x03;

    

   REFDV=0x80 | 0x01; 

   

   POSTDIV=0x00;      //PLL为64MHz


   PLLCTL_PLLON=1;  //打开PLL


   _asm(nop);         

   _asm(nop);     //等待两个机器周期

   while(CRGFLG_LOCK==0); //根据CRGFLG寄存器的LOCK位,确定PLL是否稳定 LOCK==1 稳定,==0 不稳定  

   CLKSEL_PLLSEL =1;     //内部总线时钟选择PLL作为时钟源        

}



void INIT_CAN0(void)    //初始化CAN0

{

  if(CAN0CTL0_INITRQ==0)      // 查询是否进入初始化状态   

    CAN0CTL0_INITRQ =1;        // 进入初始化状态


  while (CAN0CTL1_INITAK==0);  //初始化握手标志


  CAN0BTR0_SJW = 0;            //设置同步

  CAN0BTR0_BRP = 7;            //设置波特率  

  CAN0BTR1 = 0x1c;       //设置时段1和时段2的Tq个数 ,总线频率为250kb/s

  /*

      Bit Time = [(Prescaler value)*(1+TimeSegment1+TimeSegment2)]/fCANCLK

      f = 1 / Bit Time

      Prescaler value = 8

      TimeSegment1 = 13

      TimeSegment2 = 2

      fcanclk = 32MHz

      f = 250kb/s

  */


                                  

  CAN0IDMR0 = 0xFF;

  CAN0IDMR1 = 0xFF;

  CAN0IDMR2 = 0xFF;

  CAN0IDMR3 = 0xFF;

  CAN0IDMR4 = 0xFF;

  CAN0IDMR5 = 0xFF;

  CAN0IDMR6 = 0xFF;

  CAN0IDMR7 = 0xFF;      // 关闭滤波器


  CAN0CTL1 = 0xC0;             //使能MSCAN模块,设置为一般运行模式、使用总线时钟源 


  CAN0CTL0 = 0x00;             //返回一般模式运行


  while(CAN0CTL1_INITAK);      //等待回到一般运行模式


  while(CAN0CTL0_SYNCH==0);    //等待总线时钟同步


  CAN0RIER_RXFIE = 0;          //禁止接收中断

}



Bool MSCAN0SendMsg(struct can_msg msg)     //CAN发送

{

  unsigned char send_buf, sp;

  

  

  if(msg.len > 8)

    return(FALSE);    // 检查数据长度


  

  if(CAN0CTL0_SYNCH==0)

    return(FALSE);        // 检查总线时钟是否同步


  send_buf = 0;

  do

  {

    

    CAN0TBSEL=CAN0TFLG;

    send_buf=CAN0TBSEL;

  }

  

  while(!send_buf);  // 寻找空闲的缓冲器

  

  

  CAN0TXIDR0 = (unsigned char)(msg.id>>3);

  CAN0TXIDR1 = (unsigned char)(msg.id<<5);   // 写入标识符

  

  if(msg.RTR)

    

    CAN0TXIDR1 |= 0x10;    //检测数据帧或标准帧

    

  

  for(sp = 0; sp < msg.len; sp++)

    *((&CAN0TXDSR0)+sp) = msg.data[sp];   // 写入数据

    

  

  CAN0TXDLR = msg.len;        // 写入数据长度

  

  

  CAN0TXTBPR = msg.prty;   // 写入优先级

  

  

  CAN0TFLG = send_buf;     // 清 TXx 标志 (缓冲器准备发送)

  

  return(TRUE);

  

}


void main(void) {

  DisableInterrupts;           //禁止打开所有中断

  INIT_PLL();                  //初始化PLL模块,设置busclock=32Mhz

  INIT_CAN0();                 //初始化can0模块

  LED_dir=1;                   //LED接口PB0设置为输出口 

  LED=0;                       //初始化LED初始状态为亮

  EnableInterrupts;            //允许打开所有中断  


//填写报文内容

  msg_send.id = ID;   //填写报头ID

  for(a=0;a  {

     msg_send.data[a] = senddata[a];

  }

  msg_send.len = data_len_TX;       //设置报文长度

  msg_send.RTR = FALSE;       //设置为标志帧

  msg_send.prty = 0;          //优先级设置为0

  

  msg_send1.id = ID1;   //填写报头ID

  for(a=0;a  {

     msg_send1.data[a] = senddata1[a];

  }

  msg_send1.len = data_len_TX;       //设置报文长度

  msg_send1.RTR = FALSE;     //设置为标志帧

  msg_send1.prty = 0;        //优先级设置为0


  for(;;) 

  {

      if(!MSCAN0SendMsg(msg_send)||!MSCAN0SendMsg(msg_send1)) //发送过程出现错误

      {

          for(;;);

      }

  } 

}


**注意:**ID号我们需要通过移位处理放入CANTXIDR寄存器相应的位置,另外还需将相应的RTR和IDE数据放入相应位置


本例我们通过用ZLG-USBCAN来查看CAN发送出来的数据,具体接线实物图如下所示

然后通过CANTest可以看到循环接收到的CAN数据,如下图所示(数据是ASCII码表示的,和我们程序中所要发送的两个数组是一致的)

关键字:CAN  数据发送  控制器 引用地址:利用CAN进行简单的数据发送

上一篇:利用CAN中断进行简单的数据接收
下一篇:S12X微处理器的XGATE协处理器使用指南

小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved