SH3001六軸傳感器應用(二)(IIC驅動開發)

一、前言

我這邊使用的開發板原本已經做好了該sensor的驅動,但是使用過程中發現,原始驅動sensor是通過事件的方式上報的,加速度和陀螺儀數據并不同步,不滿足使用要求,只有重新寫一個iic的驅動,進行sensor數據的讀取了。

二、驅動編寫

1、linuxIIC驅動屬于字符設備的驅動,所以使用字符設備的固定流程進行注冊就行了。首先第一步肯定是設備數的編寫如下,這里肯定是對應硬件接口IIC子節點下,進行編寫,這里的核心是“compatible?”屬性,這個一定要和驅動文件的compatible 保持一致,不然無法找到設備節點。

&i2c4 {status = "okay";pinctrl-0 = <&i2c4m1_xfer>;sh3001_acc@36 {compatible = "sh3001";reg = <0x36>;type = <SENSOR_TYPE_ACCEL>;pinctrl-names = "default";pinctrl-0 = <&sh3001_irq_gpio>;irq-gpio = <&gpio2 RK_PC4 IRQ_TYPE_LEVEL_HIGH>;irq_enable = <0>;poll_delay_ms = <30>;layout = <2>;status = "okay";};
}

2、接下來就是設備的init 和exit了,init用于開始進行設備的匹配,exit用于在驅動設備卸載時執行。

