这两天参考网上的资料,自己写了个SPI的驱动,并实际测试通过。
硬件平台:mini2440 用的是S3C2440 的SPI1(共有2个SPI模块)
操作系统:linux-2.6.32.2
测试方法:将SPI的MISO与MOSI管脚短路,这样读数据的时候第一个发出的dummy字节即为收到的字节。
下面是驱动的源代码(mini2440_spi.c):
/***************************************************/
#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=%08Xn',*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=%02Xn',*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=%02Xn',*spi_spcon1); //Multi Master error detect disable,reserved,rech=*spi_sprdat0;lease *spi_sppin1=(0<<2)|(0<<0); printk('spi_sppin1=%02Xn',*spi_sppin1); return 0; } static int spi_release(struct inode *inode,struct file *filp) { //free irq free_irq(IRQ_EINT1, NULL); printk('<1>releasen'); 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=%dn',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 #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%02Xn',buf[0]); close(fd); return 0; } 很简单的一个程序,分别调用了open,write,read,close函数,可以观察输出结果,验证驱动程序是否正确,read的输出即为loopChar的值。 注意:open的时候要注意第二个参数flag,只有当flag为O_RDWR时,驱动中的相应的spi_read,spi_write函数才会被调用。
上一篇:linux-2.6.32在mini2440开发板上移植 移植I2C-EEPROM 驱动
下一篇:ARM9 S3C2440 定时器中断
推荐阅读最新更新时间:2024-11-11 14:02
设计资源 培训 开发板 精华推荐
- LT3460ESC6-1 5V 至 12V 升压转换器的典型应用电路
- 1000W,交流转直流单路输出电源,用于交流转直流电源
- AZ431电流源或限流稳压器的典型应用电路
- 具有 3000:1 PWM 调光的 LT3492 三路输出 LED 驱动器的典型应用
- LTC3736、具有轨跟踪功能的 2.5V/5A 和 1.2V/5A 稳压器
- LTC3374IUHF 降压稳压器的典型应用电路,具有由高压上游降压转换器驱动的顺序启动
- OP284FSZ-REEL 高端负载电流监控器的典型应用
- 使用 Analog Devices 的 AD9559BCPZ 的参考设计
- M41T62LC6F适配器板,用于标准DIL 24插座
- Sheet_2