zoukankan      html  css  js  c++  java
  • spi驱动框架学习记录

    目录

    1. spi驱动框架简要描述

    2. 通过编写itop4412开发板rfid接口的spi驱动来熟悉spi驱动的编写

    1. spi驱动框架简要描述

      如上图所示,spi硬件架构中,一个spi控制器可以同时接多个spi外设,通信时通过片选脚来选择和某个spi外设通信。

      如上图,在linux内核中,将spi驱动分为spi主机控制器驱动和具体的spi设备驱动两个部分;spi主机控制器设备和spi主机控制器驱动挂载在平台总线上,在spi控制器驱动加载成功后,会创建一个SPI总线,spi主控制上连接的spi设备以及对应的spi设备驱动挂载在这个SPI总线上。

      spi控制器驱动部分大致实现如下功能

      1. 通过spi总线,将具体的spi设备和spi设备驱动匹配起来

      2. 提供数据收发函数给spi设备驱动,将spi设备驱动下发的数据转成总线波形发送出去,将总线上接收的数据上发给spi设备驱动

           spi控制器驱动一般由厂家编写,比如itop4412开发板,spi控制器驱动对应drivers/spi/spi-s3c64xx.c这个文件;

           spi控制器驱动中使用虚拟平台驱动结构体platform_driver注册的代码段如下

     1 drivers/spi/spi-s3c64xx.c
     2 
     3 static struct platform_driver s3c64xx_spi_driver = {
     4     .driver = {
     5         .name    = "s3c64xx-spi",
     6         .pm = &s3c64xx_spi_pm,
     7         .of_match_table = of_match_ptr(s3c64xx_spi_dt_match),
     8     },
     9     .probe = s3c64xx_spi_probe,
    10     .remove = s3c64xx_spi_remove,
    11     .id_table = s3c64xx_spi_driver_ids,
    12 };
    13 
    14 module_platform_driver(s3c64xx_spi_driver);

      在编写spi设备驱动时,我们只需要在对应spi控制器中添加spi设备描述,然后编写spi设备驱动即可;

      有两种方法添加spi设备描述,第一,在arm/mach-xxx对应的板级描述文件的spi_board_info结构体中添加;第二,在设备树中的spi控制器节点中添加。

    2. 通过编写itop4412开发板rfid接口的spi驱动来熟悉spi驱动的编写

    2.1 rfid接口对应的引脚图

      如上图所示,rfid的spi接在SPI_2控制器上,片选引脚是GPC1_2

    2.2 在设备树文件的spi_2控制器节点中添加rfid的spi设备

      spi_2控制器节点如下图所示

      在dts文件中,引用spi_2节点,添加rfid的spi设备

      在设备树spi控制器的节点中添加spi设备后,内核解析设备树时,就会在这个控制器对应的spi总线上注册一个spi设备spi_device,设备对应的属性名是”spidev”。

    2.3 编写spi设备驱动

      spi设备驱动主体由两部分组成:

           1. 创建注册struct spi_driver结构体,指定结构体的compatible属性为”spidev”,这样就可以和spi设备匹配

     1 static const struct of_device_id spidev_dt_ids[] = {
     2     { .compatible = "spidev" },
     3     {},
     4 };
     5 MODULE_DEVICE_TABLE(of, spidev_dt_ids);
     6 
     7 static struct spi_driver spidev_spi_driver = {
     8     .driver = {
     9         .name =        "spidev",
    10         .of_match_table = of_match_ptr(spidev_dt_ids),
    11     },
    12     .probe =    spidev_probe,
    13     .remove =    spidev_remove,
    14 
    15 };
    16 
    17 spi_register_driver(&spidev_spi_driver);

      2. 在应用程序接口函数中使用spi控制器驱动程序提供的接口函数收发数据,函数主体如下

     1 /* 数据传输结构体 */
     2 struct spi_transfer    t = {
     3     .tx_buf        = tx_buf,
     4     .len        = count,
     5 };
     6 
     7 struct spi_message    m;
     8 spi_message_init(&m);
     9 spi_message_add_tail(&t, &m);
    10 DECLARE_COMPLETION_ONSTACK(done);
    11 m.complete = complete;
    12 m.context = &done;
    13     
    14 printk("spi_async send begin!
    ");
    15 /* 异步收发函数 */
    16 status = spi_async(my_spi,&m);
    17 if(status == 0){
    18     wait_for_completion(&done);
    19     status = m.status;
    20     if (status == 0)
    21         status = m.actual_length;
    22 }

    设备驱动示例如下,驱动中注册了一个spi_driver,然后创建了一个杂项设备和应用程序交互

      1 #include <linux/init.h>
      2 #include <linux/module.h>
      3 #include <linux/ioctl.h>
      4 #include <linux/fs.h>
      5 #include <linux/device.h>
      6 #include <linux/err.h>
      7 #include <linux/list.h>
      8 #include <linux/errno.h>
      9 #include <linux/mutex.h>
     10 #include <linux/slab.h>
     11 #include <linux/compat.h>
     12 #include <linux/of.h>
     13 #include <linux/of_device.h>
     14 #include <linux/acpi.h>
     15 #include <linux/miscdevice.h>
     16 
     17 #include <linux/spi/spi.h>
     18 #include <linux/spi/spidev.h>
     19 
     20 #include <linux/uaccess.h>
     21 
     22 /* 创建spi结构体,填入设备树属性 */
     23 /* 创建杂项设备,用于读写及ioctl控制 */
     24 
     25 static struct spi_device *my_spi;
     26 static spinlock_t        spi_lock;
     27 static struct mutex        buf_lock;
     28 
     29 static long
     30 rc522_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
     31 {
     32     int            retval = 0;
     33     struct spidev_data    *spidev;
     34     struct spi_device    *spi;
     35     u32            tmp;
     36     unsigned        n_ioc;
     37     struct spi_ioc_transfer    ioc;
     38 
     39     int i;
     40     int status;
     41     struct spi_message    m;
     42     char *usertx_buf, *userrx_buf, txbuf[255];
     43 
     44     copy_from_user(&ioc, (struct spi_ioc_transfer __user *)arg, sizeof(struct spi_ioc_transfer));
     45     usertx_buf = (unsigned long)ioc.tx_buf;
     46     copy_from_user(txbuf, usertx_buf, ioc.len);
     47 
     48     printk("ioc.len = %d
    ", ioc.len);
     49 #if 1
     50     struct spi_transfer    t = {
     51         .tx_buf        = txbuf,
     52         .rx_buf     = txbuf,
     53         .len        = ioc.len,
     54     };
     55     
     56     spi_message_init(&m);
     57     spi_message_add_tail(&t, &m);
     58     DECLARE_COMPLETION_ONSTACK(done);
     59     m.complete = complete;
     60     m.context = &done;
     61 
     62     printk("spi_async spidev_ioctl begin!
    ");
     63     status = spi_async(my_spi,&m);
     64     if(status == 0){
     65         wait_for_completion(&done);
     66         status = m.status;
     67         if (status == 0)
     68             status = m.actual_length;
     69     }
     70 
     71     userrx_buf = (unsigned long)ioc.rx_buf;
     72     copy_to_user(userrx_buf, txbuf, ioc.len);
     73 
     74     printk("
    ");
     75 #endif
     76     return status;
     77 }
     78 
     79 static ssize_t rc522_write(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
     80 {
     81     int status;
     82     unsigned char tx_buf[512];
     83     
     84     if (count > 512) {
     85         printk("count > 512, rc522_write write failed, return.
    ");
     86         return -EMSGSIZE;
     87     }
     88     
     89     status = copy_from_user(tx_buf,buf,count);
     90     
     91     struct spi_transfer    t = {
     92         .tx_buf        = tx_buf,
     93         .len        = count,
     94     };
     95     struct spi_message    m;
     96     spi_message_init(&m);
     97     spi_message_add_tail(&t, &m);
     98     DECLARE_COMPLETION_ONSTACK(done);
     99     m.complete = complete;
    100     m.context = &done;
    101     
    102     printk("spi_async send begin!
    ");
    103     status = spi_async(my_spi,&m);
    104     if(status == 0){
    105         wait_for_completion(&done);
    106         status = m.status;
    107         if (status == 0)
    108             status = m.actual_length;
    109     }
    110     return status;
    111 }
    112 
    113 static ssize_t rc522_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
    114 {
    115     int status;
    116     unsigned char rx_buf[255];
    117 
    118     struct spi_transfer    t = {
    119         .rx_buf        = rx_buf,
    120         .len        = count,
    121     };
    122     struct spi_message    m;
    123     spi_message_init(&m);
    124     spi_message_add_tail(&t, &m);
    125     DECLARE_COMPLETION_ONSTACK(done);
    126     m.complete = complete;
    127     m.context = &done;
    128     
    129     printk("spi_async read begin!
    ");
    130     status = spi_async(my_spi,&m);
    131     if(status == 0){
    132         wait_for_completion(&done);
    133         status = m.status;
    134         if (status == 0)
    135             status = m.actual_length;
    136     }
    137     
    138     status = copy_to_user(buf,&rx_buf,status);
    139     
    140     return status;
    141 }
    142 
    143 int rc522_open(struct inode *inode,struct file *filp)
    144 {
    145     printk("rc522_open.
    ");
    146     return 0;
    147 }
    148 
    149 static struct file_operations rc522_ops = {
    150     .owner     = THIS_MODULE,
    151     .open     = rc522_open,
    152     .read    = rc522_read,
    153     .write     = rc522_write,
    154     .unlocked_ioctl = rc522_ioctl,
    155 };
    156 
    157 static struct miscdevice rc522_dev = {
    158     .minor    = MISC_DYNAMIC_MINOR,
    159     .fops    = &rc522_ops,
    160     .name    = "rc522",
    161 };
    162 
    163 #ifdef CONFIG_OF
    164 static const struct of_device_id spidev_dt_ids[] = {
    165     { .compatible = "spidev" },
    166     {},
    167 };
    168 MODULE_DEVICE_TABLE(of, spidev_dt_ids);
    169 #endif
    170 
    171 static int spidev_probe(struct spi_device *spi)
    172 {
    173     int            status;
    174 
    175     /* 检查是否是设备树节点 */
    176     if (spi->dev.of_node && !of_match_device(spidev_dt_ids, &spi->dev)) {
    177         dev_err(&spi->dev, "buggy DT: spidev listed directly in DT
    ");
    178         WARN_ON(spi->dev.of_node &&
    179             !of_match_device(spidev_dt_ids, &spi->dev));
    180     }
    181 
    182     my_spi = spi;
    183     spin_lock_init(&spi_lock);
    184     mutex_init(&buf_lock);
    185 
    186     return status;
    187 }
    188 
    189 static int spidev_remove(struct spi_device *spi)
    190 {
    191     printk("spidev_remove
    ");
    192     return 0;
    193 }
    194 
    195 static struct spi_driver spidev_spi_driver = {
    196     .driver = {
    197         .name =        "spidev",
    198         .of_match_table = of_match_ptr(spidev_dt_ids),
    199     },
    200     .probe =    spidev_probe,
    201     .remove =    spidev_remove,
    202 
    203 };
    204 
    205 static int __init spidrv_dts_init(void)
    206 {
    207     int status;
    208 
    209     misc_register(&rc522_dev);
    210 
    211     status = spi_register_driver(&spidev_spi_driver);
    212     if (status < 0) {
    213         misc_deregister(&rc522_dev);
    214     }
    215     return status;
    216 }
    217 
    218 static void __exit spidrv_dts_exit(void)
    219 {
    220     spi_unregister_driver(&spidev_spi_driver);
    221     misc_deregister(&rc522_dev);
    222 }
    223 
    224 module_init(spidrv_dts_init);
    225 module_exit(spidrv_dts_exit);
    226 
    227 MODULE_LICENSE("GPL");

    测试应用程序如下,在测试时,将spi接口的MOSI和MISO引脚对接,这样spi就可以收到自己发送的数据,这两个引脚是rfid接口的外侧第3脚和内测di4脚

      1 #include <stdint.h>
      2 #include <unistd.h>
      3 #include <stdio.h>
      4 #include <stdlib.h>
      5 #include <getopt.h>
      6 #include <fcntl.h>
      7 #include <sys/ioctl.h>
      8 #include <linux/types.h>
      9 #include <linux/spi/spidev.h>
     10 #include <pthread.h>
     11 #include <errno.h>
     12 #include <time.h>
     13 #include <string.h>
     14 
     15 #define filename  "/dev/rc522"
     16 
     17 #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
     18 
     19 /* 读写需要互斥 */
     20 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
     21 /* 1. 打开设备 */
     22 static int fd;
     23 /* 2. 创建三个线程 */
     24 /* 3. 一个线程每隔2s向设备写数据 */
     25 static void createThread_write(void);
     26 static void *thread_func_write(void *arg);
     27 /* 4. 另一个线程不停地读数据 */
     28 static void createThread_read(void);
     29 static void *thread_func_read(void *arg);
     30 /* 5. 第三个线程用ioctl测试数据读写 */
     31 static void createThread_ioctl(void);
     32 static void *thread_func_ioctl(void *arg);
     33 
     34 /* repetition repetition repetition */
     35 /* 按照正常的文件读写,不停地写数据 */
     36 /* 以阻塞的方式读数据 */
     37 /* XXX 多线程操作,将同步阻塞信号通知都走一遍 */
     38 /* 驱动,实现阻塞读 */
     39 /* 线程1 */
     40 static void *thread_func_write(void *arg)
     41 {
     42     int nByte;
     43     char data[6] = {0x12, 0x22, 0x34, 0x66, 0x32, 0x11};
     44 
     45     while (1) {
     46         pthread_mutex_lock(&mutex);
     47         printf("write.
    ");
     48         if ((nByte = write(fd, data, sizeof(data))) == -1) {
     49             printf("write failed, thread_func_write return.
    ");
     50             pthread_mutex_unlock(&mutex);
     51             return;
     52         }
     53         else {
     54             printf("write %d bytes.
    ", nByte);
     55         }
     56 
     57         pthread_mutex_unlock(&mutex);
     58 
     59         /* 每2秒发一个数据包 */
     60         sleep(2);
     61     }
     62 }
     63 
     64 static void createThread_write(void)
     65 {
     66     pthread_attr_t attr;
     67     pthread_t pthread_id;
     68 
     69     /* set pthread detach */
     70     pthread_attr_init(&attr);
     71     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
     72     pthread_create(&pthread_id, &attr, thread_func_write, NULL);
     73     pthread_attr_destroy(&attr);
     74 }
     75 
     76 /* 线程2 */
     77 static void *thread_func_read(void *arg)
     78 {
     79     int nByte;
     80     char buffer[255];
     81 
     82     while (1) {
     83         pthread_mutex_lock(&mutex);
     84         memset(buffer, 0, sizeof(buffer));
     85         if ((nByte = read(fd, buffer, sizeof(buffer))) == -1) {
     86             printf("read failed , thread_func_read return.
    ");
     87             return;
     88         }
     89         pthread_mutex_unlock(&mutex);
     90 
     91         printf("read %d bytes, buffer is : %s
    ", nByte, buffer);
     92 
     93         usleep(50);
     94         sleep(2);
     95     } /* end of while (1) */
     96 }
     97 
     98 static void createThread_read(void)
     99 {
    100     pthread_attr_t attr;
    101     pthread_t pthread_id;
    102 
    103     /* set pthread detach */
    104     pthread_attr_init(&attr);
    105     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    106     pthread_create(&pthread_id, &attr, thread_func_read, NULL);
    107     pthread_attr_destroy(&attr);
    108 }
    109 
    110 /* 线程3 */
    111 static void *thread_func_ioctl(void *arg)
    112 {
    113     int ret, i;
    114 
    115     uint8_t tx[] = {
    116         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    117         0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
    118         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    119         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    120         0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
    121         0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
    122         0xF0, 0x0D,
    123     };
    124     uint8_t rx[ARRAY_SIZE(tx)] = {0, };
    125     struct spi_ioc_transfer tr = {
    126         .tx_buf = (unsigned long)tx,
    127         .rx_buf = (unsigned long)rx,
    128         .len = ARRAY_SIZE(tx),
    129         .delay_usecs = 0,
    130         .speed_hz = 500000,
    131         .bits_per_word = 8,
    132     };
    133     while (1) {
    134         pthread_mutex_lock(&mutex);
    135         if ((ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr)) == -1) {
    136             printf("ioctl failed, thread_func_ioctl return.
    ");
    137             pthread_mutex_unlock(&mutex);
    138             return;
    139         }
    140         pthread_mutex_unlock(&mutex);
    141 
    142         for (i = 0; i < ARRAY_SIZE(tx); i++) {
    143             if (i % 6 == 0) {
    144                 printf("
    ");
    145             }
    146             printf("%.2X,", rx[i]);
    147         }
    148         printf("
    ");
    149 printf("ioctl.
    ");
    150         sleep(2);
    151     }
    152 }
    153 
    154 static void createThread_ioctl(void)
    155 {
    156     pthread_attr_t attr;
    157     pthread_t pthread_id;
    158 
    159     /* set pthread detach */
    160     pthread_attr_init(&attr);
    161     pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
    162     pthread_create(&pthread_id, &attr, thread_func_ioctl, NULL);
    163     pthread_attr_destroy(&attr);
    164 }
    165 
    166 int main(int argc, char ** argv)
    167 {
    168     /* 打开文件 */
    169     if ((fd = open(filename, O_RDWR)) == -1) {
    170         printf("open file %s failed.
    ", filename);
    171         return -1;
    172     }
    173     else {
    174         printf("open file %s successful.
    ", filename);
    175     }
    176 
    177     /* 创建第一个线程,每2s向设备写入数据 */
    178     createThread_write();
    179 
    180     /* 创建第二个线程,不停地读数据 */
    181     /* 阻塞需要驱动程序中实现 */
    182     createThread_read();
    183 
    184     /* 创建第三个线程 */
    185     createThread_ioctl();
    186 
    187     while (1) {
    188         sleep(1000);
    189     }
    190 
    191     return 0;
    192 }

    参考博客

           https://www.cnblogs.com/-4412/articles/5202793.html

  • 相关阅读:
    忘记的知识点补充
    mysql使用过程中出现的问题总结
    身份证校验
    数据库plsql配置
    前端字符间间距与单词间间距
    Oracle中的instr()函数 详解及应用
    Oracle执行过程中出现的问题
    572. Subtree of Another Tree 大树里包括小树
    404. Sum of Left Leaves 左叶子之和
    637. Average of Levels in Binary Tree 二叉树的层次遍历再求均值
  • 原文地址:https://www.cnblogs.com/lztutumo/p/13514028.html
Copyright © 2011-2022 走看看