STM32F10x_模拟I2C读写EEPROM(2)(切换SDA方向 + 读ACK位 + 完整代码)
                                                            生活随笔
收集整理的這篇文章主要介紹了
                                STM32F10x_模拟I2C读写EEPROM(2)(切换SDA方向 + 读ACK位 + 完整代码)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.                        
                                文章目錄
- 前言
 - 一、宏定義
 - 二、I2C延時函數
 - 1. 注意
 
- 三、起始 / 停止信號
 - 1. 時序圖
 - 2. 起始信號
 - 3. 停止信號
 
- 四、切換SDA方向
 - 1. SDA配置為輸入模式
 - 2. SDA配置為輸出模式
 
- 五、應答位信息
 - 1. 主機(MCU)讀取 應答位
 - 2. 主機(MCU)產生 應答位
 
- 六、I2C讀 / 寫 一個字節
 - 1. 注意
 - 2. I2C寫一個字節
 - 3. I2C讀一個字節
 
- 七、E2 頁寫 / 連續讀
 - 1. E2 頁寫
 - 2. E2 連續讀
 
- 八、小結
 
前言
-  
關于此文一些名詞術語不太理解的,可以去看我這篇博文
→ 《STM32F10x_模擬I2C讀寫EEPROM(1)》 -  
讀寫E2函數(帶備份區+校驗和判斷),可以去看我這篇博文
→ 《STM32F10x_模擬I2C讀寫EEPROM(3)(讀寫E2備份區 + 校驗位 + 完整代碼 + 應用實例)》 -  
E2的中文資料可以到我博客資源里下載,沒有積分下載的,可以評論Ding我o( ̄▽ ̄)ブ
 
一、宏定義
// I2C引腳#define PORT_I2C_SCL GPIOx#define PORT_I2C_SDA GPIOx#define PIN_I2C_SCL GPIO_Pin_x#define PIN_I2C_SDA GPIO_Pin_x// 控制 SDA / SCL 高低電平#define I2C_SCL_LOW (PORT_I2C_SCL->BRR = PIN_I2C_SCL)#define I2C_SCL_HIGH (PORT_I2C_SCL->BSRR = PIN_I2C_SCL)#define I2C_SDA_LOW (PORT_I2C_SDA->BRR = PIN_I2C_SDA)#define I2C_SDA_HIGH (PORT_I2C_SDA->BSRR = PIN_I2C_SDA)// 讀 SDA 電平狀態#define I2C_SDA_READ (PORT_I2C_SDA->IDR & PIN_I2C_SDA)// 應答位信息#define I2C_ACK 0 //應答#define I2C_NOACK 1 //非應答/*1、"地址長度"根據芯片型號不同略有不同8位: AT24C01、AT24C0216位: AT24C04、AT24C08、AT24C16、AT24C32、AT24C64、AT24C128、AT24C256、AT24C5122、"頁長度"根據芯片型號不同略有不同8字節: AT24C01、AT24C0216字節: AT24C04、AT24C08、AT24C1632字節: AT24C32、AT24C6464字節: AT24C128、AT24C256128字節: AT24C512*/// 此文的E2型號 - AT24C512#define EEPROM_WORD_ADDR_SIZE 16 //地址長度#define EEPROM_PAGE_SIZE 128 //頁長度#define EEPROM_DEV_ADDR 0xA0 //地址(設備地址:與A2、A1、A0有關)#define EEPROM_WR 0x00 //寫#define EEPROM_RD 0x01 //讀二、I2C延時函數
-  
1. 注意
- 此函數實現的是非標準延時,請根據MCU速度 調節大小 /************************************************ 函數名稱 : I2C_Delay 功 能 : I2C延時(非標準延時,請根據MCU速度 調節大小) 參 數 : 無 返 回 值 : 無 *************************************************/ static void I2C_Delay(void) {uint16_t cnt = 100;while(cnt--); }
 
 
三、起始 / 停止信號
-  
1. 時序圖
 -  
2. 起始信號
- 時鐘線 SCL 保持高電平期間 數據線 SDA 電平從高到低的跳變作為I2C總線的起始信號。 /************************************************ 函數名稱 : I2C_Start 功 能 : I2C開始 參 數 : 無 返 回 值 : 無 *************************************************/ void I2C_Start(void) {I2C_SCL_HIGH; //SCL高I2C_Delay();I2C_SDA_HIGH; //SDA高I2C_Delay();I2C_SDA_LOW; //SDA低I2C_Delay();I2C_SCL_LOW; //SCL低I2C_Delay(); }
 
 -  
3. 停止信號
- 時鐘線 SCL 保持高電平期間 數據線SDA 電平從低到高的跳變作為I2C總線的停止信號。 /************************************************ 函數名稱 : I2C_Stop 功 能 : I2C停止 參 數 : 無 返 回 值 : 無 *************************************************/ void I2C_Stop(void) {I2C_SDA_LOW; //SDA低I2C_Delay();I2C_SCL_HIGH; //SCL高I2C_Delay();I2C_SDA_HIGH; //SDA高I2C_Delay(); }
 
 
四、切換SDA方向
-  
1. SDA配置為輸入模式
/************************************************ 函數名稱 : I2C_SDA_SetInput 功 能 : I2C_SDA設置為輸入 參 數 : 無 返 回 值 : 無 *************************************************/ void I2C_SDA_SetInput(void) {GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = PIN_I2C_SDA;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // I2C_SDA設置為 浮空輸入GPIO_Init(PORT_I2C_SDA, &GPIO_InitStructure); } 
-  
2. SDA配置為輸出模式
/************************************************ 函數名稱 : I2C_SDA_SetOutput 功 能 : I2C_SDA設置為輸出 參 數 : 無 返 回 值 : 無 *************************************************/ void I2C_SDA_SetOutput(void) {GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Pin = PIN_I2C_SDA;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // I2C_SDA設置為 開漏輸出GPIO_Init(PORT_I2C_SDA, &GPIO_InitStructure); } 
五、應答位信息
-  
1. 主機(MCU)讀取 應答位
- 主機(MCU)寫數據的時候,從機(E2)產生應答,主機讀取應答位進行檢測;
/************************************************ 函數名稱 : I2C_GetAck 功 能 : I2C主機讀取應答(或非應答)位 參 數 : 無 返 回 值 : I2C_ACK ----- 應答I2C_NOACK --- 非應答 *************************************************/ uint8_t I2C_GetAck(void) {uint8_t ack;I2C_SCL_LOW; //SCL低(此時從機可變化SDA電平產生應答位信息)I2C_Delay();I2C_SDA_SetInput(); //SDA配置為輸入模式(切換SDA方向)I2C_SCL_HIGH; //SCL高(SCL上升沿時,從機發應答位信息到SDA線上)I2C_Delay();if(I2C_SDA_READ) //讀取從機應答位信息ack = I2C_NOACK; //非應答elseack = I2C_ACK; //應答I2C_SCL_LOW; //SCL低(防止誤操作起始 / 結束信號)I2C_Delay();I2C_SDA_SetOutput(); //SDA配置為輸出模式(切換SDA方向)return ack; //返回應答位 } 
 - 主機(MCU)寫數據的時候,從機(E2)產生應答,主機讀取應答位進行檢測;
 
-  
2. 主機(MCU)產生 應答位
-  
主機(MCU)讀數據的時候,主機產生應答,從機(E2)讀取應答位進行檢測;
/************************************************ 函數名稱 : I2C_PutAck 功 能 : I2C主機產生應答(或非應答)位 參 數 : I2C_ACK ----- 應答I2C_NOACK --- 非應答 返 回 值 : 無 *************************************************/ void I2C_PutAck(uint8_t Ack) {I2C_SCL_LOW; //SCL低(此時主機可變化SDA電平產生應答位信息)I2C_Delay();if(I2C_ACK == Ack)I2C_SDA_LOW; //主機產生 → 應答elseI2C_SDA_HIGH; //主機產生 → 非應答I2C_Delay();I2C_SCL_HIGH; //SCL高 (SCL上升沿時,主機發應答位信息到SDA線上)I2C_Delay();I2C_SCL_LOW; //SCL低(防止誤操作起始 / 結束信號)I2C_Delay(); }
 
 -  
 
六、I2C讀 / 寫 一個字節
-  
1. 注意
- I2C讀 / 寫一字節不是 E2 讀 / 寫一字節(需要區分開來)。
 
 
-  
2. I2C寫一個字節
-  
主機每寫完一個字節后,讀取從機返回的應答位:
- 應答位若為0,表示從機應答,能繼續下一步操作;
 - 應答位若為1,表示從機非應答,不能進行下一步操作。
 
 
 -  
 
-  
3. I2C讀一個字節
- 主機每讀到一個字節(8位)后,產生應答位給從機檢測: 
- 應答位若為0,表示主機應答,主機不繼續讀取數據了;
 - 應答位若為1,表示主機非應答,主機可繼續讀取數據。
 
 
 - 主機每讀到一個字節(8位)后,產生應答位給從機檢測: 
 
七、E2 頁寫 / 連續讀
-  
1. E2 頁寫
-  
一次頁寫操作寫入的數據字節數最大值為E2的頁大小;
 -  
不同型號的E2對應的頁大小可能不同(下面簡稱頁大小的值為 P ,舉例:AT24C512 → P = 128);
 -  
E2 頁寫時序說明:
- 舉例說明:MCU寫n(n <= P)個字節數據進E2;
 - ① 主器件發送起始信號和從器件地址信息( R/W 位置零 )給從器件,主器件檢測返回應答位信息;
 - ② 主器件發送起始字節地址,主器件檢測返回應答位信息;
 - ③ 非發送最后一個字節時,主器件每發送完一個字節后,不產生停止信號,主器件檢測應答位信息。循環此步驟③ n - 1次,按順序發送數據;
 - ④ 主器件發送最后一個字節后,主器件檢測應答位信息,然后主器件產生停止信號;
 - ⑤ 從器件接收到停止信號后,從器件開始內部數據的擦寫,在擦寫過程中,從器件不再應答主器件的任何請求。
 
 -  
注意:如果進行頁寫操作時,n > p,地址計數器將自動翻轉,先前寫入的數據會被覆蓋,所以要注意每次頁寫的字節數不能大于E2的頁大小P,否則會影響數據的正確性。
/************************************************ 函數名稱 : EEPROM_WritePage 功 能 : EEPROM寫頁 參 數 : Addr ------ 地址pData ----- 數據Length -----長度(<=EEPROM_PAGE_SIZE) 返 回 值 : I2C_ACK ----應答I2C_NOACK - 非應答 *************************************************/ uint8_t EEPROM_WritePage(uint16_t Addr, uint8_t *Data, uint16_t Length) {uint8_t ack;uint8_t i;/* 1.開始信號 */I2C_Start();/* 2.設備地址/寫 */ack = I2C_WriteByte(EEPROM_DEV_ADDR | EEPROM_WR);if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}/* 3.數據地址 */#if(8 == EEPROM_WORD_ADDR_SIZE)ack = I2C_WriteByte((uint8_t)(Addr&0x00FF)); //數據地址(8位) if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}#elseack = I2C_WriteByte((uint8_t)(Addr>>8)); //數據地址(16位)if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}ack = I2C_WriteByte((uint8_t)(Addr&0x00FF)); if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}#endif/* 4.寫一字節數據(循環) */for(i = 0; i < Length; i++){ack = I2C_WriteByte(*(Data + i));if(I2C_NOACK == ack){I2C_Stop();return I2C_NOACK;}}/* 5.停止信號 */I2C_Stop();return I2C_ACK; } 
 -  
 -  
2. E2 連續讀
- 從E2讀取數據是一個復合的I2C時序,它實際上包含一個寫過程和一個讀過程。
 - E2 連續讀時序說明: 
- 舉例說明:MCU連續讀E2里的n(n <= P)個字節數據;
 - ① 主器件發送起始信號和從器件地址信息( R/W 位置1 )給從器件,主器件檢測返回應答位信息;
 - ② 主器件發送數據起始字節地址,主器件檢測返回應答位信息;
 - ③ 主器件再次發送起始信號和從器件地址信息( R/W 位置0 )給從器件,主器件檢測返回應答位信息;
 - ③ E2會向主機返回從"數據起始字節地址"開始的數據,一個字節一個字節地傳輸,主器件每接收完一個字節后,不產生停止信號,主器件響應“應答信號ACK”給E2。只要主器件的響應為"應答信號ACK",E2就會一直傳輸下去,循環此步驟③ n - 1次;
 - ④ 主器件接收最后一個字節后,主器件響應“非應答信號NOACK”給E2,然后主器件產生停止信號;
 - ⑤ 從器件接收到停止信號后,從器件停止傳輸數據,E2 連續讀結束。
 
 
 
八、小結
- 寫的著急,歡迎糾正
 - ☆⌒(*^-゜)v THX!!
 - 碼字不易,記得點小心心 ( ?? ω ?? )?
 
總結
以上是生活随笔為你收集整理的STM32F10x_模拟I2C读写EEPROM(2)(切换SDA方向 + 读ACK位 + 完整代码)的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: APP Icon
 - 下一篇: HDU 5745 La Vie en r