CAN-bus现场总线基础教程【第3章】CAN控制器驱动-SJA1000编程基础(9)
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
第3章 CAN 控制器驱动
1.1 SJA1000编程基础
1.1.1 MCU 访问SJA1000
SJA1000使用并行总线接口与MCU 连接,对MCU 来说,SJA1000可以认为是1个外扩的RAM 芯片,51系列MCU 通过地址线、数据线和控制线与SJA1000连接,如图3.1所示。
AD[0:7]是低8位地址与数据总线复用的,MCU 在操作总线时,在该接口上先输出低8位地址线,然后再进行数据操作(读或写)。SJA1000内部带有地址锁存器,由ALE 信号实现数据与地址的分离。因为SJA1000的地址宽度为8位,所以寻址空间范围是0x00~0xFF 。假如每个地址都对应一个寄存器,那么SJA1000最多支持256个寄存器。而实际上SJA1000在BasicCAN (CAN2.0A )模式下只有32个寄存器,在FullCAN (CAN2.0B )模式下则有128个寄存器。
虽然SJA1000寄存器的访问地址会因为硬件设计不同而不同,但SJA1000内部寄存器的位置关系是固定的。如果我们给SJA1000每个内部寄存器的地址都定义绝对地址(如程序清单3.1所示),那么在硬件设计发生变化时,特别是器件编址变化时,要修改的寄存器地址定义将会非常多。
为了提高驱动的可移植性,在实际访问SJA1000内部寄存器时,常采用基地址加偏移量的方式进行寄存器访问(如程序清单3.2所示)。如果把SJA1000内部寄存器看做数组的话,那基地址就是这个数组的首地址,偏移量就是数组的下标,即成员在数组中的位置。
程序清单3.1 采用绝对编址的寄存器定义
1 #define REG_CAN_MOD 0xA000 // 内部控制寄存器
2 #define REG_CAN_CMR 0xA001 // 命令寄存器
3 #define REG_CAN_SR 0xA002 // 状态寄存器
4 #define REG_CAN_IR 0xA003 // 中断寄存器
5 #define REG_CAN_IER
0xA004
// 中断使能寄存器
6
......
程序清单3.2 采用基地址加偏移量方式的寄存器定义
7 #define REG_BASE_ADD
0xA000
// SJA1000寄存器基地址 8 #define
REG_CAN_MOD 0x00 // 内部控制寄存器 9 #define REG_CAN_CMR 0x01 // 命令寄存器 10 #define REG_CAN_SR 0x02 // 状态寄存器 11 #define REG_CAN_IR 0x03 // 中断寄存器 12 #define REG_CAN_IER
0x04
// 中断使能寄存器
13
......
通常MCU 的总线上会挂载很多器件,除了SJA1000外,可能还有RAM 和ROM 等器件。所以数据总线和地址总线上的信息并不一定都是给SJA1000的。因此当MCU 在访问SJA1000
图3.1 SJA1000与MCU 扩展总线连接
时,为了明确的表明下面的操作对象是SJA1000,MCU 会把SJA1000的片选(CS )信号拉低。 SJA1000的片选信号由MCU 未使用的高位地址线经过译码电路产生,译码电路的设计决定了MCU 访问SJA1000的基地址。MCU 产生片选信号有以下三种方法。
1. 线选法
所谓线选法,就是直接利用MCU 的空闲高位地址线作为扩展芯片的片选信号。优点是线路连接简单明了,无需另外增加译码电路,缺点是寻址范围不唯一(可通过多个地址访问扩展芯片),地址空间没有被充分利用,使用单个扩展芯片时可使用该方法。
2. 全地址译码法
全地址译码法利用译码器对未使用的高位地址线进行译码,使用译码器的输出作为扩展芯片的片选信号,常用的译码芯片有74HC138、74HC139、74HC154等。优点是扩展器件的每个存储单元具有唯一的访问地址,不存在地址重叠现象,对地址的空间的使用是连续的,能有效地利用地址空间,缺点是所需译码电路较多,外扩芯片较多时使用该方法。
3. 部分地址译码法
部分地址译码法对未使用的部分高位地址线进行译码。优点是使用译码电路较少,缺点是外扩器件地址范围有重叠。
在本设计中,通过并行总线扩展的器件只有SJA1000,可以采用线选法产生SJA1000的片选信号,但考虑到系统的扩展性,我们使用部分地址译码法来产生SJA1000的片选信号。如图3.2所示,SJA1000的片选信号通过对地址线A15~A12译码来得到。
74HC139内部包含了两个“2线—4线”译码器,其真值表如表3.1所示。根据图3.2中74HC139的电路连接关系,对比74HC139的真值表,我们可以发现,当A14为低电平、A15为高电平时,1Y2输出为低电平;当A12为低电平、A13为高电平并且1Y2为低电平时,2Y2输出为低电平,即SJA1000片选信号有效,所以只要MCU 在访问SJA1000时16位地址线上输出地址为0b1010 xxxx
xxxx xxxx ,SJA1000的片选信号就会有效。因此SJA1000的访问基址为0xA000~0xAF00,为了编写程序方便,在以后的例程中,MCU 访问SJA1000的基地址统一使用0xA000。 1.1.2 读写寄存器
在C 语言中,一般使用指针来访问存储空间,SJA1000按照存储器的方式与MCU 连接,所以我们可以通过指针的方式来访问SJA1000的寄存器,实现对SJA1000的操作控制。使用C 语言读写SJA1000寄存器的代码如程序清单3.3所示。
图3.2 SJA1000片选译码电路
程序清单3.3 读写SJA1000寄存器
14#define SJA_BASE_ADDR 0xA000 // 定义SJA1000访问基址
15xdata unsigned char *SJA_CS_Point = ( xdata unsigned char *) SJA_BASE_ADDR;
16// 写SJA1000寄存器
17void WriteSJAReg (unsigned char RegAdr,unsigned char Value)
18{
19*(SJA_CS_Point + RegAdr) = Value;
20return ;
21}
22// 读SJA1000寄存器
23unsigned char ReadSJAReg (unsigned char RegAdr)
24{
25return (*(SJA_CS_Point + RegAdr));
26}
程序清单3.3第1行定义了访问SJA1000的基地址,按照电路的连接关系,该值为0xA0000。第2行定义了一个指向外部存储空间的指针变量,并将其指向SJA1000访问的基地址,对
SJA1000寄存器的读写通过操作该指针来完成。
xdata是C51编译器用于指定存储类型的扩展关键字,表示用MOVX@DPTR指令访问的外部存储器空间。( xdata unsigned char *)完成强制类型转换功能,将SJA_BASE_ADDR强制转换为1个xdata unsigned char类型的指针。
程序清单3.3第3~13行定义了对SJA1000寄存器的读写函数,在下文中对SJA1000的所有操作都以这2个函数为基础实现的。
1.1.3 寄存器位操作
在控制SJA1000时,有时我们需要对某些寄存器进行位操作(只改写指定位,其它位保持不变),如进入复位模式(模式寄存器的第0位置1)、退出复位模式(模式寄存器的第0位清0)。对寄存器的位操作一般是通过“读、改、写”的方式实现,基本步骤如下:
●读寄存器的当前值;
●根据需要修改读到的值的指定位;
●将修改后的值写回寄存器。
基于以上原理,我们实现“设置寄存器位”和“清零寄存器位”函数,以方便对SJA1000的操作。
1.设置寄存器位
设置寄存器位函数用于将指定寄存器的指定位置1,具体实现见程序清单3.4。
程序清单3.4 设置寄存器位函数
27/****************************************************************************************** ** 函数名称:SetBitMask
28** 函数功能:设置指定寄存器的指定位为1
29** 输入参数:RegAdr->寄存器地址
30** BitValue ->设置的位值
31** 输出参数:无