CAN-bus现场总线基础教程【第3章】CAN控制器驱动-发送CAN帧(12)

第3章CAN控制器驱动

1.1 发送CAN帧

1.1.1 发送流程

SJA1000发送流程详见图3.1。在发送CAN报文之前,需要确认已经初始化SJA1000参数;通过读取状态寄存器SR,测试其位2和位3是否都为1;当这两位都为1时,填充报文到发送缓冲区,并设置发送模式,SJA1000将启动发送过程;当SR的第2位和第3位不同时为1时,表明上一次发送还未完成,需要等待发送完成,或终止发送。

图3.1 SJA1000发送流程示意图

1.发送缓冲区

SJA1000具有1个12字节的发送缓冲区,位于SJA1000内部RAM空间。要发送的帧报文可以通过寄存器16~28写入,也可以通过寄存器96~108写入或读出。

图3.2 SJA1000发送缓冲区结构

SJA1000的发送缓冲区可以存放1个完整的标准帧或扩展帧报文,对于不同格式的帧报文,在发送缓冲区的定义不同,如图3.2所示,在发送不同格式的帧报文时,需要按对应的格式填充发送缓冲区。

SJA1000发送缓冲区由帧信息、帧ID、帧数据三部分组成。

2.帧信息

FF位决定了帧格式。当该位为0时,表示发送缓冲区中存放的是标准帧,SJA1000按图3.2的(a)的格式解释发送缓冲区内容;当该位为1时,表示发送缓冲区中存放的是扩展帧,SJA1000按图3.2的(b)的格式解释发送缓冲区内容。

RTR位决定了是远程帧还是数据帧。当该位为0时,表示发送缓冲区中存放的是数据帧,帧数据的有效字节数由DLC决定;当该位为1时,表示发送缓冲区中存放的是远程帧,帧数据部分不会被发送。

DLC指示了帧数据的有效数据字节数。DLC作为CAN帧的一部分,其有效值0~15都可以被传输,当DLC为0~8时,指示了帧数据的有效字节数,大于8时,帧数据的有效字节数固定为8。

3.帧ID

根据帧信息中的FF位的不同,帧ID具有不同的长度。FF为0时,帧ID占用2字节;FF 为1时,帧ID占用4字节。

4.帧数据

帧数据部分最多可存放8字节数据,有效长度由帧信息中的RTR和DLC决定。

1.1.2 发送模式

SJA1000的发送模式通过CMR寄存器(寄存器地址1)设置,CMR寄存器定义详见表3.1。

表3.1 CMR寄存器定义

1.正常发送

正常发送时,在仲裁丢失或发送错误时,SJA1000会自动重发,直到发送成功或总线关闭。正常发送通过置位TR位完成。

2.单次发送

在一些应用中,自动重发没有意义,如传输语音信息,它允许部分数据丢失,但不能容忍传输延时。在这种应用中,一般会以固定的时间间隔发送数据,自动重发会导致后面的数据无法发送,出现传输延时。单次发送通过同时置位TR和AT完成,如果仲裁丢失或发生错误,SJA1000不会重发报文。

3.自发自收

自发自收产生一次带自接收特性的单次发送,在发送出错或仲裁丢失的情况下不会执行重复发送。在发送完成后,可以从接收缓冲区中读到已发送的报文。

4.终止发送

通过设置位AT可以终止正在发送的报文数据。

5.设置发送模式

在发送CAN报文的时候,主控制器首先需要把CAN报文写入到CAN控制器的报文发送缓冲区,写入这个缓冲区的报文并不会直接发送出去,而是需要一个触发信号去启动CAN控制器完成发送工作,这时就需要调用程序清单3.1中所示的SetSJASendCmd函数(设置需要的发送模式)来启动CAN控制器发送功能。

程序清单3.1 SJA1000设置发送模式

1 /******************************************************************************************

2 ** 函数名称: char SetSJASendCmd

3 ** 函数功能: 设置SJA1000发送类型,启动发送

4 ** 输入参数: cmd-> 发送命令,0-正常发送;1-单次发送;2-自发自收;0xff-终止发送

5 ** 输出参数: 无

6 ** 返回参数: 1 ->

发送成功;0

->

发送失败

7 ******************************************************************************************/ 8

char SetSJASendCmd(unsigned char cmd) 9 { 10 unsigned char ret; 11 switch(cmd) { 12 default: 13 case 0:

// 正常发送

14 ret = SetBitMask(REG_CAN_CMR,TR_BIT); 15 break;

16 case 1:

// 单次发送

17 ret = SetBitMask(REG_CAN_CMR,TR_BIT|AT_BIT); 18 break;

19 case 2:

// 自发自收

20 ret = SetBitMask(REG_CAN_CMR,TR_BIT|SRR_BIT); 21 break;

22 case 0xff:

// 终止发送

23 ret = SetBitMask(REG_CAN_CMR, A T_BIT); 24

break;

25 }

26 return ret; // 返回值

27

}

