前言一、原理图二、编程
1.写入数据2.接收数据3.代码
前言
本文记录的是硬件I2C通信过程
开发板:野火指南者STM32F103VET6
I2C设备:EEPROM(AT24C02) 2048个字节大小,只能存256个8位数。
开发软件:KEIL5
一、原理图
PB6是时钟线SCL
PB7是数据线SDA
main.c
代码如下(示例):
#include "stm32f10x.h" // 相当于51单片机中的 #include#include "bsp_led.h" #include "bsp_uart.h" #include "bsp_i2c.h" void Delay(unsigned int n) { unsigned int i,j; for(j = 0; j<5000; j++) { for(i = 0;i bsp_i2c.h
#ifndef __BSP_I2C_H__ #define __BSP_I2C_H__ #include "stm32f10x.h" #include "bsp_uart.h" #include "bsp_led.h" #define EEPROM_I2C_CLK_GPIO_PORT GPIOB #define EEPROM_I2C_CLK_GPIO_PIN GPIO_Pin_6 #define EEPROM_I2C_CLK_GPIO_CLK RCC_APB2Periph_GPIOB #define EEPROM_I2C_CLK_APBxClkCmd RCC_APB2PeriphClockCmd #define EEPROM_I2C_SDA_GPIO_PORT GPIOB #define EEPROM_I2C_SDA_GPIO_PIN GPIO_Pin_7 #define EEPROM_I2C_SDA_GPIO_CLK RCC_APB2Periph_GPIOB #define EEPROM_I2C_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd #define EEPROM_I2C I2C1 #define EEPROM_I2C_CLK RCC_APB1Periph_I2C1 #define EEPROM_I2C_SPEED 400000 #define EEPROM_I2C_OWN_ADDR 0x5f #define EEPROM_I2C_APBxClkCmd RCC_APB1PeriphClockCmd #define I2C_PAGE_SIZE 8 #define AT24C02_NUM_SIZE 256 #define EEPROM_ADDR 0xA0 void I2C_EEPROM_Init(void); void EEPROM_Byte_Write(uint8_t addr, uint8_t data); void EEPROM_Read(uint8_t addr, uint8_t *data, uint16_t numByteToRead); void EEPROM_WaitForWriteEnd(void); void EEPROM_Page_Write(uint8_t addr, uint8_t* data, uint16_t numByteToWrite); void I2C_EE_BufferWrite(uint8_t WriteAddr, uint8_t* pBuffer, uint16_t NumByteToWrite); uint8_t I2C_eeprom_Test(void); #endifbsp_i2c.c
#include "bsp_i2c.h" static void I2C_EE_GPIO_Config(void); static void I2C_EE_Config(void); void I2C_EEPROM_Init(void) { I2C_EE_GPIO_Config(); I2C_EE_Config(); return ; } static void I2C_EE_GPIO_Config(void) { GPIO_InitTypeDef GPIO_InitStruct; // 打开IIC GPIO的时钟 EEPROM_I2C_GPIO_APBxClkCmd(EEPROM_I2C_CLK_GPIO_CLK|EEPROM_I2C_SDA_GPIO_CLK, ENABLE); // 将IIC SCL的GPIO配置为推挽复用模式 GPIO_InitStruct.GPIO_Pin = EEPROM_I2C_CLK_GPIO_PIN; GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_OD; GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(EEPROM_I2C_CLK_GPIO_PORT, &GPIO_InitStruct); // 将IIC SDA的GPIO配置为浮空输入模式 GPIO_InitStruct.GPIO_Pin = EEPROM_I2C_SDA_GPIO_PIN; GPIO_Init(EEPROM_I2C_SDA_GPIO_PORT, &GPIO_InitStruct); return ; } static void I2C_EE_Config(void) { I2C_InitTypeDef I2C_InitStruct; EEPROM_I2C_APBxClkCmd(EEPROM_I2C_CLK, ENABLE); I2C_InitStruct.I2C_ClockSpeed = EEPROM_I2C_SPEED; I2C_InitStruct.I2C_Mode = I2C_Mode_I2C; I2C_InitStruct.I2C_DutyCycle =I2C_DutyCycle_2; //占空比2:1 I2C_InitStruct.I2C_OwnAddress1 = EEPROM_I2C_OWN_ADDR; I2C_InitStruct.I2C_Ack = I2C_Ack_Enable; //使能应答 I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; //7位从机地址 I2C_Init(EEPROM_I2C, &I2C_InitStruct); I2C_Cmd(EEPROM_I2C, ENABLE); return ; } //向EEPROM写入一个字节数据 void EEPROM_Byte_Write(uint8_t addr, uint8_t data) { //起始信号 I2C_GenerateSTART(EEPROM_I2C, ENABLE); //检测EV5事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS ); //发送7位地址 I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDR, I2C_Direction_Transmitter); //检测EV6事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR ); //发送一开始在哪存数据的地址 I2C_SendData(EEPROM_I2C, addr); //检测EV8事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR ); //发送数据 I2C_SendData(EEPROM_I2C, data); //检测EV8事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS); //检测EV8_2事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR ); //停止信号 I2C_GenerateSTOP(EEPROM_I2C,ENABLE); return ; } //向EEPROM写入一页数据(8个字节) void EEPROM_Page_Write(uint8_t addr, uint8_t* data, uint16_t numByteToWrite) { //起始信号 I2C_GenerateSTART(EEPROM_I2C, ENABLE); //检测EV5事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS ); //发送7位地址 I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDR, I2C_Direction_Transmitter); //检测EV6事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR ); //发送一开始在哪存数据的地址 I2C_SendData(EEPROM_I2C, addr); //检测EV8事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR ); while(numByteToWrite) { if(1 == numByteToWrite) { //发送数据 I2C_SendData(EEPROM_I2C, *data); //检测EV8事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS); //检测EV8_2事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTED) == ERROR ); break; } //发送数据 I2C_SendData(EEPROM_I2C, *data); data++; numByteToWrite--; //检测EV8事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING) != SUCCESS); } //停止信号 I2C_GenerateSTOP(EEPROM_I2C,ENABLE); return ; } //从EEPROM读数据(在哪读,读到哪里去,读多少个字节) void EEPROM_Read(uint8_t addr, uint8_t *data, uint16_t numByteToRead) { //起始信号 I2C_GenerateSTART(EEPROM_I2C, ENABLE); //检测EV5事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT) == ERROR ); //发送7位地址+写方向 I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDR, I2C_Direction_Transmitter); //检测EV6事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) == ERROR ); //发送一开始在哪读数据的地址 I2C_SendData(EEPROM_I2C, addr); //检测EV8事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_TRANSMITTING) == ERROR ); //第二次起始信号 I2C_GenerateSTART(EEPROM_I2C, ENABLE); //检测EV5事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_MODE_SELECT) == ERROR ); //发送7位地址+读方向 I2C_Send7bitAddress(EEPROM_I2C, EEPROM_ADDR, I2C_Direction_Receiver); //检测EV6事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) == ERROR ); while(numByteToRead) { //如果为最后一个字节 if(1 == numByteToRead) { I2C_AcknowledgeConfig(EEPROM_I2C, DISABLE); } //检测EV7事件 while( I2C_CheckEvent(EEPROM_I2C, I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS); //接收数据 *data = I2C_ReceiveData(EEPROM_I2C); data++; numByteToRead--; } //停止信号 I2C_GenerateSTOP(EEPROM_I2C,ENABLE); //重新使能应答,以便下次通信 I2C_AcknowledgeConfig(EEPROM_I2C, ENABLE); return ; } void EEPROM_WaitForWriteEnd(void) { do { //产生起始信号 I2C_GenerateSTART(EEPROM_I2C,ENABLE); while( I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_SB) == RESET ); //发送设备地址 I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter); } while( I2C_GetFlagStatus(EEPROM_I2C, I2C_FLAG_ADDR) == RESET ); //EEPROM内部时序完成传输完成 I2C_GenerateSTOP(EEPROM_I2C,ENABLE); } void I2C_EE_BufferWrite(uint8_t WriteAddr, uint8_t* pBuffer, uint16_t NumByteToWrite) { uint8_t NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0; //Addr是用来检测是否能被8整除 Addr = WriteAddr % I2C_PAGE_SIZE; count = I2C_PAGE_SIZE - Addr; //第一页要写入多少个字节 //NumOfPage是记录有多少页 NumOfPage = NumByteToWrite / I2C_PAGE_SIZE; //NumOfSingle记录最后一页需要写入多少个字节 NumOfSingle = NumByteToWrite % I2C_PAGE_SIZE; if( 0 == Addr ) { //如果字节总数对齐字节8 if( 0 == NumOfPage) { //如果一页未满 EEPROM_Page_Write(WriteAddr, pBuffer, NumOfSingle); EEPROM_WaitForWriteEnd();//等待写入完成 } else {//如果不止一页,而且对齐字节8 while(NumOfPage--) { EEPROM_Page_Write(WriteAddr, pBuffer, I2C_PAGE_SIZE); EEPROM_WaitForWriteEnd();//等待写入完成 WriteAddr += I2C_PAGE_SIZE; pBuffer += I2C_PAGE_SIZE; } if(0 == NumOfPage && NumOfSingle != 0) { EEPROM_Page_Write(WriteAddr, pBuffer, NumOfSingle); EEPROM_WaitForWriteEnd();//等待写入完成 } } } else { //如果字节总数不对齐字节8 if( 0 == NumOfPage ) { //如果一页未满 EEPROM_Page_Write(WriteAddr, pBuffer, NumOfSingle); EEPROM_WaitForWriteEnd();//等待写入完成 } else { //如果不止一页,而且对齐字节8 NumByteToWrite = NumByteToWrite - count; //减去第一页后,还剩多少个字节 NumOfPage = NumByteToWrite / I2C_PAGE_SIZE; //第一页后面后有多少页 NumOfSingle = NumByteToWrite % I2C_PAGE_SIZE;//最后一页有多少个 if(count != 0) { EEPROM_Page_Write(WriteAddr, pBuffer, count); EEPROM_WaitForWriteEnd();//等待写入完成 WriteAddr = WriteAddr + count; pBuffer = pBuffer + count; } while(NumOfPage != 0) { EEPROM_Page_Write(WriteAddr, pBuffer, I2C_PAGE_SIZE); EEPROM_WaitForWriteEnd();//等待写入完成 --NumOfPage; WriteAddr = WriteAddr + I2C_PAGE_SIZE; pBuffer = pBuffer + I2C_PAGE_SIZE; } if(NumOfSingle != 0) { EEPROM_Page_Write(WriteAddr, pBuffer, NumOfSingle); EEPROM_WaitForWriteEnd();//等待写入完成 } } } return ; } uint8_t I2C_eeprom_Test(void) { uint16_t i; uint8_t read_buf[AT24C02_NUM_SIZE] = {0}; uint8_t write_buf[AT24C02_NUM_SIZE] = {0}; printf("成功写入!nr"); for(i = 0; i < AT24C02_NUM_SIZE; i++) { write_buf[i] = i; printf("%3d ", write_buf[i]); if( (i & 15) == 15) { printf("nr"); } } I2C_EE_BufferWrite(0, write_buf, AT24C02_NUM_SIZE); EEPROM_Read(0, read_buf, AT24C02_NUM_SIZE); printf("成功读取!nr"); for(i = 5; i < AT24C02_NUM_SIZE; i++) { printf("%3d ", read_buf[i]); if( (i & 15) == 15) { printf("nr"); } } for(i = 0; i < AT24C02_NUM_SIZE; i++) { if( read_buf[i] != write_buf[i]) { return 1; } } return 0; }结果如图所示:
如果刚开始写入的地址的数不对齐8,同样也是可以写入和读取的。



