#include <linux/init.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/workqueue.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/mm.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <mach/lpc32xx_ssp.h>
#include <mach/board.h>
#include <mach/lpc32xx_gpio.h>
#include <mach/platform.h>
#include <mach/sys-lpc32xx.h>
#include <mach/lpc32xx_clkpwr.h>
#define SPIDEV_MAJOR 154 /* assigned */
#define GPIO_IOBASE io_p2v(GPIO_BASE)
#define CLK_PM_IOBASE io_p2v(CLK_PM_BASE)
#define BUFLENGTH 60
//#define DEBUG
#ifdef DEBUG
/* If you are writing a driver, please use dev_dbg instead */
#define p_debug(fmt, arg...) \
printk(KERN_WARNING fmt, ##arg)
#else
#define p_debug(fmt, arg...) \
({ if (0) printk(KERN_WARNING fmt, ##arg); 0; })
#endif
struct myspistruct
{
struct cdev cdev;
dev_t devt;
void __iomem *membase;
int irq;
wait_queue_head_t waitq;
spinlock_t lock;
u8 bufa[BUFLENGTH];
u8 bufb[BUFLENGTH];
unsigned int lena;
unsigned int lenb;
};
struct myspistruct mydev;
struct myspistruct *dev=&mydev;
//***********************************************************************
static irqreturn_t lpc32xx_spi_irq(int irq, void *dev_id)
{
u8 data=0;
unsigned long flags;
int status;
/* Disable interrupts for now, do not clear the interrupt states */
__raw_writel(0, SSP_IMSC(dev->membase));
if(__raw_readl(SSP_RIS(dev->membase)) &(SSP_MIS_RTMIS | SSP_MIS_RXMIS))
{
spin_lock_irqsave(&dev->lock, flags);
/* Has an overflow occurred? */
if (unlikely(__raw_readl(SSP_MIS(dev->membase)) &
SSP_MIS_RORMIS))
{
status = -EIO;
return status;
}
/* Clear other interrupts */
__raw_writel((SSP_ICR_RORIC | SSP_ICR_RTIC),SSP_ICR(dev->membase));
/* Is there any data to read? */
while (__raw_readl(SSP_SR(dev->membase)) & SSP_SR_RNE)
data = (u8)__raw_readl(SSP_DATA(dev->membase));
if((dev->lena < BUFLENGTH)&&(dev->lena>0))
{
dev->bufa[dev->lena]=data;
dev->lena++;
goto finalpoint;
}
if((dev->lenb < BUFLENGTH)&&(dev->lenb>0))
{
dev->bufb[dev->lenb]=data;
dev->lenb++;
goto finalpoint;
}
if(dev->lena==0)
{
dev->bufa[dev->lena]=data;
dev->lena++;
p_debug("irq function data read begin\n");
goto finalpoint;
}
if(dev->lenb==0)
{
dev->bufb[dev->lenb]=data;
dev->lenb++;
}
finalpoint: spin_unlock_irqrestore(&dev->lock, flags);
if((dev->lenb) ==( dev->lena)==(BUFLENGTH))
{
printk(KERN_WARNING "read slow\n");
return -EBUSY;
}
if(dev->lena==BUFLENGTH||dev->lenb==BUFLENGTH)
wake_up_interruptible(&dev->waitq);
}
__raw_writel((SSP_IMSC_RTIM | SSP_IMSC_RXIM), SSP_IMSC(dev->membase));
return IRQ_HANDLED;
}
static int spidev_open(struct inode *inode, struct file *filp)
{
int status = -ENXIO;
int tmp;
unsigned long flags;
spin_lock_irqsave(&dev->lock, flags);
if (dev->devt == inode->i_rdev) {
status = 0;
}
tmp=__raw_readl(CLKPWR_TIMERS_PWMS_CLK_CTRL_1(CLK_PM_IOBASE));
__raw_writel(tmp&(~(1<<6)), CLKPWR_TIMERS_PWMS_CLK_CTRL_1(CLK_PM_IOBASE));//disable pwm clock
mdelay(1);
tmp=__raw_readl(GPIO_P2_MUX_SET(GPIO_IOBASE));
__raw_writel((tmp|1 << 5), GPIO_P2_MUX_SET(GPIO_IOBASE));//link to pin SSEL0
mdelay(1);
tmp=__raw_readl(GPIO_P_MUX_SET(GPIO_IOBASE));
__raw_writel( (tmp|0x03 << 9) | (0x01 << 12), GPIO_P_MUX_SET(GPIO_IOBASE));//link to pin SCK0,MOSI0,MISO0
mdelay(1);
__raw_writel(0x1, CLKPWR_SSP_CLK_CTRL(CLK_PM_IOBASE));//enable the clock
//************************************************************************
__raw_writel(SSP_CR1_SSP_DISABLE , SSP_CR1(dev->membase));
tmp=__raw_readl(SSP_CR1(dev->membase));
__raw_writel(tmp|SSP_CR1_MS, SSP_CR1(dev->membase));
//*************************************************************************
tmp=__raw_readl(SSP_CR1(dev->membase));
__raw_writel(tmp|SSP_CR1_SSP_ENABLE, SSP_CR1(dev->membase));//*******slave mode
// Setup default SPI mode
__raw_writel((SSP_CR0_DSS(8)|SSP_CR0_FRF_SPI|SSP_CR0_CPOL(0)|
SSP_CR0_CPHA(0) ), SSP_CR0(dev->membase));
// Clear and mask SSP interrupts
__raw_writel(0x0, SSP_ICR(dev->membase));
__raw_writel(0, SSP_IMSC(dev->membase));
dev->lena=0;
dev->lenb=0;
do
{
tmp = __raw_readl(SSP_DATA(dev->membase));
}
while (__raw_readl(SSP_SR(dev->membase)) & SSP_SR_RNE);
__raw_writel((SSP_IMSC_RTIM | SSP_IMSC_RXIM), SSP_IMSC(dev->membase));
enable_irq(dev->irq);
spin_unlock_irqrestore(&dev->lock, flags);
return status;
}
static int spidev_release(struct inode *inode, struct file *filp)
{
disable_irq(dev->irq);
return 0;
}
static spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
u8 *rd;
int len=BUFLENGTH;
int status;
unsigned long flags;
p_debug("driver read function before wait_event!\n");
wait_event_interruptible(dev->waitq,(dev->lena==BUFLENGTH)||(dev->lenb==BUFLENGTH));
spin_lock_irqsave(&dev->lock, flags);
if(dev->lena==BUFLENGTH)
{
dev->lena=0;
rd=dev->bufa;
}else
{
dev->lenb=0;
rd=dev->bufb;
}
if(count!=BUFLENGTH)
printk(KERN_WARNING "please keep read size %d\n",len);
p_debug("driver read function before copy_to_user!\n");
if(copy_to_user(buf, rd, BUFLENGTH))
status=-EFAULT;
else
status=BUFLENGTH;
spin_unlock_irqrestore(&dev->lock, flags);
return status;
}
static struct file_operations myspidev_fops = {
.owner = THIS_MODULE,
.read = spidev_read,
.open = spidev_open,
.release = spidev_release,
};
//**************************************************************************
static struct class *spidev_class;
static int lpc32xx_spi_probe(struct platform_device *pdev)
{
int ret;
int err;
int irq;
struct resource *res;
dev_t devno = MKDEV(SPIDEV_MAJOR,0);/*主,次设备号*/
if(SPIDEV_MAJOR){
/*register_chrdev_region(设备号指针,设备号数目,设备名)*/
ret = register_chrdev_region(devno,1,"myspidev");
}
else
/*alloc_chrdev_region(分配到的设备号指针,起始次设备号,
需要分配的设备号数目,设备名称)*/
{
ret = alloc_chrdev_region(&devno,0,1,"myspidev");
//SPIDEV_MAJOR=MAJOR(devno);
}
if (ret < 0)
return ret;
dev->devt=MKDEV(SPIDEV_MAJOR,0);
/*第二步:初始化cdev*/
cdev_init(&dev->cdev,&myspidev_fops);/*建立cdev和file_operations之间的连接*/
dev->cdev.owner=THIS_MODULE;
dev->cdev.ops=&myspidev_fops;
/*第三步:注册字符设备*/
/*cdev_add(cdev结构指针,设备号,设备数目)*/
err=cdev_add(&dev->cdev,devno,1);
if(err)
printk(KERN_NOTICE "Error %d adding globalvar",err);
spidev_class = class_create(THIS_MODULE, "myspidev");
device_create(spidev_class, NULL, MKDEV(SPIDEV_MAJOR, 0), NULL, "myspidev");
if (IS_ERR(spidev_class)){
cdev_del(&dev->cdev);/*注销cdev结构*/
unregister_chrdev_region(MKDEV(SPIDEV_MAJOR,0),1);/*注销设备号*/
return PTR_ERR(spidev_class);
}
spin_lock_init(&dev->lock);
init_waitqueue_head(&dev->waitq);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
irq = platform_get_irq(pdev, 0);
if ((!res) || (irq < 0) | (irq >= NR_IRQS))
{
printk(KERN_WARNING "no resource!\n");
return -EBUSY;
}
/* Save IO resources */
dev->membase = ioremap(res->start, res->end - res->start + 1);
if (!dev->membase)
{
ret = -EBUSY;
printk(KERN_WARNING "no memory!\n");
goto errout1;
}
dev->irq=irq;
ret = request_irq(dev->irq, lpc32xx_spi_irq,
IRQF_DISABLED, "spiirq", NULL);
if (ret)
{
ret = -EBUSY;
goto errout2;
}
disable_irq(dev->irq);
return 0;
errout2:
free_irq(dev->irq, NULL);
errout1:
iounmap(dev->membase);
return ret;
}
static int lpc32xx_spi_remove(struct platform_device *pdev)
{
free_irq(dev->irq, NULL);
iounmap(dev->membase);
class_destroy(spidev_class);
device_destroy(spidev_class,MKDEV(SPIDEV_MAJOR,0));
cdev_del(&dev->cdev);/*注销cdev结构*/
unregister_chrdev_region(MKDEV(SPIDEV_MAJOR,0),1);/*注销设备号*/
return 0;
}
static struct platform_driver lpc32xx_spi_driver = {
.probe = lpc32xx_spi_probe,
.remove = lpc32xx_spi_remove,
.driver = {
.name = "myspidev",
.owner = THIS_MODULE,
},
};
static int __init spidev_init(void)
{
printk(KERN_WARNING "hello,myspidev!\n");
return platform_driver_register(&lpc32xx_spi_driver);
}
static void __exit spidev_exit(void)
{
platform_driver_unregister(&lpc32xx_spi_driver);
printk(KERN_WARNING "goodbye,myspidev!\n");
}
module_init(spidev_init);
module_exit(spidev_exit);
MODULE_AUTHOR("yongan@zhuhaiyirui");
MODULE_DESCRIPTION("User mode SPI device interface");
MODULE_LICENSE("GPL");
//**************************************************************************************
qt测试
spidev.h
#ifndef SPIDEV_H
#define SPIDEV_H
#include <QPainter>
#include <QPen>
#include <QString>
#include <QRect>
#include <QPushButton>
#include <QPoint>
#include <QTimer>
#include <QCloseEvent>
#include "mythread.h"
class mainWidget : public QWidget
{
Q_OBJECT
public:
mainWidget(QWidget *parent = 0);
~mainWidget();
bool labelchange;
private slots:
void transData();
void threadslot();
void writeStopCmd();
void writeStartCmd();
protected:
void paintEvent(QPaintEvent *);
void closeEvent(QCloseEvent *event);
private:
int maxY;
int numYticks;
int minY;
int maxX;
int minX;
int numXticks;
enum { Margin = 20 };
QPushButton *btnclose;
QPushButton *btnstart;
static unsigned char startCmd[5];
static unsigned char stopCmd[5];
MyThread mythread;
QTimer *timer;
};
#endif // SPIDEV_H
spi.cpp
#include "spidev.h"
#include <termios.h> /*PPSIX terminal controle */
#include <stdio.h> /* stand input and output... */
#include <stdlib.h> /* stand lib */
#include <unistd.h> /* UNIX stand function */
#include <sys/types.h> /**/
#include <sys/stat.h> /**/
#include <fcntl.h> /* file controle */
unsigned char mainWidget::startCmd[5]={0xAA,0x01,0x02,0x03,0x04};
unsigned char mainWidget::stopCmd[5]={0xAA,0x04,0x03,0x02,0x01};
mainWidget::mainWidget(QWidget *parent)
: QWidget(parent)
{
setWindowTitle("myspidev data curve");
setBackgroundRole(QPalette::Dark);
setAutoFillBackground(true);
setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setFocusPolicy(Qt::StrongFocus);
minX=0;
minY=0;
maxX=500;
maxY=0xffffff;
numXticks=5;
numYticks=5;
btnclose = new QPushButton(tr("&close"),this);
btnstart = new QPushButton(tr("&start"),this);
connect(btnclose,SIGNAL(clicked()),this,SLOT(close()));
connect(btnstart,SIGNAL(clicked()),this,SLOT(transData()));
connect(&mythread,SIGNAL(finished ()),this,SLOT(writeStopCmd()));
connect(&mythread,SIGNAL(sgWriteStart()),this,SLOT(writeStartCmd()));
labelchange=false;
connect(btnstart,SIGNAL(clicked()),this,SLOT(threadslot()));
timer=new QTimer(this);
timer->start(200);
connect(timer,SIGNAL(timeout()),this,SLOT(update()));
}
mainWidget::~mainWidget()
{
}
void mainWidget::paintEvent(QPaintEvent *)
{
QPainter painter(this);
QRect rect(2*Margin, Margin,width() - 3 * Margin, height() - 4 * Margin);
if (!rect.isValid())
return;
QPen quiteDark = palette().dark().color().light();
QPen light = palette().light().color();
for (int i = 0; i <= numXticks; ++i) {
int x = rect.left() + (i * (rect.width() - 1) / numXticks);
painter.setPen(quiteDark);
painter.drawLine(x, rect.top(), x, rect.bottom());
painter.setPen(light);
painter.drawLine(x, rect.bottom(), x, rect.bottom() + 5);
painter.drawText(x, rect.bottom() + 5, Margin - 5, Margin,
Qt::AlignHCenter | Qt::AlignTop,
QString::number(i));
}
int y;
for (int j = 0; j <= numYticks; ++j) {
y = rect.bottom() - (j * (rect.height() - 1) / numYticks);
painter.setPen(quiteDark);
painter.drawLine(rect.left(), y, rect.right(), y);
painter.setPen(light);
painter.drawLine(rect.left() - 5, y, rect.left(), y);
painter.drawText(rect.left() - Margin, y - 10, Margin - 5, Margin,
Qt::AlignRight | Qt::AlignVCenter,
QString::number(j));
}
//------------------------------------------------------------coordination painted over.
btnstart->setGeometry(4*Margin,height()-2*Margin,3*Margin,Margin);
btnclose->setGeometry(10*Margin,height()-2*Margin,3*Margin,Margin);
//---------------------------------------------draw curve
QPoint points[240];
unsigned int *p=mythread.getdata();
for(int i=0;i<240;i++)
{
points[i].setX(i+rect.left());
points[i].setY((*(p+i))*(rect.height())/maxY+rect.top()+2*y);
}
painter.drawPoints(points, 240);
}
//************************************************serial port set
void set_speed(int fd, int speed)
{
int status;
struct termios Opt;
tcgetattr(fd, &Opt); // get attribute of serial port
tcflush(fd, TCIOFLUSH);
cfsetispeed(&Opt, speed);
cfsetospeed(&Opt, speed);
status = tcsetattr(fd, TCSANOW, &Opt); // set attribute
if (status != 0)
{
perror("tcsetattr fd1");
return;
}
tcflush(fd,TCIOFLUSH);
}
int set_data_format(int fd,int databits,int stopbits,int parity)
{
struct termios opt;
if( tcgetattr(fd, &opt) != 0)
{
perror("SetupSerial 1");
return -1;
}
opt.c_cflag &= ~CSIZE;
switch (databits)
{
case 5:
opt.c_cflag |= CS5;
break;
case 6:
opt.c_cflag |= CS6;
break;
case 7:
opt.c_cflag |= CS7;
break;
case 8:
opt.c_cflag |= CS8;
break;
default:
fprintf(stderr,"Unsupported data size\n");
return -1;
}
switch (parity)
{
case 'n':
case 'N':
opt.c_cflag &= ~PARENB; /* Clear parity enable */
opt.c_iflag &= ~INPCK; /* Enable parity checking */
break;
case 'o':
case 'O':
opt.c_cflag |= (PARODD | PARENB); /* parity checking */
opt.c_iflag |= INPCK; /* Disnable parity checking */
break;
case 'e':
case 'E':
opt.c_cflag |= PARENB; /* Enable parity */
opt.c_cflag &= ~PARODD; /* */
opt.c_iflag |= INPCK; /* Disnable parity checking */
break;
default:
fprintf(stderr,"Unsupported parity\n");
return -1;
}
switch (stopbits)
{
case 1:
opt.c_cflag &= ~CSTOPB;
break;
case 2:
opt.c_cflag |= CSTOPB;
break;
default:
fprintf(stderr,"Unsupported stop bits\n");
return -1;
}
/* Set input parity option */
if (parity != 'n')
opt.c_iflag |= INPCK;
opt.c_cc[VTIME] = 100; // 10 seconds
opt.c_cc[VMIN] = 0;
tcflush(fd, TCIFLUSH); /* Update the options and do it NOW */
if (tcsetattr(fd, TCSANOW, &opt) != 0)
{
perror("SetupSerial 3");
return -1;
}
return 0;
}
//******************************************************************serial port set
void mainWidget::transData()
{
//change labeltext
labelchange=!labelchange;
if(labelchange)
btnstart->setText(tr("&stop"));
else
btnstart->setText(tr("&start"));
update();
}
void mainWidget::writeStopCmd()
{
int fd;
fd = open("/dev/ttyS1", O_RDWR); // read and write
if(fd == -1)
{
printf("Can't open ttyS1!\n");
exit(0);
}
set_speed(fd, B9600); // 9600
if (set_data_format(fd, 8, 1, 'N')== -1)
{
printf("Data format Error!\n");
exit(1);
}
::write(fd,stopCmd,5);
::close(fd);
}
void mainWidget::writeStartCmd()
{
int fd;
printf("writeStartCmd works!\n");
fd = open("/dev/ttyS1", O_RDWR); // read and write
if(fd == -1)
{
printf("Can't open ttyS1!\n");
exit(0);
}
set_speed(fd, B9600); // 9600
if (set_data_format(fd, 8, 1, 'N')== -1)
{
printf("Data format Error!\n");
exit(1);
}
::write(fd,startCmd,5);
::close(fd);
}
void mainWidget::threadslot()
{
if (mythread.isRunning())
{
mythread.stop();
}
else
{
mythread.start();
}
}
void mainWidget::closeEvent(QCloseEvent *event)
{
mythread.stop();
mythread.wait();
event->accept();
}
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QtGui/QWidget>
#include <QThread>
#include <QVector>
#include <QMutex>
class MyThread : public QThread
{
Q_OBJECT
public:
MyThread();
void stop();
unsigned int* getdata();
signals:
void sgWriteStart();
protected:
void run();
private:
volatile bool stopped;
unsigned int mydata[240];
QVector<unsigned int> vect;
QMutex mutex;
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <stdio.h> /* stand input and output... */
#include <stdlib.h> /* stand lib */
#include <unistd.h> /* UNIX stand function */
#include <sys/types.h> /**/
#include <sys/stat.h> /**/
#include <fcntl.h> /* file controle */
#include <errno.h> /* error */
#include <strings.h>
#include <pthread.h>
MyThread::MyThread()
{
stopped = false;
memset(mydata,0,sizeof(mydata));
}
void MyThread::run()
{
int fd;
int res;
unsigned char spidata[60];
fd=open("/dev/myspidev",O_RDWR);
if (fd==-1){
printf("Can't open myspidev device!\n");
exit(EXIT_FAILURE);
}
vect.clear();
emit sgWriteStart();
printf(" sgWriteStart() sends!\n");
while (!stopped)
{
res=::read(fd,spidata,60);
if(res!=60)
printf("read error\n");
mutex.lock();
for(int i=0;i<60;i++)
{
vect.append((int)spidata[i]);
}
mutex.unlock();
}//------------------while ends
// printf("after while\n");
stopped = false;
::close(fd);
emit finished ();
}
void MyThread::stop()
{
stopped = true;
}
unsigned int* MyThread::getdata()
{
QVector<unsigned int>::iterator T = vect.begin();
mutex.lock();
if(vect.size()>800)
{
for(int i=0;i<240;i++)
{
mydata[i]=(vect[i*3] << 16)+(vect[i*3+1] << 8)+(vect[i*3+2]);
T++;
T++;
T++;
}
vect.erase(vect.begin(),T);
//the following used for debug
/* for(int i=0;i<720;i++)
{
if(i%15==0)
printf("\n");
printf("%3x\t",vect[i]);
}
while(1);*/
}
mutex.unlock();
return mydata;
}
main.c
#include <QtGui/QApplication>
#include "spidev.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
mainWidget w;
w.show();
return a.exec();
}