前面对CAN原理进行了讲解,这里我用一个简单的例子来说明CAN的使用。我使用的STM32芯片是STM32F103ZE,几个基本的配置是:

1.配置CAN时钟:

 /* CAN Periph clock enable */
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN, ENABLE);

2.CAN管脚配置,这里使用的PB11,PB12管脚:

/* Configure CAN pin: RX */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
  
 /* Configure CAN pin: TX */
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_Init(GPIOA, &GPIO_InitStructure);  

3.中断配置,这里打开CAN的接收和发送中断:

/* CAN RX interrupt */
NVIC_InitStructure.NVIC_IRQChannel=USB_LP_CAN_RX0_IRQChannel;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);     
    
 /* CAN TX interrupt */
 NVIC_InitStructure.NVIC_IRQChannel=USB_HP_CAN_TX_IRQChannel; 
 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 5;
 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
 NVIC_Init(&NVIC_InitStructure);   

这里需要注意的是CAN的发送中断,发送中断是发送邮箱中数据发送完毕后产生的发送完毕邮箱空中断--这和USART发送中断不同。详细点说CAN发送中断是指CAN的三个发送邮箱中的其中一个由满到发送数据完毕后空才产生的中断,当原来发送邮箱本来就是空的则不会产生发送中断。

4.CAN初始化配置:

void CAN_Ini(void)
{
  CAN_InitTypeDef        CAN_InitStructure;
  CAN_FilterInitTypeDef  CAN_FilterInitStructure;
  /* CAN register init */
  CAN_DeInit();
  CAN_StructInit(&CAN_InitStructure);

  /* CAN cell init */
  CAN_InitStructure.CAN_TTCM=DISABLE;  //禁止时间触发通信模式
  CAN_InitStructure.CAN_ABOM=DISABLE;  //软件对CAN_MCR寄存器的INRQ位进行置1随后清0后,一旦硬件检测
                                       //到128次11位连续的隐性位,就退出离线状态。
  CAN_InitStructure.CAN_AWUM=DISABLE;  //睡眠模式通过清除CAN_MCR寄存器的SLEEP位,由软件唤醒
  CAN_InitStructure.CAN_NART=DISABLE;   //CAN报文只被发送1次,不管发送的结果如何(成功、出错或仲裁丢失)
  CAN_InitStructure.CAN_RFLM=DISABLE;  //在接收溢出时FIFO未被锁定,当接收FIFO的报文未被读出,下一个收到的报文会覆盖原有的报文
  CAN_InitStructure.CAN_TXFP=ENABLE;  //用来使能或者失能发送FIFO优先级,由发送的请求顺序决定
 CAN_InitStructure.CAN_Mode=CAN_Mode_Normal;//CAN硬件工作在正常模式

 switch(CAN_BPP)
 {
    //总体配置保持,tBS1>=tBS2,tBS2>=1个CAN时钟周期,tBS2>=2tSJW
  case 100000:
  CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;   //重新同步跳跃宽度
  CAN_InitStructure.CAN_BS1=CAN_BS1_10tq;  //设定时间段1的时间单位数目
  CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;   //设定时间段2的时间单位数目
  CAN_InitStructure.CAN_Prescaler=20;         //一个时间单位的长度//(pclk1/((4+8+8)*9)) = 36Mhz/18/20 = 100Kbits
  break;
  case 50000:
  CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;
  CAN_InitStructure.CAN_BS1=CAN_BS1_10tq;
  CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;
  CAN_InitStructure.CAN_Prescaler=40;    //(pclk1/((4+8+8)*9)) = 36Mhz/18/40 = 50Kbits
  break;

  case 20000:
  CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;
  CAN_InitStructure.CAN_BS1=CAN_BS1_10tq;
  CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;
  CAN_InitStructure.CAN_Prescaler=100;    //(pclk1/((4+8+8)*9)) = 36Mhz/18/100 = 20Kbits
  break;
  case 250000:
  CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;
  CAN_InitStructure.CAN_BS1=CAN_BS1_8tq;
  CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;
  CAN_InitStructure.CAN_Prescaler=9;    //(pclk1/((1+8+7)*9)) = 36Mhz/16/9 = 250Kbits
  break;

  case 500000:
  CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;    //重新同步跳跃宽度1个时间单位
  CAN_InitStructure.CAN_BS1=CAN_BS1_6tq;    //时间段1为6个时间单位
  CAN_InitStructure.CAN_BS2=CAN_BS2_5tq;    //时间段2为5个时间单位
  CAN_InitStructure.CAN_Prescaler=6;        //(pclk1/((1+6+5)*6)) = 36Mhz/12/6 = 500Kbits设定了一个时间单位的长度9
  break;
  default:
  break;

 }

  CAN_Init(&CAN_InitStructure);

  /* CAN filter init */
  CAN_FilterInitStructure.CAN_FilterNumber=0;     //指定了待初始化的过滤器0
  CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;      //指定了过滤器将被初始化到的模式为标识符屏蔽位模式
  CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;  //给出了过滤器位宽1个32位过滤器
  CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;                  //用来设定过滤器标识符(32位位宽时为其高段位,16位位宽时为第一个)
  CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;                  //用来设定过滤器标识符(32位位宽时为其低段位,16位位宽时为第二个
  CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;              //用来设定过滤器屏蔽标识符或者过滤器标识符(32位位宽时为其高段位,16位位宽时为第一个
  CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;              //用来设定过滤器屏蔽标识符或者过滤器标识符(32位位宽时为其低段位,16位位宽时为第二个
    CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_IT_FMP0;  //设定了指向过滤器的FIFO0
    CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;              //使能过滤器
  CAN_FilterInit(&CAN_FilterInitStructure);

  /* CAN FIFO0 message pending interrupt enable */ 
  CAN_ClearITPendingBit(CAN_IT_FF0);
  CAN_ClearITPendingBit(CAN_IT_FOV0);
  CAN_ClearITPendingBit(CAN_IT_FF1);
  CAN_ClearITPendingBit(CAN_IT_FOV1);
  CAN_ClearITPendingBit(CAN_IT_RQCP0);
  CAN_ClearITPendingBit(CAN_IT_RQCP1);
  CAN_ClearITPendingBit(CAN_IT_RQCP2);
  CAN_ITConfig(CAN_IT_FMP0, ENABLE);  //使能接收中断
    //CAN_ITConfig(CAN_IT_TME, ENABLE); //TransmitMailbox empty,发送中断在CAN_Transmit()后才能打开,用来判断发送完毕发送下一个数据

}

