嵌入式设计与开发项目-AT24C02存储器应用程序设计(I2C协议实战应用)

嵌入式设计与开发项目-AT24C02存储器应用程序设计(I2C协议实战应用),第1张

嵌入式设计与开发项目-AT24C02存储器应用程序设计
  • 一、实现的功能
  • 二、根据功能实现代码
    • 1、主文件main.c
    • 2、I2C头文件“i2c.h”
    • 3、I2c源文件“i2c.c”
  • 三、实现功能过程的注意与学习点
    • 1、注意点
    • 2、学习的知识点

一、实现的功能
  • ①往AT24C02存储器写入256个字节数据;
  • ②读取写入AT24C02的字节数据;
  • ③通过串口把AT24C02保存的数据打印输出;
  • 实现效果:![在这里插入图片描述](https://img-blog.csdnimg.cn/
二、根据功能实现代码 1、主文件main.c
#include"key.h"
#include"led.h"
#include"lcd.h"
#include "usart.h"
#include "i2c.h"

unsigned char ucSec;
unsigned char pucBuf1[16],pucBuf2[256];
unsigned long ulTick_ms;


int main(void)
{
	unsigned char i;
	SysTick_Config(72000);	//定时1ms(HCLK = 72MHz)
	KEY_Init();
	LED_Init();
	
	STM3210B_LCD_Init();
	LCD_Clear(Blue);
	LCD_SetBackColor(Blue);
	LCD_SetTextColor(White);
	USART2_Init(9600);
	i2c_init();
	for(i=0;i<16;i++)
		pucBuf1[i] = i+0x30;
	pucBuf1[14] = 0x0d;
	pucBuf1[15] = 0x0a;
	
	for(i=0;i<16;i++)
	{
		i2c_write(pucBuf1,i*16,16);		//页写(最多16Byte)
		LED_Disp(i);
		LCD_DisplayChar(Line5,176,i/10+0x30);
		LCD_DisplayChar(Line5,160,i%10+0x30);
		printf("%02u",i);
	}
	
	i2c_read(pucBuf2,0,255);
	printf("\r\n%s\r\n",pucBuf2);
		
	while(1);
	}
//SysTick 中断处理程序
	void SysTick_Handler(void)
	{
		ulTick_ms++;
		if(ulTick_ms % 1000 ==0)
		ucSec++;
		
	}
		

主函数分析:❤️ ❤️ ❤️

  1. 在使用各个外设前调用初始化函数,例如KEY、LED、LCD、USART2、i2c外设;
  2. 往AT24C02写入16*16个字节(即256个字节),然后读取AT24C02内容并保存到数组中,通过串口打印输出;
2、I2C头文件“i2c.h”
#ifndef  __I2C_H__
#define  __I2C_H__

#include "stm32f10x.h"
void i2c_init(void);		//把IIC的SCL和SDA初始化为推挽输出
void delay1(unsigned int n);	//软件粗略延时

void I2CStart(void);		//IIC开始信号
void I2CStop(void);			//IIC停止信号
void I2CSendAck(void);		//主机发送应答信号
void I2CSendNotAck(void);	//主机发送不应答信号
unsigned char I2CWaitAck(void);		//等待从机应答

void I2CSendByte(unsigned char cSendByte);		//通过IIC发送一个字节
unsigned char I2CReceiveByte(void);		//通过IIC接收一个字节
void i2c_write(unsigned char* pucBuf,unsigned char ucAddr,
	unsigned char ucNum);		//往AT24C02某一个地址ucAddr写入ucNum个字节的数据pucBuf
void i2c_read(unsigned char* pucBuf,unsigned char ucAddr,
	unsigned char ucNum);		//获取AT24C02某一个地址ucAddr的ucNum个字节的数据pucBuf

#endif

简要分析:❤️ ❤️

  1. 底层的初始化引脚模式、I2C启动、I2C停止、I2C主机应答、I2C主机不应答;
  2. 中层的 I2C从机应答、I2C主机发送一个字节的数据、I2C主机接收一个字节的数据;
  3. 高层的给AT24C02某一个某一个地址ucAddr写入ucNum个字节的数据pucBuf、获取AT24C02某一个地址ucAddr的ucNum个字节的数据pucBuf;
3、I2c源文件“i2c.c”
#include "i2c.h"
/** I2C 总线接口 */
#define I2C_PORT GPIOB
#define SDA_Pin	GPIO_Pin_7
#define SCL_Pin GPIO_Pin_6

#define FAILURE 0
#define SUCCESS 1

//配置SDA信号线为输入模式
void SDA_Input_Mode()
{
	GPIO_InitTypeDef GPIO_InitStructure;

	GPIO_InitStructure.GPIO_Pin = SDA_Pin;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;	 

  	GPIO_Init(I2C_PORT, &GPIO_InitStructure);
}

//配置SDA信号线为输出模式
void SDA_Output_Mode()
{
	GPIO_InitTypeDef GPIO_InitStructure;

	GPIO_InitStructure.GPIO_Pin = SDA_Pin;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;

  	GPIO_Init(I2C_PORT, &GPIO_InitStructure);
}

//
void SDA_Output( uint16_t val )
{
	if ( val ) {
		GPIO_SetBits(I2C_PORT,SDA_Pin);
	} else {
		GPIO_ResetBits(I2C_PORT,SDA_Pin);
	}
}

//
void SCL_Output( uint16_t val )
{
	if ( val ) {
		GPIO_SetBits(I2C_PORT,SCL_Pin);
	} else {
		GPIO_ResetBits(I2C_PORT,SCL_Pin);
	}
}

//
uint8_t SDA_Input()
{
	return GPIO_ReadInputDataBit( I2C_PORT, SDA_Pin);
}

//延时程序
void delay1(unsigned int n)
{
	unsigned int i;
	for ( i=0;i<n;++i);
}

//I2C总线启动
void I2CStart(void)
{
	SDA_Output(1);delay1(500);
	SCL_Output(1);delay1(500);
	SDA_Output(0);delay1(500);
	SCL_Output(0);delay1(500);
}

//I2C总线停止
void I2CStop(void)
{
	SCL_Output(0); delay1(500);
	SDA_Output(0); delay1(500);
	SCL_Output(1); delay1(500);
	SDA_Output(1); delay1(500);

}

//等待应答
unsigned char I2CWaitAck(void)
{
	unsigned short cErrTime = 5;
	SDA_Input_Mode(); 
	delay1(500);
	SCL_Output(1);delay1(500);
	while(SDA_Input())
	{
		cErrTime--;
		delay1(500);
		if (0 == cErrTime)
		{
			SDA_Output_Mode();
			I2CStop();
			return FAILURE;
		}
	}
	//修改顺序
	SCL_Output(0);delay1(500); 
	SDA_Output_Mode();
	return SUCCESS;
}

//发送应答位
void I2CSendAck(void)
{
	SDA_Output(0);delay1(500);
	delay1(500);
	SCL_Output(1); delay1(500);
	SCL_Output(0); delay1(500);

}

//
void I2CSendNotAck(void)
{
	SDA_Output(1);
	delay1(500);
	SCL_Output(1); delay1(500);
	SCL_Output(0); delay1(500);

}

//通过I2C总线发送一个字节数据
void I2CSendByte(unsigned char cSendByte)
{
	unsigned char  i = 8;
	while (i--)
	{
		SCL_Output(0);delay1(500); 
		SDA_Output(cSendByte & 0x80); delay1(500);
		cSendByte += cSendByte;			//左移一位
		delay1(500); 
		SCL_Output(1);delay1(500); 
	}
	SCL_Output(0);delay1(500); 
}

//从I2C总线接收一个字节数据
unsigned char I2CReceiveByte(void)
{
	unsigned char i = 8;
	unsigned char cR_Byte = 0;
	SDA_Input_Mode(); 
	while (i--)
	{
		cR_Byte += cR_Byte;			//左移一位
		SCL_Output(0);delay1(500); 
		delay1(500); 
		SCL_Output(1);delay1(500); 
		cR_Byte |=  SDA_Input(); 
	}
	SCL_Output(0);delay1(500); 
	SDA_Output_Mode();
	return cR_Byte;
}

//I2C总线初始化
void i2c_init()
{
	GPIO_InitTypeDef GPIO_InitStructure;

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);

	GPIO_InitStructure.GPIO_Pin = SDA_Pin | SCL_Pin;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;	 // **

  	GPIO_Init(I2C_PORT, &GPIO_InitStructure);

}

