一、CAN模块介绍
这个实验我们来研究XEP100单片机内部的CAN模块。
XEP100单片机的CAN的基本特性如下:
• 实施CAN协议—2.0A/B版
— 标准和扩展数据帧
— 0-8字节数据长度
— 高达1Mbps的可编程比特率
— 支持远程帧
• 5个具有FIFO存储机制的接收缓冲器
• 3个具有使用“本地优先”概念的内部优先顺序的发送缓冲器
• 灵活可掩码标识符滤波器支持2个全尺寸(32位)扩展标识符滤波器或4个16位滤波器或8个8位滤波器
• 集成低通滤波器的可编程唤醒功能
• 可编程环回模式支持自测操作
• 可编程监听模式用于CAN总线监控
• 可编程总线脱离恢复功能
• 独立的信号和中断功能适用于所有CAN接收器和发射器错误状态(警报、错误严重状态、总线脱离)
• 可编程CAN时钟源,采用总线时钟或振荡器时钟
• 内部计时器提供给接收和发送的报文的时间标签
• 三种低功耗模式:睡眠、关机和CAN使能
• 配置寄存器的全局初始化
CAN模块的结构图如下:
图中RXCAN是CAN接收器输入管脚。TXCAN是CAN发送器输出管脚。
下图显示了一个具有CAN的典型CAN系统。每个CAN节点通过收发器物理连接到CAN总线线路,收发器能够驱动CAN总线所需的大电流,并具有对故障CAN或故障节点的电流保护。
CAN的接收器和发送器的结构如下图所示。
CAN收到的报文保存在5级输入FIFO中。5个报文缓冲器被交替映射到单个存储器区域。CAN有三重发送缓冲器机制,允许提前建立多条报文,从而优化了实时性能。
CAN的报文缓冲器结构见下表
Offset Address | Register | Access |
0x00X0 | Identifier Register 0 | |
0x00X1 | Identifier Register 1 | |
0x00X2 | Identifier Register 2 | |
0x00X3 | Identifier Register 3 | |
0x00X4 | Data Segment Register 0 | |
0x00X5 | Data Segment Register 1 | |
0x00X6 | Data Segment Register 2 | |
0x00X7 | Data Segment Register 3 | |
0x00X8 | Data Segment Register 4 | |
0x00X9 | Data Segment Register 5 | |
0x00XA | Data Segment Register 6 | |
0x00XB | Data Segment Register 7 | |
0x00XC | Data Length Register | |
0x00XD | Transmit Buffer Priority Register | |
0x00XE | Time Stamp Register (High Byte) | |
0x00XF | Time Stamp Register (Low Byte) |
有关CAN的更加详细的信息请参见相关的CAN协议和XEP100单片机的技术资料。
本实验的CAN接口芯片采用的是TJA1050芯片,电路的原理图如下图所示。图中L81和L82是CAN总线专用的滤波电感。J81和J82是跳线插针,用于连接120欧姆的电阻。如果CAN总线上挂接多个节点,只能保留两个120欧姆的电阻。这时需要把多余的电阻切断。就要拔下相应的跳线帽。
本实验采用CAN0发送数据,CAN1接收数据,CAN数据的格式采用标准帧格式。在开发板上通过自发自收实验来模拟真实的CAN通信过程。
二、例程测试
本实验的例程可以从本文的资源中下载。打开例程的代码,可以发现程序有些负责,程序中将CAN模块的驱动以及LCD的驱动封装到独立的文件当中,在主文件中进行调用。由于代码比较多,我们只对CAN的驱动函数进行功能性的介绍,具体细节可以对照着芯片手册进行研究。
1、CAN初始化
CAN模块的初始化函数如下所示。
void INIT_CANx(void);
这个函数的作用是对CANx模块进行初始化,将波特率设置为250kbit/s,禁止CAN滤波功能。
2、CAN发送函数
CAN模块的发送函数如下所示
Bool MSCANxSendMsg(struct can_msg msg);
这个函数的作用是通过CANx模块发送数据,struct can_msg msg为需要发送是数据的结构体,函数返回值为是否成功,发送成功返回true。struct can_msg msg结构体的定义如下。
struct can_msg //发送报文的结构体
{
unsigned int id;
Bool RTR;
unsigned char data[8];
unsigned char len;
unsigned char prty;
};
结构体中包含了数据帧的ID,RTR标志位,8个字节的数据,数据长度和优先级。
3、CAN接收函数
CAN模块的接收函数如下所示
Bool MSCANxGetMsg(struct can_msg *msg);
函数中struct can_msg *msg为指向接收到的数据结构体的指针,函数返回接收是否成功,成功返回true。
这个程序的主函数如下所示
void main(void) {
DisableInterrupts;
INIT_PLL();
initialize_ect();
INIT_PORT();
INIT_CAN0();
INIT_CAN1();
LEDCPU_dir=1;
LEDCPU=0;
EnableInterrupts;
//填写报文内容
msg_send.id = ID;
for(k=0;k<data_len_TX;k++)
{
msg_send.data[k] = senddata[k];
}
msg_send.len = data_len_TX;
msg_send.RTR = FALSE;
msg_send.prty = 0;
for(;;)
{
delay1ms(1000);
if(!MSCAN0SendMsg(msg_send)) //发送过程出现错误
{
for(;;);
}
if(datain==1)
{
lcd_clear();
lcd_string(0,0,xianshi[0]);
lcd_string(1,0,xianshi[1]);
play_data();
datain=0;
}
}
}
主函数的作用是通过CAN0发送数据,首先将需要发送的数据的ID和data赋值到发送结构体中,然后在主循环中每个1秒发送一次数据。并且在接收到数据时,在lcd上显示接收到的数据。
CAN1负责接收数据,接收采用中断的方式,中断函数如下所示。
void interrupt CAN_receive(void)
{
if(MSCAN1GetMsg(&msg_get))
{
// 接收新信息
if(msg_get.id == ID && (!msg_get.RTR))
{
LEDCPU = ~LEDCPU;
datain=1;
}
}
else
{
for(;;);
}
}
在中断函数中,主要是通过CAN1的接收函数来接收数据,接收成功后设置标志位datain。
将程序下载到单片机中,并将单片机的CAN0和CAN1接口连接在一起,运行程序,可以看到液晶上显示接收到的数据。