5.实现CAN的发送中断和接收中断,这里我们只用了一个发送邮箱进行发送--简单方便:

void USB_LP_CAN_RX0_IRQHandler(void)  //接收中断
{
  CanRxMsg RxMessage;
  U8 i=0;
  U8 targetid=0;

  //如果是标准帧
    CAN_Receive(CAN_FIFO0, &RxMessage);
    if(RxMessage.IDE==CAN_ID_STD)//只收标准帧数据
    {
        if((RxMessage.StdId&0x1F)==PCL_ID+bmqnum)   //接收到ID是取后5位是自己的ID
        {
            //每个完整的数据,以|开头,下面是ID
            while(!QIn(&Qlinecanrx,'|')){}
            targetid=(RxMessage.StdId>>6)&0x1F;
            while(!QIn(&Qlinecanrx,targetid)){}
            for(i=0;i            {
                while(!QIn(&Qlinecanrx,RxMessage.Data[i])){}
            }
        }

    }
  CAN_ITConfig(CAN_IT_FMP0, ENABLE); //中断退出
}
void USB_HP_CAN_TX_IRQHandler(void)
{
    CAN_ClearITPendingBit(CAN_IT_RQCP0);
    CAN_ClearITPendingBit(CAN_IT_RQCP1);
    CAN_ClearITPendingBit(CAN_IT_RQCP2);
    CAN_ITConfig(CAN_IT_TME, DISABLE);
    CANSendStr();   //时刻查询是否可以给上位发送数据
}

void CANSendStr()
{
    CanTxMsg TxMessage;
    TxMessage.StdId=PCL_ID+bmqnum;  //用来设定标准标识符,0x08+2=0x0A
    TxMessage.ExtId=0;              //用来设定扩展标识符
  TxMessage.RTR=CAN_RTR_DATA;     //用来设定待传输消息的帧类型,它可以设置为数据帧或者远程帧,使用数据帧
  TxMessage.IDE=CAN_ID_STD;       //用来设定消息标识符的类型,使用标准标识符
  TxMessage.DLC = 8;              //用来设定待传输消息的帧长度,它的取值范围是0到0x8
    //20160822----zhang
    TxMessage.RTR=CAN_RTR_DATA;    //数据帧
    //20160822----zhang
    TxMessage.Data[0]='~';
    TxMessage.Data[1]='0';
    TxMessage.Data[2]='1';
    TxMessage.Data[3]='2';
    TxMessage.Data[4]='3';
    TxMessage.Data[5]='4';
    TxMessage.Data[6]='5';
    TxMessage.Data[7]='6';             
    CAN_Transmit(&TxMessage);
    CAN_ITConfig(CAN_IT_TME, ENABLE); //TransmitMailbox empty,发送中断在CAN_Transmit()后才能打开,用来判断发送完毕发送下一个数据 
}

6.使用中先使用CANSendStr()发送一条数据,则发送完毕后进入发送空中断,则之后会循环发送数据

参考文章:

http://www.openedv.com/thread-38867-1-1.html

http://www.stmcu.org/module/forum/thread-565605-1-1.html

http://www.cnblogs.com/chris-cp/p/3961508.html

http://www.openedv.com/thread-51889-1-1.html


关键字:STM32  CAN  接收和发送 引用地址:STM32--CAN简单接收和发送

上一篇:STM32的CAN总线学习总结
下一篇:STM32之CAN通信

推荐阅读

   今天早些时候,苹果发布了iOS 12的Beta 12测试版,而对应的公测版他们也已经发布了,如果你参与了公测计划,那么必须要升级了。  苹果发布的iOS 12 Beta 10公测版,解决了之前的一个大Bug,就是疯狂弹窗让你更新iOS 12的信息,这让不少参与公测的用户苦不堪言,临时解决的办法只能是把日期往前调。  现在在最新的公测版中,苹果终于解决了...
智能电视行业在 2019 年下半年异常热闹与拥挤,无论你是行业人士还是看客,应该都能很轻易得到这样的共识。 线上市场价格战正酣,大屏产品一次次刷新起售价的底线;华为荣耀携鸿蒙和智慧屏入局,并高调宣称要发布“电视的未来”;一加等品牌也开始涉足电视行业,更别说还有拼多多,京东…… 在开机率日益走低的今天,为什么电视市场还能吸引一大拨新玩家...
本篇学习目的:一、学会对STM32芯片GPIO的基本操作二、对GPIO的相关函数进行二次封装,以便于后期开发开发板GPIO原理图如下由图可知LED接在GPIOC的PC0-PC7STM的GPIO有如下8中模式GPIO相关库函数GPIO模式配置函数:GPIO_Init(GPIO_TypeDef *GPIOx,GPIO_InitTypeDef *GPIO_InitStruct);第一个参数用来指定GPIO口,取值范围GPIOA—GPIOG第二个参数用来初始...
  防爆电机型号字母的含义  1、Y-代表“异”步电机  2、B-代表隔“爆”  3、YB-代表隔爆型异步电机  4、A-代表增“安”型  5、YA-则代表增安型异步电机  6、F-代表“通风”、“粉尘”,如YBF代表风机用隔爆型异步电动机,  YFB代表粉尘防爆电机  7、S-代表“水”冷、“输”送机用电机。  如YBS代表输送机用隔爆型异步电动机,YBSS代表...

史海拾趣

问答坊 | AI 解惑

第七讲电源系统设计

本帖最后由 paulhyde 于 2014-9-15 09:38 编辑 …

查看全部问答∨

步进电机驱动器

千里之行,始于足下; 运动控制,步进开始。 该图是我新近开发的步进电机驱动器,输入电压单电源12-48VDC,额定输出电流3A, 最大细分数256,......本人还可以给您提供各种运动控制的解决方案。 若需详细资料,请联系:amcc_66@sina.com ...…

查看全部问答∨

一道模数综合题!

请教大侠答案是什么,怎么来的?…

查看全部问答∨

微弱电流测量及其干扰滤除的问题 已更新原理图

本人对电化学实验中的缓慢变化的直流进行测量,该直流变化范围在nA(甚至更小)到mA级别,采用电流跟随器转换成电压测量(采用OPA132或是CA3140或是OPA121或是OPA129),现在问题在于本人使用电阻R2、R4来模拟前端系统产生待测电流 i 进入电流跟随 ...…

查看全部问答∨

hive问题

内核在加载完boot.hv后是不是加载system.hv的注册表项。…

查看全部问答∨

NT还是WDM?

在XP下做一块简单的ISA的I/O板卡(不是即插即用)做设备驱动程序(用XP的DDK),是用NT还是用WDM?谢谢!…

查看全部问答∨

请教各位高手一个PCI的问题

我们知道,PCI设备有三个空间——内存地址空间、IO地址空间和配置空间。由于PCI支持即插即用,所以PCI设备不是占用固定的内存地址空间或I/O地址空间,而是可以由操作系统决定其映射的基址。怎么配置呢?这就是配置空间的作用。 DW | Byte3 | Byte2 ...…

查看全部问答∨

用89C51的GATE位测量脉冲宽度

待测信号由P3.2接入,待测信号脉宽取5ms~50ms,脉宽值显示在数码管(共阳数码管)上,显示单位为ms.求...<img border="0" src="file:///C:/Users/Acer/Desktop/QQ截图20120501230546.png"> [ 本帖最后由 寒雪剑91 于 2012-5-1 23:22 编辑 ...…

查看全部问答∨

我最近设计的LPC1700开发板

上图:                                                   [ 本帖最后由 zha ...…

查看全部问答∨

ucos 消息队列 取消息数据的问题

问题是将消息数据填入队列,经查询消息得出的结果也对,但是等待消息函数得到的结果却总是最后一条消息数据,很纳闷,不知怎么解? 请大侠们赐教 我的目标是假如我将消息数据取出来之后,可以通过对不同的数据做判断而进行不同的函数调用。例如: ...…

查看全部问答∨
小广播
设计资源 培训 开发板 精华推荐

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

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

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

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