1.1.3 SJA1000发送函数

SJA1000的发送函数具体实现如程序清单3.2所示。

按照图3.3的流程,首先读取状态寄存器并判断发送缓冲区是否锁定(不可写)或正在发送(程序清单3.2第14行)。如果SJA1000处于空闲状态并且发送缓冲区可写,则根据发送数据的帧信息计算需要写到SJA1000发送缓冲区的字节数(程序清单3.2第17~38行),并写到发送缓冲区中,最后调用SetSJASendCmd 函数启动报文发送流程(程序清单3.2第42行)。

程序清单3.2 SJA1000发送函数

28 /****************************************************************************************** 29 ** 函数原型: char SJASendData(unsigned char *databuf, unsigned char cmd) 30

** 参数说明: databuf

->

发送CAN 帧缓冲区

图3.3 SJA1000发送流程

31** : cmd -> 发送命令,0-正常发送;1-单次发送;2-自发自收

32** 返回值 : 1 -> 发送成功;0 -> 发送失败

33** 说明: 将用户定义的数据写入SJA1000发送缓冲区,再将其发送到总线上。

34*****************************************************************************************/ 35char SJASendData(unsigned char *databuf, unsigned char cmd)

36{

37char status = 1;

38unsigned char len;

39unsigned char dlc;

40// 判断SJA1000发送缓冲区是否锁定,或正在发送

41if ((ReadSJAReg(REG_CAN_SR) & (TBS_BIT|TCS_BIT)) != (TBS_BIT|TCS_BIT)){

42status = 0; // 返回发送失败

43} else {

44dlc = (*databuf & 0x0f); // 从发送缓冲区的帧信息中取得CAN数据长度45if(dlc > 8) {

46dlc = 8;

47}

48switch(*databuf & 0xC0) { // 根据帧类型确定写到发送缓冲区中的数据长度49case 0x00: // 标准帧、数据帧

50len = STD_FRAMEID_LENTH + dlc + 1;

51break;

52case 0x40: // 标准帧、远程帧

53len=STD_FRAMEID_LENTH+1;

54break;

55case 0x80: // 扩展帧、数据帧

56len = EXT_FRAMEID_LENTH + dlc + 1;

57break;

58case 0xC0: // 扩展帧、远程帧

59len=EXT_FRAMEID_LENTH+1;

60break;

61default:

62len = 0;

63status = 0;

64break;

65}

66if (len > 0) {

67// 填充发送缓冲区

68WriteSJARegBlock(REG_CAN_TXFMINFO, databuf, len);

69SetSJASendCmd(cmd); // 设置发送命令

70status = 1;

71}

72}

73return(status);

74}

1.1.4 测试例程

SJA1000发送示例详见程序清单3.3。该例程设置SJA1000的CAN波特率为1Mbps,并以1秒的间隔发送CAN报文,CAN报文的格式(见程序清单3.3的第73~77行)为标准帧,数据长度为8字节,帧ID使用0x753,帧数据是0x55 0x55 0x55 0x55 0xaa 0xaa 0xaa 0xaa。

程序清单3.3 SJA100发送例程

75/****************************************************************************************** 76文件:main.c

77功能:设置SJA1000的波特率为1M,以1秒的间隔发送CAN帧

78******************************************************************************************/ 79#include "config.h" // 包含头文件

80// 定义帧ID长度

81#define STD_FRAMEID_LENTH 2 // 标准帧ID长度

82#define EXT_FRAMEID_LENTH 4 // 扩展帧ID长度

83#define D1 P1_0 // 定义LED

84#define SJA1000_RST P1_2 // 定义SJA1000复位引脚

85// 定义SJA1000访问基地址

86#define SJA_BASE_ADDR 0xA000

87xdata unsigned char *SJA_CS_Point = ( xdata unsigned char *) SJA_BASE_ADDR;

88

89void WriteSJAReg(unsigned char RegAdr, unsigned char Value) // 写SJA1000寄存器

90{

91...... // 程序代码请参考程序清单3.3

92}

93

94unsigned char ReadSJAReg(unsigned char RegAdr) // 读SJA1000寄存器

95{

96...... // 程序代码请参考程序清单3.3

97}

98

99char SetBitMask(unsigned char RegAdr, unsigned char BitValue) // 设置寄存器位

100{

101...... // 程序代码请参考程序清单3.4

102}

