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. 求s=a+aa+aaa+aaaa+aa...a的值,其中a是一个数字。例如2+22+222+2222+22222(此时共有5个数相加),几个数相加有键盘控制。

    package com.lw.HomeWork1;//包名 2 import java.util.Scanner; public class Demo18 { /** * @param args */ ...

  2. [LeetCode]题解(python):125 Valid Palindrome

    题目来源 https://leetcode.com/problems/valid-palindrome/ Given a string, determine if it is a palindrome ...

  3. LeetCode Read N Characters Given Read4 II - Call multiple times

    原题链接在这里:https://leetcode.com/problems/read-n-characters-given-read4-ii-call-multiple-times/ 题目: The ...

  4. Android You need to use a Theme.AppCompat theme (or descendant) with this activity.

    错误描述为:java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with ...

  5. JQuery执行DOM批量克隆并插入的提效方法

    JQuery clone方法可以实现对指定DOM对象的快速复制,并插入文档中. 对于同一类型的对象往往需要按照同一样式模板(HTML标签代码)复制N份并插入文档中,然后再将内容填入模板中,这就需要批量 ...

  6. Mac下安装UPnP Inspector

    由于工作中需要用到UPnP Inspector这个工具,而这个工具在windows下安装非常简单,在Mac下安装却很麻烦,在此记录安装流程. 这个工具依赖于两个其他的库:Coherence(一个DLN ...

  7. 为Go Web App 创建一个主页面

    原文地址    大多数web app都有一个相同的布局.这个布局可能包含一个header或者footer,甚至可能包含一个导航菜单.Go的标准库提供一个简单的方式来创建这些基本元素,通过被不同的页面重 ...

  8. Android requires compiler compliance level 5.0 or 6.0. Found '1.7' instead

    Android requires compiler compliance level 5.0 or 6.0. Found '1.7' instead 在解决问题Underscores can only ...

  9. 关于spring-servlet.xml cannot be opened because it does not exist的解决

    右键项目---->properties--->Java Build Path--->source--->Add Folder --->选择conf文件夹

  10. Android--Retrofit的简单使用(一)

    1,如果不太了解retrofit的同学可以先去官网学习一下简单使用:http://square.github.io/retrofit/,这里我们以一个简单的Get请求的例子来练习一下 2,https: ...