1、CAN通信协议基础知识
(1)两线制:CAN_H、CAN_L,两根信号线以差分电压的形式来传输0、1信息,抗干扰能力强。
(2)CAN虽然没有单独的时钟信号,但为了实现位同步、网络节点中多个设备间的仲裁,须保持主从机使用相同的波特率。
(3)数据传输格式:以“帧”为单位,“帧”中包含ID(标识符)、DATA、帧格式、有效数据长度、标识符类型等信息。网络中不同的节点通过ID来识别和过滤信息。 每帧最多包含8个字节的有效DATA。 “帧”的另一种描述为“报文”。具体组成如下:
IDR0-IDR3(4个字节的ID标识)、DSR0-DSR7(8个字节的数据)、DLR(1个字节的有效数据长度,指明前面8个字节寄存器中有几个数据是有效的)
(4)标识符ID:分为标准标识符(11bit)和扩展标识符(29bit)。
(5)波特率设置:须遵循CAN标准的时段设置(SEG2和SEG1有特定的组合约束),否则即使最终的波特率一致,也不能正常通信。
(6)ID滤波和ID屏蔽设置:为了接收网络中特定的“帧”,CAN控制器使用一套ID过滤机制来判断是否接收数据。包括IDAR(8个标识符接收寄存器)、IDMR(8个标识符掩码寄存器)。IDAR和IDMR分别分为两页,IDAR0-IDAR3、IDAR4-IDAR7、IDMR0-IDMR3、IDMR4-IDMR7。若使用标准ID标识符,则只用到IDAR0和IDAR1,以及对应的IDMR0和IDMR1;扩展标识符则用到整页的IDAR和IDMR。
举例说明,如果上位机(电脑端)通过CAN向单片机发送一帧数据,其中ID为0x08,若单片机要接收此ID对应的数据,则需要设置IDAR0为0x01,IDAR1为0x00。因为0x08的ID用11bit表示为 0000 0001 000,映射到IDAR寄存器IDAR0和IDAR1,就拆分为0x01,0x00(RTR和IDE设置为0,标准标识符,数据帧;IDAR1中的最低3位不参与过滤识别)。 不必参考网上其它作者繁琐的转换和说明,直接将需要接收的ID对应到IDAR各位,填空,再分割,就得到IDAR应该设置的值了。
IDMR的设置,按照文档说明,对应位为0,则表示接收到的IDR对应位需要通过滤波器过滤识别,为1,则表示不需要过滤(任意数据都不影响接收)。 以上述例子,对应的IDMR0设置为0x00,IDMR1设置为0x07(最低3位无关,可屏蔽)。
2、MC9S12XEP100的CAN模块初始化步骤
(1)CANCTL0的INITRQ位置1,请求进入初始化状态。
(2)查询CANCTL1的INITAK是否为1,为1表示已进入初始化状态,为0表示为正常运行状态。
(3)设置CANCTL1,使能CAN、时钟选择。
(4)设置CANBTR0,CAN的时钟分频系数、同步跳转位宽等参数。
(5)设置CANBTR1,CAN的时段1、时段2等参数。(注意时段1、时段2和同步跳转位宽须满足特定的组合,参考对照表,否则即使波特率正确,也不能正常通信)
(6)设置CANIDAC,CAN的滤波器选择、标识符种类等参数。
(7)设置IDAR、IDMR,须满足主从机的ID对应关系。
(8)CANCTL0的INITRQ位置0,请求退出初始化状态。
(9)查询CANCTL1的INITAK,为0标识已退出初始化,进入正常运行状态。
(10)查询CANCTL0的SYNCH位,为1表示CAN已同步。
(11)使能CAN 的接收或发送中断,CANRIER的RXFIE为接收中断控制位。(这一步不能放在初始化状态中,否则不能开启中断!)
3、初始化程序
; 板卡号设置
BOARD_ID EQU 1
CAN_init:
PSHA
PSHB
PSHY
PSHX
BSET CAN0CTL0,mCAN0CTL0_INITRQ
BRCLR CAN0CTL1,mCAN0CTL1_INITAK,* ; 检测是否进入初始化模式
MOVB #$80,CAN0CTL1 ; CANE=1, CLKSRC=0(外部晶振)
MOVB #$07,CAN0BTR0 ; SJW=0, BRP=7(预分频8)
MOVB #$3A,CAN0BTR1 ; TSEG2=3, TSEG1=10, 波特率=125KHz
MOVB #$10,CAN0IDAC ; IDAM=1, IDHIT=0, 4个16bit滤波器,标准标识符
JSR IDAR_init
JSR IDMR_init
BCLR CAN0CTL0,mCAN0CTL0_INITRQ
BRSET CAN0CTL1,mCAN0CTL1_INITAK,* ; 退出初始化模式
BRCLR CAN0CTL0,mCAN0CTL0_SYNCH,* ; 等待同步
MOVB #$01,CAN0RIER ; RXFIE=1, 开接收中断, 必须在退出初始化模式后使能!
PULX
PULY
PULB
PULA
RTC
IDAR_init:
CLRA
LDAB #BOARD_ID
DECB
LSLD
LSLD ; 查询表里,每个IDAR占4个字节,所以这里乘以4
LDX #Chanel1IDTable
LEAY D,X
LDX #CAN0IDAR0
MOVW 0,Y,0,X
RTS
IDMR_init:
MOVB #$00,CAN0IDMR0
MOVB #$07,CAN0IDMR1
;MOVB #$00,CAN0IDMR2
;MOVB #$07,CAN0IDMR3
;MOVB #$00,CAN0IDMR4
;MOVB #$07,CAN0IDMR5
;MOVB #$00,CAN0IDMR6
;MOVB #$07,CAN0IDMR7
RTS
4、接收中断程序
XDEF CAN0RX_ISR
XREF can0rx_buff
CAN0RX_ISR:
; Write your interrupt code here ...
MOVB #$00,CAN0RIER ; 关中断
LDY #CAN0RXDSR0
LDX #can0rx_buff
LDAB CAN0RXDLR ; 接收数据长度
STAB 1,X+
CMPB #0
BHI rec_data ; 数据长度 >0
RTI
rec_data:
MOVB 1,Y+,1,X+
DBNE B,rec_data
MOVB #$01,CAN0RIER ; 开中断
MOVB #$01,CAN0RFLG ; 清RXF标志
RTI