//AT24C02
void i2c_read(unsigned char* pucBuf,unsigned char ucAddr,
	unsigned char ucNum)
	{
		I2CStart();
		I2CSendByte(0xa0);
		I2CWaitAck();
		
		I2CSendByte(ucAddr);
		I2CWaitAck();
		
		I2CStart();
		I2CSendByte(0xa1);
		I2CWaitAck();
		
		while(ucNum--)
		{
			*pucBuf++ = I2CReceiveByte();
			if(ucNum)
				I2CSendAck();
			else
				I2CSendNotAck();
		}
		I2CStop();
	}
	
	//AT24C02写
	void i2c_write(unsigned char* pucBuf,unsigned char ucAddr,
	unsigned char ucNum)
	{
		I2CStart();
		I2CSendByte(0xa0);
		I2CWaitAck();
		
		I2CSendByte(ucAddr);
		I2CWaitAck();
		
		while(ucNum--)
		{
			I2CSendByte(*pucBuf++);
			I2CWaitAck();
		}
		I2CStop();
		delay1(500);
	}

简要分析:❤️ ❤️

  1. 先把SCL和SDA初始化为推挽输出模式,同时编写SDA设置为推挽输出和下拉输入模式的初始化函数;
  2. 封装SCK和SDA输出高低电平的函数,还得编写SDA的接收高低电平,方便接收从机的接收信号;
  3. 根据I2C的通信原理编写开始、停止、应答等函数,IIC原理可参考嵌入式物联网常用的通信协议UART、RS-232、RS-422、RS-485、CAN、IIC、SPI;
  4. SCL时钟线和SDA信号线一般设置4us的延时,定义一个粗劣的延时就行,这个延时主要影响的是IIC通信的速率;
  5. 通过I2C总线发送一个字节数据I2CSendByte(unsigned char cSendByte);
  • 先把SCL时钟线拉低,然后判断cSendByte字节的第8位与1进行&(与运算)等于1,如果是则SDA输出高电平1,否则输出低电平0;
  • 然后左移一位判断cSendByte的第七位,SCL输出高电平,锁存SDA数据,发送SDA的数据;
  • 重复以上步骤,直到把8位数据发送完。

  • 注意:IIC发送时,字节数据的高位在前,低位在后!
  1. 从I2C总线接收一个字节数据unsigned char I2CReceiveByte(void);函数
  • 定义两个char类型的变量,i记录接收的位数,cR_Byte接收IIC的每一位数据;
  • 把SDA数据线设置为下拉输入模式,如果从机或主机不应答,SDA会接收到高电平;
  • 轮询8次获取一个字节的数据,SCL先发送低电平0,等待大概8us应答信号,然后SCL输出高电平获取SDA的电平状态,并把数据保存到变量的cR_Byte中;
  • 最后cR_Byte左移一位,继续重复以上步骤;
  1. AT24C02的接收驱动函数i2c_read(unsigned char* pucBuf,unsigned char ucAddr,unsigned char ucNum)
  • 开始IIC通信,主机发送0xA0选择器件地址为A0的AT24C02存储器,等待从机应答;
  • 然后给从机发送寻址地址ucAddr,等待从机应答;
  • 再次开启IIC通信,主机发送0xA1读取从机数据地址的数据,等待从机应答;
  • 主机获取从机发送的数据,并把ucNum个字节的数据保存指针变量中,如果没接收到ucNum个字节数据,则主机发送响应信号,否则发送不响应信号,最后IIC通信停止;
  1. AT24C02存储区的发送驱动函数i2c_write(unsigned char* pucBuf,unsigned char ucAddr,unsigned char ucNum)
  • 开始IIC通信,主机发送0xA0选择器件地址为A0的AT24C02存储器,等待从机应答;
  • 然后给从机发送寻址地址ucAddr,等待从机应答;
  • 主机把ucNum个字节数据pucBuf发送给从机,每发一次数据等待从机响应,接收完成后,停止IIC通信;
三、实现功能过程的注意与学习点 1、注意点
  1. IIC的时钟线SCL和数据线SDA在拉高低电平时,应该设置4us的延时;
  2. 等待应答或者接收从机数据时,应把数据线SDA设置为下拉输入模式,接收完数据后把SDA设置为输出模式;
  3. 发送和接收完数据后,时钟线都拉低电平,防止数据混乱;
2、学习的知识点
  1. 根据IIC通信协议的原理,编写IIC的驱动代码,然后编写AT24C02应用函数,加深对IIC的理解与应用;
  2. 掌握I2C的启动、停止、发送数据、响应所对应SCL、SDA高低电平的变换;
  3. 学会主机和从机不应答,SDA会显示高电平;
  4. 通过I2C协议,实现AT24C02数据存取 *** 作;
  5. 数据左移一位等于 * 2,右移一位等于 / 2;
    ❤️ ❤️ ❤️ ❤️ ❤️ ❤️

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/674371.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-19
下一篇 2022-04-19

发表评论

登录后才能评论

评论列表(0条)

保存