天天看點

STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)

一、環境介紹

程式設計軟體: keil5

作業系統: win10

MCU型号: STM32F103ZET6

STM32程式設計方式: 寄存器開發 (友善程式移植到其他單片機)

IIC總線:  STM32本身支援IIC硬體時序的,本文采用的是模拟時序,下篇文章就介紹配置STM32的IIC硬體時序讀寫AT24C02和AT24C08。

模拟時序更加友善移植到其他單片機,通用性更高,不分MCU;硬體時序效率更高,單每個MCU配置方法不同,依賴硬體本身支援。

目前器件: 采用AT24C02  EEPROM存儲晶片

完整的工程源碼下載下傳位址,下載下傳即可編譯運作測試(包含了模拟IIC時序、STM32硬體IIC時序分别驅動AT24C02和AT24C08):  

https://download.csdn.net/download/xiaolong1126626497/19399945

二、AT24C02存儲晶片介紹

2.1 晶片功能特性介紹

AT24C02 是串行CMOS類型的EEPROM存儲晶片,AT24C0x這個系列包含了AT24C01、AT24C02、AT24C04、AT24C08、AT24C16這些具體的晶片型号。

他們容量分别是:1K (128 x 8)、2K (256 x 8)、4K (512 x 8)、8K (1024 x 8)、16K (2048 x 8)  ,其中的8表示8位(bit)

它們的管腳功能、封裝特點如下:

STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)
STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)

晶片功能描述:

AT24C02系列支援I2C,總線資料傳送協定I2C,總線協定規定任何将資料傳送到總線的器件作為發送器。任何從總線接收資料的器件為接收器;資料傳送是由産生串行時鐘和所有起始停止信号的主器件控制的。主器件和從器件都可以作為發送器或接收器,但由主器件控制傳送資料(發送或接收)的模式,由于A0、A1和A2可以組成000~111八種情況,即通過器件位址輸入端A0、A1和A2可以實作将最多8個AT24C02器件連接配接到總線上,通過進行不同的配置進行選擇器件。

晶片特性介紹:

1. 低壓和标準電壓運作

         –2.7(VCC=2.7伏至5.5伏)

         –1.8(VCC=1.8伏至5.5伏)

2. 兩線串行接口(SDA、SCL)

3. 有用于硬體資料保護的寫保護引腳

4. 自定時寫入周期(5毫秒~10毫秒),因為内部有頁緩沖區,向AT24C0x寫入資料之後,還需要等待AT24C0x将緩沖區資料寫入到内部EEPROM區域.

5. 資料儲存可達100年

6. 100萬次擦寫周期

7. 高資料傳送速率為400KHz、低速100KHZ和IIC總線相容。 100 kHz(1.8V)和400 kHz(2.7V、5V)

8. 8位元組頁寫緩沖區

      這個緩沖區大小與晶片具體型号有關: 8位元組頁(1K、2K)、16位元組頁(4K、8K、16K)

2.2 晶片裝置位址介紹

STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)

IIC裝置的标準位址位是7位。上面這個圖裡AT24C02的1010是晶片内部固定值,A2 、A1、 A0是硬體引腳、由硬體決定電平;最後一位是讀/寫位(1是讀,0是寫),讀寫位不算在位址位裡,但是根據IIC的時序順序,在操作裝置前,都需要先發送7位位址,再發送1位讀寫位,才能啟動對晶片的操作,我們在寫模拟時序為了友善統一寫for循環,按位元組發送,是以一般都是将7位址位與1位讀寫位拼在一起,組合成1個位元組,友善按位元組傳輸資料。

我現在使用的開發闆上AT24C02的原理圖是這樣的:

STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)

那麼這個AT24C02的标準裝置位址就是: 0x50(十六進制),對應的二進制就是: 1010000

如果将讀寫位組合在一起,讀權限的裝置位址: 0xA1 (10100001)  、寫權限的裝置位址: 0xA0 (10100000)

2.3  對AT24C02 按位元組寫資料的指令流程(時序)

STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)

    詳細解釋:

1.  先發送起始信号

2.  發送裝置位址(寫權限)

3. 等待AT24C02應答、低電平有效

4. 發送存儲位址、AT24C02内部一共有256個位元組空間,尋址是從0開始的,範圍是(0~255);發送這個存儲器位址就是告訴AT24C02接下來的資料改存儲到哪個地方。

5. 等待AT24C02應答、低電平有效

