最近在做CAN驱动的移植工作,具体就是将X86平台的驱动移植到ARM上面。芯片采用的是philips的sja1000芯片,ARM pxa27x。将CAN的驱动直接交叉编译比较容易通过,不过,编译完成后,运行的时候驱动始终无法发出第一帧。
以下是发送原理
芯片及程序的工作原理是这样的,首先通过发送寄存器发出第一帧数据,然后会触发一个中断,中断处理程序会将驱动 buffer的数据copy到硬件FIFO中,COPY到FIFO中后,硬件会自动发送(应该是通过移位寄存器)。然后每发发送一侦,就会触发中断,这样只要驱动 buffer 中有数据就会一直发送,直道发送完毕!
我的第一帧没有发送出去,所以中断肯定无法上来。最终就确定查找为何第一帧无法上来,读取状态寄存器,发现状态寄存器一直是0,而X86下的值就会一直变化。所以就怀疑是硬件的基地址错了,再次确认后,硬件基地址并没有错。那到底是什么地方错了呢?
经过了好多次尝试,想到了linux device driver 一书关于内核数据结构的那一章,里面有讲跨平台应该注意的数据结构。然后就仔细看了一下CAN寄存器对应的数据结构。
typedef struct canregs {
uint8 canmode; /* 0 */
uint8 cancmd;
uint8 canstat;
uint8 canirq;
uint8 canirq_enable;
uint8 reserved1; /* 5 */
uint8 cantim0;
uint8 cantim1;
uint8 canoutc;
uint8 cantest;
uint8 reserved2; /* 10 */
uint8 arbitrationlost; /* read only */
uint8 errorcode; /* read only */
uint8 errorwarninglimit;
uint8 rxerror;
uint8 txerror; /* 15 */
uint8 frameinfo;
union frame frame;
uint8 reserved3;
uint8 canrxbufferadr /* 30 */;
uint8 canclk;
} __attribute__((packed)) canregs_t;
其中 联合体 frame 的定义如下:
union frame {
struct {
u32 canid1;
u32 canid2;
u32 canid3;
u32 canid4;
u32 canxdata[8];
} extframe;
struct {
u32 canid1;
u32 canid2;
u32 candata[8];
} stdframe;
};
uint8 定义:typedef unsigned char uint8;
sja1000里面的寄存器都是8bit的。
将canregs_t里面的 canmode cancmd canstat canirq canirq_enable 的地址值打印出来,发现在ARM和X86下面它们的地址值都是相差1,在X86平台上面地址值相差1比较容易理解,而ARM上面地址值相差1就有点奇怪了。尝试将uint8改为 u32,重新编译运行,问题解决了。分析原因,应该是ARM地址是字对齐(aligned)的,而X86允许地址是不对齐(unaliged)所以两个平台有如此的差别。