STM32之I/O模擬IIC通訊協議-----學習篇


先說說廢話,第一篇博客文章,文中有不足之處或者有錯誤的地方還望指正。

IIC 總線(AT24C02)

一、先了解了解IIC總線協議基本知識

1.IIC協議是二線制,信號線包含SDA和SCL,且信號線是雙向的,開路結構,需要通過上拉電阻到VCC,具體的電阻值影響的是信號反應速度和驅動能力。

2.IC傳輸時,要保持SCL為高電平不變,SDA保持穩定;IIC開始的條件:SCL保持高電平,SDA從高電平跳躍到低電平,IIC停止通訊的條件:SCL保持高電平,SDA從低電平跳躍到高電平。

3.串行的8位雙向數據傳輸位速率在標准模式下可達100kbit/s,快速模式下可達400kbit/s,高速模式下可達3.4Mbit/s(一般不用考慮)。

單片機一般最高使用到400KBit/s(具體查看數據手冊可知),可知IIC的時鍾周期可以設置為:2.5us - 10us。


二、以下是此次學習IIC通訊協議,調試過程中一些要點

1.只有當SCL為高電平時,SDA線上的數據才有效,且在SCL電平升高前,SDA電平要保持穩定。

2.SCL,SDA為高電平時,總線為空閑狀態。

3.在主機讀數據前,應把SDA拉高,釋放SDA,交由從機控制。

4.要注意每種功能實現時總線的時序性(比如:start -> write - ack)。

5.要注意寫數據周期不能超過write cycle time(byte or page),查看數據手冊可知。

6.在寫完數據后讀取數據時,需做延時,否則會讀取不到數據(具體時間和設置時鍾周期有關)。

7.在接收ACK時,應先釋放SDA,在SCL為高電平時接收(與第3點一樣)。

三、調試過程:先看數據手冊時序圖

1.啟動、停止IIC

啟動過程:拉高SCL,SDA  (延時)-->拉低SDA(延時) -->拉低SCL(延時),最后拉低SCL是為了控制總線為忙狀態

停止過程:拉高SDA(延時) -->拉高SCL(延時) -->拉高SDA(延時)

void IIC_START(void)
{
    SDA_H;
    SCL_H;
    delay_us(5);
    SDA_L;
    delay_us(5);
    SCL_L;     //控制總線為忙狀態
    delay_us(5);
}
void IIC_STOP(void)
{
//    SCL_L;//
    SDA_L;
    delay_us(5);
    SCL_H;
    delay_us(5);
    SDA_H;
    delay_us(5);
}

2.寫數據、讀數據

這里需要特別注意,讀數據時,需先將SDA拉高,釋放控制權,交由從機控制。

寫數據:先將數據准備好(SDA拉高、或拉低)-->拉高SCL(延時)(此時從機讀取SDA的電平) -->拉低SCL(延時)(等待下一位數據,即DATA CHANG)

讀數據:拉高SDA(延時) -->拉高SCL(延時) -->讀取數據 -->拉低SCL(延時)(等待下一位數據,即DATA CHANG)

void IIC_Write_Byte(u8 ucdata)
{
    u8 i = 8;
//    SCL_L;//  
    while(i--) {
        if(ucdata & 0x80) {  // 1.先准備數據,從機在SCL為高電平時讀入數據
            SDA_H;
        }
        else {
            SDA_L;
        }
        delay_us(5);
        SCL_H;              // 2.拉高SCL,從機讀取穩定數據
        delay_us(5);
        SCL_L;              // 3.拉低SCL,等待下一位數據
        ucdata <<= 1;
    }
}
u8 IIC_Read_Byte(void)
{
    u8 i = 8,
       ucdata = 0x00;
//    SCL_L;// 
    SDA_H;             // 1.先拉高SDA,釋放SDA總線
    delay_us(5);
    while(i--) {
        SCL_H;         // 2.再拉高SCL,從機在SCL為高電平時輸出穩定的數據
        delay_us(5);
        ucdata <<= 1;
        if(Read_SDA) {  //讀數據
            ucdata = ucdata | 0x01;
        }
        SCL_L;        // 3.拉低SCL,等待下一位數據
        delay_us(5);
    }
    return ucdata;
}

3.獲取應答、回復應答

在讀數據或寫數據,在第9個時鍾周期回復應答或獲取應答,即讀取8bit數據或寫8bit數據后應答。其實這里與讀數據,寫數據是一樣的,只是這個過程只有一個周期。

回復應答(寫數據),獲取應答(讀數據)。

u8 IIC_Get_ACK(void)
{
    u8 wait_tiem,ack;
    ack = 0;
    SDA_H;           // 1.先拉高SDA,釋放SDA控制,交給從機控制
    delay_us(5);
    SCL_H;           // 2.再拉高SCL,從機輸出穩定數據
    delay_us(5);
    while(Read_SDA) { //主機讀取數據
        wait_tiem++;
        if(wait_tiem > 10) {
            ack = 1;
            break;
        }
    }
    delay_us(5);
    SCL_L;         // 3.拉低SCL,完成一個時鍾周期
    return ack;
}
void IIC_Set_ACK(u8 ack)
{
    if(ack) {
        SDA_H;   // 1.先准備數據
    }
    else {
        SDA_L;
    }  
//    SCL_L;
    delay_us(5);
    SCL_H;       // 2.拉高SCL,從機讀取穩定的數據
    delay_us(5);
    SCL_L;       // 3.拉低SCL,完成一個時鍾周期
}