static int __init sh3001_init(void)
{int ret = 0;ret = i2c_add_driver(&sh3001_driver);return ret;
}static void __exit sh3001_exit(void)
{i2c_del_driver(&sh3001_driver);
}module_init(sh3001_init);
module_exit(sh3001_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("TRACY");

3、在執行module_init(sh3001_init)的時候,會調用sh3001_driver結構體,這里將會將會進行設備的匹配,sh3001_of_match的結構體數組就是去匹配設備數相關節點的。


/* 傳統匹配方式ID列表 */
static const struct i2c_device_id ap3216c_id[] = {{"sh3001", 0},  {}
};/*設備數匹配列表*/
static const struct of_device_id sh3001_of_match[] = {{.compatible = "sh3001"},{/*Sentinel*/}
};/*sh3001驅動結構體 */
static struct i2c_driver sh3001_driver = {.probe = sh3001_probe,.remove =sh3001_remove,.driver = {.owner = THIS_MODULE,.name = "sh3001",.of_match_table = sh3001_of_match},.id_table = ap3216c_id};

這里在說明一下.id_table = ap3216c_id?和設備樹 of_match_table 之間的關系。?

.id_table 是用于 傳統非設備樹匹配(非-DT)平臺 上的 I2C 設備名稱匹配。它提供了一個 字符串設備名數組(如 "sh3001"),供內核在 手動注冊 i2c_client 的平臺 中與驅動進行匹配(例如老的板文件、用戶空間調用 i2c_new_device())。

和設備樹 of_match_table 的區別?

匹配方式使用場景依據需要的字段
.id_table非設備樹平臺i2c_client->name.id_table = ...
.of_match_table設備樹平臺(Device Tree)compatible.of_match_table = ...

也就是說:

  • 設備樹驅動匹配走的是 of_match_table(即你設備樹中寫了 compatible = "xxx"

  • 傳統方式(不使用設備樹)走的是 id_table

  • 內核 會將匹配的 of_device_id 轉換為 i2c_device_id(如果 .id_table 存在)傳給 probe 的 id 參數。

  • 你用的是設備樹,如果 id_table 不寫或者為 NULL,內核也可以匹配,但有些框架依賴 id_table 比如 i2c_register_driver() 的自動注冊機制,或者 module alias 功能就會缺失。

static const struct i2c_device_id ap3216c_id[] = {{ "ap3216c", 0 },{ }
};
MODULE_DEVICE_TABLE(i2c, ap3216c_id);  // 重要!static const struct of_device_id ap3216c_of_match[] = {{ .compatible = "liteon,ap3216c" },{ }
};
MODULE_DEVICE_TABLE(of, ap3216c_of_match);

這樣你的驅動即支持設備樹匹配,又支持非設備樹注冊,同時也能被 modprobeudev 正確識別。

4、設備數成功匹配后,就開始probe()函數的調用,首先得定義定義一個標準的IIC設備的結構體。

接下也算是固定流程:

分配結構體內存---->注冊字符設備、創建設備號----->初始化字符設備----->添加字符設備----->創建類----->創建設備。

這里需要注意的就是需要確定這些創建、添加是否成功。

struct sh3001_dev{struct i2c_client * client;   /*i2c 設備*/dev_t   devid;                  /*設備號 */struct cdev  cdev;              /*字符設備核心*/struct class * class;            /*類*/struct device * device;         /*設備*/struct device_node * nd;       /*設備節點*/};/** @description 	: i2c驅動的probe函數,當驅動與*                    設備匹配以后此函數就會執行* @param – client	: i2c設備* @param - id      	: i2c設備ID* @return          	: 0,成功;其他負值,失敗*/
static int sh3001_probe(struct i2c_client * client,const struct i2c_device_id *id)
{int ret;struct sh3001_dev * sh3001_cdev;/*分配結構體內存*/sh3001_cdev=devm_kzalloc(&client->dev,sizeof(*sh3001_cdev),GFP_KERNEL);if(!sh3001_cdev){return -ENOMEM;}/*注冊字符設備驅動*//*創建設備號*/ret = alloc_chrdev_region(&sh3001_cdev->devid,0,SH3001_CNT,SH3001_NAME);if(ret<0){pr_err("%s Couldn't alloc_chrdev_region, ret=%d\r\n", SH3001_NAME, ret);return -ENOMEM;}else{printk("alloc chrdev sucess\n");}/*初始化cdev*/sh3001_cdev->cdev.owner = THIS_MODULE;cdev_init(&sh3001_cdev->cdev,&sh3001_ops);/*添加一個cdev*/ret = cdev_add(&sh3001_cdev->cdev,sh3001_cdev->devid,SH3001_CNT);if(ret < 0){goto del_unregister;}else{printk("cdev add sucess cdev\n");}/*創建類 */sh3001_cdev->class = class_create(THIS_MODULE,SH3001_NAME);if(IS_ERR(sh3001_cdev->class))goto del_cdev;printk("create class sucess\n");/*創建設備*/sh3001_cdev->device = device_create(sh3001_cdev->class,NULL,sh3001_cdev->devid,NULL,SH3001_NAME);if(IS_ERR(sh3001_cdev->device))goto destroy_class;sh3001_cdev->client=client;i2c_set_clientdata(client,sh3001_cdev);printk("create device sucess:%p  iic_client:%p\n",(void *)&sh3001_cdev->cdev,(void *)sh3001_cdev->client);return 0;del_cdev:cdev_del(&sh3001_cdev->cdev);destroy_class:device_destroy(sh3001_cdev->class,sh3001_cdev->devid);del_unregister:unregister_chrdev_region(sh3001_cdev->devid, SH3001_CNT);return -EIO;}

5、上面probe()在初始化字符設備時是映射了應用的接口的。read()write()的方式比較常見,這里我們使用ioctl去進行寄存器的控制讀取。

 static const struct file_operations sh3001_ops = {.owner = THIS_MODULE,.open = sh3001_open,.unlocked_ioctl = sh3001_ioctl,.compat_ioctl = sh3001_ioctl,.release = sh3001_release
};cdev_init(&sh3001_cdev->cdev,&sh3001_ops);

使用ioctl需要注意的就是命令的注冊

#define  SH3001_IOCTL_WRITE    _IOW('s',0x01,struct sh3001_ioctl_data)
#define  SH3001_IOCTL_READ     _IOR('s',0x02,struct sh3001_ioctl_data)
#define  SH3001_IOCTL_START    _IOR('s',0x03,struct sh3001_ioctl_data)

這些宏是 Linux 提供的,用來創建 ioctl 命令編號。其目的是:

唯一標識一個 ioctl 命令,并且告訴內核和驅動:

  • 命令屬于哪個設備

  • 命令編號是多少

  • 用戶是否傳遞了參數

  • 參數的方向(從用戶傳給內核,還是從內核返回給用戶)

  • 參數的數據結構是什么

  • ?并且這個命令在應用層也有一樣使用調用,不然應用層無法傳遞到對應ioctl的接口。

接下來就是ioctl代碼的實現,其核心就是通過文件指針找到iic的驅動地址。

static long sh3001_ioctl (struct file * filp, unsigned int cmd, unsigned long arg_addr)
{//printk("enter ioctl\n");#if 1struct sh3001_ioctl_data data;int i;short acc_x,acc_y,acc_z,gyr_x,gyr_y,gyr_z;/* 從file結構體獲取cdev指針,再根據cdev獲取sh3001_dev首地址 */struct cdev * cdev = filp->f_path.dentry->d_inode->i_cdev;struct sh3001_dev * sh3001cdev=container_of(cdev,struct sh3001_dev,cdev);pr_info("sh3001_ioctl called with cmd=0x%x\n", cmd);/*返回 0 表示所有 n 字節數據成功拷貝返回 >0 的正數 表示拷貝失敗的字節數(即實際成功拷貝了 n - ret 字節)*/if(copy_from_user(&data,(void __user *)arg_addr,sizeof(data))){dev_err(sh3001cdev->device, "copy user data error\n");return -EFAULT;}if(!(sh3001cdev->client)){dev_err(sh3001cdev->device, "no iic client\n");return -ENODEV;}switch(cmd){case SH3001_IOCTL_WRITE:break;case SH3001_IOCTL_READ:sh3001_read_regs(sh3001cdev->client,data.reg,12,data.val);printk("ioctl reg:%d  data:\n",data.reg);for(i=0;i<6;i++){if(i%2==0)printk(" ");printk("%02x%02x",data.val[2*i+1],data.val[2*i]);}if(copy_to_user((void __user *)arg_addr,&data,sizeof(data))){return -EFAULT;}break;case SH3001_IOCTL_START:for(i=0;i<200;i++){sh3001_read_regs(sh3001cdev->client,data.reg,12,data.val);acc_x += ((data.val[1] << 8) & 0xFF00) + (data.val[0] & 0xFF);acc_y += ((data.val[3] << 8) & 0xFF00) + (data.val[2] & 0xFF);acc_z += ((data.val[5] << 8) & 0xFF00) + (data.val[4] & 0xFF);gyr_x += ((data.val[1] << 8) & 0xFF00) + (data.val[0] & 0xFF);gyr_y += ((data.val[3] << 8) & 0xFF00) + (data.val[2] & 0xFF);gyr_z += ((data.val[5] << 8) & 0xFF00) + (data.val[4] & 0xFF);msleep(5);}acc_x = acc_x/200;acc_y = acc_y/200;acc_z = acc_z/200;gyr_x = gyr_x/200;gyr_y = gyr_y/200;gyr_z = gyr_z/200;memcpy(&data.val[0],&acc_x,2);memcpy(&data.val[2],&acc_y,2);memcpy(&data.val[4],&acc_z,2);memcpy(&data.val[6],&gyr_x,2);memcpy(&data.val[8],&gyr_y,2);memcpy(&data.val[10],&gyr_z,2);if(copy_to_user((void __user *)arg_addr,&data,sizeof(data))){return -EFAULT;}break;default:break;}#endifreturn 0;
}

三、編譯?

?1、接下來就是將驅動文件.c編譯成ko模塊,makefile如下:

obj-m += sh3001_driver.oKDIR := /home/tracy/rockchip/linux_sdk/kernel  # kernel source directory
PWD := $(shell pwd)    #current directoryall:make -C $(KDIR) M=$(PWD) ARCH=arm64 CROSS_COMPILE=aarch64-buildroot-linux-gnu- modulesclean:make -C $(KDIR) M=$(PWD) clean

?2、將編譯好的驅動文件加載進驅動中可以使用insmod和modprobe命令,推薦使用modprobe命令,insmod 會自動處理依賴和參數傳遞。

rmmod移出驅動命令。

lsmod列出加載驅動命令。

四、調試技巧

在調試過程可以使用示波器查看接口是否有波形產生,多使用printk()打印調試日志。使用dmesg命令進行查看。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/92243.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/92243.shtml
英文地址,請注明出處:http://en.pswp.cn/web/92243.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

面試題:基礎的sql命令

基礎的 SQL 命令主要用于對數據庫進行查詢、新增、修改、刪除等操作&#xff0c;可分為以下幾類&#xff1a;一、數據查詢&#xff08;SELECT&#xff09;用于從表中獲取數據&#xff0c;是最常用的命令。 基本語法&#xff1a;SELECT 列名1, 列名2... FROM 表名 WHERE 條件;示…

Leetcode-3488距離最小相等元素查詢

依舊二分&#xff0c;鏈接如下3488. 距離最小相等元素查詢 看題目是個循環數組&#xff0c;記得當時做過一道什么題也是循環數組&#xff0c;就想著直接數組復制一下&#xff0c;然后跟上一道題一樣&#xff0c;用hashmap來存儲value的值以及value對應下標的vector。 和靈神的…

C++中的關聯容器

文章目錄使用關聯容器定義關聯容器關鍵字類型的要求pair類型用作返回類型關聯容器上的操作關聯容器的迭代器關聯容器和算法添加元素刪除元素map的下標操作訪問元素無序容器對關鍵字的要求關聯容器支持高效的關鍵字查找和訪問。兩個主要的關聯容器的類型是map和set。其中map中的…

【Git】git提交代碼報錯Git: husky > pre-commit

git提交代碼報錯原因 這個問題是因為當你在終端輸入git commit -m “XXX”,提交代碼的時候,pre-commit(客戶端)鉤子&#xff0c;它會在Git鍵入提交信息前運行做代碼風格檢查。如果代碼不符合相應規則&#xff0c;則報錯&#xff0c;而它的檢測規則就是根據.git/hooks/pre-commi…

Unity開發者快速認識Unreal 的C++(六)GameMode之PlayerController

繼承關系&#xff1a;Aactor&#xff0c;INavAgentInterface <--- AController<--- PlayerController &#xff0c;PlayerController也是一個Actor,繼承了Actor的一些通用的屬性和工具函數下圖是PlayerController初始化組件的一個子階段從圖中可以得到的信息是&#xf…

Vue 3 服務端渲染(SSR)與客戶端渲染(CSR)的區別及解決方案

1. SSR與CSR的區別1.1. SSR的原理服務端渲染&#xff08;SSR&#xff09;是在服務器端將 Vue 組件渲染為 HTML 字符串&#xff0c;并將其發送給客戶端。這種方式與客戶端渲染&#xff08;CSR&#xff09;不同&#xff0c;后者是在瀏覽器中執行 JavaScript 來生成 HTML。在 SSR …

Matlab快速回顧

一1.數值 顯示 格式format style 設置eg: pi format longE;or2.清除指令clc 清除命令行窗口clear 清除工作區cls3.搜索路徑設置path(path,E:\ads\)oraddpath4.M文件用戶把要實現的命令寫在一個以.m為擴展的文件中&#xff0c;然后由matlab系統進行解讀&#xff0c;最后運行結果…

開源低代碼+AI引擎:百特搭企業級開發平臺的演進

在數字化轉型進入深水區的今天&#xff0c;企業應用開發面臨前所未有的復雜挑戰&#xff1a;既要快速響應業務需求&#xff0c;又要確保系統靈活可控&#xff1b;既要降低技術門檻&#xff0c;又要保障核心安全。傳統開發模式與單一形態的低代碼工具已難以滿足多層次需求。融合…

學習 Android(十五)NDK進階及性能優化

學習 Android&#xff08;十五&#xff09;NDK進階及性能優化 對 NDK 相關知識有了初步的了解之后&#xff0c;我們可以更加深入的去學習 NDK 相關知識&#xff0c;接下來&#xff0c;我們將按照以下步驟進行深入學習&#xff1a; 深入理解JNI調用過程和性能消耗常見 JNI 坑&am…

QT5.12.8 QTabWidget 透明樣式QSS

/* 設置QTabWidget本身 :不加也行*/ QTabWidget#aaa_tabwdt {background: transparent;border: none; /* 移除邊框可能有助于透明效果 */ }/* 標簽頁內的容器部件 :必須加&#xff0c;標簽也才會透明 */ QTabWidget#aaa_tabwdt QWidget, QTabWidget#aaa_tabwdt QFrame {backgro…

【FAQ】Script導出SharePoint 目錄文件列表并統計大小

一、只導出文件列表的方法 1) 保存腳本&#xff08;建議名&#xff1a;D:\tmp\Export-SharePoint-FileList.ps1&#xff09; <# 導出 SharePoint 指定文件夾&#xff08;含子文件夾&#xff09;的文件列表到 CSV&#xff08;不統計大小&#xff09; 前提&#xff1a;已安…

