领主阿德尔 达拉然:S3C2440 spi驱动简单测试

来源:百度文库 编辑:九乡新闻网 时间:2024/04/29 21:15:22

这两天参考网上的资料,自己写了个SPI的驱动,并实际测试通过。

硬件平台:mini2440 用的是S3C2440 的SPI1(共有2个SPI模块)

操作系统:linux-2.6.32.2

测试方法:将SPI的MISO与MOSI管脚短路,这样读数据的时候第一个发出的dummy字节即为收到的字节。

下面是驱动的源代码(mini2440_spi.c):

view sourceprint? /***************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include    int loopChar=0x88; module_param(loopChar,int,S_IRUGO);                                                        static int spi_major = 55; #define spi_name "mini2440_spi" struct cdev spiCdev;    /*****************************************************/    static int spi_open(struct inode *,struct file *); static int spi_release(struct inode *,struct file *); static ssize_t spi_write(struct file *filp,const char *buf,size_t count,loff_t *f_ops); static ssize_t spi_read(struct file *filp,char *buf,size_t count,loff_t *f_ops); static ssize_t spi_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long data);    volatile int *spi_gpfcon=NULL;//GPF Part define volatile int *spi_gpfdat=NULL; volatile int *spi_gpfup=NULL;    volatile int *spi_gpgcon=NULL;//GPG Part define volatile int *spi_gpgdat=NULL; volatile int *spi_gpgup=NULL;    volatile int *s3c2440_clkcon; volatile int *spi_spcon1;//SPI Part define volatile int *spi_spsta1; volatile int *spi_sppin1; volatile int *spi_sppre1; volatile int *spi_sptdat1; volatile int *spi_sprdat1;    #define SPI_TXRX_READY      (((*spi_spsta1)&0x1) == 0x1) /**********************************************************/ static const struct file_operations spi_fops =  {     .owner=THIS_MODULE,     .open=spi_open,     .read=spi_read,     .ioctl=spi_ioctl,     .release=spi_release,     .write=spi_write, };    /********************************************************/ static int spi_open(struct inode *inode,struct file *filp) {     filp->private_data =&spiCdev;         /***********************************************     *PCLK     ************************************************/     /*control PCLK into spi block*/     *s3c2440_clkcon |=0x40000;     printk("s3c2440_clkcon=%08X\n",*s3c2440_clkcon);     /***********************************************     *GPG PORTS     ************************************************/     /*config SCK1,MOSI1,MISO1 = 11*/     *spi_gpgcon |=0x0000FC00;     /*poll up MISO1 MOSI1,SCK1*/     *spi_gpgup &=0xFF1F;     *spi_gpgup |=0x0060;     /***********************************************     *GPF PORTS     ************************************************/     *spi_gpfcon &= 0xFCF3;     *spi_gpfcon |= 0x0108;       *spi_gpfup &= 0xED;     *spi_gpfdat |= 0x10;     /***********************************************     *SPI REGS     ************************************************/      //SPI Baud Rate Prescaler Register,Baud Rate=PCLK/2/(Prescaler value+1)     *spi_sppre1=0x18;       //freq = 1M     printk("spi_sppre1=%02X\n",*spi_sppre1);     //polling,en-sck,master,low,format A,nomal = 0 | TAGD = 1     *spi_spcon1=(0<<6)|(0<<5)|(1<<4)|(1<<3)|(0<<2)|(0<<1)|(0<<0);     printk("spi_spcon1=%02X\n",*spi_spcon1);     //Multi Master error detect disable,reserved,rech=*spi_sprdat0;lease     *spi_sppin1=(0<<2)|(0<<0);     printk("spi_sppin1=%02X\n",*spi_sppin1);        return 0; }       static int spi_release(struct inode *inode,struct file *filp) {     //free irq     free_irq(IRQ_EINT1, NULL);        printk("<1>release\n");     return 0; }       static void writeByte(const char c) {     int j = 0;     *spi_sptdat1 = c;     for(j=0;j<0xFF;j++);     while(!SPI_TXRX_READY)         for(j=0;j<0xFF;j++); }    static char readByte(void) {     int j = 0;     char ch = 0;     *spi_sptdat1 = (char)loopChar;     for(j=0;j<0xFF;j++);     while(!SPI_TXRX_READY)         for(j=0;j<0xFF;j++);     ch=*spi_sprdat1;     return ch; } static ssize_t spi_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops) {     //int len=0;     char ch;     printk("<1>spi read!\n");     ch=readByte();     copy_to_user(buf,&ch,1);     return 1; }    static ssize_t spi_write(struct file *filp,const char __user *buf,size_t count,loff_t *f_ops) {     int i;     char *kbuf;     printk("<1>spi write!,count=%d\n",count);     kbuf=kmalloc(count,GFP_KERNEL);     if(copy_from_user(kbuf,buf,count))     {         printk("no enough memory!\n");         return -1;     }            for(i=0;i     {         writeByte(*kbuf);         printk("write 0x%02X!\n",*kbuf);         kbuf++;     }     return count; }    static ssize_t spi_ioctl(struct inode *inode,struct file *filp,unsigned int cmd,unsigned long data) {     return 0; }       static int __init spi_init(void) {     int result;     dev_t devno = MKDEV(spi_major, 0);        /**/     if (spi_major)         result = register_chrdev_region(devno, 1, spi_name);     else  /**/     {         result = alloc_chrdev_region(&devno, 0, 1, spi_name);         spi_major = MAJOR(devno);     }       if (result < 0)         return result;                cdev_init(&spiCdev, &spi_fops);     spiCdev.owner = THIS_MODULE;     spiCdev.ops = &spi_fops;     if (cdev_add(&spiCdev, devno, 1))         printk(KERN_NOTICE "Error adding spi %d", 0);        s3c2440_clkcon = (int *)ioremap(0x4C00000c,3);        spi_gpgcon = (int *)ioremap (0x56000060,4);     spi_gpgdat = (int *)ioremap (0x56000064,2);     spi_gpgup = (int *)ioremap (0x56000068,2);        spi_gpfcon = (int *)ioremap (0x56000050,2);     spi_gpfdat = (int *)ioremap (0x56000054,1);     spi_gpfup = (int *)ioremap (0x56000058,1);        spi_spcon1 = (int *)ioremap(0x59000020,1);     spi_spsta1 = (int *)ioremap(0x59000024,1);     spi_sppin1 = (int *)ioremap(0x59000028,1);     spi_sppre1 = (int *)ioremap(0x5900002c,1);     spi_sptdat1 = (int *)ioremap(0x59000030,1);     spi_sprdat1 = (int *)ioremap(0x59000034,1);           printk("Init spi success!\n");       return result; }       static void __exit spi_exit(void) {     cdev_del(&spiCdev);     unregister_chrdev_region(MKDEV(spi_major, 0), 1);     printk("<1>spi_exit!\n"); }    module_init(spi_init); module_exit(spi_exit);    MODULE_LICENSE("GPL"); MODULE_AUTHOR("nkzc"); MODULE_DESCRIPTION("SPI driver for S3C2440");

