<h1 style="text-align:center;"><a name="_Toc342394264"><span style="font-family:宋体;">第三十章</span> <span lang="EN-US">CAN</span></a><span style="font-family:宋体;">通讯实验</span></h1>
<span lang="EN-US"> </span><span style="font-family:宋体;">本章我们将向大家介绍如何使用</span><span lang="EN-US">STM32</span><span style="font-family:宋体;">自带的</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">控制器来实现两个开发板之间的</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">通讯,并将结果显示在</span><span lang="EN-US">TFTLCD</span><span style="font-family:宋体;">模块上。本章分为如下几个部分:</span>
<span lang="EN-US">30.1 CAN</span><span style="font-family:宋体;">简介</span>
<span lang="EN-US">30.2 </span><span style="font-family:宋体;">硬件设计</span>
<span lang="EN-US">30.3 </span><span style="font-family:宋体;">软件设计</span>
<span lang="EN-US">30.4 </span><span style="font-family:宋体;">下载验证</span>
<h2 style="margin:7.8pt 0cm;"><a name="_Toc342394265"><span lang="EN-US">30.1
CAN</span></a><span style="font-family:宋体;">简介</span></h2>
<span lang="EN-US">CAN </span><span style="font-family:宋体;">是</span><span lang="EN-US">Controller Area Network </span><span style="font-family:宋体;">的缩写(以下称为</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">),是</span><span lang="EN-US">ISO</span><span style="font-family:宋体;">国际标准化的串行通信协议。在当前的汽车产业中,出于对安全性、舒适性、方便性、低公害、低成本的要求,各种各样的电子控制系统被开发了出来。由于这些系统之间通信所用的数据类型及对可靠性的要求不尽相同,由多条总线构成的情况很多,线束的数量也随之增加。为适应“减少线束的数量”、“通过多个</span><span lang="EN-US">LAN</span><span style="font-family:宋体;">,进行大量数据的高速通信”的需要,</span><span lang="EN-US">1986 </span><span style="font-family:宋体;">年德国电气商博世公司开发出面向汽车的</span><span lang="EN-US">CAN </span><span style="font-family:宋体;">通信协议。此后,</span><span lang="EN-US">CAN </span><span style="font-family:宋体;">通过</span><span lang="EN-US">ISO11898 </span><span style="font-family:宋体;">及</span><span lang="EN-US">ISO11519 </span><span style="font-family:宋体;">进行了标准化,现在在欧洲已是汽车网络的标准协议。</span> <span style="font-family:宋体;"> </span>
<span style="font-family:宋体;">现在,</span><span lang="EN-US">CAN </span><span style="font-family:宋体;">的高性能和可靠性已被认同,并被广泛地应用于工业自动化、船舶、医疗设备、工业设备等方面。现场总线是当今自动化领域技术发展的热点之一,被誉为自动化领域的计算机局域网。它的出现为分布式控制系统实现各节点之间实时、可靠的数据通信提供了强有力的技术支持。</span>
<span lang="EN-US">CAN </span><span style="font-family:宋体;">控制器根据两根线上的电位差来判断总线电平。总线电平分为显性电平和隐性电平,二者必居其一。发送方通过使总线电平发生变化,将消息发送给接收方。</span>
<span lang="EN-US">CAN</span><span style="font-family:宋体;">协议具有一下特点:</span>
<span lang="EN-US">1)<span style="font:7pt 'Times New Roman';"> </span></span><b><span style="font-family:宋体;">多主控制。</span></b><span style="font-family:宋体;">在总线空闲时,所有单元都可以发送消息(多主控制),而两个以上的单元同时开始发送消息时,根据标识符(</span><span lang="EN-US">Identifier </span><span style="font-family:宋体;">以下称为</span><span lang="EN-US"> ID</span><span style="font-family:宋体;">)决定优先级。</span><span lang="EN-US">ID </span><span style="font-family:宋体;">并不是表示发送的目的地址,而是表示访问总线的消息的优先级。两个以上的单元同时开始发送消息时,对各消息</span><span lang="EN-US">ID </span><span style="font-family:宋体;">的每个位进行逐个仲裁比较。仲裁获胜(被判定为优先级最高)的单元可继续发送消息,仲裁失利的单元则立刻停止发送而进行接收工作。</span>
<span lang="EN-US">2)<span style="font:7pt 'Times New Roman';"> </span></span><b><span style="font-family:宋体;">系统的若软性。</span></b><span style="font-family:宋体;">与总线相连的单元没有类似于“地址”的信息。因此在总线上增加单元时,连接在总线上的其它单元的软硬件及应用层都不需要改变。</span>
<span lang="EN-US">3)<span style="font:7pt 'Times New Roman';"> </span></span><b><span style="font-family:宋体;">通信速度较快,通信距离远。</span></b><span style="font-family:宋体;">最高</span><span lang="EN-US">1Mbps</span><span style="font-family:宋体;">(距离小于</span><span lang="EN-US">40M</span><span style="font-family:宋体;">),最远可达</span><span lang="EN-US">10KM</span><span style="font-family:宋体;">(速率低于</span><span lang="EN-US">5Kbps</span><span style="font-family:宋体;">)。</span>
<span lang="EN-US">4)<span style="font:7pt 'Times New Roman';"> </span></span><b><span style="font-family:宋体;">具有错误检测、错误通知和错误恢复功能。</span></b><span style="font-family:宋体;">所有单元都可以检测错误(错误检测功能),检测出错误的单元会立即同时通知其他所有单元(错误通知功能),正在发送消息的单元一旦检测出错误,会强制结束当前的发送。强制结束发送的单元会不断反复地重新发送此消息直到成功发送为止(错误恢复功能)。</span>
<span lang="EN-US">5)<span style="font:7pt 'Times New Roman';"> </span></span><b><span style="font-family:宋体;">故障封闭功能。</span></b><span lang="EN-US">CAN </span><span style="font-family:宋体;">可以判断出错误的类型是总线上暂时的数据错误(如外部噪声等)还是持续的数据错误(如单元内部故障、驱动器故障、断线等)。由此功能,当总线上发生持续数据错误时,可将引起此故障的单元从总线上隔离出去。</span>
<span lang="EN-US">6)<span style="font:7pt 'Times New Roman';"> </span></span><b><span style="font-family:宋体;">连接节点多。</span></b><span lang="EN-US">CAN </span><span style="font-family:宋体;">总线是可同时连接多个单元的总线。可连接的单元总数理论上是没有限制的。但实际上可连接的单元数受总线上的时间延迟及电气负载的限制。降低通信速度,可连接的单元数增加;提高通信速度,则可连接的单元数减少。</span>
<span style="font-family:宋体;">正是因为</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">协议的这些特点,使得</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">特别适合工业过程监控设备的互连,因此,越来越受到工业界的重视,并已公认为最有前途的现场总线之一。</span>
<span lang="EN-US">CAN</span><span style="font-family:宋体;">协议经过</span><span lang="EN-US">ISO</span><span style="font-family:宋体;">标准化后有两个标准:</span><span lang="EN-US">ISO11898</span><span style="font-family:宋体;">标准和</span><span lang="EN-US">ISO11519-2</span><span style="font-family:宋体;">标准。其中</span><span lang="EN-US">ISO11898</span><span style="font-family:宋体;">是针对通信速率为</span><span lang="EN-US">125Kbps~1Mbps</span><span style="font-family:宋体;">的高速通信标准,而</span><span lang="EN-US">ISO11519-2</span><span style="font-family:宋体;">是针对通信速率为</span><span lang="EN-US">125Kbps</span><span style="font-family:宋体;">以下的低速通信标准。</span>
<span style="font-family:宋体;">本章,我们使用的是</span><span lang="EN-US">450Kbps</span><span style="font-family:宋体;">的通信速率,使用的是</span><span lang="EN-US">ISO11898</span><span style="font-family:宋体;">标准,该标准的物理层特征如图</span><span lang="EN-US">30.1.1</span><span style="font-family:宋体;">所示:</span>
<span lang="EN-US"><center>
<ignore_js_op>
<img src="data/attach/1908/bxxstgr6ila4v134424ky95tvm613ie3.jpg" alt=" " title=" ">
</ignore_js_op>
</center><br><div style="text-align: center;"><span style="font-family: 宋体;">图</span><span lang="EN-US">30.1.1 ISO11898</span><span style="font-family: 宋体;">物理层特性</span>
<span lang="EN-US"> </span></div>
</span><p><span style="font-family:宋体;">从该特性可以看出,显性电平对应逻辑</span><span lang="EN-US">0</span><span style="font-family:宋体;">,</span><span lang="EN-US">CAN_H</span><span style="font-family:宋体;">和</span><span lang="EN-US">CAN_L</span><span style="font-family:宋体;">之差为</span><span lang="EN-US">2.5V</span><span style="font-family:宋体;">左右。而隐性电平对应逻辑</span><span lang="EN-US">1</span><span style="font-family:宋体;">,</span><span lang="EN-US">CAN_H</span><span style="font-family:宋体;">和</span><span lang="EN-US">CAN_L</span><span style="font-family:宋体;">之差为</span><span lang="EN-US">0V</span><span style="font-family:宋体;">。在总线上显性电平具有优先权,只要有一个单元输出显性电平,总线上即为显性电平。而隐形电平则具有包容的意味,只有所有的单元都输出隐性电平,总线上才为隐性电平(显性电平比隐性电平更强)。另外,在</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">总线的起止端都有一个</span><span lang="EN-US">120</span><span style="font-family:宋体;">Ω的终端电阻,来做阻抗匹配,以减少回波反射。</span>
<span lang="EN-US"> CAN</span><span style="font-family:宋体;">协议是通过以下</span><span lang="EN-US">5</span><span style="font-family:宋体;">种类型的帧进行的:</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">数据帧</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">要控帧</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">错误帧</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">过载帧</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">帧间隔</span>
<span style="font-family:宋体;">另外,数据帧和遥控帧有标准格式和扩展格式两种格式。标准格式有</span><span lang="EN-US">11 </span><span style="font-family:宋体;">个位的标识符(</span><span lang="EN-US">ID</span><span style="font-family:宋体;">),扩展格式有</span><span lang="EN-US">29 </span><span style="font-family:宋体;">个位的</span><span lang="EN-US">ID</span><span style="font-family:宋体;">。各种帧的用途如表</span><span lang="EN-US">30.1.1</span><span style="font-family:宋体;">所示:</span>
<span style="font-family:宋体;font-size:12pt;">帧类型</span>
<span style="font-family:宋体;font-size:12pt;">帧用途</span>
<span style="font-family:宋体;font-size:12pt;">数据帧</span>
<span style="font-family:宋体;font-size:12pt;">用于发送单元向接收单元传送数据的帧</span>
<span style="font-family:宋体;font-size:12pt;">遥控帧</span>
<span style="font-family:宋体;font-size:12pt;">用于接收单元向具有相同<span lang="EN-US"> ID </span>的发送单元请求数据的帧</span>
<span style="font-family:宋体;font-size:12pt;">错误帧</span>
<span style="font-family:宋体;font-size:12pt;">用于当检测出错误时向其它单元通知错误的帧</span>
<span style="font-family:宋体;font-size:12pt;">过载帧</span>
<span style="font-family:宋体;font-size:12pt;">用于接收单元通知其尚未做好接收准备的帧</span>
<span style="font-family:宋体;font-size:12pt;">间隔帧</span>
<span style="font-family:宋体;font-size:12pt;">用于将数据帧及遥控帧与前面的帧分离开来的帧</span>
<span style="font-family:宋体;">表</span><span lang="EN-US">30.1.1 CAN</span><span style="font-family:宋体;">协议各种帧及其用途</span>
<span lang="EN-US"> </span><span style="font-family:宋体;">由于篇幅所限,我们这里仅对数据帧进行详细介绍,数据帧一般由</span><span lang="EN-US">7</span><span style="font-family:宋体;">个段构成,即:</span>
<span lang="EN-US">(1)<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">帧起始。表示数据帧开始的段。</span>
<span lang="EN-US">(2)<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">仲裁段。表示该帧优先级的段。</span>
<span lang="EN-US">(3)<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">控制段。表示数据的字节数及保留位的段。</span>
<span lang="EN-US">(4)<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">数据段。数据的内容,一帧可发送</span><span lang="EN-US">0~8</span><span style="font-family:宋体;">个字节的数据。</span>
<span lang="EN-US">(5)<span style="font:7pt 'Times New Roman';"> </span></span><span lang="EN-US">CRC</span><span style="font-family:宋体;">段。检查帧的传输错误的段。</span>
<span lang="EN-US">(6)<span style="font:7pt 'Times New Roman';"> </span></span><span lang="EN-US">ACK</span><span style="font-family:宋体;">段。表示确认正常接收的段。</span>
<span lang="EN-US">(7)<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">帧结束。表示数据帧结束的段。</span>
<span style="font-family:宋体;">数据帧的构成如图</span><span lang="EN-US">30.1.2</span><span style="font-family:宋体;">所示:</span>
<span style="font-family:宋体;"></span></p><center>
<ignore_js_op>
<img src="data/attach/1908/e5zl1fe3m93yo1ke0e92f0toj052k2fh.jpg" alt=" " title=" ">
</ignore_js_op>
</center><br><p style="text-align: center; ">
图<span lang="EN-US">30.1.2 </span><span style="font-family: 宋体;">数据帧的构成</span> </p><p><span style="font-family:宋体;">图中</span><span lang="EN-US">D</span><span style="font-family:宋体;">表示显性电平,</span><span lang="EN-US">R</span><span style="font-family:宋体;">表示隐形电平(下同)。</span>
<span style="font-family:宋体;">帧起始,这个比较简单,标准帧和扩展帧都是由</span><span lang="EN-US">1</span><span style="font-family:宋体;">个位的显性电平表示帧起始。</span>
<span style="font-family:宋体;">仲裁段,表示数据优先级的段,标准帧和扩展帧格式在本段有所区别,如图</span><span lang="EN-US">30.1.3</span><span style="font-family:宋体;">所示:</span>
</p><center>
<ignore_js_op>
<img src="data/attach/1908/3fr47pt1wvnvzo7kvw1nyc5qc0rwhvfs.jpg" alt=" " title=" ">
</ignore_js_op>
</center>
<span style="font-family:宋体;"><div style="text-align: center;">图<span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">30.1.3 </span>数据帧仲裁段构成<span style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;"> </span></div></span><p><span style="font-family:宋体;">标准格式的</span><span lang="EN-US">ID </span><span style="font-family:宋体;">有</span><span lang="EN-US">11 </span><span style="font-family:宋体;">个位。从</span><span lang="EN-US">ID28 </span><span style="font-family:宋体;">到</span><span lang="EN-US">ID18 </span><span style="font-family:宋体;">被依次发送。禁止高</span><span lang="EN-US">7 </span><span style="font-family:宋体;">位都为隐性(禁止设定:</span><span lang="EN-US">ID=1111111XXXX</span><span style="font-family:宋体;">)。扩展格式的</span><span lang="EN-US"> ID </span><span style="font-family:宋体;">有</span><span lang="EN-US">29 </span><span style="font-family:宋体;">个位。基本</span><span lang="EN-US">ID </span><span style="font-family:宋体;">从</span><span lang="EN-US">ID28 </span><span style="font-family:宋体;">到</span><span lang="EN-US">ID18</span><span style="font-family:宋体;">,扩展</span><span lang="EN-US">ID </span><span style="font-family:宋体;">由</span><span lang="EN-US">ID17 </span><span style="font-family:宋体;">到</span><span lang="EN-US">ID0 </span><span style="font-family:宋体;">表示。基本</span><span lang="EN-US">ID </span><span style="font-family:宋体;">和标准格式的</span><span lang="EN-US">ID </span><span style="font-family:宋体;">相同。禁止高</span><span lang="EN-US">7 </span><span style="font-family:宋体;">位都为隐性(禁止设定:基本</span><span lang="EN-US">ID=1111111XXXX</span><span style="font-family:宋体;">)。</span>
<span style="font-family:宋体;">其中</span><span lang="EN-US">RTR</span><span style="font-family:宋体;">位用于标识是否是远程帧(</span><span lang="EN-US">0</span><span style="font-family:宋体;">,数据帧;</span><span lang="EN-US">1</span><span style="font-family:宋体;">,远程帧),</span><span lang="EN-US">IDE</span><span style="font-family:宋体;">位为标识符选择位(</span><span lang="EN-US">0</span><span style="font-family:宋体;">,使用标准标识符;</span><span lang="EN-US">1</span><span style="font-family:宋体;">,使用扩展标识符),</span><span lang="EN-US">SRR</span><span style="font-family:宋体;">位为代替远程请求位,为隐性位,它代替了标准帧中的</span><span lang="EN-US">RTR</span><span style="font-family:宋体;">位。</span>
<span style="font-family:宋体;">控制段,由</span><span lang="EN-US">6</span><span style="font-family:宋体;">个位构成,表示数据段的字节数。标准帧和扩展帧的控制段稍有不同,如图</span><span lang="EN-US">30.1.4</span><span style="font-family:宋体;">所示:</span>
<span lang="EN-US"></span></p><center>
<ignore_js_op>
<img src="data/attach/1908/vilo62v78kowgn152jtu8mqgq2vva789.jpg" alt=" " title=" ">
</ignore_js_op>
</center>
<span style="font-family:宋体;"><div style="text-align: center;">图<span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">30.1.4 </span>数据帧控制段构成<span style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">
</span><span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;"> </span></div></span><p><span lang="EN-US"></span><span style="font-family:宋体;">上图中,</span><span lang="EN-US">r0</span><span style="font-family:宋体;">和</span><span lang="EN-US">r1</span><span style="font-family:宋体;">为保留位,必须全部以显性电平发送,但是接收端可以接收显性、隐性及任意组合的电平。</span><span lang="EN-US">DLC</span><span style="font-family:宋体;">段为数据长度表示段,高位在前,</span><span lang="EN-US">DLC</span><span style="font-family:宋体;">段有效值为</span><span lang="EN-US">0~8</span><span style="font-family:宋体;">,但是接收方接收到</span><span lang="EN-US">9~15</span><span style="font-family:宋体;">的时候并不认为是错误。</span>
<span style="font-family:宋体;">数据段,该段可包含</span><span lang="EN-US">0~8</span><span style="font-family:宋体;">个字节的数据。从最高位(</span><span lang="EN-US">MSB</span><span style="font-family:宋体;">)开始输出,标准帧和扩展帧在这个段的定义都是一样的。如图</span><span lang="EN-US">30.1.5</span><span style="font-family:宋体;">所示:</span>
<span lang="EN-US"></span></p><center>
<ignore_js_op>
<img src="data/attach/1908/br3fdj7powgxlqjl67vhyfcwd0lw4jcz.jpg" alt=" " title=" ">
</ignore_js_op>
</center>
<span style="font-family:宋体;"><div style="text-align: center;">图<span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">30.1.5 </span>数据帧数据段构成<span style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">
</span><span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;"> </span></div></span><p><span lang="EN-US"> CRC</span><span style="font-family:宋体;">段,该段用于检查帧传输错误。由</span><span lang="EN-US">15</span><span style="font-family:宋体;">个位的</span><span lang="EN-US">CRC</span><span style="font-family:宋体;">顺序和</span><span lang="EN-US">1</span><span style="font-family:宋体;">个位的</span><span lang="EN-US">CRC</span><span style="font-family:宋体;">界定符(用于分隔的位)组成,标准帧和扩展帧在这个段的格式也是相同的。如图</span><span lang="EN-US">30.1.6</span><span style="font-family:宋体;">所示:</span>
<span lang="EN-US"></span></p><center>
<ignore_js_op>
<img src="data/attach/1908/w5k8p4w76e4wurx62ucztzkryg9ll0v2.jpg" alt=" " title=" ">
</ignore_js_op>
</center>
<span style="font-family:宋体;"><div style="text-align: center;">图<span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">30.1.6 </span>数据帧<span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">CRC</span>段构成<span style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;"> </span></div></span><p><span lang="EN-US"> </span><span style="font-family:宋体;">此段</span><span lang="EN-US">CRC</span><span style="font-family:宋体;">的值计算范围包括:帧起始、仲裁段、控制段、数据段。接收方以同样的算法计算</span><span lang="EN-US"> CRC </span><span style="font-family:宋体;">值并进行比较,不一致时会通报错误。</span>
<span lang="EN-US"> ACK</span><span style="font-family:宋体;">段,此段用来确认是否正常接收。由</span><span lang="EN-US">ACK</span><span style="font-family:宋体;">槽</span><span lang="EN-US">(ACK Slot)</span><span style="font-family:宋体;">和</span><span lang="EN-US">ACK</span><span style="font-family:宋体;">界定符</span><span lang="EN-US">2</span><span style="font-family:宋体;">个位组成。标准帧和扩展帧在这个段的格式也是相同的。如图</span><span lang="EN-US">30.1.7</span><span style="font-family:宋体;">所示:</span>
<span lang="EN-US"></span></p><center>
<ignore_js_op>
<img src="data/attach/1908/el64ared0dkitfrsk53zi4golwvikcf1.jpg" alt=" " title=" ">
</ignore_js_op>
</center>
<span style="font-family:宋体;"><div style="text-align: center;">图<span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">30.1.7 </span>数据帧<span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">CRC</span>段构成<span style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;"> </span></div></span><p><span style="font-family:宋体;">发送单元的</span><span lang="EN-US">ACK</span><span style="font-family:宋体;">,发送</span><span lang="EN-US">2</span><span style="font-family:宋体;">个位的隐性位,而接收到正确消息的单元在</span><span lang="EN-US">ACK</span><span style="font-family:宋体;">槽(</span><span lang="EN-US">ACK
Slot</span><span style="font-family:宋体;">)发送显性位,通知发送单元正常接收结束,这个过程叫发送</span><span lang="EN-US">ACK/</span><span style="font-family:宋体;">返回</span><span lang="EN-US">ACK</span><span style="font-family:宋体;">。发送</span><span lang="EN-US">
ACK </span><span style="font-family:宋体;">的是在既不处于总线关闭态也不处于休眠态的所有接收单元中,接收到正常消息的单元(发送单元不发送</span><span lang="EN-US">ACK</span><span style="font-family:宋体;">)。所谓正常消息是指不含填充错误、格式错误、</span><span lang="EN-US">CRC </span><span style="font-family:宋体;">错误的消息。</span>
<span style="font-family:宋体;">帧结束,这个段也比较简单,标准帧和扩展帧在这个段格式一样,由</span><span lang="EN-US">7</span><span style="font-family:宋体;">个位的隐性位组成。</span>
<span lang="EN-US"> </span><span style="font-family:宋体;">至此,数据帧的</span><span lang="EN-US">7</span><span style="font-family:宋体;">个段就介绍完了,其他帧的介绍,请大家参考光盘的</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">入门书</span><span lang="EN-US">.pdf</span><span style="font-family:宋体;">相关章节。接下来,我们再来看看</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">的位时序。</span>
<span style="font-family:宋体;">由发送单元在非同步的情况下发送的每秒钟的位数称为位速率。一个位可分为</span><span lang="EN-US"> 4 </span><span style="font-family:宋体;">段。</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">同步段(</span><span lang="EN-US">SS</span><span style="font-family:宋体;">)</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">传播时间段(</span><span lang="EN-US">PTS</span><span style="font-family:宋体;">)</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">相位缓冲段</span><span lang="EN-US">1</span><span style="font-family:宋体;">(</span><span lang="EN-US">PBS1</span><span style="font-family:宋体;">)</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">相位缓冲段</span><span lang="EN-US">2</span><span style="font-family:宋体;">(</span><span lang="EN-US">PBS2</span><span style="font-family:宋体;">)</span>
<span style="font-family:宋体;">这些段又由可称为</span><span lang="EN-US"> Time
Quantum</span><span style="font-family:宋体;">(以下称为</span><span lang="EN-US">Tq</span><span style="font-family:宋体;">)的最小时间单位构成。</span>
<span lang="EN-US">1 </span><span style="font-family:宋体;">位分为</span><span lang="EN-US">4 </span><span style="font-family:宋体;">个段,每个段又由若干个</span><span lang="EN-US">Tq </span><span style="font-family:宋体;">构成,这称为位时序。</span>
<span lang="EN-US">1 </span><span style="font-family:宋体;">位由多少个</span><span lang="EN-US">Tq </span><span style="font-family:宋体;">构成、每个段又由多少个</span><span lang="EN-US">Tq </span><span style="font-family:宋体;">构成等,可以任意设定位时序。通过设定位时序,多个单元可同时采样,也可任意设定采样点。各段的作用和</span><span lang="EN-US"> Tq </span><span style="font-family:宋体;">数如表</span><span lang="EN-US">30.1.2</span><span style="font-family:宋体;">所示:</span>
<span lang="EN-US"></span></p><center>
<ignore_js_op>
<img src="data/attach/1908/34g9po57sxmccetbkbbo7pr4k9sgiyc8.jpg" alt=" " title=" ">
</ignore_js_op>
</center>
<span style="font-family:宋体;"><div style="text-align: center;">表<span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">30.1.2 一个</span>位各段及其作用<span style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">
</span><span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;"> </span></div></span><p><span lang="EN-US">1</span><span style="font-family:宋体;">个位的构成如图</span><span lang="EN-US">30.1.8</span><span style="font-family:宋体;">所示:</span>
<span lang="EN-US"></span></p><center>
<ignore_js_op>
<img src="data/attach/1908/d0vwnnvod5rut0faqr0lgcb3kknsmp4w.jpg" alt=" " title=" ">
</ignore_js_op>
</center>
<span style="font-family:宋体;"><div style="text-align: center;">图<span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">30.1.8 </span>一个位的构成<span style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">
</span><span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;"> </span></div></span><p><span style="font-family:宋体;">上图的采样点,是指读取总线电平,并将读到的电平作为位值的点。位置在</span><span lang="EN-US"> PBS1 </span><span style="font-family:宋体;">结束处。根据这个位时序,我们就可以计算</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">通信的波特率了。具体计算方法,我们等下再介绍,前面提到的</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">协议具有仲裁功能,下面我们来看看是如何实现的。</span>
<span style="font-family:宋体;">在总线空闲态,最先开始发送消息的单元获得发送权。</span>
<span style="font-family:宋体;">当多个单元同时开始发送时,各发送单元从仲裁段的第一位开始进行仲裁。连续输出显性电平最多的单元可继续发送。实现过程,如图</span><span lang="EN-US">30.1.9</span><span style="font-family:宋体;">所示:</span>
<span lang="EN-US"></span></p><center>
<ignore_js_op>
<img src="data/attach/1908/w7n53qoscygjebpxvbn7av00edw9cd57.jpg" alt=" " title=" ">
</ignore_js_op>
</center>
<span style="font-family:宋体;"><div style="text-align: center;">图<span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">30.1.9 CAN</span>总线仲裁过程<span style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">
</span><span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;"> </span></div></span><p><span style="font-family:宋体;">上图中,单元</span><span lang="EN-US">1</span><span style="font-family:宋体;">和单元</span><span lang="EN-US">2</span><span style="font-family:宋体;">同时开始向总线发送数据,开始部分他们的数据格式是一样的,故无法区分优先级,直到</span><span lang="EN-US">T</span><span style="font-family:宋体;">时刻,单元</span><span lang="EN-US">1</span><span style="font-family:宋体;">输出隐性电平,而单元</span><span lang="EN-US">2</span><span style="font-family:宋体;">输出显性电平,此时单元</span><span lang="EN-US">1</span><span style="font-family:宋体;">仲裁失利,立刻转入接收状态工作,不再与单元</span><span lang="EN-US">2</span><span style="font-family:宋体;">竞争,而单元</span><span lang="EN-US">2</span><span style="font-family:宋体;">则顺利获得总线使用权,继续发送自己的数据。这就实现了仲裁,让连续发送显性电平多的单元获得总线使用权。</span>
<span lang="EN-US"> </span><span style="font-family:宋体;">通过以上介绍,我们对</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">总线有了个大概了解(详细介绍参考光盘的:《</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">入门书</span><span lang="EN-US">.pdf</span><span style="font-family:宋体;">》),接下来我们介绍下</span><span lang="EN-US">STM32</span><span style="font-family:宋体;">的</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">控制器。</span>
<span lang="EN-US"> STM32</span><span style="font-family:宋体;">自带的是</span><span lang="EN-US">bxCAN</span><span style="font-family:宋体;">,即基本扩展</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">。它支持</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">协议</span><span lang="EN-US">2.0A</span><span style="font-family:宋体;">和</span><span lang="EN-US">2.0B</span><span style="font-family:宋体;">。它的设计目标是,以最小的</span><span lang="EN-US">CPU</span><span style="font-family:宋体;">负荷来高效处理大量收到的报文。它也支持报文发送的优先级要求</span><span lang="EN-US">(</span><span style="font-family:宋体;">优先级特性可软件配置</span><span lang="EN-US">)</span><span style="font-family:宋体;">。对于安全紧要的应用,</span><span lang="EN-US">bxCAN</span><span style="font-family:宋体;">提供所有支持时间触发通信模式所需的硬件功能。</span>
<span lang="EN-US">STM32</span><span style="font-family:宋体;">的</span><span lang="EN-US">bxCAN</span><span style="font-family:宋体;">的主要特点有:</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">支持</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">协议</span><span lang="EN-US">2.0A</span><span style="font-family:宋体;">和</span><span lang="EN-US">2.0B</span><span style="font-family:宋体;">主动模式</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">波特率最高达</span><span lang="EN-US">1Mbps</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">支持时间触发通信</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">具有</span><span lang="EN-US">3</span><span style="font-family:宋体;">个发送邮箱</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">具有</span><span lang="EN-US">3</span><span style="font-family:宋体;">级深度的</span><span lang="EN-US">2</span><span style="font-family:宋体;">个接收</span><span lang="EN-US">FIFO</span>
<span style="font-family:Wingdings;" lang="EN-US">l<span style="font:7pt 'Times New Roman';"> </span></span><span style="font-family:宋体;">可变的过滤器组(最多</span><span lang="EN-US">28</span><span style="font-family:宋体;">个)</span>
<span style="font-family:宋体;">在</span><span lang="EN-US">STM32</span><span style="font-family:宋体;">互联型产品中,带有</span><span lang="EN-US">2</span><span style="font-family:宋体;">个</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">控制器,而我们使用的</span><span lang="EN-US">STM32F103ZET6</span><span style="font-family:宋体;">属于增强型,不是互联型,只有</span><span lang="EN-US">1</span><span style="font-family:宋体;">个</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">控制器。双</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">的框图如图</span><span lang="EN-US">30.1.10</span><span style="font-family:宋体;">所示:</span>
<span lang="EN-US"></span></p><center><center>
<ignore_js_op>
<img src="data/attach/1908/j6l93vb8hwe8s9y70bgdd2onygdmyo0z.jpg" alt=" " title=" ">
</ignore_js_op>
</center></center>
<span style="font-family:宋体;"><div style="text-align: center;">图<span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">30.1.10 </span>双<span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">CAN</span>框图<span style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;">
</span><span lang="EN-US" style="font-family: "PingFang SC", "Lantinghei SC", "Microsoft YaHei", "HanHei SC", "Helvetica Neue", "Open Sans", Arial, "Hiragino Sans GB", 微软雅黑, STHeiti, "WenQuanYi Micro Hei", SimSun, sans-serif;"> </span></div></span><p><span style="font-family:宋体;">从图中可以看出两个</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">都分别拥有自己的发送邮箱和接收</span><span lang="EN-US">FIFO</span><span style="font-family:宋体;">,但是他们共用</span><span lang="EN-US">28</span><span style="font-family:宋体;">个滤波器。通过</span><span lang="EN-US">CAN_FMR</span><span style="font-family:宋体;">寄存器的设置,可以设置滤波器的分配方式。</span>
<span lang="EN-US"> STM32</span><span style="font-family:宋体;">的标识符过滤是一个比较复杂的东东,它的存在减少了</span><span lang="EN-US">CPU</span><span style="font-family:宋体;">处理</span><span lang="EN-US">CAN</span><span style="font-family:宋体;">通信的开销。</span><span lang="EN-US">STM32</span><span style="font-family:宋体;">的过滤器组最多有</span><span lang="EN-US">28</span><span style="font-family:宋体;">个(互联型),但是</span><span lang="EN-US">STM32F103ZET6</span><span style="font-family:宋体;">只有</span><span lang="EN-US">14</span><span style="font-family:宋体;">个(增强型),每个滤波器组</span><span lang="EN-US">x</span><span style="font-family:宋体;">由</span><span lang="EN-US">2</span><span style="font-family:宋体;">个</span><span lang="EN-US">32</span><span style="font-family:宋体;">为寄存器,</span><span lang="EN-US">CAN_FxR1</span><span style="font-family:宋体;">和</span><span lang="EN-US">CAN_FxR2</span><span style="font-family:宋体;">组成。</span>
<span lang="EN-US">STM32</span><span style="font-family:宋体;">每个过滤器组的位宽都可以独立配置,以满足应用程序的不同需求。根据位宽的不同,每个过滤器组可提供:</span>
<span style="font-family:宋体;">●</span><span lang="EN-US"> 1</span><span style="font-family:宋体;">个</span><span lang="EN-US">32</span><span style="font-family:宋体;">位过滤器,包括:</span><span lang="EN-US">STDID[10:0]</span><span style="font-family:宋体;">、</span><span lang="EN-US">EXTID[17:0]</span><span style="font-family:宋体;">、</span><span lang="EN-US">IDE</span><span style="font-family:宋体;">和</span><span lang="EN-US">RTR</span><span style="font-family:宋体;">位</span>
<span style="font-family:宋体;">●</span><span lang="EN-US"> 2</span><span style="font-family:宋体;">个</span><span lang="EN-US">16</span><span style="font-family:宋体;">位过滤器,包括:</span><span lang="EN-US">STDID[10:0]</span><span style="font-family:宋体;">、</span><span lang="EN-US">IDE</span><span style="font-family:宋体;">、</span><span lang="EN-US">RTR</span><span style="font-family:宋体;">和</span><span lang="EN-US">EXTID[17:15]</span><span style="font-family:宋体;">位</span>
<span style="font-family:宋体;">此外过滤器可配置为,屏蔽位模式和标识符列表模式。</span>
<span style="font-family:宋体;">在屏蔽位模式下,标识符寄存器和屏蔽寄存器一起,指定报文标识符的任何一位,应该按照“必须匹配”或“不用关心”处理。</span>
<span style="font-family:宋体;">而在标识符列表模式下,屏蔽寄存器也被当作标识符寄存器用。因此,不是采用一个标识符加一个屏蔽位的方式,而是使用</span><span lang="EN-US">2</span><span style="font-family:宋体;">个标识符寄存器。接收报文标识符的每一位都必须跟过滤器标识符相同。</span>
<span style="font-family:宋体;">通过</span><span lang="EN-US">CAN_FMR</span><span style="font-famil
30.3 软件设计
打开上一章的工程,首先在HARDWARE文件夹下新建一个CAN的文件夹,然后新建一个can.c和can.h的文件保存在CAN文件夹下,并将CAN文件夹加入头文件包含路径。
打开can.c文件,输入如下代码:
#include "can.h"
#include "led.h"
#include "delay.h"
#include "usart.h"
//CAN初始化
//tsjw:重新同步跳跃时间单元.范围:1~3;
//tbs2:时间段2的时间单元.范围:1~8;
//tbs1:时间段1的时间单元.范围:1~16;
//brp :波特率分频器.范围:1~1024;(实际要加1,也就是1~1024) tq=(brp)*tpclk1
//注意以上参数任何一个都不能设为0,否则会乱.
//波特率=Fpclk1/((tbs1+tbs2+1)*brp);
//mode:0,普通模式;1,回环模式;
//Fpclk1的时钟在初始化的时候设置为36M,如果设置CAN_Normal_Init(1,8,7,5,1);
//则波特率为:36M/((8+7+1)*5)=450Kbps
//返回值:0,初始化OK;
// 其他,初始化失败;
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode)
{
u16 i=0;
if(tsjw==0||tbs2==0||tbs1==0||brp==0)return 1;
tsjw-=1;//先减去1.再用于设置
tbs2-=1;
tbs1-=1;
brp-=1;
RCC->APB2ENR|=1<<2; //使能PORTA时钟
GPIOA->CRH&=0XFFF00FFF;
GPIOA->CRH|=0X000B8000;//PA11 RX,PA12 TX推挽输出
GPIOA->ODR|=3<<11;
RCC->APB1ENR|=1<<25;//使能CAN时钟 CAN使用的是APB1的时钟(max:36M)
CAN->MCR=0x0000; //退出睡眠模式(同时设置所有位为0)
CAN->MCR|=1<<0; //请求CAN进入初始化模式
while((CAN->MSR&1<<0)==0)
{
i++;
if(i>100)return 2;//进入初始化模式失败
}
CAN->MCR|=0<<7; //非时间触发通信模式
CAN->MCR|=0<<6; //软件自动离线管理
CAN->MCR|=0<<5; //睡眠模式通过软件唤醒(清除CAN->MCR的SLEEP位)
CAN->MCR|=1<<4; //禁止报文自动传送
CAN->MCR|=0<<3; //报文不锁定,新的覆盖旧的
CAN->MCR|=0<<2; //优先级由报文标识符决定
CAN->BTR=0x00000000;//清除原来的设置.
CAN->BTR|=mode<<30; //模式设置 0,普通模式;1,回环模式;
CAN->BTR|=tsjw<<24; //重新同步跳跃宽度(Tsjw)为tsjw+1个时间单位
CAN->BTR|=tbs2<<20; //Tbs2=tbs2+1个时间单位
CAN->BTR|=tbs1<<16; //Tbs1=tbs1+1个时间单位
CAN->BTR|=brp<<0; //分频系数(Fdiv)为brp+1
//波特率:Fpclk1/((Tbs1+Tbs2+1)*Fdiv)
CAN->MCR&=~(1<<0); //请求CAN退出初始化模式
while((CAN->MSR&1<<0)==1)
{
i++;
if(i>0XFFF0)return 3;//退出初始化模式失败
}
//过滤器初始化
CAN->FMR|=1<<0; //过滤器组工作在初始化模式
CAN->FA1R&=~(1<<0); //过滤器0不激活
CAN->FS1R|=1<<0; //过滤器位宽为32位.
CAN->FM1R|=0<<0; //过滤器0工作在标识符屏蔽位模式
CAN->FFA1R|=0<<0; //过滤器0关联到FIFO0
CAN->sFilterRegister[0].FR1=0X00000000;//32位ID
CAN->sFilterRegister[0].FR2=0X00000000;//32位MASK
CAN->FA1R|=1<<0; //激活过滤器0
CAN->FMR&=0<<0; //过滤器组进入正常模式
#if CAN_RX0_INT_ENABLE
//使用中断接收
CAN->IER|=1<<1; //FIFO0消息挂号中断允许.
MY_NVIC_Init(1,0,USB_LP_CAN_RX0_IRQChannel,2);//组2
#endif
return 0;
}
//id:标准ID(11位)/扩展ID(11位+18位)
//ide:0,标准帧;1,扩展帧
//rtr:0,数据帧;1,远程帧
//len:要发送的数据长度(固定为8个字节,在时间触发模式下,有效数据为6个字节)
//*dat:数据指针.
//返回值:0~3,邮箱编号.0XFF,无有效邮箱.
u8 Can_Tx_Msg(u32 id,u8 ide,u8 rtr,u8 len,u8 *dat)
{
u8 mbox;
if(CAN->TSR&(1<<26))mbox=0; //邮箱0为空
else if(CAN->TSR&(1<<27))mbox=1; //邮箱1为空
else if(CAN->TSR&(1<<28))mbox=2; //邮箱2为空
else return 0XFF; //无空邮箱,无法发送
CAN->sTxMailBox[mbox].TIR=0; //清除之前的设置
if(ide==0) //标准帧
{
id&=0x7ff;//取低11位stdid
id<<=21;
}else //扩展帧
{
id&=0X1FFFFFFF;//取低32位extid
id<<=3;
}
CAN->sTxMailBox[mbox].TIR|=id;
CAN->sTxMailBox[mbox].TIR|=ide<<2;
CAN->sTxMailBox[mbox].TIR|=rtr<<1;
len&=0X0F;//得到低四位
CAN->sTxMailBox[mbox].TDTR&=~(0X0000000F);
CAN->sTxMailBox[mbox].TDTR|=len; //设置DLC.
//待发送数据存入邮箱.
CAN->sTxMailBox[mbox].TDHR=(((u32)dat[7]<<24)|
((u32)dat[6]<<16)|
((u32)dat[5]<<8)|
((u32)dat[4]));
CAN->sTxMailBox[mbox].TDLR=(((u32)dat[3]<<24)|
((u32)dat[2]<<16)|
((u32)dat[1]<<8)|
((u32)dat[0]));
CAN->sTxMailBox[mbox].TIR|=1<<0; //请求发送邮箱数据
return mbox;
}
//获得发送状态.
//mbox:邮箱编号;
//返回值:发送状态. 0,挂起;0X05,发送失败;0X07,发送成功.
u8 Can_Tx_Staus(u8 mbox)
{
u8 sta=0;
switch (mbox)
{
case 0:
sta |= CAN->TSR&(1<<0); //RQCP0
sta |= CAN->TSR&(1<<1); //TXOK0
sta |=((CAN->TSR&(1<<26))>>24); //TME0
break;
case 1:
sta |= CAN->TSR&(1<<8)>>8; //RQCP1
sta |= CAN->TSR&(1<<9)>>8; //TXOK1
sta |=((CAN->TSR&(1<<27))>>25); //TME1
break;
case 2:
sta |= CAN->TSR&(1<<16)>>16; //RQCP2
sta |= CAN->TSR&(1<<17)>>16; //TXOK2
sta |=((CAN->TSR&(1<<28))>>26); //TME2
break;
default:
sta=0X05;//邮箱号不对,肯定失败.
break;
}
return sta;
}
//得到在FIFO0/FIFO1中接收到的报文个数.
//fifox:0/1.FIFO编号;
//返回值:FIFO0/FIFO1中的报文个数.
u8 Can_Msg_Pend(u8 fifox)
{
if(fifox==0)return CAN->RF0R&0x03;
else if(fifox==1)return CAN->RF1R&0x03;
else return 0;
}
//接收数据
//fifox:邮箱号
//id:标准ID(11位)/扩展ID(11位+18位)
//ide:0,标准帧;1,扩展帧
//rtr:0,数据帧;1,远程帧
//len:接收到的数据长度(固定为8个字节,在时间触发模式下,有效数据为6个字节)
//dat:数据缓存区
void Can_Rx_Msg(u8 fifox,u32 *id,u8 *ide,u8 *rtr,u8 *len,u8 *dat)
{
*ide=CAN->sFIFOMailBox[fifox].RIR&0x04;//得到标识符选择位的值
if(*ide==0)//标准标识符
{
*id=CAN->sFIFOMailBox[fifox].RIR>>21;
}else //扩展标识符
{
*id=CAN->sFIFOMailBox[fifox].RIR>>3;
}
*rtr=CAN->sFIFOMailBox[fifox].RIR&0x02; //得到远程发送请求值.
*len=CAN->sFIFOMailBox[fifox].RDTR&0x0F;//得到DLC
//*fmi=(CAN->sFIFOMailBox[FIFONumber].RDTR>>8)&0xFF;//得到FMI
//接收数据
dat[0]=CAN->sFIFOMailBox[fifox].RDLR&0XFF;
dat[1]=(CAN->sFIFOMailBox[fifox].RDLR>>8)&0XFF;
dat[2]=(CAN->sFIFOMailBox[fifox].RDLR>>16)&0XFF;
dat[3]=(CAN->sFIFOMailBox[fifox].RDLR>>24)&0XFF;
dat[4]=CAN->sFIFOMailBox[fifox].RDHR&0XFF;
dat[5]=(CAN->sFIFOMailBox[fifox].RDHR>>8)&0XFF;
dat[6]=(CAN->sFIFOMailBox[fifox].RDHR>>16)&0XFF;
dat[7]=(CAN->sFIFOMailBox[fifox].RDHR>>24)&0XFF;
if(fifox==0)CAN->RF0R|=0X20;//释放FIFO0邮箱
else if(fifox==1)CAN->RF1R|=0X20;//释放FIFO1邮箱
}
#if CAN_RX0_INT_ENABLE //使能RX0中断
//中断服务函数
void USB_LP_CAN1_RX0_IRQHandler(void)
{
u8 rxbuf[8];
u32 id;
u8 ide,rtr,len;
Can_Rx_Msg(0,&id,&ide,&rtr,&len,rxbuf);
printf("id:%d ",id);
printf("ide:%d ",ide);
printf("rtr:%d ",rtr);
printf("len:%d ",len);
printf("rxbuf[0]:%d ",rxbuf[0]);
printf("rxbuf[1]:%d ",rxbuf[1]);
printf("rxbuf[2]:%d ",rxbuf[2]);
printf("rxbuf[3]:%d ",rxbuf[3]);
printf("rxbuf[4]:%d ",rxbuf[4]);
printf("rxbuf[5]:%d ",rxbuf[5]);
printf("rxbuf[6]:%d ",rxbuf[6]);
printf("rxbuf[7]:%d ",rxbuf[7]);
}
#endif
//can发送一组数据(固定格式:ID为0X12,标准帧,数据帧)
//len:数据长度(最大为8)
//msg:数据指针,最大为8个字节.
//返回值:0,成功;
// 其他,失败;
u8 Can_Send_Msg(u8* msg,u8 len)
{
u8 mbox;
u16 i=0;
mbox=Can_Tx_Msg(0X12,0,0,len,msg);
while((Can_Tx_Staus(mbox)!=0X07)&&(i<0XFFF))i++;//等待发送结束
if(i>=0XFFF)return 1; //发送失败?
return 0; //发送成功;
}
//can口接收数据查询
//buf:数据缓存区;
//返回值:0,无数据被收到;
// 其他,接收的数据长度;
u8 Can_Receive_Msg(u8 *buf)
{
u32 id;
u8 ide,rtr,len;
if(Can_Msg_Pend(0)==0)return 0; //没有接收到数据,直接退出
Can_Rx_Msg(0,&id,&ide,&rtr,&len,buf); //读取数据
if(id!=0x12||ide!=0||rtr!=0)len=0; //接收错误
return len;
}
此部分代码总共8个函数,我们挑其中几个比较重要的函数简单介绍下,首先是:CAN_Mode_Init函数。该函数用于CAN的初始化,该函数带有5个参数,可以设置CAN通信的波特率和工作模式等,在该函数中,我们就是按30.1节末尾的介绍来初始化的,本章中,我们设计滤波器组0工作在32位标识符屏蔽模式,从设计值可以看出,该滤波器是不会对任何标识符进行过滤的,因为所有的标识符位都被设置成不需要关心,这样设计,主要是方便大家实验。
第二个函数,Can_Tx_Msg函数。该函数用于CAN报文的发送,该函数先查找空的发送邮箱,然后设置标识符ID等信息,最后写入数据长度和数据,并请求发送,实现一次报文的发送。
第三个函数,Can_Msg_Pend函数。该函数用于查询接收FIFOx(x=0/1)是否为空,如果返回0,则表示FIFOx空,如果为其他值,则表示FIFOx有数据。
第四个函数,Can_Rx_Msg函数。该函数用于CAN报文的接收,该函数先读取标识符,然后读取数据长度,并读取接收到的数据,最后释放邮箱数据。
can.c里面,还包含了中断接收的配置,通过can.h的CAN_RX0_INT_ENABLE宏定义,来配置是否使能中断接收,本章我们不开启中断接收的。其他函数我们就不一一介绍了,都比较简单,大家自行理解即可。保存can.c,并把该文件加入HARDWARE组下面,然后我们打开can.h在里面输入如下代码:
#ifndef __CAN_H
#define __CAN_H
#include "sys.h"
//CAN接收RX0中断使能
#define CAN_RX0_INT_ENABLE 0 //0,不使能;1,使能.
u8 CAN_Mode_Init(u8 tsjw,u8 tbs2,u8 tbs1,u16 brp,u8 mode); //CAN初始化
u8 Can_Tx_Msg(u32 id,u8 ide,u8 rtr,u8 len,u8 *dat); //发送数据
u8 Can_Msg_Pend(u8 fifox); //查询邮箱报文
void Can_Rx_Msg(u8 fifox,u32 *id,u8 *ide,u8 *rtr,u8 *len,u8 *dat);//接收数据
u8 Can_Tx_Staus(u8 mbox); //返回发送状态
u8 Can_Send_Msg(u8* msg,u8 len); //发送数据
u8 Can_Receive_Msg(u8 *buf); //接收数据
#endif
其中CAN_RX0_INT_ENABLE用于设置是否使能中断接收,本章我们不用中断接收,故设置为0。保存can.h。最后,我们在test.c里面,修改main函数如下:
int main(void)
{
u8 key;
u8 i=0,t=0;
u8 cnt=0;
u8 canbuf[8];
u8 res;
u8 mode=1;//CAN工作模式;0,普通模式;1,环回模式
Stm32_Clock_Init(9); //系统时钟设置
uart_init(72,9600); //串口初始化为9600
delay_init(72); //延时初始化
LED_Init(); //初始化与LED连接的硬件接口
LCD_Init(); //初始化LCD
usmart_dev.init(72); //初始化USMART
KEY_Init(); //按键初始化
CAN_Mode_Init(1,8,7,5,mode);//CAN初始化,波特率450Kbps
POINT_COLOR=RED;//设置字体为红 {MOD}
LCD_ShowString(60,50,200,16,16,"WarShip STM32");
LCD_ShowString(60,70,200,16,16,"CAN TEST");
LCD_ShowString(60,90,200,16,16,"ATOM@ALIENTEK");
LCD_ShowString(60,110,200,16,16,"2012/9/11");
LCD_ShowString(60,130,200,16,16,"LoopBack Mode");
LCD_ShowString(60,150,200,16,16,"KEY0:Send WK_UP:Mode");//显示提示信息
POINT_COLOR=BLUE;//设置字体为蓝 {MOD}
LCD_ShowString(60,170,200,16,16,"Count:"); //显示当前计数值
LCD_ShowString(60,190,200,16,16,"Send Data:"); //提示发送的数据
LCD_ShowString(60,250,200,16,16,"Receive Data:"); //提示接收到的数据
while(1)
{
key=KEY_Scan(0);
if(key==KEY_RIGHT)//KEY0按下,发送一次数据
{
for(i=0;i<8;i++)
{
canbuf=cnt+i;//填充发送缓冲区
if(i<4)LCD_ShowxNum(60+i*32,210,canbuf,3,16,0X80); //显示数据
else LCD_ShowxNum(60+(i-4)*32,230,canbuf,3,16,0X80); //显示数据
}
res=Can_Send_Msg(canbuf,8);//发送8个字节
if(res)LCD_ShowString(60+80,190,200,16,16,"Failed"); //提示发送失败
else LCD_ShowString(60+80,190,200,16,16,"OK "); //提示发送成功
}else if(key==KEY_UP)//WK_UP按下,改变CAN的工作模式
{
mode=!mode;
CAN_Mode_Init(1,8,7,5,mode);//CAN模式初始化,波特率450Kbps
POINT_COLOR=RED;//设置字体为红 {MOD}
if(mode==0)//普通模式,需要2个开发板
{
LCD_ShowString(60,130,200,16,16,"Nnormal Mode ");
}else //回环模式,一个开发板就可以测试了.
{
LCD_ShowString(60,130,200,16,16,"LoopBack Mode");
}
POINT_COLOR=BLUE;//设置字体为蓝 {MOD}
}
key=Can_Receive_Msg(canbuf);
if(key)//接收到有数据
{
LCD_Fill(60,270,130,310,WHITE);//清除之前的显示
for(i=0;i<key;i++)
{
if(i<4)LCD_ShowxNum(60+i*32,270,canbuf,3,16,0X80); //显示数据
else LCD_ShowxNum(60+(i-4)*32,290,canbuf,3,16,0X80); //显示数据
}
}
t++;
delay_ms(10);
if(t==20)
{
LED0=!LED0;//提示系统正在运行
t=0;
cnt++;
LCD_ShowxNum(60+48,170,cnt,3,16,0X80); //显示数据
}
}
}
此部分代码,我们主要关注下CAN_Mode_Init(1,8,7,5,mode);该函数用于设置波特率和CAN的模式,根据前面的波特率计算公式,我们知道这里的波特率被初始化为450Kbps。mode参数用于设置CAN的工作模式(普通模式/环回模式),通过WK_UP按键,可以随时切换模式。cnt是一个累加数,一旦KEY_RIGHT(KEY0)按下,就以这个数位基准连续发送5个数据。当CAN总线收到数据的时候,就将收到的数据直接显示在LCD屏幕上。
30.4 下载验证
在代码编译成功之后,我们通过下载代码到ALIENTEK战舰STM32开发板上,得到如图30.4.1所示:
图30.4.1 程序运行效果图
伴随DS0的不停闪烁,提示程序在运行。默认我们是设置的环回模式,此时,我们按下KEY0就可以在LCD模块上面看到自发自收的数据(如上图所示),如果我们选择普通模式(通过WK_UP按键切换),就必须连接两个开发板的CAN接口,然后就可以互发数据了。如图30.4.2所示:
图30.4.2 CAN普通模式数据收发测试
上图中,左侧的图片来自开发板A,发送了8个数据,右侧来自开发板B,收到了来自开发板A的8个数据。
一周热门 更多>