STM32之 AT24C16(EEPROM)驱动代码(程序稳定,清晰明了)

AT24C16电路连接
AT24C16电路图

 

第一部分:IIC协议代码头文件(iic.h)

#ifndef IIC_H
#define IIC_H
#include "stm32f10x.h"
#include "sys.h"
#include "delay.h"

#define write 0
#define read  1

//IIC总线地址接口定义
#define IIC_SCL PBout(7)
#define IIC_SDA_OUT PBout(6)
#define IIC_SDA_IN PBin(6)

#define IIC_INPUT_MODE_SET()  {GPIOB->CRL&=0xF0FFFFFF;GPIOB->CRL|=0x08000000;}
#define IIC_OUTPUT_MODE_SET() {GPIOB->CRL&=0xF0FFFFFF;GPIOB->CRL|=0x03000000;}

//函数声明
void IIC_Init(void);
void IIC_START(void);
void IIC_STOP(void);
u8 IIC_GetACK(void);
void IIC_SendAck(u8 ack);
void IIC_WriteOneByte(u8 data);
u8 IIC_ReadOneByte(void);
#endif

第二部分:IIC协议代码(iic.c)

#include "iic.h"
#include "stdio.h"
/*
函数功能: IIC总线初始化
硬件连接:
        SCL-PB7
        SDA-PB6
*/
void IIC_Init(void)
{
	/*1. 开时钟*/
	RCC->APB2ENR|=1<<3; //PB
	RCC->APB2ENR|=1<<0; //开启AFIO时钟
	
	/*2. 配置模式*/
//	AFIO->MAPR |= 0x2<<24;//关闭JTAG-DP,启用SW-DP
	GPIOB->CRL&=0x00FFFFFF;
	GPIOB->CRL|=0x33000000;
	
	/*3. 上拉*/
	GPIOB->ODR|=0x1<<6;
	printf("IIC_Init OK!\n");
}


/*
函数功能: 起始信号
*/
void IIC_START(void)
{
        IIC_OUTPUT_MODE_SET(); //配置输出模式
	IIC_SDA_OUT = 1;       //拉高数据线
	IIC_SCL = 1;           //拉高时钟线
	DelayUs(2);            //延时
	IIC_SDA_OUT = 0;       //产生下降沿
	DelayUs(2);            //延时
	IIC_SCL = 0;           //拉低时钟线//告诉从机,通信开始(主机将要给从机发送数据)。
}

/*
函数功能: 停止信号
*/
void IIC_STOP(void)
{
        IIC_OUTPUT_MODE_SET(); 	//配置输出模式
	IIC_SDA_OUT = 0;        //拉低数据线
	IIC_SCL = 1;            //拉高时钟线
	DelayUs(2);             //延时
	IIC_SDA_OUT = 1;        //产生上升沿
	DelayUs(2);             //延时
}

/*
函数功能:  获取从机发给主机的应答
返回值  :  0表示获取成功,1表示获取失败
目的: 读取总线上一位数据的值。这一位数据的正确值0
*/
u8 IIC_GetACK(void)
{
    u8 cnt=0;
    IIC_INPUT_MODE_SET(); //输入模式
    IIC_SDA_OUT=1; //上拉
    IIC_SCL=0; //告诉从机主机需要数据
    DelayUs(2);
    IIC_SCL=1; //告诉从机主机正在读数据
    while(IIC_SDA_IN) //等待应答
    {
        cnt++;
        DelayUs(1);
        if(cnt>=250)//等待时间过长,产生停止信号,返回1,表示接收应答失败
	{
		IIC_STOP();
		//printf("N0 ACK\n");
		return 1;
	}
    }
    IIC_SCL=0; //告诉从机,主机准备发送数据
    return 0;
}


/*
函数功能: 主机给从机发送应答
函数参数: 1(非应答) 0(应答)
目的: 发送一位数据
*/
void IIC_SendAck(u8 ack)
{
	IIC_OUTPUT_MODE_SET(); 	//配置输出模式
	IIC_SCL = 0;              //拉低时钟线,告诉从机,主机将要发送数据
	if(ack) IIC_SDA_OUT = 1; 
	else IIC_SDA_OUT = 0;     //写应答信号
	DelayUs(2);               //延时
	IIC_SCL = 1;              //拉高时钟线
	DelayUs(2);               //延时
	IIC_SCL = 0;              //拉低时钟线
}


/*
函数功能:  发送一个字节数据
函数参数:  data将要发送数据
*/
void IIC_WriteOneByte(u8 data)
{
	u8 i;
	IIC_OUTPUT_MODE_SET();
	IIC_SCL = 0;	//拉低时钟线, 数据准备发送
	DelayUs(2);		//延时
	for(i=0;i<8;i++)//从高位开始一位一位地传送
	{
		if(data&0x80)IIC_SDA_OUT=1;//送数据口
		else IIC_SDA_OUT=0;
		data<<=1;//移出数据的最高位
		IIC_SCL=1;//拉高时钟线,发送数据
		DelayUs(2);
		IIC_SCL=0;//拉低时钟线,数据发送完毕
		DelayUs(2);
	}
}

/*
函数功能:  读取一个字节数据
返回值  :  读取成功的数据
*/
u8 IIC_ReadOneByte(void)
{
    u8 data=0,i=0;
    IIC_INPUT_MODE_SET();//使能内部上拉,准备读取数据
    for(i=0;i<8;i++)
    {
        IIC_SCL=0;//置时钟线为低,准备接收数据位
        DelayUs(2);//时钟低电平周期大于4.7μs
        IIC_SCL=1;//置时钟线为高使数据线上数据有效,主机开始读数据,从机不能再改变数据了,即改变SDA的电平
        data<<=1;
        if(IIC_SDA_IN)data|=0x01;//读数据 
        DelayUs(2);
    }
    IIC_SCL=0;
    return data;
}

第三部分:AT24C16代码头文件(AT24C16.h)

#ifndef AT24C16_H
#define AT24C16_H
#include <stdio.h>
#include "stm32f10x.h"
#include "iic.h"

#define AT24C16_WRITE_ADDR 0xA0
#define AT24C16_READ_ADDR  0xA1

void AT24C16_WriteOneByte(u8 data,u8 addr);
void AT24C16_ReadData(u16 addr,u16 len,char *p);
void AT24C16_WriteData(u16 addr,u16 len,u8 *p);
void AT24C16_PageWrite(u16 addr,u16 len,u8 *p);

#endif

第四部分:AT24C16代码(AT24C16.c)

#include "at24c16.h"

/*
函数功能:  AT24C16写一个字节
函数参数:
      data:写入的字节数据
      addr:存放位置(0~2048)
*/

void AT24C16_WriteOneByte(u8 data,u8 addr)
{
	IIC_START();
	IIC_WriteOneByte(AT24C16_WRITE_ADDR); //发送设备地址
	IIC_GetACK(); //获取应答
	IIC_WriteOneByte(addr);  //发送存放数据的地址
	IIC_GetACK(); //获取应答
	IIC_WriteOneByte(data);  //发送实际要存放的数据
	IIC_GetACK(); //获取应答
	IIC_STOP();   //发送停止信号
	DelayMs(10);  //等待写完成
}

/*
函数功能: 指定位置读取指定数量的数据
函数参数:
    addr: 从哪里开始读取数据
    len : 读取多长的数据
    *p  : 存放数据的缓冲区

编写程序对AT24C16第100页的第3个字节进行读数据的时候,步骤如下:
1)发送起始信号;
2)发送器件地址0XA6(1010 0110,1010是固定地址,011是页地址的高三位,0表示写操作);
3)发送操作地址0X43(0100 0011,0100是页地址的低四位,0011是页地址偏移量,即第100页内的第三个字节,
4)发送要写的数据,
5)发送终止信号。
(对于AT24C02,直接写设备地址和数据地址)
*/
void AT24C16_ReadData(u16 addr,u16 len,char *p)
{
	u16 i;
	u8 page_addr,page_addr_H,Devicce_Write_Addr,Devicce_Read_Addr;
	page_addr=addr>>4;
	page_addr_H=page_addr>>5;
	Devicce_Write_Addr=AT24C16_WRITE_ADDR + (page_addr_H<<1);
	Devicce_Read_Addr=AT24C16_READ_ADDR+(page_addr_H<<1);
//	printf("Devicce_Write_Addr = 0x%X \n Devicce_Read_Addr = 0x%X \n addr =0x%X \n",Devicce_Write_Addr,Devicce_Read_Addr,addr&0xFF);
	IIC_START();
	IIC_WriteOneByte(Devicce_Write_Addr); //发送设备地址(写)
	IIC_GetACK(); //获取应答
	IIC_WriteOneByte(addr&0xFF); //发送存放数据的地址(即将读取数据的地址)
	IIC_GetACK(); //获取应答
	IIC_START();
	IIC_WriteOneByte(Devicce_Read_Addr); //发送设备地址(读)
	IIC_GetACK(); //获取应答

	for(i=0;i<len;i++)
	{
			p[i]=IIC_ReadOneByte(); //接收数据
			IIC_SendAck(0);  //给从机发送应答
	}
	IIC_SendAck(1);  //给从机发送非应答
	IIC_STOP();      //停止信号
} 


