1.CAN通讯的理解

 想学习CAN通讯,那么要对通讯协议有一定的认知。通讯协议是指通信双方对数据传送控制的一种约定。约定中包括对数据格式,同步方式,传输速度,传送步骤,检纠错方式以及控制字符定义等问题做出统一规定,通信双方必须共同遵守。

 CAN通讯全称控制器局域网通讯,是用来在局域网中高效传输,处理信息的一种通讯方式。它采用数据块编码的方式,数据块根据帧类型的不同有四种格式,可使不同的节点接收到相同的数据,然后再根据各节点内CAN配置选择处理还是丢弃该信息(这与TCP/IP协议栈的链路层的MAC地址过滤很相似,是可以互通理解的),CAN的位流是按照非归零(NRZ)码方式编码,一个完整的位电平有显性和隐性两种方式。显性和隐性是根据CAN总线上的差分电压VCAN1H-VCAN1L, 若小于阈值则为隐性位,代表逻辑1,大于阈值则为显性位,代表逻辑0,这种将单电平转换成两根差分线的方式提高了电路的可靠性,不过也决定局域网里同时只能有一路数据传输,因此CAN通讯是半双工的。

2.CAN通讯帧格式

 CAN报文有四种不同的帧类型:

 (1).数据帧:数据帧将数据从发送器传输到接收器。

 数据帧和可以使用标准帧和扩展帧两种格式。它们用一个帧间空间与前面的帧分隔。

  1).帧起始(SOF) 标志帧的开始,由一个“显性(0)”位构成。只有在总线空闲时才允许节点发送(信号),其它所有节点必须同步于首先开始发送报文的节点的帧起始前沿。

     2).仲裁场 由标识符和传送帧类型(RTR)组成的仲裁场

     标准帧格式:

    

    扩展帧格式:

    

对于数据帧 RTR恒为0,SRR恒为1,因此可以根据仲裁场起始第12个字符数判断是标准帧还是扩展帧。

    3).控制场 保留位R1,R0(恒为0),以及帧长度选择位DLC(4位)构成的。

    4).数据场 由数据帧里的发送数据组成,长度由DLC控制,但小于等于8字节。

    5).CRC场 由CRC序列(CRC Sequence),以及CRC界定符(CRC Delimiter)构成。CRC序列之后是CRC界定符,它包含一个单独的“隐性(1)”位。

    6).应答场(ACK Field) 2位,包含应答间隙(ACK Slot)和应答界定符(ACK Delimiter),当接收器正确地接收到有效的报文,接收器就会在应答间隙(ACK Slot)中写入显性位(0),在返回给发送器,完成一次通讯(半双工).

      ① 应答间隙

     所有接收到匹配CRC序列的节点会在应答间隙期间用“显性(0)”的位写入发送器的“隐性(1)”位来组成应答数据。

      ② 应答界定符

    应答界定符是应答场的第二位,并且必须为“隐性(1)”的位。因此,应答间隙(ACK Slot)被两个“隐性”的位所包围,也就是CRC界定符和应答界定符。

7).帧结束  由7个隐性位构成,代表帧的结束。

  (2).远程帧:总线节点发出远程帧,请求发送具有同一识别符的数据帧。

远程帧除了RTR位默认为1,没有数据场外,其它与数据帧相同,不在赘述。

  (3).错误帧:报文发送过程中,检测到任一节点出错,即于下一位发送出错帧,通知发送端停止发送。

错误标志:有两种形式的错误标志:激活错误标志和认可错误标志。

    1).激活错误”标志由6个连续的“显性”位组成

    2).“认可错误”标志由6个连续的“隐性”的位组成,除非被其他节点的“显性”位重写。

错误界定符:错误界定符包括8个“隐性”的位。

    错误标志传送了以后,每一个节点就发送一个“隐性”的位,并一直监视总线直到检测出一个“隐性”的位为止,然后就开始发送其余7个“隐性”位。

 (4).过载帧:接收端用于要求发送端延缓发送下一个数据帧或者远程帧。