几点需要注意的地方:

1.一开始在spi_exit()函数中使用了void unregister_chrdev(unsigned int major, const char *name)函数来注销设备,但再次insmod驱动的时候提示"Device or resource busy",改为unregister_chrdev_region()后一切正常,说明即使只注册了一个设备,register_chrdev_region()和unregister_chrdev_region()也要配套使用。

2.定义spi_spcon1等寄存器变量时前面要加上volatile关键字,这样每次访问该变量时cpu会从实际内存中读取该值而不是使用寄存器中的值。尤其是spi_spsta1变量,它的最低位代表了spi发送接收是否ready,如果没有volatile,可能会在readByte()或writeByte()函数中导致死循环。

3.使用了module_param()宏向驱动传递参数,这里定义了一个int型的loopChar参数,加载模块时使用insmod mini2440_spi.ko loopChar=123 来设置loopChar的值。

 

测试程序:spi_test.c

view sourceprint? #include #include #include #include #include #include #include    int main(int argc, char **argv) {     int fd;     int count=0;     char buf[]={0x11,0x22,0x33,0x44,0x55};     fd = open("/dev/mini2440_spi", O_RDWR);     if (fd < 0) {         perror("open device spi");         exit(1);     }     count=write(fd,buf,sizeof(buf)/sizeof(buf[0]));     read(fd,buf,1);     printf("read byte is: 0x%02X\n",buf[0]);     close(fd);     return 0; }

很简单的一个程序,分别调用了open,write,read,close函数,可以观察输出结果,验证驱动程序是否正确,read的输出即为loopChar的值。

注意:open的时候要注意第二个参数flag,只有当flag为O_RDWR时,驱动中的相应的spi_read,spi_write函数才会被调用。