STM32模拟iic驱动eeprom24c128
- 格式:docx
- 大小:15.36 KB
- 文档页数:9
STM32之EEPROM驱动本⽂介绍如何使⽤STM32标准外设库驱动EEPROM,本例程驱动的EEPROM为AT24C02,通讯协议为IIC,使⽤IO⼝模拟⽅式。
本⽂适合对单⽚机及C语⾔有⼀定基础的开发⼈员阅读,MCU使⽤STM32F103VE系列。
1. EEPROM简介EEPROM全称为EEPROM(Electrically Erasable Programmable Read Only Memory)是电可擦除可编程只读存储器。
虽然名称为只读存储器,但是擦除和写⼊都是直接使⽤电路控制,不需要再使⽤外部设备来擦写,即设备在运⾏过程中即可随时擦除和写⼊。
可以按字节为单位修改数据,⽆需整个芯⽚擦除,且掉电后数据不丢失,⼀般⽤来存储⼀些配置信息,以便系统重新上电的时候加载。
2. 常⽤EEPROM⼀般常⽤的EEPROM为ATMEL公司(已被Microchip收购)的AT24Cxx系列,常⽤容量从1K到64Kbit不等,换算成字节为128到8K Bytes,可以根据项⽬需求和价格综合考虑选型。
3. EEPROM操作说明AT24C02容量为2Kbit,即256Byte,地址范围为0~255,即0~0xFF,使⽤1个字节即可表⽰,因此地址长度为1字节。
3.1. 通讯⽅式IAT24C02使⽤IIC协议跟MCU通讯。
3.2. 设备地址如果仅接⼊⼀个AT24C02,可以将设备的A0、A1、A2引脚全部接⼊低电平,那么此时该设备的地位为0x50,再增加⼀位读写标志位,最终读取操作时地址为0xA1,写⼊操作时地址为0xA0。
3.3. 读取数据读取当前字节:MCU直接发起读操作,设备返回当前字节,当前字节⾃动加1,该操作较少使⽤。
读取指定地址⼀个字节:MCU先向AT24C02写⼊⼀个地址,然后再发起⼀个读操作,AT24C02返回该地址存储的字节。
连续读取:MCU发起读当前字节,或者读指定地址字节,设备返回数据,MCU发送ACK,设备继续返回后续地址数据,直到MCU发送NACK,设备不再返回数据。
IIC_EEPROM说明书
一:原理图
IIC_EEPROM电路图
二:工作原理
使用IIC控制器读写IIC_EEPROM中的数据。
开启IIC控制器,IIC_SCL产生正确的时序,通过判断总线的状态,通过IIC_SDA传输数据。
请参考《STM32中文参考资料》的IIC相关内容。
三:实验现象及操作
本实验使用使用串口通信显示数据。
在上位机上打开串口助手,波特率设置为115200,无校验。
串口助手中使用文本接受模式。
按RESET键后,可发现串口助手接受区中显示,“这是一个IIC_EEPROM测试例程”字符串;
在按下一次K3键,程序网IIC_EEPROM中写入0x00,0x01,0x02,0x03等等共20个数据,在串口助手中有显示,然后会自动读取IIC_EEPROM中的数据并发送给PC机;
若再次按下K3键,数据会乘2再写入;
再按下K3键,数据则会乘3再写入;
依次类推。
单片机模拟I2C总线读写EEPROM(24CXX)程序一下面是一个最简单的读写程序,可以用来检测线路状况。
先附上程序和电路,后面附有说明。
电路:说明:P2 口的LED 都是我用来检测电路执行到哪一步的,个人觉得一目了然。
程序:#include #define unit unsigned int#define uchar unsigned charint ok;sbit scl=P0;sbit sda=P0;sb it led0=P2;sbit led1=P2;sb it led2=P2 ;sbit led3=P2;sb it led4=P2;sb it led5=P2 ;sbit led6=P2;sb it led7=P2;delay(void) //delay{ int i; led1=1; for(i=0;istart(void) //start{ sda=1; scl=1; delay(); sda=0; delay(); scl=0; led0=0;}stop(void) //stop{ sda=0; scl=1; delay(); sda=1; delay(); scl=0;}checkanswer(void) //check answer{ sda=1; scl=1; if(sda==1) { F0=1; led7=0; } scl=0; led3=0;}sendabyte(int temps) //send a byte{ uchar n=8; while(n--) { led2=1; if((temps&0x80)==0x80){ sda=1; scl=1; delay(); scl=0;}else{ sda=0; scl=1; delay(); scl=0;}temps=tempsreciveabyte() //recive a byte{ uchar n=8,tempr; while(n--) {//uchar idata *abyte scl=1;tempr=temprmain(void) //MAIN{start();sendabyte(0xa0);checkanswer();if(F0==1) return;sendabyte(0x00);checkanswer();if(F0==1) return;sendabyte(0x11);checkanswer();if(F0==1) return;/*-----------------------*/start(); sendabyte(0xa0);checkanswer();if(F0==1) return;。
STM32模拟iic驱动eeprom24c128void IIC_Init(void) //IIC初始化函数{GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);//使能GPIOB时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOE, &GPIO_InitStructure);GPIO_SetBits(GPIOE,GPIO_Pin_2|GPIO_Pin_3); //PE2,PE3 输出高}void IIC_Start(void) //IIC开始函数{SDA_OUT(); //sda线输出IIC_SDA=1;IIC_SCL=1;delay_us(5);IIC_SDA=0;//START:when CLK is high,DATA change form high to lowdelay_us(5);IIC_SCL=0; //钳住I2C总线,准备发送或接收数据}void IIC_Stop(void) //IIC停止函数{SDA_OUT();//sda线输出IIC_SCL=0;IIC_SDA=0;//STOP:when CLK is high DATA change form low to highdelay_us(5);IIC_SCL=1;IIC_SDA=1;//发送I2C总线结束信号delay_us(5);}u8 IIC_Wait_Ack(void) //等待应答{u8 ucErrTime=0;SDA_IN(); //SDA设置为输入IIC_SDA=1;delay_us(5);IIC_SCL=1;delay_us(1);while(READ_SDA){ucErrTime++;if(ucErrTime>250){IIC_Stop();return 1;}}IIC_SCL=0;//时钟输出0return 0;}void IIC_Ack(void) //SDA输出低电平,IIC应答{IIC_SCL=0;SDA_OUT();IIC_SDA=0;delay_us(2);IIC_SCL=1;delay_us(4);IIC_SCL=0;}void IIC_NAck(void) //SDA输出高电平,IIC非应答{IIC_SCL=0;SDA_OUT();IIC_SDA=1;delay_us(2);IIC_SCL=1;delay_us(4);IIC_SCL=0;}void IIC_Send_Byte(u8 txd) //IIC发送一个字节{u8 t;SDA_OUT(); //数据线输出模式IIC_SCL=0; //拉低时钟开始数据传输for(t=0;t<8;t++){IIC_SDA=(txd&0x80)>>7;txd<<=1;delay_us(5); //对TEA5767这三个延时都是必须的IIC_SCL=1;delay_us(5);IIC_SCL=0;delay_us(5);}}u8 IIC_Read_Byte(unsigned char ack) //IIC读取一个字节{unsigned char i,receive=0;SDA_IN();//SDA设置为输入for(i=0;i<8;i++ ){IIC_SCL=0;delay_us(5);IIC_SCL=1;receive<<=1;if(READ_SDA){receive++;}delay_us(5);}if (!ack)IIC_NAck();//发送nACKelseIIC_Ack(); //发送ACKreturn receive;}void AT24CXX_Init(void) //AT254C128初始化{IIC_Init();}u8 AT24CXX_ReadOneByte(u16 ReadAddr) //AT24C128读取一个字节{u8 temp=0;IIC_Start();IIC_Send_Byte(0XA0); //发送写命令IIC_Wait_Ack();IIC_Send_Byte(ReadAddr>>8);//发送高地址IIC_Wait_Ack();IIC_Send_Byte(ReadAddr%256); //发送低地址IIC_Wait_Ack();IIC_Start();IIC_Send_Byte(0XA1); //进入接收模式IIC_Wait_Ack();temp=IIC_Read_Byte(0);IIC_Stop(); //产生一个停止条件return temp;}void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite) {IIC_Start();IIC_Send_Byte(0XA0); //发送写命令IIC_Wait_Ack();IIC_Send_Byte(WriteAddr>>8);//发送高地址IIC_Wait_Ack();IIC_Send_Byte(WriteAddr%256); //发送低地址IIC_Wait_Ack();IIC_Send_Byte(DataToWrite); //发送字节IIC_Wait_Ack();IIC_Stop();//产生一个停止条件delay_ms(20);}u8 AT24CXX_Check(void){u8 temp;temp=AT24CXX_ReadOneByte(12333);//避免每次开机都写AT24CXXif(temp==0X55){return 0;}else//排除第一次初始化的情况{AT24CXX_WriteOneByte(12333,0X55);temp=AT24CXX_ReadOneByte(12333);if(temp==0X55){return 0;}}return 1;}void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead) {while(NumToRead){*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);NumToRead--;}}void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite) {while(NumToWrite--){AT24CXX_WriteOneByte(WriteAddr,*pBuffer);WriteAddr++;pBuffer++;}}BY MaiLaoDie。
stm32Flash模拟eeprom心得(精品)STM32F10某FLASH模拟EEPROM心得花了几天时间研究tm32用Flah模拟EEPROM的问题,终于彻底弄懂了这种机制,由于我英文很菜,所以官方文档没有仔细看,而是直接去抠官方给出的例子程序,当然这种方法比较笨,但最终效果是一样的。
下面仅将我学习过程中的一些心得体会给大家介绍一下,希望能对需要的人有所帮助,有不足之处望大家积极指正。
首先推荐大家看的文档就是ST的官方文档《AN2594.pdf》和前辈总结出的《STM32FLASH模拟EEPROM使用和优化.pdf》和已经优化过的例程代码《FW_V3.1.0优化(FLASH模拟EEPROM).rar》下面开始进入主题1.为什么要用flah模拟eeprom?在许多应用场合下需要用eeprom保存非易失性的数据,但是意法半导体为了控制成本,没有在STM32F10某系列芯片中集成EEPROM,所以我们就需要用其内部集成的FLASH通过软件模拟EEPROM来达到同样的效果。
2.tm32中的片上FLASH特点根据《STM32F10某闪存编程》中的介绍,以小容量为例(如下图),我们要使用的是32个1K字节/页的主存储空间,也就是说这段空间里除了保存用户代码的部分,其余部分我们是可以利用其作为数据存储使用的。
tm32的FLASH分为主存储块和信息块。
主存储块用于保存具体的程序代码和用户数据,信息块用于负责由tm32出厂是放置2KB的启动程序(Bootloader)并锁死,用户无法更改。
选项字节存储芯片的配置信息及对主存储块的保护信息。
STM32的FLASH主存储块按页组织,有的产品每页1KB,有的产品每页2KB。
页面典型的用途就是用于按页擦除FLASH。
从这点来看,页面有点像通用FLASH的扇区上图中FLASH一页大小为1KB。
范围为从地址0某08000000开始的32KB内。
对Flah的写入操作要“先擦除后写入”的原则;闪存的读写涉及一个概念,字(Word)32bit和半字(HalfWord)16bit,虽然STM32FLASH也是由字节组成,但STM32FLASH的编程每次都是以16bit半字为单位,且FLASH地址必须为偶数,否则会出错。
STM32的I2C-EEPROM已调试成功万利的I2C-EEPROM例程有些问题,经本人两个昼夜的反复试验,已修改完善。
修改了两个地方,在void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite)写操作函数和void I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)读操作函数体内的开头先要执行一句I2C_EE_WaitEepromStandbyState();这样在以后调用写操作函数和读操作函数时就不用执行I2C_EE_WaitEepromStandbyState()了。
但上电复位后先要执行一次读操作,以后就可以无限制的随便调用这两个函数了。
详细如下。
/* Includes ------------------------------------------------------------------*/#include "stm32f10x_lib.h"#include "i2c_ee.h"#include"delay.h"/* Private typedef -----------------------------------------------------------*//* Private define ------------------------------------------------------------*/#define I2C_Speed 10000#define I2C1_SLAVE_ADDRESS7 0xA0#define I2C_PageSize 4/* Private macro -------------------------------------------------------------*//* Private variables ---------------------------------------------------------*/u16 EEPROM_ADDRESS;/* Private function prototypes -----------------------------------------------*/void I2C_Configuration(void);/************************************************************** ****************** Function Name : I2C_Configuration* Description : I2C Configuration* Input : None* Output : None* Return : None*************************************************************** ****************/void I2C_Configuration(void){GPIO_InitTypeDef GPIO_InitStructure;I2C_InitTypeDef I2C_InitStructure;/* Configure I2C1 pins: SCL and SDA */GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;//开漏输出GPIO_Init(GPIOB, &GPIO_InitStructure);/* I2C configuration */I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;//设置 I2C为 I2C 模式I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;//I2C快速模式 Tlow / Thigh = 2I2C_InitStructure.I2C_OwnAddress1 = I2C1_SLAVE_ADDRESS7;//设置第一个设备地址I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;//使能应答I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;//应答 7位地址I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;//设置时钟频率/* I2C Peripheral Enable */I2C_Cmd(I2C1, ENABLE);//使能I2C外设/* Apply I2C configuration after enabling it */I2C_Init(I2C1, &I2C_InitStructure);}/************************************************************** ****************** Function Name : I2C_EE_Init* Description : Initializes peripherals used by the I2C EEPROM driver.* Input : None* Output : None* Return : None*************************************************************** ****************/void I2C_EE_Init(){/* I2C configuration */I2C_Configuration();/* depending on the EEPROM Address selected in thei2c_ee.h file */#ifdef EEPROM_Block0_ADDRESS/* Select the EEPROM Block0 to write on */EEPROM_ADDRESS = EEPROM_Block0_ADDRESS;#endif#ifdef EEPROM_Block1_ADDRESS/* Select the EEPROM Block1 to write on */EEPROM_ADDRESS = EEPROM_Block1_ADDRESS;#endif#ifdef EEPROM_Block2_ADDRESS/* Select the EEPROM Block2 to write on */EEPROM_ADDRESS = EEPROM_Block2_ADDRESS;#endif#ifdef EEPROM_Block3_ADDRESS/* Select the EEPROM Block3 to write on */EEPROM_ADDRESS = EEPROM_Block3_ADDRESS;#endif}/************************************************************** ****************** Function Name : I2C_EE_BufferWrite* Description : Writes buffer of data to the I2C EEPROM.* Input : - pBuffer : pointer to the buffer containing the data to be* written to the EEPROM.* - WriteAddr : EEPROM's internal address to write to.* - NumByteToWrite : number of bytes to write to the EEPROM.* Output : None* Return : NonepBuffer:指向要写入数据数组的指针WriteAddr:24c02中要写入数据的首地址NumByteToWrite:写入的字节数*************************************************************** ****************/void I2C_EE_BufferWrite(u8* pBuffer, u8 WriteAddr, u16 NumByteToWrite)//将缓冲器的数据写入EEPROM{u8 NumOfPage = 0, NumOfSingle = 0, Addr = 0, count = 0;Addr = WriteAddr % I2C_PageSize;//写入地址是每页的第几位count = I2C_PageSize - Addr;//在开始的一页要写入的个数NumOfPage = NumByteT oWrite / I2C_PageSize;//要写入的页数NumOfSingle = NumByteToWrite % I2C_PageSize;//不足一页的个数I2C_EE_WaitEepromStandbyState();//EEPROM设为待命状态/* If WriteAddr is I2C_PageSize aligned */if(Addr == 0) //写入地址是页的开始{/* If NumByteToWrite < I2C_PageSize */if(NumOfPage == 0) //数据小于一页{I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//写少于一页的数据I2C_EE_WaitEepromStandbyState();//EEPROM设为待命状态}/* If NumByteToWrite > I2C_PageSize */else //数据大于等于一页{while(NumOfPage--)//要写入的页数{I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize); //写一页的数据I2C_EE_WaitEepromStandbyState();//EEPROM设为待命状态WriteAddr += I2C_PageSize;pBuffer += I2C_PageSize;}if(NumOfSingle!=0)//剩余数据小于一页{I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//写少于一页的数据I2C_EE_WaitEepromStandbyState();//EEPROM设为待命状态}}}/* If WriteAddr is not I2C_PageSize aligned */else //写入地址不是页的开始{/* If NumByteToWrite < I2C_PageSize */if(NumOfPage== 0) //数据小于一页{I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle);//写少于一页的数据I2C_EE_WaitEepromStandbyState();//EEPROM设为待命状态}/* If NumByteToWrite > I2C_PageSize */else//数据大于等于一页{NumByteToWrite -= count;NumOfPage = NumByteT oWrite / I2C_PageSize; //重新计算要写入的页数NumOfSingle = NumByteToWrite % I2C_PageSize;//重新计算不足一页的个数if(count != 0)//在此处count一定不为0,此判断条件好象有点多余{I2C_EE_PageWrite(pBuffer, WriteAddr, count);//将开始的空间写满一页I2C_EE_WaitEepromStandbyState();//EEPROM设为待命状态WriteAddr += count;pBuffer += count;}while(NumOfPage--)//要写入的页数{I2C_EE_PageWrite(pBuffer, WriteAddr, I2C_PageSize);//写一页的数据I2C_EE_WaitEepromStandbyState();//EEPROM设为待命状态WriteAddr += I2C_PageSize;pBuffer += I2C_PageSize;}if(NumOfSingle != 0)//剩余数据小于一页{I2C_EE_PageWrite(pBuffer, WriteAddr, NumOfSingle); //写少于一页的数据I2C_EE_WaitEepromStandbyState();//EEPROM设为待命状态}}}}/************************************************************** ****************** Function Name : I2C_EE_ByteWrite* Description : Writes one byte to the I2C EEPROM.* Input : - pBuffer : pointer to the buffer containing the data to be* written to the EEPROM.* - WriteAddr : EEPROM's internal address to write to.* Output : None* Return : NonepBuffer:指向要写入数据数组的指针WriteAddr:24c02中要写入数据的首地址*************************************************************** ****************/void I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr)//写一个字节到EEPROM{/* Send STRAT condition */I2C_GenerateSTART(I2C1, ENABLE);//产生 I2Cx传输 START条件/* Test on EV5 and clear it */while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //检查最近一次I2C事件是否是输入的事件/* Send EEPROM address for write */I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//向指定的从 I2C设备传送地址字,选择发送方向/* Test on EV6 and clear it */while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//检查最近一次 I2C事件是否是输入的事件/* Send the EEPROM's internal address to write to */I2C_SendData(I2C1, WriteAddr);//通过外设 I2Cx发送地址/* Test on EV8 and clear it */while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//检查最近一次 I2C事件是否是输入的事件/* Send the byte to be written */I2C_SendData(I2C1, *pBuffer); //通过外设 I2Cx发送数据/* Test on EV8 and clear it */while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//检查最近一次 I2C事件是否是输入的事件/* Send STOP condition */I2C_GenerateSTOP(I2C1, ENABLE);//产生 I2Cx传输 STOP条件}/************************************************************** ****************** Function Name : I2C_EE_PageWrite* Description : Writes more than one byte to the EEPROM with a single WRITE* cycle. The number of byte can't exceed the EEPROM page size.* Input : - pBuffer : pointer to the buffer containing the data to be* written to the EEPROM.* - WriteAddr : EEPROM's internal address to write to.* - NumByteToWrite : number of bytes to write to the EEPROM.* Output : None* Return : NonepBuffer:指向要写入数据数组的指针WriteAddr:24c02中要写入数据的首地址NumByteToWrite:写入的字节数*************************************************************** ****************/void I2C_EE_PageWrite(u8* pBuffer, u8 WriteAddr, u8 NumByteToWrite)//写少于一页的数据{/* Send START condition */I2C_GenerateSTART(I2C1, ENABLE);//产生 I2Cx传输 START条件/* Test on EV5 and clear it */while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT)); //检查最近一次I2C事件是否是输入的事件/* Send EEPROM address for write */I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//向指定的从 I2C设备传送地址字,选择发送方向/* Test on EV6 and clear it */while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)); //检查最近一次 I2C事件是否是输入的事件/* Send the EEPROM's internal address to write to */I2C_SendData(I2C1, WriteAddr); //通过外设 I2Cx发送地址/* Test on EV8 and clear it */while(! I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED)); //检查最近一次I2C 事件是否是输入的事件/* While there is data to be written */while(NumByteT oWrite--){/* Send the current byte */I2C_SendData(I2C1, *pBuffer); //通过外设 I2Cx发送数据/* Point to the next byte to be written */pBuffer++;/* Test on EV8 and clear it */while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED));//检查最近一次 I2C事件是否是输入的事件}/* Send STOP condition */I2C_GenerateSTOP(I2C1, ENABLE);//产生 I2Cx传输 STOP条件}/************************************************************** ****************** Function Name : I2C_EE_BufferRead* Description : Reads a block of data from the EEPROM.* Input : - pBuffer : pointer to the buffer that receives the data read* from the EEPROM.* - ReadAddr : EEPROM's internal address to read from.* - NumByteT oRead : number of bytes to read from the EEPROM.* Output : None* Return : NonepBuffer:指向要保存读出数据的数组的指针ReadAddr:24c02中要读出数据的首地址NumByteToRead:读出的字节数*************************************************************** ****************/void I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)//将EEPROM的数据读入缓冲器{I2C_EE_WaitEepromStandbyState();//EEPROM设为待命状态/* Send START condition */I2C_GenerateSTART(I2C1, ENABLE);//产生 I2Cx传输 START条件/* Test on EV5 and clear it */while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));//检查最近一次 I2C事件是否是输入的事件/* In the case of a single data transfer disable ACK before reading the data */if(NumByteToRead==1){I2C_AcknowledgeConfig(I2C1, DISABLE);//使能或者失能指定I2C的应答功能}/* Send EEPROM address for write */I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//向指定的从 I2C设备传送地址字,选择发送方向/* Test on EV6 and clear it */while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));//检查最近一次 I2C事件是否是输入的事件/* Clear EV6 by setting again the PE bit */I2C_Cmd(I2C1, ENABLE);//使能或者失能 I2C外设/* Send the EEPROM's internal address to write to */I2C_SendData(I2C1, ReadAddr); //通过外设 I2Cx发送地址/* Test on EV8 and clear it */while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_TRANSMITTED));//检查最近一次 I2C事件是否是输入的事件/* Send STRAT condition a second time */I2C_GenerateSTART(I2C1, ENABLE);//产生 I2Cx传输 START条件/* Test on EV5 and clear it */while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_MODE_SELECT));//检查最近一次 I2C事件是否是输入的事件/* Send EEPROM address for read */I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver);//向指定的从I2C设备传送地址字,选择接收方向/* Test on EV6 and clear it */while(!I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));//检查最近一次 I2C事件是否是输入的事件/* While there is data to be read */while(NumByteT oRead){/* Test on EV7 and clear it */if(I2C_CheckEvent(I2C1,I2C_EVENT_MASTER_BYTE_RECEIVED)) //检查最近一次I2C事件是否是输入的事件{if(NumByteToRead == 2){/* Disable Acknowledgement */I2C_AcknowledgeConfig(I2C1, DISABLE);//使能或者失能指定I2C的应答功能}if(NumByteToRead == 1){/* Send STOP Condition */I2C_GenerateSTOP(I2C1, ENABLE);//产生 I2Cx传输 STOP条件}/* Read a byte from the EEPROM */*pBuffer = I2C_ReceiveData(I2C1);//返回通过 I2Cx最近接收的数据/* Point to the next location where the byte read will be saved */pBuffer++;/* Decrement the read bytes counter */NumByteToRead--;}}/* Enable Acknowledgement to be ready for another reception */I2C_AcknowledgeConfig(I2C1, ENABLE);//使能或者失能指定I2C的应答功能}/************************************************************** ****************** Function Name : I2C_EE_WaitEepromStandbyState* Description : Wait for EEPROM Standby state* Input : None* Output : None* Return : None*************************************************************** ****************/void I2C_EE_WaitEepromStandbyState(void) //EEPROM设为待命状态{vu16 SR1_Tmp = 0;do{/* Send START condition */I2C_GenerateSTART(I2C1, ENABLE);//产生 I2Cx传输 START条件/* Read I2C1 SR1 register */SR1_Tmp = I2C_ReadRegister(I2C1, I2C_Register_SR1);//读取指定的 I2C寄存器 I2C_SR1 并返回其值/* Send EEPROM address for write */I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Transmitter);//向指定的从I2C设备传送地址字,选择发送方向}while(!(I2C_ReadRegister(I2C1, I2C_Register_SR1) & 0x0002));//地址发送结束/* Clear AF flag */I2C_ClearFlag(I2C1, I2C_FLAG_AF);//清除 I2Cx的应答错误标志位}。
/*********文件名:i2c_ee.h**********//* Define to prevent recursive inclusion ------------------------------------ */#ifndef __I2C_EE_H#define __I2C_EE_H/* Includes ------------------------------------------------------------------*/#include "stm32f10x.h"/* Exported macro ------------------------------------------------------------*/#define ADDR_24CXX 0xA0#define SCLH GPIOB->BSRR = GPIO_Pin_6#define SCLL GPIOB->BRR = GPIO_Pin_6#define SDAH GPIOB->BSRR = GPIO_Pin_7#define SDAL GPIOB->BRR = GPIO_Pin_7#define SCLread GPIOB->IDR & GPIO_Pin_6#define SDAread GPIOB->IDR & GPIO_Pin_7/* Exported functions ------------------------------------------------------- */void I2C_EE_Init(void);uint8_t I2C_EE_BufferWrite(uint8_t *psrc_data,uint8_t adr,uint8_t nbyte); uint8_t I2C_EE_BufferRead(uint8_t *pdin_data,uint8_t adr,uint8_t nbyte);#endif /* __I2C_EE_H *//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////*********文件名:i2c_ee.c**********/#include "i2c_ee.h"enum ENUM_TWI_REPLY{TWI_NACK=0,TWI_ACK=1};enum ENUM_TWI_BUS_STATE{TWI_READY=0,TWI_BUS_BUSY=1,TWI_BUS_ERROR=2};void I2C_EE_Init(void){GPIO_InitTypeDef GPIO_InitStructure;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);// Configure I2C1 pins: SCL and SDAGPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;GPIO_Init(GPIOB, &GPIO_InitStructure);}void TWI_delay(void){uint8_t i=10; //i=10延时1.5us//这里可以优化速度,经测试最低到5还能写入while(i--);}/**************************************************************************延时ms:延时的毫秒数CYCLECOUNTER / 72000000***************************************************************************/void DelayMs(uint16_t ms){uint16_t iq0;uint16_t iq1;for(iq0 = ms; iq0 > 0; iq0--){for(iq1 = 11998; iq1 > 0; iq1--); // ( (6*iq1+9)*iq0+15 ) / 72000000}}uint8_t TWI_Start(void){SDAH;SCLH;TWI_delay();if(!SDAread)return TWI_BUS_BUSY; //SDA线为低电平则总线忙,退出SDAL;TWI_delay();if(SDAread) return TWI_BUS_ERROR; //SDA线为高电平则总线出错,退出 SCLL;TWI_delay();return TWI_READY;}/*void TWI_Stop(void){SCLL;TWI_delay();SDAL;SCLH;TWI_delay();SDAH;TWI_delay();}*/void TWI_Stop(void) {SDAL;SCLL;TWI_delay();SCLH;TWI_delay();SDAH;TWI_delay();}void TWI_Ack(void) {SCLL;TWI_delay();SDAL;TWI_delay();SCLH;TWI_delay();SCLL;TWI_delay();}void TWI_NoAck(void) {SCLL;TWI_delay();SDAH;TWI_delay();SCLH;SCLL;TWI_delay();}uint8_t TWI_WaitAck(void) //返回为:=1有ACK,=0无ACK {SCLL;TWI_delay();SDAH;TWI_delay();SCLH;TWI_delay();if(SDAread){SCLL;return 0;}SCLL;return 1;}void TWI_SendByte(uint8_t SendByte) //数据从高位到低位// {uint8_t i=8;while(i--){SCLL;TWI_delay();if(SendByte&0x80)SDAH;elseSDAL;SendByte<<=1;TWI_delay();SCLH;TWI_delay();}SCLL;}uint8_t TWI_ReceiveByte(void) //数据从高位到低位//{uint8_t i=8;uint8_t ReceiveByte=0;SDAH;while(i--){ReceiveByte <<= 1;SCLL;TWI_delay();SCLH;TWI_delay();if(SDAread){ReceiveByte |= 0x01;}}SCLL;return ReceiveByte;}//返回:3写入成功;0写器件地址出错,1总线忙,2出错//写入1字节数据SendByte:待写入数据WriteAddress:待写入地址uint8_t TWI_WriteByte(uint8_t SendByte, uint8_t WriteAddress){uint8_t i;uint16_t j;i = TWI_Start();if(i)return i;TWI_SendByte( ADDR_24CXX & 0xFE);//写器件地址写入:地址最低位是0,读取:地址最低位是1if(!TWI_WaitAck()){TWI_Stop();return 0;}TWI_SendByte(WriteAddress); //设置起始地址TWI_WaitAck();TWI_SendByte(SendByte); //写数据TWI_WaitAck();TWI_Stop();//注意:因为这里要等待EEPROM写完,可以采用查询或延时方式(10ms) DelayMs(12); //写入延时12ms 写周期大于10ms即可return 3;}//返回:0写器件地址出错,1总线忙,2出错,//读出1字节数据//ReadAddress:待读出地址uint8_t TWI_ReadByte( uint8_t ReadAddress){uint8_t i,temp;i = TWI_Start();if(i)return i;TWI_SendByte((ADDR_24CXX & 0xFE));//写器件地址,先执行一次伪写操作if(!TWI_WaitAck()){TWI_Stop();return 0;}TWI_SendByte(ReadAddress); //设置起始地址TWI_WaitAck();TWI_Start();TWI_SendByte((ADDR_24CXX & 0xFE)|0x01); //读器件地址写入:地址最低位是0,读取:地址最低位是1TWI_WaitAck();//*pDat = TWI_ReceiveByte();temp = TWI_ReceiveByte();TWI_NoAck();TWI_Stop();return temp;//返回的如果是0,1,2则与错误代码相同了,再考虑一下}/***************************************************************************向24c256中写多个字节psrc_data:指向要写入数据数组的指针adr:24c256中要写入数据的首地址nbyte:写入的字节数返回值: 0:执行完毕;1:执行出现错误形参中:C02只有一个地址adr;C256中有高位地址hadr和低位地址ladr***************************************************************************/uint8_t I2C_EE_BufferWrite(uint8_t *psrc_data,uint8_t adr,uint8_t nbyte){uint8_t i;for(;nbyte!=0;nbyte--){i = TWI_Start();if(i)return i;TWI_SendByte( ADDR_24CXX & 0xFE);//写器件地址if(!TWI_WaitAck()){TWI_Stop();return 0;}TWI_SendByte(adr); //设置起始地址TWI_WaitAck();TWI_SendByte(*psrc_data); //写数据TWI_WaitAck();psrc_data++; //指向待写数据的指针加1adr++; //对24C08的操作地址加1TWI_Stop();//注意:因为这里要等待EEPROM写完,可以采用查询或延时方式(10ms) DelayMs(12); //写入延时12ms 写周期大于10ms即可}return 0;}/***************************************************************************从24c02读多个字节pdin_data:指向要保存读出数据的数组的指针adr:24c02中要读出数据的首地址nbyte:读出的字节数返回值: 0:执行完毕;1:执行出现错误***************************************************************************/uint8_t I2C_EE_BufferRead(uint8_t *pdin_data,uint8_t adr,uint8_t nbyte) {uint8_t i;i = TWI_Start();if(i)return i;TWI_SendByte((ADDR_24CXX & 0xFE));//写器件地址,先执行一次伪写操作if(!TWI_WaitAck()){TWI_Stop();return 0;}TWI_SendByte(adr); //设置起始地址TWI_WaitAck();TWI_Start();TWI_SendByte((ADDR_24CXX & 0xFE)|0x01); //读器件地址写入:地址最低位是0,读取:地址最低位是1TWI_WaitAck();while(nbyte!=1) //读入前(nbyte-1)个字节{*pdin_data = TWI_ReceiveByte(); //循环从24C02中读数据,存入pdin_data所指的存储器中TWI_Ack(); //IIC应答pdin_data++; //指向存储读入数据的存储器指针加1nbyte--; //剩余要读入的字节减1};*pdin_data = TWI_ReceiveByte(); //读入最后一个字节TWI_NoAck(); //IIC无应答操作TWI_Stop();return 0;}/*void TWI_24CXX_Write(uint8_t* pDat, uint8_t nAddr, uint8_t nLen) {uint16_t i;for(i=0;i<nLen;i++){TWI_WriteByte(*(pDat+i), nAddr+i);}}void TWI_24CXX_Read(uint8_t* pDat, uint8_t nAddr, uint8_t nLen) {uint16_t i;for(i=0; i<nLen; i++)*(pDat+i) = TWI_ReadByte(nAddr+i);}*/。
STM32驱动AT24CXX系列芯片标签:stm32存储AT24CXX驱动2014-10-03 22:25 963人阅读评论(0) 收藏举报分类:设备驱动(29)版权声明:本文为博主原创文章,未经博主允许不得转载。
AT24Cxx系列EEPROM是由美国Mcrochip公司出品,1-512K位的支持I2C总线数据传送协议的串行CMOS E2PROM,可用电擦除,可编程自定时写周期(包括自动擦除时间不超过10ms,典型时间为5ms)的。
串行E2PROM一般具有两种写入方式,一种是字节写入方式,还有另一种页写入方式。
允许在一个写周期内同时对1个字节到一页的若干字节的编程写入,1页的大小取决于芯片内页寄存器的大小。
其中,AT24C01具有8字节数据的页面写能力,AT24C02/04/08/16具有16字节数据的页面写能力,AT24C32/64具有32字节数据的页面写能力AT24CXX的驱动是基于IIC的,在基本的IIC上加入了一些通讯协议,具体如下1.随机写主器件发送起始命令和从器件地址信息(R/W 位置0)给从器件,主器件在收到从器件产生应答信号后,主器件发送 1 个8 位字节地址写入AT24C01/02/04/08/16 的地址指针,对于AT24C31/64/128/256 来说,所不同的是主器件发送两个8 位地址字写入AT24C32/64/128/256 的地址指针。
主器件在收到从器件的另一个应答信号后,再发送数据到被寻址的存储单元。
AT24Cxx 再次应答,并在主器件产生停止信号后开始内部数据的擦写,在内部擦写过程中,AT24Cxx 不再应答主器件的任何请求2.页写。
在页写模式下,AT24C01/02/04/08/16/32/64/128/256可一次写入8 /16/16/16/16/32/32/64/64 个字节数据。
页写操作的启动和字节写一样,不同的是在于传送了一字节数据后并不产生停止信号。
STM32模拟iic驱动eeprom24c128
void IIC_Init(void) //IIC初始化函数
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
//使能GPIOB时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2|GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStructure);
GPIO_SetBits(GPIOE,GPIO_Pin_2|GPIO_Pin_3); //PE2,PE3 输出高
}
void IIC_Start(void) //IIC开始函数
{
SDA_OUT(); //sda线输出
IIC_SDA=1;
IIC_SCL=1;
delay_us(5);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(5);
IIC_SCL=0; //钳住I2C总线,准备发送或接收数据}
void IIC_Stop(void) //IIC停止函数
{
SDA_OUT();//sda线输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(5);
IIC_SCL=1;
IIC_SDA=1;//发送I2C总线结束信号
delay_us(5);
}
u8 IIC_Wait_Ack(void) //等待应答
{
u8 ucErrTime=0;
SDA_IN(); //SDA设置为输入
IIC_SDA=1;
delay_us(5);
IIC_SCL=1;
delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0
return 0;
}
void IIC_Ack(void) //SDA输出低电平,IIC应答{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(4);
IIC_SCL=0;
}
void IIC_NAck(void) //SDA输出高电平,IIC非应答{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(4);
IIC_SCL=0;
}
void IIC_Send_Byte(u8 txd) //IIC发送一个字节{
u8 t;
SDA_OUT(); //数据线输出模式
IIC_SCL=0; //拉低时钟开始数据传输
for(t=0;t<8;t++)
{
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
delay_us(5); //对TEA5767这三个延时都是必须的
IIC_SCL=1;
delay_us(5);
IIC_SCL=0;
delay_us(5);
}
}
u8 IIC_Read_Byte(unsigned char ack) //IIC读取一个字节{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(5);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)
{
receive++;
}
delay_us(5);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
void AT24CXX_Init(void) //AT254C128初始化
{
IIC_Init();
}
u8 AT24CXX_ReadOneByte(u16 ReadAddr) //AT24C128读取一个字节
{
u8 temp=0;
IIC_Start();
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr>>8);//发送高地址
IIC_Wait_Ack();
IIC_Send_Byte(ReadAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Start();
IIC_Send_Byte(0XA1); //进入接收模式
IIC_Wait_Ack();
temp=IIC_Read_Byte(0);
IIC_Stop(); //产生一个停止条件
return temp;
}
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite) {
IIC_Start();
IIC_Send_Byte(0XA0); //发送写命令
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr>>8);//发送高地址
IIC_Wait_Ack();
IIC_Send_Byte(WriteAddr%256); //发送低地址
IIC_Wait_Ack();
IIC_Send_Byte(DataToWrite); //发送字节
IIC_Wait_Ack();
IIC_Stop();//产生一个停止条件
delay_ms(20);
}
u8 AT24CXX_Check(void)
{
u8 temp;
temp=AT24CXX_ReadOneByte(12333);//避免每次开机都写AT24CXX
if(temp==0X55)
{
return 0;
}
else//排除第一次初始化的情况
{
AT24CXX_WriteOneByte(12333,0X55);
temp=AT24CXX_ReadOneByte(12333);
if(temp==0X55)
{
return 0;
}
}
return 1;
}
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead) {
while(NumToRead)
{
*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);
NumToRead--;
}
}
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite) {
while(NumToWrite--)
{
AT24CXX_WriteOneByte(WriteAddr,*pBuffer);
WriteAddr++;
pBuffer++;
}
}
BY MaiLaoDie。