103char ClearBitMask(unsigned char RegAdr, unsigned char BitValue) // 清零寄存器位

104{

105...... // 程序代码请参考程序清单3.5

106}

107// 连续写多个寄存器

108char WriteSJARegBlock(unsigned char RegAddr,unsigned char *ValueBuf, unsigned char len)

109{

110...... // 程序代码请参考程序清单3.6

111}

112// 连续读多个寄存器

113char ReadSJARegBlock(unsigned char RegAddr, unsigned char *ValueBuf, unsigned char len)

114{

115...... // 程序代码请参考程序清单3.7

116}

117

118void SJA1000_Init(unsigned char btr0, unsigned char btr1, unsigned char *filter) // SJA1000初始化119{

120...... // 程序代码请参考程序清单3.13

121}

122

123char SetSJASendCmd(unsigned char cmd) // 设置发送命令

124{

125...... // 程序代码请参考程序清单3.14

126}

127

128char SJASendData(unsigned char *databuf, unsigned char cmd) // 发送CAN报文

129{

130...... // 程序代码请参考程序清单3.13

131}

132

133void timerInit(void) // Timer初始化

134{

135...... // 程序代码请参考程序清单3.9

136}

137void timerDelay(unsigned int n) // 延时(0.01 * n)秒

138{

139...... // 程序代码请参考程序清单3.9

140}

141// 定义验收滤波器的参数,接收所有帧

142unsigned char SJA_CAN_Filter[8] = {

1430x00, 0x00, 0x00, 0x00, // ACR0~ACR3

1440xff, 0xff, 0xff, 0xff // AMR0~AMR3

145};

146// CAN发送报文缓冲区

147unsigned char STD_SEND_BUFFER[11] = {

1480x08, // 帧信息,标准数据帧,数据长度=8 1490xEA, 0x60, // 帧ID=0x753

1500x55,0x55,0x55,0x55,0xaa,0xaa,0xaa,0xaa // 帧数据

151};

152

153void main(void) // 主函数,程序入口

154{

155timerInit(); // 初始化

156// D1 = 0;

157SJA1000_RST = 1; // 硬件复位SJA1000

158timerDelay(50); // 延时500ms

159SJA1000_RST = 0;

160SJA1000_Init(0x00,0x14,SJA_CAN_Filter); // 初始化SJA1000,设置波特率为1Mbps 161// 无限循环,main()函数不允许返回

162for (;;) {

163SJASendData(STD_SEND_BUFFER,0x0);

164timerDelay(100); // 延时1000ms

165}

166}

运行上面的程序后,使用CAN协议分析器CANScope可以获得如图3.4所示的CAN总线波形。波形图中各部分的含义如下:

①起始位:由单独的一个显性位表示;

②仲裁场波形:包括11位帧ID(这里帧ID为0x753)和一个RTR位(显性代表数据帧,隐性代表远程帧),帧ID表明了该帧的含义,RTR表明该帧的类型;

③控制场波形:图中代表数据长度为8字节;

④数据场波形:其对应的数据为0x55 0x55 0x55 0x55 0xaa 0xaa 0xaa 0xaa;

⑤CRC校验数据波形:CRC校验数据长度为15位,末尾跟随的隐性位是CRC界定符;

⑥应答位:由应答间隙和应答界定符组成,接收到正确CAN帧的节点会在应答间隙期间通过发送显性位的方式来应答;

⑦结束标志:由7个隐性位组成。

图3.4 1Mbps波特率的CAN总线波形图

图3.4是通过CAN总线协议分析器在一条并接了终端电阻,传输波特率高达1Mbps的CAN 总线上抓获的,虽然此时的信号波形不是理想的方波(有一点过冲,这种情况的发生与CAN

收发器自身电器特性和终端电阻都有关系),但是整个传输波形还是比较稳定和干净的,干扰

信号成分很少。如果不接终端电阻,并且传输波特率较高时,信号过冲现象将会非常严重,因为阻抗在线缆末端的突变会引起传输信号的大量反射,并叠加在正常传输的信号上,导致CAN 总线上信号传输质量大幅度降低,传输数据容易出错。

图3.5 250kbps波特率的CAN总线波形图

在测试过程中我们也截取了CAN总线上波特率为250kbps且不接终端电阻时的波形,详见图3.5。从图中可以看见总线上波形确实严重失真,信号过冲也非常明显,而且跳变沿时间较长,所以无法进行更高速率的数据传输(短距离传输测试时数据收发能达到的最高波特率仅为

250Kbps)。通过对图3.4和图3.5的比较和分析也很容易看出终端电阻在CAN总线通信中的重要性。

相关主题
相关文档
最新文档