一、編程要點
1、I2C相關GPIO以及外設使能
2、初始化I2C結構體(模式、地址、速率等參數)
3、編寫I2C讀寫函數
4、對EEPROM進行讀寫
二、結構體&庫函數
1、I2C初始化結構 ( I2C_InitTypeDef )
typedef struct
{
uint32_t I2C_ClockSpeed; /*!< I2C通訊速率 不高於400 000 */
uint16_t I2C_Mode; /*!< 模式 I2C and */
uint16_t I2C_DutyCycle; /*!<設置佔空比 2 16/9 */
uint16_t I2C_OwnAddress1; /*!< 設置STM32自身地址 在總線上唯一即可*/
uint16_t I2C_Ack; /*!<產生應答信號 */
uint16_t I2C_AcknowledgedAddress; /*!< 7位地址模式 */
}I2C_InitTypeDef;
2、起始 停止 信號產生函數
I2C_GenerateSTART(I2C_TypeDef* I2Cx, FunctionalState NewState)
I2C_GenerateSTART(EEPROM_I2C,ENABLE);
@brief 產生起始信號
@param1:使能或使能
I2C_GenerateSTOP(I2C_TypeDef* I2Cx, FunctionalState NewState)
I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
@brief 產生停止信號
@param1:使能或使能
3、標誌位檢查函數
I2C_CheckEvent(I2C_TypeDef* I2Cx, uint32_t I2C_EVENT)
@brief 檢查I2C相應標誌位
@param1:i2c端口號
@param2:要檢查的相應標誌位(EV5 EV6 等)
4、廣播設備地址函數I2C_Send7bitAddress(I2C_TypeDef* I2Cx, uint8_t Address, uint8_t I2C_Direction)
I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);
@brief 廣播要通訊的設備地址(7位)
@param1:i2c端口號
@param2:設備地址
@param2:讀寫方向
5、發送 讀取數據函數
I2C_SendData(I2C_TypeDef* I2Cx, uint8_t Data)
I2C_SendData (EEPROM_I2C,data);
@brief 向數據緩存寄存器發送數據
@param1:i2c端口號
@param2:要發送的數據(8位)
uint8_t I2C_ReceiveData(I2C_TypeDef* I2Cx)
data = I2C_ReceiveData(EEPROM_I2C);
@brief 從數據緩存寄存器讀取數據
@param1:i2c端口號
@retval:讀取到的數據
6、產生應答信號
I2C_AcknowledgeConfig(I2C_TypeDef* I2Cx, FunctionalState NewState)
@brief 產生應答或不產生應答信號
@param1:i2c端口號
@retval:使能或失能 (應答 非應答)
7、判斷EEPROM內部時序完成函數
void EE_Await(void)
{
do
{
I2C_GenerateSTART(I2C1,ENABLE); //發送起始信號
while((I2C_GetFlagStatus(I2C1, I2C_FLAG_SB))==RESET); //檢測EV5時間並清除標誌
I2C_Send7bitAddress(I2C1,0XA0,I2C_Direction_Transmitter);//廣播要發送的設備地址
}
while((I2C_GetFlagStatus(I2C1, I2C_FLAG_ADDR))==RESET); //檢測EV6時間並清除標誌
I2C_GenerateSTOP(I2C1,ENABLE); //產生結束信號
}
三、I2C通訊時序
發送數據時序:
S -> -> EEPROM地址 -> -> Addr -> -> data -> -> data n -> data(p) -> ->p
EV5 EV6 EV8 EV8 EV8 EV8_2
讀數據時序:
S -> -> EEPROM地址 -> -> Addr -> ->
EV5 EV6 EV8
S -> -> EEPROM地址(讀方向) -> (判斷是否最後一個方向) ->read()->p
EV5 EV6(讀方向) (是則使能應答) EV7(有新數據到來)
四、程序源碼
1、eeprom.c文件
#include "eeprom.h"
void I2C_EE_config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE); //打開GPIO時鐘
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //選擇SCL線
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //開漏輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //輸出速率
GPIO_Init(GPIOB,&GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //選擇SDA線
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD; //開漏輸出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //輸出速率
GPIO_Init(GPIOB,&GPIO_InitStructure);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1,ENABLE);
I2C_InitStructure.I2C_ClockSpeed = 400000;
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_OwnAddress1 = 0x5f;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Init(I2C1, &I2C_InitStructure);
I2C_Cmd(I2C1,ENABLE);
}
void I2C_WriteByte(uint8_t Addr,uint8_t *pBuffer,uint8_t num)
{
//uint32_t i=0x1000;
//延時等待時間
I2C_GenerateSTART(I2C1,ENABLE); //發送起始信號
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)== ERROR); //檢測EV5時間並清除標誌
I2C_Send7bitAddress(I2C1,0XA0,I2C_Direction_Transmitter); //廣播要發送的設備地址
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)== ERROR);//檢測EV6時間並清除標誌
I2C_SendData(I2C1,Addr); //發送寫入設備內存地址
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING)== ERROR); //檢測EV8時間並清除標誌
while(num)
{
num--;
I2C_SendData(I2C1,*pBuffer); //發送寫入數據
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING)== ERROR); //檢測EV8時間並清除標誌
pBuffer++;
}
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)== ERROR); //檢測EV8-2時間並清除標誌
I2C_GenerateSTOP(I2C1,ENABLE); //產生結束信號
}
void I2C_ReadByte(u8 Addr,uint8_t *data,uint8_t num)
{
I2C_GenerateSTART(I2C1,ENABLE); //發送起始信號
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)== ERROR); //檢測EV5時間並清除標誌
I2C_Send7bitAddress(I2C1,0XA0,I2C_Direction_Transmitter); //廣播要發送的設備地址
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)== ERROR);//檢測EV6時間並清除標誌
I2C_SendData(I2C1,Addr); //發送寫入設備內存地址
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTING)== ERROR); //檢測EV8時間並清除標誌
I2C_GenerateSTART(I2C1,ENABLE); //第二次發送起始信號
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT)== ERROR); //檢測EV5時間並清除標誌
I2C_Send7bitAddress(I2C1,0XA0,I2C_Direction_Receiver); //廣播要發送的設備地址
while(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)== ERROR); //檢測EV6時間並清除標誌
while(num)
{
if(num==1)I2C_AcknowledgeConfig(I2C1,DISABLE);
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_RECEIVED ) == ERROR); //檢測到EV7事件 表示有新的數據到來
*data=I2C_ReceiveData(I2C1);
data++;
num--;
}
//數據傳輸完成
I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
//重新配置ACK使能,以便下次通訊
I2C_AcknowledgeConfig (EEPROM_I2C,ENABLE);
}
void EE_Await(void)
{
do
{
I2C_GenerateSTART(I2C1,ENABLE); //發送起始信號
while((I2C_GetFlagStatus(I2C1, I2C_FLAG_SB))==RESET); //檢測EV5時間並清除標誌
I2C_Send7bitAddress(I2C1,0XA0,I2C_Direction_Transmitter); //廣播要發送的設備地址
}
while((I2C_GetFlagStatus(I2C1, I2C_FLAG_ADDR))==RESET); //檢測EV6時間並清除標誌
I2C_GenerateSTOP(I2C1,ENABLE); //產生結束信號
}
2、eeprom.h 文件
#ifndef __EEPROM_H
#define __EEPROM_H
#include "stm32f10x.h"
#define EEPROM_I2C I2C1
void I2C_EE_config(void);
void I2C_WriteByte(uint8_t Buffer,uint8_t Addr);
void I2C_ReadByte(u8 Addr,uint8_t *data,uint8_t num);
void EE_Await(void);
#endif //__EEPROM_H
3、main.c 文件#include "stm32f10x.h"
#include "bsp_led.h"
#include "usart.h"
#include "eeprom.h"
#define GPIOB_ODR_Addr (GPIOB_BASE+0X0C)
#define PBout(n) *(unsigned int *)((GPIOB_ODR_Addr & 0xF0000000)+0x02000000+((GPIOB_ODR_Addr&0x00FFFFFF)<<5)+(n<<2))
uint8_t ReadData[10]={0};
uint8_t writeData[8]={0,1,2,3,4,5,6,7};
int main(void)
{
u8 i;
LED_GPIO_Config();
PBout(0)=1;
USART_config();
I2C_EE_config();
I2C_WriteByte(8,writeData,8); //數據 地址 數量
EE_Await();
I2C_ReadByte(8,ReadData,8); //地址 數據數量
for(i=0;i<8;i++) //循環通過串口發送讀取到的數據
{
printf("%2d",ReadData[i]);
PBout(0)=0;
}
while(1);
}