简介:

  CAN是Controller Area Network,是 ISO 国际标准化的串行通信协议。

  CAN  控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平和隐性电平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。

  STM32 自带的是 bxCAN,即基本扩展 CAN。它支持 CAN 协议 2.0A 和 2.0B。它的设计目标是,以最小的 CPU 负荷来高效处理大量收到的报文。它也支持报文发送的优先级要求(优先级特性可软件配置)。对于安全紧要的应用,bxCAN 提供所有支持时间触发通信模式所需的硬件功能。

  STM32 的 bxCAN 的主要特点有:

1.支持 CAN 协议 2.0A 和 2.0B 主动模式

2.波特率最高达 1Mbps

3.支持时间触发通信

4.具有 3 个发送邮箱

5.具有 3 级深度的 2 个接收 FIFO

6.可变的过滤器组(最多 28 个)

  STM32(互联型):带有两个CAN控制器;过滤器组最多有 28 个

STM32(增强型):STM32F103ZET6只有1个CAN控制器;过滤器组只有 14 个

  每个滤波器都是组x都是由2个32位寄存器,CAN_FxR1 和 CAN_FxR2 组成。 

  STM32 每个过滤器组的位宽都可以独立配置,以满足应用程序的不同需求。根据位宽的不同,每个过滤器组可提供:CAN 的初始化配置步骤,CAN 相关的固件库函数和定义分布在文件 stm32f10x_can.c 和头文件 stm32f10x_can.h 文件中。

1.配置相关引脚的复用功能,使能 CAN 时钟。

  使用CAN,第一步就要使能 CAN 的时钟。第二步要设置 CAN 的相关引脚为复用输出,这里我们需要设置 PA11 为上拉输入(CAN_RX 引脚)PA12 为复用输出(CAN_TX 引脚),并使能 PA 口的时钟。

  使能 CAN1 时钟的函数是:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能 CAN1 时钟
2.设置 CAN 工作模式及波特率等。

  这一步通过第一步设置 CAN_MCR 寄存器的 INRQ 位,让 CAN 进入初始化模式,然后设置CAN_MCR 的其他相关控制位。再通过 CAN_BTR 设置波特率和工作模式(正常模式/环回模式)等信息。  最后设置 INRQ 为 0,退出初始化模式。

  在库函数中,提供了函数 CAN_Init()用来初始化 CAN 的工作模式以及波特率,CAN_Init()函数体中,在初始化之前,会设置 CAN_MCR 寄存器的 INRQ 为 1 让其进入初始化模式,然后初始化 CAN_MCR 寄存器和 CRN_BTR 寄存器之后,会设置 CAN_MCR 寄存器的 INRQ 为 0让其退出初始化模式。所以我们在调用这个函数的前后不需要再进行初始化模式设置。

  下面我们来看看 CAN_Init()函数的定义:

uint8_t CAN_Init(CAN_TypeDef* CANx, CAN_InitTypeDef* CAN_InitStruct); 

  第一个参数是 CAN 标号,这里我们的芯片只有一个 CAN,所以就是 CAN1。

  第二个参数是 CAN 初始化结构体指针,结构体类型是 CAN_InitTypeDef,

  下面我们来看看这个结构体的定义:

typedef struct
{
uint16_t CAN_Prescaler;
uint8_t CAN_Mode;
uint8_t CAN_SJW;
uint8_t CAN_BS1;
uint8_t CAN_BS2; FunctionalState CAN_TTCM;
FunctionalState CAN_ABOM;
FunctionalState CAN_AWUM;
FunctionalState CAN_NART;
FunctionalState CAN_RFLM;
FunctionalState CAN_TXFP;
} CAN_InitTypeDef;

  这个结构体看起来成员变量比较多,实际上参数可以分为两类。

  前面 5 个参数是用来设置寄存器 CAN_BTR,用来设置模式以及波特率相关的参数,设置模式的参数是CAN_Mode, 我们实验中用到回环模式 CAN_Mode_LoopBack 和常规模式 CAN_Mode_Normal,大家还可以选择静默模式以及静默回环模式测试。其他设置波特率相关的参数 CAN_Prescaler,CAN_SJW,CAN_BS1 和 CAN_BS2 分别用来设置波特率分频器,重新同步跳跃宽度以及时间段 1 和时间段 2 占用的时间单元数。

  后面 6 个成员变量用来设置寄存器 CAN_MCR,也就是设置 CAN 通信相关的控制位。