IIC總線協議底層程序完成,接下來是寫芯片應用程序,讀寫過程一定要嚴格按照芯片數據手冊的時序。

4.應用(AC24C02)

①.首先要知道設備地址,數據存儲地址(個人理解)。AT24C02芯片是一個2K 大小的EEPROM存儲芯片,2 * 1024 = 2048 字節,總共256個數據。

設備地址由固定的“1010” + 三位硬件選擇位(與硬件連接有關) + 一位讀/寫命令位;

數據存儲地址:0 - 255(0x00 - 0xFF)。

②寫數據,可以單次寫8位(字節寫),或多次寫8位(頁寫);1K/2K的EEPROM的芯片以8位數據(即一個字節)為一頁。

byte write:啟動 --> 寫設備地址+寫命令 -->獲取應答(ack) -->寫位置地址 -->獲取應答 -->寫數據 -->獲取應答 --> 停止

page write:與byte write 過程一樣,寫完一頁數據獲取到從機應答后繼續寫數據,直到寫完最后一個數據獲取到應答后發送停止。

/***************************
**函數名稱:IIC_Write_Data
**參   數:DeviceAddr:設備地址        
            WordAddr:數據存儲地址
            *Data   :需要寫的數據
            num     :寫個數    
**作   用:隨機寫數據,返回值為1:寫數據成功;返回值為0:寫數據失敗  
**日   期:2020.2.17
**作   者:Yangyang
****************************/
u8 IIC_Write_Data(u8 DeviceAddr,u16 WordAddr,u8 *Data,u8 num)
{
    u8 AddrTemp,
       i;
    IIC_START();                       //啟動
    IIC_Write_Byte(DeviceAddr & 0xfe); //設備地址 + 寫命令
    if(IIC_Get_ACK()) {
        IIC_STOP();
        return 0;
    }
    AddrTemp = (WordAddr & 0xff);   //存儲數據地址
    IIC_Write_Byte(AddrTemp);
    if(IIC_Get_ACK()) {
        IIC_STOP();
        return 0;
    }
    for(i = 0; i < num; i++) {
        IIC_Write_Byte(*(Data + i));//寫數據
        if(IIC_Get_ACK()) {
            IIC_STOP();
            return 0;
        }
    }
    IIC_STOP();
    delay_ms(3);//最少需延時2ms,在寫完數據后讀數據,不延時會讀取數據失敗(也可以函數外部延時)
    return 1;
}

③讀數據,讀數據可分為當前地址讀數據、隨機地址讀數據和順序地址讀數據(此處就不說當前地址讀數據,基本不用)

隨機讀數據:啟動 --> 先寫設備地址 + 寫命令 -->獲取應答 -->  寫讀取數據的位置 -->獲取應答 --> 再啟動 --> 寫設備地址 + 讀命令 --> 獲取應答 --> 讀取數據 --> 回復不應答 --> 停止

順序讀數據:讀取方式與隨機讀數據相似,在讀取數據后是回復應答,當讀到最后一個數據后再回復不應答:....讀取數據n-1 --> 回復應答 -->讀取數據n --> 回復不應答 --> 停止

/***************************
**函數名稱:IIC_Read_Data
**參   數:DeviceAddr: 設備地址    
            WordAddr: 數據存儲地址
            *Data   : 讀取的數據
            num     : 讀取個數
**作   用:隨機讀取數據,順序讀取數據,返回值為1:讀取數據成功;返回值為0:讀取數據失敗
**日   期:2020.2.17
**作   者:Yangyang
****************************/
u8 IIC_Read_Data(u8 DeviceAddr,u16 WordAddr,u8 *Data,u8 num)
{
    u8 AddrTemp,
       i;
    IIC_START();                      //啟動
    IIC_Write_Byte(DeviceAddr & 0xfe);    //設備地址 + 寫命令
    if(IIC_Get_ACK()) {                    
         IIC_STOP();                       //Internally organized with 32 pages of 8 byteseach,
         return 0;                         //the 2K requires an 8-bit data word address for random word addressing.
    }                                       //此說明在數據手冊可知
    AddrTemp = (WordAddr & 0xff);    //地址可取0 - 255
    IIC_Write_Byte(AddrTemp);
    if(IIC_Get_ACK()) {
        IIC_STOP();
        return 0;
    }
    delay_us(100);
    IIC_START();                          //再啟動
    IIC_Write_Byte(DeviceAddr | 0x01);   //設備地址 + 讀命令
    if(IIC_Get_ACK()) {                  //
        IIC_STOP();
        return 0;
    }
    for(i = 0; i < num; i++) {
        *(Data + i) = IIC_Read_Byte();  //讀數據
        if(i < num - 1) {
            IIC_Set_ACK(0);            //接收N-1個數據后應該從機
        }
        else {                         //接收最后一個數據時不應答從機
            IIC_Set_ACK(1);
        }
    }
    IIC_STOP();                       //停止
    return 1;
}

④測試

使用硬件:STM32F103 + AT24C02

#define at24c02_byte_num 8
u8 IIC_ref_data[at24c02_byte_num] = {0};
u8 IIC_ref_data2[at24c02_byte_num] = {0};
u8 IIC_write_data[at24c02_byte_num] = {0x20,0x20,0x02,0x29,0x21,0x51,0x55,0x55}

u8 Test_IIC(void)
{
    u8 i = 0;
    IIC_Write_Data(0xa0,0,IIC_write_data,at24c02_byte_num);
//    delay_ms(3);
    i = IIC_Read_Data(0xa0,0,IIC_ref_data2,at24c02_byte_num);
    return i;
}

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM