37_软件I2C通信实验

目录

I2C通信协议

多主机I2C总线系统结构

I2C协议

应答信号ACK

数据有效性

数据传输

I2C设备地址

I2C通讯整个过程

硬件连接

EEPROM(24C02)

 24C02字节写时序

24C02字节读时序

实验源码

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 ****/

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值