硬件资源
片选,使用了nCS2,根据LPC3250的存储器MAP:
Four static memory banks, 16 MB each:
EMC_CS0 0xE000 0000 ~ 0xE0FF FFFF
EMC_CS1 0xE100 0000 ~ 0xE1FF FFFF
EMC_CS2 0xE200 0000 ~ 0xE2FF FFFF
EMC_CS3 0xE300 0000 ~ 0xE3FF FFFF
电路上ALE和我CLE的偏移地址分别是0x02和0x01,但是由于LPC3250比较特殊,物理总线被配置为16位还是32位,地址线A0总是有效,因而写往总线的地址需要进行处理。CAN所在总线被配置为16位宽度,所以实际地址应该将物理偏移地址左移1位,于是得到:
ALE —— 0xE200 0004
CLE —— 0xE200 0002
中断 —— GPIO_01
![](http://sites.google.com/site/myembededlife/_/rsrc/1234335631137/Home/lpc3250/sja1000-can/can-sja1000.jpg)
![](http://sites.google.com/site/myembededlife/_/rsrc/1234335631137/Home/lpc3250/sja1000-can/can-sja1000.jpg)
![](http://sites.google.com/site/myembededlife/_/rsrc/1234335631137/Home/lpc3250/sja1000-can/can-sja1000.jpg)
中断处理
GPIO_01可以作为中断输入引脚,并且可选择唤醒CPU。
唤醒CPU的寄存器:START_ER_INT[1],0——禁能,1——使能。
中断使能寄存器: SIC2_EN[1],0——禁止,1——使能;
中断极性寄存器: SIC2_APR[1],0——低电平或者下降沿,1——高电平或者上升沿;
中断类型寄存器: SIC2_ATR[1],0——电平触发,1——边沿触发;
IO方向寄存器: P2_DIR_SET[
26
],0——输入,1——输出;
输入状态寄存器: P3_INP_STATE[
11
];
输出寄存器: P3_OUTP_SET[
26
];
SJA1000推荐用电平触发中断。
总线配置:
如下是在ADS中的测试代码:
SJA1000使用的是Bank2,总线配置如下:
#define WAITWEN2 0x02 /* 配置EMCStaticWaitWen2 */
#define WAITOEN2 0x02 /* 配置EMCStaticWaitOen2 */
#define WAITRD2 0x1F /* 配置EMCStaticWaitRd2 */
#define WAITPAGE2 0x0F /* 配置EMCStaticWaitPage2 */
#define WAITWR2 0x1F /* 配置EMCStaticWaitWr2 */
#define WAITTURN2 0x0F /* 配置EMCStaticWaitTurn2 */
#define BCFG_16DEF 0x00000001 /* 16Bit Bus */
/*
* | 页模式 | 片选极性 |字节定位状态| 延长等待 | 写缓冲区 | 写保护 |
* | PM | PC | PB | EW | B | P |
* |0:禁能1:使能| 0:低 1:高 | |0:禁能1:使能|0:禁能1:使能|0:禁能1:使能|
*/
#define BCFG0 ( (0x00 < <03) | (0x00 < <06) | (0x01 < <07) | (0x00 < <8) | (0x00 < <19) | (0x00 < <20) )
#define BCFG1 ( (0x00 < <03) | (0x00 < <06) | (0x01 < <07) | (0x00 < <8) | (0x00 < <19) | (0x00 < <20) )
#define BCFG2 ( (0x00 < <03) | (0x00 < <06) | (0x01 < <07) | (0x00 < <8) | (0x00 < <19) | (0x00 < <20) )
#define BCFG3 ( (0x00 < <03) | (0x00 < <06) | (0x01 < <07) | (0x00 < <8) | (0x00 < <19) | (0x00 < <20) )
#define STATICCFG2 ( BCFG_16DEF | BCFG2 )
EMCStaticConfig2 = STATICCFG2;
EMCStaticWaitWen2 = WAITWEN2;
EMCStaticWaitOen2 = WAITOEN2;
EMCStaticWaitRd2 = WAITRD2;
EMCStaticWaitPage2= WAITPAGE2;
EMCStaticWaitWr2 = WAITWR2;
EMCStaticWaitTurn2 = WAITTURN2;
Linux中总线配置的实现:
229 /* set BANK2's EMC REGs */
230 __raw_writel(STATICCFG2, EMCStaticConfig2(LPC32XX_EMC_BASE));
231 __raw_writel(WAITWEN2, EMCStaticWaitWen2(LPC32XX_EMC_BASE));
232 __raw_writel(WAITOEN2, EMCStaticWaitOen2(LPC32XX_EMC_BASE));
233 __raw_writel(WAITRD2, EMCStaticWaitRd2(LPC32XX_EMC_BASE));
234 __raw_writel(WAITPAGE2, EMCStaticWaitPage2(LPC32XX_EMC_BASE));
235 __raw_writel(WAITWR2, EMCStaticWaitWr2(LPC32XX_EMC_BASE));
236 __raw_writel(WAITTURN2, EMCStaticWaitTurn2(LPC32XX_EMC_BASE));
寄存器访问
静态映射
首先需要在系统内核中对CAN所在BANK进行IO映射,因为LPC32XX默认仅仅对片内外设的IO空间进行了IO映射:
598 /*
599 * By Chenxibing(Abing)
600 */
601 static struct map_desc smartarm3250_io_desc[] __initdata = {
602 { /*
nCS2, CAN SJA1000 */
603 .virtual = io_p2v(EMC_CS2_BASE),
604 .pfn = __phys_to_pfn(EMC_CS2_BASE),
605 .length = SZ_1M,
606 .type = MT_DEVICE
607 },
608 { /*
nCS1, CF Card */
609 .virtual = io_p2v(EMC_CS1_BASE),
610 .pfn = __phys_to_pfn(EMC_CS1_BASE),
611 .length = SZ_1M,
612 .type = MT_DEVICE
613 }
614
615 };
然后使用io_p2v将CAN的寄存器从物理地址转换为虚拟地址:
106 #define SJA_BASE EMC_CS2_BASE //0xE2000000
107 #define SJA1000_BASE io_p2v(SJA_BASE)
108 #define SJA1000_DATA (SJA1000_BASE + 0x02)
109 #define SJA1000_ADDR (SJA1000_BASE + 0x04)
最后使用__raw_read/__raw_write函数组进行访问:
259 __raw_writeb(0x09, SJA1000_ADDR);
260 __raw_writeb(1<<i, SJA1000_DATA);
261 __raw_writeb(0x09, SJA1000_ADDR);
没有经过静态IO映射的空间是不能使用io_p2v进行物理虚拟地址转换的。
至于寄存器访问函数iowrite、__raw_writel等,在新版内核已经改到arch/arm/include/asm/io.h函数中定义了,如:
49 #define __raw_writeb(v,a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a) = (v))
50 #define __raw_writew(v,a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a) = (v))
51 #define __raw_writel(v,a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a) = (v))
52
53 #define __raw_readb(a) (__chk_io_ptr(a), *(volatile unsigned char __force *)(a))
54 #define __raw_readw(a) (__chk_io_ptr(a), *(volatile unsigned short __force *)(a))
55 #define __raw_readl(a) (__chk_io_ptr(a), *(volatile unsigned int __force *)(a))
228 /*
229 * io{read,write}{8,16,32} macros
230 */
231 #ifndef ioread8
232 #define ioread8(p) ({ unsigned int __v = __raw_readb(p); __v; })
233 #define ioread16(p) ({ unsigned int __v = le16_to_cpu((__force __le16)__raw_readw(p)); __v; })
234 #define ioread32(p) ({ unsigned int __v = le32_to_cpu((__force __le32)__raw_readl(p)); __v; })
235
236 #define iowrite8(v,p) __raw_writeb(v, p)
237 #define iowrite16(v,p) __raw_writew((__force __u16)cpu_to_le16(v), p)
238 #define iowrite32(v,p) __raw_writel((__force __u32)cpu_to_le32(v), p)
动态IO映射
如果在系统中没有对CAN所在BANK进行静态IO映射,就不能使用io_p2v进行虚拟地址转换,必须使用ioremap进行动态IO映射。当然,经过静态IO映射的物理空间也可以使用ioremap进行动态IO映射:
98 #define SJA_ALE_PADR 0xE2000004 //SJA1000锁存器端口物理地址
99 #define SJA_DAT_PADR 0xE2000002 //SJA1000数据端口物理地址
100 #define SJA_SRC_LEN 0x02 //SJA1000数据长度,1字节
45 void *sja1000_ale;
46 void *sja1000_dat;
238 //映射IO
239 sja1000_dat = ioremap(SJA_DAT_PADR, SJA_SRC_LEN);
240 sja1000_ale = ioremap(SJA_ALE_PADR, SJA_SRC_LEN);
然后使用ioread系列函数进行访问:
245 iowrite8(0x09,sja1000_ale);
246 iowrite8(1<<i,sja1000_dat);
247 iowrite8(0x09,sja1000_ale);
问题和解决
问题
目前驱动能够进行正确的发送,但是接收程序仅仅能够响应一次,然后就再也不响应了。很有可能是中断没有处理好,是不是中断标志没有清除,无法再次进入中断?
那LPC3250的IO中断该如何处理?
解决
解决办法:在中断服务程序中清除GPIO_01的中断标志后,重新再次使能GPIO_01中断。
340 static irqreturn_t can_interrupt(int irq , void* dev_id, struct pt_regs *regs)
341 {
342 unsigned int *sic2_rsr;
343 unsigned int *sic2_er;
344
345 sic2_er = io_p2v(SIC2_BASE + INTC_MASK);
346 sic2_rsr = io_p2v(SIC2_BASE + INTC_RAW_STAT);
347
348 IntEntry();
349 wake_up_interruptible(&can_wait);
350
351 __raw_writel((1<<1), sic2_rsr); //clear interrupt flag //清除GPIO_01的中断标志
352 __raw_writel((1<<1), sic2_er); //re-enable GPIO_01 interrupt //重新使能GPIO_01的中断
353 return IRQ_HANDLED;
354 }
另外,初始化函数中的使能GPIO_01中断的代码必不可少:
217 int can_init(void)
218 {
219 int i,result;
220 int bak,tmp;
221
222 unsigned int *sic2_er;
223 sic2_er = io_p2v(SIC2_BASE + INTC_MASK);
224
__raw_writel((1<<1), sic2_er); //使能GPIO_01的中断
......
}
简单测试
在测试CAN接收的时候,上位机帧间隔时间不能为0,否则会丢帧,可以设定一个比较小的时间间隔,如为10m。
2009-05-21
在新的班子上遇到了发送测试程序基本没有问题,但是发送程序关闭之后CANTest软件还是会收到数据帧,不知道什么原因。
另外问题较大的就是接收程序,偶尔能够接受正确,很多情况下接收到的数据都是错的,帧ID一直都是错的。
2009-05-22
除ID问题之外的其它问题是硬件问题,换了另外一块板子没有问题了。
申请中断必须使用IRQF_DISABLED|IRQF_TRIGGER_FALLING FLAGS:
rc = request_irq(b->irq, (handler_t)interrupt_handler, IRQF_DISABLED|IRQF_TRIGGER_FALLING, b->name, b );