## I2C使用情形: 1.如果不需要同步接收或是發送之情形(只需一條數據線SDA),需同時發送或接收資料可使用SPI。 2.傳送資料需要回應,接收資料需要回答,確保數據不丟失,就像TCP協議。 3.可同時掛載多個設備,且信號線不會搶佔(由主機決定與誰進行通訊)。 4.有時鐘信號進行同步讀寫(CPU可進中斷,中斷完後可繼續傳輸指令),需要一條clk(SCL)。 因此I2C只需兩條線通信(SCL and SDA):  **I2C內部電路** 使用開漏輸出,高電平由外部電壓提供。  使用開漏輸出原因:  為了使用多個設備,如果用推挽如果同時出現高和低電平,將會導致MOS管燒壞, 因此掛載之設備只提供低電平,高電平由外部提供,並通過上拉電阻進行限流,將狀態輸入到主機中,電路如下:  **I2C通訊協議** 啟動和結束:  發送資料給從機 跟always @ (posedge clk or negedge rst_n)差不多 (資料發送是高位先行)  主機接收從機資料 跟發送差不多  接收和發送後 主機或從機須回答接收或發送是否成功 0為應答,跟開漏輸出有關,應為0是強接地,1有可能是從機未回應。  **I2C相關細節** I2C每個設備都有不同的地址,同一個設備會分配相同地址,當需掛載相同設備時,可根據引腳修改後面幾位的地址。 可通過A0 A1(不一定有) A2(不一定有) 引腳,調整後三位地址。 I2C操作步驟通常是主機發送一個字節,其中前7位是地址,以及最後一位讀寫位 0為寫 1為讀 ,並接收從機應答,應答之後,則根據i2c設備進行後續指令,比如說ADC或是MPU6050,有可能是發送操控指令,或是讀取的記憶體位置。 讀取資料比較麻煩,因為在傳送讀取指令(第一個字節)後,會直接進入接收模式,接收從機資料,無法指令讀取底幾個記憶體位置,所以在讀取之前,會先進入寫模式,透過寫模式傳遞記憶體位置,這時候的從機記憶體指針會有紀錄,這時候在通過讀取指令,就能讀取對應位置。 **I2C device tree 配置** 使用的I2C設備為 AP3216C   是一種感測光的sensor,可量測 ALS(光強度),PS(距離)IR(光照sensor) 開發板使用引腳:  修改設備樹: 根據上面引腳使用,設置I2C引腳和電氣屬性(原來就有)  前面是地址,後面則為電器屬性,用於**pinctrl 子系統**。 配置I2C1使用的設備,屬性和地址,名稱不重要,只是用於查找。  如果再i2c1總線上遇到子節點設備地址一樣,需修改地址,不能一樣。 **driver撰寫**: 跟platform概念很像,分成driver 和 device。 首先是 driver 使用: 使用i2c_driver 結構體配置 i2c device tree 訊息,和probe成功時執行的函數。 那 module init 自然是register i2c_driver。  i2c_driver結構體:  id_table 不是用 device tree 進行匹配,而是i2c的設備id,適合沒有device tree 的情況。  接著是 of_match_table 用來匹配 device tree 的 compatiable  接著是在probe函數中註冊一個char device,方便使用read 和 write等 system call,配合應用進行i2c data 的讀取。 ## register char device 複習(之前的寫的太爛) char device 可以分為三個部分  1.首先是註冊設備號,要自己選設備號可以用**register_chrdev_region()**,由系統分配可以用**alloc_chrdev_region()**,MKDEV 和 MAJOR和 MINOR就是宏定義,可以處理這些地址得知設備號。 2.接著是創建設備,用CDEV結構體  讓KERNEL知道,當前設備進行WRITE等system call 時,要使用那些function(file_operations 結構體),用**cdev_init()** 進行連接,cnt 就是幾個設備,最後用**cdev_add()**和設備號相連。 file_operations 結構體需要註冊對應 function:   3.最後是使用class和device結構體,把設備掛載到/dev下,class則是設備的類,像是GPIO、I2C等。 分別使用class_create()和device_create()即可。 ## I2c Client 使用 **i2c driver 函數撰寫:** 使用**i2c_transfer()** 函數即可,第一個函數傳入i2c_client 中的adapter(裡面包括i2c匹配的演算法等),第二個傳入msg結構體矩陣,根據需求修改,第三個參數則是決定msg矩陣大小,read通常是兩個,因為read 是 先write 決定讀第幾個reg才read,write通常是一個。 read: msg 結構體: addr 是設備地址 flags是讀寫位 len是buf要寫或讀幾個byte buf 是要寫或讀的buffer  **i2c_read func:** msg[0]是寫操作,addr傳設備地址,flags=0為寫,buf傳reg,代表要讀的register,len為1 msg[1]是寫完的讀操作,flags=0x0001為讀操作,val為讀出來的資料,len是要讀幾位。 最後把adapter 和 msg[2] 以及msg矩陣大小傳進 i2c_transfer即可。  **i2c_write func:** buf 要進行修改,創一個新矩陣b,第一位一樣填要寫的reg位置,接著是要寫的資料,長度變成len+1,其他跟讀一樣。  使用方式如下:  42(rxd是sda)和43(txd是scl) PIN 是 I2C , mini沒有集成,需要另外接。  引腳配置如下:  用之前stm32的mpu6050做實驗。 在open中加入mpu6050_i2cinit()函數,利用write_reg去配置mpu6050相關電器屬性(抄以前stm32的)。  在來用read 去讀取三軸的信息:  最後是應用的撰寫:  成果:  代碼地址: [i2c代碼(不小心把mpu6050的部分刪除了,只剩ap3216c的)](https://github.com/amazingfraf177/linuxdriver/tree/main/14.i2c)
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up