一、编程要点
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);
}