应用QQ2440(s3c2440)ARM开发板驱动MMA7455加速度计的linux设备驱动编写

课题水下机器人需要测定水下机器人的位姿,为此应用了加速度计MMA7455,该传感器可以用SPI或I2C读取数字信号到MCU。

驱动MMA7455在atmega128上已经实现,但是由于mega128的速度、资源等瓶颈使得继续开发受到一定限制,故改用arm处理器。

 

在arm处理器上运行linux操作系统,要完成对MMA7455加速度计的驱动需要了解linux下的设备驱动程序,因此编写了相应的设备驱动程序。

在学习过程中Makefile的编写非常重要,对于我这样的菜鸟来说感觉makefile是设备驱动的一大障碍,试了好多各种各样的makefile,最终发现一个好用的makefile,而且似乎可以通用,于是记录下来了:http://blog.csdn.net/joygo007/article/details/6639368。

我用的是SPI来驱动MMA7455,因为相对I2C来说,我觉得SPI还是稍微简单些。

先将代码附上,再在后面慢慢分享从中遇到的各种困难以及解决的过程。

 

Code1:linux 下针对MMA7455的SPI设备驱动程序

 

[cpp]  view plain copy
  1. #include <linux/config.h>  
  2. #include <linux/kernel.h>  
  3. #include <linux/fs.h>//fops  
  4. #include <linux/module.h>  
  5. #include <linux/init.h>  
  6. #include <linux/devfs_fs_kernel.h>  
  7. #include <linux/miscdevice.h>  
  8. #include <asm/arch/regs-gpio.h>//gpio寄存器地址头文件  
  9. #include <asm/hardware.h>//s3c2410_gpio_setpin等函数  
  10. #include <linux/delay.h>  
  11. #include <asm/irq.h>  
  12. #include <asm/io.h>  
  13. #include <asm/uaccess.h>  
  14.   
  15. #define DEVICE_NAME "spi_gh"    //设备名称  
  16. #define SPI_MAJOR_NUM 236   //主设备号  
  17.   
  18. //int whtaever=0;  
  19. volatile int *spi_gpecon=NULL;//GPG Part define    
  20. volatile int *spi_gpedat=NULL;  
  21. volatile int *spi_gpeup=NULL;   
  22. volatile int *s3c2440_clkcon=NULL;    
  23. volatile int *spi_spcon0=NULL;//SPI Part define    
  24. volatile int *spi_spsta0=NULL;    
  25. volatile int *spi_sppin0=NULL;  
  26. volatile int *spi_sppre0=NULL;  
  27. volatile int *spi_sptdat0=NULL;    
  28. volatile int *spi_sprdat0=NULL;  
  29.   
  30. #define SPI_TXRX_READY      (((*spi_spsta0)&0x1) == 0x1)    
  31. int loopChar=0x88;    
  32.   
  33. //定义SPI对应的引脚号  
  34. static unsigned long spi_pin_tab[]={  
  35.     S3C2410_GPE11,  
  36.     S3C2410_GPE12,  
  37.     S3C2410_GPE13,  
  38.     S3C2410_GPG2,  
  39.   
  40. };  
  41.   
  42. //配置SPI引脚功能  
  43. static unsigned long spi_pin_cfg_tab[]={  
  44.     S3C2410_GPE11_SPIMISO0,  
  45.     S3C2410_GPE12_SPIMOSI0,  
  46.     S3C2410_GPE13_SPICLK0,  
  47.     S3C2410_GPG2_OUTP,  
  48. };  
  49. /*====================================== 
  50. 驱动程序函数:__open 
  51. 功能说明    :挂载模块退出程序 
  52. =======================================*/  
  53. static int spi_open(struct inode *inode,struct file *filp)    
  54. {   
  55.     *s3c2440_clkcon |=0x40000;    
  56.     printk("s3c2440_clkcon=%08X\n",*s3c2440_clkcon);    
  57.   
  58.     *spi_sppre0=0x18;         
  59.     printk("spi_sppre0=%02X\n",*spi_sppre0);    
  60.     *spi_spcon0=(0<<6)|(0<<5)|(1<<4)|(1<<3)|(0<<2)|(0<<1)|(0<<0);    
  61.     printk("spi_spcon0=%02X\n",*spi_spcon0);    
  62.     *spi_sppin0=(0<<2)|(0<<0);    
  63.         printk("spi_sppin0=%02X\n",*spi_sppin0);    
  64.     printk("KKK:OPEN OK\n");    
  65.      return 0;    
  66.   
  67. }    
  68.   
  69. /*========================================================================== 
  70. 普通函数:static void writeByte(const char c)  
  71. 功能说明    :写一个字节到SPI 
  72. 入口参数:预写入的值 
  73. 返回值:无 
  74. 说明: 
  75. ==============================================================================*/  
  76. static void writeByte(const char c)    
  77. {    
  78.     int j = 0;    
  79.     *spi_sptdat0 = c;    
  80.     for(j=0;j<0xFF;j++);    
  81.     while(!SPI_TXRX_READY)    
  82.         for(j=0;j<0xFF;j++);    
  83. }    
  84.   
  85. /*========================================================================== 
  86. 普通函数:static char readByte(void)   
  87. 功能说明    :从spi读一个字节 
  88. 入口参数:无 
  89. 返回值:读到的值 
  90. 说明: 
  91. ==============================================================================*/     
  92. static char readByte(void)    
  93. {    
  94.     int j = 0;    
  95.     char ch = 0;    
  96.     *spi_sptdat0 = (char)loopChar;    
  97.     for(j=0;j<0xFF;j++);    
  98.     while(!SPI_TXRX_READY)    
  99.         for(j=0;j<0xFF;j++);    
  100.     ch=*spi_sprdat0;    
  101.     return ch;    
  102. }    
  103. /*====================================== 
  104. 驱动程序函数:ioctl 
  105. 功能说明    : 
  106. =======================================*/  
  107. static int spi_ioctl(   struct inode *inode, struct file *file,   
  108.                         unsigned int RdorWr, char * spiBuf      )  
  109. {  
  110.     char whtevr;  
  111.     char * ch;   
  112.     char * kbuf=&whtevr;   
  113.     switch(RdorWr)  
  114.         {  
  115.             case 0://读spi设备的值  
  116.                     //printk("<1>spi read!\n");   
  117.                     ch=readByte();    
  118.                     copy_to_user(spiBuf,&ch,1);    
  119.                     //printk("<1>spi read content:%x\n",*spiBuf);   
  120.                     return 1;    
  121.                     break;  
  122.             case 1://向spi写入数据  
  123.                     //printk("<1>spi write!\n");   
  124.                     copy_from_user(kbuf,spiBuf,1);  
  125.                     //printk("<1>copy_from_user OK!\n");  
  126.                     writeByte(*kbuf);   
  127.                     //printk("<1>spi write content:%x\n",*spiBuf);   
  128.                     return 1;  
  129.                     break;  
  130.             case 3://设置SS引脚为低  
  131.                     s3c2410_gpio_setpin(spi_pin_tab[3],0);//nSS0=0  
  132.                     return 0;  
  133.                     break;  
  134.             case 4://设置SS引脚为高  
  135.                     s3c2410_gpio_setpin(spi_pin_tab[3],1);//nSS0=1  
  136.                     return 0;  
  137.                     break;  
  138.             default:  
  139.                     return -EINVAL;  
  140.         }  
  141. }  
  142.   
  143. /*====================================== 
  144. 驱动程序函数:fops 
  145. 功能说明    :注册驱动程序的接口功能函数 
  146. =======================================*/  
  147. static struct file_operations spi_fops = {  
  148.     .open =spi_open,  
  149.     .ioctl = spi_ioctl,  
  150. };  
  151.   
  152. /*====================================== 
  153. 驱动程序函数:__init 
  154. 功能说明    :挂载模块初始化程序 
  155. =======================================*/  
  156. static int __init spi_init(void)  
  157. {  
  158.     int ret,i;  
  159.     ret=register_chrdev(SPI_MAJOR_NUM,DEVICE_NAME,&spi_fops);//注册  
  160.     if(ret<0)  
  161.         {  
  162.             printk(DEVICE_NAME" can't register major number\n");  
  163.             return ret;  
  164.         }  
  165.     devfs_mk_cdev(MKDEV(SPI_MAJOR_NUM, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEVICE_NAME); //设备注册完成后用此句创建设备节点  
  166.   
  167.     //初始化引脚  
  168.     for(i=0;i<4;i++)  
  169.         {  
  170.             s3c2410_gpio_cfgpin(spi_pin_tab[i],spi_pin_cfg_tab[i]);  
  171.         }  
  172.       
  173.         s3c2440_clkcon = (int *)ioremap(0x4C00000c,3);    
  174.     //GPIO_E寄存器(其中有SPI_0的引脚设置)  
  175.         spi_gpecon = (int *)ioremap (0x56000040,4); //GPeCON ,765位为SPI_0  
  176.         spi_gpedat = (int *)ioremap (0x56000044,2);  //GPeDAT  
  177.         spi_gpeup = (int *)ioremap (0x56000048,2);  //GPeUP  
  178.   
  179.         spi_spcon0 = (int *)ioremap(0x59000000,1);  //spcon0:spi控制寄存器0  
  180.         spi_spsta0 = (int *)ioremap(0x59000004,1);  //spsta0:spi状态寄存器0  
  181.         spi_sppin0 = (int *)ioremap(0x59000008,1);  //sppin0:spi引脚控制寄存器0  
  182.         spi_sppre0= (int *)ioremap(0x5900000c,1);   //sppre0:spi波特率预分频寄存器0  
  183.         spi_sptdat0 = (int *)ioremap(0x59000010,1);  //sptdat0:spi发送数据寄存器0  
  184.         spi_sprdat0 = (int *)ioremap(0x59000014,1);  //sprdat0:spi接收数据寄存器0  
  185.       
  186.     printk(DEVICE_NAME" initial OK!\n");  
  187.   
  188. }  
  189.   
  190. /*====================================== 
  191. 驱动程序函数:__exit 
  192. 功能说明    :挂载模块退出程序 
  193. =======================================*/  
  194. static void __exit spi_exit(void)  
  195. {  
  196.     devfs_remove(DEVICE_NAME);  
  197.     unregister_chrdev(SPI_MAJOR_NUM,DEVICE_NAME);  
  198. }  
  199.   
  200. /*====================================== 
  201. 驱动程序函数 
  202. 功能说明    :指定模块初始化函数和退出函数 
  203. =======================================*/  
  204. module_init(spi_init);  
  205. module_exit(spi_exit);  
  206.   
  207. //end  


Code2:针对以上SPI设备驱动代码的makefile文件:

[plain]  view plain copy
  1. obj-m := spi_MMA7455.o #spi_MMA7455为上面代码文件的名字    
  2. ifeq ($(v),arm)  
  3. KERNELDIR :=/opt/friendlyARMQQ2440/ghCodes/kernel-2.6.13 #这里是我的ARM开发板的内核路径  
  4. else  
  5. KERNELDIR := /lib/modules/$(shell uname -r)/build #这里是PC机端的内核路径,在ARM开发时用不着,这个是用来在PC机上做其他测试时用的  
  6. endif   
  7. default:   
  8.     make -C $(KERNELDIR) M=$(shell pwd) modules   
  9. install:   
  10.     insmod spi_MMA7455.ko  
  11. uninstall:   
  12.     rmmod spi_MMA7455.ko   
  13. clean:   
  14.     make -C $(KERNELDIR) M=$(shell pwd) clean  


在运行该makefile文件时,要用命令

[plain]  view plain copy
  1. make v=arm  

因为在makefile中设置了条件判断ifeq().

 

Code3:用于测试生成的驱动程序的测试程序

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <sys/ioctl.h>  
  5.   
  6. //定义SPI设备驱动的读写标志符  
  7. #define SPI_READFLAG 0//读SPI设备标识  
  8. #define SPI_WRITEFLAG 1//写SPI设备标识  
  9.   
  10. #define SPI_SS_L    3//置SS位为低  
  11. #define SPI_SS_H    4//置SS位为高  
  12.   
  13. //定义加速度计的三轴输出寄存器  
  14. #define accXreg 0x06  
  15. #define accYreg 0x07  
  16. #define accZreg 0x08  
  17.   
  18. //定义加速度计的感应精度  
  19. #define ACC_2G_RANGE 0x05  
  20. #define ACC_4G_RANGE 0x09  
  21. #define ACC_8G_RANGE 0x01  
  22.   
  23.   
  24. char Xdata=0,Ydata=0,Zdata=0;  
  25. /*================================== 
  26. 函数: void spiWrite2acc(int fd,unsigned char reg,char data) 
  27. 功能: SPI主机向MMA7455加速度计写入指令 
  28. 参数: MMA7455的寄存器和指令 
  29. 返回值:   无 
  30. 说明: 
  31. ==================================*/  
  32. void spiWrite2acc(int fd,unsigned char reg,char data)  
  33. {  
  34.     char wter=0;  
  35.     char *writebuf=&wter;  
  36.     *writebuf=((reg&0x3F)<<1)|0x80;  
  37.     ioctl(fd, SPI_SS_L);  
  38.     ioctl(fd, SPI_WRITEFLAG, writebuf);  
  39.     ioctl(fd, SPI_WRITEFLAG, &data);  
  40.     ioctl(fd, SPI_SS_H);  
  41. }  
  42.   
  43. /*================================== 
  44. 函数: void spiReadacc(int fd,unsigned char reg,char *rdbuf) 
  45. 功能: SPI主机读取MMA7455加速度计值 
  46. 参数: MMA7455的寄存器 
  47. 返回值:   指定寄存器的值 
  48. 说明: 
  49. ==================================*/  
  50. void spiReadacc(int fd,unsigned char reg,char *rdbuf)  
  51. {  
  52.     char wter=0;  
  53.     char *writebuf=&wter;  
  54.     ioctl(fd, SPI_SS_L);  
  55.     *writebuf=(reg &0x3F)<<1;//预读的地址  
  56.     ioctl(fd, SPI_WRITEFLAG, writebuf);//发送预读的地址  
  57.     ioctl(fd, SPI_READFLAG, rdbuf);  
  58.     ioctl(fd, SPI_SS_H);  
  59. }  
  60.   
  61. /*================================== 
  62. 函数: void readAcc(void) 
  63. 功能: SPI初始化 
  64. 参数: spi设备文件句柄 
  65. 返回值:    无 
  66. 说明: 
  67. ==================================*/  
  68. void readAcc(int fd)  
  69. {  
  70.     spiReadacc(fd,accXreg,&Xdata);    
  71.     spiReadacc(fd,accYreg,&Ydata);    
  72.     spiReadacc(fd,accZreg,&Zdata);    
  73. }  
  74.   
  75. /*================================== 
  76. 函数: void readAcc(void) 
  77. 功能: SPI初始化 
  78. 参数: spi设备文件句柄 
  79. 返回值:    无 
  80. 说明: 
  81. ==================================*/  
  82. void showAcc(void)  
  83. {  
  84.     printf("%x %x %x",Xdata,Ydata,Zdata);     
  85. }  
  86.   
  87. /*================================== 
  88. 函数: void acc_init(int fd) 
  89. 功能: SPI初始化 
  90. 参数: spi设备文件句柄 
  91. 返回值:    无 
  92. 说明: 
  93. ==================================*/  
  94. void acc_init(int fd)  
  95. {  
  96.     char ret;  
  97.     ioctl(fd, SPI_SS_H);  
  98.     spiWrite2acc(fd,0x16,ACC_2G_RANGE); //四线模式  
  99.     spiReadacc(fd,0x16,&ret);           //回读该寄存器确认  
  100.     if(ACC_2G_RANGE!=ret)  
  101.         printf("No acceleration!\n");  
  102.   
  103.     readAcc(fd);  
  104. }  
  105.   
  106. void delay_gh(int time)  
  107. {  
  108.     int i,j;  
  109.     for(i=0;i<0xffff;i++)  
  110.         for(j=time;j>0;j--);  
  111. }  
  112.   
  113. /*================================== 
  114. 函数: int main(int argc, char **argv) 
  115. 功能: 测试程序入口 
  116. 参数:  
  117. 返回值:    无 
  118. 说明:spi测试程序 
  119. ==================================*/  
  120. int main(int argc, char **argv)  
  121. {  
  122.   
  123.     int fd;  
  124.     int on=1;  
  125.     int led_no;  
  126.     char wtevr=0;  
  127.   
  128.     char whtevr1,whtevr2=0x56;  
  129.     char *redbuf=&whtevr1;  
  130.     char *writebuf=&whtevr2;  
  131.       
  132.     fd = open("/dev/spi_gh", 0);  
  133.     if (fd < 0) {  
  134.         perror("open device spi_gh");  
  135.         exit(1);  
  136.     }  
  137.     printf("OPEN OK!\n");  
  138.       
  139.     acc_init(fd);  
  140.     while(1)  
  141.     {  
  142.             readAcc(fd);  
  143.             printf("%x %x %x\n",Xdata,Ydata,Zdata);   
  144.             delay_gh(0x10);  
  145.     }  
  146.   
  147.     close(fd);  
  148.     return 0;  
  149. }  

这个程序能够完成基本的测试功能,读出MMA7455加速度计的值,要用交叉编译哦:

[plain]  view plain copy
  1. arm-linux-gcc -o test test.c  

 

完成以上程序的编译后下载到ARM开发板,记住不要用NFS哦,会出错的,错误好像是说版本问题:

insmod: kernel-module version mismatch
        chdev_test.ko was compiled for kernel version
        chdev_test.ko was compiled for kernel version

出错了,就要解决错误,方法是:把下载程序的方法改为用ftp下载或U盘下载等多种方法,看看使用手册吧

 

之后要挂载我们的.ko文件,挂载方法为:

[plain]  view plain copy
  1. insmod xxxx.ko  

其中xxxx就是你的设备驱动程序的文件名

挂载上之后不需要在mknod了,因为在我们的设备驱动程序初始化中已经有了MKDEV了:

[cpp]  view plain copy
  1. devfs_mk_cdev(MKDEV(SPI_MAJOR_NUM, 0), S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, DEVICE_NAME); //设备注册完成后用此句创建设备节点  

之后就运行我们的测试程序:

[plain]  view plain copy
  1. ./test  

然后呢又出错了:好像是什么Permission denied,好说,修改一下权限:

[plain]  view plain copy
  1. chmod 777 test  


然后再运行

[plain]  view plain copy
  1. ./test  

这下OK了!可以运行鸟。。。

 

其中遇到过N多的问题,让我灰心丧气了好久好久,然后一点一点的解决,今天终于读出数来了,心里那个兴奋啊。。。呵呵

你可能感兴趣的:(应用QQ2440(s3c2440)ARM开发板驱动MMA7455加速度计的linux设备驱动编写)