当前位置: 首页> 科技> 数码 > 免费建站哪家好_中国软件开发公司排行_爱站数据_优化师培训机构

免费建站哪家好_中国软件开发公司排行_爱站数据_优化师培训机构

时间:2025/7/9 22:26:51来源:https://blog.csdn.net/weixin_56459724/article/details/142416818 浏览次数:0次
免费建站哪家好_中国软件开发公司排行_爱站数据_优化师培训机构

一、什么是IIC

1、I2C总线概念

I2C总线是由Philips公司开发的一种双向二线制同步串行半双工总线。它只需要两根线(SCL、SDA)即可在连接于总线上的器件之间传送信息。

2、主机与从机的关系

主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件。在总线上主和从、发和收的关系不是恒定的,而取决于此时数据传送方向。如果主机要发送数据给从器件,则主机首先寻址从器件,然后主动发送数据至从器件,最后由主机终止数据传送;如果主机要接收从器件的数据,首先由主器件寻址从器件.然后主机接收从器件发送的数据,最后由主机终止接收过程。在这种情况下.主机负责产生定时时钟和终止数据传送。

3、主机的作用

3.1 波特率的选择

3.2 数据通信的方向

X:数据传输的方向 0:主机发送数据 1:主机接收数据

3.3 数据的发送和接收

写数据:开始--->发送从机地址+写(0)--->主发从收--->结束

读数据:开始--->发送从机地址+读(1)--->从发主收--->结束

注意:无论是开始还是结束都是由主机决定的

4、通信配置方式

        配置IIC通信的方式一般有两种,一种是配置通信控制器中的寄存器,利用IIC通信控制器驱动IO口产生通信协议时序,这种方式的优点就是稳定;;另一种就是IO口模拟通信时序,主要根据时序图利用IO口输出高低电平模仿通信时序,从而可以达到模拟通信时序的效果,并且这种方式的优点就是移植性好。由于考虑到后续程序的移植,所以IIC通信一般会使用IO口模拟时序的配置方式。两种方式具体的区别如下:

而此篇使用的是IO口模拟通信时序来配置IIC通信,为了更好的移植。 

二、如何配置IIC

1、IO口模拟通信时序

如何看时序图

规则:

因为一个通信协议规则是由时钟线和数据线一起体现出来的,即规则应该是时钟线和数据线结合看。

时钟线的某个电平状态态此时数据线是什么状态?

        规则①: 时钟线为低电平,数据线呈现出交叉状态

                        时钟线为低电平,可以写数据

                        想要写入数据,就要先把时钟线拉低

        规则②:时钟线为高电平,数据线呈现出平行状态

                        时钟线为高电平,可以读数据

                        想要读数据,就要先把时钟线拉高

看时序图方法:

        ①分清时钟线和数据线

        ②数据线数据段---------平行和交叉

        ③将时钟线与数据线结合--数据段规则         

        时钟线什么电平的时候可以写数据,时钟线什么电平的时候可以读数据

        ④看空闲状态

        ⑤看其他(从左到右看)

2、IO口模拟IIC时序

2.1  数据线的开漏类型

时钟线:是芯片提供时钟源,所以时钟线管脚为通用输出(推挽)

数据线:既能输入,又能输出,配置双向的,所以数据线配置为开漏输出

IIC的数据线为什么要配置开漏? 怎么输出?如何切换到输入?

首先,因为在输入时,输出禁止;而推挽输出既可以输出高电平又可以输出低电平,无法在输入时禁止输出;而开漏输出只能输出低电平,如果想输出高电平时,可以借助IIC的外接上拉电阻输出高电平;如果想切换到输入,就让输出数据寄存器ODR置1,使得输出禁止,这时就可以输入了。

开漏输出的优势:

  1. 防止短路
  2. 实现线与逻辑

外接上拉电阻与内置上拉电阻的区别

一、结构区别

内置上拉电阻:这是芯片本身自带的电阻,一般在输入端口内部连接。其电阻值固定,常见的有1KΩ、10KΩ等,具体值取决于芯片的设计。

外接上拉电阻:需要通过外部引脚连接到芯片上。其电阻值和型号可以根据实际需要选择,具有更高的灵活性。

二、使用区别

内置上拉电阻

优点:可以直接在硬件设计中使用,无需连接外部元器件,节省引脚资源和PCB面积,提高设计的灵活性。同时,简化了电路设计,降低了开发难度,适用于大多数应用场合。

缺点:电阻值固定,无法根据电路要求进行变化。在一些特殊场合下,如高精度模拟信号处理,内置上拉电阻的参数可能会产生影响。

外接上拉电阻

优点:电阻值可以根据实际需求进行选取,提高了电路的灵活性和适用性。尤其是在需要高精度信号处理的场合下,外接上拉电阻更具优势。

缺点:需要占用更多的引脚资源和PCB面积。

2.2 IIC的时序图

时序帧: 空闲段+起始段+数据段+应答段+停止段

空闲:

时钟线为高电平,数据线为高电平

起始信号:

时钟线为高电平,数据线为下降沿

数据段:

时钟线为低电平,数据线平行(可写)

时钟线为高电平,数据线交叉(可读)

停止信号:

时钟线为高电平,数据线为上升沿

应答:

IIC通信每传输一个字节(8位)就要有一个应答/不应答信号产生

主机是如何接收应答的?

①当主机作为发送数据端:---------主机检测应答/不应答信号

从机有IIC硬件电路,所以接收数据/检测应答--->从机自动完成

②当主机作为接收数据端:---------主机发送应答/不应答信号

从机有IIC硬件电路,所以发送数据/检测应答--->从机自动完成

 

应答/不应答信号总结:

①主机发送数据端

  1. 主机发送数据  //写程序
  2. 从机接收数据  //自动
  3. 从机发送应答信号/不应答信号 //自动
  4. 主机检测发送应答信号/不应答信号 //写程序

所以:检测应答/不应答函数

②主机接收数据端

  1. 从机发送数据  //自动
  2. 主机接收数据  //写程序
  3. 主机发送应答信号/不应答信号 //写程序
  4. 从机检测发送应答信号/不应答信号 //自动

所以:发送应答/不应答函数

 数据帧格式:

一帧数据帧的格式--->起始位+数据位+应答位+停止位 

总结:

空闲信号:

        IIC所用iO口初始化函数

起始信号:

        IIC起始信号函数

数据传输段:

        IIC发送一个字节函数

        IIC接收一个字节函数

应答段:

        发送应答/不应答信号函数

        检测应答/不应答函数

停止信号:

        IIC停止信号函数

3、程序设计

IIC标准时序图:

AT24Cxx时序图:

①起始信号函数:

时钟线拉低//为了改变数据线

数据线拉高//为下降沿做准备

时钟线拉高//起始信号的条件

延时5ms

数据线拉低//产生起始信号

延时5ms

时钟线拉低//安全作用

/*
函数名: iic_star
函数功能:IIC起始信号函数
返回值:void
形参:void
函数说明: */
void iic_star(void)
{//时钟线拉低   //为了可以改变数据线IIC_SCL_L;//数据线拉高   //为下降沿做准备IIC_SDA_OUT_H;//时钟线拉高   //起始信号的条件IIC_SCL_H;timer6_delay_us(5);//数据线拉低   //产生起始信号IIC_SDA_OUT_L;timer6_delay_us(5);//时钟线拉低 //安全作用IIC_SCL_L;
}

②停止信号函数:

时钟线拉低//为了改变数据线

数据线拉低//为上升沿做准备

时钟线拉高//停止信号的条件

延时5ms

数据线拉高//产生停止信号

延时5ms

/*
函数名: iic_stop
函数功能:IIC停止信号函数
返回值:void
形参:void
函数说明: */
void iic_stop(void)
{//时钟线拉低   //为了可以改变数据线IIC_SCL_L;//数据线拉低   //为上升沿做准备IIC_SDA_OUT_L;//时钟线拉高   //停止信号的条件IIC_SCL_H;timer6_delay_us(5);//数据线拉高   //产生停止信号IIC_SDA_OUT_H;timer6_delay_us(5);
}

③发送应答/不应答函数:

时钟线拉低//为了改变数据线

延时3ms

数据线拉低/拉高

延时2ms

时钟线拉高//主机帮助从机拉高时钟线,从机可以读取此位数据位

延时5ms

时钟线拉低 //安全作用

/*
函数名: iic_send_ack
函数功能:IIC发送应答/不应答函数
返回值:void
形参:u8 ack
函数说明: 传入1  主机停止接收数据传入0  主机继续接收数据
*/
void iic_send_ack(u8 ack)
{//时钟线拉低   		//为了可以改变数据线IIC_SCL_L;timer6_delay_us(3);//不应答--数据线拉高  应答数据线拉低ack ? (IIC_SDA_OUT_H) : (IIC_SDA_OUT_L); timer6_delay_us(2);//时钟线拉高   		//产生应答\不应答信号的条件IIC_SCL_H;timer6_delay_us(5);//时钟线拉低			 //安全作用IIC_SCL_L;}

接收应答\不应答函数:

 /*切换到输入模式*/

时钟线拉低//为了改变数据线

数据线拉高//为了关闭输出通道

/*读取应答信号*/

时钟线拉低//主机帮助从机拉低时钟线,从机自动发送应答/不应答信号

延时5ms

时钟线拉高//主机读取数据线

延时5ms

读取数据线   //读取数据不需要时间

时钟线拉低  //安全作用

返回应答

/*
函数名: iic_rec_ack
函数功能:IIC检测应答\不应答函数
返回值:u8
形参:void
函数说明: 返回0   主机接收到从机的应答返回1   主机接收到从机的不应答
*/
u8 iic_rec_ack(void)
{u8 ack;/*将数据线切换到输入*///时钟线拉低   		//为了可以改变数据线IIC_SCL_L;//数据线拉高      //为了关闭输出通道IIC_SDA_OUT_H;/*读取应答信号*///时钟线拉低      //主机帮助从机拉低时钟线,从机自动发送应答/不应答信号IIC_SCL_L;timer6_delay_us(5);//时钟线拉高IIC_SCL_H;timer6_delay_us(5);//读取数据线---读取数据不需要时间if(IIC_SDA_IN){ack = 1;//不应答}else {ack = 0;//应答}//时钟线拉低			 //安全作用IIC_SCL_L;return ack;}

⑤发送一个字节函数: 

时钟线拉低//为了改变数据线

延时3ms

/*循环一位一位的发送*/

时钟线拉低//为了可以改变数据线

延时3ms

根据要发送的数据的对应为是0还是1,决定时钟线拉高还是拉低

延时2ms

时钟线拉高//主机帮助从机拉高时钟线,从机可以读取此位数据位

延时5ms

下一位数据位

时钟线拉低  //安全作用

/*
函数名: iic_send_byte
函数功能:IIC发送一字节函数
返回值:void
形参:u8 data
函数说明: 		   */
void iic_send_byte(u8 data)
{u8 i;//发一位接一位for(i = 0;i < 8; i++){//时钟线拉低   		//为了可以改变数据线IIC_SCL_L;timer6_delay_us(3);if(data & 0x80){//数据线拉高IIC_SDA_OUT_H;}else{//数据线拉低IIC_SDA_OUT_L;}timer6_delay_us(2);//时钟线拉高IIC_SCL_H;//主机帮助从机拉高时钟线,从机可以读取此位数据位timer6_delay_us(5);data = data << 1;//下一位数据}//时钟线拉低			 //安全作用IIC_SCL_L;}

⑥接收一个字节函数:

 /*切换到输入模式*/

时钟线拉低//为了改变数据线

数据线拉高//为了关闭输出通道

 /*读取数据*/

//循环-->一位一位的读取

时钟线拉低///主机帮助从机拉低时钟线,从机自动数据

延时5ms

时钟线拉高//主机读取此位数据

延时5ms

左移一位数据

读取数据线

时钟线拉低//安全作用

返回数据

/*
函数名: iic_rec_byte
函数功能:IIC接收一字节函数
返回值:u8 
形参:void
函数说明: 		*/
u8 iic_rec_byte(void)
{u8 i;u8 data;/*将数据线切换到输入*///时钟线拉低   		//为了可以改变数据线IIC_SCL_L;//数据线拉高      //为了关闭输出通道IIC_SDA_OUT_H;/*读取数据*/for(i = 0;i < 8;i++){//时钟线拉低    //主机帮助从机拉低时钟线,从机改变数据线IIC_SCL_L;timer6_delay_us(5);//时钟线拉高    //主机读取此位数据位IIC_SCL_H;timer6_delay_us(5);data = data << 1;//低位补0//读取数据线if(IIC_SDA_IN){data |= 0x01;}}//时钟线拉低			 //安全作用IIC_SCL_L;return data;}

三、具体使用IIC

使用IIC通信是基于EEPROM的存储器上的,故需要了解什么是AT24C02

EEPROM(Electrically Erasable Programmable Read-Only Memory)是指带电可擦可编程只读存储器,它是一种非易失性存储器,能够在系统断电后仍然保留数据。

1、AT24C02

1.1 内存分类

RAM与ROM是计算机中常见的存储器类型。RAM(Random Access Memory)是一种临时存储器,用于存储计算机正在运行的程序和数据。它具有快速的读写速度和随机访问的特点。相比之下,ROM(Read-Only Memory)是一种只读存储器,用于存储固定的程序指令和数据。ROM中的数据在计算机断电时不会丢失,因此被称为非易失性存储器。ROM可分为MASKROM(掩模只读存储器)、PROM(可编程只读存储器)、EPROM(可擦除可编程只读存储器)、EEPROM、FLASH。而最常用的ROM是EEPROM和FLASHEEPROM是一种电可擦除的可编程ROM,相对于EPROM,它不需要紫外线擦除,它是利用电压脉冲进行擦除和编程数据;而FLASH是一种基于EEPROM技术的存储器,它是通过块擦除方式进行修改,相比EEPROM更具灵活性和可编程 

1.2 什么是AT24C02

AT24C02就属于EEPROM中的一种存储芯片

AT24C02特点

①at24c02是一种EEPROM存储芯片

②工作电压范围 1.8~5.5v (工作电压范围宽)

③支持低功耗模式(待机)

④内置256*8bit(共2k)存储空间

内部存储格式为8字节一页,共32页,构成256字节空间绝对地址0~255

⑥硬件数据写保护

把数据写进去,可以通过写保护管脚接入相应的电平时候,只能读不能写

⑦内部写周期最大5ms

当写数据时候,他先将数据存在存储器中,把相应地址空间的数据清除,然后才会把数据写到对应地址中。要给5ms的时间(写一次所需的时间,最大写入8个字节也就是一页)

⑧可按字节写,也可以按页写

按字节写:对应的找到地址空间,写入数据

按页写  :一页8字节,可以自动写下一个字节但是不能自动换页

起始地址编号  和  内容

读是没有限定的可以从0地址空间读到255地址空间(自动地址递增)

⑨可擦写100万次。(注意:跟读没关系)

1.3 内部结构框图

数据传输到数据缓冲器里,需要5ms的时间再写入到存储器中 

1.4 引脚说明

Vcc      : 电源

GND     : 地

A0,A1,A2 : 器件地址位

此器件地址: 1 0 1 0 A2 A1 A0 R/W 前四位固定,后三位自由设置,最后一位为读写控制位

                        (1:主机读AT24的数据   0:主机写数据到AT24)

根据原理图可知:A2 A1 A0 接地  

所以器件地址为: 1 0 1 0 0 0 0 R/W      写:10100000(0xa0)     读:10100001(0xa1)   

                                                                 0xa0:写模式                   0xa1:读模式

SDA      : IIC通信的数据线

SCL      : IIC通信的时钟线

WP       : 写保护管脚,接地可以正常读写,高电平只能读

2、基于IIC基础协议对AT24C02进行读写操作

2.1 读写位

接下来STM32要对存储芯片的读写操作

写: 0                 10100000      0xa0

读: 1                 10100001      0xa1

                                                                                

2.2 单字节写入 

字地址:器件内部存储空间地址(0~255)

/***************************************
*函数名			:at24c02_write_byte
*函数功能		:对at24c02某个空间存储一个字节数据
*函数参数		:u8  inner_addr     要存储的内部地址u8  data           要存储的数据
*函数返回值	:u8
*函数描述		:阶段性错误返回标志AT24C02_NO_ERR  0;   //应答  无错误AT24C02_ERR1   1;   //无应答AT24C02_ERR2   2;   //无应答AT24C02_ERR3   3;   //无应答
****************************************/
u8 at24c02_write_byte(u8 inner_addr,u8 data)
{u8 ack;iic_star();iic_send_byte(ATC24C02_ADDR_W);ack = iic_rec_ack();if(ack == 1){iic_stop();return AT24C02_ERR1;}iic_send_byte(inner_addr);ack = iic_rec_ack();if(ack == 1){iic_stop();return AT24C02_ERR2;}iic_send_byte(data);ack = iic_rec_ack();if(ack == 1){iic_stop();return AT24C02_ERR3;}iic_stop();//写周期timer6_delay_ms(5);return AT24C02_NO_ERR;
}
 2.3 单字节读取

 

/***************************************
*函数名			:at24c02_read_byte
*函数功能		:从at24c02某个空间读一个字节数据
*函数参数		:u8  inner_addr     要读取的内部地址u8 *data           读取到的内容放的地址
*函数返回值	:u8
*函数描述		:阶段性错误返回标志AT24C02_NO_ERR  0;   //应答  无错误AT24C02_ERR1    1;   //无应答AT24C02_ERR2    2;   //无应答AT24C02_ERR3    3;   //无应答
****************************************/
u8 at24c02_read_byte(u8 inner_addr,u8 *data)
{u8 ack;iic_star();iic_send_byte(ATC24C02_ADDR_W);ack = iic_rec_ack();if(ack == 1){iic_stop();return AT24C02_ERR1;}iic_send_byte(inner_addr);ack = iic_rec_ack();if(ack == 1){iic_stop();return AT24C02_ERR2;}iic_star();iic_send_byte(ATC24C02_ADDR_R);ack = iic_rec_ack();if(ack == 1){iic_stop();return AT24C02_ERR1;}*data = iic_rec_byte();iic_send_ack(1);//停止接收应答iic_stop();return AT24C02_NO_ERR;}
 2.4 页写

为什么不用IIC发送一字节函数连续写入数据?

首先IIC发送一字节函数每发一次字节有5ms的写入周期,如果连续写入多个数据后则会造成写周期时间累积,导致效率低;所以可以用页写进行操作,页写是每页写完才会进行一次写周期,并且页内是可以自动写下一个字节但是不能自动换页。

分析:

需要根据写入的起始地址和写入的字节数判断是否跨页

Inner/8==(inner + num_byte-1)/8  不跨页

/***************************************
函数名    :at24c02_write_page
函数功能  :对at24c02的存储空间页写操作       
函数参数  :u8 inner_addr    //起始空间地址u8 num_byte     //要写的字节数u8 *str         //要写内容的起始地址
函数返回值:每次发送都会加测响应,通过返回值判断具体哪次发送出问题宏定义错误标志:AT24C02_NO_ERR 0;   //应答  无错误AT24C02_ERR1   1;   //无应答AT24C02_ERR2   2;   //无应答AT24C02_ERR3   3;   //无应答AT24C02_OVER   4;   //跨页操作错误返回
****************************************/
u8 at24c02_write_page(u8 inner_addr,u8 num_byte,u8 *str)
{u8 ack;if(inner_addr/8 != (inner_addr+num_byte-1)/8 )//跨页{return AT24C02_OVER;}/*跨页操作*/iic_star();iic_send_byte(ATC24C02_ADDR_W);ack = iic_rec_ack();if(ack == 1){iic_stop();return AT24C02_ERR1;}iic_send_byte(inner_addr);ack = iic_rec_ack();if(ack == 1){iic_stop();return AT24C02_ERR2;}//循环发送while(num_byte)//abc{iic_send_byte(*str);ack = iic_rec_ack();if(ack == 1){return AT24C02_ERR3;}num_byte--;str++;}iic_stop();//写周期timer6_delay_ms(5);return AT24C02_NO_ERR;}
 2.5 连续读

 

/***************************************
函数名  :at24c02_read_bytes
函数功能:从at24c02的某个地址空间开始连续读多个字节
函数参数:u8  inner_addr     //器件内部存储空间地址  0~255 u16  num_byte      //要读多少个字节u8   *data          //将读到的数据写入此地址	  返回值  :每次发送都会加测响应,通过返回值判断具体哪次发送出问题宏定义错误标志:AT24C02_NO_ERR 0;   //应答  无错误AT24C02_ERR1   1;   //无应答AT24C02_ERR2   2;   //无应答AT24C02_ERR3   3;   //无应答AT24C02_OVER   4;   //跨页操作错误返回
****************************************/
u8 at24c02_read_bytes(u8 inner_addr,u16 num_byte,u8 *data)
{u8 ack;iic_star();iic_send_byte(ATC24C02_ADDR_W);ack = iic_rec_ack();if(ack == 1){iic_stop();return AT24C02_ERR1;}iic_send_byte(inner_addr);ack = iic_rec_ack();if(ack == 1){iic_stop();return AT24C02_ERR2;}iic_star();iic_send_byte(ATC24C02_ADDR_R);ack = iic_rec_ack();if(ack == 1){iic_stop();return AT24C02_ERR1;}/*连续读操作*/while(num_byte){*data = iic_rec_byte();if(num_byte == 1){iic_send_ack(1);//停止接收应答}else{iic_send_ack(0);}num_byte--;data++;}iic_stop();return AT24C02_NO_ERR;}
2.6 连续页写 

说明:

从任意地址位置,连续写多字节,不要考虑跨页限制

分析:

不能跨页的原因:

页内地址自动递增,页之间的地址是不能自动递增的, 7号地址不能自动递增到8号地址

跨页的关键:如何切换到下一页

思路:

/************************************************
*函数名			:at24c02_write_bytes
*函数功能		:对at20c02的存储空间开始连续写 可跨页
*函数参数		:u8  inner_addr      起始地址u16 num_byte        要读的字节数u8  *str            要写的数据的首地址
*函数返回值	:u8
*函数描述		:
*************************************************/
void at24c02_write_bytes(u8 inner_addr,u16 num_byte,u8 *str)
{u8 less_byte;while(1){//计算本页还剩多少空间可写 less_byte = 8 - inner_addr % 8   less_byte = 8 - inner_addr % 8;//如果要写的内容不需要跨页 less_byte >= num_byteif(less_byte >= num_byte){//调用发送页写函数(inner_addr,num_byte,str)at24c02_write_page(inner_addr,num_byte,str);break;}//如果要写的内容需要跨页 less_byte < num_byteelse {//调用发送页写函数把本页剩下的空间写完(inner_addr,less_byte,str)at24c02_write_page(inner_addr,less_byte,str);//计算出剩下多少个字节:num_byte = num_byte - less_bytenum_byte = num_byte - less_byte;//下一页的首地址: inner_addr = inner_addr + less_byteinner_addr = inner_addr + less_byte;//剩余要写的内容数据的地址:str = str + less_bytestr = str + less_byte;}}}

3、具体使用

需求1:往AT24C02中存储结构体,并且读出

分析:

存储一本书的信息

typedef struct book
{u8 name[15];u8 writer[15];u8 number[15];u32 hot;u32 sc;float price;
}BK;int main(void)
{u8 start_val;u8 set_flag = 0;BK send_book = {"西游记","吴承恩","W201955",0,30,55.5};BK rec_book;NVIC_SetPriorityGrouping(5); //设置优先级分组Usart1_init(115200);//串口初始化at24c02_init();at24c02_write_bytes(0,sizeof(send_book),(u8 *)&send_book);at24c02_read_bytes(0,sizeof(rec_book),(u8 *)&rec_book);while(1){printf("name:%s writer:%s number:%s hot:%d sc:%d price:%.1f\r\n",rec_book.name,rec_book.writer,rec_book.number,rec_book.hot,rec_book.sc,rec_book.price);timer11_delay_ms(200);}}

需求2:打印开机次数

分析:

获取上一次的开机次数(从AT24C02中读出来)

读出来的数据+1,再写到AT24C02中(下次用)

根据需求怎么分析?

首先根据分析可得,先读取上一次开机次数->次数+1->打印开机次数->往AT24C02中存储本次的开机次数;

发现问题:第一次开机之前就没存过开机次数

解决问题:分情况第一次开机与不是第一次开机

又发现问题:如何判断第一次开机

解决问题:一个空间作为第一次开机标志位

具体如下:

                                                                        比喻

每次开机都去读取某个空间-------->每次都去”到此一游”留名处

如果读到不是0xff(自己定),则是第一次开机------>无”到此一游”则证明没来过

并且将0xff写入到空间内,以便下次开机判断是否为第一次开机------>写上”到此一游”,下次再来则证明来过了

    at24c02_init();/**********************10号是开机标志位 11号是开机次数**********************///一开机就读取10号空间,以便于判断是否为第一次开机at24c02_read_byte(10,&open_flag);//第一次开机if(open_flag != 0xff){open_cnt = 1;printf("开机次数:%d\r\n",open_cnt);//往AT24C02中存储开机次数at24c02_write_byte(11,open_cnt);//往AT24C02中存储开机标志位open_flag = 0xff;at24c02_write_byte(10,open_flag);}//不是第一次开机else{//获取上一次开机的次数at24c02_read_byte(11,&open_cnt);//打印open_cnt++;printf("开机次数:%d\r\n",open_cnt);//往AT24C02中存储开机次数at24c02_write_byte(11,open_cnt);}

需求3:存储流水灯速度,按键调控流水灯,调控的速度要存储到AT24C02中

优化:

①按按键1存储数据

KEY1在没有开流水灯的时候,起到开启作用--->显示初始速度或者上次存储的速度

开流水灯后,起到保存当前速度作用--->确定保存速度并且显示

②调整到某个速度后,3s不做任何操作,速度自动保存

按下按键开始计时     

如何做到按下按键开始计时?

利用定时中断-->按按键并且到时间 才保存速度(一次)

    at24c02_init();/**********************12号空间是开机标志位 13号空间作为存储流水灯的速度**********************///一开机就读取12号空间,以便于判断是否为第一次开机at24c02_read_byte(12,&open_flag);//第一次开机if(open_flag != 0xff){//赋值流水灯速度speed = 5;//往AT24C02中存储流水灯速度at24c02_write_byte(13,speed);//往AT24C02中存储开机标志位open_flag = 0xff;at24c02_write_byte(12,open_flag);}//不是第一次开机else{//获取上一次开机流水灯的速度at24c02_read_byte(13,&speed);}printf("当前流水灯速度:%d\r\n",speed);while(1){keynum = Key_scan();switch(keynum){case 1:timer7_flag = 0;at24c02_write_byte(13,speed);printf("当前流水灯速度:%d\r\n",speed);led_flag = 1;break;//按键1打开流水灯case 2:led_flag = 0;LED_OFF;break;//按键2关闭流水灯case 3:if(led_flag){Tim7_cnt[8] = 0;timer7_flag = 1;speed -= 1;if(speed < 1)speed = 5;}break;//按键3加速case 4:if(led_flag){Tim7_cnt[8] = 0;timer7_flag = 1;speed += 1;if(speed > 10)speed = 5;}break;//按键4减速}if(led_flag == 1){LED_flash(speed);}}
/*
函数名:TIM7_IRQHandler
函数功能:基本定时器中断服务函数
返回值:void
形参:void
函数说明:*/
u16 Tim7_cnt[10];
u8 RGB_flag = 0;
u8 speed;
u8 timer7_flag = 0;
void TIM7_IRQHandler(void)
{//清除中断标志位TIM7->SR &= ~(1<<0);//紧急事件Tim7_cnt[8]++;if(Tim7_cnt[8] >= 3000){if(timer7_flag == 1){//标志位清零timer7_flag = 0;//获取流水灯速度at24c02_write_byte(13,speed);printf("当前流水灯速度:%d\r\n",speed);Beep_ON;}Tim7_cnt[8] = 0;}if(Tim7_cnt[8] >= 300)Beep_OFF;}

③利用结构体来管理数据

typedef struct ctrval

{

        u8 open_flag; //第一次开机标志

        u8 speed;     //存储流水灯速度

        u16 open_cont;//存储开机次数

}CTRVAL_t;

at24c02_init();/**************************************设置参数存放地址:1***************************************///读取开机标志空间at24c02_read_bytes(1,sizeof(ctr_data),(u8 *)&ctr_data);//第一次开机if(ctr_data.open_flag != 0xff){//赋值开机标志位ctr_data.open_flag = 0xff;//赋值开机次数ctr_data.open_cont = 1;printf("第%d次开机\r\n",ctr_data.open_cont);//赋值流水灯速度ctr_data.speed = 5;printf("当前的速度:%d\r\n",ctr_data.speed);//往AT24C02中储存结构体数据at24c02_write_bytes(1,sizeof(ctr_data),(u8 *)&ctr_data);}//不是第一次开机else{//开机次数自增ctr_data.open_cont++;printf("第%d次开机\r\n",ctr_data.open_cont);//往AT24C02中储存结构体数据at24c02_write_bytes(1,sizeof(ctr_data),(u8 *)&ctr_data);}printf("当前流水灯速度:%d\r\n",ctr_data.speed);while(1){keynum = Key_scan();switch(keynum){case 1:timer7_flag = 0;at24c02_write_bytes(1,sizeof(ctr_data),(u8 *)&ctr_data);printf("当前流水灯速度:%d\r\n",ctr_data.speed);led_flag = 1;break;//按键1打开流水灯case 2:led_flag = 0;LED_OFF;break;//按键2关闭流水灯case 3:if(led_flag){Tim7_cnt[8] = 0;timer7_flag = 1;ctr_data.speed -= 1;if(ctr_data.speed < 1)ctr_data.speed = 5;}break;//按键3加速case 4:if(led_flag){Tim7_cnt[8] = 0;timer7_flag = 1;ctr_data.speed += 1;if(ctr_data.speed > 10)ctr_data.speed = 5;}break;//按键4减速}if(led_flag == 1){LED_flash(ctr_data.speed);}}}

关键字:免费建站哪家好_中国软件开发公司排行_爱站数据_优化师培训机构

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: