day20:stm32向eeprom写入小数和长整型

程序继承上一节内容:https://www.cnblogs.com/josephcnblog/articles/9220312.html

工程结构:

 

程序清单:

 bsp_usart.h

#ifndef __BSP_USART_H__
#define __BSP_USART_H__

#include "stm32f10x.h"
#include "stdio.h"

// ----------------------- 串口1-USART1
// 使用哪个串口(串口1..5)
#define  DEBUG_USARTx                   USART1					
// APB2串口的同步时钟
#define  DEBUG_USART_CLK                RCC_APB2Periph_USART1	
// APB2系统时钟(因为串口USART1是挂载到APB2总线上的,所以要打开APB2总线的时钟)
#define  DEBUG_USART_APBxClkCmd         RCC_APB2PeriphClockCmd	
// 串口通信的波特率
#define  DEBUG_USART_BAUDRATE           19200


// ----------------------- USART GPIO 引脚宏定义
// GPIO引脚
#define  DEBUG_USART_GPIO_CLK           (RCC_APB2Periph_GPIOA)	
// APB2系统时钟(因为串口USART1是挂载到APB2总线上的,所以要打开APB2总线的时钟)
#define  DEBUG_USART_GPIO_APBxClkCmd    RCC_APB2PeriphClockCmd	

// GPIO引脚,发送接PA9,接收接PA10   
#define  DEBUG_USART_TX_GPIO_PORT       GPIOA   		
#define  DEBUG_USART_TX_GPIO_PIN        GPIO_Pin_9
#define  DEBUG_USART_RX_GPIO_PORT       GPIOA
#define  DEBUG_USART_RX_GPIO_PIN        GPIO_Pin_10

#define  DEBUG_USART_IRQ                USART1_IRQn
#define  DEBUG_USART_IRQHandler         USART1_IRQHandler

/* 串口调试配置函数:配置串口的相关参数,使能串口 */
void DEBUG_USART_Config(void);

/* 发送一个字节 */
void Usart_SendByte(USART_TypeDef* pUSARTx, uint8_t ch);

/* 发送字符串 */
void Usart_SendString(USART_TypeDef* pUSARTx, char* str);

/* 串口中断配置函数 */
static void NVIC_Configuration(void);

#endif		/* __BSP_USART_H__ */

 bsp_usart.c

#include "./usart/bsp_usart.h"

/* 串口中断配置函数 */

static void NVIC_Configuration(void)
{
	NVIC_InitTypeDef NVIC_InitStructure;
  
	/* 嵌套向量中断控制器组选择 */
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
  
	/* 配置USART为中断源 */
	NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
	/* 抢断优先级*/
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	/* 子优先级 */
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	/* 使能中断 */
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	
	/* 初始化配置NVIC */
	NVIC_Init(&NVIC_InitStructure);
}

/* 串口调试配置函数:配置串口的相关参数,使能串口 */
void DEBUG_USART_Config(void)
{
	/* 结构体变量声明 */
	GPIO_InitTypeDef GPIO_InitStructure;		// GPIO
	USART_InitTypeDef USART_InitStructure;		// USART
	
	/* ------------ 第一步:初始化GPIO */
	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);	
	
	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;		// 引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;				// 模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;			// 速率
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);	// 初始化结构体
	
	// 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	/* ------------ 第二步:配置串口的初始化结构体 */
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
	/* 配置串口的工作参数 */
	// 波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 停止位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 校验位
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	// 工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USARTx, &USART_InitStructure);
	
	/* -------------------------------------------------------- */
	// 串口中断优先级配置
	NVIC_Configuration();	
	
	// 使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
	/* -------------------------------------------------------- */
	
	/* ------------ 第三步:使能串口 */
	USART_Cmd(DEBUG_USARTx, 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);
}

/* 重定向c库函数printf到串口,重定向后可使用printf函数 */
int fputc(int ch, FILE *f)
{
	/* 发送一个字节数据到串口 */
	USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
	/* 等待发送完毕 */
	while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
	return (ch);
}

/* 重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数 */
int fgetc(FILE *f)
{
	/* 等待串口输入数据 */
	while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

	return (int)USART_ReceiveData(DEBUG_USARTx);
}

 bsp_i2c_gpio.h

#ifndef __BSP_I2C_H__
#define __BSP_I2C_H__

#include "stm32f10x.h"

