imx6ull-裸機學習實驗16——I2C 實驗

目錄

前言

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.7,總線空閑的時候 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 位地址為例):

  1. ??START 信號??(SDA 由高→低,SCL 高電平)
  2. ??發送從機地址 + 寫位(0)??(7位地址 + 1位方向,共 8 位)
  3. ??從機應答 ACK??(SDA 被從機拉低)
  4. ??發送數據字節??(8 位)
  5. ??從機應答 ACK??
  6. ??重復 4~5(可選,連續寫入多字節)??
  7. ??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 這三個傳感器的值,然后使用串口打印出來。

配置步驟如下:

  1. 初始化相應的 IO:初始化 I2C1 相應的 IO,設置其復用功能,如果要使用 AP3216C 中斷功能的話,還需要設置 AP3216C 的中斷 IO。
  2. 初始化 I2C1:初始化 I2C1 接口,設置波特率。
  3. 初始化 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的值隨之改變。

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

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

相關文章

【TCP/IP】5. IP 協議

5. IP 協議5. IP 協議5.1 概述5.2 IP 數據報格式5.3 無連接數據報傳輸5.3.1 首部校驗5.3.2 數據分片與重組5.4 IP 數據報選項5.4.1 選項格式5.4.2 選項類型5.5 IP 模塊的結構本章要點5. IP 協議 5.1 概述 IP 協議是 TCP/IP 協議簇的核心協議&#xff0c;位于網絡層&#xff0…

Linux 服務器挖礦病毒深度處理與防護指南

在 Linux 服務器運維中&#xff0c;挖礦病毒是常見且危害較大的安全威脅。此類病毒通常會隱蔽占用大量 CPU 資源進行加密貨幣挖礦&#xff0c;導致服務器性能驟降、能耗激增&#xff0c;甚至被黑客遠程控制。本文將從病毒特征識別、應急處理流程、深度防護措施三個維度&#xf…

MySQL數據表設計 系統的營銷功能 優惠券、客戶使用優惠券的設計

系統的營銷功能營銷功能概述&#xff1a;系統的營銷功能主要是&#xff1a;市場活動管理、營銷自動化、銷售線索管理以及數據分析和報告等。?ToC?&#xff08;Consumer&#xff09;&#xff1a;面向個人消費者&#xff0c;滿足日常消費需求。?優惠券的種類&#xff1a;ToC的…

讓 3 個線程串行的幾種方式

1、通過join()的方式 子線程調用join()的時候&#xff0c;主線程等待子線程執行完再執行。如果讓多個線程順序執行的話&#xff0c;那么需要他們按順序調用start()。/*** - 第一個迭代&#xff08;i0&#xff09;&#xff1a;* 啟動線程t1 -> 然后調用t1.join()。* …

在 Vue 項目中關閉 ESLint 規則

在 Vue 2 項目中關閉 ESLint 規則有以下幾種方法&#xff0c;根據您的需求選擇合適的方式&#xff1a; 1. 完全禁用 ESLint 修改 vue.config.js&#xff08;推薦&#xff09; module.exports {// 關閉 ESLintlintOnSave: false }或修改 package.json {"scripts": {&…

電腦息屏工具,一鍵黑屏超方便

軟件介紹 今天為大家推薦一款實用的PC端屏幕管理工具——CloseDsp。這款"息屏小能手"能一鍵關閉顯示器&#xff0c;解決各種場景下的屏幕管理需求。 核心功能 CloseDsp最突出的特點是能瞬間關閉顯示器屏幕。只需點擊"關閉顯示器"按鈕&#xff0c;屏幕…

嵌入式調試LOG日志輸出(以STM32為例)

引言在嵌入式系統開發中&#xff0c;調試是貫穿整個生命周期的關鍵環節。與傳統PC端程序不同&#xff0c;嵌入式設備資源受限&#xff08;如內存、存儲、處理器性能&#xff09;&#xff0c;且運行環境復雜&#xff08;無顯示器、鍵盤&#xff09;&#xff0c;傳統的斷點調試或…

Zephyr的設備驅動模型

默認配置默認配置 boards/arm/nucleo_f401re/ ├── nucleo_f401re.dts ← 板卡設備樹主入口 ├── nucleo_f401re_defconfig ← 默認 Kconfig 配置 ├── board.cmake ← CMake 構建入口overlay1.新增加驅動需要修改對應板的設備樹文件&#xf…

Mysql字段沒有索引,通過where x = 3 for update是使用什么級別的鎖

沒有索引時&#xff0c;FOR UPDATE 會鎖住整個表 現在&#xff0c;你正在一本一本地翻看所有書&#xff0c;尋找“維修中”的書&#xff0c;并且你對管理員說&#xff1a;“在我清點和修改完之前&#xff0c;別人不能動這些書&#xff0c;也不能往這個范圍里加新書&#xff01;…

