I2C总线是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息,SDA数据线和SCL时钟信号线。
主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件.在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。
总线空闲—SDA和SCL都为高电平
数据的有效位规定
I2C的起始和终止信号
SCL线为高电平期间,SDA线由高电平—>低电平,表示起始信号
SCL线为高电平期间,SDA线由低电平—>高电平,表示终止信号
3.I2C字节的传送与应答
每一个字节是8位,先传最高位(MSB),每传一个字节后面必须跟随一个应答位(即一帧有9位)。
下面代码用到了串口返回读取的数据信息,此处用到了串口一
用到的库函数
主发送器通讯过程
主接收器
bsp_i2c_eeprom.h
#ifndef __BSP_I2C_EEPROM_H
#define __BSP_I2C_EEPROM_H
#include "stm32f4xx.h"
#define I2C_OWN_ADDR 0x77 /*主机的八位地址,只要不和从机重复*/
#define I2C_SPEED 400000 /*快速模式最高400khz*/
#define EEPROM_ADDR (0x50<<1) //0xA0,
//引脚定义,移植时修改此处即可,野火的I2C口为PB6和PB7
/*******************************************************/
#define EEPROM_I2C I2C1
#define EEPROM_I2C_CLK RCC_APB1Periph_I2C1
#define EEPROM_SCL_GPIO_PORT GPIOB
#define EEPROM_SCL_GPIO_CLK RCC_AHB1Periph_GPIOB
#define EEPROM_SCL_PIN GPIO_Pin_6
#define EEPROM_SCL_AF GPIO_AF_I2C1
#define EEPROM_SCL_SOURCE GPIO_PinSource6
#define EEPROM_SDA_GPIO_PORT GPIOB
#define EEPROM_SDA_GPIO_CLK RCC_AHB1Periph_GPIOB
#define EEPROM_SDA_PIN GPIO_Pin_7
#define EEPROM_SDA_AF GPIO_AF_I2C1
#define EEPROM_SDA_SOURCE GPIO_PinSource7
/************************************************************/
void EEPROM_GPIO_Config(void);
void EEPROM_I2C_ModeConfig(void);
void EEPROM_Byte_Write(uint8_t* pData,uint8_t addr);
uint8_t EEPROM_Byte_Read(uint8_t addr);
#endif /* __BSP_I2C_EEPROM_H */
bsp_i2c_eeprom.c
#include "./i2c/bsp_i2c_eeprom.h"
void Wait_for_EEPROM(void);
//1.初始化GPIO
void EEPROM_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_AHB1PeriphClockCmd(EEPROM_SCL_GPIO_CLK|EEPROM_SDA_GPIO_CLK,ENABLE);
/* 连接SCL*/
GPIO_PinAFConfig(EEPROM_SCL_GPIO_PORT,EEPROM_SCL_SOURCE,EEPROM_SCL_AF);
/* 连接 SDA*/
GPIO_PinAFConfig(EEPROM_SDA_GPIO_PORT,EEPROM_SDA_SOURCE,EEPROM_SDA_AF);
/* GPIO初始化 */
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/* 配置SCL引脚为复用功能 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = EEPROM_SCL_PIN ;
GPIO_Init(EEPROM_SCL_GPIO_PORT, &GPIO_InitStructure);
/* 配置SDA引脚为复用功能 */
GPIO_InitStructure.GPIO_Pin = EEPROM_SDA_PIN;
GPIO_Init(EEPROM_SDA_GPIO_PORT, &GPIO_InitStructure);
}
//2.初始化I2C模式
void EEPROM_I2C_ModeConfig(void)
{
I2C_InitTypeDef I2C_InitStruct;
/* 使能 I2C时钟 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
I2C_InitStruct.I2C_Ack = I2C_Ack_Enable;
I2C_InitStruct.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStruct.I2C_ClockSpeed = I2C_SPEED;
I2C_InitStruct.I2C_DutyCycle = I2C_DutyCycle_2 ;
I2C_InitStruct.I2C_Mode = I2C_Mode_I2C ;
I2C_InitStruct.I2C_OwnAddress1 = I2C_OWN_ADDR;
I2C_Init(EEPROM_I2C,&I2C_InitStruct);
I2C_Cmd(EEPROM_I2C,ENABLE);
}
//3.byte write 函数
void EEPROM_Byte_Write(uint8_t* pData,uint8_t addr)
{
I2C_GenerateSTART(EEPROM_I2C,ENABLE);
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS )
{
//....
}
I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS )
{
}
I2C_SendData(EEPROM_I2C,addr);
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS )
{
}
I2C_SendData(EEPROM_I2C,*pData);
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS )
{
}
I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
}
//4.random read
uint8_t EEPROM_Byte_Read(uint8_t addr)
{
uint8_t readTemp;
Wait_for_EEPROM();
I2C_GenerateSTART(EEPROM_I2C,ENABLE);
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS )
{
//....
}
I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED) != SUCCESS )
{
}
I2C_SendData(EEPROM_I2C,addr);
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_TRANSMITTED) != SUCCESS )
{
}
I2C_GenerateSTART(EEPROM_I2C,ENABLE);
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS )
{
//....
}
I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Receiver);
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED) != SUCCESS )
{
}
I2C_AcknowledgeConfig(EEPROM_I2C,DISABLE);
while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_BYTE_RECEIVED) != SUCCESS )
{
}
readTemp = I2C_ReceiveData(EEPROM_I2C);
I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
return readTemp;
}
//5.等待EEPROM内部写完毕
void Wait_for_EEPROM(void)
{
do
{
I2C_GenerateSTART(EEPROM_I2C,ENABLE);
// while(I2C_CheckEvent(EEPROM_I2C,I2C_EVENT_MASTER_MODE_SELECT) != SUCCESS )
// {
// //....
// }
I2C_Send7bitAddress(EEPROM_I2C,EEPROM_ADDR,I2C_Direction_Transmitter);
}while(I2C_GetFlagStatus(EEPROM_I2C,I2C_FLAG_ADDR) == RESET);
//等待ADDR = 1 才执行下面的语句
I2C_ClearFlag(EEPROM_I2C,I2C_FLAG_AF);
I2C_GenerateSTOP(EEPROM_I2C,ENABLE);
while(I2C_GetFlagStatus(EEPROM_I2C,I2C_FLAG_BUSY) == SET);
//总线空闲,执行下面的语句
}
bsp_debug_usart.h
#ifndef __DEBUG_USART_H
#define __DEBUG_USART_H
#include "stm32f4xx.h"
#include
//引脚定义
/*******************************************************/
#define DEBUG_USART USART1
#define DEBUG_USART_CLK RCC_APB2Periph_USART1
#define DEBUG_USART_BAUDRATE 115200 //串口波特率
#define DEBUG_USART_RX_GPIO_PORT GPIOA
#define DEBUG_USART_RX_GPIO_CLK RCC_AHB1Periph_GPIOA
#define DEBUG_USART_RX_PIN GPIO_Pin_10
#define DEBUG_USART_RX_AF GPIO_AF_USART1
#define DEBUG_USART_RX_SOURCE GPIO_PinSource10
#define DEBUG_USART_TX_GPIO_PORT GPIOA
#define DEBUG_USART_TX_GPIO_CLK RCC_AHB1Periph_GPIOA
#define DEBUG_USART_TX_PIN GPIO_Pin_9
#define DEBUG_USART_TX_AF GPIO_AF_USART1
#define DEBUG_USART_TX_SOURCE GPIO_PinSource9
#define DEBUG_USART_IRQHandler USART1_IRQHandler
#define DEBUG_USART_IRQ USART1_IRQn
/************************************************************/
void Debug_USART_Config(void);
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch);
void Usart_SendString( USART_TypeDef * pUSARTx, char *str);
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch);
#endif /* __USART1_H */
bsp_debug_usart.c
#include "./usart/bsp_debug_usart.h"
/**
* @brief 配置嵌套向量中断控制器NVIC
* @param 无
* @retval 无
*/
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
/* 抢断优先级为1 */
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级为1 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief DEBUG_USART GPIO 配置,工作模式配置。115200 8-N-1 ,中断接收模式
* @param 无
* @retval 无
*/
void Debug_USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_AHB1PeriphClockCmd(DEBUG_USART_RX_GPIO_CLK|DEBUG_USART_TX_GPIO_CLK,ENABLE);
/* 使能 USART 时钟 */
RCC_APB2PeriphClockCmd(DEBUG_USART_CLK, ENABLE);
/* GPIO初始化 */
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
/* 配置Tx引脚为复用功能 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_PIN ;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
/* 配置Rx引脚为复用功能 */
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_PIN;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
/* 连接 PXx 到 USARTx_Tx*/
GPIO_PinAFConfig(DEBUG_USART_RX_GPIO_PORT,DEBUG_USART_RX_SOURCE,DEBUG_USART_RX_AF);
/* 连接 PXx 到 USARTx__Rx*/
GPIO_PinAFConfig(DEBUG_USART_TX_GPIO_PORT,DEBUG_USART_TX_SOURCE,DEBUG_USART_TX_AF);
/* 配置串DEBUG_USART 模式 */
/* 波特率设置:DEBUG_USART_BAUDRATE */
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
/* 字长(数据位+校验位):8 */
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
/* 停止位:1个停止位 */
USART_InitStructure.USART_StopBits = USART_StopBits_1;
/* 校验位选择:不使用校验 */
USART_InitStructure.USART_Parity = USART_Parity_No;
/* 硬件流控制:不使用硬件流 */
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
/* USART模式控制:同时使能接收和发送 */
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
/* 完成USART初始化配置 */
USART_Init(DEBUG_USART, &USART_InitStructure);
/* 嵌套向量中断控制器NVIC配置 */
NVIC_Configuration();
/* 使能串口接收中断 */
USART_ITConfig(DEBUG_USART, USART_IT_RXNE, ENABLE);
/* 使能串口 */
USART_Cmd(DEBUG_USART, ENABLE);
}
/***************** 发送一个字符 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
/* 发送一个字节数据到USART */
USART_SendData(pUSARTx,ch);
/* 等待发送数据寄存器为空 */
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
/***************** 发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
unsigned int k=0;
do
{
Usart_SendByte( pUSARTx, *(str + k) );
k++;
} while(*(str + k)!='\0');
/* 等待发送完成 */
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET)
{}
}
/***************** 发送一个16位数 **********************/
void Usart_SendHalfWord( USART_TypeDef * pUSARTx, uint16_t ch)
{
uint8_t temp_h, temp_l;
/* 取出高八位 */
temp_h = (ch&0XFF00)>>8;
/* 取出低八位 */
temp_l = ch&0XFF;
/* 发送高八位 */
USART_SendData(pUSARTx,temp_h);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
/* 发送低八位 */
USART_SendData(pUSARTx,temp_l);
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)
{
/* 发送一个字节数据到串口 */
USART_SendData(DEBUG_USART, (uint8_t) ch);
/* 等待发送完毕 */
while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET);
return (ch);
}
///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)
{
/* 等待串口输入数据 */
while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_RXNE) == RESET);
return (int)USART_ReceiveData(DEBUG_USART);
}
/*串口中断服务函数*/
void DEBUG_USART_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetITStatus(DEBUG_USART,USART_IT_RXNE)!=RESET)
{
ucTemp = USART_ReceiveData( DEBUG_USART );
USART_SendData(DEBUG_USART,ucTemp);
}
}
main.c
#include "stm32f4xx.h"
#include "./usart/bsp_debug_usart.h"
#include "./i2c/bsp_i2c_eeprom.h"
int main(void)
{
uint8_t test_addr = 0x02;
uint8_t test_writeData = 0x27;
uint8_t test_readData =0;
/*初始化USART 配置模式为 115200 8-N-1,中断接收*/
Debug_USART_Config();
/* 发送一个字符串 */
Usart_SendString( DEBUG_USART,"这是一个EEPROM测试实验\n");
printf("这是一个EEPROM测试实验\n");
printf("\r\n初始化I2C\r\n");
EEPROM_GPIO_Config();
EEPROM_I2C_ModeConfig();
printf("\r\nI2C初始化完毕\r\n");
EEPROM_Byte_Write(&test_writeData,test_addr);
test_readData = EEPROM_Byte_Read(test_addr);
printf("test_readData =0x%x",test_readData);
while(1)
{
}
}