1).超载标志: 过载标志由6个“显性”的位组成。过载标志的所有形式和“激活错误”标志的一样

       2).过载界定符包括8个“隐性”的位,具体动作与错误界定符一致

  CAN通讯是数据块编码的半双工通讯方式,没有主从设备区别,因此发出报文的节点为该报文的发送器,该节点在总线空闲或丢失仲裁前恒为发送器;如果一个节点不是报文发送器,并且总线不处于空闲状态,则该节点为接收器。

3.CAN通讯的STM32实现

      CAN协议是比较复杂的一种通讯协议,因此需要在学习如何使用stm32实现前了解协议本身的很多内容,下面就可以开始stm32中CAN协议环回测试,用来简单的理解CAN协议的测试。既然是要STM32实现,那么步骤的设计如下:

   (1).工作原理图

  了解了CAN通讯,下面进入正题,CAN通讯连接首先看原理图如下:

  从上面可以看出CAN1_TX: PD1    CAN1_RX PD0

  (2).CAN硬件驱动配置

  CAN通讯端口配置还是比较简单的:

 GPIO_InitTypeDef GPIO_InitStructure;

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD | RCC_APB2Periph_AFIO, ENABLE);

 GPIO_InitStructure.GPIO_Pin = CAN1_TX_Pin;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //将CAN1输出端PD1配置为推挽输出模式
GPIO_Init(CAN1_TX_Port,&GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = CAN1_RX_Pin;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //将CAN1输入端PD0配置为浮空输入
GPIO_Init(CAN1_RX_Port, &GPIO_InitStructure);

CAN通讯模式配置(因为是简单的测试,因此配置为环回模式,过滤器配置为屏蔽位模式)

void CAN1_MODE_Config(void)
{
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
NVIC_InitTypeDef NVIC_InitStructure; RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
CAN_DeInit(CAN1); CAN_InitStructure.CAN_ABOM = DISABLE; //离线模式由软件实现
CAN_InitStructure.CAN_AWUM = DISABLE; //软件唤醒
CAN_InitStructure.CAN_TTCM = DISABLE; //禁止时间触发通信模式
CAN_InitStructure.CAN_NART = ENABLE; //禁止自动重传
CAN_InitStructure.CAN_TXFP = DISABLE; //优先级由报文的标识符来决定
CAN_InitStructure.CAN_RFLM = DISABLE; //接受溢出时FIFO不锁定,下一个收到的报文覆盖原有报文
 CAN_InitStructure.CAN_Mode = CAN_Mode_LoopBack; //CAN硬件工作环回模式
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq; //重新同步跳跃宽度为2个时间单位
CAN_InitStructure.CAN_BS1 = CAN_BS1_8tq; //时间段为8个时间单位
CAN_InitStructure.CAN_BS2 = CAN_BS2_7tq; //时间段为7个时间单位
 CAN_InitStructure.CAN_Prescaler = ; //设定一个时间单位的长度为5,范围(1~1024)
CAN_Init(CAN1, &CAN_InitStructure); CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask; //设定过滤器组为屏蔽位模式
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit; //过滤器位宽为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_Filter_FIFO0; //过滤器FIFO0指向过滤器0
CAN_FilterInitStructure.CAN_FilterNumber = ; //指定待初始化的过滤器,范围1~13
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE; //使能过滤器
CAN_FilterInit(&CAN_FilterInitStructure); CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE); //FIF0消息挂号中断允许 NVIC_InitStructure.NVIC_IRQChannelSubPriority = ;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = ;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn; //CAN1_RX0中断向量表中开启
NVIC_Init(&NVIC_InitStructure);
}

完成了驱动方面的配置,下面要进行的就是发送CAN数据和接收CAN数据的生成和实现了:

