目录
I2C通信协议
I2C(IIC,Inter-Integrated Circuit),两线式串行总线,由PHILIPS公司开发用于连接微控制器及其外围设备。它是由数据线SDA和时钟SCL构成的串行总线,可发送和接收数据。在CPU与被控IC之间、IC与IC之间进行双向传送,高速IIC总线一般可达400kbps以上。IIC是半双工通信方式。
多主机I2C总线系统结构
I2C协议空闲时都是高电平
I2C协议
空闲状态
I2C总线总线的SDA和SCL两条信号线同时处于高电平时,规定为总线的空闲状态。此时各个器件的输出级场效应管均处在截止状态,即释放总线,由两条信号线各自的上拉电阻把电平拉高。
开始信号停止信号
起始信号:当SCL为高期间, SDA由高到低的跳变;启动信号是种电平跳变时序信号,而不是一个电平信号。
停止信号:当SCL为高期间, SDA由低到高的跳变;停止信号也是一种电平跳变时序信号,而不是一个电平信号。
应答信号ACK
发送器每发送一个字节,就在时钟脉冲9期间释放数据线,由接收器反馈一个应答信号。应签信号为低电平时,规定为有效应答位(ACK简称应答位)表示接收器已经成功地接收了该字节;应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。对于反馈有效应答位ACK的要求是,接收器在第9个时钟脉冲之前的低电平期间将SDA线拉低,并且确保在该时钟的高电平期间为稳定的低电平。如果接收器是主控器,则在它收到最后一个字节后,发送一个NACK信号,以通知被控发送器结束数据发送,并释放SDA线,以便主控接收器发送一个停止信。
数据有效性
I2C总线进行数据传送时,时钟信号为高电平期间,数据线上的数据必须保持稳定,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。
即,数据在SCL的上升沿到来之前就需准备好。并在在下降沿到来之前必须稳定。
数据传输
在I2C总线上传送的每一位数据都有一个时钟脉冲相对应(或同步控制),即在SCL串行时钟的配合下,在SDA上逐位地串行传送每一位数据。数据位的传输是边沿触发。
I2C设备地址
I2C设备地址一般为8位,最高位7bit位为1时代表读,为0时代表写操作,0~6bit位为从设备地址。
I2C通讯整个过程
写数据
1.主机检测到总线空闲状态下,发送开始信号。
2.主机发送从机地址(8bit)。
3.主机发送地址后,总线上设备将会与自己地址比较,相同则发送应答信号(ACK),确定发送器和接收器。
5.发送器(此时是主机)在发送一个数据(要写到哪里),等待应答。
6.发送器(此时是主机)收到应答信号后开始发送一个字节。
7.接收器(此时是从机)接收完成后发发送一个应答信号(ACK);
8.循环6、7步骤知道数据所有数据发送完成。
9.发送器(此时是主机)发送最后一个字节完成后,收到接收器(此时是从机)应答后,会发送停止信号(告诉接收器此次通讯结束),释放总线。
读数据
1.主机检测到总线空闲状态下,发送开始信号。
2.主机发送从机地址(8bit)写。
3.主机发送地址后,总线上设备将会与自己地址比较,相同则发送应答信号(ACK)
4.主机在发送一个数据(要的读的地址),收到从机应答(ACK)。
5.主机收到应答后重新发送开始信号,然后发送设备地址改为读数据,确定发送器和接收器。
6.发送器(此时是从机)开始发送一个字节。
7.接收器(此时是主机)接收完成后发发送一个应答信号(ACK);
8.直到接受器(此时是主机),不想要数据后,接受到最后一个字节后,不会发送应答信号,而是发送一个停止信号,随后释放总线。
硬件连接
EEPROM(24C02)
总容量是256(2048bit/8)个字节。接口: IIC
24C02字节写时序
24C02字节读时序
实验源码
/**
******************************************************************************
* @file : user_rcc_config.c
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "user_rcc_config.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
/*!
\brief RCC配置
\param[in] none
\param[out] none
\retval none
*/
void Rcc_config(void)
{
/*使能GPIOA时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
/*使能GPIOA时钟*/
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB,ENABLE);
/*使能UART1时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
}
/************************************************************** END OF FILE ****/
/**
******************************************************************************
* @file : user_gpio.c
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "user_gpio.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
/*!
\brief GPIO初始化函数
\param[in] none
\param[out] none
\retval none
*/
void Gpio_Init(void)
{
/*GPIO结构体*/
GPIO_InitTypeDef GPIO_InitTypeDefstruct;
/*UART1发送引脚配置*/
GPIO_InitTypeDefstruct.GPIO_Mode = GPIO_Mode_AF_PP;//推挽复用输出
GPIO_InitTypeDefstruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitTypeDefstruct.GPIO_Speed = GPIO_Speed_10MHz;
/*写入结构体到GPIOA*/
GPIO_Init(GPIOA,&GPIO_InitTypeDefstruct);
/*UART1接收引脚配置*/
GPIO_InitTypeDefstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_InitTypeDefstruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitTypeDefstruct.GPIO_Speed = GPIO_Speed_10MHz;
/*写入结构体到GPIOA*/
GPIO_Init(GPIOA,&GPIO_InitTypeDefstruct);
/*软件I2C GPIOB6时钟线, GPIOB7数据线*/
GPIO_InitTypeDefstruct.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;
GPIO_InitTypeDefstruct.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出
GPIO_InitTypeDefstruct.GPIO_Speed = GPIO_Speed_50MHz;
/*写入结构体到GPIOB*/
GPIO_Init(GPIOB, &GPIO_InitTypeDefstruct);
/*默认设置为I2C空闲状态*/
GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);
}
/************************************************************** END OF FILE ****/
/**
******************************************************************************
* @file : user_uart.c
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "user_uart.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
extern uint16_t USART_RX_STA;
extern uint8_t USART_RX_BUF[200];
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
#if 1
#pragma import(__use_no_semihosting)
/*实现Printf代码*/
struct __FILE
{
int handle;
};
FILE __stdout;
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
/*!
\brief UART1初始化
\param[in] none
\param[out] none
\retval none
*/
void Uart1_Init(u32 bound)
{
/*UART结构体*/
USART_InitTypeDef USART_InitTypeDefstruct;
/*UART结构体配置*/
USART_InitTypeDefstruct.USART_BaudRate = bound; //波特率
USART_InitTypeDefstruct.USART_HardwareFlowControl =USART_HardwareFlowControl_None; //不使用硬件流
USART_InitTypeDefstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//发送接收使能
USART_InitTypeDefstruct.USART_Parity = USART_Parity_No; //不使用奇偶校验
USART_InitTypeDefstruct.USART_StopBits = USART_StopBits_1; //1个停止位
USART_InitTypeDefstruct.USART_WordLength = USART_WordLength_8b; //8个数据位
/*写入USART1*/
USART_Init(USART1,&USART_InitTypeDefstruct);
/*使能串口1*/
USART_Cmd(USART1,ENABLE);
}
/*!
\brief UART1中断服务函数
\param[in] none
\param[out] none
\retval none
*/
void USART1_IRQHandler(void)
{
}
/************************************************************** END OF FILE ****/
/**
******************************************************************************
* @file : user_i2c.h
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Define to prevent recursive incluson---------------------------------------*/
#ifndef _USER_I2C_H__
#define _USER_I2C_H__
/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
#include "user_delay.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+12) //0x4001080C
#define GPIOB_ODR_Addr (GPIOB_BASE+12) //0x40010C0C
#define GPIOC_ODR_Addr (GPIOC_BASE+12) //0x4001100C
#define GPIOD_ODR_Addr (GPIOD_BASE+12) //0x4001140C
#define GPIOE_ODR_Addr (GPIOE_BASE+12) //0x4001180C
#define GPIOF_ODR_Addr (GPIOF_BASE+12) //0x40011A0C
#define GPIOG_ODR_Addr (GPIOG_BASE+12) //0x40011E0C
#define GPIOA_IDR_Addr (GPIOA_BASE+8) //0x40010808
#define GPIOB_IDR_Addr (GPIOB_BASE+8) //0x40010C08
#define GPIOC_IDR_Addr (GPIOC_BASE+8) //0x40011008
#define GPIOD_IDR_Addr (GPIOD_BASE+8) //0x40011408
#define GPIOE_IDR_Addr (GPIOE_BASE+8) //0x40011808
#define GPIOF_IDR_Addr (GPIOF_BASE+8) //0x40011A08
#define GPIOG_IDR_Addr (GPIOG_BASE+8) //0x40011E08
//IO方向设置
#define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;} //输入模式
#define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;} //输出模式
//IO操作函数
#define IIC_SCL BIT_ADDR(GPIOB_ODR_Addr,6) //SCL 输出高
#define IIC_SDA BIT_ADDR(GPIOB_ODR_Addr,7) //SDA 输出高
#define READ_SDA BIT_ADDR(GPIOB_IDR_Addr,7) //输入SDA 读取
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
void IIC_Start(void);
void IIC_Stop(void);
uint8_t IIC_Wait_Ack(void);
void IIC_Ack(void);
void IIC_NAck(void);
void IIC_Send_Byte(u8 txd);
u8 IIC_Read_Byte(uint8_t ack);
#endif
/************************************************************** END OF FILE ****/
/**
******************************************************************************
* @file : user_i2c.c
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "user_i2c.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
/*!
\brief I2C开始信号
\param[in] none
\param[out] none
\retval none
*/
void IIC_Start(void)
{
/*数据线配置为输出,为准备开始信号做准备*/
SDA_OUT();
/*数据线空闲*/
IIC_SDA = 1;
IIC_SCL = 1;
/*等待*/
delay_us(4);
/*数据线高,数据线又高变低产生一个开始信号*/
IIC_SDA=0;
/*等待*/
delay_us(4);
/*锁住I2C总线,防止其他设备用总线,准备发送或接受数据*/
IIC_SCL=0;
}
/*!
\brief I2C停止信号
\param[in] none
\param[out] none
\retval none
*/
void IIC_Stop(void)
{
/*数据线配置为输出,为准备开始信号做准备*/
SDA_OUT();
/*数据线强制拉低总线*/
IIC_SCL = 0;
IIC_SDA = 0;
/*等待*/
delay_us(4);
/*释放总线*/
IIC_SCL = 1;
IIC_SDA = 1;
/*等待*/
delay_us(4);
}
/*!
\brief 等待应答信号
\param[in] none
\param[out] none
\retval 1,接收应答失败,0,接收应答成功
*/
uint8_t IIC_Wait_Ack(void)
{
uint8_t ucErrTime=0;
/*SDA设置为输入 */
SDA_IN();
/*释放总线*/
IIC_SDA=1;delay_us(1);
IIC_SCL=1;delay_us(1);
/*等待应答*/
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
/*时钟拉低防止总线被其他用*/
IIC_SCL=0;//时钟输出0
return 0;
}
/*!
\brief 应答信号
\param[in] none
\param[out] none
\retval none
*/
void IIC_Ack(void)
{
/*应答时序准备等待*/
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
/*应答信号时钟线高,数据线低*/
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
/*!
\brief 不应答信号
\param[in] none
\param[out] none
\retval none
*/
void IIC_NAck(void)
{
/*不应答时序准备等待*/
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
/*不应答信号时钟线高,数据线高*/
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
/*!
\brief 发送一个字节
\param[in] 数据
\param[in] none
\retval none
*/
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
/*拉低时钟准备数据传输*/
IIC_SCL=0;
for(t=0;t<8;t++)
{
if((txd&0x80)>>7)
IIC_SDA=1;
else
IIC_SDA=0;
txd<<=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
/*!
\brief 读一个字节
\param[in] ack=1时,发送ACK,ack=0,发送nACK
\param[in] none
\retval none
*/
u8 IIC_Read_Byte(uint8_t ack)
{
uint8_t i,receive=0;
/*SDA设置为输入*/
SDA_IN();
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
/************************************************************** END OF FILE ****/
/**
******************************************************************************
* @file : user_eeprom_24c02.c
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "user_eeprom_24c02.h"
#include "user_uart.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
/* Variables 变量--------------------------------------------------------------*/
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
/*!
\brief 读取一个字节函数
\param[in] 要读的地址
\param[in] none
\retval none
*/
uint8_t AT24C02_ReadOneByte(uint8_t ReadAddr)
{
uint8_t temp=0;
/*开始信号*/
IIC_Start();
/*发送器件地址0XA0,写数据*/
IIC_Send_Byte(0XA0);
/*等待应答*/
IIC_Wait_Ack();
/*发送要读的地址*/
IIC_Send_Byte(ReadAddr);
/*等待应答*/
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0XA1); //进入接收模式
IIC_Wait_Ack();
temp=IIC_Read_Byte(0);
IIC_Stop();//产生一个停止条件
return temp;
}
/*!
\brief 写一个字节函数
\param[in] 要写的地址
\param[in] 要写的数据
\retval none
*/
void AT24C02_WriteOneByte(uint8_t WriteAddr,uint8_t DataToWrite)
{
/*开始信号*/
IIC_Start();
/*发送器件地址0XA0,写数据 */
IIC_Send_Byte(0XA0);
/*等待应答*/
IIC_Wait_Ack();
/*发送要写地址*/
IIC_Send_Byte(WriteAddr);
/*等待应答*/
IIC_Wait_Ack();
/*发送要写数据*/
IIC_Send_Byte(DataToWrite);
/*等待应答*/
IIC_Wait_Ack();
/*产生一个停止条件 */
IIC_Stop();
delay_ms(10);
}
/*!
\brief 检查EEPROM是否正常函数
\param[in] none
\param[in] none
\retval 1:检测失败 0:检测成功
*/
uint8_t AT24C02_Check(void)
{
uint8_t temp;
/*255地址写入0x55*/
AT24C02_WriteOneByte(0,0X20);
/*读出255地址是否,是写入数据*/
temp=AT24C02_ReadOneByte(0);
if(temp==0X20)
{
/*正常*/
return 0;
}
/*不正常*/
return 1;
}
/*!
\brief 连续读取指定地址指定个数据函数
\param[in] 开始读出的地址0~255
\param[in] 存放数据数组首地址
\param[in] 要读出数据的个数
\retval none
*/
void AT24C02_Read(uint8_t ReadAddr,u8 *pBuffer,uint8_t NumToRead)
{
while(NumToRead)
{
*pBuffer++=AT24C02_ReadOneByte(ReadAddr++);
NumToRead--;
}
}
/*!
\brief 连续写指定地址指定个数据函数
\param[in] 开始写的地址0~255
\param[in] 要写数据数组首地址
\param[in] 要写出数据的个数
\retval none
*/
void AT24C02_Write(uint8_t WriteAddr,uint8_t *pBuffer,uint8_t NumToWrite)
{
while(NumToWrite--)
{
AT24C02_WriteOneByte(WriteAddr,*pBuffer);
WriteAddr++;
pBuffer++;
}
}
/************************************************************** END OF FILE ****/
/**
******************************************************************************
* @file : user_mian.h
* @brief : V1.00
******************************************************************************
* @attention
*
******************************************************************************
*/
/* Include 包含---------------------------------------------------------------*/
#include "stm32f10x.h"
#include <stdbool.h>
#include "user_gpio.h"
#include "user_delay.h"
#include "user_rcc_config.h"
#include "user_uart.h"
#include "user_eeprom_24c02.h"
/* Typedef 类型----------------------------------------------------------------*/
/* Define 定义----------------------------------------------------------------*/
/* Macro 宏------------------------------------------------------------------*/
#define SIZE sizeof(Test_Buffer)
/* Variables 变量--------------------------------------------------------------*/
uint8_t Test_Buffer[]={"I2C,Test"};
uint8_t Read_Buffer[SIZE];
/* Constants 常量--------------------------------------------------------------*/
/* Function 函数--------------------------------------------------------------*/
int main(void)
{
uint8_t i;
/*配置系统中断分组为2位抢占2位响应*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/*延时函数初始化*/
delay_init();
/*RCC配置*/
Rcc_config();
/*GPIO初始化*/
Gpio_Init();
/*USART1初始化*/
Uart1_Init(9600);
/*检测EEPROM*/
while(AT24C02_Check()){
printf("没有检测到芯片\r\n");
}
/*死循环*/
while(1){
/*写数据*/
AT24C02_Write(0,(uint8_t*)Test_Buffer,SIZE);
delay_ms(1000);
/*读数据*/
AT24C02_Read(0,Read_Buffer,SIZE);
/*打印*/
for(i = 0; i<SIZE; i++)
{
printf("%c",Read_Buffer[i]);
}
printf("\r\n");
}
}
/************************************************************** END OF FILE ****/