6. 發送一個位元組的資料,這個資料就是想存儲到AT24C02裡儲存的資料。

7. 等待AT24C02應答、低電平有效

8. 發送停止信号

2.3  對AT24C02 按頁寫資料的指令流程(時序)

STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)

詳細解釋:

6. 可以循環發送8個位元組的資料,這些資料就是想存儲到AT24C02裡儲存的資料。

   AT24C02的頁緩沖區是8個位元組,所有這裡的循環最多也隻能發送8個位元組,多發送的位元組會将前面的覆寫掉。

  需要注意的地方:  這個頁緩沖區的尋址也是從0開始,比如:  0~7算第1頁,8~15算第2頁......依次類推。 如果現在寫資料的起始位址是3,那麼這一頁隻剩下5個位元組可以寫;并不是說從哪裡都可以循環寫8個位元組。

     詳細流程: 這裡程式裡一般使用for循環實作

    (1).  發送位元組1

    (2). 等待AT24C02應答,低電平有效

    (3). 發送位元組2

    (4). 等待AT24C02應答,低電平有效

    .........

    最多8次.  

2.4  從AT24C02任意位址讀任意位元組資料(時序)

STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)

AT24C02支援目前位址讀、任意位址讀,最常用的還是任意位址讀,因為可以指定讀取資料的位址,比較靈活,上面這個指定時序圖就是任意位址讀。

詳細解釋:

4. 發送存儲位址、AT24C02内部一共有256個位元組空間,尋址是從0開始的,範圍是(0~255);發送這個存儲器位址就是告訴AT24C02接下來應該傳回那個位址的資料給單片機。

6.  重新發送起始信号(切換讀寫模式)

7. 發送裝置位址(讀權限)

8.  等待AT24C02應答、低電平有效

9. 循環讀取資料:  接收AT24C02傳回的資料.

  讀資料沒有位元組限制,可以第1個位元組、也可以連續将整個晶片讀完。

10. 發送非應答(高電平有效)

11. 發送停止信号

三、IIC總線介紹

      2.1 IIC總線簡介

I2C(Inter-Integrated Circuit)總線是由PHILIPS公司開發的兩線式串行總線,用于連接配接微控制器及其外圍裝置,是微電子通信控制領域廣泛采用的一種總線标準。具有接口線少,控制方式簡單,器件封裝形式小,通信速率較高等優點。

I2C規程運用主/從雙向通訊。器件發送資料到總線上,則定義為發送器,器件接收資料則定義為接收器。主器件和從器件都可以工作于接收和發送狀态。

I2C 總線通過串行資料(SDA)線和串行時鐘(SCL)線在連接配接到總線的器件間傳遞資訊。每個器件都有一個唯一的位址識别,而且都可以作為一個發送器或接收器(由器件的功能決定)。

I2C有四種工作模式:

      1.主機發送

      2.主機接收

      3.從機發送

      4.從機接收

I2C總線隻用兩根線:串行資料SDA(Serial Data)、串行時鐘SCL(Serial Clock)。

總線必須由主機(通常為微控制器)控制,主機産生串行時鐘(SCL)控制總線的傳輸方向,并産生起始和停止條件。

SDA線上的資料狀态僅在SCL為低電平的期間才能改變。

2.2 IIC總線上的裝置連接配接圖

STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)

I2C 總線在實體連接配接上非常簡單,分别由SDA(串行資料線)和SCL(串行時鐘線)及上拉電阻組成。通信原理是通過對SCL和SDA線高低電平時序的控制,來産生I2C總線協定所需要的信号進行資料的傳遞。在總線空閑狀态時,這兩根線一般被上面所接的上拉電阻拉高,保持着高電平。

其中上拉電阻範圍是4.7K~100K。

2.3 I2C總線特征

I2C總線上的每一個裝置都可以作為主裝置或者從裝置,而且每一個從裝置都會對應一個唯一的位址(可以從I2C器件的資料手冊得知)。主從裝置之間就通過這個位址來确定與哪個器件進行通信,在通常的應用中,我們把CPU帶I2C總線接口的子產品作為主裝置,把挂接在總線上的其他裝置都作為從裝置。

1.    總線上能挂接的器件數量

    I2C總線上可挂接的裝置數量受總線的最大電容400pF 限制,如果所挂接的是相同型号的器件,則還受器件位址的限制。

   一般I2C裝置位址是7位位址(也有10位),位址分成兩部分:晶片固化位址(生産晶片時候哪些接地,哪些接電源,已經固定),可程式設計位址(引出IO口,由硬體裝置決定)。

    例如: 某一個器件是7 位位址,其中10101 xxx  高4位出廠時候固定了,低3位可以由設計者決定。

則一條I2C總線上隻能挂該種器件最少8個。

如果7位位址都可以程式設計,那理論上就可以達到128個器件,但實際中不會挂載這麼多。

2.    總線速度傳輸速度:

I2C總線資料傳輸速率在标準模式下可達100kbit/s,快速模式下可達400kbit/s,高速模式下可達3.4Mbit/s。一般通過I2C總線接口可程式設計時鐘來實作傳輸速率的調整。

3.    總線資料長度

I2C總線上的主裝置與從裝置之間以位元組(8位)為機關進行雙向的資料傳輸。

2.4 I2C總線協定基本時序信号

空閑狀态:SCL和SDA都保持着高電平。

起始條件:總線在空閑狀态時,SCL和SDA都保持着高電平,當SCL為高電平期間而SDA由高到低的跳變,表示産生一個起始條件。在起始條件産生後,總線處于忙狀态,由本次資料傳輸的主從裝置獨占,其他I2C器件無法通路總線。

停止條件:當SCL為高而SDA由低到高的跳變,表示産生一個停止條件。

答應信号:每個位元組傳輸完成後的下一個時鐘信号,在SCL高電平期間,SDA為低,則表示一個應答信号。

非答應信号:每個位元組傳輸完成後的下一個時鐘信号,在SCL高電平期間,SDA為高,則表示一個應答信号。應答信号或非應答信号是由接收器發出的,發送器則是檢測這個信号(發送器,接收器可以從裝置也可以主裝置)。

注意:起始和結束信号總是由主裝置産生。

2.5  起始信号與停止信号

起始信号就是:  時鐘線SCL處于高電平的時候,資料線SDA由高電平變為低電平的過程。SCL=1;SDA=1;SDA=0;

停止信号就是: 時鐘線SCL處于低電平的時候,  資料線SDA由低電平變為高電平的過程。SCL=1;SDA=0;SDA=1;

STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)

2.6  應答信号

資料位的第9位就時應答位。 讀取應答位的流程和讀取資料位是一樣的。示例:   SCL=0;SCL=1;ACK=SDA;       這個ACK就是讀取的應答狀态。

STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)

2.7 資料位傳輸時序

通過時序圖了解到,SCL處于高電平的時候資料穩定,SCL處于低電平的時候資料不穩定。

那麼對于寫一位資料(STM32--->AT24C02): SCL=0;SDA=data; SCL=1;

那麼對于讀一位資料(STM32<-----AT24C02): SCL=0;SCL=1;data=SDA;  

STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)
  2.8 總線時序
STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)

四、IIC總線時序代碼、AT24C02讀寫代碼

  在調試IIC模拟時序的時候,可以在淘寶上買一個24M的USB邏輯分析儀,時序出現問題,使用邏輯分析儀一分析就可以快速找到問題。

STM32入門開發: 介紹IIC總線、讀寫AT24C02(EEPROM)(采用模拟時序)
4.1 iic.c  這是IIC模拟時序完整代碼

#include "iic.h"
 
/*
函數功能:IIC接口初始化
硬體連接配接:
SDA:PB7
SCL:PB6
*/
void IIC_Init(void)
{
    RCC->APB2ENR|=1<<3;//PB
    GPIOB->CRL&=0x00FFFFFF;
    GPIOB->CRL|=0x33000000;
    GPIOB->ODR|=0x3<<6;
}
 
/*
函數功能:IIC總線起始信号
*/
void IIC_Start(void)
{
    IIC_SDA_OUTMODE(); //初始化SDA為輸出模式
    IIC_SDA_OUT=1;       //資料線拉高
    IIC_SCL=1;           //時鐘線拉高
    DelayUs(4);        //電平保持時間
    IIC_SDA_OUT=0;       //資料線拉低
    DelayUs(4);        //電平保持時間
    IIC_SCL=0;           //時鐘線拉低
}
 
 
/*
函數功能:IIC總線停止信号
*/
void IIC_Stop(void)
{
    IIC_SDA_OUTMODE();    //初始化SDA為輸出模式
    IIC_SDA_OUT=0;       //資料線拉低
    IIC_SCL=0;           //時鐘線拉低
    DelayUs(4);           //電平保持時間
    IIC_SCL=1;           //時鐘線拉高
    DelayUs(4);           //電平保持時間
    IIC_SDA_OUT=1;       //資料線拉高
}
 
