EEPROM存储实验

16.1 eeprom概述eeprom(electrically erasable programmable read only memory),称为带电可擦除可编程只读存储器,是一种可以断电保存数据的存储芯片,eeprom可以在电脑上或专用设备上擦除已有信息,重新编程,一般用在即插即用设备中,这种存储芯片可以通过高于普通电压的作用来擦除或重写,eeprom芯片一般用在需要频繁存储数据,但是数据量不大的场合,本实验以atmel公司设计的at24c02为例,来详细描述eeprom的基本操作。
at24c02是一片存储容量在2kbit的的存储芯片,即存储容量512byte,通过iic总线协议进行数据通信,stm32f1内置的iic模块,但是由于当时设计的时候为了规避飞利浦关于iic通信协议的专利技术,将iic设计的比较复杂,并且当操作不当的时候容易锁住总线,但是st公司关于硬件iic方面也提出了对应的软件解决方案,我们在这个存储实验中采用io口模拟iic协议与硬件iic模块两种方式来实现eeprom存储。
iic是一种只利用两根线来进行数据交换的串行通信协议,iic的电气线路包括两根线,即时钟线scl和数据线sda,高速iic总线一般可达400kbps以上,在传送过程中一共有三种类型的信号,分别是开始信号,结束信号和应答信号,我们在51单片机开发中曾将iic协议通过端口模拟成功的控制了eeprom的读写,现在只需要将之前的代码移植过来修改一下底层寄存器即可使用。stm32f1系列的硬件iic结构框图如下图所示。
从结构可以发现,stm32的硬件iic模块我们只需要配置好寄存器,然后既可以不考虑具体的iic协议,直接读数据寄存器就可以获取到总线上的数据,这也是硬件iic的优势所在。
16.2 at24c02通信时序16.2.1 写时序(1)写1个字节
第1步:发送开始信号
第2步:发送器件7位地址+1位读写控制后等待芯片应答
第3步:发送写入的地址后等待芯片应答
第4步:写入需要存储的数据后等待芯片应答
第5步:发送结束信号
第6步:等待20ms左右
(2)写n个字节
写n个字节适用于在连续的n个地址上写入n个数据,当需要写入n个数据的时候,这种连续写的方式比单个写的速度有显著性优势,具体步骤如下。
第1步:发送开始信号
第2步:发送器件7位地址+1位读写控制后等待芯片应答
第3步:发送写入的地址后等待芯片应答
第4步:写入需要存储的数据1后等待芯片应答
……
第n+4步:写入需要存储的数据n后等待芯片应答
第n+5步:发送结束信号
第n+6步:等待20ms左右
注:at24c系列芯片进行1次完整的写时序,必须等待5ms以上,手册给出的典型值是5ms,一般默认20ms。
16.2.2 读时序(1)读1个字节
第1步:发送开始信号
第2步:发送器件7位地址+1位读写控制后等待芯片应答
第3步:发送写入的地址后等待芯片应答
第4步:重新发送开始信号
第5步:发送器件7位地址+1位读写控制(读)后等待芯片应答
第6步:开始接收返回的的数据
第7步:发送结束信号
(2)读n个字节
写n个字节适用于读取存储在连续的n个地址上写入n个数据,当需要写入n个数据的时候,这种连续写的方式比单个写的速度有显著性优势,具体步骤如下。
第1步:发送开始信号
第2步:发送器件7位地址+1位读写控制后等待芯片应答
第3步:发送写入的地址后等待芯片应答
第4步:重新发送开始信号
第5步:发送器件7位地址+1位读写控制(读)后等待芯片应答
第6步:接收返回的的数据1后发送应答信号
第7步:接收返回的的数据2后发送应答信号
……
第n+6步:接收返回的的数据n
第n+7步:发送结束信号
16.3 stm32内部iic协议相关寄存器16.3.1 控制寄存器1:i2cx_cr11514131211109876543210
swrst - alert pec pos ack stop start nostretch engc enpec enapr smbtype - smbus pe
bit 15:软件复位
0:i2c模块不处于复位
1:i2c模块处于复位
bit 13:smbus提醒:软件可以设置或清除该位,当pe=0时,由硬件清除
0:释放smbalert引脚使其变高,提醒响应地址头紧跟在nack信号后面
1:驱动smbalert引脚使其变低,提醒响应地址头紧跟在ack信号后面
bit 12:数据包出错检测
0:无pec传输
1:pec传输
bit 11:应答/pec位置
0:ack位控制当前移位寄存器内正在接收的字节的ack。pec位表明当前移位寄存器内的字节是pec
1:ack位控制在移位寄存器里接收的下一个字节的ack。pec位表明在移位寄存器里接收的下一个字节是pec
注1:pos位只能用在2字节的接收配置中,必须在接收数据之前配置
注2:为了nack第2个字节,必须在清除addr为之后清除ack位
注3:为了检测第2个字节的pec,必须在配置了pos位之后,拉伸addr事件时设置pec位
bit 10:应答使能
0:无应答返回
1:在接收到一个字节后返回一个应答
bit 9:停止条件产生
在主模式下:
0:无停止条件产生
1:在当前字节传输或在当前起始条件发出后产生停止条件
在从模式下:
0:无停止条件产生
1:在当前字节传输或释放scl和sda线
bit 8:起始条件产生
在主模式下:
0:无起始条件产生
1:重复产生起始条件
在从模式下:
0:无起始条件产生
1:当总线空闲时,产生起始条件
bit 7:禁止时钟延长
0:允许时钟延长
1:禁止时钟延长
bit 6:广播呼叫使能
0:禁止广播呼叫,以非应答响应地址00h
1:允许广播呼叫,以应答响应地址00h
bit 5:pec使能
0:禁止pec计算
1:开启pec计算
bit 4:arp使能
0:禁止arp
1:使能arp
注1:如果smbtype=0,使用smbus设备的默认地址
注2:如果smbtype=1,使用smbus的主地址
bit 3:smbus类型
0:smbus设备
1:smbus主机
bit 1:smbus模式
0:i2c模式
1:smbus模式
bit 0:i2c模块使能
0:禁用i2c模块
1:启用i2c模块,根据smbus位的设置,相应的i/o口需配置为复用功能
注:在主模式下,通讯结束之前,绝不能清除该位
16.3.2 控制寄存器2:i2cx_cr21514131211109876543210
- last dmaen itbufen itevten iterren - freq[5:0]
bit 12:dma最后一次传输
0:下一次dma的eot不是最后的传输
1:下一次dma的eot是最后的传输
bit 11:dma请求使能
0:禁止dma请求
1:当txe=1或rxne=1时,允许dma请求
bit 10:缓冲器中断使能
0:当txe=1或rxne=1时,不产生任何中断
1:当txe=1或rxne=1时,产生事件中断
bit 9:事件中断使能
0:禁止事件中断
1:允许事件中断
在下列条件下,将产生该中断:
sb=1(主模式)
addr=1(主/从模式)
add10=1(主模式)
stopf=1(从模式)
btf=1,但是没有txe或rxne事件
如果itbufen=1,txe事件为1
如果itbufen=1,rxne事件为1
bit 8:出错中断使能
0:禁止出错中断
1:允许出错中断
在下列条件下,将产生该中断:
berr=1
arlo=1
af=1
ovr=1
pecerr=1
timeout=1
smbalert=1
bit 5~bit 0:i2c模块时钟频率,允许的范围在2~36mhz之间
000000:禁用
000001:禁用
000010:2mhz
...
100100:36mhz
大于100100:禁用
16.3.3 上升时间寄存器:i2cx_trise1514131211109876543210
- trise[5:0]
bit 5~bit 0:在快速/标准模式下的scl最大上升时间(主模式)
例如:标准模式中最大允许scl上升时间为1000ns。如果在i2c_cr2寄存器中freq中的值等于0x08且tpclk1=125ns,故trise中必须写入09h(1000ns/125ns=8+1)
注:只有当pe=0时,才能设置trise
16.3.4 时钟控制寄存器:i2cx_ccr1514131211109876543210
f/s duty - ccr[11:0]
bit 15:i2c主模式选项
0:标准模式的i2c
1:快速模式的i2c
bit 14:快速模式时的占空比
0:快速模式下t low /t high =2
1:快速模式下t low /t high =16/9
bit 11~bit 0:快速/标准模式下的时钟控制分频系数(主模式)
在i2c标准模式或smbus模式下:
t high =ccr×tpclk1
t low =ccr×tpclk1
在i2c快速模式下:
如果duty=0:
t high =ccr×tpclk1
t low =2×ccr×tpclk1
如果duty=1:
t high =9×ccr×tpclk1
t low =16×ccr×tpclk1
例如:在标准模式下,产生100khz的scl的频率,如果freqr=08,tpclk1=125ns,则ccr必须写入0x28(40×125ns=5000ns)
注1:允许设定的最小值为0x04,在快速duty模式下允许的最小值为0x01
注2:fck应当是10mhz的整数倍,这样可以正确产生400khz的快速时钟
16.3.5 自身地址寄存器1:i2cx_oar11514131211109876543210
addmode - add[9:8] add[7:1] add0
bit 15:寻址模式(从模式)
0:7位从地址(不响应10位地址)
1:10位从地址(不响应7位地址)
bit 9~bit 8:接口地址
7位地址模式时不用关心
10位地址模式时为地址的9~8位
bit 7bit 1:接口地址,地址的71位
bit 0:接口地址
7位地址模式时不用关心
10位地址模式时为地址第0位
16.3.6 自身地址寄存器2:i2cx_oar21514131211109876543210
- add2[7:1] endual
bit 7bit 1:接口地址,在双地址模式下地址的71位
bit 0:双地址模式使能位
0:在7位地址模式下,只有oar1被识别
1:在7位地址模式下,oar1和oar2都被识别
16.3.7 状态寄存器1:i2cx_sr11514131211109876543210
smbalert timeout - pecerr ovr af arl0 berr txe rxne - stopf add10 btf addr sb
bit 15: smbus提醒
在smbus主机模式下:
0:无smbus提醒
1:在引脚上产生smbalert提醒事件
在smbus从机模式下:
0:没有smbalert响应地址头序列
1:收到smbalert响应地址头序列至smbalert变低
bit 14:超时或tlow错误
0:无超时错误
1:scl低电平达到25ms;或主机低电平累积时间超过10ms;或从设备低电平累积时间超过25ms
bit 12:在接收时发生pec错误
0:无pec错误:接收到pec后接收器返回ack(如果ack=1)
1:有pec错误:接收到pec后接收器返回nack(不管ack是什么值)
bit 11:过载/欠载
0:无过载/欠载
1:出现过载/欠载
bit 10:应答失败
0:没有应答失败
1:应答失败
bit 9:仲裁丢失(主模式)
0:没有检测到仲裁丢失
1:检测到仲裁丢失
bit 8:总线出错
0:无起始或停止条件出错
1:起始或停止条件出错
bit 7:数据寄存器为空(发送时)
0:数据寄存器非空
1:数据寄存器空
bit 6:数据寄存器非空(接收时)
0:数据寄存器为空
1:数据寄存器非空
bit 4:停止条件检测位(从模式)
0:没有检测到停止条件
1:检测到停止条件
bit 3:10位头序列已发送(主模式)
0:没有add10事件发生
1:主设备已经将第一个地址字节发送出去
bit 2:字节发送结束
0:字节发送未完成
1:字节发送结束
bit 1:地址已被发送(主模式)/地址匹配(从模式)
地址匹配(从模式)
0:地址不匹配或没有收到地址
1:收到的地址匹配bit 1:
地址发送标志(主模式)
0:地址发送没有结束
1:地址发送结束
10位地址模式时,当收到地址的第二个字节的ack后该位被置1
7位地址模式时,当收到地址的ack后该位被置1
bit 0:起始位(主模式)
0:未发送起始条件
1:起始条件已发送
16.3.8 状态寄存器2:i2cx_sr21514131211109876543210
pec[7:0] dualf smbhost smbdefault gencall - tra busy msl
bit 15~bit 8:数据包出错检测,当enpec=1时,pec[7:0]存放内部的pec的值
bit 7:双标志(从模式)
0:接收到的地址与oar1内的内容相匹配
1:接收到的地址与oar2内的内容相匹配
bit 6:smbus主机头系列(从模式)
0:未收到smbus主机的地址
1:当smbtype=1且enarp=1时,收到smbus主机地址
bit 5:smbus设备默认地址(从模式)
0:未收到smbus设备的默认地址
1:当enarp=1时,收到smbus设备的默认地址
bit 4:广播呼叫地址(从模式)
0:未收到广播呼叫地址
1:当engc=1时,收到广播呼叫的地址
bit 2:发送/接收
0:接收到数据
1:数据已发送
bit 1:总线忙,在检测到sda或scl为低电平时,硬件将该位1
0:在总线上无数据通讯
1:在总线上正在进行数据通讯
bit 0:主从模式
0:从模式
1:主模式
16.3.9 数据寄存器:i2cx_dr1514131211109876543210
- dr[7:0]
bit 7~bit 0:8位数据寄存器,用于存放接收到的数据或放置用于发送到总线的数据
发送器模式:当写一个字节至dr寄存器时,自动启动数据传输。一旦传输开始,如果能及时把下一个需传输的数据写入dr寄存器,i2c模块将保持连续的数据流
接收器模式:接收到的字节被拷贝到dr寄存器。在接收到下一个字节之前读出数据寄存器,即可实现连续的数据传送
注1:在从模式下,地址不会被拷贝进数据寄存器dr
注2:硬件不管理写冲突(如果txe=0,仍能写入数据寄存器)
注3:如果在处理ack脉冲时发生arlo事件,接收到的字节不会被拷贝到数据寄存器里,因此不能读到它
16.4 实验例程16.4.1 软件模拟iic控制(1)创建at24cxx.h文件,并输入以下代码。
/********************************************************************************************************* eeprom 驱 动 文 件*********************************************************************************************************/#ifndef _at24cxx_h_#define _at24cxx_h_#include sys.h/********************************************************************************************************* 硬 件 端 口 定 义*********************************************************************************************************/#define iic_scl pbout( 6 )#define iic_sda pbout( 7 )#define iic_sda_read pbin( 7 )/********************************************************************************************************* 函 数 列 表*********************************************************************************************************/void at24cxx_init( void ) ; //at24c初始化void at24cxx_write_data( u16 address, u8 data ) ; //写入1个数据void at24cxx_write_ndata( u16 address, u8 *buffer, u16 len ) ; //写入n个数据void at24cxx_read_data( u16 address, u8 *data ) ; //读取1个数据void at24cxx_read_ndata( u16 address, u8 *buffer, u16 len ) ; //读取n个数据#endif(2)创建at24cxx.c文件,并输入以下代码。
/********************************************************************************************************* eeprom 驱 动 程 序*********************************************************************************************************/#include at24cxx.h#include delay.h/***************************************************name :iic_startfunction :iic起始信号paramater :nonereturn :none***************************************************/void iic_start(){ gpiob->crl &= 0x0fffffff ; //pb7推挽输出 gpiob->crl |= 0x30000000 ; iic_sda = 1 ; iic_scl = 1 ; delay_us( 4 ) ; iic_sda = 0 ; delay_us( 4 ) ; iic_scl = 0 ;}/***************************************************name :iic_stopfunction :iic停止信号paramater :nonereturn :none***************************************************/void iic_stop(){ gpiob->crl &= 0x0fffffff ; //pb7推挽输出 gpiob->crl |= 0x30000000 ; iic_scl = 0 ; iic_sda = 0 ; delay_us( 4 ) ; iic_scl = 1 ; iic_sda = 1 ; delay_us( 4 ) ;}/***************************************************name :iic_wait_ackfunction :iic等待应答paramater :nonereturn : 0:成功 1:失败***************************************************/void iic_wait_ack(){ u8 time = 0 ; gpiob->crl &= 0x0fffffff ; gpiob->crl |= 0x80000000 ; iic_sda = 1 ; delay_us( 1 ) ; iic_scl = 1 ; delay_us( 1 ) ; while( iic_sda_read ) { time ++ ; if( time>250 ) { iic_stop() ; break ; } } iic_scl = 0 ;}/***************************************************name :iic_send_bytefunction :iic发送一个字节paramater : ack:应答使能 0:不应答 1:应答return :none***************************************************/void iic_send_byte( u8 byte ){ u8 i; gpiob->crl &= 0x0fffffff ; //pb7推挽输出 gpiob->crl |= 0x30000000 ; iic_scl = 0 ; for( i=0; i<8; i++ ) { if( ( byte&0x80 )==0x80 ) iic_sda = 1 ; else iic_sda = 0 ; byte crl |= 0x80000000 ; for( i=0; i<8; i++ ) { iic_scl = 0 ; delay_us( 2 ) ; iic_scl = 1 ; byte crl |= 0x30000000 ; iic_sda = 1 - ack ; delay_us( 2 ) ; iic_scl = 1 ; delay_us( 2 ) ; iic_scl = 0 ; return byte ;}/***************************************************name :at24cxx_write_datafunction :写入1个数据paramater : address:地址 data:数据return :读到的数据***************************************************/void at24cxx_write_data( u16 address, u8 data ){ iic_start() ; iic_send_byte( 0xa0|( address/256 )<<1 ) ; //发送器件地址,写数据 iic_wait_ack() ; iic_send_byte( address%256 ) ; //发送低地址 iic_wait_ack() ; iic_send_byte( data ) ; //发送字节 iic_wait_ack() ; iic_stop() ; //产生一个停止条件 delay_ms( 10 ) ; //eeprom的写入速度比较慢}/***************************************************name :at24cxx_write_ndatafunction :写入n个数据paramater : address:地址 *buffer:数据缓存 len:数据长度return :none***************************************************/void at24cxx_write_ndata( u16 address, u8 *buffer, u16 len ) { u16 i ; for( i=0; iapb2enr |= 1crl |= 0x33000000 ; gpiob->odr |= 3sr1&0x02 )==0 )&&( timesr1&0x400 )==0x400 ) { i2c1->sr1 &= ~( 1sr2 ; //等待txe标志置0 time = 0 ; while( ( ( i2c1->sr2&0x04)==0 )&&( timesr1&0x400 )==0x400 ) { i2c1->sr1 &= ~( 1sr1&0x80 )==0 )&&( timesr1&0x400 )==0x400 ) { i2c1->sr1 &= ~( 1sr1&0x04 )==0 )&&( timesr1&0x400 )==0x400 ) { i2c1->sr1 &= ~( 1sr1&0x02 )==0 )&&( timesr1&0x400 )==0x400 ) { i2c1->sr1 &= ~( 1sr2; //等待txe标志置1 while( ( ( i2c1->sr1&0x80 )==0 )&&( timesr1&0x400)==0x400 ) { i2c1->sr1 &= ~( 1sr1&0x04 )==0 )&&( timesr1&0x400)==0x400 ) { i2c1->sr1 &= ~( 1sr1&0x02 )==0 )&&( timesr1&0x10 )==0x10 ) { i2c1->sr1 &= ~( 1sr2; i2c1->cr1 |= 1sr1 &= ~( 1#include sys.h#include delay.h#include usart1.h#include at24cxx.hu8 text_buffer[] = stm32f103 iic test ;int main(){ u8 datatemp[ 17 ] ; stm32_clock_init( 9 ) ; //stm32时钟初始化 systick_init( 72 ) ; //systick初始化 usart1_init( 72, 115200 ) ; //初始化串口1波特率115200 at24cxx_init() ; //at24c初始化 at24cxx_write_ndata( 0, text_buffer, 18 ) ; //从第0个地址处开始写入 at24cxx_read_ndata( 0, datatemp, 18 ) ; //从第0个地址处开始读出 while( 1 ) { }}

什么是TVS二极管?有哪些主要参数呢
中兴Blade 20 5G曝光!疑为Blade系列续作
PowerVR G6100 Series6 ‘Rogue’内核将推动OpenGL ES3.0的普及
人工智能与传感器产业链保持较快增长
蓝牙4.0创造医保、安防及家庭娱乐等领域新商机
EEPROM存储实验
解析2023年OLED市场份额价值走向
综述 | 石墨烯在高端电子器件领域研究进展
超宽带(UWB):工作原理及其非凡潜力
特斯拉2017年的首要任务,不仅仅只是造车!
电动汽车的养护问题该怎么解决
回顾美国半导体企业放缓招聘中国员工,担心知识产权外泄
物联网的突破口在无人驾驶?ARM借Mali-C71发力GPU
制作LVDS连接器时的注意事项
蔚来汽车将发布首款旗舰轿车
骁龙8cx第二代5G计算平台为用户提供极速的数千兆比特连接速度
知名可穿戴设备生产商将生产音圈电机呼吸机
光隆科技激光器芯片生产与封装项目正式投产
代币发行开发
三星诚意满满,A8s给出良心价,网友:抱歉,我还是选华为Nova4!