/*
函数功能: 指定位置写入指定数量的数据
函数参数:
    addr: 从哪里开始写数据
    len : 写入多长的数据
    *p  : 存放数据的缓冲区
*/
void AT24C16_WriteData(u16 addr,u16 len,u8 *p)
{
   u8 page_remain=16-addr%16; //得到当前页剩余的字节数量
   if(page_remain>=len)
   {
      page_remain=len;
   }
   while(1)
   {
       AT24C16_PageWrite(addr,page_remain,p);
       if(page_remain==len)break;
       addr+=page_remain;
       p+=page_remain;
       len-=page_remain;
       if(len>=16)page_remain=16;
       else page_remain=len;
   }
}

/*
函数功能:  AT24C16页写函数
函数参数:
    addr: 从哪里开始写数据
    len : 写入多长的数据
    *p  : 存放数据的缓冲区

说明: AT24C16内部有一个页写缓冲器,页地址超出之后会复位到当前页的起始地址。
AT24C16页缓冲器大小:  16个字节
编写程序对AT24C16第100页的第3个字节进行写数据的时候,步骤如下:
1)发送起始信号;
2)发送器件地址0XA6(1010 0110,1010是固定地址,011是页地址的高三位,0表示写操作);
3)发送操作地址0X43(0100 0011,0100是页地址的低四位,0011是页地址偏移量,即第100页内的第三个字节,
4)发送要写的数据,
5)发送终止信号。
(对于AT24C02,直接写设备地址和数据地址)
*/
void AT24C16_PageWrite(u16 addr,u16 len,u8 *p)
{
	u8 i;
	u8 page_addr,page_addr_H,Devicce_Write_Addr,Devicce_Read_Addr;
	page_addr=addr>>4;
	page_addr_H=page_addr>>5;
	Devicce_Write_Addr=AT24C16_WRITE_ADDR + (page_addr_H<<1);
	Devicce_Read_Addr=AT24C16_READ_ADDR+(page_addr_H<<1);
	printf("Devicce_Write_Addr = 0x%X \n Devicce_Read_Addr = 0x%X \n addr =0x%X \n",Devicce_Write_Addr,Devicce_Read_Addr,addr&0xFF);
	IIC_START();
	IIC_WriteOneByte(Devicce_Write_Addr); //发送设备地址
	IIC_GetACK(); //获取应答
	IIC_WriteOneByte(addr&0xFF);  //发送存放数据的地址
	IIC_GetACK(); //获取应答
	for(i=0;i<len;i++)
	{
		 IIC_WriteOneByte(p[i]);  //发送实际要存放的数据
		 IIC_GetACK(); //获取应答
	}
	IIC_STOP();   //发送停止信号
	DelayMs(10);  //等待写完成
}

 

  • 7
    点赞
  • 87
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
STM32F103是一款32位的ARM Cortex-M3处理器,而AT24C16是一款16KB的串行EEPROM存储器。为了实现STM32F103与AT24C16驱动,需要进行以下步骤: 1. 首先,通过I2C总线连接STM32F103与AT24C16。I2C总线是一种常用的串行通信协议,可以实现多个设备之间的通信。在STM32F103上配置相关的IO引脚,并将其设置为I2C模式。 2. 在STM32F103的代码中,首先需要初始化I2C总线,包括设置通信时钟频率、引脚配置和总线的使能。通过I2C总线可以发送读写命令和存储地址到AT24C16。 3. 在读取数据时,首先发送READ命令和要读取的存储地址到AT24C16。然后,AT24C16会在总线上返回读取的数据。STM32F103通过接收数据的方式获取AT24C16返回的数据。 4. 在写入数据时,首先发送WRITE命令和要写入的存储地址到AT24C16。然后,STM32F103通过I2C总线发送要写入的数据到AT24C16AT24C16通过接收数据的方式确认数据是否成功写入。 5. 在代码中,还要实现数据的读写校验和错误处理。例如,可以检查写入后读取的数据是否与预期一致,以确保数据的准确性。 6. 最后,通过编写相应的读写函数,将AT24C16驱动代码集成到STM32F103的应用程序中。可以根据需要,封装更高层次的API函数,简化数据读写的操作。 总之,STM32F103和AT24C16之间的驱动主要涉及I2C总线的配置和数据读写操作,通过正确的配置和代码实现,可以实现STM32F103与AT24C16的正常通信和数据读写。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值