基于S3C2440的SPI驱动的开发和测试


这几天导师有一个基于嵌入式的心电采集系统的实验课要开,让我帮忙设计。系统涉及到SPI驱动的开发,这里把代码贴出来供大家参考交流。


开发板型号:small2440

Linux内核版本:linux-2.6.38

文件系统:yaffs2

交叉编译器版本:arm-linux-gcc-4.3.3


/*******************************************
*文件描述:small2440开发板spi驱动,用于和stm32
* 进行通信,接收stm32采集的心电数据,
* stm32的AD位数为12位。
*作者:GYL
*日期:2015年5月5日
********************************************/

#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/pci.h>
#include <asm/uaccess.h>

#define MY_MAJOR 0 //主设备号
#define MY_MINOR 0//从设备号
#define DEVICE_NAME "small2440_spi" //设备名

static int spi_major = MY_MAJOR;
static struct cdev *spiCdev = NULL; //字符设备结构体
static struct class *myclass = NULL; //设备类结构体
/*****************************************************/
static int __init spi_init(void);
static void __exit spi_exit(void);
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 long spi_ioctl(struct file *filp,unsigned int cmd,unsigned long data);

static void __iomem *base_addr0;
static void __iomem *base_addr1;
static void __iomem *base_addr2;

#define S3C2440_GPG 0x56000060
#define S3C2440_CLK 0x4C00000C
#define S3C2440_SPI1 0x59000020

#define GPGCON (*(volatile unsigned long *)(base_addr0 + 0x00))
#define GPGDAT (*(volatile unsigned long *)(base_addr0 + 0x04))
#define GPGUP (*(volatile unsigned long *)(base_addr0 + 0x08))

#define CLKCON (*(volatile unsigned long *)(base_addr1 + 0x00))

#define SPCON1 (*(volatile unsigned long *)(base_addr2 + 0x00))
#define SPSTA1 (*(volatile unsigned long *)(base_addr2 + 0x04))
#define SPPIN1 (*(volatile unsigned long *)(base_addr2 + 0x08))
#define SPPRE1 (*(volatile unsigned long *)(base_addr2 + 0x0C))
#define SPTDAT1 (*(volatile unsigned long *)(base_addr2 + 0x10))
#define SPRDAT1 (*(volatile unsigned long *)(base_addr2 + 0x14))

#define SPI_TXRX_READY (((SPSTA1)&0x1) == 0x1)
#define SET_CS_LOW() (GPGCON &= ~(1 << 3))
#define SET_CS_HIGH() (GPGCON |= (1 << 3))

/*******************************************************
*spi_open函数,进行寄存器内存分配,寄存器配置,设备初始化
*********************************************************/
static int spi_open(struct inode *inode,struct file *filp)
{
base_addr0 = ioremap(S3C2440_GPG,12);
base_addr1 = ioremap (S3C2440_CLK,4);
base_addr2 = ioremap (S3C2440_SPI1,24);
/***********************************************
*PCLK
************************************************/
/*control PCLK into spi block, enable spi clock*/
CLKCON |= (1 << 18);
/***********************************************
*GPG PORTS
************************************************/
/*config SCK1,MOSI1,MISO1 = 11*/
GPGCON |= (3 << 14) | (3 << 12) | (3 << 10) | (3 << 6);
/*poll up MISO1 MOSI1,SCK1,nCSS1*/
GPGDAT &= ~((1 << 7) | (1 << 6) | (1 << 5) | (1 << 3));
GPGUP |= (1 << 7) | (1 << 6) | (1 << 5) | (1 << 3);
/***********************************************
*SPI REGS
************************************************/
//SPI Baud Rate Prescaler Register,Baud Rate=PCLK/2/(Prescaler value+1)
SPPRE1 = 0x18; //freq = 1M,
//polling,en-sck,master,CPOL = 0,CPHA = 1,nomal = 0 | TAGD = 1
SPCON1 = (0<<6)|(0<<5)|(1<<4)|(1<<3)|(0<<2)|(1<<1)|(0<<0);
//Multi Master error detect disable,reserved,rech=*spi_sprdat0;lease
SPPIN1 = (0<<2)|(0<<0);

filp->private_data = spiCdev;

try_module_get(THIS_MODULE); //模块引用计数器自加
return 0;
}

/**********************************************************
*spi_release函数:内存释放
************************************************************/
static int spi_release(struct inode *inode,struct file *filp)
{
iounmap(base_addr0);
iounmap(base_addr1);
iounmap(base_addr2);

module_put(THIS_MODULE); //模块引用计数器自减

printk("<1>release\n");
return 0;
}

