精华内容
下载资源
问答
  • the Difference Between Stateful ...关键字:IPv6 IPv4 IP DHCP DHCPv6 autoconf IPv6地址 自动分配 自动配置 有状态 无状态 stateful stateless Well, instead of me just jumping right into explaining the differ

    the Difference Between Stateful and Stateless DHCP

    关键字:IPv6 IPv4 IP DHCP DHCPv6 autoconf IPv6地址 自动分配 自动配置 有状态 无状态 stateful stateless

    Well, instead of me just jumping right into explaining the difference between Stateful and Stateless DHCP. I’m going to slow down just a bit and briefly explain DHCP first.

    DHCP stands for Dynamic Host Configuration Protocol, this client / server protocol has been around for awhile now, and is regually implemented on IPv4 networks. IPv4 DHCP, is explained in RFC 2131 and IPv6 DHCP is explained in RFC 3315. But, in a nutshell; network adminstrators use DHCP to provide a framework that is used by networked devices (DHCP clients) to obtain various necessary parameters from DHCP Servers so the DHCP clients can operate in an Internet Protocol (IP) network. Now when it comes to DHCP for IPv6 (RFC 3315), there are two ways DHCPv6 can be implemented, either Stateful or Stateless.

    Stateful DHCP is centrally managed on a DHCP server(s); and the DHCP clients use Stateful DHCP to obtain an IP address(es) and other useful configuration informaiton from the DHCP server(s).

    在介绍有状态和无状态IPv6地址自动配置的区别之前,请允许我先介绍一下DHCP。

    DHCP是动态主机配置协议的缩写,这种C/S方式的协议在IPv4网络中已经广泛部署并为大家所认可。IPV4 DHCP是在RFC2131中定义的,IPv6DHCP是在RFC3315中定义的,简单地说,DHCP客户端从DHCP服务端得到IP地址,加入IP网络,而无需手工干预。当发展到IPv6网络时,就有两种IPv6地址的自动配置的方式:有状态地址自动配置和无状态地址自动配置。

    有状态的地址自动配置是指由DHCP服务器统一管理,客户端从DHCP服务器的地址池中拿到IPv6地址和其他信息(例如DNS等)。
    But, Stateless DHCP on the other hand; means the DHCP server(s) is not required to store any dynamic state information on the DHCP server(s) about any indivisual DHCP clients. Instead, the DHCP clients autoconfigure their own IP address(es) based on router advertisments. So, with Stateless DHCP, the DHCP clients don’t use the DHCP server(s) to obtain IP address(es) information, they use the DHCP server(s) to obtain the other useful configuration informaiton (like the address(es) of DNS servers).

    无状态地址自动配置是指不需要DHCP服务器进行管理,客户端根据网络RA(路由通告)并根据自己的MAC地址计算出自己的IPv6地址。他们一般使用DHCP服务器来获取DNS服务器的地址。

    Currently, Cisco recommends that you use Stateless DHCP instead of Stateful DHCP when implementing and deploying IPv6 networks; because, Cisco Routers are not designed to act as Stateful DHCPv6 servers. But, if you need to implement Stateful DHCP on your IPv6 network; Cisco makes a product named Cisco Network Registar (CNR) that can help you out a lot.
    I invite you to visit my website were you’ll find the latest information regarding Cisco IPv6 Design and ImplementationTechniques.

    一般情况下,Cisco推荐使用无状态地址自动配置,cisco路由器并没有设计DHCPv6服务器。但是如果你需要在网络中部署DHCPv6服务器,可以选择cisco的CNR。

    欢迎大家来我的网站来学习和探讨cisco网络技术。

    To your success,

    Charles Ross, CCNP #CSCO10444244 is the owner of Ittechtips.com; where you’ll find free comprehensive information and videos

    about IPv6 technology and how it works with Cisco Systems technology.

    Sign-Up for “18 Free Videos” that will teach you IPv6 Address Representation In Under 10 Minutes! at his website.

    http://www.ciscoipv6ittechtips.com

    Article Source: http://EzineArticles.com/?expert=Charles_E_Ross
    本文源地址,原文不太好找了。

    ====================================================
    翻译者总结:
    当前IPv6地址自动配置可以有有状态和无状态之分。
    有状态是指在DHCP服务器中保存已分配的IPv6地址信息,无状态是指客户端通过RA通告得到网络前缀和其他信息。

    DHCPv6 不支持默认网关的分配。
    RA 通告不支持DNS分发(正在实验并讨论中……)

    DHCPD最新版4.2.0P2已经支持DHCPv6部分功能。

    参考资料第三篇给出了DHCPv6和RA结合使用的案例
    参考资料四给出了RA通告情况下DNS服务器的获取情况

    参考资料:
    http://www.ietf.org/mail-archive/web/ipv6/current/msg08684.html
    http://www.cisco.com/en/US/prod/collateral/iosswrel/ps6537/ps6554/ps6600/ps6641/aag_C45-456070_v2.pdf
    http://www.cnblogs.com/qlight/archive/2010/11/20/1882573.html
    http://bbs.51cto.com/thread-4206-1.html
    http://www.cost.edu.cn/forum.php?mod=viewthread&tid=5119&extra=page%3D1
    RFC3315
    RFC2462

    展开全文
  • 本人现在设计的DALI主控制采用32位LPC2292 CPU,具有独立的2路DALI总线回路,同时使用CAN总线与其它系统连接,整个主控制器实现两种DALI总线设备的管理,包括地址分配,分组,场景设置,时间表,时序控制等功能。...
  • 汽车行业 SAE J1939 ID 地址分配,0-255对应地址表格速查,对应ID地址 引擎 #1 引擎 #2 涡轮增压器 变速器 #1 变速器 #2 变档仪表板-主 变档仪表板-副 动力卸掉-(主要的或后面的) 车轴-转向系 车轴-驱动 #1 车轴-...
  • 提出了一种在CAN总线中实现CAN控制器地址空间自动配置的新方法,该方法是在CAN网络节点的地址配置初始化过程中,使用了CAN协议扩展帧中的2个保留位,利用这2个保留位构成了3种状态:地址获得、地址分配待定、地址...
  • 提出了一种在CAN总线中实现CAN控制器地址空间自动配置的新方法,该方法是在CAN网络节点的地址配置初始化过程中,使用了CAN协议扩展帧中的2个保留位,利用这2个保留位构成了3种状态:地址获得、地址分配待定、地址...
  • 本人现在设计的DALI主控制采用32位LPC2292 CPU,具有独立的2路DALI总线回路,同时使用CAN总线与其它系统连接,整个主控制器实现两种DALI总线设备的管理,包括地址分配,分组,场景设置,时间表,时序控制等功能。...
  • LTE基础:LTE网络中的IP地址分配

    万次阅读 2017-02-13 10:22:53
    Data Network)建立连接,一个PDN地址分配给UE,同时,会在UE和P-GW之间建立默认承载(default bearer),默认承载会保持连接直到UE分离(detach)LTE网络。 LTE网络为每一个用户APN建立默认承载,为每一个APN...

    LTE网络全IP化,数据均在IP包内传输。当UE连接LTE网络,为了和PDN(Packet Data Network)建立连接,一个PDN地址会分配给UE,同时,会在UE和P-GW之间建立默认承载(default bearer),默认承载会保持连接直到UE分离(detach)LTE网络。

    LTE网络为每一个用户APN建立默认承载,为每一个APN分配一个唯一的IP地址,这些IP地址可以是IPv4,IPv6,或者IPv4/IPv6类型。

    本文将介绍当UE附着网络时,LTE如何为用户分配IP地址?

    IP地址分配的类型

    当UE initial Attach LTE网络,它请求一个PDN连接,为此,P-GW会分配一个IP地址给UE,并在默认承载建立的同时将IP地址传送给UE。有了这个IP地址,UE才能使用通过PDN提供的服务。

    P-GW分配两种类型IP地址:动态和静态IP地址。动态IP地址下,UE每次接入网络,系统会自动分配一个IP地址给UE。静态IP地址下,每一次会分配一指定的IP地址。

    动态IP分配

    网络首先在P-GW提供一个IP池(IP pool),当UE initial Attach LTE网络,PGW会动态分配一个IP地址给UE。

    静态IP地址分配

    网络会分配一个永久的IP地址给UE,用户的静态IP信息存储在HSS(Home Subscriber Server,用户归属地服务器),当UE initial Attach网络,P-GW从HSS获取UE静态IP地址,并转发给UE。

    动态IP地址分配

    流程如下(点击放大看):

    P-GW IP分配

    在P-GW,会提供一个包含IP地址的IP pool,以及DNS服务器IP地址。

    1. [UE ->MME] Requesting for PDN(internet)Connectivity

    UE发送 PDN Connectivity Request (PDN type=IPv4,PCO=DNS Server IPv4 Address Request)消息到MME,请求一个UE的IPv4地址和DNS 服务器IP地址(通过PCO域)。PDN Connectivity Request是一个ESM消息,该消息包含在Attach Request的ESM Message container内。

    2~3.  [MME->S-GW->P-GW] Requesting for Session Creation

    MME从HSS获取用户属性(subscription profile),发送 Create Session Request(IMSI,PDN Type=IPv4,PDN Adress=0.0.0.0,PCO=DNS Server IPv4 Address Request)消息到P-GW。因为这是动态IP地址分配,用户信息并不包含IP地址。在Create Session Request消息中,PDN地址域设置为0.0.0.0。

    4.  [P-GW] 分配PDN地址和DNS服务器地址

    P-GW检查PDN类型和PDN地址(0.0.0.0),知道需要分配IPv4地址,它从IPv4 pool里选择一个IP 地址(比如:UE IP=1.1.1.5)分配给UE。同时相应分配DNS服务器地址。

    5~6.  [MME<-S-GW<-P-GW] Responding to Create Session Request

    作为对2~3步骤请求的响应,P-GW发送Create Session Response 消息给MME。这一消息包含了在PDN地址域的UE IP地址(由本地P-GW动态分配)和在PCO域的DNS服务器地址。

    7.  [UE<-MME]  Requesting for Activation of Default Bearer Context

    MME发送UE Activate Default EPS Bearer Context Request (PDN Type=IPv4,PDN Address=UE IP (1.1.1.5),PCO={Primary DNS IP,Secondary DNS IP})消息以激活默认承载内容。这一EMS消息包含DNS服务器IP地址和UE IP地址,当传送时嵌入Attach Accept消息内。

    8. [UE] Obtaining Dynamic IP Address for using PDN service

    UE获得动态IP地址(1.1.1.5)和DNS 服务器IP地址(Primary DNS IP=10.1.1.1,Secondary DNS IP=10.1.1.2)。默认承载在UE和P-GW间建立。UE现在可以连接PDN(internet),可以通过自己的动态IP地址使用internet服务。

    静态IP地址分配

    流程如下(点击放大):

    HSS

    HSS提供每个用户的用户属性(subscription profile),这些属性信息包括用于PDN连接的PDN类型和PDN地址。

    P-GW

    P-GW已设置好DNS服务器IP地址

    当用户开机,UE开始initial Attach LTE网络。

    1.  [UE->MME]  Requesting for PDN(Internet) Connectivity

    UE发送 PDN Connectivity Request (PDN type=IPv4,PCO=DNS Server IPv4 Address Request)消息到MME,请求一个UE的IPv4地址和DNS 服务器IP地址(通过PCO域)。

    2.  [MME->HSS] Requesting the LTE Network for Registration

    MME发送Update Location Request 消息通知HSS,MME1下有UE请求注册LTE网络。

    3. [MME<-HSS] Forwarding Subscription Profile

    HSS确认UE在MME1注册后,通过Update Location Answer(IMSI,PDN Type=IPv4,PDN Address = Static UE IP(1.1.1.1))消息发送UE 用户属性到MME1 。这一用户属性中包含了分配给UE的静态IP地址。

    4~5.  [MME->S-GW->P-GW]  Requesting for Session Creation

    当MME从HSS接收到用户属性后,MME知道了UE的静态IP地址(1.1.1.1)。MME准备Create Session Request(IMSI,PDN Type=IPv4,PDN Adress=Static UE IP(1.1.1.1),PCO=DNS Server IPv4 Address Request)消息,并发送到P-GW。这一消息包含了在PDN域的静态IP地址。

    6~7. [MME<-S-GW<-P-GW] Responding to Create Session Request

    作为对4~5步骤请求的响应,P-GW发送Create Session Response (IMSI,PDN Type=IPv4,PDN Adress=Static UE IP(1.1.1.1),PCO={Primary DNS IP,Secondary DNS IP})消息给MME。这一消息包含在PDN地址域的静态UE IP地址和在PCO域的DNS服务器IP地址。

    8.  [UE<-MME]  Requesting for Activation of Default Bearer Context

    MME发送UE Activate Default EPS Bearer Context Request (PDN Type=IPv4,PDN Address=Static UE IP(1.1.1.1),PCO={Primary DNS IP,Secondary DNS IP})消息以激活默认承载内容。这一EMS消息包含DNS服务器IP地址和UE IP地址(1.1.1.1),当传送时嵌入Attach Accept消息内。

    9.  [UE] Obtaining Dynamic IP Address for using PDN service

    UE获得静态IP地址(1.1.1.1)和DNS 服务器IP地址(Primary DNS IP=10.1.1.1,Secondary DNS IP=10.1.1.2)。默认承载在UE和P-GW间建立。UE现在可以连接PDN(internet),可以通过自己的静态IP地址使用internet服务。

    当用户完成有效网络注册后,PDN地址(IP地址)和默认承载会一直保留给用户,即使用户不在使用Internet服务,这叫为用户提供“always-on IP connectivity”。




    The high-level network architecture of LTE is comprised of following three main components:

    • The User Equipment (UE).

    • The Evolved UMTS Terrestrial Radio Access Network (E-UTRAN).

    • The Evolved Packet Core (EPC).

    The evolved packet core communicates with packet data networks in the outside world such as the internet, private corporate networks or the IP multimedia subsystem. The interfaces between the different parts of the system are denoted Uu, S1 and SGi as shown below:

    LTE Architecture

    The User Equipment (UE)

    The internal architecture of the user equipment for LTE is identical to the one used by UMTS and GSM which is actually a Mobile Equipment (ME). The mobile equipment comprised of the following important modules:

    • Mobile Termination (MT) : This handles all the communication functions.

    • Terminal Equipment (TE) : This terminates the data streams.

    • Universal Integrated Circuit Card (UICC) : This is also known as the SIM card for LTE equipments. It runs an application known as the Universal Subscriber Identity Module (USIM).

    A USIM stores user-specific data very similar to 3G SIM card. This keeps information about the user's phone number, home network identity and security keys etc.

    The E-UTRAN (The access network)

    The architecture of evolved UMTS Terrestrial Radio Access Network (E-UTRAN) has been illustrated below.

    LTE E-UTRAN

    The E-UTRAN handles the radio communications between the mobile and the evolved packet core and just has one component, the evolved base stations, called eNodeB or eNB. Each eNB is a base station that controls the mobiles in one or more cells. The base station that is communicating with a mobile is known as its serving eNB.

    LTE Mobile communicates with just one base station and one cell at a time and there are following two main functions supported by eNB:

    • The eBN sends and receives radio transmissions to all the mobiles using the analogue and digital signal processing functions of the LTE air interface.

    • The eNB controls the low-level operation of all its mobiles, by sending them signalling messages such as handover commands.

    Each eBN connects with the EPC by means of the S1 interface and it can also be connected to nearby base stations by the X2 interface, which is mainly used for signalling and packet forwarding during handover.

    A home eNB (HeNB) is a base station that has been purchased by a user to provide femtocell coverage within the home. A home eNB belongs to a closed subscriber group (CSG) and can only be accessed by mobiles with a USIM that also belongs to the closed subscriber group.

    The Evolved Packet Core (EPC) (The core network)

    The architecture of Evolved Packet Core (EPC) has been illustrated below. There are few more components which have not been shown in the diagram to keep it simple. These components are like the Earthquake and Tsunami Warning System (ETWS), the Equipment Identity Register (EIR) and Policy Control and Charging Rules Function (PCRF).

    LTE EPC

    Below is a brief description of each of the components shown in the above architecture:

    • The Home Subscriber Server (HSS) component has been carried forward from UMTS and GSM and is a central database that contains information about all the network operator's subscribers.

    • The Packet Data Network (PDN) Gateway (P-GW) communicates with the outside world ie. packet data networks PDN, using SGi interface. Each packet data network is identified by an access point name (APN). The PDN gateway has the same role as the GPRS support node (GGSN) and the serving GPRS support node (SGSN) with UMTS and GSM.

    • The serving gateway (S-GW) acts as a router, and forwards data between the base station and the PDN gateway.

    • The mobility management entity (MME) controls the high-level operation of the mobile by means of signalling messages and Home Subscriber Server (HSS).

    • The Policy Control and Charging Rules Function (PCRF) is a component which is not shown in the above diagram but it is responsible for policy control decision-making, as well as for controlling the flow-based charging functionalities in the Policy Control Enforcement Function (PCEF), which resides in the P-GW.

    The interface between the serving and PDN gateways is known as S5/S8. This has two slightly different implementations, namely S5 if the two devices are in the same network, and S8 if they are in different networks.

    Functional split between the E-UTRAN and the EPC

    Following diagram shows the functional split between the E-UTRAN and the EPC for an LTE network:

    LTE E-UTRAN and EPC

    2G/3G Versus LTE

    Following table compares various important Network Elements & Signaling protocols used in 2G/3G abd LTE.

    2G/3GLTE
    GERAN and UTRANE-UTRAN
    SGSN/PDSN-FAS-GW
    GGSN/PDSN-HAPDN-GW
    HLR/AAAHSS
    VLRMME
    SS7-MAP/ANSI-41/RADIUSDiameter
    DiameterGTPc-v0 and v1GTPc-v2
    MIPPMIP

    展开全文
  • CAN通信中地址设置的问题

    千次阅读 2017-09-26 12:07:08
    我在用STM32F107做双CAN口测试时,遇到的问题是不管如何设置过滤器和ID,发送的数据都能够传送过去,先做个标记。程序代码如下,后续会回复问题的解决办法 u8 CAN1_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 ...

    我在用STM32F107做双CAN口测试时,遇到的问题是不管如何设置过滤器和ID,发送的数据都能够传送过去,先做个标记。程序代码如下,后续会回复问题的解决办法
    问题解决办法:由于过滤器MASKID的设置错误,STDID所处的位置如下图所示,位于最高的11位,所以MASKID应该设置位0XFFF0 0000,简单设置位0XFFFF FFFF也可以。

    u8 CAN1_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
    {
    
    	GPIO_InitTypeDef GPIO_InitStructure; 
    	CAN_InitTypeDef        CAN_InitStructure;
     	CAN_FilterInitTypeDef  CAN_FilterInitStructure;
    
       	NVIC_InitTypeDef  NVIC_InitStructure;
    
    
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);//ʹÄÜPORTAʱÖÓ	                   											 
    
      	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);//ʹÄÜCAN1ʱÖÓ	
        GPIO_PinRemapConfig(GPIO_Remap1_CAN1,ENABLE);
    	
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//¸´ÓÃÍÆÍì
        GPIO_Init(GPIOB, &GPIO_InitStructure);		//³õʼ»¯IO
       
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//ÉÏÀ­ÊäÈë
        GPIO_Init(GPIOB, &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;						 //±¨ÎIJ»Ëø¶¨,еĸ²¸Ç¾ÉµÄ // 
      	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  ÓÃÀ´È·¶¨½ÓÊÕµ½µÄÊý¾ÝIDµÄÆÚÍûÖµ
      	CAN_FilterInitStructure.CAN_FilterIdLow=0x0022;
      	CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0x0000;//32λMASK  ÓÃÀ´È·¶¨±ØÐë¹ØÐĵÄÄǼ¸Î»Îª±ØÐë·ûºÏµÄIDλ
      	CAN_FilterInitStructure.CAN_FilterMaskIdLow=0x00FF;
      	CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//¹ýÂËÆ÷0¹ØÁªµ½FIFO0
     	CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //¼¤»î¹ýÂËÆ÷0
    
      	CAN_FilterInit(&CAN_FilterInitStructure);//Â˲¨Æ÷³õʼ»¯
    
    	
    	CAN_ITConfig(CAN1,CAN_IT_FMP0,ENABLE);//FIFO0ÏûÏ¢¹ÒºÅÖжÏÔÊÐí.		    
      
      	NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
      	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;     // Ö÷ÓÅÏȼ¶Îª1
      	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;            // ´ÎÓÅÏȼ¶Îª0
      	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      	NVIC_Init(&NVIC_InitStructure);
    
    	return 0;
    }   
     
    
    //ÖжϷþÎñº¯Êý			    
    void CAN1_RX0_IRQHandler(void)
    {
      	
      	CanRxMsg RxMessage;
    	
    	int i=0;
    	int res;
    	LED2=!LED2;
        CAN_Receive(CAN1, 0, &RxMessage);
    	if(RxMessage.Data[0]==112)
    	{	
    //		canbuf[0]=111;
    //		res=Can2_Send_Msg(canbuf,8);//·¢ËÍ8¸ö×Ö½Ú 
    //		if(res==0)
    //		{
    //			LED0=!LED0;
    //		}
    		LED1=!LED1;
    	}
    //	else if(RxMessage.Data[0]==112)
    //	{
    //   LED5=!LED5;
    //	}
    //	for(i=0;i<8;i++)
    //	printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
    }
    
    
    //can·¢ËÍÒ»×éÊý¾Ý(¹Ì¶¨¸ñʽ:IDΪ0X12,±ê×¼Ö¡,Êý¾ÝÖ¡)	
    //len:Êý¾Ý³¤¶È(×î´óΪ8)				     
    //msg:Êý¾ÝÖ¸Õë,×î´óΪ8¸ö×Ö½Ú.
    //·µ»ØÖµ:0,³É¹¦;
    //		 ÆäËû,ʧ°Ü;
    u8 Can1_Send_Msg(u8* msg,u8 len)
    {	
      u8 mbox;
      u16 i=0;
      CanTxMsg TxMessage;
      TxMessage.StdId=0x0033;					 // ±ê×¼±êʶ·ûΪ0
      TxMessage.ExtId=0x0000;				 // ÉèÖÃÀ©Õ¹±êʾ·û£¨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 Can1_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;	
    }
    
    
    /************************************************************************************************/
    //CAN2³õʼ»¯º¯Êý
    u8 CAN2_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
    {
    
    	GPIO_InitTypeDef GPIO_InitStructure; 
    	CAN_InitTypeDef        CAN_InitStructure;
     	CAN_FilterInitTypeDef  CAN_FilterInitStructure;
    
       	NVIC_InitTypeDef  NVIC_InitStructure;
    
    
    	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);//ʹÄÜPORTAʱÖÓ	                   											 
    
      	RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN2, ENABLE);//ʹÄÜCAN1ʱÖÓ	
        GPIO_PinRemapConfig(GPIO_Remap_CAN2,ENABLE);
    	
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	//¸´ÓÃÍÆÍì
        GPIO_Init(GPIOB, &GPIO_InitStructure);		//³õʼ»¯IO
       
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//ÉÏÀ­ÊäÈë
        GPIO_Init(GPIOB, &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;						 //±¨ÎIJ»Ëø¶¨,еĸ²¸Ç¾ÉµÄ // 
      	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(CAN2, &CAN_InitStructure);            // ³õʼ»¯CAN2
    
     	CAN_FilterInitStructure.CAN_FilterNumber=14;	  //¹ýÂËÆ÷14
     	CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask; //±êʶ·ûÆÁ±Îλģʽ
      	CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit; //32λ 
        CAN_FilterInitStructure.CAN_FilterIdHigh   = (((u32)0x33<<21)&0xffff0000)>>16;
        CAN_FilterInitStructure.CAN_FilterIdLow   = (((u32)0x33<<21)|CAN_ID_STD|CAN_RTR_DATA)&0xffff;
      	CAN_FilterInitStructure.CAN_FilterMaskIdHigh=0xFFFF;//32λMASK
      	CAN_FilterInitStructure.CAN_FilterMaskIdLow=0xFFFF;
      	CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0;//¹ýÂËÆ÷14¹ØÁªµ½FIFO0
     	CAN_FilterInitStructure.CAN_FilterActivation=ENABLE; //¼¤»î¹ýÂËÆ÷0
    	
    //	CAN_FilterInitStructure.CAN_FilterNumber=0;
    //    CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;
    //    CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;//CAN_FilterScale_16bit; //32bit
    
    //    CAN_FilterInitStructure.CAN_FilterIdHigh   = (((u32)slave_id<<21)&0xffff0000)>>16;
    //    CAN_FilterInitStructure.CAN_FilterIdLow   = (((u32)slave_id<<21)|CAN_ID_STD|CAN_RTR_DATA)&0xffff;
    //    CAN_FilterInitStructure.CAN_FilterMaskIdHigh  = 0xFFFF;
    //    CAN_FilterInitStructure.CAN_FilterMaskIdLow   = 0xFFFF;
    
        CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_FIFO0;
        CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;   //?????
        CAN_FilterInit(&CAN_FilterInitStructure);
    
        CAN_ITConfig(CAN1,CAN_IT_FMP0|CAN_IT_EPV, ENABLE);
    
      	CAN_FilterInit(&CAN_FilterInitStructure);//Â˲¨Æ÷³õʼ»¯
    
    	
    	CAN_ITConfig(CAN2,CAN_IT_FMP0,ENABLE);//FIFO0ÏûÏ¢¹ÒºÅÖжÏÔÊÐí.		    
      
      	NVIC_InitStructure.NVIC_IRQChannel = CAN2_RX0_IRQn;
      	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;     // Ö÷ÓÅÏȼ¶Îª1
      	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;            // ´ÎÓÅÏȼ¶Îª0
      	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
      	NVIC_Init(&NVIC_InitStructure);
    
    	return 0;
    }   
     
    
    //ÖжϷþÎñº¯Êý			    
    void CAN2_RX0_IRQHandler(void)
    {
      	
      	CanRxMsg RxMessage;
    	
    	int i=0;
      int res;
        CAN_Receive(CAN2, 0, &RxMessage);
    	if(RxMessage.Data[0]==1)
    	{	
    //		canbuf[0]=112;
    //		res=Can1_Send_Msg(canbuf,8);//·¢ËÍ8¸ö×Ö½Ú 
    //		if(res==0)
    //		{
    			LED1=!LED1;
    //		}
    		LED2=!LED2;
    	}
    //	else if(RxMessage.Data[0]==112)
    //	{
    //   LED5=!LED5;
    //	}
    //	for(i=0;i<8;i++)
    //	printf("rxbuf[%d]:%d\r\n",i,RxMessage.Data[i]);
    }
    
    
    //can·¢ËÍÒ»×éÊý¾Ý(¹Ì¶¨¸ñʽ:IDΪ0X12,±ê×¼Ö¡,Êý¾ÝÖ¡)	
    //len:Êý¾Ý³¤¶È(×î´óΪ8)				     
    //msg:Êý¾ÝÖ¸Õë,×î´óΪ8¸ö×Ö½Ú.
    //·µ»ØÖµ:0,³É¹¦;
    //		 ÆäËû,ʧ°Ü;
    u8 Can2_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(CAN2, &TxMessage);   
      i=0;
      while((CAN_TransmitStatus(CAN2, mbox)==CAN_TxStatus_Failed)&&(i<0XFFF))i++;	//µÈ´ý·¢ËͽáÊø
      if(i>=0XFFF)return 1;
      return 0;		
    
    }
    //can¿Ú½ÓÊÕÊý¾Ý²éѯ
    //buf:Êý¾Ý»º´æÇø;	 
    //·µ»ØÖµ:0,ÎÞÊý¾Ý±»ÊÕµ½;
    //		 ÆäËû,½ÓÊÕµÄÊý¾Ý³¤¶È;
    u8 Can2_Receive_Msg(u8 *buf)
    {		   		   
     	u32 i;
    	CanRxMsg RxMessage;
        if( CAN_MessagePending(CAN2,CAN_FIFO0)==0)return 0;		//ûÓнÓÊÕµ½Êý¾Ý,Ö±½ÓÍ˳ö 
        CAN_Receive(CAN2, CAN_FIFO0, &RxMessage);//¶ÁÈ¡Êý¾Ý	
        for(i=0;i<8;i++)
        buf[i]=RxMessage.Data[i];  
    	return RxMessage.DLC;	
    }
    



    展开全文
  • CAN

    千次阅读 2018-07-25 17:36:32
    若对CAN通讯协议不了解,可先阅读《CAN总线入门》、《CAN-bus规范》文档内容学习。 关于实验板上的CAN收发器可查阅《TJA1050》文档了解。 40.1 CAN协议简介 CAN是控制器局域网络(Controlle...

    本章参考资料:《STM32F4xx 中文参考手册2》、《STM32F4xx规格书》、库帮助文档《stm32f4xx_dsp_stdperiph_lib_um.chm》。

    若对CAN通讯协议不了解,可先阅读《CAN总线入门》、《CAN-bus规范》文档内容学习。

    关于实验板上的CAN收发器可查阅《TJA1050》文档了解。

    40.1 CAN协议简介

    CAN是控制器局域网络(Controller Area Network)的简称,它是由研发和生产汽车电子产品著称的德国BOSCH公司开发的,并最终成为国际标准(ISO11519),是国际上应用最广泛的现场总线之一。

    CAN总线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线,并且拥有以CAN为底层协议专为大型货车和重工机械车辆设计的J1939协议。近年来,它具有的高可靠性和良好的错误检测能力受到重视,被广泛应用于汽车计算机控制系统和环境温度恶劣、电磁辐射强及振动大的工业环境。

    40.1.1 CAN物理层

    与I2C、SPI等具有时钟信号的同步通讯方式不同,CAN通讯并不是以时钟信号来进行同步的,它是一种异步通讯,只具有CAN_High和CAN_Low两条信号线,共同构成一组差分信号线,以差分信号的形式进行通讯。

    1.    闭环总线网络

    CAN物理层的形式主要有两种,图 401中的CAN通讯网络是一种遵循ISO11898标准的高速、短距离"闭环网络",它的总线最大长度为40m,通信速度最高为1Mbps,总线的两端各要求有一个"120欧"的电阻。

    图 401 CAN闭环总线通讯网络

    2.    开环总线网络

    图 402中的是遵循ISO11519-2标准的低速、远距离"开环网络",它的最大传输距离为1km,最高通讯速率为125kbps,两根总线是独立的、不形成闭环,要求每根总线上各串联有一个"2.2千欧"的电阻。

    图 402 CAN开环总线通讯网络

    3.    通讯节点    

    从CAN通讯网络图可了解到,CAN总线上可以挂载多个通讯节点,节点之间的信号经过总线传输,实现节点间通讯。由于CAN通讯协议不对节点进行地址编码,而是对数据内容进行编码的,所以网络中的节点个数理论上不受限制,只要总线的负载足够即可,可以通过中继器增强负载。

    CAN通讯节点由一个CAN控制器及CAN收发器组成,控制器与收发器之间通过CAN_Tx及CAN_Rx信号线相连,收发器与CAN总线之间使用CAN_High及CAN_Low信号线相连。其中CAN_Tx及CAN_Rx使用普通的类似TTL逻辑信号,而CAN_High及CAN_Low是一对差分信号线,使用比较特别的差分信号,下一小节再详细说明。

    当CAN节点需要发送数据时,控制器把要发送的二进制编码通过CAN_Tx线发送到收发器,然后由收发器把这个普通的逻辑电平信号转化成差分信号,通过差分线CAN_High和CAN_Low线输出到CAN总线网络。而通过收发器接收总线上的数据到控制器时,则是相反的过程,收发器把总线上收到的CAN_High及CAN_Low信号转化成普通的逻辑电平信号,通过CAN_Rx输出到控制器中。

    例如,STM32的CAN片上外设就是通讯节点中的控制器,为了构成完整的节点,还要给它外接一个收发器,在我们实验板中使用型号为TJA1050的芯片作为CAN收发器。CAN控制器与CAN收发器的关系如同TTL串口与MAX3232电平转换芯片的关系,MAX3232芯片把TTL电平的串口信号转换成RS-232电平的串口信号,CAN收发器的作用则是把CAN控制器的TTL电平信号转换成差分信号(或者相反)。

    4.    差分信号

    差分信号又称差模信号,与传统使用单根信号线电压表示逻辑的方式有区别,使用差分信号传输时,需要两根信号线,这两个信号线的振幅相等,相位相反,通过两根信号线的电压差值来表示逻辑0和逻辑1。见图 403,它使用了V+与V-信号的差值表达出了图下方的信号。

    图 403 差分信号

    相对于单信号线传输的方式,使用差分信号传输具有如下优点:

        抗干扰能力强,当外界存在噪声干扰时,几乎会同时耦合到两条信号线上,而接收端只关心两个信号的差值,所以外界的共模噪声可以被完全抵消。

        能有效抑制它对外部的电磁干扰,同样的道理,由于两根信号的极性相反,他们对外辐射的电磁场可以相互抵消,耦合的越紧密,泄放到外界的电磁能量越少。

        时序定位精确,由于差分信号的开关变化是位于两个信号的交点,而不像普通单端信号依靠高低两个阈值电压判断,因而受工艺,温度的影响小,能降低时序上的误差,同时也更适合于低幅度信号的电路。

    由于差分信号线具有这些优点,所以在USB协议、485协议、以太网协议及CAN协议的物理层中,都使用了差分信号传输。

    5.    CAN协议中的差分信号

    CAN协议中对它使用的CAN_High及CAN_Low表示的差分信号做了规定,见表 401及图 404。以高速CAN协议为例,当表示逻辑1时(隐性电平),CAN_High和CAN_Low线上的电压均为2.5v,即它们的电压差VH-VL=0V;而表示逻辑0时(显性电平),CAN_High的电平为3.5V,CAN_Low线的电平为1.5V,即它们的电压差为VH-VL=2V。例如,当CAN收发器从CAN_Tx线接收到来自CAN控制器的低电平信号时(逻辑0),它会使CAN_High输出3.5V,同时CAN_Low输出1.5V,从而输出显性电平表示逻辑0。

    表 401 CAN协议标准表示的信号逻辑

    信号

    ISO11898(高速)

    ISO11519-2(低速)

    隐性(逻辑1)

    显性(逻辑0)

    隐性(逻辑1)

    显性(逻辑0)

    最小值

    典型值

    最大值

    最小值

    典型值

    最大值

    最小值

    典型值

    最大值

    最小值

    典型值

    最大值

    CAN_High(V)

    2.0

    2.5

    3.0

    2.75

    3.5

    4.5

    1.6

    1.75

    1.9

    3.85

    4.0

    5.0

    CAN_Low(V)

    2.0

    2.5

    3.0

    0.5

    1.5

    2.25

    3.10

    3.25

    3.4

    0

    1.0

    1.15

    High-Low电位差 (V)

    -0.5

    0

    0.05

    1.5

    2.0

    3.0

    -0.3

    -1.5

    -

    0.3

    3.0

    -

    图 404 CAN的差分信号(高速)

    在CAN总线中,必须使它处于隐性电平(逻辑1)或显性电平(逻辑0)中的其中一个状态。假如有两个CAN通讯节点,在同一时间,一个输出隐性电平,另一个输出显性电平,类似I2C总线的"线与"特性将使它处于显性电平状态,显性电平的名字就是这样来的,即可以认为显性具有优先的意味。

    由于CAN总线协议的物理层只有1对差分线,在一个时刻只能表示一个信号,所以对通讯节点来说,CAN通讯是半双工的,收发数据需要分时进行。在CAN的通讯网络中,因为共用总线,在整个网络中同一时刻只能有一个通讯节点发送信号,其余的节点在该时刻都只能接收。

    40.1.2 协议层

    以上是CAN的物理层标准,约定了电气特性,以下介绍的协议层则规定了通讯逻辑。

    1.    CAN的波特率及位同步

    由于CAN属于异步通讯,没有时钟信号线,连接在同一个总线网络中的各个节点会像串口异步通讯那样,节点间使用约定好的波特率进行通讯,特别地,CAN还会使用"位同步"的方式来抗干扰、吸收误差,实现对总线电平信号进行正确的采样,确保通讯正常。

    位时序分解

    为了实现位同步,CAN协议把每一个数据位的时序分解成如图 405所示的SS段、PTS段、PBS1段、PBS2段,这四段的长度加起来即为一个CAN数据位的长度。分解后最小的时间单位是Tq,而一个完整的位由8~25个Tq组成。为方便表示,图 405中的高低电平直接代表信号逻辑0或逻辑1(不是差分信号)。

    图 405 CAN位时序分解图

    该图中表示的CAN通讯信号每一个数据位的长度为19Tq,其中SS段占1Tq,PTS段占6Tq,PBS1段占5Tq,PBS2段占7Tq。信号的采样点位于PBS1段与PBS2段之间,通过控制各段的长度,可以对采样点的位置进行偏移,以便准确地采样。

    各段的作用如介绍下:

        SS段(SYNC SEG)

    SS译为同步段,若通讯节点检测到总线上信号的跳变沿被包含在SS段的范围之内,则表示节点与总线的时序是同步的,当节点与总线同步时,采样点采集到的总线电平即可被确定为该位的电平。SS段的大小固定为1Tq。

        PTS段(PROP SEG)

    PTS译为传播时间段,这个时间段是用于补偿网络的物理延时时间。是总线上输入比较器延时和输出驱动器延时总和的两倍。PTS段的大小可以为1~8Tq。

        PBS1段(PHASE SEG1),

    PBS1译为相位缓冲段,主要用来补偿边沿阶段的误差,它的时间长度在重新同步的时候可以加长。PBS1段的初始大小可以为1~8Tq。

        PBS2段(PHASE SEG2)

    PBS2这是另一个相位缓冲段,也是用来补偿边沿阶段误差的,它的时间长度在重新同步时可以缩短。PBS2段的初始大小可以为2~8Tq。

    通讯的波特率

    总线上的各个通讯节点只要约定好1个Tq的时间长度以及每一个数据位占据多少个Tq,就可以确定CAN通讯的波特率。

    例如,假设上图中的1Tq=1us,而每个数据位由19个Tq组成,则传输一位数据需要时间T1bit =19us,从而每秒可以传输的数据位个数为:

    1x106/19 = 52631.6 (bps)

    这个每秒可传输的数据位的个数即为通讯中的波特率。

    同步过程分析

        波特率只是约定了每个数据位的长度,数据同步还涉及到相位的细节,这个时候就需要用到数据位内的SS、PTS、PBS1及PBS2段了。

    根据对段的应用方式差异,CAN的数据同步分为硬同步和重新同步。其中硬同步只是当存在"帧起始信号"时起作用,无法确保后续一连串的位时序都是同步的,而重新同步方式可解决该问题,这两种方式具体介绍如下:

    (1)    硬同步

    若某个CAN节点通过总线发送数据时,它会发送一个表示通讯起始的信号(即下一小节介绍的帧起始信号),该信号是一个由高变低的下降沿。而挂载到CAN总线上的通讯节点在不发送数据时,会时刻检测总线上的信号。

    见图 406,可以看到当总线出现帧起始信号时,某节点检测到总线的帧起始信号不在节点内部时序的SS段范围,所以判断它自己的内部时序与总线不同步,因而这个状态的采样点采集得的数据是不正确的。所以节点以硬同步的方式调整,把自己的位时序中的SS段平移至总线出现下降沿的部分,获得同步,同步后采样点就可以采集得正确数据了。

    图 406 硬同步过程图

    (2)    重新同步

    前面的硬同步只是当存在帧起始信号时才起作用,如果在一帧很长的数据内,节点信号与总线信号相位有偏移时,这种同步方式就无能为力了。因而需要引入重新同步方式,它利用普通数据位的高至低电平的跳变沿来同步(帧起始信号是特殊的跳变沿)。重新同步与硬同步方式相似的地方是它们都使用SS段来进行检测,同步的目的都是使节点内的SS段把跳变沿包含起来。

    重新同步的方式分为超前和滞后两种情况,以总线跳变沿与SS段的相对位置进行区分。第一种相位超前的情况如图 407,节点从总线的边沿跳变中,检测到它内部的时序比总线的时序相对超前2Tq,这时控制器在下一个位时序中的PBS1段增加2Tq的时间长度,使得节点与总线时序重新同步。

    图 407 相位超前时的重新同步

    第二种相位滞后的情况如图 408,节点从总线的边沿跳变中,检测到它的时序比总线的时序相对滞后2Tq,这时控制器在前一个位时序中的PBS2段减少2Tq的时间长度,获得同步。

    图 408 相位滞后时的重新同步

    在重新同步的时候,PBS1和PBS2中增加或减少的这段时间长度被定义为"重新同步补偿宽度SJW (reSynchronization Jump Width)"。一般来说CAN控制器会限定SJW的最大值,如限定了最大SJW=3Tq时,单次同步调整的时候不能增加或减少超过3Tq的时间长度,若有需要,控制器会通过多次小幅度调整来实现同步。当控制器设置的SJW极限值较大时,可以吸收的误差加大,但通讯的速度会下降。

    2.    CAN的报文种类及结构

    在SPI通讯中,片选、时钟信号、数据输入及数据输出这4个信号都有单独的信号线,I2C协议包含有时钟信号及数据信号2条信号线,异步串口包含接收与发送2条信号线,这些协议包含的信号都比CAN协议要丰富,它们能轻易进行数据同步或区分数据传输方向。而CAN使用的是两条差分信号线,只能表达一个信号,简洁的物理层决定了CAN必然要配上一套更复杂的协议,如何用一个信号通道实现同样、甚至更强大的功能呢?CAN协议给出的解决方案是对数据、操作命令(如读/写)以及同步信号进行打包,打包后的这些内容称为报文。

    报文的种类

    在原始数据段的前面加上传输起始标签、片选(识别)标签和控制标签,在数据的尾段加上CRC校验标签、应答标签和传输结束标签,把这些内容按特定的格式打包好,就可以用一个通道表达各种信号了,各种各样的标签就如同SPI中各种通道上的信号,起到了协同传输的作用。当整个数据包被传输到其它设备时,只要这些设备按格式去解读,就能还原出原始数据,这样的报文就被称为CAN的"数据帧"。

    为了更有效地控制通讯,CAN一共规定了5种类型的帧,它们的类型及用途说明如表 402。

    表 402 帧的种类及其用途

    帧用途

    数据帧

    用于节点向外传送数据

    遥控帧

    用于向远端节点请求数据

    错误帧

    用于向远端节点通知校验错误,请求重新发送上一个数据

    过载帧

    用于通知远端节点:本节点尚未做好接收准备

    帧间隔

    用于将数据帧及遥控帧与前面的帧分离开来

    数据帧的结构

    数据帧是在CAN通讯中最主要、最复杂的报文,我们来了解它的结构,见图 409。

    图 409 数据帧的结构

    数据帧以一个显性位(逻辑0)开始,以7个连续的隐性位(逻辑1)结束,在它们之间,分别有仲裁段、控制段、数据段、CRC段和ACK段。

        帧起始

    SOF段(Start Of Frame),译为帧起始,帧起始信号只有一个数据位,是一个显性电平,它用于通知各个节点将有数据传输,其它节点通过帧起始信号的电平跳变沿来进行硬同步。

        仲裁段

    当同时有两个报文被发送时,总线会根据仲裁段的内容决定哪个数据包能被传输,这也是它名称的由来。

    仲裁段的内容主要为本数据帧的ID信息(标识符),数据帧具有标准格式和扩展格式两种,区别就在于ID信息的长度,标准格式的ID为11位,扩展格式的ID为29位,它在标准ID的基础上多出18位。在CAN协议中,ID起着重要的作用,它决定着数据帧发送的优先级,也决定着其它节点是否会接收这个数据帧。CAN协议不对挂载在它之上的节点分配优先级和地址,对总线的占有权是由信息的重要性决定的,即对于重要的信息,我们会给它打包上一个优先级高的ID,使它能够及时地发送出去。也正因为它这样的优先级分配原则,使得CAN的扩展性大大加强,在总线上增加或减少节点并不影响其它设备。

    报文的优先级,是通过对ID的仲裁来确定的。根据前面对物理层的分析我们知道如果总线上同时出现显性电平和隐性电平,总线的状态会被置为显性电平,CAN正是利用这个特性进行仲裁。

    若两个节点同时竞争CAN总线的占有权,当它们发送报文时,若首先出现隐性电平,则会失去对总线的占有权,进入接收状态。见图 4010,在开始阶段,两个设备发送的电平一样,所以它们一直继续发送数据。到了图中箭头所指的时序处,节点单元1发送的为隐性电平,而此时节点单元2发送的为显性电平,由于总线的"线与"特性使它表达出显示电平,因此单元2竞争总线成功,这个报文得以被继续发送出去。

    图 4010 仲裁过程

    仲裁段ID的优先级也影响着接收设备对报文的反应。因为在CAN总线上数据是以广播的形式发送的,所有连接在CAN总线的节点都会收到所有其它节点发出的有效数据,因而我们的CAN控制器大多具有根据ID过滤报文的功能,它可以控制自己只接收某些ID的报文。

    回看图 409中的数据帧格式,可看到仲裁段除了报文ID外,还有RTR、IDE和SRR位。

    (1)    RTR位(Remote Transmission Request Bit),译作远程传输请求位,它是用于区分数据帧和遥控帧的,当它为显性电平时表示数据帧,隐性电平时表示遥控帧。

    (2)    IDE位(Identifier Extension Bit),译作标识符扩展位,它是用于区分标准格式与扩展格式,当它为显性电平时表示标准格式,隐性电平时表示扩展格式。

    (3)    SRR位(Substitute Remote Request Bit),只存在于扩展格式,它用于替代标准格式中的RTR位。由于扩展帧中的SRR位为隐性位,RTR在数据帧为显性位,所以在两个ID相同的标准格式报文与扩展格式报文中,标准格式的优先级较高。

        控制段

    在控制段中的r1和r0为保留位,默认设置为显性位。它最主要的是DLC段(Data Length Code),译为数据长度码,它由4个数据位组成,用于表示本报文中的数据段含有多少个字节,DLC段表示的数字为0~8。

        数据段

    数据段为数据帧的核心内容,它是节点要发送的原始信息,由0~8个字节组成,MSB先行。

        CRC段

    为了保证报文的正确传输,CAN的报文包含了一段15位的CRC校验码,一旦接收节点算出的CRC码跟接收到的CRC码不同,则它会向发送节点反馈出错信息,利用错误帧请求它重新发送。CRC部分的计算一般由CAN控制器硬件完成,出错时的处理则由软件控制最大重发数。

    在CRC校验码之后,有一个CRC界定符,它为隐性位,主要作用是把CRC校验码与后面的ACK段间隔起来。

        ACK段

    ACK段包括一个ACK槽位,和ACK界定符位。类似I2C总线,在ACK槽位中,发送节点发送的是隐性位,而接收节点则在这一位中发送显性位以示应答。在ACK槽和帧结束之间由ACK界定符间隔开。

        帧结束

    EOF段(End Of Frame),译为帧结束,帧结束段由发送节点发送的7个隐性位表示结束。

    其它报文的结构

    关于其它的CAN报文结构,不再展开讲解,其主要内容见图 4011。

    图 4011 各种CAN报文的结构

    40.2 STM32的CAN外设简介

    STM32的芯片中具有bxCAN控制器 (Basic Extended CAN),它支持CAN协议2.0A和2.0B标准。

    该CAN控制器支持最高的通讯速率为1Mb/s;可以自动地接收和发送CAN报文,支持使用标准ID和扩展ID的报文;外设中具有3个发送邮箱,发送报文的优先级可以使用软件控制,还可以记录发送的时间;具有2个3级深度的接收FIFO,可使用过滤功能只接收或不接收某些ID号的报文;可配置成自动重发;不支持使用DMA进行数据收发。

    40.2.1 STM32的CAN架构剖析

    图 4012 STM32的CAN外设架构图

    STM32的有两组CAN控制器,其中CAN1是主设备,框图中的"存储访问控制器"是由CAN1控制的,CAN2无法直接访问存储区域,所以使用CAN2的时候必须使能CAN1外设的时钟。框图中主要包含CAN控制内核、发送邮箱、接收FIFO以及验收筛选器,下面对框图中的各个部分进行介绍。

    1.    CAN控制内核

    框图中标号处的CAN控制内核包含了各种控制寄存器及状态寄存器,我们主要讲解其中的主控制寄存器CAN_MCR及位时序寄存器CAN_BTR。

    主控制寄存器CAN_MCR

    主控制寄存器CAN_MCR负责管理CAN的工作模式,它使用以下寄存器位实现控制。

    (1)    DBF调试冻结功能

    DBF(Debug freeze)调试冻结,使用它可设置CAN处于工作状态或禁止收发的状态,禁止收发时仍可访问接收FIFO中的数据。这两种状态是当STM32芯片处于程序调试模式时才使用的,平时使用并不影响。

    (2)    TTCM时间触发模式

    TTCM(Time triggered communication mode)时间触发模式,它用于配置CAN的时间触发通信模式,在此模式下,CAN使用它内部定时器产生时间戳,并把它保存在CAN_RDTxR、CAN_TDTxR寄存器中。内部定时器在每个CAN位时间累加,在接收和发送的帧起始位被采样,并生成时间戳。利用它可以实现ISO 11898-4 CAN标准的分时同步通信功能。

    (3)    ABOM自动离线管理

    ABOM(Automatic bus-off management) 自动离线管理,它用于设置是否使用自动离线管理功能。当节点检测到它发送错误或接收错误超过一定值时,会自动进入离线状态,在离线状态中,CAN不能接收或发送报文。处于离线状态的时候,可以软件控制恢复或者直接使用这个自动离线管理功能,它会在适当的时候自动恢复。

    (4)    AWUM自动唤醒

    AWUM(Automatic bus-off management),自动唤醒功能,CAN外设可以使用软件进入低功耗的睡眠模式,如果使能了这个自动唤醒功能,当CAN检测到总线活动的时候,会自动唤醒。

    (5)    NART自动重传

    NART(No automatic retransmission)报文自动重传功能,设置这个功能后,当报文发送失败时会自动重传至成功为止。若不使用这个功能,无论发送结果如何,消息只发送一次。

    (6)    RFLM锁定模式

    RFLM(Receive FIFO locked mode)FIFO锁定模式,该功能用于锁定接收FIFO。锁定后,当接收FIFO溢出时,会丢弃下一个接收的报文。若不锁定,则下一个接收到的报文会覆盖原报文。

    (7)    TXFP报文发送优先级的判定方法

    TXFP(Transmit FIFO priority)报文发送优先级的判定方法,当CAN外设的发送邮箱中有多个待发送报文时,本功能可以控制它是根据报文的ID优先级还是报文存进邮箱的顺序来发送。

    位时序寄存器(CAN_BTR)及波特率

    CAN外设中的位时序寄存器CAN_BTR用于配置测试模式、波特率以及各种位内的段参数。

    (1)    测试模式

    为方便调试,STM32的CAN提供了测试模式,配置位时序寄存器CAN_BTR的SILM及LBKM寄存器位可以控制使用正常模式、静默模式、回环模式及静默回环模式,见图 4013。

    图 4013 四种工作模式

    各个工作模式介绍如下:

        正常模式

    正常模式下就是一个正常的CAN节点,可以向总线发送数据和接收数据。

        静默模式

    静默模式下,它自己的输出端的逻辑0数据会直接传输到它自己的输入端,逻辑1可以被发送到总线,所以它不能向总线发送显性位(逻辑0),只能发送隐性位(逻辑1)。输入端可以从总线接收内容。由于它只可发送的隐性位不会强制影响总线的状态,所以把它称为静默模式。这种模式一般用于监测,它可以用于分析总线上的流量,但又不会因为发送显性位而影响总线。

        回环模式

    回环模式下,它自己的输出端的所有内容都直接传输到自己的输入端,输出端的内容同时也会被传输到总线上,即也可使用总线监测它的发送内容。输入端只接收自己发送端的内容,不接收来自总线上的内容。使用回环模式可以进行自检。

        回环静默模式

    回环静默模式是以上两种模式的结合,自己的输出端的所有内容都直接传输到自己的输入端,并且不会向总线发送显性位影响总线,不能通过总线监测它的发送内容。输入端只接收自己发送端的内容,不接收来自总线上的内容。这种方式可以在"热自检"时使用,即自我检查的时候,不会干扰总线。

    以上说的各个模式,是不需要修改硬件接线的,如当输出直连输入时,它是在STM32芯片内部连接的,传输路径不经过STM32的CAN_Tx/Rx引脚,更不经过外部连接的CAN收发器,只有输出数据到总线或从总线接收的情况下才会经过CAN_Tx/Rx引脚和收发器。

    (2)    位时序及波特率

    STM32外设定义的位时序与我们前面解释的CAN标准时序有一点区别,见图 4014。

    图 4014 STM32中CAN的位时序

    STM32的CAN外设位时序中只包含3段,分别是同步段SYNC_SEG、位段BS1及位段BS2,采样点位于BS1及BS2段的交界处。其中SYNC_SEG段固定长度为1Tq,而BS1及BS2段可以在位时序寄存器CAN_BTR设置它们的时间长度,它们可以在重新同步期间增长或缩短,该长度SJW也可在位时序寄存器中配置。

    理解STM32的CAN外设的位时序时,可以把它的BS1段理解为是由前面介绍的CAN标准协议中PTS段与PBS1段合在一起的,而BS2段就相当于PBS2段。

    了解位时序后,我们就可以配置波特率了。通过配置位时序寄存器CAN_BTR的TS1[3:0]及TS2[2:0]寄存器位设定BS1及BS2段的长度后,我们就可以确定每个CAN数据位的时间:

    BS1段时间:

    TS1=Tq x (TS1[3:0] + 1),

    BS2段时间:

    TS2= Tq x (TS2[2:0] + 1),

    一个数据位的时间:

    T1bit =1Tq+TS1+TS2 =1+ (TS1[3:0] + 1)+ (TS2[2:0] + 1)= N Tq

    其中单个时间片的长度Tq与CAN外设的所挂载的时钟总线及分频器配置有关,CAN1和CAN2外设都是挂载在APB1总线上的,而位时序寄存器CAN_BTR中的BRP[9:0]寄存器位可以设置CAN外设时钟的分频值,所以:

    Tq = (BRP[9:0]+1) x TPCLK

    其中的PCLK指APB1时钟,默认值为45MHz。

    最终可以计算出CAN通讯的波特率:

    BaudRate = 1/N Tq

    例如表 403说明了一种把波特率配置为1Mbps的方式。

    表 403 一种配置波特率为1Mbps的方式

    参数

    说明

    SYNC_SE段

    固定为1Tq

    BS1段

    设置为5Tq (实际写入TS1[3:0]的值为4)

    BS2段

    设置为3Tq (实际写入TS2[2:0]的值为2)

    TPCLK

    APB1按默认配置为F=45MHz,TPCLK=1/45M

    CAN外设时钟分频

    设置为5分频(实际写入BRP[9:0]的值为4)

    1Tq时间长度

    Tq = (BRP[9:0]+1) x TPCLK     =     5 x 1/45M=1/9M

    1位的时间长度

    T1bit =1Tq+TS1+TS2             =     1+5+3 = 9Tq

    波特率

    BaudRate = 1/N Tq            =    1/(1/9M x 9)=1Mbps

    2.    CAN发送邮箱

    回到图 245中的CAN外设框图,在标号‚处的是CAN外设的发送邮箱,它一共有3个发送邮箱,即最多可以缓存3个待发送的报文。

    每个发送邮箱中包含有标识符寄存器CAN_TIxR、数据长度控制寄存器CAN_TDTxR及2个数据寄存器CAN_TDLxR、CAN_TDHxR,它们的功能见表 405。

    表 404 发送邮箱的寄存器

    寄存器名

    功能

    标识符寄存器CAN_TIxR

    存储待发送报文的ID、扩展ID、IDE位及RTR位

    数据长度控制寄存器CAN_TDTxR

    存储待发送报文的DLC段

    低位数据寄存器CAN_TDLxR

    存储待发送报文数据段的Data0-Data3这四个字节的内容

    高位数据寄存器CAN_TDHxR

    存储待发送报文数据段的Data4-Data7这四个字节的内容

    当我们要使用CAN外设发送报文时,把报文的各个段分解,按位置写入到这些寄存器中,并对标识符寄存器CAN_TIxR中的发送请求寄存器位TMIDxR_TXRQ置1,即可把数据发送出去。

    其中标识符寄存器CAN_TIxR中的STDID寄存器位比较特别。我们知道CAN的标准标识符的总位数为11位,而扩展标识符的总位数为29位的。当报文使用扩展标识符的时候,标识符寄存器CAN_TIxR中的STDID[10:0]等效于EXTID[18:28]位,它与EXTID[17:0]共同组成完整的29位扩展标识符。

    3.    CAN接收FIFO

    图 245中的CAN外设框图,在标号ƒ处的是CAN外设的接收FIFO,它一共有2个接收FIFO,每个FIFO中有3个邮箱,即最多可以缓存6个接收到的报文。当接收到报文时,FIFO的报文计数器会自增,而STM32内部读取FIFO数据之后,报文计数器会自减,我们通过状态寄存器可获知报文计数器的值,而通过前面主控制寄存器的RFLM位,可设置锁定模式,锁定模式下FIFO溢出时会丢弃新报文,非锁定模式下FIFO溢出时新报文会覆盖旧报文。

    跟发送邮箱类似,每个接收FIFO中包含有标识符寄存器CAN_RIxR、数据长度控制寄存器CAN_RDTxR及2个数据寄存器CAN_RDLxR、CAN_RDHxR,它们的功能见表 405。

    表 405 发送邮箱的寄存器

    寄存器名

    功能

    标识符寄存器CAN_RIxR

    存储收到报文的ID、扩展ID、IDE位及RTR位

    数据长度控制寄存器CAN_RDTxR

    存储收到报文的DLC段

    低位数据寄存器CAN_RDLxR

    存储收到报文数据段的Data0-Data3这四个字节的内容

    高位数据寄存器CAN_RDHxR

    存储收到报文数据段的Data4-Data7这四个字节的内容

    通过中断或状态寄存器知道接收FIFO有数据后,我们再读取这些寄存器的值即可把接收到的报文加载到STM32的内存中。

    4.    验收筛选器

    图 245中的CAN外设框图,在标号„处的是CAN外设的验收筛选器,一共有28个筛选器组,每个筛选器组有2个寄存器,CAN1和CAN2共用的筛选器的。

    在CAN 协议中,消息的标识符与节点地址无关,但与消息内容有关。因此,发送节点将报文广播给所有接收器时,接收节点会根据报文标识符的值来确定软件是否需要该消息,为了简化软件的工作,STM32的CAN外设接收报文前会先使用验收筛选器检查,只接收需要的报文到FIFO中。

    筛选器工作的时候,可以调整筛选ID的长度及过滤模式。根据筛选ID长度来分类有有以下两种:

    (1)    检查STDID[10:0]、EXTID[17:0]、IDE 和RTR 位,一共31位。

    (2)    检查STDID[10:0]、RTR、IDE 和EXTID[17:15],一共16位。

    通过配置筛选尺度寄存器CAN_FS1R的FSCx位可以设置筛选器工作在哪个尺度。

    而根据过滤的方法分为以下两种模式:

    (1)    标识符列表模式,它把要接收报文的ID列成一个表,要求报文ID与列表中的某一个标识符完全相同才可以接收,可以理解为白名单管理。

    (2)    掩码模式,它把可接收报文ID的某几位作为列表,这几位被称为掩码,可以把它理解成关键字搜索,只要掩码(关键字)相同,就符合要求,报文就会被保存到接收FIFO。

    通过配置筛选模式寄存器CAN_FM1R的FBMx位可以设置筛选器工作在哪个模式。

    不同的尺度和不同的过滤方法可使筛选器工作在图 4015的4种状态。

    图 4015 筛选器的4种工作状态

    每组筛选器包含2个32位的寄存器,分别为CAN_FxR1和CAN_FxR2,它们用来存储要筛选的ID或掩码,各个寄存器位代表的意义与图中两个寄存器下面"映射"的一栏一致,各个模式的说明见表 406。

    表 406 筛选器的工作状态说明

    模式

    说明

    32位掩码模式

    CAN_FxR1存储ID,CAN_FxR2存储哪个位必须要与CAN_FxR1中的ID一致,2个寄存器表示1组掩码。

    32位标识符模式

    CAN_FxR1和CAN_FxR2各存储1个ID,2个寄存器表示2个筛选的ID

    16位掩码模式

    CAN_FxR1高16位存储ID,低16位存储哪个位必须要与高16位的ID一致;

    CAN_FxR2高16位存储ID,低16位存储哪个位必须要与高16位的ID一致

    2个寄存器表示2组掩码。

    16位标识符模式

    CAN_FxR1和CAN_FxR2各存储2个ID,2个寄存器表示4个筛选的ID

    例如下面的表格所示,在掩码模式时,第一个寄存器存储要筛选的ID,第二个寄存器存储掩码,掩码为1的部分表示该位必须与ID中的内容一致,筛选的结果为表中第三行的ID值,它是一组包含多个的ID值,其中x表示该位可以为1可以为0。

    ID

    1

    0

    1

    1

    1

    0

    1

    掩码

    1

    1

    1

    0

    0

    1

    0

    筛选的ID

    1

    0

    1

    x

    x

    0

    x

    而工作在标识符模式时,2个寄存器存储的都是要筛选的ID,它只包含2个要筛选的ID值(32位模式时)。

    如果使能了筛选器,且报文的ID与所有筛选器的配置都不匹配,CAN外设会丢弃该报文,不存入接收FIFO。

    5.    整体控制逻辑

    回到图 245结构框图,图中的标号…处表示的是CAN2外设的结构,它与CAN1外设是一样的,他们共用筛选器且由于存储访问控制器由CAN1控制,所以要使用CAN2的时候必须要使能CAN1的时钟。

    40.3 CAN初始化结构体

    从STM32的CAN外设我们了解到它的功能非常多,控制涉及的寄存器也非常丰富,而使用STM32标准库提供的各种结构体及库函数可以简化这些控制过程。跟其它外设一样,STM32标准库提供了CAN初始化结构体及初始化函数来控制CAN的工作方式,提供了收发报文使用的结构体及收发函数,还有配置控制筛选器模式及ID的结构体。这些内容都定义在库文件"stm32f4xx_can.h"及"stm32f4xx_can.c"中,编程时我们可以结合这两个文件内的注释使用或参考库帮助文档。

    首先我们来学习初始化结构体的内容,见代码清单 241。

    代码清单 401 CAN初始化结构体

    1 /**

    2 * @brief CAN 初始化结构体

    3 */

    4 typedef struct {

    5 uint16_t CAN_Prescaler; /*配置CAN外设的时钟分频,可设置为1-1024*/

    6 uint8_t CAN_Mode; /*配置CAN的工作模式,回环或正常模式*/

    7 uint8_t CAN_SJW; /*配置SJW极限值 */

    8 uint8_t CAN_BS1; /*配置BS1段长度*/

    9 uint8_t CAN_BS2; /*配置BS2段长度 */

    10 FunctionalState CAN_TTCM; /*是否使能TTCM时间触发功能*/

    11 FunctionalState CAN_ABOM; /*是否使能ABOM自动离线管理功能*/

    12 FunctionalState CAN_AWUM; /*是否使能AWUM自动唤醒功能 */

    13 FunctionalState CAN_NART; /*是否使能NART自动重传功能*/

    14 FunctionalState CAN_RFLM; /*是否使能RFLM锁定FIFO功能*/

    15 FunctionalState CAN_TXFP; /*配置TXFP报文优先级的判定方法*/

    16 } CAN_InitTypeDef;

    这些结构体成员说明如下,其中括号内的文字是对应参数在STM32标准库中定义的宏,这些结构体成员都是"40.2.1 1CAN控制内核"小节介绍的内容,可对比阅读:

    (1)    CAN_Prescaler

    本成员设置CAN外设的时钟分频,它可控制时间片Tq的时间长度,这里设置的值最终会减1后再写入BRP寄存器位,即前面介绍的Tq计算公式:

    Tq = (BRP[9:0]+1) x TPCLK

    等效于:Tq = CAN_Prescaler x TPCLK

    (2)    CAN_Mode

    本成员设置CAN的工作模式,可设置为正常模式(CAN_Mode_Normal)、回环模式(CAN_Mode_LoopBack)、静默模式(CAN_Mode_Silent)以及回环静默模式(CAN_Mode_Silent_LoopBack)。

    (3)    CAN_SJW

    本成员可以配置SJW的极限长度,即CAN重新同步时单次可增加或缩短的最大长度,它可以被配置为1-4Tq(CAN_SJW_1/2/3/4tq)。

    (4)    CAN_BS1

    本成员用于设置CAN位时序中的BS1段的长度,它可以被配置为1-16个Tq长度(CAN_BS1_1/2/3…16tq)。

    (5)    CAN_BS2

    本成员用于设置CAN位时序中的BS2段的长度,它可以被配置为1-8个Tq长度(CAN_BS2_1/2/3…8tq)。

    SYNC_SEG、BS1段及BS2段的长度加起来即一个数据位的长度,即前面介绍的原来计算公式:

    T1bit =1Tq+TS1+TS2 =1+ (TS1[3:0] + 1)+ (TS2[2:0] + 1)

    等效于:T1bit = 1Tq+CAN_BS1+CAN_BS2

    (6)    CAN_TTCM

    本成员用于设置是否使用时间触发功能(ENABLE/DISABLE),时间触发功能在某些CAN标准中会使用到。

    (7)    CAN_ABOM

    本成员用于设置是否使用自动离线管理(ENABLE/DISABLE),使用自动离线管理可以在节点出错离线后适时自动恢复,不需要软件干预。

    (8)    CAN_ AWUM

    本成员用于设置是否使用自动唤醒功能(ENABLE/DISABLE),使能自动唤醒功能后它会在监测到总线活动后自动唤醒。

    (9)    CAN_ABOM

    本成员用于设置是否使用自动离线管理功能(ENABLE/DISABLE),使用自动离线管理可以在出错时离线后适时自动恢复,不需要软件干预。

    (10)    CAN_NART

    本成员用于设置是否使用自动重传功能(ENABLE/DISABLE),使用自动重传功能时,会一直发送报文直到成功为止。

    (11)    CAN_RFLM

    本成员用于设置是否使用锁定接收FIFO(ENABLE/DISABLE),锁定接收FIFO后,若FIFO溢出时会丢弃新数据,否则在FIFO溢出时以新数据覆盖旧数据。

    (12)    CAN_TXFP

    本成员用于设置发送报文的优先级判定方法(ENABLE/DISABLE),使能时,以报文存入发送邮箱的先后顺序来发送,否则按照报文ID的优先级来发送。

     

    配置完这些结构体成员后,我们调用库函数CAN_Init即可把这些参数写入到CAN控制寄存器中,实现CAN的初始化。

    40.4 CAN发送及接收结构体

    在发送或接收报文时,需要往发送邮箱中写入报文信息或从接收FIFO中读取报文信息,利用STM32标准库的发送及接收结构体可以方便地完成这样的工作,它们的定义见代码清单 402。

    代码清单 402 CAN发送及接收结构体

    1 /**

    2 * @brief CAN Tx message structure definition

    3 * 发送结构体

    4 */

    5 typedef struct {

    6 uint32_t StdId; /*存储报文的标准标识符11位,0-0x7FF. */

    7 uint32_t ExtId; /*存储报文的扩展标识符29位,0-0x1FFFFFFF. */

    8 uint8_t IDE; /*存储IDE扩展标志 */

    9 uint8_t RTR; /*存储RTR远程帧标志*/

    10 uint8_t DLC; /*存储报文数据段的长度,0-8 */

    11 uint8_t Data[8]; /*存储报文数据段的内容 */

    12 } CanTxMsg;

    13

    14 /**

    15 * @brief CAN Rx message structure definition

    16 * 接收结构体

    17 */

    18 typedef struct {

    19 uint32_t StdId; /*存储了报文的标准标识符11位,0-0x7FF. */

    20 uint32_t ExtId; /*存储了报文的扩展标识符29位,0-0x1FFFFFFF. */

    21 uint8_t IDE; /*存储了IDE扩展标志 */

    22 uint8_t RTR; /*存储了RTR远程帧标志*/

    23 uint8_t DLC; /*存储了报文数据段的长度,0-8 */

    24 uint8_t Data[8]; /*存储了报文数据段的内容 */

    25 uint8_t FMI; /*存储了本报文是由经过筛选器存储进FIFO的,0-0xFF */

    26 } CanRxMsg;

    这些结构体成员都是"40.2.1 2CAN发送邮箱及CAN接收FIFO"小节介绍的内容,可对比阅读,发送结构体与接收结构体是类似的,只是接收结构体多了一个FMI成员,说明如下:

    (1)    StdId

    本成员存储的是报文的11位标准标识符,范围是0-0x7FF。

    (2)    ExtId

    本成员存储的是报文的29位扩展标识符,范围是0-0x1FFFFFFF。ExtId与StdId这两个成员根据下面的IDE位配置,只有一个是有效的。

    (3)    IDE

    本成员存储的是扩展标志IDE位,当它的值为宏CAN_ID_STD时表示本报文是标准帧,使用StdId成员存储报文ID;当它的值为宏CAN_ID_EXT时表示本报文是扩展帧,使用ExtId成员存储报文ID。

    (4)    RTR

    本成员存储的是报文类型标志RTR位,当它的值为宏CAN_RTR_Data时表示本报文是数据帧;当它的值为宏CAN_RTR_Remote时表示本报文是遥控帧,由于遥控帧没有数据段,所以当报文是遥控帧时,下面的Data[8]成员的内容是无效的。

    (5)    DLC

    本成员存储的是数据帧数据段的长度,它的值的范围是0-8,当报文是遥控帧时DLC值为0。

    (6)    Data[8]

    本成员存储的就是数据帧中数据段的数据。

    (7)    FMI

    本成员只存在于接收结构体,它存储了筛选器的编号,表示本报文是经过哪个筛选器存储进接收FIFO的,可以用它简化软件处理。

     

    当需要使用CAN发送报文时,先定义一个上面发送类型的结构体,然后把报文的内容按成员赋值到该结构体中,最后调用库函数CAN_Transmit把这些内容写入到发送邮箱即可把报文发送出去。

    接收报文时,通过检测标志位获知接收FIFO的状态,若收到报文,可调用库函数CAN_Receive把接收FIFO中的内容读取到预先定义的接收类型结构体中,然后再访问该结构体即可利用报文了。

    40.5 CAN筛选器结构体

    CAN的筛选器有多种工作模式,利用筛选器结构体可方便配置,它的定义见代码清单 403。

    代码清单 403 CAN筛选器结构体

    1 /**

    2 * @brief CAN filter init structure definition

    3 * CAN筛选器结构体

    4 */

    5 typedef struct {

    6 uint16_t CAN_FilterIdHigh; /*CAN_FxR1寄存器的高16位 */

    7 uint16_t CAN_FilterIdLow; /*CAN_FxR1寄存器的低16位*/

    8 uint16_t CAN_FilterMaskIdHigh; /*CAN_FxR2寄存器的高16位*/

    9 uint16_t CAN_FilterMaskIdLow; /*CAN_FxR2寄存器的低16位 */

    10 uint16_t CAN_FilterFIFOAssignment; /*设置经过筛选后数据存储到哪个接收FIFO */

    11 uint8_t CAN_FilterNumber; /*筛选器编号,范围0-27*/

    12 uint8_t CAN_FilterMode; /*筛选器模式 */

    13 uint8_t CAN_FilterScale; /*设置筛选器的尺度 */

    14 FunctionalState CAN_FilterActivation; /*是否使能本筛选器*/

    15 } CAN_FilterInitTypeDef;

    这些结构体成员都是"40.2.1 4验收筛选器"小节介绍的内容,可对比阅读,各个结构体成员的介绍如下:

    (1)    CAN_FilterIdHigh

    CAN_FilterIdHigh成员用于存储要筛选的ID,若筛选器工作在32位模式,它存储的是所筛选ID的高16位;若筛选器工作在16位模式,它存储的就是一个完整的要筛选的ID。

    (2)    CAN_FilterIdLow

    类似地,CAN_FilterIdLow成员也是用于存储要筛选的ID,若筛选器工作在32位模式,它存储的是所筛选ID的低16位;若筛选器工作在16位模式,它存储的就是一个完整的要筛选的ID。

    (3)    CAN_FilterMaskIdHigh

    CAN_FilterMaskIdHigh存储的内容分两种情况,当筛选器工作在标识符列表模式时,它的功能与CAN_FilterIdHigh相同,都是存储要筛选的ID;而当筛选器工作在掩码模式时,它存储的是CAN_FilterIdHigh成员对应的掩码,与CAN_FilterIdLow组成一组筛选器。

    (4)    CAN_FilterMaskIdLow

    类似地,CAN_FilterMaskIdLow存储的内容也分两种情况,当筛选器工作在标识符列表模式时,它的功能与CAN_FilterIdLow相同,都是存储要筛选的ID;而当筛选器工作在掩码模式时,它存储的是CAN_FilterIdLow成员对应的掩码,与CAN_FilterIdLow组成一组筛选器。

    上面四个结构体的存储的内容很容易让人糊涂,请结合前面的图 4014和下面的表 407理解,如果还搞不清楚,再结合库函数CAN_FilterInit的源码来分析。

    表 407 不同模式下各结构体成员的内容

    模式

    CAN_FilterIdHigh

    CAN_FilterIdLow

    CAN_FilterMaskIdHigh

    CAN_FilterMaskIdLow

    32位列表模式

    ID1的高16位

    ID1的低16位

    ID2的高16位

    ID2的低16位

    16位列表模式

    ID1的完整数值

    ID2的完整数值

    ID3的完整数值

    ID4的完整数值

    32位掩码模式

    ID1的高16位

    ID1的低16位

    ID1掩码的高16位

    ID1掩码的低16位

    16位掩码模式

    ID1的完整数值

    ID2的完整数值

    ID1掩码的完整数值

    ID2掩码完整数值

    对这些结构体成员赋值的时候,还要注意寄存器位的映射,即注意哪部分代表STID,哪部分代表EXID以及IDE、RTR位。

    (5)    CAN_FilterFIFOAssignment

    本成员用于设置当报文通过筛选器的匹配后,该报文会被存储到哪一个接收FIFO,它的可选值为FIFO0或FIFO1(宏CAN_Filter_FIFO0/1)。

    (6)    CAN_FilterNumber

    本成员用于设置筛选器的编号,即本过滤器结构体配置的是哪一组筛选器,CAN一共有28个筛选器,所以它的可输入参数范围为0-27。

    (7)    CAN_FilterMode

    本成员用于设置筛选器的工作模式,可以设置为列表模式(宏CAN_FilterMode_IdList)及掩码模式(宏CAN_FilterMode_IdMask)。

    (8)    CAN_FilterScale

    本成员用于设置筛选器的尺度,可以设置为32位长(宏CAN_FilterScale_32bit)及16位长(宏CAN_FilterScale_16bit)。

    (9)    CAN_FilterActivation

    本成员用于设置是否激活这个筛选器(宏ENABLE/DISABLE)。

     

    配置完这些结构体成员后,我们调用库函数CAN_FilterInit即可把这些参数写入到筛选控制寄存器中,从而使用筛选器。我们前面说如果不理解那几个ID结构体成员存储的内容时,可以直接阅读库函数CAN_FilterInit的源代码理解,就是因为它直接对寄存器写入内容,代码的逻辑是非常清晰的。

    40.6 CAN—双机通讯实验

    本小节演示如何使用STM32的CAN外设实现两个设备之间的通讯,该实验中使用了两个实验板,如果您只有一个实验板,也可以使用CAN的回环模式进行测试,不影响学习的。为此,我们提供了"CAN—双机通讯"及"CAN—回环测试"两个工程,可根据自己的实验环境选择相应的工程来学习。这两个工程的主体都是一样的,本教程主要以"CAN—双机通讯"工程进行讲解。

    40.6.1 硬件设计

    图 4016 双CAN通讯实验硬件连接图

    图 4016中的是两个实验板的硬件连接。在单个实验板中,作为CAN控制器的STM32引出CAN_Tx和CAN_Rx两个引脚与CAN收发器TJA1050相连,收发器使用CANH及CANL引脚连接到CAN总线网络中。为了方便使用,我们每个实验板引出的CANH及CANL都连接了1个120欧的电阻作为CAN总线的端电阻,所以要注意如果您要把实验板作为一个普通节点连接到现有的CAN总线时,是不应添加该电阻的!

    要实现通讯,我们还要使用导线把实验板引出的CANH及CANL两条总线连接起来,才能构成完整的网络。实验板之间CANH1与CANH2连接,CANL1与CANL2连接即可。

    要注意的是,由于我们的实验板CAN使用的信号线与液晶屏共用了,为防止干扰,平时我们默认是不给CAN收发器供电的,使用CAN的时候一定要把CAN接线端子旁边的"C/4-5V"排针使用跳线帽与"5V"排针连接起来进行供电,并且把液晶屏从板子上拔下来。

    如果您使用的是单机回环测试的工程实验,就不需要使用导线连接板子了,而且也不需要给收发器供电,因为回环模式的信号是不经过收发器的,不过,它还是不能和液晶屏同时使用的。

    40.6.2 软件设计

    为了使工程更加有条理,我们把CAN控制器相关的代码独立分开存储,方便以后移植。在"串口实验"之上新建"bsp_can.c"及"bsp_can.h"文件,这些文件也可根据您的喜好命名,它们不属于STM32标准库的内容,是由我们自己根据应用需要编写的。

    1.    编程要点

    (1)    初始化CAN通讯使用的目标引脚及端口时钟;

    (2)    使能CAN外设的时钟;

    (3)    配置CAN外设的工作模式、位时序以及波特率;

    (4)    配置筛选器的工作方式;

    (5)    编写测试程序,收发报文并校验。

    2.    代码分析

    CAN硬件相关宏定义

    我们把CAN硬件相关的配置都以宏的形式定义到"bsp_can.h"文件中,见代码清单 242。

    代码清单 404 CAN硬件配置相关的宏(bsp_can.h文件)

    1

    2 /*CAN硬件相关的定义*/

    3 #define CANx CAN1

    4 #define CAN_CLK RCC_APB1Periph_CAN1

    5 /*接收中断号*/

    6 #define CAN_RX_IRQ CAN1_RX0_IRQn

    7 /*接收中断服务函数*/

    8 #define CAN_RX_IRQHandler CAN1_RX0_IRQHandler

    9

    10 /*引脚*/

    11 #define CAN_RX_PIN GPIO_Pin_8

    12 #define CAN_TX_PIN GPIO_Pin_9

    13 #define CAN_TX_GPIO_PORT GPIOB

    14 #define CAN_RX_GPIO_PORT GPIOB

    15 #define CAN_TX_GPIO_CLK RCC_AHB1Periph_GPIOB

    16 #define CAN_RX_GPIO_CLK RCC_AHB1Periph_GPIOB

    17 #define CAN_AF_PORT GPIO_AF_CAN1

    18 #define CAN_RX_SOURCE GPIO_PinSource8

    19 #define CAN_TX_SOURCE GPIO_PinSource9

    以上代码根据硬件连接,把与CAN通讯使用的CAN号、引脚号、引脚源以及复用功能映射都以宏封装起来,并且定义了接收中断的中断向量和中断服务函数,我们通过中断来获知接收FIFO的信息。

    初始化CAN的 GPIO

    利用上面的宏,编写CAN的初始化函数,见代码清单 243。

    代码清单 405 CAN的GPIO初始化函数(bsp_can.c文件)

     
    1. 1 /*

    2.  

    2 * 函数名:CAN_GPIO_Config

    3 * 描述:CAN的GPIO 配置

    4 * 输入:无

    5 * 输出 : 无

    6 * 调用:内部调用

    7 */

    8 static void CAN_GPIO_Config(void)

    9 {

    10 GPIO_InitTypeDef GPIO_InitStructure;

    11

    12 /* 使能GPIO时钟*/

    13 RCC_AHB1PeriphClockCmd(CAN_TX_GPIO_CLK|CAN_RX_GPIO_CLK, ENABLE);

    14

    15 /* 引脚源*/

    16 GPIO_PinAFConfig(CAN_TX_GPIO_PORT, CAN_RX_SOURCE, CAN_AF_PORT);

    17 GPIO_PinAFConfig(CAN_RX_GPIO_PORT, CAN_TX_SOURCE, CAN_AF_PORT);

    18

    19 /* 配置 CAN TX 引脚 */

    20 GPIO_InitStructure.GPIO_Pin = CAN_TX_PIN;

    21 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

    22 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    23 GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;

    24 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;

    25 GPIO_Init(CAN_TX_GPIO_PORT, &GPIO_InitStructure);

    26

    27 /* 配置 CAN RX 引脚 */

    28 GPIO_InitStructure.GPIO_Pin = CAN_RX_PIN ;

    29 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

    30 GPIO_Init(CAN_RX_GPIO_PORT, &GPIO_InitStructure);

    31 }

    与所有使用到GPIO的外设一样,都要先把使用到的GPIO引脚模式初始化,配置好复用功能。CAN的两个引脚都配置成通用推挽输出模式即可。

    配置CAN的工作模式

    接下来我们配置CAN的工作模式,由于我们是自己用的两个板子之间进行通讯,波特率之类的配置只要两个板子一致即可。如果您要使实验板与某个CAN总线网络的通讯的节点通讯,那么实验板的CAN配置必须要与该总线一致。我们实验中使用的配置见代码清单 244。

    代码清单 406 配置CAN的工作模式(bsp_can.c文件)

    1 /*

    2 * 函数名:CAN_Mode_Config

    3 * 描述:CAN的模式配置

    4 * 输入:无

    5 * 输出 : 无

    6 * 调用:内部调用

    7 */

    8 static void CAN_Mode_Config(void)

    9 {

    10 CAN_InitTypeDef CAN_InitStructure;

    11 /************************CAN通信参数设置************************/

    12 /* Enable CAN clock */

    13 RCC_APB1PeriphClockCmd(CAN_CLK, ENABLE);

    14

    15 /*CAN寄存器初始化*/

    16 CAN_DeInit(CAN1);

    17 CAN_StructInit(&CAN_InitStructure);

    18

    19 /*CAN单元初始化*/

    20 CAN_InitStructure.CAN_TTCM=DISABLE; //MCR-TTCM 关闭时间触发通信模式使能

    21 CAN_InitStructure.CAN_ABOM=ENABLE; //MCR-ABOM 使能自动离线管理

    22 CAN_InitStructure.CAN_AWUM=ENABLE; //MCR-AWUM 使用自动唤醒模式

    23 CAN_InitStructure.CAN_NART=DISABLE; //MCR-NART 禁止报文自动重传

    24 CAN_InitStructure.CAN_RFLM=DISABLE; //MCR-RFLM 接收FIFO 不锁定

    25                         // 溢出时新报文会覆盖原有报文

    26 CAN_InitStructure.CAN_TXFP=DISABLE; //MCR-TXFP 发送FIFO优先级取决于报文标示符

    27 CAN_InitStructure.CAN_Mode = CAN_Mode_Normal; //正常工作模式

    28 CAN_InitStructure.CAN_SJW=CAN_SJW_2tq; //BTR-SJW 重新同步跳跃宽度 2个时间单元

    29

    30 /* ss=1 bs1=5 bs2=3 位时间宽度为(1+5+3) 波特率即为时钟周期tq*(1+3+5) */

    31 CAN_InitStructure.CAN_BS1=CAN_BS1_5tq; //BTR-TS1 时间段1 占用了5个时间单元

    32 CAN_InitStructure.CAN_BS2=CAN_BS2_3tq; //BTR-TS1 时间段2 占用了3个时间单元

    33

    34 /* CAN Baudrate = 1 MBps (1MBps已为stm32的CAN最高速率) (CAN 时钟频率为 APB 1 = 45 MHz) */

    35 BTR-BRP 波特率分频器定义了时间单元的时间长度 45/(1+5+3)/5=1 Mbps

    36 CAN_InitStructure.CAN_Prescaler =5;

    37 CAN_Init(CANx, &CAN_InitStructure);

    38 }

    这段代码主要是把CAN的模式设置成了正常工作模式,如果您阅读的是"CAN—回环测试"的工程,这里是被配置成回环模式的,除此之外,两个工程就没有其它差别了。

    代码中还把位时序中的BS1和BS2段分别设置成了5Tq和3Tq,再加上SYNC_SEG段,一个CAN数据位就是9Tq了,加上CAN外设的分频配置为5分频,CAN所使用的总线时钟fAPB1 = 45MHz,于是我们可计算出它的波特率:

    1Tq = 1/(45M/5)=1/9 us

    T1bit=(5+3+1) x Tq =1us

    波特率=1/T1bit =1Mbps

    配置筛选器

    以上是配置CAN的工作模式,为了方便管理接收报文,我们还要把筛选器用起来,见代码清单 245。

    代码清单 407 配置CAN的筛选器(bsp_can.c文件)

    1

    2 /*IDE位的标志*/

    3 #define CAN_ID_STD ((uint32_t)0x00000000) /*标准ID */

    4 #define CAN_ID_EXT ((uint32_t)0x00000004) /*扩展ID */

    5

    6 /*RTR位的标志*/

    7 #define CAN_RTR_Data ((uint32_t)0x00000000) /*数据帧 */

    8 #define CAN_RTR_Remote ((uint32_t)0x00000002) /*远程帧*/

    9

    10 /************************************************************************/

    11 /*

    12 * 函数名:CAN_Filter_Config

    13 * 描述:CAN的筛选器配置

    14 * 输入:无

    15 * 输出 : 无

    16 * 调用:内部调用

    17 */

    18 static void CAN_Filter_Config(void)

    19 {

    20 CAN_FilterInitTypeDef CAN_FilterInitStructure;

    21

    22 /*CAN筛选器初始化*/

    23 CAN_FilterInitStructure.CAN_FilterNumber=0; //筛选器组0

    24 //工作在掩码模式

    25 CAN_FilterInitStructure.CAN_FilterMode=CAN_FilterMode_IdMask;

    26 //筛选器位宽为单个32位。

    27 CAN_FilterInitStructure.CAN_FilterScale=CAN_FilterScale_32bit;

    28

    29 /* 使能筛选器,按照标志符的内容进行比对筛选,

    30 扩展ID不是如下的就抛弃掉,是的话,会存入FIFO0。 */

    31//要筛选的ID高位,第0位保留,第1位为RTR标志,第2位为IDE标志,从第3位开始是EXID

    32CAN_FilterInitStructure.CAN_FilterIdHigh= ((((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF0000)>>16;

    33 //要筛选的ID低位

    34 CAN_FilterInitStructure.CAN_FilterIdLow= (((u32)0x1314<<3)|CAN_ID_EXT|CAN_RTR_DATA)&0xFFFF;

    35 //筛选器高16位每位必须匹配

    36 CAN_FilterInitStructure.CAN_FilterMaskIdHigh= 0xFFFF;

    37 //筛选器低16位每位必须匹配

    38 CAN_FilterInitStructure.CAN_FilterMaskIdLow= 0xFFFF;

    39 //筛选器被关联到FIFO0

    40 CAN_FilterInitStructure.CAN_FilterFIFOAssignment=CAN_Filter_FIFO0 ;

    41 //使能筛选器

    42 CAN_FilterInitStructure.CAN_FilterActivation=ENABLE;

    43

    44 CAN_FilterInit(&CAN_FilterInitStructure);

    45 /*CAN通信中断使能*/

    46 CAN_ITConfig(CANx, CAN_IT_FMP0, ENABLE);

    47 }

    这段代码把筛选器第0组配置成了32位的掩码模式,并且把它的输出连接到接收FIFO0,若通过了筛选器的匹配,报文会被存储到接收FIFO0。

    筛选器配置的重点是配置ID和掩码,根据我们的配置,这个筛选器工作在图 4017中的模式。

    图 4017 一个32位的掩码模式筛选器

    在该配置中,结构体成员CAN_FilterIdHigh和CAN_FilterIdLow存储的是要筛选的ID,而CAN_FilterMaskIdHigh和CAN_FilterMaskIdLow存储的是相应的掩码。在赋值时,要注意寄存器位的映射,在32位的ID中,第0位是保留位,第1位是RTR标志,第2位是IDE标志,从第3位起才是报文的ID(扩展ID)。

    因此在上述代码中我们先把扩展ID"0x1314"、IDE位标志"宏CAN_ID_EXT"以及RTR位标志"宏CAN_RTR_DATA"根据寄存器位映射组成一个32位的数据,然后再把它的高16位和低16位分别赋值给结构体成员CAN_FilterIdHigh和CAN_FilterIdLow。

    而在掩码部分,为简单起见我们直接对所有位赋值为1,表示上述所有标志都完全一样的报文才能经过筛选,所以我们这个配置相当于单个ID列表的模式,只筛选了一个ID号,而不是筛选一组ID号。这里只是为了演示方便,实际使用中一般会对不要求相等的数据位赋值为0,从而过滤一组ID,如果有需要,还可以继续配置多个筛选器组,最多可以配置28个,代码中只是配置了筛选器组0。

    对结构体赋值完毕后调用库函数CAN_FilterInit把个筛选器组的参数写入到寄存器中。

    配置接收中断

    在配置筛选器代码的最后部分我们还调用库函数CAN_ITConfig使能了CAN的中断,该函数使用的输入参数宏CAN_IT_FMP0表示当FIFO0接收到数据时会引起中断,该接收中断的优先级配置如下,见代码清单 246。

    代码清单 408 配置CAN接收中断的优先级(bsp_can.c文件)

    1 /*接收中断号*/

    2 #define CAN_RX_IRQ CAN1_RX0_IRQn

    3 /*

    4 * 函数名:CAN_NVIC_Config

    5 * 描述:CAN的NVIC 配置,第1优先级组,0,0优先级

    6 * 输入:无

    7 * 输出 : 无

    8 * 调用:内部调用

    9 */

    10 static void CAN_NVIC_Config(void)

    11 {

    12 NVIC_InitTypeDef NVIC_InitStructure;

    13 /* Configure one bit for preemption priority */

    14 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);

    15 /*中断设置*/

    16 NVIC_InitStructure.NVIC_IRQChannel = CAN_RX_IRQ; //CAN RX中断

    17 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;

    18 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;

    19 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    20 NVIC_Init(&NVIC_InitStructure);

    21 }

    这部分与我们配置其它中断的优先级无异,都是配置NVIC结构体,优先级可根据自己的需要配置,最主要的是中断向量,上述代码中把中断向量配置成了CAN的接收中断。

    设置发送报文

    要使用CAN发送报文时,我们需要先定义一个发送报文结构体并向它赋值,见代码清单 247。

    代码清单 409 设置要发送的报文(bsp_can.c文件)

    1 /*IDE位的标志*/

    2 #define CAN_ID_STD ((uint32_t)0x00000000) /*标准ID */

    3 #define CAN_ID_EXT ((uint32_t)0x00000004) /*扩展ID */

    4

    5 /*RTR位的标志*/

    6 #define CAN_RTR_Data ((uint32_t)0x00000000) /*数据帧 */

    7 #define CAN_RTR_Remote ((uint32_t)0x00000002) /*远程帧*/

    8

    9 /*

    10 * 函数名:CAN_SetMsg

    11 * 描述:CAN通信报文内容设置,设置一个数据内容为0-7的数据包

    12 * 输入:无

    13 * 输出 : 无

    14 * 调用:外部调用

    15 */

    16 void CAN_SetMsg(CanTxMsg *TxMessage)

    17 {

    18 uint8_t ubCounter = 0;

    19

    20 //TxMessage.StdId=0x00;

    21 TxMessage->ExtId=0x1314; //使用的扩展ID

    22 TxMessage->IDE=CAN_ID_EXT; //扩展模式

    23 TxMessage->RTR=CAN_RTR_DATA; //发送的是数据

    24 TxMessage->DLC=8; //数据长度为8字节

    25

    26 /*设置要发送的数据0-7*/

    27 for (ubCounter = 0; ubCounter < 8; ubCounter++)

    28 {

    29 TxMessage->Data[ubCounter] = ubCounter;

    30 }

    31 }

    这段代码是我们为了方便演示而自己定义的设置报文内容的函数,它把报文设置成了扩展模式的数据帧,扩展ID为0x1314,数据段的长度为8,且数据内容分别为0-7,实际应用中您可根据自己的需求发设置报文内容。当我们设置好报文内容后,调用库函数CAN_Transmit即可把该报文存储到发送邮箱,然后CAN外设会把它发送出去:

    CAN_Transmit(CANx, &TxMessage);

    接收报文

    由于我们设置了接收中断,所以接收报文的操作是在中断的服务函数中完成的,见代码清单 248。

    代码清单 4010 接收报文(stm32f4xx_it.c)

    1

    2 /*接收中断服务函数*/

    3 #define CAN_RX_IRQHandler CAN1_RX0_IRQHandler

    4

    5 extern __IO uint32_t flag ; //用于标志是否接收到数据,在中断函数中赋值

    6 extern CanRxMsg RxMessage; //接收缓冲区

    7 /********************************************************************/

    8 void CAN_RX_IRQHandler(void)

    9 {

    10 /*从邮箱中读出报文*/

    11 CAN_Receive(CANx, CAN_FIFO0, &RxMessage);

    12

    13 /* 比较ID是否为0x1314 */

    14 if ((RxMessage.ExtId==0x1314) && (RxMessage.IDE==CAN_ID_EXT) && (RxMessage.DLC==8) )

    15 {

    16 flag = 1; //接收成功

    17 }

    18 else

    19 {

    20 flag = 0; //接收失败

    21 }

    22 }

    根据我们前面的配置,若CAN接收的报文经过筛选器匹配后会被存储到FIFO0中,并引起中断进入到这个中断服务函数中,在这个函数里我们调用了库函数CAN_Receive把报文从FIFO复制到自定义的接收报文结构体RxMessage中,并且比较了接收到的报文ID是否与我们希望接收的一致,若一致就设置标志flag=1,否则为0,通过flag标志通知主程序流程获知是否接收到数据。

    要注意如果设置了接收报文中断,必须要在中断内调用CAN_Receive函数读取接收FIFO的内容,因为只有这样才能清除该FIFO的接收中断标志,如果不在中断内调用它清除标志的话,一旦接收到报文,STM32会不断进入中断服务函数,导致程序卡死。

    3.    main函数

    最后我们来阅读main函数,了解整个通讯流程,见代码清单 2414。

    代码清单 4011 main函数

    1

    2 _IO uint32_t flag = 0; //用于标志是否接收到数据,在中断函数中赋值

    3 CanTxMsg TxMessage; //发送缓冲区

    4 CanRxMsg RxMessage; //接收缓冲区

    5

    6 /**

    7 * @brief 主函数

    8 * @param 无

    9 * @retval 无

    10 */

    11 int main(void)

    12 {

    13 LED_GPIO_Config();

    14

    15 /*初始化USART1*/

    16 Debug_USART_Config();

    17

    18 /*初始化按键*/

    19 Key_GPIO_Config();

    20

    21 /*初始化can,在中断接收CAN数据包*/

    22 CAN_Config();

    23

    24 printf("\r\n欢迎使用秉火 STM32 F429 开发板。\r\n");

    25 printf("\r\n秉火F429 CAN通讯实验例程\r\n");

    26

    27 printf("\r\n实验步骤:\r\n");

    28

    29 printf("\r\n 1.使用导线连接好两个CAN讯设备\r\n");

    30 printf("\r\n 2.使用跳线帽连接好:5v --- C/4-5V \r\n");

    31 printf("\r\n 3.按下开发板的KEY1键,会使用CAN向外发送0-7的数据包,包的扩展ID为0x1314 \r\n");

    32 printf("\r\n 4.若开发板的CAN接收到扩展ID为0x1314的数据包,会把数据以打印到串口。 \r\n");

    33 printf("\r\n 5.本例中的can波特率为1MBps,为stm32的can最高速率。 \r\n");

    34

    35 while (1)

    36 {

    37 /*按一次按键发送一次数据*/

    38 if ( Key_Scan(KEY1_GPIO_PORT,KEY1_PIN) == KEY_ON)

    39 {

    40 LED_BLUE;

    41 /*设置要发送的报文*/

    42 CAN_SetMsg(&TxMessage);

    43 /*把报文存储到发送邮箱,发送*/

    44 CAN_Transmit(CANx, &TxMessage);

    45

    46 can_delay(10000);//等待发送完毕,可使用CAN_TransmitStatus查看状态

    47

    48 LED_GREEN;

    49

    50 printf("\r\n已使用CAN发送数据包!\r\n");

    51 printf("\r\n发送的报文内容为:\r\n");

    52 printf("\r\n扩展ID号ExtId:0x%x \r\n",TxMessage.ExtId);

    53 CAN_DEBUG_ARRAY(TxMessage.Data,8);

    54 }

    55 if (flag==1)

    56 {

    57 LED_GREEN;

    58 printf("\r\nCAN接收到数据:\r\n");

    59

    60 CAN_DEBUG_ARRAY(RxMessage.Data,8);

    61

    62 flag=0;

    63 }

    64 }

    65 }

    在main函数里,我们调用了CAN_Config函数初始化CAN外设,它包含我们前面解说的GPIO初始化函数CAN_GPIO_Config、中断优先级设置函数CAN_NVIC_Config、工作模式设置函数CAN_Mode_Config以及筛选器配置函数CAN_Filter_Config。

    初始化完成后,我们在while循环里检测按键,当按下实验板的按键1时,它就调用CAN_SetMsg函数设置要发送的报文,然后调用CAN_Transmit函数把该报文存储到发送邮箱,等待CAN外设把它发送出去。代码中并没有检测发送状态,如果需要,您可以调用库函数CAN_TransmitStatus检查发送状态。

    while循环中在其它时间一直检查flag标志,当接收到报文时,我们的中断服务函数会把它置1,所以我们可以通过它获知接收状态,当接收到报文时,我们把它使用宏CAN_DEBUG_ARRAY输出到串口。

    40.6.3 下载验证

    下载验证这个CAN实验时,我们建议您先使用"CAN—回环测试"的工程进行测试,它的环境配置比较简单,只需要一个实验板,用USB线使实验板"USB TO UART"接口跟电脑连接起来,在电脑端打开串口调试助手,并且把编译好的该工程下载到实验板,然后复位。这时在串口调试助手可看到CAN测试的调试信息,按一下实验板上的KEY1按键,实验板会使用回环模式向自己发送报文,在串口调试助手可以看到相应的发送和接收的信息。

    使用回环测试成功后,如果您有两个实验板,需要按照"硬件设计"小节中的图例连接两个板子的CAN总线,并且一定要接上跳线帽给CAN收发器供电、把液晶屏拔掉防止干扰。用USB线使实验板"USB TO UART"接口跟电脑连接起来,在电脑端打开串口调试助手,然后使用"CAN—双机通讯"工程编译,并给两个板子都下载该程序,然后复位。这时在串口调试助手可看到CAN测试的调试信息,按一下其中一个实验板上的KEY1按键,另一个实验板会接收到报文,在串口调试助手可以看到相应的发送和接收的信息。

    展开全文
  • CAN 总线协议已经成为汽车计算机控制系统和嵌入式工业控制局域网的标准总线,并且拥有以CAN 为底层协议专为大型货车和重工机械车辆设计的 J1939 协议。近年来,它具有的高可靠性和良好的错误检测
  • ipv6 地址自动分配解析

    千次阅读 2014-05-13 12:36:46
    Address autoconfiguration can only be performed on multicast-capable interfaces. Address autoconfiguration is described in RFC 2462, "IPv6 Stateless Address Autoconfiguration." Autoconfigured ...
  • 青流流量计仪表modoubus 寄存器地址
  • 通过SDK可以基于ip地址分配,但这里测试只能对private桌面进行ip,或者机器名来分配 可以使用get-brokerprivatedesktop 来查看当前privatedesktop的情况 PS C:\Program Files\Citrix\Desktop Studio> set-...
  • 如果将原来由11个标识符位定义的地址范围扩大,则可以更方便地使用can实现这些应用程序。因此,引入了第二种消息格式(“扩展格式”),其提供由29位定义的更大的地址范围。   The acceptance and introduction of...
  • 搜索错误关键字“Cannot assign requested address”,"返回" JSON 内容,返回的引号是因为其实并不是远程接口返回的,而是 IdentityModel 返回的,毕竟直译后从字面意思理解“无法分配请求的地址”,也可以理解为...
  • CAN通信详解

    万次阅读 多人点赞 2017-04-11 16:59:34
    本章我们将向大家介绍如何使用STM32自带的CAN控制器来实现两个开发板之间的CAN通讯,并将结果显示在TFTLCD模块上。本章分为如下几个部分: 30.1 CAN简介 30.2硬件设计 30.3软件设计 30.4下载验证 30.1 CAN简介 CAN...
  • CAN协议中文版

    2018-04-08 21:41:40
    随着串行通讯进入更多应用领域,因此,在一些应用里,需要对通讯功能的报文识别位提出分配标准化的要求。原先的地址范围由11 个识别位定义,如果地址范围扩大,则这些应用就可以更好地由CAN 来实现。
  • 基于can总线的bootloader

    2018-01-17 09:16:36
    1、自动检测连接到CAN总线上的节点,并自动为每个节点分配一个唯一的地址; 2、自动检测节点是否在线,若离线则从列表中删除; 3、可以升级更新指定节点的程序,同时可以发送命令控制程序的执行; PC端程序所需硬件...
  • CAN总线接口电路

    2011-11-26 22:20:08
    介绍了采用 PHILIP 公司生产的控制器局域网的高度集成的通信控制器 SJA1000 ...及看门狗芯片的特点、内部结构、寄存器结构及地址分配,说明一种通用型 CAN总线 的设计和开发.探讨应用中需注意的一些问题。
  • 介绍CAN控制器SJA1000的特点、内部结构以及SJA1000的寄存器结构及地址分配CAN协议通信格式。并以独立CAN控制器SJA1000为例,结合CAN协议说明了一种通用型CAN总线的开发与设计。
  • CAN通信协议(一)

    万次阅读 多人点赞 2018-03-06 18:10:46
    链接地址 前言 因为工作,需要研究CAN总线。博主的CAN学习参考正点原子和野火的教程。虽然没有买板子,不过对于博主现在来说,感觉开发板都差不多吧!毕竟工作中开发板肯定是不一样的! CAN...
  • 摘要:介绍CAN控制器SJA1000的特点、内部结构以及SJA1000的寄存器结构及地址分配CAN协议通信格式。并以独立CAN控制器SJA1000为例,结合CAN协议说明了一种通用型CAN总线的开发与设计。  关键词:CAN总线 SJA1000...
  • (1)内存映射模块(mmap):负责把磁盘文件的逻辑地址映射到虚拟地址,以及把虚拟地址映射到物理地址。 (2)交换模块(swap):负责控制内存内容的换入和换出,它通过交换机制,使得在物理内存的页面(RAM 页)中...
  • CAN 2.0协议中文版

    2010-08-27 10:43:34
    随着串行通讯进入更多应用领域,因此,在一些应用里,需要对通讯功能的报文识别位提出分配标准 化的要求。原先的地址范围由11 个识别位定义,如果地址范围扩大,则这些应用就可以更好地由CAN 来 实现。
  • 三个FlexCAN模块(一路CAN-FD支持) 用于通信协议和外围设备(UART、I2C、SPI、I2S、LIN、PWM等)仿真的FlexIO模块。(8个引脚可配置为UART、SPI、I2C、I2S) 仿真和调试: SWD, JTAG (ITM, SWV, SWO) ==密码服务...
  • 【stm32f407】CAN总线

    万次阅读 多人点赞 2017-06-23 16:58:10
    一.CAN简介 CAN 是ControllerArea Network 的缩写(以下称为CAN),是ISO国际标准化的串行通信协议。在当前的汽车产业中,出于对安全性、舒适性、方便性、低公害、低成本的要求,各种各样的电子控制系统被开发了...
  • CAN通信详解(全)

    万次阅读 多人点赞 2019-02-25 17:56:58
    本章我们将向大家介绍如何使用STM32自带的CAN控制器来实现两个开发板之间的CAN通讯,并将结果显示在TFTLCD模块上。本章分为如下几个部分: 30.1 CAN简介 30.2 硬件设计 30.3 软件设计 30.4 下载验证 30.1 CAN简介 ...
  • CAN FD协议描述

    千次阅读 2018-12-17 13:59:26
    CAN FD描述 随着电动汽车,无人驾驶汽车技术的快速发展,以及对汽车高级驾驶辅助系统和人机交互的增加,传统的CAN总线在传输速率和带宽等方面越来越显得力不从心,因此改进版的CAN总线应运而生。从2012年第13届ICC...
  • CAN总线协议介绍

    千次阅读 2018-12-13 09:51:48
    CAN是控制器局域网络(Controller Area Network)的简称。 具有的高可靠性和良好的错误检测能力。为半双工。 1. CAN 物理层 与I2C、SPI等具有时钟信号的同步通讯方式不同,CAN通讯并不是以时钟信号来进行同步的,它...
  • 原先的地址范围由 11 个识别位定义,如果地址范围扩大,则这些应用就可以更好地由 CAN 来 实现。 因此引入了第二种报文格式(‘扩展格式’)的概念,其定义的地址范围更宽,由 29 位定义。系统设计 者将从考虑定义...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 90,763
精华内容 36,305
关键字:

can地址分配