SJA1000 CAN驅動
來源:互聯網
上載者:User
硬體資源 片選,使用了nCS2,根據LPC3250的儲存空間MAP:Four static memory banks, 16 MB each:EMC_CS0 0xE000 0000 ~ 0xE0FF FFFFEMC_CS1 0xE100 0000 ~ 0xE1FF FFFFEMC_CS2 0xE200 0000 ~ 0xE2FF FFFFEMC_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_DEVICE607 },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_DEVICE613 }614615 }; 然後使用io_p2v將CAN的寄存器從物理地址轉換為虛擬位址:106 #define SJA_BASE EMC_CS2_BASE //0xE2000000107 #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} macros230 */231 #ifndef ioread8232 #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; })235236 #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 //映射IO239 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;344345 sic2_er = io_p2v(SIC2_BASE + INTC_MASK);346 sic2_rsr = io_p2v(SIC2_BASE + INTC_RAW_STAT);347348 IntEntry();349 wake_up_interruptible(&can_wait);350351 __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;221222 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 );