/**************************************************
*writeByte函数:向SPI发送数据寄存器写入一个字节数据
*****************************************************/
static void writeByte(const char c)
{
int j = 0;
SPTDAT1 = c;
for(j=0;j<0xFF;j++);
while(!SPI_TXRX_READY)
for(j=0;j<0xFF;j++);
}

/*****************************************************
*readByte函数:从SPI数据接收寄存器读取一个字节数据
*******************************************************/
static char readByte(void)
{
int j = 0;
char ch = 0;
SPTDAT1 = 0xFF;
for(j=0;j<0xFF;j++);
while(!SPI_TXRX_READY)
for(j=0;j<0xFF;j++);
ch=SPRDAT1;
return ch;
}

/************************************************************
*spi_read函数:接收stm32传过来的心电数据
*************************************************************/
static ssize_t spi_read(struct file *filp,char __user *buf,size_t count,loff_t *f_ops)
{
//int len=0;
unsigned short rdData;
printk("<1>spi read!\n");
// SET_CS_LOW();
readByte();
rdData = (unsigned short)readByte() << 8;
rdData |= (unsigned short)readByte();
// SET_CS_HIGH();
copy_to_user(buf,&rdData,2);
return 1;
}

/*****************************************************************
*spi_write函数:向stm32发送数据
******************************************************************/
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;
}
// SET_CS_LOW();
for(i=0;i<count;i++)
{
writeByte(*kbuf);
printk("write 0x%02X!\n",*kbuf);
kbuf++;
}
// SET_CS_HIGH();

kfree(kbuf);
kbuf = NULL;
return count;
}


static long spi_ioctl(struct file *filp,unsigned int cmd,unsigned long data)
{
return 0;
}

/**********************************************************/
//结构体file_operations在头文件 linux/fs.h中定义,用来存储驱动内核模块提供的对设备进行各种操作的函数的指针
static const struct file_operations spi_fops =
{
.owner=THIS_MODULE,
.open=spi_open,
.read=spi_read,
.unlocked_ioctl=spi_ioctl,
.release=spi_release,
.write=spi_write,
};

static int __init spi_init(void)
{
int ret;
dev_t devno = MKDEV(spi_major, MY_MINOR); //将主设备号和次设备号转换成dev_t类型
printk("spi_init initializing...\n");

spiCdev = cdev_alloc(); //为cdev动态分配内存
if(spiCdev == NULL)
{
printk("cdev_alloc error\n");
return -ENOMEM;
}
if (!spi_major)
{
ret = alloc_chrdev_region(&devno, 0, 1, DEVICE_NAME); //动态分配设备号
if(!ret)
{
spi_major = MAJOR(devno);

}
if(ret < 0)
{
return ret;
}
}

// cdev_init(spiCdev, &spi_fops); //初始化cdev字符设备结构体
spiCdev->owner = THIS_MODULE;
spiCdev->ops = &spi_fops;


if (cdev_add(spiCdev, devno, 1)) //将spi设备注册到系统
{
cdev_del(spiCdev);
spiCdev = NULL;
printk("cdev_add error\n");
return -1;
}

myclass = class_create(THIS_MODULE, DEVICE_NAME); //创建设备类
device_create(myclass, NULL, MKDEV(spi_major, MY_MINOR), NULL, DEVICE_NAME);
//创建设备节点
printk("Init spi success!\n");
return 0;
}


static void __exit spi_exit(void)
{
cdev_del(spiCdev); //将cdev从系统移除,并释放对应的内存
device_destroy(myclass, MKDEV(spi_major, MY_MINOR));//删除设备节点
class_destroy(myclass);//删除设备类

printk("spi_exit!\n");
}

module_init(spi_init);
module_exit(spi_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("gyl");
MODULE_DESCRIPTION("SPI driver for S3C2440");



测试程序:为了测试SPI驱动是否能正常工作,用简单的测试程序对其进行了测试,将MISO和MOSI短接即可。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>

int main(int argc, char **argv)
{
int fd;
int count=100;
unsigned short rdbuf;

char buf[]={0x11,0x22,0x33,0x44,0x55};
fd = open("/dev/small2440_spi", O_RDWR);
if (fd < 0) {
perror("open device spi");
exit(1);
}

write(fd,buf,sizeof(buf)/sizeof(buf[0]));
while(count--)
{
read(fd,&rdbuf,2);
printf("read byte is: 0x%04X\n",rdbuf);
}

close(fd);
return 0;
}




注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com 联系我们: