Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1249660
  • 博文数量: 479
  • 博客积分: 12240
  • 博客等级: 上将
  • 技术积分: 4999
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-24 17:12
文章分类

全部博文(479)

文章存档

2014年(1)

2013年(1)

2012年(1)

2011年(95)

2010年(177)

2009年(167)

2008年(16)

2007年(21)

分类: LINUX

2009-10-14 20:31:58

SJA1000 CAN驱动


硬件资源

    片选,使用了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     );


阅读(1885) | 评论(0) | 转发(1) |
0

上一篇:Beep Driver

下一篇:TTY and Login

给主人留下些什么吧!~~