//CAN发送帧构成
void CAN_TxMessageInit(uint32_t std_id, uint32_t ext_id, uint8_t ide, uint8_t rtr, uint8_t dlc, uint8_t *pdata)
{
uint8_t i;
assert_param(dlc>);
CanTxMessage.StdId = std_id&0x7ff; //设定标准标识符0~0x7ff 11位
CanTxMessage.ExtId = ext_id&0x3ffff; //设定额外标识符0~0x3ffff 18位
CanTxMessage.IDE = ide; //输出标识符类型,STD(标准标识符)或EXT(额外标识符)
CanTxMessage.RTR = rtr; //输出帧类型,DATA(数据帧)或者REMOTE(远程帧)
CanTxMessage.DLC = dlc; //帧长度,0~8
 for(i=; i<dlc; i++)
{
CanTxMessage.Data[i] = *(pdata+i);
}
} //CAN中断接收函数
void CAN1_RX0_IRQHandler(void)
{
ITStatus Status; Status = CAN_GetITStatus(CAN1, CAN_IT_FMP0); //判断接受到过滤器中断信号 if(Status == SET)
{
  CAN_Receive(CAN1, CAN_FIFO0, &CanRxMessage); //接收一个CAN报文
memcpy(rdata, &(CanRxMessage.Data[]), ); //将接收到的数据转存到rdata数组中
} CAN_ClearITPendingBit(CAN1, CAN_IT_FMP0);
}

  因为错误帧和超载帧是有stm32自带的硬件实现的,因此由我们提供的是远程帧和数据帧,单个帧内数据0~8字节,如果要发送多个数据,需要在其上添加传输层实现,具体实现我会在以后在研究(双CAN多帧通讯实验)。

主函数的实现如下:

int main(void)
{
BSP_Init();               //硬件初始化
CAN_TxMessageInit(0x011, 0x0000, CAN_ID_STD,
              CAN_RTR_DATA, sizeof(tdata), tdata); //生成CAN报文
CAN_Transmit(CAN1, &CanTxMessage); //发送CAN报文 GPIO_ResetBits(GPIOD, GPIO_LED_1);
ARM_DELAY();
GPIO_SetBits(GPIOD, GPIO_LED_1);
ARM_DELAY(); while()
{
if(CanRxMessage.StdId == 0x11 && CanRxMessage.DLC == sizeof(tdata)) //判断接收到的报文
{
if(strncmp((char *)tdata, (char *)rdata, sizeof(rdata)) == )
{
GPIO_ResetBits(GPIOD, GPIO_LED_1);
printf("stdID is %x, DIC is %x, receive data is %s \r\n",
               CanRxMessage.StdId, CanRxMessage.DLC, rdata);
}
  memset(&CanRxMessage, , sizeof(CanRxMessage)); //清除接收到的报文
CAN_Transmit(CAN1, &CanTxMessage); //发送CAN报文
}
ARM_DELAY();
GPIO_SetBits(GPIOD, GPIO_LED_1);
ARM_DELAY();
}
}

如此就完成了简单的CAN环回测试实验,具体实验可通过串口接收端口查看,如下:

具体代码参考:http://files.cnblogs.com/files/zc110747/8.CAN-Loopback.7z

