1. 程式人生 > 實用技巧 >spi驅動框架學習記錄

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!\n");
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\n", 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!\n");
 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("\n");
 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.\n");
 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!\n");
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!\n");
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.\n");
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\n");
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\n");
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.\n");
 48         if ((nByte = write(fd, data, sizeof(data))) == -1) {
 49             printf("write failed, thread_func_write return.\n");
 50             pthread_mutex_unlock(&mutex);
 51             return;
 52         }
 53         else {
 54             printf("write %d bytes.\n", 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.\n");
 87             return;
 88         }
 89         pthread_mutex_unlock(&mutex);
 90 
 91         printf("read %d bytes, buffer is : %s\n", 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.\n");
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("\n");
145             }
146             printf("%.2X,", rx[i]);
147         }
148         printf("\n");
149 printf("ioctl.\n");
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.\n", filename);
171         return -1;
172     }
173     else {
174         printf("open file %s successful.\n", 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