接下來進入其他兩種串行通信方式:SPI和I2C的學習,因為以后的項目中會用到這些通信方式,而且正點原子的開發板里面也有用I2C和SPI通信的傳感器來做實例,分別是一個距離傳感器和六軸陀螺儀,這樣就可以很好的通過實例來學習了。這兩個通信方式最大的區別就是速度,I2C的最高通信速度是400KHz,而SPI最高可以到幾百MHz,所以在低速應用時I2C即可,到了高速的場合就必須用SPI了。
先學習I2C的應用,這里還是和以前一樣,重點放在應用上,然后研究距離傳感器的datasheet來嘗試編寫傳感芯片的驅動!
I.MX6UL有4路I2C,正點原子寫的I2C驅動包含了一系列的函數,這些函數其實就是對I2C相關的寄存器做一些置1或者0的操作,所以我們可以學習下常用的對寄存器按位邏輯操作的方法:
- 使某一位置0,其他位不變
base->I2CR &= ~(1 << 7); //第7位置0,使用按位與
base->I2CR &= ~((1 << 5) | (1 << 4) | (1 << 3)); //[5:3]位同時清零
- 使某一位置1,其他位不變
base->I2CR |= ( 1 << 7); //第7位置1,按位或
base->I2CR |= (1 << 4) | (1 << 2); //同時把第4位和第1位置1
- 檢測某一位是否為0或者1
if(base->I2SR & (1 << 5)) == 1 //即I2SR的第五位是否為1,如果為1則邏輯與的結果是1
if((base->I2CR) & (1 << 5)) == 0) 即I2SR的第五位是否為0
這里不會深入研究正點原子的I2C驅動,只需要知道如何調用函數進行I2C數據的讀寫即可,把重點放在開發板上的傳感器芯片AP3216C上。研究它的datasheet先。
The AP3216C is an integrated ALS & PS module that includes a digital ambient light sensor [ALS], a proximity sensor [PS], and an IR LED in a single package.
所以它包含了3個模塊,光傳感器ALS
、距離傳感器PS
和紅外線LEDIR
,最常用在手機和平板上用來檢測耳朵是否接觸聽筒,或者光傳感器來檢測光照強度調節屏幕亮度。
在datasheet里查到這個芯片的地址是0x1E,再研究datasheet第12頁的表格,傳感器的設置、數據讀取和寫入都是從不同的寄存器地址,所以我們先用一些宏來把寄存器地址在頭文件中定義好,方便后面調用。宏的名字一定要一眼就看出意思來
#define AP3216C_ADDR 0X1E /* AP3216C器件地址 */#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數據高字節 */
先編寫讀寫數據的函數ap3216c_write(...)
,其實就是配置i2c_transfer結構體的各個參數,這個函數的參數是設備地址、要寫入的寄存器、寫入的數據,返回值可以擴展,可以是0-4,對應未寫入(0),I2C14寫入(返回值14)
unsigned char ap3216c_write(unsigned char addr, unsigned char reg, unsigned char data)
{unsigned char status = 0; unsigned char writedata = data;struct i2c_transfer masterXfer; /* 定義masterXfer結構體并配置 */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_read(...)
,參數是設備地址、要讀的寄存器、返回值就是讀取的數據。
unsigned char ap3216c_read(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;
}
根據讀寫的基礎函數,我們可以編寫讀寫IR,PS,ALS幾個模塊數據的函數ap3216c_readdata(...)
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_read(AP3216C_ADDR, AP3216C_IRDATALOW + i); /* 從0X0A到0X0F */}if(buf[0] & 0X80) /* IR_OF位為1,則數據無效 */*ir = 0; else /* 讀取IR傳感器的數據 */*ir = ((unsigned short)buf[1] << 2) | (buf[0] & 0X03); /* 只保留buf[0]即IR低字節,并把高字節左移兩位,獲取IR完整數據 */ *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);
}
至此AP3216C這個芯片的驅動就基本完成了。開發板的硬件上是通過I.MX6UL的I2C1通道和傳感器連接,所以一開始需要初始化I2C1,這里就不贅述。
在main.c里的while(1)循環中讀取這3個模塊的數據并通過串口打印出來:
while(1)
{ap3216c_readdata(&ir, &ps, &als);printf("\r\n Data of IR, PS, ALS is: %d, %d, %d\r\n\r\n", ir, ps, als);delayms(200);
}
Makefile后燒寫,在測試時用手去靠近開發板上的芯片,觀察輸出結果,也可以用光照,看看輸出結果是否和預想一致。
I2C的學習到此結束,接下去進入SPI外設的學習。
未完待續