基於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 联系我们: