硬件资源 片选,使用了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 中断处理 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< 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< 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 ); |