初始化实例:

CAN_InitStructure.CAN_TTCM=DISABLE;      //非时间触发通信模式
CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理
CAN_InitStructure.CAN_AWUM=DISABLE; //睡眠模式通过软件唤醒
CAN_InitStructure.CAN_NART=ENABLE; //禁止报文自动传送
CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的
CAN_InitStructure.CAN_TXFP=DISABLE; //优先级由报文标识符决定
CAN_InitStructure.CAN_Mode= CAN_Mode_LoopBack; //模式设置: 1,回环模式; //设置波特率
CAN_InitStructure.CAN_SJW=CAN_SJW_1tq;//重新同步跳跃宽度为个时间单位
CAN_InitStructure.CAN_BS1=CAN_BS1_8tq; //时间段 1 占用 8 个时间单位
CAN_InitStructure.CAN_BS2=CAN_BS2_7tq;//时间段 2 占用 7 个时间单位
CAN_InitStructure.CAN_Prescaler=5; //分频系数(Fdiv) CAN_Init(CAN1, &CAN_InitStructure); // 初始化 CAN
3.设置滤波器 

  我们将使用滤波器组 0,并工作在 32 位标识符屏蔽位模式下。先设置 CAN_FMR的 FINIT 位,让过滤器组工作在初始化模式下,然后设置滤波器组 0 的工作模式以及标识符 ID和屏蔽位。最后激活滤波器,并退出滤波器初始化模式。

  在库函数中,提供了函数 CAN_FilterInit ()用来初始化 CAN 的滤波器相关参数, CAN_Init()函数体中,在初始化之前,会设置 CAN_FMR 寄存器的 INRQ 为 INIT 让其进入初始化模式,然后初始化 CAN 滤波器相关的寄存器之后,会设置 CAN_FMR 寄存器的 FINIT 为 0 让其退出初始化模式。所以我们在调用这个函数的前后不需要再进行初始化模式设置。

  下面我们来看看CAN_FilterInit ()函数的定义:

void CAN_FilterInit(CAN_FilterInitTypeDef* CAN_FilterInitStruct);

  这个函数只有一个入口参数就是CAN滤波器初始化结构体指针,结构体类型 为CAN_FilterInitTypeDef,

  下面我们看看类型定义:

ypedef struct
{
uint16_t CAN_FilterIdHigh;
uint16_t CAN_FilterIdLow;
uint16_t CAN_FilterMaskIdHigh;
uint16_t CAN_FilterMaskIdLow;
uint16_t CAN_FilterFIFOAssignment;
uint8_t CAN_FilterNumber;
uint8_t CAN_FilterMode;
uint8_t CAN_FilterScale;
FunctionalState CAN_FilterActivation;
} CAN_FilterInitTypeDef; 

  结构体一共有 9 个成员变量:

  第 1 个至第 4 个是用来设置过滤器的 32 位 id 以及 32 位 mask id,分别通过 2 个 16 位来组合的

  第 5 个成员变量 CAN_FilterFIFOAssignment 用来设置 FIFO 和过滤器的关联关系,我们的实验是关联的过滤器 0 到 FIFO0,值为 CAN_Filter_FIFO0。

  第 6 个成员变量 CAN_FilterNumber 用来设置初始化的过滤器组,取值范围为 0~13。

  第 7 个成员变量 FilterMode 用来设置过滤器组的模式,取值为标识符列表模式CAN_FilterMode_IdList 和标识符屏蔽位模式 CAN_FilterMode_IdMask。

  第 8 个成员变量 FilterScale 用来设置过滤器的位宽为 2 个 16 位 CAN_FilterScale_16bit 还是 1 个32 位 CAN_FilterScale_32bit。

  第 9 个成员变量 CAN_FilterActivation 就很明了了,用来激活该过滤器。

  过滤器初始化参考实例代码:

CAN_FilterInitStructure.CAN_FilterNumber=0;    //过滤器 0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32 位
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;////32 位 ID
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32 位 MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;// FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活过滤器 0
CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化

  至此,CAN 就可以开始正常工作了。如果用到中断,就还需要进行中断相关的配置

4.发送接受消息

  在初始化 CAN 相关参数以及过滤器之后,接下来就是发送和接收消息了。库函数中提供了发送和接受消息的函数。

  发送消息的函数是:

uint8_t CAN_Transmit(CAN_TypeDef* CANx, CanTxMsg* TxMessage);

  第一个参数是 CAN 标号,我们使用 CAN1。第二个参数是相关消息结构体 CanTxMsg 指针类型,CanTxMsg 结构体的成员变量用来设置标准标识符,扩展标示符,消息类型和消息帧长度等信息

  接受消息的函数是:

void CAN_Receive(CAN_TypeDef* CANx, uint8_t FIFONumber, CanRxMsg* RxMessage);

  前两个参数分别为CAN 标号和 FIFO 号。第三个参数 RxMessage 是用来存放接受到的消息信息。

  结构体 CanRxMsg 和结构体 CanTxMsg 比较接近,分别用来定义发送消息和描述接受消息

5.CAN 状态获取

  对于 CAN 发送消息的状态,挂起消息数目等等之类的传输状态信息,库函数提供了一些列的函数,包括 CAN_TransmitStatus()函数,CAN_MessagePending()函数,CAN_GetFlagStatus()函数等等,大家可以根据需要来调用。

//CAN初始化
//tsjw:重新同步跳跃时间单元.范围:1~3; CAN_SJW_1tq CAN_SJW_2tq CAN_SJW_3tq CAN_SJW_4tq
//tbs2:时间段2的时间单元.范围:1~8;
//tbs1:时间段1的时间单元.范围:1~16; CAN_BS1_1tq ~CAN_BS1_16tq
//brp :波特率分频器.范围:1~1024;(实际要加1,也就是1~1024) tq=(brp)*tpclk1
//注意以上参数任何一个都不能设为0,否则会乱.
//波特率=Fpclk1/((tsjw+tbs1+tbs2)*brp);
//mode:0,普通模式;1,回环模式;
//Fpclk1的时钟在初始化的时候设置为36M,如果设置CAN_Normal_Init(1,8,7,5,1);
//则波特率为:36M/((1+8+7)*5)=450Kbps
//返回值:0,初始化OK;
// 其他,初始化失败;
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{ GPIO_InitTypeDef GPIO_InitStructure;
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
#if CAN_RX0_INT_ENABLE
NVIC_InitTypeDef NVIC_InitStructure;
#endif RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//使能PORTA时钟 RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//使能CAN1时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化IO //CAN单元设置
CAN_InitStructure.CAN_TTCM=DISABLE; //非时间触发通信模式 //
CAN_InitStructure.CAN_ABOM=DISABLE; //软件自动离线管理 //
CAN_InitStructure.CAN_AWUM=DISABLE; //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)//
CAN_InitStructure.CAN_NART=ENABLE; //禁止报文自动传送 //
CAN_InitStructure.CAN_RFLM=DISABLE; //报文不锁定,新的覆盖旧的 //
CAN_InitStructure.CAN_TXFP=DISABLE; //优先级由报文标识符决定 //
CAN_InitStructure.CAN_Mode= mode; //模式设置: mode:0,普通模式;1,回环模式; //
//设置波特率
CAN_InitStructure.CAN_SJW=tsjw; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位 CAN_SJW_1tq CAN_SJW_2tq CAN_SJW_3tq CAN_SJW_4tq
CAN_InitStructure.CAN_BS1=tbs1; //Tbs1=tbs1+1个时间单位CAN_BS1_1tq ~CAN_BS1_16tq
CAN_InitStructure.CAN_BS2=tbs2;//Tbs2=tbs2+1个时间单位CAN_BS2_1tq ~ CAN_BS2_8tq
CAN_InitStructure.CAN_Prescaler=brp; //分频系数(Fdiv)为brp+1 //
CAN_Init(CAN1, &CAN_InitStructure); // 初始化CAN1 CAN_FilterInitStructure.CAN_FilterNumber=0; //过滤器0
CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32位
CAN_FilterInitStructure.CAN_FilterIdHigh=0x0000;////32位ID
CAN_FilterInitStructure.CAN_FilterIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32位MASK
CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//过滤器0关联到FIFO0
CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //激活过滤器0 CAN_FilterInit(&CAN_FilterInitStructure);//滤波器初始化 #if CAN_RX0_INT_ENABLE CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);//FIFO0消息挂号中断允许. NVIC_InitStructure.NVIC_IRQChannel = USB_LP_CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 主优先级为1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 次优先级为0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
#endif
return 0; } #if CAN_RX0_INT_ENABLE //使能RX0中断
//中断服务函数
void USB_LP_CAN1_RX0_IRQHandler(void)
{
CanRxMsg RxMessage;
int i=0;
CAN_Receive(CAN1, 0, &RxMessage);
for(i=0;i<8;i++)
printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
}
#endif //can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)
//len:数据长度(最大为8)
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
// 其他,失败;
u8 Can_Send_Msg(u8* msg,u8 len)
{
u8 mbox;
u16 i=0;
CanTxMsg TxMessage;
TxMessage.StdId=0x12; // 标准标识符为0
TxMessage.ExtId=0x12; // 设置扩展标示符(29位)
TxMessage.IDE=0; // 使用扩展标识符
TxMessage.RTR=0; // 消息类型为数据帧,一帧8位
TxMessage.DLC=len; // 发送两帧信息
for(i=0;i<len;i++)
TxMessage.Data[i]=msg[i]; // 第一帧信息
mbox= CAN_Transmit(CAN1, &TxMessage);
i=0;
while((CAN_TransmitStatus(CAN1, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++; //等待发送结束
if(i>=0XFFF)return 1;
return 0; } //can口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到;
// 其他,接收的数据长度;
u8 Can_Receive_Msg(u8 *buf)
{
u32 i;
CanRxMsg RxMessage;
if( CAN_MessagePending(CAN1,CAN_FIFO0)==0)return 0; //没有接收到数据,直接退出
CAN_Receive(CAN1, CAN_FIFO0, &RxMessage);//读取数据
for(i=0;i<8;i++)
buf[i]=RxMessage.Data[i];
return RxMessage.DLC;
}

  转自:http://blog.chinaunix.net/uid-24219701-id-4104131.html

STM32(12)——CAN的更多相关文章

  1. Netruon 理解(12):使用 Linux bridge 将 Linux network namespace 连接外网

    学习 Neutron 系列文章: (1)Neutron 所实现的虚拟化网络 (2)Neutron OpenvSwitch + VLAN 虚拟网络 (3)Neutron OpenvSwitch + GR ...

  2. 基于MVC4+EasyUI的Web开发框架经验总结(12)--利用Jquery处理数据交互的几种方式

    在基于MVC4+EasyUI的Web开发框架里面,大量采用了Jquery的方法,对数据进行请求或者提交,方便页面和服务器后端进行数据的交互处理.本文主要介绍利用Jquery处理数据交互的几种方式,包括 ...

  3. Web 在线文件管理器学习笔记与总结(11)获取文件夹信息 (12)返回上一级操作

    (11)获取文件夹信息 文件夹没有修改操作. index.php: <?php require 'dir.func.php'; require 'file.func.php'; require ...

  4. 在Mac OS X中使用VIM开发STM32(2)

    本文原创于http://www.cnblogs.com/humaoxiao,非法转载者请自重! 在我先前的博文⎣在Mac OS X中使用VIM开发STM32(1)⎤中,我们安装完成了MACVIM,这一 ...

  5. Android菜鸟的成长笔记(12)——Handler、Loop、MessageQueue

    原文:[置顶] Android菜鸟的成长笔记(12)——Handler.Loop.MessageQueue 当一个程序第一次启动时,Android会启动一条主线程(Main Thread),主线程主要 ...

  6. Windows Phone开发(12):认识一下独具个性的磁贴

    原文:Windows Phone开发(12):认识一下独具个性的磁贴 对"磁贴"的理解是一点也不抽象的,为什么呢?只要你愿意启动WP系统,无论你是在模拟器中还是在真机中,是的,桌面 ...

  7. 网站静态化处理—web前端优化—中(12)

    网站静态化处理—web前端优化—中(12) Web前端很多优化原则都是从如何提升网络通讯效率的角度提出的,但是这些原则使用的时候还是有很多陷阱在里面,如果我们不能深入理解这些优化原则背后所隐藏的技术原 ...

  8. python入门(12)dict

    python入门(12)dict Python内置了字典:dict的支持,dict全称dictionary,在其他语言中也称为map,使用键-值(key-value)存储,具有极快的查找速度. 举个例 ...

  9. (1-2)line-height的各类属性值

    (1-2)line-height的各类属性值 首先来个疑问!没有问题印象不深嘛 一.line-height支持哪些属性值呢? 五只手指头就能数过来了咯. 比如normal, <number> ...

随机推荐

  1. C#接收WebSphere MQ中文乱码问题

    最近在用IBM的WebSphere MQ做数据传输,客户端操作系统Windows,服务器是AIX,我们用的都是UTF-8编码,但是联调时发现带中文的都出现了乱码.折腾了好久才发现问题,原来是CCSID ...

  2. javaEE版本的eclipse中导入工程,发现server里面找不到工程,根本发布不了也不能运行

    原文:http://www.cnblogs.com/sxmcACM/p/3674545.html 1.具体解决方法 首先确保,你导入的工程所用的JDK版本和你的机器上安装的版本是同一版本, 如果不同做 ...

  3. MEGER sentence in oracle

    MEGE Sentence This oracle tutorial explains how to use the oralce MEGER sentence with syntax and sam ...

  4. ASP.NET Core - 中间件与管道(1)

    今天来讨论一个ASP.NET Core 很重要概念管道和中间件,在ASP.NET Core中,针对HTTP请求采用pipeline也就是通常说的管道方式来处理,而管道容器内可以挂载很多中间件(处理逻辑 ...

  5. Windows环境双系统安装环境配置

    (最惊喜的事情莫过于...在安装系统完成重新试图安装Docker时解决了关于HyperV的问题,结果提示Docker只能在Win10 Pro或者Enterprise环境下运行...我很坚强...可以按 ...

  6. android之对于view的一点深入理解

    最近在写程序中,遇到了之前自己没遇到过的代码,一番理解后才知道原来是在动态设定xml布局中的属性.即利用LayoutParams可以动态的设定布局或者控件的宽和高,以及的它的左间距,右间距,内间距,和 ...

  7. Oracle中序列的操作以及使用前对序列的初始化

    Oracle中序列的操作以及使用前对序列的初始化   一 创建序列 create sequence myseq start with 1 increment by 1 nomaxvalue minva ...

  8. BZOJ2882:工艺(SAM)

    Description 小敏和小燕是一对好朋友. 他们正在玩一种神奇的游戏,叫Minecraft. 他们现在要做一个由方块构成的长条工艺品.但是方块现在是乱的,而且由于机器的要求,他们只能做到把这个工 ...

  9. 16、SpringBoot-CRUD错误处理机制(3)

    3).将自己指定的数据携带出去 出现错误以后,会来到/error请求,会被BasicErrorController 进行处理 响应出去的数据是由 getErrorAttributes 得到的( Abs ...

  10. 删除oracle实例

    1.在开始菜单中,点击ORAHOME目录下的"Configuration and Migration Tools"下的"Database Configuration As ...