stm32-IIC讀寫EEPROM


I2C 通訊協議:(InterIntegrated Circuit)是由Phiilps 公司開發的,由於它引腳少,硬件實現簡單,可擴展性強,不需要USARTCAN  等通訊協議的外部收發設備,現在被廣泛地使用在系統內多個集成電路(IC)間的通訊,其常用的連接方式如下:

 

 物理層:

(1) 它是一個支持設備的總線。“總線”指多個設備共用的信號線。在一個I2C 通訊總線中,可連接多個I2C 通訊設備,支持多個通訊主機及多個通訊從機。

(2) 一個I2C 總線只使用兩條總線線路,一條雙向串行數據線(SDA) ,一條串行時鍾線(SCL)。數據線即用來表示數據,時鍾線用於數據收發同步。

(3) 每個連接到總線的設備都有一個獨立的地址,主機可以利用這個地址進行不同設備之間的訪問。

(4) 總線通過上拉電阻接到電源。當I2C 設備空閑時,會輸出高阻態,而當所有設備都空閑,都輸出高阻態時,由上拉電阻把總線拉成高電平。

(5) 多個主機同時使用總線時,為了防止數據沖突,會利用仲裁方式決定由哪個設備占用總線。

(6) 具有三種傳輸模式:標准模式傳輸速率為100kbit/s ,快速模式為400kbit/s ,高速模式下可達3.4Mbit/s,但目前大多I C 設備尚不支持高速模式。

(7) 連接到相同總線的IC 數量受到總線的最大電容400pF 限制

 

 

協議層:

  1. 起始信號:由主機的IIC接口產生的傳輸起始信號,這時連接到總線上的所有從機都會收到這個信號;
  2. 地址廣播:起始信號產生后,所有從機都會開始等待主機接下來廣播的從機地址信號,以選中從機設備,沒有被選中的將會忽略之后的數據信號;
  3. 傳輸方向:0表示主機向從機寫數據,1表示主機向從機讀數據;
  4. 應答信號:從機接收到匹配的地址之后,從機或主機會返回一個應答(ACK)或非應答(NACK)信號,只有接收到信號,主機才能繼續發送或接受數據;
  5. 寫數據/讀數據:主機每發送完一個數據包之后。都要重新等待從機的應答信號,並重復這個步驟;/從機每發送玩一個數據包之后,都會重新等待主機的應答信號,並重復這個步驟;
  6. 停止信號:數據傳輸完成后(得到NACK信號后),主機向從機發送一個停止傳輸信號,表示不再傳輸。/當主機希望停止接受數據時,就會返回一個NACK信號給從機,從機就會自動停止數據傳輸;

 

除了基本的讀寫,I2C通訊更常用的是復合格式,即第三幅圖的情況,該傳輸過程有兩次起始信號(S)。一般在第一次傳輸中,主機通過SLAVE_ADDRESS  尋找到從設備后,發送一段“數據”,這段數據通常用於表示從設備內部的寄存器或存儲器地址(注意區分它與 SLAVE_ADDRESS  的區別);在第二次的傳輸中,對該地址的內容進行讀或寫。也就是說,第一次通訊是告訴從機讀寫地址,第二次則是讀寫的實際內容。

bsp_iic_ee.h文件如下:

 

#ifndef __I2C_EE_H
#define __I2C_EE_H
#include "stm32f10x.h"

#define I2Cx_EEPROM    I2C1   
#define I2C_EEPROM_CLK    RCC_APB1Periph_I2C1
#define I2C_EEPROM_GPIO_CLK     RCC_APB2Periph_GPIOB
#define I2C_EPPROM_SCL_PORT    GPIOB//時鍾線
#define I2C_EEPROM_SCL_PIN     GPIO_Pin_6
#define I2C_EEPROM_SDA_PORT    GPIOB//數據線
#define I2C_EEPROM_SDA_PIN     GPIO_Pin_7

#define I2C_Speed      400000//通訊速度kbits/s
#define I2Cx_OWN_ADDRESS7      0X0A   //主機地址可自行配置,但在總線上這個地址要唯一
#define I2C_PageSize   8//每頁8個字節
#define I2CT_FLAG_TIMEOUT         ((uint32_t)0x1000)//等待超時時間
#define I2CT_LONG_TIMEOUT         ((uint32_t)(10 * I2CT_FLAG_TIMEOUT))

#define EEPROM_DEBUG_ON         0

#define EEPROM_INFO(fmt,arg...)           printf("<<-EEPROM-INFO->> "fmt"\n",##arg)//##args表示參數個數可變
#define EEPROM_ERROR(fmt,arg...)          printf("<<-EEPROM-ERROR->> "fmt"\n",##arg)
#define EEPROM_DEBUG(fmt,arg...)          do{\
                                          if(EEPROM_DEBUG_ON)\
                                          printf("<<-EEPROM-DEBUG->> [%d]"fmt"\n",__LINE__, ##arg);\
                                          }while(0)
      
/* 
 * AT24C02 2kb = 2048bit = 2048/8 B = 256 B
 * 32 pages of 8 bytes each
 *
 * Device Address
 * 1 0 1 0 A2 A1 A0 R/W
 * 1 0 1 0 0  0  0  0 = 0XA0//寫地址
 * 1 0 1 0 0  0  0  1 = 0XA1 //讀地址
 */

/* EEPROM Addresses defines */
#define EEPROM_Block0_ADDRESS 0xA0   /* E2 = 0 */
//#define EEPROM_Block1_ADDRESS 0xA2 /* E2 = 0 */
//#define EEPROM_Block2_ADDRESS 0xA4 /* E2 = 0 */
//#define EEPROM_Block3_ADDRESS 0xA6 /* E2 = 0 */


void I2C_EE_Init(void);
void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite);
uint32_t I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr);
uint32_t I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite);
uint32_t I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead);
void I2C_EE_WaitEepromStandbyState(void);

#endif

 

bsp_iic_ee.c文件如下:

#include "bsp_iic_ee.h"
#include "bsp_usart.h"

uint16_t EEPROM_ADDRESS;//從設備地址
static __IO uint32_t  I2CTimeout = I2CT_LONG_TIMEOUT;   //定義超時變量
static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode);//聲明超時用戶回調函數

static void I2C_GPIO_Config(void)//引腳配置
{
  GPIO_InitTypeDef  GPIO_InitStructure; 

//開啟I2C,GPIO時鍾 
  RCC_APB1PeriphClockCmd(I2C_EEPROM_CLK, ENABLE );
  RCC_APB2PeriphClockCmd(I2C_EEPROM_GPIO_CLK, ENABLE );
//配置SCL    
  GPIO_InitStructure.GPIO_Pin = I2C_EEPROM_SCL_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;   //這里必須復用開漏輸出       
  GPIO_Init(I2C_EPPROM_SCL_PORT , &GPIO_InitStructure);
//配置SDA    
  GPIO_InitStructure.GPIO_Pin = I2C_EEPROM_SDA_PIN;
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;  //也必須復用開漏輸出     
  GPIO_Init(I2C_EEPROM_SDA_PORT , &GPIO_InitStructure);        
}

static void I2C_Mode_Config(void)//模式配置
{
  I2C_InitTypeDef  I2C_InitStructure; 
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//此處未分主從,直接選這個
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//占空比
  I2C_InitStructure.I2C_OwnAddress1 =I2Cx_OWN_ADDRESS7; //主機地址
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable ;//使能響應
  I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//一般使用7位地址
  I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;//速度
  I2C_Init(I2Cx_EEPROM , &I2C_InitStructure);//初始化配置
  I2C_Cmd(I2Cx_EEPROM , ENABLE);   //使能I2C1
}

void I2C_EE_Init(void)
{

  I2C_GPIO_Config(); 
 
  I2C_Mode_Config();
#ifdef EEPROM_Block0_ADDRESS
  EEPROM_ADDRESS = EEPROM_Block0_ADDRESS;//塊0被選中
#endif

#ifdef EEPROM_Block1_ADDRESS  
  EEPROM_ADDRESS = EEPROM_Block1_ADDRESS;
#endif

#ifdef EEPROM_Block2_ADDRESS  
  EEPROM_ADDRESS = EEPROM_Block2_ADDRESS;
#endif

#ifdef EEPROM_Block3_ADDRESS  
  EEPROM_ADDRESS = EEPROM_Block3_ADDRESS;
#endif
}
//將緩沖區的數據寫入到I2C_EE中
void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite) { u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0; Addr = WriteAddr % I2C_PageSize; count = I2C_PageSize - Addr; NumOfPage = NumByteToWrite / I2C_PageSize; NumOfSingle = NumByteToWrite % I2C_PageSize; /* If WriteAddr is I2C_PageSize aligned */ if(Addr == 0) { /* If NumByteToWrite < I2C_PageSize */ if(NumOfPage == 0) { I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); I2C_EE_WaitEepromStandbyState(); } /* If NumByteToWrite > I2C_PageSize */ else { while(NumOfPage--) { I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize); I2C_EE_WaitEepromStandbyState(); WriteAddr += I2C_PageSize; pBuffer += I2C_PageSize; } if(NumOfSingle!=0) { I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); I2C_EE_WaitEepromStandbyState(); } } } /* If WriteAddr is not I2C_PageSize aligned */ else { /* If NumByteToWrite < I2C_PageSize */ if(NumOfPage== 0) { I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); I2C_EE_WaitEepromStandbyState(); } /* If NumByteToWrite > I2C_PageSize */ else { NumByteToWrite -= count; NumOfPage = NumByteToWrite / I2C_PageSize; NumOfSingle = NumByteToWrite % I2C_PageSize; if(count != 0) { I2C_EE_PageWrite(pBuffer, WriteAddr, count); I2C_EE_WaitEepromStandbyState(); WriteAddr += count; pBuffer += count; } while(NumOfPage--) { I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize); I2C_EE_WaitEepromStandbyState(); WriteAddr += I2C_PageSize; pBuffer += I2C_PageSize; } if(NumOfSingle != 0) { I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); I2C_EE_WaitEepromStandbyState(); } } } } //寫一個字節給EE,讀讀英文解釋吧,這就是主機發送的過程 uint32_t I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr) { /* Send STRAT condition */ I2C_GenerateSTART(I2Cx_EEPROM , ENABLE); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV5 and clear it */ while(!I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_MODE_SELECT)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(0); } I2CTimeout = I2CT_FLAG_TIMEOUT; /* Send EEPROM address for write */ I2C_Send7bitAddress(I2Cx_EEPROM , EEPROM_ADDRESS, I2C_Direction_Transmitter); /* Test on EV6 and clear it */ while(!I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(1); } /* Send the EEPROM's internal address to write to */ I2C_SendData(I2Cx_EEPROM , WriteAddr); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV8 and clear it */ while(!I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(2); } /* Send the byte to be written */ I2C_SendData(I2Cx_EEPROM , *pBuffer); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV8 and clear it */ while(!I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3); } /* Send STOP condition */ I2C_GenerateSTOP(I2Cx_EEPROM , ENABLE); return 1; } //頁寫入,但NumByteToWrite不能超過8 uint32_t I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite) { I2CTimeout = I2CT_LONG_TIMEOUT; while(I2C_GetFlagStatus(I2Cx_EEPROM , I2C_FLAG_BUSY)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(4); } /* Send START condition */ I2C_GenerateSTART(I2Cx_EEPROM , ENABLE); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV5 and clear it */ while(!I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_MODE_SELECT)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(5); } /* Send EEPROM address for write */ I2C_Send7bitAddress(I2Cx_EEPROM , EEPROM_ADDRESS, I2C_Direction_Transmitter); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV6 and clear it */ while(!I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(6); } /* Send the EEPROM's internal address to write to */ I2C_SendData(I2Cx_EEPROM , WriteAddr); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV8 and clear it */ while(! I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(7); } /* While there is data to be written */ while(NumByteToWrite--) { /* Send the current byte */ I2C_SendData(I2Cx_EEPROM , *pBuffer); /* Point to the next byte to be written */ pBuffer++; I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV8 and clear it */ while (!I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(8); } } /* Send STOP condition */ I2C_GenerateSTOP(I2Cx_EEPROM , ENABLE); return 1; } //讀緩存區里的數據,主機接受的過程 uint32_t I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead) { I2CTimeout = I2CT_LONG_TIMEOUT; //*((u8 *)0x4001080c) |=0x80; while(I2C_GetFlagStatus(I2Cx_EEPROM , I2C_FLAG_BUSY)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(9); } /* Send START condition */ I2C_GenerateSTART(I2Cx_EEPROM , ENABLE); //*((u8 *)0x4001080c) &=~0x80; I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV5 and clear it */ while(!I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_MODE_SELECT)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(10); } /* Send EEPROM address for write */ I2C_Send7bitAddress(I2Cx_EEPROM , EEPROM_ADDRESS, I2C_Direction_Transmitter); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV6 and clear it */ while(!I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(11); } /* Clear EV6 by setting again the PE bit */ I2C_Cmd(I2Cx_EEPROM , ENABLE); /* Send the EEPROM's internal address to write to */ I2C_SendData(I2Cx_EEPROM , ReadAddr); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV8 and clear it */ while(!I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_BYTE_TRANSMITTED)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(12); } /* Send STRAT condition a second time */ I2C_GenerateSTART(I2Cx_EEPROM , ENABLE); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV5 and clear it */ while(!I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_MODE_SELECT)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(13); } /* Send EEPROM address for read */ I2C_Send7bitAddress(I2Cx_EEPROM , EEPROM_ADDRESS, I2C_Direction_Receiver); I2CTimeout = I2CT_FLAG_TIMEOUT; /* Test on EV6 and clear it */ while(!I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(14); } /* While there is data to be read */ while(NumByteToRead) { if(NumByteToRead == 1) { /* Disable Acknowledgement */ I2C_AcknowledgeConfig(I2Cx_EEPROM , DISABLE); /* Send STOP Condition */ I2C_GenerateSTOP(I2Cx_EEPROM , ENABLE); } /* Test on EV7 and clear it */ I2CTimeout = I2CT_LONG_TIMEOUT; while(I2C_CheckEvent(I2Cx_EEPROM , I2C_EVENT_MASTER_BYTE_RECEIVED)==0) { if((I2CTimeout--) == 0) return I2C_TIMEOUT_UserCallback(3); } { /* Read a byte from the EEPROM */ *pBuffer = I2C_ReceiveData(I2Cx_EEPROM ); /* Point to the next location where the byte read will be saved */ pBuffer++; /* Decrement the read bytes counter */ NumByteToRead--; } } /* Enable Acknowledgement to be ready for another reception */ I2C_AcknowledgeConfig(I2Cx_EEPROM , ENABLE); return 1; } //此函數內容是,在發送從設備地址之后,檢測EE的響應,若EE發送ACK則准備好了,可以開始下一次通訊 void I2C_EE_WaitEepromStandbyState(void) { vu16 SR1_Tmp = 0; do { /* Send START condition */ I2C_GenerateSTART(I2Cx_EEPROM , ENABLE); /* Read I2C1 SR1 register */ SR1_Tmp = I2C_ReadRegister(I2Cx_EEPROM , I2C_Register_SR1); /* Send EEPROM address for write */ I2C_Send7bitAddress(I2Cx_EEPROM , EEPROM_ADDRESS, I2C_Direction_Transmitter); }while(!(I2C_ReadRegister(I2Cx_EEPROM , I2C_Register_SR1) & 0x0002)); /* Clear AF flag */ I2C_ClearFlag(I2Cx_EEPROM , I2C_FLAG_AF); /* STOP condition */ I2C_GenerateSTOP(I2Cx_EEPROM , ENABLE); } //能定位是哪里超時了 static uint32_t I2C_TIMEOUT_UserCallback(uint8_t errorCode) { /* Block communication and all processes */ EEPROM_ERROR("I2C等待超時!errorCode = %d",errorCode); return 0; }

main.c文件如下:

#include "stm32f10x.h"
#include "bsp_led.h"
#include "bsp_usart.h"
#include "bsp_iic_ee.h"
#include <string.h>

#define  EEP_Firstpage      0x00
uint8_t I2c_Buf_Write[256];
uint8_t I2c_Buf_Read[256];
uint8_t I2C_Test(void);

int main(void)
{ 
  LED_GPIO_Config();
  
  blue(ON);
    USART_Config();
    printf("\r\n 這是一個IIC外設讀寫實驗 \r\n");
    I2C_EE_Init();

    printf("\r\n 這是一個IIC外設讀寫實驗 \r\n");    
    if(I2C_Test() ==1)
    {
            green(ON);
    }
    else
    {
            red(ON);
    }
  
  while (1)
  {      
  }
}

uint8_t I2C_Test(void)
{
    uint16_t i;

    printf("寫入的數據\n\r");
    
    for ( i=0; i<=255; i++ ) 
  {   
    I2c_Buf_Write[i] = i;

    printf("0x%02X ", I2c_Buf_Write[i]);
    if(i%16 == 15)    
        printf("\n\r");    
   }

//將數據寫入到EEPROM 
    I2C_EE_BufferWrite( I2c_Buf_Write, EEP_Firstpage, 256);
  
      EEPROM_INFO("\n\r寫成功\n\r");
       EEPROM_INFO("\n\r讀出的數據\n\r");
    I2C_EE_BufferRead(I2c_Buf_Read, EEP_Firstpage, 256); 
    for (i=0; i<256; i++)
    {    
        if(I2c_Buf_Read[i] != I2c_Buf_Write[i])
        {
            EEPROM_ERROR("0x%02X ", I2c_Buf_Read[i]);
            EEPROM_ERROR("錯誤,讀入數據與寫入數據不一致\n\r");
            return 0;
        }
    printf("0x%02X ", I2c_Buf_Read[i]);
    if(i%16 == 15)    
        printf("\n\r");
    
    }
  EEPROM_INFO("I2C(AT24C02)讀寫測試成功\n\r");
  
  return 1;
}

 


免責聲明!

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



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