目錄
前言
I2C簡介
基本特性??
I2C 協議
起始位
停止位
數據傳輸
應答信號
I2C 寫時序
I2C 讀時序
I.MX6U I2C
簡介
寄存器
地址寄存器I2Cx_IADR(x=1~4)
分頻寄存器I2Cx_IFDR
控制寄存器I2Cx_I2CR
狀態寄存器I2Cx_I2SR
數據寄存器I2Cx_I2DR
AP3216C 簡介
硬件原理圖
實驗程序編寫
i2c.h
i2c.c
ap3216c.h
ap3216c.c
main.c
前言
I2C 是最常用的通信接口,眾多的傳感器都會提供 I2C 接口來和主控相連,比如陀螺儀、加速度計、觸摸屏等等。
I.MX6U 有 4 個 I2C 接口,其中I2C1 接口連接了一個距離傳感器 AP3216C,本講實驗我們就來學習如何使用 I.MX6U 的 I2C 接口來驅動 AP3216C,讀取AP3216C 的傳感器數據。
I2C簡介
I2C(Inter-Integrated Circuit)是一種??同步、串行、半雙工??的通信協議,由飛利浦(現恩智浦)在1980年代設計,主要用于短距離板級設備間的通信。以下是其核心要點:
基本特性??
- ??兩根信號線??:
- ??SCL??(Serial Clock):時鐘線,由主設備控制。
- ??SDA??(Serial Data):雙向數據線。
- 這兩條數據線需要接上拉電阻(一般是 4.7kΩ),總線空閑的時候 SCL 和 SDA 處于高電平。
- ??多主多從架構??:支持多個主設備(需仲裁)和多個從設備(通過地址區分)。
- ??地址尋址??:每個從設備有唯一地址(7位或10位),主設備通過地址發起通信。
- ??速度模式??:
- 標準模式(100 kbps)
- 快速模式(400 kbps)
- 高速模式(3.4 Mbps)
?關鍵機制??
- ??時鐘同步??:多個主設備可通過SCL線“線與”同步時鐘。
- ??仲裁??:若兩個主設備同時發送,SDA數據沖突時,高電平設備退出。
- ??上拉電阻??:SCL和SDA需接上拉電阻(通常4.7kΩ),確保空閑時為高電平。
I2C 協議
起始位
I2C 通信起始標志,通過這個起始位就可以告訴 I2C 從機,主機要開始進行 I2C 通信了。
SCL為高電平時,SDA由高→低跳變,標志通信開始,如圖:
停止位
停止位就是停止 I2C 通信的標志位,和起始位的功能相反。
SCL為高電平時,SDA由低→高跳變,結束通信,如圖:
數據傳輸
I2C 總線在數據傳輸的時候要保證在 SCL 高電平期間, SDA 上的數據穩定,因此 SDA 上的數據變化只能在 SCL 低電平期間發生,如圖:
應答信號
當 I2C 主機發送完 8 位數據以后會將 SDA 設置為輸入狀態,等待 I2C 從機應答,也就是等到 I2C 從機告訴主機它接收到數據了。
從機通過將 SDA 拉低來表示發出應答信號,表示通信成功,否則表示通信失敗。
- ??ACK(確認)??:接收方(從機或主機)成功接收數據后,拉低SDA表示應答。
- ??NACK(非確認)??:接收方未成功接收(或無需繼續通信),保持SDA高電平。
??常見場景??:
- ??ACK??:從機地址匹配、數據接收成功。
- ??NACK??:從機地址不匹配、接收方忙、主機終止讀取(最后一次字節)。
I2C 寫時序
I2C 總線單字節寫時序如圖:
步驟??(以 7 位地址為例):
- ??START 信號??(SDA 由高→低,SCL 高電平)
- ??發送從機地址 + 寫位(0)??(7位地址 + 1位方向,共 8 位)
- ??從機應答 ACK??(SDA 被從機拉低)
- ??發送數據字節??(8 位)
- ??從機應答 ACK??
- ??重復 4~5(可選,連續寫入多字節)??
- ??STOP 信號??(SDA 由低→高,SCL 高電平)
I2C 讀時序
讀時序分為 4 大步:
- 發送設備地址,
- 發送要讀取的寄存器地址,
- 重新發送設備地址,
- I2C 從器件輸出要讀取的寄存器值
I2C 多字節讀寫時序:
多字節讀寫時序和單字節的基本一致,只是在讀寫數據的時候可以連續發送多個自己的數據,
I.MX6U I2C
簡介
I.MX6U 提供了 4 個 I2C 外設,通過這四個 I2C 外設即可完成與 I2C 從器件進行通信。
I.MX6U 的 I2C 支持兩種模式:標準模式和快速模式,標準模式下 I2C 數據傳輸速率最高是 100Kbits/s,在快速模式下數據傳輸速率最高為 400Kbits/s。
寄存器
我們接下來看一下 I2C 的幾個重要的寄存器:
- 地址寄存器I2Cx_IADR(x=1~4)
- 分頻寄存器I2Cx_IFDR
- 控制寄存器I2Cx_I2CR
- 狀態寄存器I2Cx_I2SR
- 數據寄存器I2Cx_I2DR
地址寄存器I2Cx_IADR(x=1~4)
寄存器 I2Cx_IADR 只有 ADR(bit7:1)位有效,用來保存 I2C 從設備地址數據。當我們要訪問某個 I2C 從設備的時候就需要將其設備地址寫入到 ADR 里面。
分頻寄存器I2Cx_IFDR
I2C_IFDR提供了一個可編程的預分頻器,用于配置時鐘以選擇比特率。寄存器不會通過軟件重置而重置。
I2C時鐘來自PERCLK_ROOT,后者從IPG_CLK_ROOT路由。I2C時鐘頻率可以通過使用以下公式輕松獲得:
寄存器 I2Cx_IFDR 也只有 IC(bit5:0)這個位,用來設置 I2C 的波特率:
I2C 的時鐘源可以選擇 IPG_CLK_ROOT=66MHz,通過設置 IC 位來設置波特率。 IC 位可選的設置如圖:
這就是IC 的所有可選值。如果I2C的時鐘源為66MHz,我們要設置I2C的波特率為100KHz,那么IC就可以設置為0X15,也就是 640 分頻。 66000000/640=103.125KHz≈100KHz。
控制寄存器I2Cx_I2CR
I2C_I2CR用于啟用I2C和I2C中斷。它還包含主從模式選擇位。
IEN(bit7): I2C 使能位,為 1 的時候使能 I2C,為 0 的時候關閉 I2C。
IIEN(bit6): I2C 中斷使能位,為 1 的時候使能 I2C 中斷,為 0 的時候關閉 I2C 中斷。
MSTA(bit5):主從模式選擇位,設置 IIC 工作在主模式還是從模式,為 1 的時候工作在主模式,為 0 的時候工作在從模式。
MTX(bit4):傳輸方向選擇位,用來設置是進行發送還是接收,為 0 的時候是接收,為 1 的時候是發送。
TXAK(bit3):傳輸應答位使能,為 0 的話發送 ACK 信號,為 1 的話發送 NO ACK 信號。
RSTA(bit2):重復開始信號,為 1 的話產生一個重新開始信號。
狀態寄存器I2Cx_I2SR
ICF(bit7):數據傳輸狀態位,為 0 的時候表示數據正在傳輸,為 1 的時候表示數據傳輸完成。
IAAS(bit6):當為 1 的時候表示 I2C 地址,也就是 I2Cx_IADR 寄存器中的地址是從設備地址。
IBB(bit5): I2C 總線忙標志位,當為 0 的時候表示 I2C 總線空閑,為 1 的時候表示 I2C 總線忙。
IAL(bit4):仲裁丟失位,為 1 的時候表示發生仲裁丟失。
SRW(bit2):從機讀寫狀態位,當 I2C 作為從機的時候使用,此位用來表明主機發送給從機的是讀還是寫命令。為 0 的時候表示主機要向從機寫數據,為 1 的時候表示主機要從從機讀取數據。
IIF(bit1): I2C 中斷掛起標志位,當為 1 的時候表示有中斷掛起,此位需要軟件清零。
RXAK(bit0): 應答信號標志位,為 0 的時候表示接收到 ACK 應答信號,為 1 的話表示檢測到 NO ACK 信號。
數據寄存器I2Cx_I2DR
寄存器 I2Cx_I2DR,是數據寄存器,此寄存器只有低 8 位有效,當要發送數據的時候將要發送的數據寫入到此寄存器,如果要接收數據的話直接讀取此寄存器即可得到接收到的數據。
AP3216C 簡介
AP3216C是一個三合一環境傳感器,支持環境光強度(ALS)、接近距離(PS)和紅外線強度(IR)這三個環境參數檢測。
I.MX6U-ALPHA 開發板上通過 I2C1 連接的該傳感器。
AP3216C 常被用于手機、平板、導航設備等,其內置的接近傳感器可以用于檢測是否有物體接近,比如手機上用來檢測耳朵是否接觸聽筒,如果檢測到的話就表示正在打電話,手機就會關閉手機屏幕以省電。也可以使用環境光傳感器檢測光照強度,可以實現自動背光亮度調節。
AP3216C 結構如圖:
AP3216 的設備地址為 0X1E,我們等會寫代碼發送的從機地址就填這個。
AP3216C 內部也有一些寄存器,通過這些寄存器我們可以配置 AP3216C 的工作模式,并且讀取相應的數據。
0X00 這個寄存器是模式控制寄存器,用來設置 AP3216C 的工作模式,先設置為 0X04,也就是先軟件復位一次 AP3216C,再設置為 0X03,也就是開啟 ALS+PS+IR。
0X0A~0X0F 這 6 個寄存器就是數據寄存器,保存著 ALS、 PS 和 IR 這三個傳感器獲取到的數據值。如果同時打開 ALS、PS 和 IR 則讀取間隔最少要 112.5ms(數據轉換間隔)。
硬件原理圖
本講實驗我們通過 I.MX6U 的 I2C1 來讀取 AP3216C 內部的 ALS、 PS 和 IR 這三個傳感器的值,然后使用串口打印出來。
配置步驟如下:
- 初始化相應的 IO:初始化 I2C1 相應的 IO,設置其復用功能,如果要使用 AP3216C 中斷功能的話,還需要設置 AP3216C 的中斷 IO。
- 初始化 I2C1:初始化 I2C1 接口,設置波特率。
- 初始化 AP3216C:初始化 AP3216C,讀取 AP3216C 的數據。
本試驗用到的資源如下:指示燈 LED0、AP3216C、串口。
AP3216C 是在 I.MX6U-ALPHA 開發板底板上,原理圖如圖:
AP3216C 使用的是 I2C1,其中 I2C1_SCL 使用的 UART4_TXD 這個IO、 I2C1_SDA 使用的是 UART4_R XD 這個 IO。
實驗程序編寫
在 bsp 文件夾下創建名為“i2c”和“ap3216c”的文件夾。
在 bsp/i2c 中新建 bsp_i2c.c 和 bsp_i2c.h 這兩個文件,是I.MX6U 的 I2C 文件.
在 bsp/ap3216c 中新建 bsp_ap3216c.c 和 bsp_ap3216c.h 這兩個文件,是 AP3216C 的驅動文件。
i2c.h
定義了一些I2C狀態相關的宏。
定義了一個枚舉類型i2c_direction,此枚舉類型用來表示 I2C 主機對從機的操作,也就是讀數據還是寫數據。
定義了一個結構體 i2c_transfer,此結構體用于 I2C 的數據傳輸。
#ifndef _BSP_I2C_H
#define _BSP_I2C_H#include "imx6ul.h"/* 相關宏定義 */
#define I2C_STATUS_OK (0)
#define I2C_STATUS_BUSY (1)
#define I2C_STATUS_IDLE (2)
#define I2C_STATUS_NAK (3)
#define I2C_STATUS_ARBITRATIONLOST (4)
#define I2C_STATUS_TIMEOUT (5)
#define I2C_STATUS_ADDRNAK (6)/** I2C方向枚舉類型*/
enum i2c_direction
{kI2C_Write = 0x0, /* 主機向從機寫數據 */kI2C_Read = 0x1, /* 主機從從機讀數據 */
} ;/** 主機傳輸結構體*/
struct i2c_transfer
{unsigned char slaveAddress; /* 7位從機地址 */enum i2c_direction direction; /* 傳輸方向 */unsigned int subaddress; /* 寄存器地址 */unsigned char subaddressSize; /* 寄存器地址長度 */unsigned char *volatile data; /* 數據緩沖區 */volatile unsigned int dataSize; /* 數據緩沖區長度 */
};/**函數聲明*/
void i2c_init(I2C_Type *base);
unsigned char i2c_master_start(I2C_Type *base, unsigned char address, enum i2c_direction direction);
unsigned char i2c_master_repeated_start(I2C_Type *base, unsigned char address, enum i2c_direction direction);
unsigned char i2c_check_and_clear_error(I2C_Type *base, unsigned int status);
unsigned char i2c_master_stop(I2C_Type *base);
void i2c_master_write(I2C_Type *base, const unsigned char *buf, unsigned int size);
void i2c_master_read(I2C_Type *base, unsigned char *buf, unsigned int size);
unsigned char i2c_master_transfer(I2C_Type *base, struct i2c_transfer *xfer);#endif
i2c.c
i2c.c中一共有8個函數:
- 函數i2c_init
- 函數i2c_master_repeated_start
- 函數 i2c_master_start
- 函數 i2c_check_and_clear_error
- 函數 i2c_master_stop
- 函數i2c_master_write?
- 函數 i2c_master_read
- 函數 i2c_master_transfer
函數i2c_init,此函數用來初始化 I2C,重點是設置 I2C 的波特率,初始化完成以后開啟 I2C。
/** @description : 初始化I2C,波特率100KHZ* @param - base : 要初始化的IIC設置* @return : 無*/
void i2c_init(I2C_Type *base)
{/* 1、配置I2C */base->I2CR &= ~(1 << 7); /* 要訪問I2C的寄存器,首先需要先關閉I2C *//* 設置波特率為100K* I2C的時鐘源來源于IPG_CLK_ROOT=66Mhz* IC2 時鐘 = PERCLK_ROOT/dividison(IFDR寄存器)* 設置寄存器IFDR,IFDR寄存器參考IMX6UL參考手冊P1260頁,表29-3,* 根據表29-3里面的值,挑選出一個還是的分頻數,比如本例程我們* 設置I2C的波特率為100K, 因此當分頻值=66000000/100000=660.* 在表29-3里面查找,沒有660這個值,但是有640,因此就用640,* 即寄存器IFDR的IC位設置為0X15*/base->IFDR = 0X15 << 0;/** 設置寄存器I2CR,開啟I2C* bit[7] : 1 使能I2C,I2CR寄存器其他位其作用之前,此位必須最先置1*/base->I2CR |= (1<<7);
}
函數i2c_master_repeated_start,發送一個重復開始信號,發送開始信號的時候也會順帶發送從設備地址。
/** @description : 發送重新開始信號* @param - base : 要使用的IIC* @param - addrss : 設備地址* @param - direction : 方向* @return : 0 正常 其他值 出錯*/
unsigned char i2c_master_repeated_start(I2C_Type *base, unsigned char address, enum i2c_direction direction)
{/* I2C忙并且工作在從模式,跳出 */if(base->I2SR & (1 << 5) && (((base->I2CR) & (1 << 5)) == 0)) return 1;/** 設置寄存器I2CR* bit[4]: 1 發送* bit[2]: 1 產生重新開始信號*/base->I2CR |= (1 << 4) | (1 << 2);/** 設置寄存器I2DR* bit[7:0] : 要發送的數據,這里寫入從設備地址* 參考資料:IMX6UL參考手冊P1249*/ base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);return 0;
}
函數 i2c_master_start,發送一個開始信號,發送開始信號的時候也順帶發送從設備地址。
/** @description : 發送開始信號* @param - base : 要使用的IIC* @param - addrss : 設備地址* @param - direction : 方向* @return : 0 正常 其他值 出錯*/
unsigned char i2c_master_start(I2C_Type *base, unsigned char address, enum i2c_direction direction)
{if(base->I2SR & (1 << 5)) /* I2C忙 */return 1;/** 設置寄存器I2CR* bit[5]: 1 主模式* bit[4]: 1 發送*/base->I2CR |= (1 << 5) | (1 << 4);/** 設置寄存器I2DR* bit[7:0] : 要發送的數據,這里寫入從設備地址* 參考資料:IMX6UL參考手冊P1249*/ base->I2DR = ((unsigned int)address << 1) | ((direction == kI2C_Read)? 1 : 0);return 0;
}
函數 i2c_check_and_clear_error,檢查并清除錯誤。
/** @description : 檢查并清除錯誤* @param - base : 要使用的IIC* @param - status : 狀態* @return : 狀態結果*/
unsigned char i2c_check_and_clear_error(I2C_Type *base, unsigned int status)
{/* 檢查是否發生仲裁丟失錯誤 */if(status & (1<<4)){base->I2SR &= ~(1<<4); /* 清除仲裁丟失錯誤位 */base->I2CR &= ~(1 << 7); /* 先關閉I2C */base->I2CR |= (1 << 7); /* 重新打開I2C */return I2C_STATUS_ARBITRATIONLOST;} else if(status & (1 << 0)) /* 沒有接收到從機的應答信號 */{return I2C_STATUS_NAK; /* 返回NAK(No acknowledge) */}return I2C_STATUS_OK;
}
函數 i2c_master_stop,產生一個停止信號。
/** @description : 停止信號* @param - base : 要使用的IIC* @param : 無* @return : 狀態結果*/
unsigned char i2c_master_stop(I2C_Type *base)
{unsigned short timeout = 0xffff;/** 清除I2CR的bit[5:3]這三位*/base->I2CR &= ~((1 << 5) | (1 << 4) | (1 << 3));/* 等待忙結束 */while((base->I2SR & (1 << 5))){timeout--;if(timeout == 0) /* 超時跳出 */return I2C_STATUS_TIMEOUT;}return I2C_STATUS_OK;
}
函數i2c_master_write ,向 I2C 從設備寫數據。
/** @description : 發送數據* @param - base : 要使用的IIC* @param - buf : 要發送的數據* @param - size : 要發送的數據大小* @param - flags : 標志* @return : 無*/
void i2c_master_write(I2C_Type *base, const unsigned char *buf, unsigned int size)
{/* 等待傳輸完成 */while(!(base->I2SR & (1 << 7))); base->I2SR &= ~(1 << 1); /* 清除標志位 */base->I2CR |= 1 << 4; /* 發送數據 */while(size--){base->I2DR = *buf++; /* 將buf中的數據寫入到I2DR寄存器 */while(!(base->I2SR & (1 << 1))); /* 等待傳輸完成 */ base->I2SR &= ~(1 << 1); /* 清除標志位 *//* 檢查ACK */if(i2c_check_and_clear_error(base, base->I2SR))break;}base->I2SR &= ~(1 << 1);i2c_master_stop(base); /* 發送停止信號 */
}
函數 i2c_master_read,從 I2C 從設備讀數據。
/** @description : 讀取數據* @param - base : 要使用的IIC* @param - buf : 讀取到數據* @param - size : 要讀取的數據大小* @return : 無*/
void i2c_master_read(I2C_Type *base, unsigned char *buf, unsigned int size)
{volatile uint8_t dummy = 0;dummy++; /* 防止編譯報錯 *//* 等待傳輸完成 */while(!(base->I2SR & (1 << 7))); base->I2SR &= ~(1 << 1); /* 清除中斷掛起位 */base->I2CR &= ~((1 << 4) | (1 << 3)); /* 接收數據 *//* 如果只接收一個字節數據的話發送NACK信號 */if(size == 1)base->I2CR |= (1 << 3);dummy = base->I2DR; /* 假讀 */while(size--){while(!(base->I2SR & (1 << 1))); /* 等待傳輸完成 */ base->I2SR &= ~(1 << 1); /* 清除標志位 */if(size == 0){i2c_master_stop(base); /* 發送停止信號 */}if(size == 1){base->I2CR |= (1 << 3);}*buf++ = base->I2DR;}
}
函數 i2c_master_transfer,此函數就是用戶最終調用的,用于完成 I2C通信的函數,此函數會使用前面的函數拼湊出 I2C 讀/寫時序。
/** @description : I2C數據傳輸,包括讀和寫* @param - base: 要使用的IIC* @param - xfer: 傳輸結構體* @return : 傳輸結果,0 成功,其他值 失敗;*/
unsigned char i2c_master_transfer(I2C_Type *base, struct i2c_transfer *xfer)
{unsigned char ret = 0;enum i2c_direction direction = xfer->direction; base->I2SR &= ~((1 << 1) | (1 << 4)); /* 清除標志位 *//* 等待傳輸完成 */while(!((base->I2SR >> 7) & 0X1)){}; /* 如果是讀的話,要先發送寄存器地址,所以要先將方向改為寫 */if ((xfer->subaddressSize > 0) && (xfer->direction == kI2C_Read)){direction = kI2C_Write;}ret = i2c_master_start(base, xfer->slaveAddress, direction); /* 發送開始信號 */if(ret){ return ret;}while(!(base->I2SR & (1 << 1))){}; /* 等待傳輸完成 */ret = i2c_check_and_clear_error(base, base->I2SR); /* 檢查是否出現傳輸錯誤 */if(ret){i2c_master_stop(base); /* 發送出錯,發送停止信號 */return ret;}/* 發送寄存器地址 */if(xfer->subaddressSize){do{base->I2SR &= ~(1 << 1); /* 清除標志位 */xfer->subaddressSize--; /* 地址長度減一 */base->I2DR = ((xfer->subaddress) >> (8 * xfer->subaddressSize)); //向I2DR寄存器寫入子地址while(!(base->I2SR & (1 << 1))); /* 等待傳輸完成 *//* 檢查是否有錯誤發生 */ret = i2c_check_and_clear_error(base, base->I2SR);if(ret){i2c_master_stop(base); /* 發送停止信號 */return ret;} } while ((xfer->subaddressSize > 0) && (ret == I2C_STATUS_OK));if(xfer->direction == kI2C_Read) /* 讀取數據 */{base->I2SR &= ~(1 << 1); /* 清除中斷掛起位 */i2c_master_repeated_start(base, xfer->slaveAddress, kI2C_Read); /* 發送重復開始信號和從機地址 */while(!(base->I2SR & (1 << 1))){};/* 等待傳輸完成 *//* 檢查是否有錯誤發生 */ret = i2c_check_and_clear_error(base, base->I2SR);if(ret){ret = I2C_STATUS_ADDRNAK;i2c_master_stop(base); /* 發送停止信號 */return ret; }}} /* 發送數據 */if ((xfer->direction == kI2C_Write) && (xfer->dataSize > 0)){i2c_master_write(base, xfer->data, xfer->dataSize);}/* 讀取數據 */if ((xfer->direction == kI2C_Read) && (xfer->dataSize > 0)){i2c_master_read(base, xfer->data, xfer->dataSize);}return 0;
}
ap3216c.h
我們使用前面編寫的 I2C 操作函數來配置 AP3216C ,配置完成以后就可以讀取 AP3216C 里面的傳感器數據。
ap3216c.h文件里定義了一些宏,分別為 AP3216C 的設備地址和寄存器地址。
#ifndef _BSP_AP3216C_H
#define _BSP_AP3216C_H#include "imx6ul.h"#define AP3216C_ADDR 0X1E /* AP3216C器件地址 *//* AP3316C寄存器 */
#define AP3216C_SYSTEMCONG 0x00 /* 配置寄存器 */
#define AP3216C_INTSTATUS 0X01 /* 中斷狀態寄存器 */
#define AP3216C_INTCLEAR 0X02 /* 中斷清除寄存器 */
#define AP3216C_IRDATALOW 0x0A /* IR數據低字節 */
#define AP3216C_IRDATAHIGH 0x0B /* IR數據高字節 */
#define AP3216C_ALSDATALOW 0x0C /* ALS數據低字節 */
#define AP3216C_ALSDATAHIGH 0X0D /* ALS數據高字節 */
#define AP3216C_PSDATALOW 0X0E /* PS數據低字節 */
#define AP3216C_PSDATAHIGH 0X0F /* PS數據高字節 *//* 函數聲明 */
unsigned char ap3216c_init(void);
unsigned char ap3216c_readonebyte(unsigned char addr,unsigned char reg);
unsigned char ap3216c_writeonebyte(unsigned char addr,unsigned char reg, unsigned char data);
void ap3216c_readdata(unsigned short *ir, unsigned short *ps, unsigned short *als);#endif
ap3216c.c
文件 bsp_ap3216c.c 里面共有 4 個函數,
- 函數?ap3216c_init
- 函數ap3216c_writeonebyte
- 函數ap3216c_readonebyte
- 函數 ap3216c_readdata
函數ap3216c_init,用于初始化 AP3216C。
此函數先初始化所使用到的 IO,比如初始化 I2C1 的相關 IO,并設置其復用為 I2C1。然后此函數會調用 i2c_init來初始化 I2C1,最后初始化 AP3216C。
/** @description : 初始化AP3216C* @param : 無* @return : 0 成功,其他值 錯誤代碼*/
unsigned char ap3216c_init(void)
{unsigned char data = 0;/* 1、IO初始化,配置I2C IO屬性 * I2C1_SCL -> UART4_TXD* I2C1_SDA -> UART4_RXD*/IOMUXC_SetPinMux(IOMUXC_UART4_TX_DATA_I2C1_SCL, 1);IOMUXC_SetPinMux(IOMUXC_UART4_RX_DATA_I2C1_SDA, 1);/* *bit 16:0 HYS關閉*bit [15:14]: 1 默認47K上拉*bit [13]: 1 pull功能*bit [12]: 1 pull/keeper使能 *bit [11]: 0 關閉開路輸出*bit [7:6]: 10 速度100Mhz*bit [5:3]: 110 驅動能力為R0/6*bit [0]: 1 高轉換率*/IOMUXC_SetPinConfig(IOMUXC_UART4_TX_DATA_I2C1_SCL, 0x70B0);IOMUXC_SetPinConfig(IOMUXC_UART4_RX_DATA_I2C1_SDA, 0X70B0);i2c_init(I2C1); /* 初始化I2C1 *//* 2、初始化AP3216C */ap3216c_writeonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG, 0X04); /* 復位AP3216C */delayms(50); /* AP33216C復位至少10ms */ap3216c_writeonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG, 0X03); /* 開啟ALS、PS+IR */data = ap3216c_readonebyte(AP3216C_ADDR, AP3216C_SYSTEMCONG); /* 讀取剛剛寫進去的0X03 */if(data == 0X03)return 0; /* AP3216C正常 */else return 1; /* AP3216C失敗 */
}
函數 ap3216c_writeonebyte ,向 AP3216C 寫入數據。
/** @description : 向AP3216C寫入數據* @param - addr: 設備地址* @param - reg : 要寫入的寄存器* @param - data: 要寫入的數據* @return : 操作結果*/
unsigned char ap3216c_writeonebyte(unsigned char addr,unsigned char reg, unsigned char data)
{unsigned char status=0;unsigned char writedata=data;struct i2c_transfer masterXfer;/* 配置I2C xfer結構體 */masterXfer.slaveAddress = addr; /* 設備地址 */masterXfer.direction = kI2C_Write; /* 寫入數據 */masterXfer.subaddress = reg; /* 要寫入的寄存器地址 */masterXfer.subaddressSize = 1; /* 地址長度一個字節 */masterXfer.data = &writedata; /* 要寫入的數據 */masterXfer.dataSize = 1; /* 寫入數據長度1個字節 */if(i2c_master_transfer(I2C1, &masterXfer))status=1;return status;
}
函數ap3216c_readonebyte,從 AP3216C 讀取數據。
/** @description : 從AP3216C讀取一個字節的數據* @param - addr: 設備地址* @param - reg : 要讀取的寄存器* @return : 讀取到的數據。*/
unsigned char ap3216c_readonebyte(unsigned char addr,unsigned char reg)
{unsigned char val=0;struct i2c_transfer masterXfer; masterXfer.slaveAddress = addr; /* 設備地址 */masterXfer.direction = kI2C_Read; /* 讀取數據 */masterXfer.subaddress = reg; /* 要讀取的寄存器地址 */masterXfer.subaddressSize = 1; /* 地址長度一個字節 */masterXfer.data = &val; /* 接收數據緩沖區 */masterXfer.dataSize = 1; /* 讀取數據長度1個字節 */i2c_master_transfer(I2C1, &masterXfer);return val;
}
函數 ap3216c_readdata,讀取 AP3216C 中的 ALS、 PS 和 IR 傳感器數據。
/** @description : 讀取AP3216C的數據,讀取原始數據,包括ALS,PS和IR, 注意!* : 如果同時打開ALS,IR+PS的話兩次數據讀取的時間間隔要大于112.5ms* @param - ir : ir數據* @param - ps : ps數據* @param - ps : als數據 * @return : 無。*/
void ap3216c_readdata(unsigned short *ir, unsigned short *ps, unsigned short *als)
{unsigned char buf[6];unsigned char i;/* 循環讀取所有傳感器數據 */for(i = 0; i < 6; i++) {buf[i] = ap3216c_readonebyte(AP3216C_ADDR, AP3216C_IRDATALOW + i); }if(buf[0] & 0X80) /* IR_OF位為1,則數據無效 */*ir = 0; else /* 讀取IR傳感器的數據 */*ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); *als = ((unsigned short)buf[3] << 8) | buf[2]; /* 讀取ALS傳感器的數據 */ if(buf[4] & 0x40) /* IR_OF位為1,則數據無效 */*ps = 0; else /* 讀取PS傳感器的數據 */*ps = ((unsigned short)(buf[5] & 0X3F) << 4) | (buf[4] & 0X0F);
}
main.c
首先調用 ap3216c_init 來初始化 AP3216C,如果 AP3216C 初始化失敗的話就會進入循環,會在 LCD 上不斷的閃爍字符串“AP3216C Check Failed!”和“Please Check!”,直到 AP3216C初始化成功。
在while(1)里循環調用函數 ap3216c_readdata 來獲取 AP3216C 的 ALS、 PS 和 IR 傳感器數據值,獲取完成以后就會在 LCD 上顯示出來。
#include "bsp_clk.h"
#include "bsp_delay.h"
#include "bsp_led.h"
#include "bsp_beep.h"
#include "bsp_key.h"
#include "bsp_int.h"
#include "bsp_uart.h"
#include "bsp_lcd.h"
#include "bsp_lcdapi.h"
#include "bsp_rtc.h"
#include "bsp_ap3216c.h"
#include "stdio.h"/** @description : main函數* @param : 無* @return : 無*/
int main(void)
{unsigned short ir, als, ps;unsigned char state = OFF;int_init(); /* 初始化中斷(一定要最先調用!) */imx6u_clkinit(); /* 初始化系統時鐘 */delay_init(); /* 初始化延時 */clk_enable(); /* 使能所有的時鐘 */led_init(); /* 初始化led */beep_init(); /* 初始化beep */uart_init(); /* 初始化串口,波特率115200 */lcd_init(); /* 初始化LCD */tftlcd_dev.forecolor = LCD_RED; lcd_show_string(30, 50, 200, 16, 16, (char*)"ALPHA-IMX6U IIC TEST"); lcd_show_string(30, 70, 200, 16, 16, (char*)"AP3216C TEST"); lcd_show_string(30, 90, 200, 16, 16, (char*)"ATOM@ALIENTEK"); lcd_show_string(30, 110, 200, 16, 16, (char*)"2019/3/26"); while(ap3216c_init()) /* 檢測不到AP3216C */{lcd_show_string(30, 130, 200, 16, 16, (char*)"AP3216C Check Failed!");delayms(500);lcd_show_string(30, 130, 200, 16, 16, (char*)"Please Check! ");delayms(500);} lcd_show_string(30, 130, 200, 16, 16, (char*)"AP3216C Ready!"); lcd_show_string(30, 160, 200, 16, 16, (char*)" IR:"); lcd_show_string(30, 180, 200, 16, 16, (char*)" PS:"); lcd_show_string(30, 200, 200, 16, 16, (char*)"ALS:"); tftlcd_dev.forecolor = LCD_BLUE; while(1) {ap3216c_readdata(&ir, &ps, &als); /* 讀取數據 */lcd_shownum(30 + 32, 160, ir, 5, 16); /* 顯示IR數據 */lcd_shownum(30 + 32, 180, ps, 5, 16); /* 顯示PS數據 */lcd_shownum(30 + 32, 200, als, 5, 16); /* 顯示ALS數據 */ delayms(120);state = !state;led_switch(LED0,state); }return 0;
}
使用 Make 命令編譯代碼,編譯成功以后使用軟件 imxdownload 將編譯完成的 ap3216c.bin文件下載到 SD 卡中,燒寫成功以后將 SD 卡插到開發板的 SD 卡槽中,然后復位開發板。
還記得我們說過,AP3216C是一個三合一環境傳感器,支持環境光強度(ALS)、接近距離(PS)和紅外線強度(IR)這三個環境參數檢測。實驗現象大概就是,光線改變,ALS的值隨之改變;用手靠近或者遠離AP3216C,PS的值隨之改變。