现在的位置: 首页 > 综合 > 正文

spi从机驱动(lpc3250)

2013年09月02日 ⁄ 综合 ⁄ 共 18061字 ⁄ 字号 评论关闭

#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();
}

抱歉!评论已关闭.