/* I2C的GPIO端口定义:SCL接PA2,SDA接PA3 */
#define I2C_SCL_GPIO_PORT			GPIOA					// A端口
#define I2C_SCL_GPIO_PIN			GPIO_Pin_2				// 引脚2
#define I2C_SCL_GPIO_CLK			RCC_APB2Periph_GPIOA	// 时钟:PA2挂在到APB2总线上

#define I2C_SDA_GPIO_PORT			GPIOA					// A端口
#define I2C_SDA_GPIO_PIN			GPIO_Pin_3				// 引脚3
#define I2C_SDA_GPIO_CLK			RCC_APB2Periph_GPIOA	// 时钟:PA3挂在到APB2总线上

/* EEPROM的引脚高低电平设置 */
#define EEPROM_I2C_SCL_1()			GPIO_SetBits(I2C_SCL_GPIO_PORT, I2C_SCL_GPIO_PIN)		// 设置SCL引脚为高电平
#define EEPROM_I2C_SCL_0()			GPIO_ResetBits(I2C_SCL_GPIO_PORT, I2C_SCL_GPIO_PIN)		// 设置SCL引脚为低电平
#define EEPROM_I2C_SDA_1()			GPIO_SetBits(I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PIN)		// 设置SDA引脚为高电平
#define EEPROM_I2C_SDA_0()			GPIO_ResetBits(I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PIN)		// 设置SDA引脚为低电平

/* STM32读取EEPROM设备的数据 */
#define EEPROM_I2C_SDA_READ()		GPIO_ReadInputDataBit(I2C_SDA_GPIO_PORT, I2C_SDA_GPIO_PIN)


/* I2C的GPIO端口初始化 */
void I2C_GPIO_CONFIG(void);

/* I2C产生起始信号 */
void I2C_START(void);

/* I2C产生结束信号 */
void I2C_STOP(void);

/* 产生应答信号 */
void I2C_ASK(void);

/* STM32读EEPROM的数据时,EEPROM产生非应答信号 */
void I2C_NO_ASK(void);

/* 等待EEPROM的应答信号:应答置0,非应答置1 */
uint8_t I2C_WAIT_ASK(void);

/* STM32写一个字节数据到EEPROM */
void I2C_WRITE_BYTE(uint8_t data);

/* STM32读EEPROM的一个字节 */
uint8_t I2C_READ_BYTE(void);

#endif	/* __BSP_I2C_H__ */

 bsp_i2c_gpio.c

#include "./i2c/bsp_i2c_gpio.h"

/* 延迟时间 */
static void i2c_Delay(void)
{
	uint8_t i;

	/* 
	下面的时间是通过"逻辑分析仪"测试得到的。
    工作条件:CPU主频72MHz ,MDK编译环境,1级优化
  
	循环次数为10时,SCL频率 = 205KHz 
	循环次数为7时,SCL频率 = 347KHz, SCL高电平时间1.5us,SCL低电平时间2.87us 
	循环次数为5时,SCL频率 = 421KHz, SCL高电平时间1.25us,SCL低电平时间2.375us 
	*/
	for (i = 0; i < 10; i++);
}

/* I2C的GPIO端口初始化 */
void I2C_GPIO_CONFIG(void)
{
	/* GPIO结构体 */
	GPIO_InitTypeDef GPIO_InitStructure;   
    
	/* 打开时钟 */
    RCC_APB2PeriphClockCmd(I2C_SCL_GPIO_CLK | I2C_SDA_GPIO_CLK, ENABLE);
    
	/* 实例化SCL结构体 */
    GPIO_InitStructure.GPIO_Pin = I2C_SCL_GPIO_PIN;     // SCL引脚     
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;	// 开漏输出模式
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;   // I2C一般最大为400k/s,50MHZ足够了
    
	/* 初始化SCL */
	GPIO_Init(I2C_SCL_GPIO_PORT, &GPIO_InitStructure);	
	
	/* 实例化SDA结构体 */
    GPIO_InitStructure.GPIO_Pin = I2C_SDA_GPIO_PIN;  	         
	
	/* 初始化SDA */
    GPIO_Init(I2C_SDA_GPIO_PORT, &GPIO_InitStructure);	
}

/* I2C产生起始信号:根据信号图来写 */
void I2C_START(void)
{
	EEPROM_I2C_SDA_1();
	EEPROM_I2C_SCL_1();
	i2c_Delay();
	
	EEPROM_I2C_SDA_0();
	i2c_Delay();
	EEPROM_I2C_SCL_0();
	i2c_Delay();
}

/* I2C产生结束信号:根据时序图来写 */
void I2C_STOP(void)
{
	EEPROM_I2C_SDA_0();
	EEPROM_I2C_SCL_1();
	i2c_Delay();
	
	EEPROM_I2C_SDA_1();
	i2c_Delay();
}

/* STM32读EEPROM的数据时,EEPROM产生应答信号 */
void I2C_ASK(void)
{
	EEPROM_I2C_SDA_0();
	EEPROM_I2C_SCL_1();
	i2c_Delay();
	
	EEPROM_I2C_SCL_0();
	i2c_Delay();

	/*
	释放SDA总线控制权,如果不置1,即拉成高阻态,则SDA为低电平0,
	因为SDA线为低电平比高电平优先,所以这条线一直都是低电平,
	那么其他EEPROM设备就无法接入SCL线
	*/
	EEPROM_I2C_SDA_1();
	i2c_Delay();
}

/* STM32读EEPROM的数据时,EEPROM产生非应答信号 */
void I2C_NO_ASK(void)
{
	EEPROM_I2C_SDA_1();
	EEPROM_I2C_SCL_1();
	i2c_Delay();
	
	EEPROM_I2C_SCL_0();
	i2c_Delay();
}

/* 等待EEPROM的应答信号:应答置0,非应答置1 */
uint8_t I2C_WAIT_ASK(void)
{
	uint8_t reply;
	
	// 释放SDA线的控制权
	EEPROM_I2C_SDA_1();
	
	EEPROM_I2C_SCL_1();
	i2c_Delay();
	
	// 判断是应答信号还是非应答信号:看时序图
	if(EEPROM_I2C_SDA_READ()==1)		// 非应答
	{
		reply = 1;
	}
	else								// 应答
	{
		reply = 0;
	}
	
	// SCL拉低
	EEPROM_I2C_SCL_0();
	i2c_Delay();
	
	// 返回信号
	return reply;	
}

/* STM32写一个字节数据到EEPROM */
void I2C_WRITE_BYTE(uint8_t data)
{
	uint8_t i;
	
	// 每次发送一位,循环8次,将一个字节发送完,先发送最高位,后发送低位
	for(i=0; i<8; i++)
	{
		/*
		比如:data = 0101 0010,0x80 = 1000 0000,则:
		第一次循环:data & 0x80 = 0000 0000,为0,执行:EEPROM_I2C_SDA_0();发送0
		data <<= 1;之后,data = 1010 0100
		第二次循环:data & 0x80 = 1000 0000,为1,执行:EEPROM_I2C_SDA_1();发送1
		data <<= 1;之后,data = 0100 1000
		第三次循环:data & 0x80 = 0000 0000,为0,执行:EEPROM_I2C_SDA_0();发送0
		data <<= 1;之后,data = 1001 0000
		第四次循环:data & 0x80 = 1000 0000,为1,执行:EEPROM_I2C_SDA_1();发送1
		data <<= 1;之后,data = 0010 0000
		第五次循环:data & 0x80 = 0000 0000,为0,执行:EEPROM_I2C_SDA_0();发送0
		data <<= 1;之后,data = 0100 0000
		第六次循环:data & 0x80 = 0000 0000,为0,执行:EEPROM_I2C_SDA_0();发送0
		data <<= 1;之后,data = 1000 0000
		第七次循环:data & 0x80 = 1000 0000,为1,执行:EEPROM_I2C_SDA_1();发送1
		data <<= 1;之后,data = 0000 0000
		第八次循环:data & 0x80 = 0000 0000,为0,执行:EEPROM_I2C_SDA_0();发送0
		经过八次循环之后,发送的数据为:0101 0010,就是一开始要发送的数据data的值
		*/
		if(data & 0x80)
		{
			EEPROM_I2C_SDA_1();
		}
		else
		{
			EEPROM_I2C_SDA_0();
		}
		// 每次都延迟,相当于事先将数据准备好,再产生时钟时序,这样数据发送稳定一点
		i2c_Delay();
		
		// 每次循环,SCL都产生一个0/1方波,表示在一个时钟周期内发送数据
		EEPROM_I2C_SCL_1();
		i2c_Delay();	
		EEPROM_I2C_SCL_0();
		i2c_Delay();
		
		// 将刚刚发送的位移掉
		data <<= 1;
		
		// 判断如果发送完数据了,就要释放SDA线的控制权
		if(i == 7)
		{
			EEPROM_I2C_SDA_1();
		}
	}
}

/* STM32读EEPROM的一个字节 */
uint8_t I2C_READ_BYTE(void)
{
	uint8_t i;
	uint8_t temp = 0;
	
	// 每次发送一位,循环8次,将一个字节发送完,先发送最高位,后发送低位
	for(i=0; i<8; i++)
	{
		// 在每次开始之前先将最后一位腾出位置
		temp <<= 1;
		
		// 时钟开始
		EEPROM_I2C_SCL_1();
		i2c_Delay();
		
		/* 在时钟为高电平时开始读取数据 */
		/* 分析过程和写过程类似 */
		if(EEPROM_I2C_SDA_READ() == 1)
		{
			temp += 1;
		}
		// 每次都延迟,相当于事先将数据准备好,再产生时钟时序,这样数据发送稳定一点
		i2c_Delay();	
	
		// 时钟结束
		EEPROM_I2C_SCL_0();
		i2c_Delay();
	}
	
	// 返回读取的数据
	return temp;
}

 bsp_i2c_eeprom.h

#ifndef __BSP_I2C_EEPROM_H__
#define __BSP_I2C_EEPROM_H__

#include "stm32f10x.h"

/* EEPROM的读写方向位 */
#define EEPROM_WRITE_ADDR			0xA0
#define EERPOM_READ_ADDR			0xA1

/* 检测EEPROM是否正常工作 */
uint8_t EEPROM_CHECK_DEVICE(uint8_t addr);

/* 向EEPROM写入一个字节数据 */
uint8_t EEPROM_WRITE_BYTES(uint8_t w_addr, uint8_t *data, uint16_t size);

/* 从EEPROM读取多个字节数据 */
uint8_t EEPROM_READ_BYTES(uint8_t r_addr, uint8_t *data, uint16_t size);

/* 等待EEPROM内部时序完成 */
uint8_t EEPROM_WAIT_STANDPY(void);

#endif	/* __BSP_I2C_EEPROM_H__ */

 bsp_i2c_eeprom.c

#include "./i2c/bsp_i2c_eeprom.h"
#include "./i2c/bsp_i2c_gpio.h"

/* 
检测EEPROM是否正常工作 
addr:EEPROM的设备地址
返回1:未检测到EEPROM
返回0:检测到EEPROM
*/
uint8_t EEPROM_CHECK_DEVICE(uint8_t addr)
{
	// 响应结果的返回值
	uint8_t result;
	
	// 产生起始信号
	I2C_START();
	
	// 发送EEPROM的设备地址
	I2C_WRITE_BYTE(addr);
	
	// 判断EEPROM是否响应
	if(I2C_WAIT_ASK())
	{
		result = 1;		// 没有响应
	}
	else
	{
		result = 0;		// 响应
	}
	
	// 不响应
	I2C_NO_ASK();
	
	// 产生结束信号
	I2C_STOP();
	
	return result;
}

/* 
等待EEPROM内部时序完成 
返回1:超时
返回0:完成
*/
uint8_t EEPROM_WAIT_STANDPY(void)
{
	uint16_t cycle = 0;
	while(EEPROM_CHECK_DEVICE(EEPROM_WRITE_ADDR))
	{
		cycle++;
		if(cycle > 10000)
		{
			return 1;
		}
	}
	return 0;
}

/* 
向EEPROM写入多个字节数据
w_addr:EEPROM的存储单元格地址
data:要写入EEPROM的数据
size:要写入多少个字节(EEPROM一次最多只能写入8个字节)
返回1:成功
返回0:失败
*/
uint8_t EEPROM_WRITE_BYTES(uint8_t w_addr, uint8_t *data, uint16_t size)
{
	uint16_t i;
	
	// 写入多个字节数据
	for(i=0; i<size; i++)
	{	
		// 每写入8个字节就产生一次起始信号(因为EEPROM一次最多只能发送8个字节,需要发送超过八个字节就需要多次循环发送起始信号)
		if(i==0 || (w_addr)%8==0)
		{
			// 产生结束信号
			I2C_STOP();
			
			// 在写完数据之后,给EEPROM足够的反应时间
			if(EEPROM_WAIT_STANDPY())
			{
				// 如果超时了,就直接结束
				goto w_fail;
			}
		
			// 发送起始信号
			I2C_START();
		
			// 发送EEPROM的设备地址
			I2C_WRITE_BYTE(EEPROM_WRITE_ADDR);
			
			// 判断EEPROM是否响应
			if(I2C_WAIT_ASK())
			{
				goto w_fail;		// 没有响应
			}
			else
			{
				// 发送要写入的存储单元格地址
				I2C_WRITE_BYTE(w_addr);
				
				// 发送完存储单元格地址,判断EEPROM是否响应
				if(I2C_WAIT_ASK())		// 没有响应
				{
					goto w_fail;
				}
			}
		}
			
		// 响应,发送要写入EEPROM的数据
		I2C_WRITE_BYTE(*data);
		
		// 发送完数据继续检测是否响应
		if(I2C_WAIT_ASK())		// 没有响应
		{
			// 写入失败执行
			goto w_fail;		
		}
		
		// 指针指向下一个数据
		data++;
		w_addr++;	// 存储单元格地址自增
	}
	
	
	// 如果要写入的数据不到size个,也产生一次结束信号,保证稳定
	I2C_STOP();
	// 在写完数据之后,给EEPROM足够的反应时间
	if(EEPROM_WAIT_STANDPY())
	{
		// 如果超时了,就直接结束
		goto w_fail;
	}
	
	return 1;

// 写入失败执行
w_fail:
	I2C_STOP();
	return 0;
}

/* 
从EEPROM读取多个字节数据(看单片机资料的时序图来写程序)
r_addr:EEPROM的存储单元格地址
*data:从EEPROM读取到的数据放到一个指针里面,在调用处直接获取指针地址就可以把数据拿出来
size:读取多少个字节
返回1:成功
返回0:失败
*/
uint8_t EEPROM_READ_BYTES(uint8_t r_addr, uint8_t *data, uint16_t size)
{
	// 在开始之前先延迟,给EEPROM足够的反应时间
	if(EEPROM_WAIT_STANDPY())
	{
		// 如果超时了,就直接结束
		goto r_fail;
	}
	
	// 产生起始信号
	I2C_START();
	
	// 第一次:发送EEPROM的设备地址,写方向的设备地址(开始由STM32主动寻址)
	I2C_WRITE_BYTE(EEPROM_WRITE_ADDR);
	
	// 判断EEPROM是否响应
	if(I2C_WAIT_ASK())
	{
		goto r_fail;		// 没有响应
	}
	else
	{
		// 发送要读取的存储单元格地址
		I2C_WRITE_BYTE(r_addr);
		
		// 发送完存储单元格地址,判断EEPROM是否响应
		if(I2C_WAIT_ASK())		// 没有响应
		{
			goto r_fail;
		}
		else					// 响应
		{
			// 第二次发送起始信号
			I2C_START();
			
			// 第二次:发送第二次的设备地址,读方向(第二次由EEPROM主动发数据到STM32)
			I2C_WRITE_BYTE(EERPOM_READ_ADDR);
			
			// 发送完数据继续检测是否响应
			if(I2C_WAIT_ASK())		// 没有响应
			{
				// 读入失败执行
				goto r_fail;		
			}
			else
			{
				uint8_t i;
				// 如果EEPROM有响应,就读取数据
				for(i=0; i<size; i++)
				{
					// 读一个字节数据
					*data = I2C_READ_BYTE();
					
					// 判断是否读完
					if(i == size-1)
					{
						I2C_NO_ASK();	// 读完就产生非应答信号
					}
					else
					{
						// 每读完一个字节数据都要产生一个应答信号
						I2C_ASK();
						data++;
					}
				}
				
			}
		}	
	}
	
	// 产生结束信号
	I2C_STOP();
	
	/* 读取没有内部时序,所以不需要延迟
	if(EEPROM_WAIT_STANDPY())
	{
		// 如果超时了,就直接结束
		goto r_fail;
	}
	*/	
	return 1;

// 写入失败执行
r_fail:
	I2C_STOP();
	return 0;
}

bsp_i2c_eeprom_decimal.h

#ifndef __BSP_I2C_EEPROM_DECIMAL_H__
#define __BSP_I2C_EEPROM_DECIMAL_H__

#include "stm32f10x.h"

/* 24xx02的页面大小 */
#define EEPROM_PAGE_SIZE		  	8	
/* 24xx02总容量 */
#define EEPROM_SIZE				  	256			  

/* 从串行EEPROM指定地址处开始读取若干数据 */
uint8_t EEPROM_READ_BYTES_DECIMAL(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize);

/* 向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率 */
uint8_t EEPROM_WRITE_BYTES_DECIMAL(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize);

#endif		/* __BSP_I2C_EEPROM_DECIMAL_H__ */

 bsp_i2c_eeprom_decimal.c

#include "./i2c/bsp_i2c_eeprom_decimal.h"
#include "./i2c/bsp_i2c_gpio.h"
#include "./i2c/bsp_i2c_eeprom.h"


/*
*********************************************************************************************************
*	函 数 名: ee_ReadBytes
*	功能说明: 从串行EEPROM指定地址处开始读取若干数据
*	形    参:_usAddress : 起始地址
*			 _usSize : 数据长度,单位为字节
*			 _pReadBuf : 存放读到的数据的缓冲区指针
*	返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t EEPROM_READ_BYTES_DECIMAL(uint8_t *_pReadBuf, uint16_t _usAddress, uint16_t _usSize)
{
	uint16_t i;
	
	/* 采用串行EEPROM随即读取指令序列,连续读取若干字节 */
	
	/* 第1步:发起I2C总线启动信号 */
	I2C_START();
	
	/* 第2步:发起控制字节 */
	I2C_WRITE_BYTE(EEPROM_WRITE_ADDR);	/* 此处是写指令 */
	 
	/* 第3步:等待ACK */
	if (I2C_WAIT_ASK() != 0)
	{
		goto cmd_fail;	/* EEPROM器件无应答 */
	}

	/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
	I2C_WRITE_BYTE((uint8_t)_usAddress);
	
	/* 第5步:等待ACK */
	if (I2C_WAIT_ASK() != 0)
	{
		goto cmd_fail;	/* EEPROM器件无应答 */
	}
	
	/* 第6步:重新启动I2C总线。前面的代码的目的向EEPROM传送地址,下面开始读取数据 */
	I2C_START();
	
	/* 第7步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
	I2C_WRITE_BYTE(EERPOM_READ_ADDR);	/* 此处是读指令 */
	
	/* 第8步:发送ACK */
	if (I2C_WAIT_ASK() != 0)
	{
		goto cmd_fail;	/* EEPROM器件无应答 */
	}	
	
	/* 第9步:循环读取数据 */
	for (i = 0; i < _usSize; i++)
	{
		_pReadBuf[i] = I2C_READ_BYTE();	/* 读1个字节 */
		
		/* 每读完1个字节后,需要发送Ack, 最后一个字节不需要Ack,发Nack */
		if (i != _usSize - 1)
		{
			I2C_ASK();	/* 中间字节读完后,CPU产生ACK信号(驱动SDA = 0) */
		}
		else
		{
			I2C_NO_ASK();	/* 最后1个字节读完后,CPU产生NACK信号(驱动SDA = 1) */
		}
	}
	/* 发送I2C总线停止信号 */
	I2C_STOP();
	return 1;	/* 执行成功 */

cmd_fail: /* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
	/* 发送I2C总线停止信号 */
	I2C_STOP();
	return 0;
}