/*
函數功能:擷取應答信号
返 回 值:1表示失敗,0表示成功
*/
u8 IIC_GetACK(void)
{
    u8 cnt=0;
    IIC_SDA_INPUTMODE();//初始化SDA為輸入模式
    IIC_SDA_OUT=1;        //資料線上拉
    DelayUs(2);         //電平保持時間
    IIC_SCL=0;            //時鐘線拉低,告訴從機,主機需要資料
    DelayUs(2);         //電平保持時間,等待從機發送資料
    IIC_SCL=1;            //時鐘線拉高,告訴從機,主機現在開始讀取資料
    while(IIC_SDA_IN)   //等待從機應答信号
    {
        cnt++;
        if(cnt>250)return 1;
    }
    IIC_SCL=0;            //時鐘線拉低,告訴從機,主機需要資料
    return 0;
}
 
 
/*
函數功能:主機向從機發送應答信号
函數形參:0表示應答,1表示非應答
*/
void IIC_SendACK(u8 stat)
{
    IIC_SDA_OUTMODE(); //初始化SDA為輸出模式
    IIC_SCL=0;           //時鐘線拉低,告訴從機,主機需要發送資料
    if(stat)IIC_SDA_OUT=1; //資料線拉高,發送非應答信号
    else IIC_SDA_OUT=0;      //資料線拉低,發送應答信号
    DelayUs(2);            //電平保持時間,等待時鐘線穩定
    IIC_SCL=1;               //時鐘線拉高,告訴從機,主機資料發送完畢
    DelayUs(2);            //電平保持時間,等待從機接收資料
    IIC_SCL=0;               //時鐘線拉低,告訴從機,主機需要資料
}
 
 
/*
函數功能:IIC發送1個位元組資料
函數形參:将要發送的資料
*/
void IIC_WriteOneByteData(u8 data)
{
    u8 i;
    IIC_SDA_OUTMODE(); //初始化SDA為輸出模式
    IIC_SCL=0;           //時鐘線拉低,告訴從機,主機需要發送資料
    for(i=0;i<8;i++)
    {
        if(data&0x80)IIC_SDA_OUT=1; //資料線拉高,發送1
        else IIC_SDA_OUT=0;      //資料線拉低,發送0
        IIC_SCL=1;               //時鐘線拉高,告訴從機,主機資料發送完畢
        DelayUs(2);            //電平保持時間,等待從機接收資料
        IIC_SCL=0;                   //時鐘線拉低,告訴從機,主機需要發送資料
        DelayUs(2);            //電平保持時間,等待時鐘線穩定
        data<<=1;              //先發高位
    }
}
 
 
/*
函數功能:IIC接收1個位元組資料
返 回 值:收到的資料
*/
u8 IIC_ReadOneByteData(void)
{
    u8 i,data;
    IIC_SDA_INPUTMODE();//初始化SDA為輸入模式
    for(i=0;i<8;i++)
    {
        IIC_SCL=0;            //時鐘線拉低,告訴從機,主機需要資料
        DelayUs(2);         //電平保持時間,等待從機發送資料
        IIC_SCL=1;            //時鐘線拉高,告訴從機,主機現在正在讀取資料
        data<<=1;           
        if(IIC_SDA_IN)data|=0x01;
        DelayUs(2);         //電平保持時間,等待時鐘線穩定
    }
    IIC_SCL=0;                  //時鐘線拉低,告訴從機,主機需要資料 (必須拉低,否則将會識别為停止信号)
    return data;
}
 
       

4.2 AT24C02.c 這是AT24C02完整的讀寫代碼