《Thinking in Java》讀書筆記---控制執行流程

就像有感知的生物一樣&#xff0c;程序必須在執行過程中控制它的世界&#xff0c;并做出選擇。在Java中&#xff0c;你要使用執行控制語句來作出選擇。一、流程控制基礎概念1.1 流程控制的重要性流程控制結構決定了程序執行的順序和邏輯分支&#xff0c;是編程語言中最基礎也是…

極驗 G-star 人才特訓營:為業務安全領域培養下一代新興力量

本文導讀 極驗為什么要啟動 G-star 實習生培養計劃&#xff1f;50多位來自多所高校的同學&#xff0c;在極驗經歷了一場怎樣的“非典型”實習&#xff1f;技術大咖親授&#xff0c;先培訓再實戰&#xff0c;極驗打造的是怎樣的人才體系&#xff1f;同學有話說&#xff1a;培養計…

攻防世界-web-csaw-mfw

一.題目分析這邊提示使用了Git&#xff0c;試著訪問.git看是否存在.git泄露瀏覽了一下&#xff0c;很多都是亂碼&#xff0c;想著用githack將git庫克隆下看一下二.操作python2 GitHack.py http://url/.git訪問了一下flag.php&#xff0c;沒啥發現&#xff0c;在看一下index.php…

202506 電子學會青少年等級考試機器人四級實際操作真題

更多內容和歷年真題請查看網站&#xff1a;【試卷中心 -----> 電子學會 ----> 機器人技術 ----> 四級】 網站鏈接 青少年軟件編程歷年真題模擬題實時更新 2025年6月 青少年等級考試機器人實操真題四級 實際操作 主題&#xff1a;感應節能燈&#xff08;四級&am…

DLT645電表數據 保存到MySQL數據庫項目案例

目錄 1 案例說明 2 VFBOX網關工作原理 3 準備工作 4 配置VFBOX網關采集DLT645電表數據 5 網關寫數據到MYSQL數據庫 6 安裝MYSQL數據庫 7 其他說明 8 案例總結 1 案例說明 設置網關采集DLT645電表數據數據把采集的數據保存到MySQL數據庫。 2 VFBOX網關工作原理 VFBOX網關…

Redux與React - 異步狀態操作(React快速上手4)

異步操作樣板代碼1. 創建store的寫法保持不變&#xff0c;配置好同步修改狀態的方法 2. 單獨封裝一個函數&#xff0c;在函數內部return一個新函數&#xff0c;在新函數中 2.1 封裝異步請求獲取數據 2.2 調用同步actionCreater傳入異步數據生成一個action對象&#xff0c;并使用…

win10桌面右鍵沒有新建word

win10右鍵新建word不見解決方法1、點擊開始&#xff0c;找到運行命令行&#xff0c;輸入regedit&#xff0c;打開注冊表。2、在左側找到HKEY_CLASSES_ROOT目錄&#xff0c;并展開。3.找到.docx 雙擊&#xff08;默認&#xff09;一項&#xff0c;將其改為 Word.Document.12。關…

Docker國內可用鏡像(2025.08.06測試)

Docker渡渡鳥鏡像可用&#xff08;測試時間2025.08.06&#xff09;https://docker.aityp.com/使用渡渡鳥鏡像pull ollama latest 例子&#xff1a;docker pull swr.cn-north-4.myhuaweicloud.com/ddn-k8s/docker.io/ollama/ollama:0.10.1毫秒鏡像和軒轅鏡像也可用&#xff0c;但…

決策樹的實際案例

決策樹作為一種直觀、易解釋的機器學習算法&#xff0c;在金融、醫療、電商、風控等多個領域都有廣泛的實際應用。以下將講解1個典型案例&#xff1a;貸款違約預測。對于貸款違約預測&#xff0c;即在貸款人員在貸款之前對其進行預測&#xff0c;通過他的眾多特征情況判別是否可…