/*
*********************************************************************************************************
*	函 数 名: ee_WriteBytes
*	功能说明: 向串行EEPROM指定地址写入若干数据,采用页写操作提高写入效率
*	形    参:_usAddress : 起始地址
*			 _usSize : 数据长度,单位为字节
*			 _pWriteBuf : 存放读到的数据的缓冲区指针
*	返 回 值: 0 表示失败,1表示成功
*********************************************************************************************************
*/
uint8_t EEPROM_WRITE_BYTES_DECIMAL(uint8_t *_pWriteBuf, uint16_t _usAddress, uint16_t _usSize)
{
	uint16_t i,m;
	uint16_t usAddr;
	
	/* 
	写串行EEPROM不像读操作可以连续读取很多字节,每次写操作只能在同一个page。
	对于24xx02,page size = 8
	简单的处理方法为:按字节写操作模式,每写1个字节,都发送地址
	为了提高连续写的效率: 本函数采用page wirte操作。
	*/

	usAddr = _usAddress;	
	for (i = 0; i < _usSize; i++)
	{
		/* 当发送第1个字节或是页面首地址时,需要重新发起启动信号和地址 */
		if ((i == 0) || (usAddr & (EEPROM_PAGE_SIZE - 1)) == 0)
		{
			/* 第0步:发停止信号,启动内部写操作 */
			I2C_STOP();
			
			/* 通过检查器件应答的方式,判断内部写操作是否完成,一般小于 10ms 	
			
			  CLK频率为200KHz时,查询次数为30次左右	
			  原理同 ee_WaitStandby 函数,但该函数检查完成后会产生停止信号,不适用于此处				
			*/
			for (m = 0; m < 1000; m++)
			{				
				/* 第1步:发起I2C总线启动信号 */
				I2C_START();
				
				/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
				I2C_WRITE_BYTE(EEPROM_WRITE_ADDR);	/* 此处是写指令 */
				
				/* 第3步:发送一个时钟,判断器件是否正确应答 */
				if (I2C_WAIT_ASK() == 0)
				{
					break;
				}
			}
			if (m  == 1000)
			{
				goto cmd_fail;	/* EEPROM器件写超时 */
			}
		
			/* 第4步:发送字节地址,24C02只有256字节,因此1个字节就够了,如果是24C04以上,那么此处需要连发多个地址 */
			I2C_WRITE_BYTE((uint8_t)usAddr);
			
			/* 第5步:等待ACK */
			if (I2C_WAIT_ASK() != 0)
			{
				goto cmd_fail;	/* EEPROM器件无应答 */
			}
		}
	
		/* 第6步:开始写入数据 */
		I2C_WRITE_BYTE(_pWriteBuf[i]);
	
		/* 第7步:发送ACK */
		if (I2C_WAIT_ASK() != 0)
		{
			goto cmd_fail;	/* EEPROM器件无应答 */
		}

		usAddr++;	/* 地址增1 */		
	}
	
	/* 命令执行成功,发送I2C总线停止信号 */
	I2C_STOP();
	
	//等待最后一次EEPROM内部写入完成
	if(EEPROM_WAIT_STANDPY() == 1) //等于1表示超时
		goto cmd_fail;
	
	return 1;

/* 命令执行失败后,切记发送停止信号,避免影响I2C总线上其他设备 */
cmd_fail: 
	/* 发送I2C总线停止信号 */
	I2C_STOP();
	return 0;
}

 main.c

/* 
I2C的初始化工程:
包括:端口与引脚的定义、起始信号、终止信号、应答与非应答、读写输入函数等等I2C通信的支持函数
*/

#include "stm32f10x.h"
#include "./usart/bsp_usart.h"
#include "./i2c/bsp_i2c_gpio.h" 
#include "./i2c/bsp_i2c_eeprom.h"
#include "./i2c/bsp_i2c_eeprom_decimal.h"

int main(void)
{
	/* 写入的数据 */
	double w_temp = 564.23;
	
	/* 读取的数据 */
	uint8_t r_temp[sizeof(w_temp)] = {0};
	
	/* USB转串口初始化 */
	DEBUG_USART_Config();
	
	/* I2C初始化 */
	I2C_GPIO_CONFIG();
	
	/* 检测EEPROM是否正常 */
	printf("\r\n\r\n ========== 这是一个EEPROM读写小数和长整数实验 ========== \r\n");
	if(EEPROM_CHECK_DEVICE(EEPROM_WRITE_ADDR) == 0)
	{
		printf("\r\n 检测到EEPROM \r\n");
	}
	else
	{
		printf("\r\n 未检测到EEPROM \r\n");
	}
	
	/* 写 */
	EEPROM_WRITE_BYTES_DECIMAL((uint8_t*)&w_temp, 0, sizeof(w_temp));
	printf("\r\n 【写32位浮点型】w_temp = %f \r\n", w_temp);
	
	/* 读 */
	EEPROM_READ_BYTES_DECIMAL(r_temp, 0, sizeof(w_temp));
	printf("\r\n 【读32位浮点型】r_temp = %f \r\n", *(double*)r_temp);
	printf("\r\n 【读32位浮点型+四则运算:加10】r_temp = %f \r\n", *(double*)r_temp+10);
}

实验结果:

 

posted @ 2018-07-02 21:08  半生戎马,共话桑麻、  阅读(1314)  评论(0)    收藏  举报
levels of contents