這兩天參考網(wǎng)上的資料,自己寫(xiě)了個(gè)SPI的驅(qū)動(dòng),并實(shí)際測(cè)試通過(guò)。
硬件平臺(tái):mini2440 用的是S3C2440 的SPI1(共有2個(gè)SPI模塊)
操作系統(tǒng):linux-2.6.32.2
測(cè)試方法:將SPI的MISO與MOSI管腳短路,這樣讀數(shù)據(jù)的時(shí)候第一個(gè)發(fā)出的dummy字節(jié)即為收到的字節(jié)。
下面是驅(qū)動(dòng)的源代碼(mini2440_spi.c):
#include <linux/miscdevice.h> |
#include <linux/interrupt.h> |
#include <mach/regs-gpio.h> |
#include <mach/hardware.h> |
#include <linux/kernel.h> |
#include <linux/module.h> |
#include <linux/moduleparam.h> |
#include <linux/string.h> |
#include <linux/spinlock.h> |
module_param(loopChar, int ,S_IRUGO); |
static int spi_major = 55; |
#define spi_name "mini2440_spi" |
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; |
volatile int *spi_gpfdat=NULL; |
volatile int *spi_gpfup=NULL; |
volatile int *spi_gpgcon=NULL; |
volatile int *spi_gpgdat=NULL; |
volatile int *spi_gpgup=NULL; |
volatile int *s3c2440_clkcon; |
volatile int *spi_spcon1; |
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 = |
static int spi_open( struct inode *inode, struct file *filp) |
filp->private_data =&spiCdev; |
*s3c2440_clkcon |=0x40000; |
printk( "s3c2440_clkcon=%08X\n" ,*s3c2440_clkcon); |
*spi_gpgcon |=0x0000FC00; |
printk( "spi_sppre1=%02X\n" ,*spi_sppre1); |
*spi_spcon1=(0<<6)|(0<<5)|(1<<4)|(1<<3)|(0<<2)|(0<<1)|(0<<0); |
printk( "spi_spcon1=%02X\n" ,*spi_spcon1); |
*spi_sppin1=(0<<2)|(0<<0); |
printk( "spi_sppin1=%02X\n" ,*spi_sppin1); |
static int spi_release( struct inode *inode, struct file *filp) |
free_irq(IRQ_EINT1, NULL); |
static void writeByte( const char c) |
static char readByte( void ) |
*spi_sptdat1 = ( char )loopChar; |
static ssize_t spi_read( struct file *filp, char __user *buf, size_t count,loff_t *f_ops) |
printk( "<1>spi read!\n" ); |
static ssize_t spi_write( struct file *filp, const char __user *buf, size_t count,loff_t *f_ops) |
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" ); |
printk( "write 0x%02X!\n" ,*kbuf); |
static ssize_t spi_ioctl( struct inode *inode, struct file *filp,unsigned int cmd,unsigned long data) |
static int __init spi_init( void ) |
dev_t devno = MKDEV(spi_major, 0); |
result = register_chrdev_region(devno, 1, spi_name); |
result = alloc_chrdev_region(&devno, 0, 1, spi_name); |
spi_major = MAJOR(devno); |
cdev_init(&spiCdev, &spi_fops); |
spiCdev.owner = THIS_MODULE; |
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" ); |
static void __exit spi_exit( void ) |
unregister_chrdev_region(MKDEV(spi_major, 0), 1); |
printk( "<1>spi_exit!\n" ); |
MODULE_DESCRIPTION( "SPI driver for S3C2440" ); |
幾點(diǎn)需要注意的地方:
1.一開(kāi)始在spi_exit()函數(shù)中使用了void unregister_chrdev(unsigned int major, const char *name)函數(shù)來(lái)注銷(xiāo)設(shè)備,但再次insmod驅(qū)動(dòng)的時(shí)候提示"Device or resource busy",改為unregister_chrdev_region()后一切正常,說(shuō)明即使只注冊(cè)了一個(gè)設(shè)備,register_chrdev_region()和unregister_chrdev_region()也要配套使用。
2.定義spi_spcon1等寄存器變量時(shí)前面要加上volatile關(guān)鍵字,這樣每次訪問(wèn)該變量時(shí)cpu會(huì)從實(shí)際內(nèi)存中讀取該值而不是使用寄存器中的值。尤其是spi_spsta1變量,它的最低位代表了spi發(fā)送接收是否ready,如果沒(méi)有volatile,可能會(huì)在readByte()或writeByte()函數(shù)中導(dǎo)致死循環(huán)。
3.使用了module_param()宏向驅(qū)動(dòng)傳遞參數(shù),這里定義了一個(gè)int型的loopChar參數(shù),加載模塊時(shí)使用insmod mini2440_spi.ko loopChar=123 來(lái)設(shè)置loopChar的值。
測(cè)試程序:spi_test.c
int main( int argc, char **argv) |
char buf[]={0x11,0x22,0x33,0x44,0x55}; |
fd = open( "/dev/mini2440_spi" , O_RDWR); |
perror ( "open device spi" ); |
count=write(fd,buf, sizeof (buf)/ sizeof (buf[0])); |
printf ( "read byte is: 0x%02X\n" ,buf[0]); |
很簡(jiǎn)單的一個(gè)程序,分別調(diào)用了open,write,read,close函數(shù),可以觀察輸出結(jié)果,驗(yàn)證驅(qū)動(dòng)程序是否正確,read的輸出即為loopChar的值。
注意:open的時(shí)候要注意第二個(gè)參數(shù)flag,只有當(dāng)flag為O_RDWR時(shí),驅(qū)動(dòng)中的相應(yīng)的spi_read,spi_write函數(shù)才會(huì)被調(diào)用。