STM32学习笔记(十) CAN通讯测试(环回模式)的更多相关文章

  1. STM32学习笔记(九) 外部中断,待机模式和事件唤醒

    学会知识只需要不段的积累和提高,但是如何将知识系统的讲解出来就需要深入的认知和系统的了解.外部中断和事件学习难度并不高,不过涉及到STM32的电源控制部分,还是值得认真了解的,在本文中我将以实际代码为 ...

  2. python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例

    python3.4学习笔记(十四) 网络爬虫实例代码,抓取新浪爱彩双色球开奖数据实例 新浪爱彩双色球开奖数据URL:http://zst.aicai.com/ssq/openInfo/ 最终输出结果格 ...

  3. go微服务框架kratos学习笔记十(熔断器)

    目录 go微服务框架kratos学习笔记十(熔断器) 什么是熔断 熔断器逻辑 kratos Breaker kratos 熔断逻辑 kratos熔断器使用说明 bladmaster client br ...

  4. STM32学习笔记——OLED屏

    STM32学习笔记--OLED屏 OLED屏的特点: 1.  模块有单色和双色可选,单色为纯蓝色,双色为黄蓝双色(本人选用双色): 2.  显示尺寸为0.96寸 3.  分辨率为128*64 4.   ...

  5. STM32学习笔记——点亮LED

    STM32学习笔记——点亮LED 本人学习STM32是直接通过操作stm32的寄存器,使用的开发板是野火ISO-V2版本: 先简单的介绍一下stm32的GPIO: stm32的GPIO有多种模式: 1 ...

  6. stm32学习笔记----双串口同时打开时的printf()问题

    stm32学习笔记----双串口同时打开时的printf()问题 最近因为要使用串口2外接PN532芯片实现通信,另一方面,要使用串口1来将一些提示信息输出到上位机,于是重定义了printf(),使其 ...

  7. stm32学习笔记——外部中断的使用

    stm32学习笔记——外部中断的使用 基本概念 stm32中,每一个GPIO都可以触发一个外部中断,但是,GPIO的中断是以组为一个单位的,同组间的外部中断同一时间只能使用一个.比如说,PA0,PB0 ...

  8. STM32学习笔记(四)——串口控制LED(中断方式)

    目录: 一.时钟使能,包括GPIO的时钟和串口的时钟使能 二.设置引脚复用映射 三.GPIO的初始化配置,注意要设置为复用模式 四.串口参数初始化配置 五.中断分组和中断优先级配置 六.设置串口中断类 ...

  9. Learning ROS for Robotics Programming Second Edition学习笔记(十) indigo Gazebo rviz slam navigation

    中文译著已经出版,详情请参考:http://blog.csdn.net/ZhangRelay/article/category/6506865 moveit是书的最后一章,由于对机械臂完全不知,看不懂 ...

随机推荐

  1. SinalR+WebSocket

    1.参考资料: http://www.asp.net/signalr/overview/guide-to-the-api/hubs-api-guide-server http://signalr.ne ...

  2. 集合中list、ArrayList、LinkedList、Vector的区别、Collection接口的共性方法以及数据结构的总结

    List (链表|线性表) 特点: 接口,可存放重复元素,元素存取是有序的,允许在指定位置插入元素,并通过索引来访问元素 1.创建一个用指定可视行数初始化的新滚动列表.默认情况下,不允许进行多项选择. ...

  3. StringBuffer与StringBuilder有什么区别

    package String比较; /* * StringBuffer与StringBuilder有什么区别 * StringBuilder是JDK5增加的一个新类,功能几乎与StringBuffer ...

  4. 【转】OpenStack奥斯汀峰会Keynotes国内抢先看

    http://www.openstack.cn/?p=5341 OpenStack奥斯汀峰会Keynotes国内抢先看入口:http://www.tudou.com/home/_903780397/i ...

  5. 测试与log

    1 加载logback private static void loadloack() { LoggerContext lc = (LoggerContext) LoggerFactory.getIL ...

  6. Linux下安装APache

    1:在图形界面下下载apache 安装包,我下的是 httpd-2.2.9.tar.gz 源码安装包,地址是http://httpd.apache.org/download.cgi 2:用:gzip ...

  7. JDBC-java访问数据库

    定义:JDBC是一个独立于数据库管理系统的,通用的SQL数据库存取和操作的公共接口 (由JAVA类和接口组成,接口由各数据库厂家负责实现) 使用方法: 1.加载驱动 Class.forName(&qu ...

  8. 多列布局——column-width

    column-width的使用和CSS中的width属性一样,不过不同的是,column-width属性在定义元素列宽的时候,既可以单独使用,也可以和多列属性中其他属性配合使用.其基本语法如下所示 : ...

  9. paper 113:Bhattacharyya distance

    在统计理论中,Bhattacharyya距离用来度量两个离散或连续概率分布的相似性.它与Bhattacharyya系数(Bhattacharyya coefficient)高度相关,后者是用来度量两个 ...

  10. ESB 中的流量控制

    ESB 中的流量控制