TCP-與-UDP-協議詳解:原理、區別與應用場景全解析

TCP 與 UDP 協議詳解&#xff1a;原理、區別與應用場景全解析 在日常使用網絡的過程中&#xff0c;我們經常聽到 TCP 和 UDP 這兩個詞。你打開網頁、發送消息、觀看視頻&#xff0c;背后都在使用 TCP 或 UDP 進行數據傳輸。那么這兩個協議到底是怎么工作的&#xff1f;它們之間…

GitHub信息收集

目錄 簡介 一、入門搜索技巧 1. 基本關鍵詞搜索 2. 文件類型限定搜索 3. 用戶/組織定向搜索 二、精準定位技巧 1. 組合搜索條件 2. 排除干擾結果 3. 路徑限定搜索 三、防御建議 四、法律與道德提醒 簡介 GitHub作為全球最大的代碼托管平臺&#xff0c;存儲著數十億…

由 DB_FILES 參數導致的 dg 服務器無法同步問題

由 DB_FILES 參數導致的 dg 服務器無法同步問題 用戶反映&#xff0c;dg 服務器數據從昨晚&#xff08;7月8日&#xff09;開始停止同步。 連接服務器發現沒有 mrp 進程&#xff0c;并且 OPEN_MODE 參數也不正確。具體情況如下所示&#xff1a; SQL> select process, status…

Go語言泛型-泛型對代碼結構的優化

在Go語言中,Go泛型-泛型對代碼結構的優化部分主要探討了泛型如何幫助我們優化代碼結構、減少重復代碼,并提高代碼的可維護性、可讀性和復用性。以下是詳細內容: 一、引言 Go 1.18 引入了泛型,極大地提高了語言的靈活性。泛型使得我們可以編寫更加通用、可復用且類型安全的…

【1-快速上手】

文章目錄前言簡介什么是 Konva&#xff1f;安裝 Konva概述它是如何工作的&#xff1f;基本形狀樣式事件拖放濾鏡動畫選擇器序列化與反序列化性能前言 結合項目實際業務需求&#xff0c;在 Fabric、Konva 等圖形化框架中&#xff0c;我選擇了性能表現好的 Konva。首先去學習官方…

【LeetCode】209. 長度最小的子數組(前綴和 + 二分)

【LeetCode】209. 長度最小的子數組&#xff08;前綴和 二分&#xff09;題目描述前綴和二分優化前綴和總結二分總結題目描述 題目鏈接&#xff1a;【LeetCode】209. 長度最小的子數組&#xff08;前綴和 二分&#xff09; 給定一個含有 n 個整數的數組和一個整數 target。…

文件系統----底層架構

當我們談到文件系統的時候&#xff0c;最重要的點在于&#xff1a;文件的內容與屬性是如何存儲在磁盤中的&#xff1f;以及操作系統是如何精準定位到這些文件內容的&#xff1f;在談及文件的內核前&#xff0c;我們先來了解一下儲存文件的硬件-----硬盤一.理解硬件首先我們來看…

小程序開發平臺,自主開發小程序源碼系統,多端適配,帶完整的部署教程

溫馨提示&#xff1a;文末有資源獲取方式全開源與自主開發源碼完全開放&#xff1a;開發者可自由修改前端界面、后端邏輯及數據庫結構&#xff0c;支持深度定制&#xff08;如調整用戶端交互流程、商家端管理功能等&#xff09;。技術棧透明&#xff1a;基于主流技術&#xff0…

stp拓撲變化分類

Max Age 20sHellotime 2sForward delay 153、拓撲改變需要多長時間1&#xff09;根橋故障&#xff1a;需要50秒&#xff08;Max age2個forwarding delay&#xff09;2&#xff09;非直連鏈路&#xff1a;非直連故障在穩定的STP網絡&#xff0c;非根橋會定期收到來自根橋的BPDU報…

一、深度學習——神經網絡

一、神經網絡 1.神經網絡定義&#xff1a;人工神經網絡&#xff08;Artificial Neural Network&#xff0c;ANN&#xff09;也簡稱為神經網絡&#xff08;NN&#xff09;&#xff0c;是一種模仿生物神經網絡結構和功能的計算模型。人腦可以看作是一個生物神經網絡&#xff0c;由…

【牛客算法】 小紅的奇偶抽取

文章目錄 一、題目介紹1.1 題目描述1.2 輸入描述1.3 輸出描述1.4 示例二、解題思路2.1 核心算法設計2.2 性能優化關鍵2.3 算法流程圖三、解法實現3.1 解法一:字符串分離法3.1.1 初級版本分析3.2 解法二:數學逐位構建法(推薦)3.2.1 優化版本分析四、總結與拓展4.1 關鍵優化技…