参考一:
这两天参考网上的资料,自己写了个SPI的驱动,并实际测试通过。
硬件平台:mini2440 用的是S3C2440 的SPI1(共有2个SPI模块)
操作系统:linux-2.6.32.2
测试方法:将SPI的MISO与MOSI管脚短路,这样读数据的时候第一个发出的dummy字节即为收到的字节。
下面是驱动的源代码(mini2440_spi.c):
/***************************************************/ |
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;//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 = |
/********************************************************/ |
static int spi_open(struct inode *inode,struct file *filp) |
filp->private_data =&spiCdev; |
/*********************************************** |
************************************************/ |
/*control PCLK into spi block*/ |
*s3c2440_clkcon |=0x40000; |
printk("s3c2440_clkcon=%08X\n",*s3c2440_clkcon); |
/*********************************************** |
************************************************/ |
/*config SCK1,MOSI1,MISO1 = 11*/ |
*spi_gpgcon |=0x0000FC00; |
/*poll up MISO1 MOSI1,SCK1*/ |
/*********************************************** |
************************************************/ |
/*********************************************** |
************************************************/ |
//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); |
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"); |
几点需要注意的地方:
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
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]); |
很简单的一个程序,分别调用了open,write,read,close函数,可以观察输出结果,验证驱动程序是否正确,read的输出即为loopChar的值。
注意:open的时候要注意第二个参数flag,只有当flag为O_RDWR时,驱动中的相应的spi_read,spi_write函数才会被调用。
参考二:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include //printk()
#include
#include
#include
#include
#include //u8,u16,u3……
#include
#include
#include
#include //和任务相关
#include
#include
#include
#include
#include
#include
#include
#include //copy_to_user(),copy_from_user()
#include
#include
#include
#include
#include
//--------------------------------------------------------------------------------------------------------------
#define SPI_NAME "MINI2440_SPI"
static int SPI_MAJOR= 55;
//static int spi_major=0;//主设备号为0表示动态分配主设备号,自定义次设备号。MKDEV()表示主次合并为设备号
struct spi_dev
{
struct cdev cdev;
char dataTx[4];
char dataRx[4];
};
struct spi_dev *spi_devp;
//-------------------------------------------------------------------------------------------------------
//GPG and GPE control register setting
#define GPG_DEFAULT (S3C2410_GPG3_EINT11|S3C2410_GPG6_EINT14|S3C2410_GPG2_nSS0)
#define GPE_DEFAULT (S3C2410_GPE11_SPIMISO0|S3C2410_GPE12_SPIMOSI0|S3C2410_GPE13_SPICLK0)
#define SPI_CON_DEFAULT (0<<0|0<<1|1<<2|1<<3|1<<4|0<<5|0<<6)//具体设置根据情况确定
#define SPI_TXRX_READY ((readl(SPSTA0)&0x1)==0x1)//发送接收标志位判断
//-------------------------------------------------------------------------------------------------------
//GPG GPE SPI address declare
//GPG
#define GPGCON (unsigned long)ioremap(0x56000060,4)
#define GPGDAT (unsigned long)ioremap(0x56000064,4)
#define GPGUP (unsigned long)ioremap(0x56000068,4)
//GPE
#define GPECON (unsigned long)ioremap(0x56000040,4)
#define GPEDAT (unsigned long)ioremap(0x56000044,4)
#define GPEUP (unsigned long)ioremap(0x56000048,4)
//SPI
#define SPCON0 (unsigned long)ioremap(0x59000000,4)
#define SPSTA0 (unsigned long)ioremap(0x59000004,4)
#define SPPIN0 (unsigned long)ioremap(0x59000008,4)
#define SPPRE0 (unsigned long)ioremap(0x5900000c,4)
#define SPTDAT0 (unsigned long)ioremap(0x59000010,1)
#define SPRDAT0 (unsigned long)ioremap(0x59000014,1)
//CLK control
#define CLKCON (unsigned long)ioremap(0x4c00000c,4)
#define SCRPND (unsigned long)ioremap(0x4a000000,4)
#define INTPND (unsigned long)ioremap(0x4a000010,4)
#define INTMSK (unsigned long)ioremap(0x4a000008,4)
//----------------------------------------------------------------------------------------------------------------------------
//mini2440 SPI config
static void mini2440_spi_config(void)
{
unsigned int port_status;
if(!(CLKCON&(1<<18)))
{
port_status=readl(CLKCON);
port_status|=(1<<18);
writel(port_status,CLKCON);//时钟使能
}
port_status=readl(GPGCON);
port_status&=~GPG_DEFAULT;
port_status|=GPG_DEFAULT;//GPG control
writel(port_status,GPGCON);
port_status=readl(GPECON);
port_status&=~GPE_DEFAULT;
port_status|=GPE_DEFAULT;
writel(port_status,GPECON);//GPE control
port_status=0x0;
port_status=readl(GPEUP);
port_status&=~(1<<13|1<<12|1<<11);
writel(port_status,GPEUP);//GPE_UP
port_status=0x0;
port_status=readl(GPGUP);
port_status&=~(1<<2|1<<3|1<<6);
writel(port_status,GPGUP);//GPG_UP
//disable_irq(IRQ_SPI0);
port_status=readl(SPPRE0);//SPI Baud speed
port_status=0x31;//PCLK=50MHz SPICLK=PCLK/2/(value+1)=1MHz,对于CC2500最大支持500K,一般采用250K
writel(port_status,SPPRE0);
port_status=readl(SPCON0);
port_status=SPI_CON_DEFAULT;
writel(port_status,SPCON0);//SPI control
port_status=readl(SPPIN0);
port_status&=~(0<<0|1<<1|1<<2);
port_status|=(1<<2|1<<1|0<<0);
writel(port_status,SPPIN0);//SPI pin setting
port_status=readl(SCRPND);
port_status|=(1<<22);
writel(port_status,SCRPND);
port_status=readl(INTPND);
port_status|=(1<<22);
writel(port_status,INTPND);
//enable_irq(IRQ_SPI0);
}
//-----------------------------------------------------------------------------------------------------------------
static int spi_open(struct inode *inode,struct file *filp)
{
filp->private_data=spi_devp;
mini2440_spi_config();
//filep->private_data=spi_devp;
s3c2410_gpio_setpin(S3C2410_GPG(2),0);
printk("-----------------------------------/n");
printk("MINI2440 SPI GPG GPE CLK set over/n");
printk("-----------------------------------/n");
return 0;
}
static int spi_release(struct inode *inode,struct file *filp)
{
printk("-----------------------------/n");
printk("S3C2440-mini2440_spi closed/n");
printk("------------------------------/n");
return 0;
}
//-----------------------------------------------------------------------------------------------------------
static ssize_t spi_read(struct file *filp,char __user *buf,size_t count)
{
//char *spiRxData;
struct spi_dev *dev=filp->private_data;
copy_to_user(buf,dev->dataRx,count);
return 0;
}
static ssize_t spi_write(struct file *filp,char __user *buf,size_t count)
{
volatile int endSpiTx=0;
volatile char spiTxData[4];
//int config;
int i=0;
//char str[20];
//char *txStr,*rxStr;
//volatile char *spiTxStr,*spiRxStr;
unsigned char string;
unsigned int port_status;
struct spi_dev *dev=filp->private_data;
//filp->private_data;
//获得设备结构体的指针
copy_from_user(dev->dataTx,buf,count);
for(i=0;i<4;i++)
{
spiTxData[i]=dev->dataTx[i];
}
printk("receive form user is %s/n",spiTxData);
while(endSpiTx==0)
{
//if(*spiTxData!='/0')
while(!SPI_TXRX_READY)
printk("SPI TXRX unready/n");
for(i=0;i<4;i++)
{
//s3c2410_gpio_setpin(S3C2410_GPG(2),0);//使能从SPI
writeb(spiTxData[i],SPTDAT0);//=*(spiTxData++);
string=readb(SPTDAT0);
printk("transfer char=%c/n",string);
//while(!SPI_TXRX_READY);
dev->dataRx[i]=readb(SPRDAT0);
printk("----------------------------/n");
printk("receive char=%c/n",dev->dataRx[i]);
printk("----------------------------/n");
//s3c2410_gpio_setpin(S3C2410_GPG(2),1);//解除使能从SPI
//i++;
}
endSpiTx=1;
}
port_status=readl(SPCON0);
port_status|=((0<<5)|(0<<4)|(1<<3)|(1<<2)|(0<<1)|(0<<0));
writel(port_status,SPCON0);
return 0;
}
//---------------------------------------------------------------------------------------------------------------
static const struct file_operations spi_fops={
.owner=THIS_MODULE,
.open=spi_open,
.release=spi_release,
.read=spi_read,
.write=spi_write,
};
static void spi_interrupt(int irq,void *dev_id,struct pt_regs *reg)
{
unsigned int port_status;
unsigned int i=0;
volatile int endSpiRx=0;
port_status=readl(SCRPND);
port_status|=(1<<22);
writel(port_status,SCRPND);
port_status=readl(INTPND);
port_status|=(1<<22);
writel(port_status,INTPND);
//SCRPND|=(1<<22);
//INTPND|=(1<<22);
disable_irq(IRQ_SPI0);
// SPCON0|=(0<<6)|(0<<5)|(0<<4)|(0<<3)|(1<<2)|(0<<1)|(1<<0);
while(endSpiRx==0)
{
while (!SPI_TXRX_READY);
writeb(0xff,SPTDAT0);
while(!SPI_TXRX_READY);
spi_devp->dataRx[i]=SPRDAT0;
i++;
if(i>4)
{
endSpiRx=1;
}
}
port_status=readl(SPCON0);
port_status|=((0<<6)|(1<<5)|(0<<4)|(1<<3)|(1<<2)|(0<<1)|(0<<0));
writel(port_status,SPCON0);
enable_irq(IRQ_SPI0);
printk("SPI irq end/n");
}
static void spi_setup_cdev(struct spi_dev *dev,int index)
{
int err,devno=MKDEV(SPI_MAJOR,index);
cdev_init(&dev->cdev,&spi_fops);
//cdev_init(&spi_dev,&spi_fops);
dev->cdev.owner=THIS_MODULE;
dev->cdev.ops=&spi_fops;
err=cdev_add(&dev->cdev,devno,1);
if(err)
printk("error/n");
printk("-----------------------------------/n");
}
static int __init spi_init(void)
{
int ret;
dev_t devno=MKDEV(SPI_MAJOR,0);
if(SPI_MAJOR)//申请设备号
ret=register_chrdev_region(devno,1,SPI_NAME);
else//动态申请设备号
{
ret=alloc_chrdev_region(&devno,0,1,SPI_NAME);
SPI_MAJOR=MAJOR(devno);
}
if(ret<0)
return ret;
//动态申请设备结构体的内存
spi_devp=kmalloc(sizeof(struct spi_dev),GFP_KERNEL);
if(!spi_devp)//申请失败
{
ret=-ENOMEM;
goto fail_malloc;
}
memset(spi_devp,0,sizeof(struct spi_dev));
spi_setup_cdev(spi_devp,0);
printk("init spi success/n");
return 0;
fail_malloc:
unregister_chrdev_region(devno,1);
return ret;
}
static void __exit spi_exit(void)
{
free_irq(IRQ_SPI0,NULL);
unregister_chrdev(SPI_MAJOR,SPI_NAME);
}
//-----------------------------------------------------------------------------------------------------------------------
module_init(spi_init);
module_exit(spi_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Gflytu");
MODULE_DESCRIPTION("SPI DRIVER FOR MINI_S3C2440");