首先由於之前並沒用過配置寄存器的方式實現主機模式的I2C通信,故想吃一次“螃蟹”。然後從網上找了一些資料做準備工作,初始化IO、配置I2C、實現讀寫函數,感覺還挺順手的。接着馬上下載到板子上開始調試,然後辨出現了意料之中的事:程序掛了。。。然後開始了各種懷疑。參考網上的例程和其他資料,好不靠譜啊。比對了好久,還是不能確定我寫的程序到底對不對。然後小小地糾結了一下,唉,還是用模擬I2C寫驅動吧,起碼以前還成功用過。
隨後一狠心將之前研究了半天的代碼(突然感覺好悲催)給刪除了 。好吧,重新整理下思路,調整下心情,開始模擬I2C。
配IO、找相關控制寄存器做上宏定義、寫函數(start、stop、read、write四個函數),測試函數寫完,帶着略有興奮的心情,又一次將程序下載到板子裏。這一次的結果可是出乎意料的:雖然程序沒掛,但是讀寫程序都報錯了。。。好失敗啊。。可是儘管這樣我還是比較相信我的代碼的(因爲之前這可是好用着呢)。於是去問度娘了,看看,有悲慘遭遇的小夥伴還不少(多少有點安慰)。
找了半天羅列了一些:地址不對?延時不對?然後一個一個嘗試,先找地址,手冊上是這樣描述的:
額,我的器件是PCF8574TS,沒有。。。 雖然原理圖上寫的是0x40,但還是謹慎一點爲好(看到網上好多罵奸商坑人的文章)。
可是。。。這兩種地址都做了嘗試,都不行啊。。都快崩潰了。週日晚上加班到八點半,沒搞定,灰溜溜地回去了。。
第二天,就是今天啦,虛心請教了大神同事。也是各種懷疑(我都快懷疑人生了),然後檢查唄,都準備亮出必殺技(量波形)了。唉,上午就這麼快過了,下午大神同事說,看看供電吧。趕緊拿個萬用表,看準原理圖比對元件,一戳,0V,額沒供電。。。。
突然感覺上邊那麼多話都成廢話了。。。
請教了硬件工程師之後,飛了根線,供電供上了。馬上下載程序(當時用了地址0x40),調試了一下,喲,進去了,成功了!哎呀,萬事開頭難,這下好了,可以正常一點了。接下來就簡單了,先貼兩個圖吧:
看了半天,感覺不對勁,①寫時序中沒有stop信號。。。②讀時序中stop信號之前有個1。。。
第一個問題又去請教大神同事(不恥下問嘛),說stop信號必須有。
第二個問題額,1不就是NACK麼(好蠢,還是經同事提醒想起來的)。
貼上I2C標準時序:
想通之後,重新修改整理代碼,調試通過,哎呀,完美了。。好了,貼代碼了。
i2c.h
文件
#ifndef _I2C_H_
#define _I2C_H_
#include "config.h"
#define I2C_NOT_LAST 0x00
#define I2C_LAST_BYTE 0x01
#define I2C_READ 0x01
#define I2C_WRITE 0x00
#define PCF8574TS 0x40
/*!
* 定義I2C1的IO訪問操作
* 配置I2C1引腳:SCL:PB6(92) SDA:PB7(93)
*/
#define I2C1_SDA_IDR (GPIOB_BASE+0x10 - PERIPH_BASE) //!<輸入數據寄存器(選擇B端口)
#define I2C1_SDA_ODR (GPIOB_BASE+0x14 - PERIPH_BASE) //!<輸出數據寄存器(選擇B端口)
#define I2C1_SCL_ODR (GPIOB_BASE+0x14 - PERIPH_BASE) //!<輸出數據寄存器(選擇B端口)
/*定義I2C1 SDA地址*/
#define I2C1_SDA_NUM 7
#define I2C1_SDA_OUA (PERIPH_BB_BASE + (I2C1_SDA_ODR * 32) + (I2C1_SDA_NUM * 4))
#define I2C1_SDA_ODA *(__IO uint32_t *)I2C1_SDA_OUA //!<數據寄存器輸出地址
#define I2C1_SDA_INA (PERIPH_BB_BASE + (I2C1_SDA_IDR * 32) + (I2C1_SDA_NUM * 4))
#define I2C1_SDA_IDA *(__IO uint32_t *)I2C1_SDA_INA //!<數據寄存器輸入地址(時鐘線不需要讀輸入寄存器數據)
#define I2C1_SDA_MCA *(__IO uint32_t *)(GPIOB_BASE+0) //!<控制寄存器
/*定義I2C1 SCL地址*/
#define I2C1_SCL_NUM 6
#define I2C1_SCL_OUA (PERIPH_BB_BASE + (I2C1_SCL_ODR * 32) + (I2C1_SCL_NUM * 4))
#define I2C1_SCL_ODA *(__IO uint32_t *)I2C1_SCL_OUA
#define I2C1_SCL_MCA *(__IO uint32_t *)(GPIOB_BASE+0) //!<控制寄存器
/*定義I2C1 SDA IO操作*/
#define I2C1_SDA_OUT() I2C1_SDA_MCA = (((I2C1_SDA_MCA) & 0xFFFF3FFF) | 0x00004000) //!<數據線配置爲輸出(PB7)
#define I2C1_SDA_H() I2C1_SDA_ODA = 1 //!<數據線拉高
#define I2C1_SDA_L() I2C1_SDA_ODA = 0 //!<數據線拉低
#define I2C1_SDA_IN() I2C1_SDA_MCA = (((I2C1_SDA_MCA) & 0xFFFF3FFF) | 0x00000000) //!<數據線配置爲輸入(PB7)
#define I2C1_SDA_READ() I2C1_SDA_IDA //!<讀取數據線上電平高低值
/*定義I2C1 SCL IO操作*/
#define I2C1_SCL_OUT() I2C1_SCL_MCA = (((I2C1_SCL_MCA) & 0xFFFFCFFF) | 0x00001000) //!<時鐘線配置爲輸出(PB6)
#define I2C1_SCL_H() I2C1_SCL_ODA = 1 //!<時鐘線拉高
#define I2C1_SCL_L() I2C1_SCL_ODA = 0 //!<時鐘線拉低
void I2CSysCtlDelay(unsigned long ulCount);
void I2C1_Start(void);
void I2C1_Stop(void);
uint8 I2C1_Receive_byte(uint8 flag);
uint8 I2C1_Send_byte(uint8 data);
#endif
i2c.c
文件
#include "i2c.h"
#define I2C_DELAY() I2CSysCtlDelay(30)
/*
* @brief SysCtlDelay
* @param ulCount 延時值,必須大於0
* @retval None
*/
void I2CSysCtlDelay(unsigned long ulCount)
{
__asm(" subs r0, #1\n"
" bne.n I2CSysCtlDelay\n"
" bx lr");
}
/*!
* @brief I2C1 GPIO初始化
* @param none
* @return NONE
* @note PB6-SCL,PB7-SDA
*/
void I2C1_IO_init(void)
{
GPIO_InitTypeDef GPIO_Init_s;
GPIO_Init_s.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 ;
GPIO_Init_s.GPIO_Mode = GPIO_Mode_OUT; //!<輸出模式
GPIO_Init_s.GPIO_OType = GPIO_OType_OD; //!<開漏
GPIO_Init_s.GPIO_Speed = GPIO_Speed_50MHz; //!<快速
GPIO_Init_s.GPIO_PuPd = GPIO_PuPd_UP;//GPIO_PuPd_NOPULL; //無上拉
GPIO_Init(GPIOB, &GPIO_Init_s);
}
/*!
* @brief I2C1起始信號
* @param none
* @return none
* @note 數據:D 時鐘:C 高:H 低:L 輸出:O 輸入:I 延時:_
* @note DOCO_DHCH_DL__CL
*/
void I2C1_Start(void)
{
I2C1_SDA_OUT();
I2C1_SCL_OUT();
I2C_DELAY();
I2C1_SDA_H();
I2C1_SCL_H();
I2C_DELAY();
I2C1_SDA_L();
I2C_DELAY();
I2C_DELAY();
I2C1_SCL_L();
}
/*!
* @brief I2C1結束信號
* @param none
* @return none
* @note 數據:D 時鐘:C 高:H 低:L 輸出:O 輸入:I 延時:_
* @note DOCO_DLCL_CH___DH__
*/
void I2C1_Stop(void)
{
I2C1_SDA_OUT();
I2C1_SCL_OUT();
I2C1_SDA_L();
I2C1_SCL_L();
I2C_DELAY();
I2C1_SCL_H();
I2C_DELAY();
I2C_DELAY();
I2C_DELAY();
I2C1_SDA_H();
I2C_DELAY();
I2C_DELAY();
}
/*!
* @brief 主機向I2C1總線發送一個字節
* @param data:數據
* @return 0:失敗 1:成功
* @note 數據:D 時鐘:C 高:H 低:L 輸出:O 輸入:I 延時:_ 讀取:R
* @note (_DH/L_CH__CL)*8_DHDI_CH_DR_CL_DO
*/
uint8 I2C1_Send_byte(uint8 data)
{
uint8 k;
for(k=0;k<8;k++){//!<發送8bit數據
I2C_DELAY();
if(data&0x80){
I2C1_SDA_H();
}else{
I2C1_SDA_L();
}
data=data<<1;
I2C_DELAY();
I2C1_SCL_H();
I2C_DELAY();
I2C_DELAY();
I2C1_SCL_L();
}
I2C_DELAY();//!<延時讀取ACK響應
I2C1_SDA_H();
I2C1_SDA_IN();//!<設爲輸入
I2C_DELAY();
I2C1_SCL_H();
I2C_DELAY();
k=I2C1_SDA_READ();//讀取ACK/NACK
I2C_DELAY();
I2C1_SCL_L();
I2C_DELAY();
I2C1_SDA_OUT();
if(k){ ////NACK響應
return 0;
}
return 1;
}
/*!
* @brief 主機從I2C1總線上接收一個字節
* @param flag:是否爲讀取的最後一個字節
* @return data:讀取的數值
* @note 數據:D 時鐘:C 高:H 低:L 輸出:O 輸入:I 延時:_ 讀取:R
* @note DI(_CH_DR_CL_)*8DODL_CH__CL_DO
*/
uint8 I2C1_Receive_byte(uint8 flag)
{
uint32 k,data;
I2C1_SDA_IN();//!<置爲輸入線
data=0;
for(k=0;k<8;k++){ //!<接收8bit數據
I2C_DELAY();
I2C1_SCL_H();
I2C_DELAY();
data=data|I2C1_SDA_READ();//!<讀數據
data=data<<1;
I2C_DELAY();
I2C1_SCL_L();
I2C_DELAY();
}
data=data>>1; //!<往回移動1次
I2C1_SDA_OUT(); //!<置爲輸出線
if(flag){
I2C1_SDA_H(); //!<拉高爲NACK
}else{
I2C1_SDA_L(); //!<拉低爲ACK
}
I2C_DELAY();
I2C1_SCL_H();
I2C_DELAY();
I2C_DELAY();
I2C1_SCL_L();
I2C_DELAY();
I2C1_SDA_OUT();
return (uint8)data; //!<返回讀取的數據
}
i2c_PCF8574TS.c 文件
#include "i2c.h"
/*!
* @brief 主機從I2C1總線上的PCF8574TS設備中讀取一個字節數據
* @param data:數據回傳指針
* @return 0:失敗 1:成功
* @note 獲取的數據爲8個IO口狀態
*/
uint8 i2c_EIO_RD(uint8* data)
{
uint8 state,ret;
I2C1_Start(); //!<啓動I2C總線
state = I2C1_Send_byte(PCF8574TS|I2C_READ); //!<發送器件地址,主機讀
if(state == 1){
*data = I2C1_Receive_byte(I2C_LAST_BYTE); //!<讀取數據
ret = 1;
}else{
ret = 0;
}
I2C1_Stop();
return ret;
}
/*!
* @brief 主機向I2C1總線上的PCF8574TS設備中寫入一個字節數據
* @param data:寫入的數據
* @return 0:失敗 1:成功
* @note none
*/
uint8 i2c_EIO_WD(uint8 data)
{
uint8 state,ret;
I2C1_Start(); //!<啓動I2C總線
state = I2C1_Send_byte(PCF8574TS|I2C_WRITE); //!<發送器件地址,主機寫
if(state==1){
state = I2C1_Send_byte(data);
if(state==1){
ret = 1;
}else{
ret = 0;
}
}else{
ret = 0;
}
I2C1_Stop();
return ret;
}
以上僅做記錄,望以後少走彎路!但總的來說,雖然很痛苦,可誰叫自己閱歷不豐富咧。