#include "at24c02.h"
/*
函數功能:檢查AT24C02是否存在
返 回 值:1表示失敗,0表示成功
*/
u8 At24c02Check(void)
{
    u8 data;
    At24c02WriteOneByteData(255,0xAA);
    data=At24c02ReadOneByteData(255);
    if(data==0xAA)return 0;
    else return 1;
}
 
 
/*
函數功能:AT24C02随機讀資料
函數形參:讀取的位址(0~255)
返 回 值:讀出一個資料
*/
u8 At24c02ReadOneByteData(u32 addr)
{
    u8 data;
    IIC_Start(); //發送起始信号   
    IIC_WriteOneByteData(AT24C02_WRITE_ADDR); //設定寫模式
    IIC_GetACK();//擷取應答
    IIC_WriteOneByteData(addr); //設定讀取資料的位置
    IIC_GetACK();//擷取應答
 
    IIC_Start(); //發送起始信号   
    IIC_WriteOneByteData(AT24C02_READ_ADDR); //設定讀模式
    IIC_GetACK();//擷取應答
    data=IIC_ReadOneByteData(); //接收資料
    IIC_SendACK(1); //發送非應答信号
    IIC_Stop(); //停止信号
    return data;
}
 
 
/*
函數功能:AT24C02寫一個位元組的資料
函數形參:
        addr:寫入的位址(0~255)
        data:寫入的資料
*/
void At24c02WriteOneByteData(u32 addr,u8 data)
{
    IIC_Start(); //發送起始信号
    IIC_WriteOneByteData(AT24C02_WRITE_ADDR); //設定寫模式
    IIC_GetACK();//擷取應答
    IIC_WriteOneByteData(addr); //設定寫入資料的位置
    IIC_GetACK();//擷取應答
    IIC_WriteOneByteData(data); //設定寫入的資料
    IIC_GetACK();//擷取應答
    IIC_Stop();  //停止信号
    DelayMs(10); //等待寫入完畢
}
 
 
/*
函數 功 能:AT24C02目前位置讀一個位元組資料
函數傳回值:讀出的資料
*/
u8 At24c02CurrentAddrReadOneByteData(void)
{
    u8 data;
    IIC_Start(); //發送起始信号
    IIC_WriteOneByteData(AT24C02_READ_ADDR); //設定讀模式
    IIC_GetACK();//擷取應答
    data=IIC_ReadOneByteData(); //接收資料
    IIC_SendACK(1); //發送非應答信号
    IIC_Stop(); //停止信号
    return data;
}
 
 
 
/*
函數功能:AT24C02連續讀資料
函數形參:
u8 addr   //讀取的位址(0~255)
u8 len    //讀取的長度
u8 *buff  //讀出的資料存放緩沖區
*/
void At24c02ReadByteData(u32 addr,u8 len,u8 *buff)
{
    u8 i;
    IIC_Start(); //發送起始信号   
    IIC_WriteOneByteData(AT24C02_WRITE_ADDR); //設定寫模式
    IIC_GetACK();//擷取應答
    IIC_WriteOneByteData(addr); //設定讀取資料的位置
    IIC_GetACK();//擷取應答 
    IIC_Start(); //發送起始信号   
    IIC_WriteOneByteData(AT24C02_READ_ADDR); //設定讀模式
    IIC_GetACK();//擷取應答
 
    for(i=0;i<len;i++)
    {
         buff[i]=IIC_ReadOneByteData(); //接收資料
         IIC_SendACK(0); //發送應答信号
    }
    IIC_SendACK(1); //發送非應答信号
    IIC_Stop(); //停止信号
}
 
 
/*
函數功能:AT24C02頁寫
函數形參:
        addr:寫入的位址(0~255)
        *data:寫入的資料緩沖區
        len :寫入的長度
1. 頁寫的緩沖區大小是8個位元組,一次最多寫8個位元組進去。
2. 頁寫的位址是固定的。
0~7 是第一頁
8~15是第二頁            
*/
void At24c02PageWrite(u32 addr,u8 *data,u8 len)
{
    u8 i;
    IIC_Start(); //發送起始信号
    IIC_WriteOneByteData(AT24C02_WRITE_ADDR); //設定寫模式
    IIC_GetACK();//擷取應答
    IIC_WriteOneByteData(addr); //設定寫入資料的位置
    IIC_GetACK();//擷取應答
 
    for(i=0;i<len;i++)
    {
        IIC_WriteOneByteData(data[i]); //設定寫入的資料
        IIC_GetACK();//擷取應答
    }
    IIC_Stop();  //停止信号
    DelayMs(10); //等待寫入完畢
}
 
 
void AT24C02_WriteData(u32 addr,u8 *data,u8 len)
{
    u32 page_remain=8-addr%8; //一頁剩餘的位元組數量
    if(page_remain>=len)
    {
        page_remain=len;
    }
    while(1)
    {
        At24c02PageWrite(addr,data,page_remain);
        if(page_remain==len)
        {
            break;
        }
        addr+=page_remain;
        data+=page_remain;
        len-=page_remain;
        if(len>=8)page_remain=8;
        else page_remain=len;
    }
}