领主阿德尔 达拉然: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函数才会被调用。