历尽千幸万苦,终于把beaglebone上的LED点亮了.现在记录一下,以免以后忘记.
首先想到的是写寄存器的方式进行点灯.虽然TI的文档相当地烂,不过我还是找到了LED所在的GPIO相关寄存器. 123456789 | #define TIA8_GPIO1_PA (0x4804C000)#define TIA8_GPIO_CLEARDATAOUT (0x190)#define TIA8_GPIO_SETDATAOUT (0x194)#define TIA8_GPIO_CTRL (0x130)#define TIA8_GPIO_OE (0x134)#define LED1 ( 1 << 21)#define LED2 ( 1 << 22)#define LED3 ( 1 << 23)#define LED4 ( 1 << 24) |
然后在驱动中直接使用__raw_writel()进行写内存.结果不行.后来根据出错信息发现__raw_writel只是写的虚拟地址.得建立一个物理地址到虚拟地址的映射才行.接下来又学会了使用ioremap. 1234567891011 | volatile unsigned long *p = ioremap(TIA8_GPIO1_PA,0x2048); __raw_writel((2<<1),(p+TIA8_GPIO_CTRL));tmp = __raw_readl(p+TIA8_GPIO_OE);tmp&=(~(LED1|LED2|LED3|LED4));__raw_writel(tmp,(p+TIA8_GPIO_OE)); __raw_writel(LED1,(base+TIA8_GPIO_SETDATAOUT));__raw_writel(LED2,(TIA8_GPIO_SETDATAOUT+base));__raw_writel(LED3,(TIA8_GPIO_CLEARDATAOUT+base));__raw_writel(LED4,(TIA8_GPIO_CLEARDATAOUT+base)); |
经过这样修改后,发现依然不成功.也使用过这样的方式.依旧不行. 123456 | *(p+TIA8_GPIO_SETDATAOUT)=LED1;*(p+TIA8_GPIO_SETDATAOUT)=LED2;*(p+TIA8_GPIO_CLEARDATAOUT)=LED3;*(p+TIA8_GPIO_CLEARDATAOUT)=LED4;/* 经过被多人打脸,unsigned long *p指针+1 实际地址会是+unsigned long所占的字节数.所以上面的代码肯定不行*//* 典型的不会用指针屌丝 */ |
问题出在什么地方呢??在写虚拟内存时,被cache了?好吧.我把ioremap换成了ioremap_nocache,但依旧不行.
后来我使用kernel的GPIO接口测试,发现能正常点亮LED,也通过方法排除了GPIO没被设置的可能性,最后去根踪TI BSP里的代码,发现他们都是通过void __iomem *base来进行操作.于是乎google:void __iomem *base,发现描述它的页面并不多.很多都是鸟文的,看起来特累.后来在睡前决定我也试试.于是下面的代码就产生了: 12345 | void __iomem *base = ioremap_nocache(TIA8_GPIO1_PA,0x2048);__raw_writel(LED1,(base+TIA8_GPIO_SETDATAOUT));__raw_writel(LED2,(TIA8_GPIO_SETDATAOUT+base));__raw_writel(LED3,(TIA8_GPIO_CLEARDATAOUT+base));__raw_writel(LED4,(TIA8_GPIO_CLEARDATAOUT+base)); |
编译模块,加载运行....成功鸟!!!.
原来我们从ioremap返回的值最好是赋给void __iomem类型.在LDD3中,只是不建义直接使用指针进行访问ioremap过的地址,但没说具体如何使用.
但具体为什么使用__iomem就可以,而直接的指针就访问异常就不知道了.但至少以后写驱动就注意一下,对于设备寄存器的操作,最好是不直接映射后就使用指针操作.
|