目录

1、完成了ATK8266wifi的使用。包括,stm32和wifi的通信,通过wifi发送数据到TCP通信,发送数据到原子云上面,接收原子云的数据命令;
2、完成DS1302的时钟芯片控制,包括读取时间,电子时钟的按键调整功能;
3、完成了AT24C02的EEPROM数据存储,以及读出。主要是设置好数据之后,存储到24C02中,开机读取数据,不用每次都通过按键更新。
4、通过状态机按键key0,key1,key2,key3,key4五个按键进行调节。按键0和按键1用来调节时间,按键2、3和按键4调节温度、湿度等阈值,以及保存设置,撤销设置等。

下面分4个方面进行讲解。

1、wifi的使用。
买的是正点原子的wifi模块,学习步骤:
一、用USB转TTL模块,和WIFi模块进行连接,用网络串口助手,进行AT指令的学习,包括调节参数,设置AP,STA模式等。

参考资料:1、正点原子的wifi说明;/2、青柚视频的AT指令的讲解视频。包括配套视频和pdf文件,同时学习熟悉WIFI模块的基本知识,TCP,IP,UDP,等。
https://www.bilibili.com/video/BV1YW411C7tC?from=search&seid=8263631381031500092
二、用USB转TTL模块,和wifi模块进行连接,用网络串口助手,和原子云端进行通信。
参考资料:正点原子的WIFI说明的原子云部分内容;

三、用wifi的smartconfig功能,不用每次自动设置wifi等参数,开机自动连接。

四、将青柚的工程的wifi部分,内容进行修改和移植,移植到stm32F4。

说明,上面都有别人做好的视频、文档,所以不再做具体的视频和文档了。
移植到F4板子,用的是串口2,PD5和PD6这两个接口。
PD5(TXD)----wifi的RXD;
PD6(RXD)—wifi的TXD;
接线一定要对。

用的是串口2的空闲中断。

具体代码:

network.h

/******************************************************************* 文件:NetWork.c* 功能:声明TCP、UDP通信相关函数* 日期:2018-04-06* 作者:zx* 版本:Ver.1.0 | 最初版本* * Copyright (C) 2018 zx. All rights reserved.
*******************************************************************/
#ifndef __NETWORK_H
#define __NETWORK_H#include "sys.h"
#include "usart.h"
#include "Lcd_Driver.h"/*连接AP宏定义*/
#define SSID "WIFI_SSID"
#define PWD  "WIFI_PWD"/*连接服务器宏定义*/
#define TCP "TCP"
#define UDP "UDP"
#define IP  "192.168.xx.xx"
#define PORT 22958/*定义原子云的设备地址和密码,在原子云中设置设备的时候,连接原子云的时候需要*/
#define YZYDevAddr "24858907199923482471"
#define YZYDevPassWord "12345678"/*发送接收缓冲区长度宏定义*/
#define TXBUFFER_LEN 100
#define RXBUFFER_LEN 100u8 checkESP8266(void);
u8 initESP8266(void);
void restoreESP8266(void);u8 connectAP(u8* ssid,u8* pwd);u8 connectServer(u8* mode,u8* ip,u16 port);extern uint32_t flashtime;
extern u8 TXBuffer[TXBUFFER_LEN];  //网络通信数据发送缓冲
extern u8 RXBuffer[RXBUFFER_LEN];  //网络通信数据接收缓冲void sendBuffertoServer(u8* buffer);
void processServerBuffer(void);
u8 disconnectServer(void);
u8 initESP8266(void);
u8 connectYZY(char* yzyDevNumtemp,char* yzyDevPassWordtemp);
u8 disconnectYZY(void);
#endif

network.c

/******************************************************************
*******************************************************************/
#include "stdio.h"
#include "string.h"
#include "NetWork.h"u8 TXBuffer[TXBUFFER_LEN] = {0};  //网络通信数据发送缓冲
u8 RXBuffer[RXBUFFER_LEN] = {0};  //网络通信数据接收缓冲/*** 功能:查找字符串中是否包含另一个字符串* 参数:*       dest:待查找目标字符串*       src:待查找内容*       retry_cn:查询超时时间* 返回值:查找结果,非0为查找成功,0为失败* 说明:*       当发出一个AT指令后,需要一段时间等待ESP8266回复,因此就需要等待一段时间,*       这个时间通常是几百ms(除了连接服务器和AP指令),本程序一般指令通常等待*       2S,耗时的连接AP和服务器的设置等待为8S,其实花不了那么多时间,但如果发生超时*       就一定可以判断是通信问题*/
static u8 findStr(u8* dest,u8* src,u16 retry_cn)
{u16 retry = retry_cn;                   //超时时间u8 result_flag = 0;                     //查找结果while(strstr(dest,src)==0 && --retry!=0)//等待串口接收完毕或超时退出{delay_ms(10);}if(retry==0)                            //如果超时则有问题,此时返回0{return 0;}result_flag = 1;                        //执行到这里说明一切正常, 表示查找成功if(result_flag){return 1;}else {return 0;}
}/*** 功能:初始化ESP8266* 参数:None* 返回值:初始化结果,非0为初始化成功,0为失败*/
u8 initESP8266(void)
{               LCD_ShowStringAsc160(0,10,RED, WHITE,"init8266inY");sendString(USART2,"+++");          //退出透传 LCD_ShowStringAsc160(0,10,RED, WHITE,"init8266in0");delay_ms(500);sendString(USART2,"AT+RST\r\n");   //重启ESP8266 LCD_ShowStringAsc160(0,10,RED, WHITE,"init8266in1");delay_ms(500);if(checkESP8266()==0)              //使用AT指令检查ESP8266是否存在{return 0;}memset(RXBuffer,0,RXBUFFER_LEN);   //清空接收缓冲sendString(USART2,"ATE0\r\n");     //关闭回显  LCD_ShowStringAsc160(0,10,RED, WHITE,"init8266in2");if(findStr(RXBuffer,"OK",200)==0)  //设置不成功{return 0;      }return 1;                         //设置成功}/*** 功能:恢复出厂设置* 参数:None* 返回值:None* 说明:此时ESP8266中的用户设置将全部丢失回复成出厂状态*/
void restoreESP8266(void)
{sendString(USART2,"+++");           //退出透传delay_ms(500);sendString(USART2,"AT+RESTORE\r\n");//恢复出厂 NVIC_SystemReset();                 //同时重启单片机
}/*** 功能:检查ESP8266是否正常* 参数:None* 返回值:ESP8266返回状态*        非0 ESP8266正常*        0 ESP8266有问题  */
u8 checkESP8266(void)
{memset(RXBuffer,0,RXBUFFER_LEN); //清空接收缓冲sendString(USART2,"AT\r\n");     //发送AT握手指令if(findStr(RXBuffer,"OK",200)!=0)//ESP8266正常{return 1;  }else                            //ESP8266不正常 {return 0;}
}/*** 功能:连接热点* 参数:*         ssid:热点名*         pwd:热点密码* 返回值:*         连接结果,非0连接成功,0连接失败* 说明: *         失败的原因有以下几种(UART通信和ESP8266正常情况下)*         1. WIFI名和密码不正确*         2. 路由器连接设备太多,未能给ESP8266分配IP*/
u8 connectAP(u8* ssid,u8* pwd)
{memset(RXBuffer,0,RXBUFFER_LEN);                       sendString(USART2,"AT+CWMODE?\r\n");                       //查询此时WIFI工作模式if(findStr(RXBuffer,"CWMODE:1",200)==0)                    //如果此时不是MODE1模式,即不是STATION模式{memset(RXBuffer,0,RXBUFFER_LEN);     sendString(USART2,"AT+CWMODE_CUR=1\r\n");              //设置为STATION模式if(findStr(RXBuffer,"OK",200)==0){return 0;}             }memset(TXBuffer,0,RXBUFFER_LEN);                            //清空发送缓冲memset(RXBuffer,0,RXBUFFER_LEN);                            //清空接收缓冲sprintf(TXBuffer,"AT+CWJAP_CUR=\"%s\",\"%s\"\r\n",ssid,pwd);//连接目标APsendString(USART2,TXBuffer);if(findStr(RXBuffer,"OK",800)!=0)                           //连接成功且分配到IP{return 1;}
}/*** 功能:使用指定协议(TCP/UDP)连接到服务器* 参数:*         mode:协议类型 "TCP","UDP"*         ip:目标服务器IP*         port:目标是服务器端口号* 返回值:*         连接结果,非0连接成功,0连接失败* 说明: *         失败的原因有以下几种(UART通信和ESP8266正常情况下)*         1. 远程服务器IP和端口号有误*         2. 未连接AP*         3. 服务器端禁止添加(一般不会发生)*/
u8 connectServer(u8* mode,u8* ip,u16 port)
{memset(RXBuffer,0,RXBUFFER_LEN);      memset(TXBuffer,0,RXBUFFER_LEN);   sendString(USART2,"+++");                   //多次连接需退出透传delay_ms(500);/*格式化待发送AT指令*/    sprintf(TXBuffer,"AT+CIPSTART=\"%s\",\"%s\",%d\r\n",mode,ip,port);sendString(USART2,TXBuffer);if(findStr(RXBuffer,"CONNECT",800)!=0){memset(RXBuffer,0,RXBUFFER_LEN);    sendString(USART2,"AT+CIPMODE=1\r\n");  //设置为透传模式if(findStr(RXBuffer,"OK",200)!=0){memset(RXBuffer,0,RXBUFFER_LEN); sendString(USART2,"AT+CIPSEND\r\n");//开始处于透传发送状态if(findStr(RXBuffer,">",200)!=0){return 1;}else {return 0;}    }else {return 0;}}else {return 0;}
}/*** 功能:主动和服务器断开连接* 参数:None* 返回值:*         连接结果,非0断开成功,0断开失败*/
u8 disconnectServer(void)
{sendString(USART2,"+++");            //退出透传delay_ms(500);memset(RXBuffer,0,RXBUFFER_LEN);  sendString(USART2,"AT+CIPCLOSE\r\n");//关闭链接if(findStr(RXBuffer,"CLOSED",200)!=0)//操作成功,和服务器成功断开{return 1;}else {return 0;}
}/*** 功能:透传模式下的数据发送函数* 参数:*      buffer:待发送数据* 返回值:None*/
void sendBuffertoServer(u8* buffer)
{memset(RXBuffer,0,RXBUFFER_LEN);sendString(USART2,buffer);
}/*** 功能:连接原子云https://cloud.alientek.com* 参数:*         mode:协议类型 "TCP","UDP"*         ip:目标服务器IP*         port:目标是服务器端口号* 返回值:*         连接结果,非0连接成功,0连接失败* 说明: *        */
u8 connectYZY(char* yzyDevNumtemp,char* yzyDevPassWordtemp)
{memset(RXBuffer,0,RXBUFFER_LEN);      memset(TXBuffer,0,RXBUFFER_LEN);   delay_ms(3000);/*格式化待发送AT指令连接原子云//    sendString(USART2,"AT+RST\r\n");   //重启ESP8266 */    sendString(USART2,"AT+ATKCLDSTA=\"24858907199923482471\",\"12345678\"\r\n");
//      sendString(USART2,"AT+ATKCLDSTA=");
//   sendString(USART2,"\"24858907199923482471\",");
//   sendString(USART2,"\"12345678\"\r\n");delay_ms(8000);//连接成功,会返回CLOUD CONNECTEDif(findStr(RXBuffer,"OK",800)!=0){         LCD_ShowStringAsc160(0,5,RED, WHITE,"ok");return 1;}else {            LCD_ShowStringAsc160(0,5,RED, WHITE,"NG");return 0;}
}/*** 功能:断开原子云* 参数:None* 返回值:*         连接结果,非0断开成功,0断开失败*/
u8 disconnectYZY(void)
{sendString(USART2,"+++");            //退出透传delay_ms(500);memset(RXBuffer,0,RXBUFFER_LEN);  sendString(USART2,"AT+ATKCLDCLS\r\n");//关闭链接if(findStr(RXBuffer,"CLOUD DISCONNECT",200)!=0)//操作成功,和服务器成功断开{return 1;}else {return 0;}
}/*** 功能:处理服务器发回来的控制指令* 参数:None* 返回值:None*/
void processServerBuffer(void)
{ u8 i = 0;u8 numbuftemp0[9]={0};unsigned long numtemp0=0;/*LED状态控制*/if(strstr(RXBuffer,"LED_ON")){//提取亮度数值格式:LED_ON=50#,将等号和#中间的数提取出来//第一步,找到RXBuffer中的等号的位置,#号的下标,然后将数据复制出来到另外一个字符串//将字符串转为memset(numbuftemp0,0,9);numtemp0=FindNumInStr(RXBuffer,numbuftemp0);delay_ms(5);LCD_ShowNumAsc160(0,5,RED, BLUE,numtemp0,5);flashtime=numtemp0;//  openLED();}else if(strstr(RXBuffer,"LED_OFF")){++i;LCD_ShowStringAsc160(0,6,RED, WHITE,"LED_OFF");//  closeLED();}else {}/*继电器1状态控制*/if(strstr(RXBuffer,"RELAY1_CLOSE")){++i;LCD_ShowStringAsc160(0,7,RED, WHITE,"RELAY1_CLOSE");// setRelay(RELAY1,RELAY_CLOSE);}else if(strstr(RXBuffer,"RELAY1_OPEN")){++i;LCD_ShowStringAsc160(0,7,RED, WHITE,"RELAY1_OPEN");//  setRelay(RELAY1,RELAY_OPEN);}else {}/*继电器2状态控制*/if(strstr(RXBuffer,"RELAY2_CLOSE")){++i;// setRelay(RELAY2,RELAY_CLOSE);}else if(strstr(RXBuffer,"RELAY2_OPEN")){++i;//  setRelay(RELAY2,RELAY_OPEN);}else {}  /*只在接收控制指令时才清空,这样可避免接收AT指令时导致失败*/if(i!=0){memset(RXBuffer,0,RXBUFFER_LEN);}
}

第二个DS1302的使用。
主要参考资料
金沙滩的DS1302资料。
用的是burst模式;
初始化之后,读取时间就行了。需要修改时间的话,记得是DS1302是BCD编码。
什么是BCD编码?
DS1302中,看寄存器手册。用8Ch或者是8D年份寄存器。
比如2015年,这个寄存器只放的是后面两个15,将15的1分离出来,放在高四位,5分离出来,放在第四位,就可以设置了。

怎么分离?
十进制的23,,怎么转变成===0x23呢,?
(23/10)=2,用16进制是0x02,左移4位,23%10=3
则(23/10)<<4+(23%10)=0x23.

0x23如何转成十进制的23呢?
(0x23>>4)*10+(0x23&0x0F)=23.

另外注意,DS1302的数据IO口,有时候做输入口,有时候做输出口, 需跟进实际情况设置

DS1302.h

#ifndef __DS1302_H
#define __DS1302_H
#include "sys.h"
#include "delay.h"#define DS1302_CE PFout(3)  //
#define DS1302_CK PFout(5)  //
#define DS1302_IOin PFin(6) //
#define DS1302_IOout PFout(6)   // #define DS1302_CE_PORT     GPIOF //需要根据实际修改:端口
#define DS1302_CE_PIN       GPIO_Pin_3//需要根据修改:引脚号
#define DS1302_CE_CLK_ENA() {RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);}
//根据实际修改:时钟线,时钟使能#define DS1302_CK_PORT     GPIOF //需要根据实际修改:端口
#define DS1302_CK_PIN       GPIO_Pin_5//需要根据修改:引脚号
#define DS1302_CK_CLK_ENA() {RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);}
//根据实际修改:时钟线,时钟使能#define DS1302_IO_PORT     GPIOF //需要根据实际修改:端口
#define DS1302_IO_PIN       GPIO_Pin_6//需要根据修改:引脚号
#define DS1302_IO_CLK_ENA() {RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF,ENABLE);}
//根据实际修改:时钟线,时钟使能struct sTime {  //日期时间结构体定义unsigned int  year;  //年0x13===2013年。前面的20是默认的。不用书写unsigned char mon;   //月0x10unsigned char day;   //日0x08unsigned char hour;  //时0x12unsigned char min;   //分0x30unsigned char sec;   //秒0x00unsigned char week;  //星期
};void DS1302_IOIN();
void DS1302_IOOUT();/* 发送一个字节到DS1302通信总线上 */
void DS1302ByteWrite(unsigned char dat);
/* 由DS1302通信总线上读取一个字节 */
unsigned char DS1302ByteRead();
/* 用单次写操作向某一寄存器写入一个字节,reg-寄存器地址,dat-待写入字节 */
void DS1302SingleWrite(unsigned char reg, unsigned char dat);
/* 用单次读操作从某一寄存器读取一个字节,reg-寄存器地址,返回值-读到的字节 */
unsigned char DS1302SingleRead(unsigned char reg);
/* 用突发模式连续写入8个寄存器数据,dat-待写入数据指针 */
void DS1302BurstWrite(unsigned char *dat);
/* 用突发模式连续读取8个寄存器的数据,dat-读取数据的接收指针 */
void DS1302BurstRead(unsigned char *dat);
/* 获取实时时间,即读取DS1302当前时间并转换为时间结构体格式 */
void GetRealTime(struct sTime *time);
/* 设定实时时间,时间结构体格式的设定时间转换为数组并写入DS1302 */
void SetRealTime(struct sTime *time);
/* DS1302初始化,如发生掉电则重新设置初始时间 */
void InitDS1302();#endif

DS1302.c

/*
*******************************************************************************
*                     《手把手教你学51单片机(C语言版)》
*                    配套 KST-51 单片机开发板 示例源代码
*
*         (c) 版权所有 2014 金沙滩工作室/清华大学出版社 保留所有权利
*                 获取更多资料请访问:http://www.kingst.org
*
* 文件名:DS1302.c
* 描  述:实时时钟芯片DS1302驱动模块
* 版本号:v1.0.0
* 备  注:
*******************************************************************************
*/#include "DS1302.h"/* 发送一个字节到DS1302通信总线上 */
void DS1302ByteWrite(unsigned char dat)
{// unsigned char mask;u8 i=0;DS1302_IOOUT();delay_ms(1);for(i = 0; i < 8; i ++){DS1302_IOout     = dat & 0x01;DS1302_CK = 1;DS1302_CK = 0;dat    >>= 1;}DS1302_IOout = 1;           //最后确保释放IO引脚
}
/* 由DS1302通信总线上读取一个字节 */
unsigned char DS1302ByteRead()
{unsigned char mask;unsigned char dat = 0;DS1302_IOIN();delay_ms(1);for (mask=0x01; mask!=0; mask<<=1)  //低位在前,逐位读取{if (DS1302_IOin != 0)  //首先读取此时的IO引脚,并设置dat中的对应位{dat |= mask;}DS1302_CK = 1;       //然后拉高时钟delay_us(5);DS1302_CK = 0;       //再拉低时钟,完成一个位的操作}return dat;              //最后返回读到的字节数据
}
/* 用单次写操作向某一寄存器写入一个字节,reg-寄存器地址,dat-待写入字节 */
void DS1302SingleWrite(unsigned char reg, unsigned char dat)
{DS1302_CE = 1;                   //使能片选信号delay_us(5);DS1302ByteWrite((reg<<1)|0x80);  //发送写寄存器指令delay_us(5);DS1302ByteWrite(dat);            //写入字节数据delay_us(5);DS1302_CE = 0;                   //除能片选信号
}
/* 用单次读操作从某一寄存器读取一个字节,reg-寄存器地址,返回值-读到的字节 */
unsigned char DS1302SingleRead(unsigned char reg)
{unsigned char dat;DS1302_CE = 1;                   //使能片选信号delay_us(5);DS1302ByteWrite((reg<<1)|0x81);  //发送读寄存器指令delay_us(5);dat = DS1302ByteRead();          //读取字节数据delay_us(5);DS1302_CE = 0;                   //除能片选信号return dat;
}
/* 用突发模式连续写入8个寄存器数据,dat-待写入数据指针 */
void DS1302BurstWrite(unsigned char *dat)
{unsigned char i;DS1302_CE = 1;DS1302ByteWrite(0xBE);  //发送突发写寄存器指令for (i=0; i<8; i++)     //连续写入8字节数据{DS1302ByteWrite(dat[i]);}DS1302_CE = 0;
}
/* 用突发模式连续读取8个寄存器的数据,dat-读取数据的接收指针 */
void DS1302BurstRead(unsigned char *dat)
{unsigned char i;DS1302_CE = 1;delay_us(5);DS1302ByteWrite(0xBF);  //发送突发读寄存器指令for (i=0; i<8; i++)     //连续读取8个字节{dat[i] = DS1302ByteRead();delay_us(5);}DS1302_CE = 0;
}
/* 获取实时时间,即读取DS1302当前时间并转换为时间结构体格式 */
void GetRealTime(struct sTime *time)
{unsigned char buf[8];DS1302BurstRead(buf);time->year = buf[6]+ 0x2000 ;time->mon  = buf[4];time->day  = buf[3];time->hour = buf[2];time->min  = buf[1];time->sec  = buf[0];time->week = buf[5];
}
/* 设定实时时间,时间结构体格式的设定时间转换为数组并写入DS1302 */
void SetRealTime(struct sTime *time)
{unsigned char buf[8];buf[7] = 0;buf[6] = time->year;//0x2021buf[5] = time->week;buf[4] = time->mon;buf[3] = time->day;buf[2] = time->hour;buf[1] = time->min;buf[0] = time->sec;DS1302BurstWrite(buf);//数组
}
/* DS1302初始化,如发生掉电则重新设置初始时间 */
void InitDS1302()
{unsigned char dat;struct sTime InitTime = {  //2013年10月8日 12:30:00 星期二0x2021,0x01,0x01, 0x23,0x56,0x00, 0x02};//  u8 temp=45;GPIO_InitTypeDef GPIO_InitStructure;//InitTime.min=((temp/10)<<4)+(temp%10);DS1302_CE_CLK_ENA();GPIO_InitStructure.GPIO_Pin = DS1302_CE_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ;   //推挽输出GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(DS1302_CE_PORT, &GPIO_InitStructure);DS1302_CK_CLK_ENA();GPIO_InitStructure.GPIO_Pin = DS1302_CK_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ;   //推挽输出GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(DS1302_CK_PORT, &GPIO_InitStructure);DS1302_IO_CLK_ENA();GPIO_InitStructure.GPIO_Pin = DS1302_IO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ;   //推挽输出GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(DS1302_IO_PORT, &GPIO_InitStructure);DS1302_CE = 0;  //初始化DS1302通信引脚DS1302_CK = 0;dat = DS1302SingleRead(0);  //读取秒寄存器if ((dat & 0x80) != 0) //不等于0,进入判断,没有备用电池   //由秒寄存器最高位CH的值判断DS1302是否已停止//CH的作用:判断掉电后是否正常时钟//CH0:有备用电源//CH1:没有备用电池{DS1302SingleWrite(7, 0x00);  //撤销写保护以允许写入数据delay_ms(1);SetRealTime(&InitTime);      //设置DS1302为默认的初始时间}//回复默认的时间
//       SetRealTime(&InitTime);      //设置DS1302为默认的初始时间}
//设置输入模式
void DS1302_IOIN()
{GPIO_InitTypeDef GPIO_InitStructure;DS1302_IO_CLK_ENA();GPIO_InitStructure.GPIO_Pin =DS1302_IO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN ;   //推挽GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(DS1302_IO_PORT, &GPIO_InitStructure);
}
//设置输出模式
void DS1302_IOOUT()
{GPIO_InitTypeDef GPIO_InitStructure;DS1302_IO_CLK_ENA();GPIO_InitStructure.GPIO_Pin =DS1302_IO_PIN;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ;   //推挽GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(DS1302_IO_PORT, &GPIO_InitStructure);
}

第三个,AT24C02,用的是正点原子的例子。
AT2402,可以放252个字节,主要是IIC的使用。
使用的时候,
初始化,
检测初始化是不是OK。
读取24c02的数据,
写入数据。
这里一个一字节songoing,接收。
包括额IIC的代码和24C02的代码。

myiic.h

#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK STM32F407开发板
//IIC 驱动代码
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//创建日期:2014/5/6
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2014-2024
//All rights reserved
//
//IO操作函数
#define IIC_SCL    PBout(6) //SCL#define IIC_SDA    PBout(7) //SDA
#define READ_SDA   PBin(7)  //输入SDA //IO方向设置
//#define SDA_IN()  {GPIOF->MODER&=~(3<<(9*2));GPIOF->MODER|=0<<9*2;}   //PB9输入模式
//#define SDA_OUT() {GPIOF->MODER&=~(3<<(9*2));GPIOF->MODER|=1<<9*2;} //PB9输出模式
void SDA_IN();
void SDA_OUT();//IIC所有操作函数
void IIC_Init(void);                //初始化IIC的IO口
void IIC_Start(void);               //发送IIC开始信号
void IIC_Stop(void);                //发送IIC停止信号
void IIC_Send_Byte(u8 txd);         //IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void);              //IIC等待ACK信号
void IIC_Ack(void);                 //IIC发送ACK信号
void IIC_NAck(void);                //IIC不发送ACK信号void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif

myiic.c

#include "myiic.h"
#include "delay.h"
//
//初始化IIC
void IIC_Init(void)
{                        GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ;   //推挽输出GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOB, &GPIO_InitStructure);//GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);    //PB6,PB7 输出高IIC_SCL=1;IIC_SDA=1;}
void SDA_IN()
{GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟GPIO_InitStructure.GPIO_Pin =GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN ;   //推挽GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOB, &GPIO_InitStructure);}
void SDA_OUT()
{GPIO_InitTypeDef GPIO_InitStructure;RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);//使能GPIOB时钟GPIO_InitStructure.GPIO_Pin =GPIO_Pin_7;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT ;   //推挽GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉GPIO_Init(GPIOB, &GPIO_InitStructure);
}//产生IIC起始信号
void IIC_Start(void)
{SDA_OUT();     //sda线输出IIC_SDA=1;       IIC_SCL=1;delay_us(4);IIC_SDA=0;//START:when CLK is high,DATA change form high to low delay_us(4);IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{SDA_OUT();//sda线输出IIC_SCL=0;IIC_SDA=0;//STOP:when CLK is high DATA change form low to highdelay_us(4);IIC_SCL=1; IIC_SDA=1;//发送I2C总线结束信号delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
//        0,接收应答成功
u8 IIC_Wait_Ack(void)
{u8 ucErrTime=0;SDA_IN();      //SDA设置为输入  IIC_SDA=1;delay_us(1);    IIC_SCL=1;delay_us(1);   while(READ_SDA){ucErrTime++;if(ucErrTime>250){IIC_Stop();return 1;}}IIC_SCL=0;//时钟输出0       return 0;
}
//产生ACK应答
void IIC_Ack(void)
{IIC_SCL=0;SDA_OUT();IIC_SDA=0;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;
}
//不产生ACK应答
void IIC_NAck(void)
{IIC_SCL=0;SDA_OUT();IIC_SDA=1;delay_us(2);IIC_SCL=1;delay_us(2);IIC_SCL=0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{                        u8 t;   SDA_OUT();         IIC_SCL=0;//拉低时钟开始数据传输for(t=0;t<8;t++){              //IIC_SDA=(txd&0x80)>>7;if((txd&0x80)>>7)IIC_SDA=1;elseIIC_SDA=0;txd<<=1;       delay_us(2);   //对TEA5767这三个延时都是必须的IIC_SCL=1;delay_us(2); IIC_SCL=0;    delay_us(2);}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
{unsigned char i,receive=0;SDA_IN();//SDA设置为输入for(i=0;i<8;i++ ){IIC_SCL=0; delay_us(2);IIC_SCL=1;receive<<=1;if(READ_SDA)receive++;   delay_us(1); }                   if (!ack)IIC_NAck();//发送nACKelseIIC_Ack(); //发送ACK   return receive;
}

24cxx.h

#ifndef __24CXX_H
#define __24CXX_H
#include "myiic.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK战舰STM32开发板
//24CXX驱动 代码(适合24C01~24C16,24C32~256未经过测试!有待验证!)
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/9/9
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
//#define AT24C01       127
#define AT24C02     255
#define AT24C04     511
#define AT24C08     1023
#define AT24C16     2047
#define AT24C32     4095
#define AT24C64     8191
#define AT24C128    16383
#define AT24C256    32767
//Mini STM32开发板使用的是24c02,所以定义EE_TYPE为AT24C02
#define EE_TYPE AT24C02u8 AT24CXX_ReadOneByte(u16 ReadAddr);                            //指定地址读取一个字节
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite);        //指定地址写入一个字节
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len);//指定地址开始写入指定长度的数据
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len);                   //指定地址开始读取指定长度数据
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite);   //从指定地址开始写入指定长度的数据
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead);      //从指定地址开始读出指定长度的数据u8 AT24CXX_Check(void);  //检查器件
void AT24CXX_Init(void); //初始化IIC
#endif

24cxx.c

#include "24cxx.h"
#include "delay.h"
//
//本程序只供学习使用,未经作者许可,不得用于其它任何用途
//ALIENTEK战舰STM32开发板
//24CXX驱动 代码(适合24C01~24C16,24C32~256未经过测试!有待验证!)
//正点原子@ALIENTEK
//技术论坛:www.openedv.com
//修改日期:2012/9/9
//版本:V1.0
//版权所有,盗版必究。
//Copyright(C) 广州市星翼电子科技有限公司 2009-2019
//All rights reserved
////初始化IIC接口
void AT24CXX_Init(void)
{IIC_Init();
}
//在AT24CXX指定地址读出一个数据
//ReadAddr:开始读数的地址
//返回值  :读到的数据
u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{                 u8 temp=0;                                                                                IIC_Start();  if(EE_TYPE>AT24C16){IIC_Send_Byte(0XA0);     //发送写命令IIC_Wait_Ack();IIC_Send_Byte(ReadAddr>>8);//发送高地址IIC_Wait_Ack();         }else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据    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;
}
//在AT24CXX指定地址写入一个数据
//WriteAddr  :写入数据的目的地址
//DataToWrite:要写入的数据
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{                                                                                            IIC_Start();  if(EE_TYPE>AT24C16){IIC_Send_Byte(0XA0);      //发送写命令IIC_Wait_Ack();IIC_Send_Byte(WriteAddr>>8);//发送高地址}else{IIC_Send_Byte(0XA0+((WriteAddr/256)<<1));   //发送器件地址0XA0,写数据 }   IIC_Wait_Ack();       IIC_Send_Byte(WriteAddr%256);   //发送低地址IIC_Wait_Ack();                                                      IIC_Send_Byte(DataToWrite);     //发送字节                              IIC_Wait_Ack();                     IIC_Stop();//产生一个停止条件 delay_ms(10);
}
//在AT24CXX里面的指定地址开始写入长度为Len的数据
//该函数用于写入16bit或者32bit的数据.
//WriteAddr  :开始写入的地址
//DataToWrite:数据数组首地址
//Len        :要写入数据的长度2,4
void AT24CXX_WriteLenByte(u16 WriteAddr,u32 DataToWrite,u8 Len)
{   u8 t;for(t=0;t<Len;t++){AT24CXX_WriteOneByte(WriteAddr+t,(DataToWrite>>(8*t))&0xff);}
}//在AT24CXX里面的指定地址开始读出长度为Len的数据
//该函数用于读出16bit或者32bit的数据.
//ReadAddr   :开始读出的地址
//返回值     :数据
//Len        :要读出数据的长度2,4
u32 AT24CXX_ReadLenByte(u16 ReadAddr,u8 Len)
{   u8 t;u32 temp=0;for(t=0;t<Len;t++){temp<<=8;temp+=AT24CXX_ReadOneByte(ReadAddr+Len-t-1);                      }return temp;
}
//检查AT24CXX是否正常
//这里用了24XX的最后一个地址(255)来存储标志字.
//如果用其他24C系列,这个地址要修改
//返回1:检测失败
//返回0:检测成功
u8 AT24CXX_Check(void)
{u8 temp;temp=AT24CXX_ReadOneByte(255);//避免每次开机都写AT24CXX              if(temp==0X55)return 0;           else//排除第一次初始化的情况{AT24CXX_WriteOneByte(255,0X55);temp=AT24CXX_ReadOneByte(255);   if(temp==0X55)return 0;}return 1;
}//在AT24CXX里面的指定地址开始读出指定个数的数据
//ReadAddr :开始读出的地址 对24c02为0~255
//pBuffer  :数据数组首地址
//NumToRead:要读出数据的个数
void AT24CXX_Read(u16 ReadAddr,u8 *pBuffer,u16 NumToRead)
{while(NumToRead){*pBuffer++=AT24CXX_ReadOneByte(ReadAddr++);  NumToRead--;}
}
//在AT24CXX里面的指定地址开始写入指定个数的数据
//WriteAddr :开始写入的地址 对24c02为0~255
//pBuffer   :数据数组首地址
//NumToWrite:要写入数据的个数
void AT24CXX_Write(u16 WriteAddr,u8 *pBuffer,u16 NumToWrite)
{while(NumToWrite--){AT24CXX_WriteOneByte(WriteAddr,*pBuffer);WriteAddr++;pBuffer++;}
}

第四个,key状态机,用了5个按键,定时器3做定时扫描。

key.h

#ifndef __KeyStatusMachineTimer3_H
#define __KeyStatusMachineTimer3_H
#include "sys.h"
#include "Lcd_Driver.h"//KEY端口定义
#define KEY0 PGin(3)    // DS0
#define KEY1 PGin(2)    // DS0
#define KEY2 PGin(4)    // DS0
#define KEY3 PGin(0)    // DS0
#define KEY4 PGin(1)    // DS0#define KEY0_PORT       GPIOG //需要根据实际修改:端口
#define KEY0_PIN       GPIO_Pin_3//需要根据修改:引脚号
#define KEY0_CLK_ENA() {RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);}//根据实际修改:时钟线,时钟使能#define KEY1_PORT     GPIOG //需要根据实际修改:端口
#define KEY1_PIN       GPIO_Pin_2//需要根据修改:引脚号
#define KEY1_CLK_ENA() {RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);}//根据实际修改:时钟线,时钟使能#define KEY2_PORT     GPIOG //需要根据实际修改:端口
#define KEY2_PIN       GPIO_Pin_4//需要根据修改:引脚号
#define KEY2_CLK_ENA() {RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);}//根据实际修改:时钟线,时钟使能#define KEY3_PORT     GPIOG //需要根据实际修改:端口
#define KEY3_PIN       GPIO_Pin_0//需要根据修改:引脚号
#define KEY3_CLK_ENA() {RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);}//根据实际修改:时钟线,时钟使能#define KEY4_PORT     GPIOG //需要根据实际修改:端口
#define KEY4_PIN       GPIO_Pin_1//需要根据修改:引脚号
#define KEY4_CLK_ENA() {RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);}//根据实际修改:时钟线,时钟使能//定义枚举类型
typedef enum
{STA1_KEY_Up                    =(unsigned char)0x01,STA2_KEY_DownShake    =(unsigned char)0x02,STA3_KEY_Down             =(unsigned char)0x03,STA4_KEY_UpShake      =(unsigned char)0x04,
}STA_Machine_Status_t;//定义按键结构体的类型
typedef struct
{uint8_t volatile Key_Flag;uint8_t Click;uint8_t DoubleClick;uint8_t LongPress;void (*KEY_IOInit)(void);void (*KEY_Press)(void);//按键检测}KEY_t;
//定义状态机的结构体类型
typedef struct
{STA_Machine_Status_t               ucSTA_Machine_Status;//状态机状态uint16_t volatile   ucSTA_Machine_Scan_Timer;//状态机扫描定时器uint16_t volatile    ucKey_DoubleClick_Timer;//按键双击定时器uint16_t volatile  ucKey_LongPress_Timer;//按键长按扫描定时器
}STA_Machine_t;//定时器Time3的设置,定时器5ms进行扫描
typedef enum
{Timer3_10ms=(uint16_t)2,Timer3_50ms=(uint16_t)10,Timer3_100ms=(uint16_t)20,Timer3_200ms=(uint16_t)40,Timer3_500ms=(uint16_t)100,Timer3_1s=(uint16_t)200,Timer3_2s=(uint16_t)400,
}TIMER3_Value_t;typedef struct
{uint16_t volatile usMCU_RUN_Timer;void (*Timer3_Init)(uint16_t arr,uint16_t psc);
}Timer3_t;/*extern valueable---*/
extern KEY_t Keyii,Keyi1,Keyi2,Keyi3,Keyi4;
extern u8 setIndex;
extern u8 setParaIndex;
extern Timer3_t timer3;extern void  IncSetTime();
extern void LeftShiftTimeSet();
extern void RightShiftTimeSet();
extern void EnterTimeSet();
extern void ExitTimeSet(u8 save);
extern void DecSetTime();
extern void IncSetTime();extern void AddPar();
extern void SubPar();
extern void RightShiftParaSet();
extern void LeftShiftParaSet();
extern void EnterParaSet();
extern void ExitParaSet(u8 save);
extern void RefreshYZShow();
extern void RefreshSetParaShow();/* 刷新当前设置位的光标指示 */
extern void RefreshDataShow();
extern void DataBackUp();//备份数据到24C02中//================按键的函数
void KeyGpioInit(void);//初始化
void KeyTimer3Init(uint16_t arr,uint16_t psc);
void KeyConfig(void);//static void KEY_Detect(void);
#endif

key.c

#include "KeyStatusMachineTimer3.h"static void KEY_Detect(void);#define SetLongPressTime Timer3_2s //设定长按的时间
#define SetDoubleClickTime Timer3_200ms //设定双击时间static uint8_t ClickBuf=0;//单击状态缓存
static uint8_t ClickBuf1=0;//单击状态缓存
static uint8_t ClickBuf2=0;//单击状态缓存
static uint8_t ClickBuf3=0;//单击状态缓存
static uint8_t ClickBuf4=0;//单击状态缓存static uint8_t ClickCount=0;//按键次数
static uint8_t ClickCount1=0;static uint8_t DoubleClickCount=0;//按键次数
static uint8_t DoubleClickCount1=0;
static uint8_t LongClickCount=0;//按键次数
static uint8_t LongClickCount1=0;static uint8_t T1HourSet=8;//早上八点
static uint8_t T1MinSet=30;//早上三十分钟static uint8_t T2HourSet=11;//早上八点
static uint8_t T2MinSet=30;//早上三十分钟static uint8_t T3HourSet=17;//早上八点
static uint8_t T3MinSet=30;//早上三十分钟
//1浇水,0不浇水
uint8_t T1Flag=1;//1,一个时间点,早上浇水,=2,中午浇水,=3,晚上浇水,=4三个都可以浇水
uint8_t T2Flag=1;//1,一个时间点,早上浇水,=2,中午浇水,=3,晚上浇水,=4三个都可以浇水
uint8_t T3Flag=1;//1,一个时间点,早上浇水,=2,中午浇水,=3,晚上浇水,=4三个都可以浇水KEY_t Keyii={0,0,0,0,KeyGpioInit,KEY_Detect};
KEY_t Keyi1={0,0,0,0,KeyGpioInit,KEY_Detect};
KEY_t Keyi2={0,0,0,0,KeyGpioInit,KEY_Detect};
KEY_t Keyi3={0,0,0,0,KeyGpioInit,KEY_Detect};
KEY_t Keyi4={0,0,0,0,KeyGpioInit,KEY_Detect};Timer3_t timer3={0,KeyTimer3Init};
STA_Machine_t STA_Machine={STA1_KEY_Up,0,0,0};
STA_Machine_t STA_Machine1={STA1_KEY_Up,0,0,0};
STA_Machine_t STA_Machine2={STA1_KEY_Up,0,0,0};
STA_Machine_t STA_Machine3={STA1_KEY_Up,0,0,0};
STA_Machine_t STA_Machine4={STA1_KEY_Up,0,0,0};void KeyGpioInit(void)//初始化
{GPIO_InitTypeDef  GPIO_InitStructure;      KEY0_CLK_ENA();GPIO_InitStructure.GPIO_Pin = KEY0_PIN;GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN;   //输出模式GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;    //上拉GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度选择   GPIO_Init(KEY0_PORT, &GPIO_InitStructure);KEY1_CLK_ENA();GPIO_InitStructure.GPIO_Pin = KEY1_PIN;GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN;   //输出模式GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;  //上拉GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度选择   GPIO_Init(KEY1_PORT, &GPIO_InitStructure);KEY2_CLK_ENA();GPIO_InitStructure.GPIO_Pin = KEY2_PIN;GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN;   //输出模式GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;  //上拉GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度选择   GPIO_Init(KEY2_PORT, &GPIO_InitStructure);  KEY3_CLK_ENA();GPIO_InitStructure.GPIO_Pin = KEY3_PIN;GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN;   //输出模式GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;    //上拉GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度选择   GPIO_Init(KEY3_PORT, &GPIO_InitStructure);KEY4_CLK_ENA();GPIO_InitStructure.GPIO_Pin = KEY4_PIN;GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN;   //输出模式GPIO_InitStructure.GPIO_PuPd  = GPIO_PuPd_UP;  //上拉GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度选择   GPIO_Init(KEY4_PORT, &GPIO_InitStructure);}//-------------------------//定时5ms
void KeyTimer3Init(uint16_t arr,uint16_t psc)
{TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure;RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);     //=====TIM8时钟使能    TIM_TimeBaseInitStructure.TIM_Period = arr;    //自动重装载值TIM_TimeBaseInitStructure.TIM_Prescaler=psc;  //定时器分频TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;// 重复计数器的值,没用到不用管TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化TIM8TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //允许定时器8更新中断  TIM_ClearFlag(TIM3,TIM_FLAG_Update);TIM_Cmd(TIM3,ENABLE); //定时器8//确定定时器8的中断优先级,//     //定时器8中断配置NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; //定时器8更新中断NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x00; //抢占优先级0NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x01; //子优先级01NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_Init(&NVIC_InitStructure);
}void TIM3_IRQHandler(void)
{static int count00=0;if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET){  TIM_ClearITPendingBit(TIM3, TIM_IT_Update);STA_Machine.ucKey_DoubleClick_Timer++;STA_Machine.ucKey_LongPress_Timer++;STA_Machine.ucSTA_Machine_Scan_Timer++;STA_Machine1.ucKey_DoubleClick_Timer++;STA_Machine1.ucKey_LongPress_Timer++;STA_Machine1.ucSTA_Machine_Scan_Timer++;STA_Machine2.ucKey_DoubleClick_Timer++;STA_Machine2.ucKey_LongPress_Timer++;STA_Machine2.ucSTA_Machine_Scan_Timer++;STA_Machine3.ucKey_DoubleClick_Timer++;STA_Machine3.ucKey_LongPress_Timer++;STA_Machine3.ucSTA_Machine_Scan_Timer++;STA_Machine4.ucKey_DoubleClick_Timer++;STA_Machine4.ucKey_LongPress_Timer++;STA_Machine4.ucSTA_Machine_Scan_Timer++;count00++;if(count00>=Timer3_1s)//125/5=25{count00=0;}} }static void KEY_Detect(void)
{//定时时间到10ms,进行一次状态监测if(STA_Machine.ucSTA_Machine_Scan_Timer>Timer3_10ms){//LED0=!LED0;//判断第一个个按键switch(STA_Machine.ucSTA_Machine_Status){case STA1_KEY_Up://如果当前是弹起状态{//LED0=0;if(KEY0==1)//没有按下按键{if(ClickBuf==1)//之前有一次按下{if(STA_Machine.ucKey_DoubleClick_Timer>=SetDoubleClickTime)//超过双击时间{Keyii.Key_Flag=1;//有按键按下Keyii.Click=1;//ClickBuf=0;//清除单击缓存}}}else //之前没有按键按下来,现在按键暗下来,那么进入下一个状态{STA_Machine.ucSTA_Machine_Status=STA2_KEY_DownShake;}break;}case STA2_KEY_DownShake://如果当前是抖动状态,10ms到现在这里,检测到有按键暗下来{if(KEY0==0)//如果有按键按下,跳转到按键按下状态,否则是弹起状态{STA_Machine.ucSTA_Machine_Status=STA3_KEY_Down;STA_Machine.ucKey_LongPress_Timer=0;//长按定时器清0,开始计时}
//                  else
//                      {STA_Machine.ucSTA_Machine_Status=STA1_KEY_Up;}    break;}case STA3_KEY_Down:{if(KEY0==0){//长按检测if(Keyii.LongPress==0)//检测长按是不是执行,不然会重复判断,没有长按{if(STA_Machine.ucKey_LongPress_Timer>=SetLongPressTime){STA_Machine.ucSTA_Machine_Status=STA4_KEY_UpShake;Keyii.Key_Flag=1;Keyii.LongPress=1;//超过两秒,说明是长按的状态//LED0=!LED0;if (setIndex == 0)  //不处于设置状态时,进入设置状态{EnterTimeSet();}else                //已处于设置状态时,保存时间并退出设置状态{ExitTimeSet(1);}if(ClickBuf==1)ClickBuf=0;//检测到长按,清除上一次的按键单击缓存}}}else{STA_Machine.ucSTA_Machine_Status=STA4_KEY_UpShake;if(Keyii.LongPress==0){//双击检查if(ClickBuf==1){Keyii.Key_Flag=1;Keyii.DoubleClick=1;ClickBuf=0;}else{ClickBuf=1;STA_Machine.ucKey_DoubleClick_Timer=0;}}}break;}case STA4_KEY_UpShake:{if(KEY0==1)//如果有按键按下,跳转到按键抖动状态,否则的确是弹起状态{STA_Machine.ucSTA_Machine_Status=STA1_KEY_Up;}break;}default:STA_Machine.ucSTA_Machine_Status=STA1_KEY_Up;}
//======================================================//判断第2个个按键switch(STA_Machine1.ucSTA_Machine_Status){case STA1_KEY_Up://如果当前是弹起状态{if(KEY1==1)//没有按下按键{if(ClickBuf1==1)//之前有一次按下{if(STA_Machine1.ucKey_DoubleClick_Timer>=SetDoubleClickTime)//超过双击时间{Keyi1.Key_Flag=1;//有按键按下Keyi1.Click=1;//ClickBuf1=0;//清除单击缓存}}}else //之前没有按键按下来,现在按键暗下来,那么进入下一个状态{STA_Machine1.ucSTA_Machine_Status=STA2_KEY_DownShake;}break;}case STA2_KEY_DownShake://如果当前是抖动状态,10ms到现在这里,检测到有按键暗下来{if(KEY1==0)//如果有按键按下,跳转到按键按下状态,否则是弹起状态{STA_Machine1.ucSTA_Machine_Status=STA3_KEY_Down;STA_Machine1.ucKey_LongPress_Timer=0;//长按定时器清0,开始计时}
//                  else
//                      {STA_Machine.ucSTA_Machine_Status=STA1_KEY_Up;}    break;}case STA3_KEY_Down:{if(KEY1==0){//长按检测if(Keyi1.LongPress==0)//检测长按是不是执行,不然会重复判断,没有长按{if(STA_Machine1.ucKey_LongPress_Timer>=SetLongPressTime){STA_Machine1.ucSTA_Machine_Status=STA4_KEY_UpShake;Keyi1.Key_Flag=1;Keyi1.LongPress=1;//超过两秒,说明是长按的状态//    LED0=!LED0;//   ExitTimeSet(0);if(ClickBuf1==1)ClickBuf1=0;//检测到长按,清除上一次的按键单击缓存}}}else{STA_Machine1.ucSTA_Machine_Status=STA4_KEY_UpShake;if(Keyi1.LongPress==0){//双击检查if(ClickBuf1==1){Keyi1.Key_Flag=1;Keyi1.DoubleClick=1;ClickBuf1=0;}else{ClickBuf1=1;STA_Machine1.ucKey_DoubleClick_Timer=0;}}}break;}case STA4_KEY_UpShake:{if(KEY1==1)//如果有按键按下,跳转到按键抖动状态,否则的确是弹起状态{STA_Machine1.ucSTA_Machine_Status=STA1_KEY_Up;}break;}default:STA_Machine1.ucSTA_Machine_Status=STA1_KEY_Up;}//KEY2========================================================switch(STA_Machine2.ucSTA_Machine_Status){case STA1_KEY_Up://如果当前是弹起状态{//LED0=0;if(KEY2==1)//没有按下按键{if(ClickBuf2==1)//之前有一次按下{if(STA_Machine2.ucKey_DoubleClick_Timer>=SetDoubleClickTime)//超过双击时间{Keyi2.Key_Flag=1;//有按键按下Keyi2.Click=1;//ClickBuf2=0;//清除单击缓存}}}else //之前没有按键按下来,现在按键暗下来,那么进入下一个状态{STA_Machine2.ucSTA_Machine_Status=STA2_KEY_DownShake;}break;}case STA2_KEY_DownShake://如果当前是抖动状态,10ms到现在这里,检测到有按键暗下来{if(KEY2==0)//如果有按键按下,跳转到按键按下状态,否则是弹起状态{STA_Machine2.ucSTA_Machine_Status=STA3_KEY_Down;STA_Machine2.ucKey_LongPress_Timer=0;//长按定时器清0,开始计时}
//                  else
//                      {STA_Machine.ucSTA_Machine_Status=STA1_KEY_Up;}    break;}case STA3_KEY_Down:{if(KEY2==0){//长按检测if(Keyi2.LongPress==0)//检测长按是不是执行,不然会重复判断,没有长按{if(STA_Machine2.ucKey_LongPress_Timer>=SetLongPressTime){STA_Machine2.ucSTA_Machine_Status=STA4_KEY_UpShake;Keyi2.Key_Flag=1;Keyi2.LongPress=1;//超过两秒,说明是长按的状态
//长按,不断增加while(KEY2==0){AddPar();delay_ms(10);}if(ClickBuf2==1)ClickBuf2=0;//检测到长按,清除上一次的按键单击缓存}}}else{STA_Machine2.ucSTA_Machine_Status=STA4_KEY_UpShake;if(Keyi2.LongPress==0){//双击检查if(ClickBuf2==1){Keyi2.Key_Flag=1;Keyi2.DoubleClick=1;ClickBuf2=0;}else{ClickBuf2=1;STA_Machine2.ucKey_DoubleClick_Timer=0;}}}break;}case STA4_KEY_UpShake:{if(KEY2==1)//如果有按键按下,跳转到按键抖动状态,否则的确是弹起状态{STA_Machine2.ucSTA_Machine_Status=STA1_KEY_Up;}break;}default:STA_Machine2.ucSTA_Machine_Status=STA1_KEY_Up;}//KEY3===============================switch(STA_Machine3.ucSTA_Machine_Status){case STA1_KEY_Up://如果当前是弹起状态{//LED0=0;if(KEY3==1)//没有按下按键{if(ClickBuf3==1)//之前有一次按下{if(STA_Machine3.ucKey_DoubleClick_Timer>=SetDoubleClickTime)//超过双击时间{Keyi3.Key_Flag=1;//有按键按下Keyi3.Click=1;//ClickBuf3=0;//清除单击缓存}}}else //之前没有按键按下来,现在按键暗下来,那么进入下一个状态{STA_Machine3.ucSTA_Machine_Status=STA2_KEY_DownShake;}break;}case STA2_KEY_DownShake://如果当前是抖动状态,10ms到现在这里,检测到有按键暗下来{if(KEY3==0)//如果有按键按下,跳转到按键按下状态,否则是弹起状态{STA_Machine3.ucSTA_Machine_Status=STA3_KEY_Down;STA_Machine3.ucKey_LongPress_Timer=0;//长按定时器清0,开始计时}
//                  else
//                      {STA_Machine.ucSTA_Machine_Status=STA1_KEY_Up;}    break;}case STA3_KEY_Down:{if(KEY3==0){//长按检测if(Keyi3.LongPress==0)//检测长按是不是执行,不然会重复判断,没有长按{if(STA_Machine3.ucKey_LongPress_Timer>=SetLongPressTime){STA_Machine3.ucSTA_Machine_Status=STA4_KEY_UpShake;Keyi3.Key_Flag=1;Keyi3.LongPress=1;//超过两秒,说明是长按的状态while(KEY3==0){SubPar();delay_ms(10);}if(ClickBuf3==1)ClickBuf3=0;//检测到长按,清除上一次的按键单击缓存}}}else{STA_Machine3.ucSTA_Machine_Status=STA4_KEY_UpShake;if(Keyi3.LongPress==0){//双击检查if(ClickBuf3==1){Keyi3.Key_Flag=1;Keyi3.DoubleClick=1;ClickBuf3=0;}else{ClickBuf3=1;STA_Machine3.ucKey_DoubleClick_Timer=0;}}}break;}case STA4_KEY_UpShake:{if(KEY3==1)//如果有按键按下,跳转到按键抖动状态,否则的确是弹起状态{STA_Machine3.ucSTA_Machine_Status=STA1_KEY_Up;}break;}default:STA_Machine3.ucSTA_Machine_Status=STA1_KEY_Up;}
//KEY4==========KEY4==========key4switch(STA_Machine4.ucSTA_Machine_Status){case STA1_KEY_Up://如果当前是弹起状态{//LED0=0;if(KEY4==1)//没有按下按键{if(ClickBuf4==1)//之前有一次按下{if(STA_Machine4.ucKey_DoubleClick_Timer>=SetDoubleClickTime)//超过双击时间{Keyi4.Key_Flag=1;//有按键按下Keyi4.Click=1;//ClickBuf4=0;//清除单击缓存}}}else //之前没有按键按下来,现在按键暗下来,那么进入下一个状态{STA_Machine4.ucSTA_Machine_Status=STA2_KEY_DownShake;}break;}case STA2_KEY_DownShake://如果当前是抖动状态,10ms到现在这里,检测到有按键暗下来{if(KEY4==0)//如果有按键按下,跳转到按键按下状态,否则是弹起状态{STA_Machine4.ucSTA_Machine_Status=STA3_KEY_Down;STA_Machine4.ucKey_LongPress_Timer=0;//长按定时器清0,开始计时}
//                  else
//                      {STA_Machine.ucSTA_Machine_Status=STA1_KEY_Up;}    break;}case STA3_KEY_Down:{if(KEY4==0){//长按检测if(Keyi4.LongPress==0)//检测长按是不是执行,不然会重复判断,没有长按{if(STA_Machine4.ucKey_LongPress_Timer>=SetLongPressTime){STA_Machine4.ucSTA_Machine_Status=STA4_KEY_UpShake;Keyi4.Key_Flag=1;Keyi4.LongPress=1;//超过两秒,说明是长按的状态if(ClickBuf4==1)ClickBuf4=0;//检测到长按,清除上一次的按键单击缓存}}}else{STA_Machine4.ucSTA_Machine_Status=STA4_KEY_UpShake;if(Keyi4.LongPress==0){//双击检查if(ClickBuf4==1){Keyi4.Key_Flag=1;Keyi4.DoubleClick=1;ClickBuf4=0;}else{ClickBuf4=1;STA_Machine4.ucKey_DoubleClick_Timer=0;}}}break;}case STA4_KEY_UpShake:{if(KEY4==1)//如果有按键按下,跳转到按键抖动状态,否则的确是弹起状态{STA_Machine4.ucSTA_Machine_Status=STA1_KEY_Up;}break;}default:STA_Machine4.ucSTA_Machine_Status=STA1_KEY_Up;}//===================================================================
//执行按键1程序if(Keyii.Key_Flag==1){Keyii.Key_Flag=0;if(Keyii.Click==1){LED0=!LED0;IncSetTime();}if(Keyii.DoubleClick==1){LED0=!LED0;LeftShiftTimeSet();}if(Keyii.LongPress==1){LED0=!LED0;if (setIndex == 0)  //不处于设置状态时,进入设置状态{EnterTimeSet();}else                //已处于设置状态时,保存时间并退出设置状态{ExitTimeSet(1);}}Keyii.Click=0;Keyii.DoubleClick=0;Keyii.LongPress=0;}//执行按键2if(Keyi1.Key_Flag==1){Keyi1.Key_Flag=0;if(Keyi1.Click==1){LED0=!LED0;DecSetTime();}if(Keyi1.DoubleClick==1){LED0=!LED0;RightShiftTimeSet();}if(Keyi1.LongPress==1){LED0=!LED0;ExitTimeSet(0);}Keyi1.Click=0;Keyi1.DoubleClick=0;Keyi1.LongPress=0;}
//===按键3if(Keyi2.Key_Flag==1){Keyi2.Key_Flag=0;if(Keyi2.Click==1){LED0=!LED0;AddPar();//加参数}if(Keyi2.DoubleClick==1){LED0=!LED0;
//切换参数RightShiftParaSet();}if(Keyi2.LongPress==1){LED0=!LED0;//在状态机中实现,实现长+}Keyi2.Click=0;Keyi2.DoubleClick=0;Keyi2.LongPress=0;}//按键4if(Keyi3.Key_Flag==1){Keyi3.Key_Flag=0;if(Keyi3.Click==1){//减参数LED0=!LED0;SubPar();}if(Keyi3.DoubleClick==1){LED0=!LED0;LeftShiftParaSet();}if(Keyi3.LongPress==1){LED0=!LED0;
//长减,在状态机中实现}Keyi3.Click=0;Keyi3.DoubleClick=0;Keyi3.LongPress=0;}    //按键5if(Keyi4.Key_Flag==1){Keyi4.Key_Flag=0;if(Keyi4.Click==1){//切换wifi和自制模式//开机的时候选择了,这里不用执行}if(Keyi4.DoubleClick==1){LED0=!LED0;//退出设置模式ExitParaSet(0);LCD_ShowCharAsc160(0,3,BLUE,WHITE,'?'); LCD_ShowCharAsc160(7,3,BLUE,WHITE,'?'); LCD_ShowCharAsc160(14,3,BLUE,WHITE,'?'); LCD_ShowCharAsc160(0,5,BLUE,WHITE,'?'); LCD_ShowCharAsc160(7,5,BLUE,WHITE,'?'); LCD_ShowCharAsc160(14,5,BLUE,WHITE,'?'); LCD_ShowCharAsc160(1,7,BLUE,WHITE,'?'); LCD_ShowCharAsc160(4,7,BLUE,WHITE,'?'); LCD_ShowCharAsc160(7,7,BLUE,WHITE,'?'); LCD_ShowCharAsc160(10,7,BLUE,WHITE,'?'); LCD_ShowCharAsc160(13,7,BLUE,WHITE,'?'); LCD_ShowCharAsc160(16,7,BLUE,WHITE,'?'); //显示数据RefreshDataShow();}if(Keyi4.LongPress==1){LED0=!LED0;if (setParaIndex == 0)  //不处于设置状态时,进入设置状态{EnterParaSet();}else                //已处于设置状态时,保存时间并退出设置状态{LCD_ShowCharAsc160(0,3,BLUE,WHITE,'?'); LCD_ShowCharAsc160(7,3,BLUE,WHITE,'?'); LCD_ShowCharAsc160(14,3,BLUE,WHITE,'?'); LCD_ShowCharAsc160(0,5,BLUE,WHITE,'?'); LCD_ShowCharAsc160(7,5,BLUE,WHITE,'?'); LCD_ShowCharAsc160(14,5,BLUE,WHITE,'?'); LCD_ShowCharAsc160(1,7,BLUE,WHITE,'?'); LCD_ShowCharAsc160(4,7,BLUE,WHITE,'?'); LCD_ShowCharAsc160(7,7,BLUE,WHITE,'?'); LCD_ShowCharAsc160(10,7,BLUE,WHITE,'?'); LCD_ShowCharAsc160(13,7,BLUE,WHITE,'?'); LCD_ShowCharAsc160(16,7,BLUE,WHITE,'?'); //显示设置的数据ExitParaSet(1);RefreshDataShow();//显示当前的数据阈值}}Keyi4.Click=0;Keyi4.DoubleClick=0;Keyi4.LongPress=0;}//===STA_Machine.ucSTA_Machine_Scan_Timer=0;STA_Machine1.ucSTA_Machine_Scan_Timer=0;STA_Machine2.ucSTA_Machine_Scan_Timer=0;STA_Machine3.ucSTA_Machine_Scan_Timer=0;STA_Machine4.ucSTA_Machine_Scan_Timer=0;}}

LCD屏幕,用SPI,320*240

综合1:stm32F4,ATKESP8266wifi,DS1302,AT24C02,KEY状态机,LCD屏幕320*240相关推荐

  1. STM32F4单片机读取AT24c02

    ​STM32F4是由ST(意法半导体)开发的一种高性能微控制器系列.其采用了90nm的NVM工艺和ART技术(自适应实时存储加速器,Adaptive Real-Time MemoryAccelerat ...

  2. PIC16F887 单片机 智能小夜灯 DS18B20 DS1302 AT24C02 EEPROM

    这题真花时间,做了小半天才搞定. 硬件:8位数码管.光敏传感器.DS1302时钟模块.蜂鸣器.按键.LED灯.人体红外传感器HC-SR501(课设时可用按键替代做输入信号).要求:1.能在数码管上显示 ...

  3. 51单片机 普中V2 数字时钟 电子时钟 万年历 DS1302 LCD1602 AT24C02

    1 一个普通的万年历 仿真图和hex文件 点我 仿真软件proteus 8.9 的安装 点我 硬件:DS1302.按键.LCD.蜂鸣器 要求: 1.读出DS1302芯片当前的内容,在LCD上显示. 2 ...

  4. verdi显示状态机名字_如何写好状态机(三)

    大侠好,欢迎来到FPGA技术江湖,江湖偌大,相见即是缘分.大侠可以关注FPGA技术江湖,在"闯荡江湖"."行侠仗义"栏里获取其他感兴趣的资源,或者一起煮酒言欢. ...

  5. 对时序逻辑电路采用不同描述方式,ISE综合出来的电路(RTL Schematic)比较(以模5计数器为例)

    目录 前言 行为级描述 Verilog HDL设计代码为: ISE综合 RTL Schematic Technology Schematic 状态机描述状态转移图 Verilog HDL代码 测试文件 ...

  6. verilog之状态机详细解释(二)

    一.有限状态机设计的一般步骤: 1) 逻辑抽象,得出状态转换图 就是把给出的一个实际逻辑关系表示为时序逻辑函数,可以用状态转换表来描述,也可以用状态转换图来描述.这就需要: • 分析给定的逻辑问题,确 ...

  7. arduino智能闹钟_【Arduino综合项目】小闹钟

    小闹钟项目 在额济纳支教这一年,给孩子们上了很多Arduino开发的课,也带他们完成了几个Arduino综合项目,下面陆续把这些小项目放上来,欢迎围观~~~非科班出身,C语言学的不好,望各路大神批评指 ...

  8. (实验55)单片机,STM32F4学习笔记,代码讲解【网络通信实验】【正点原子】【原创】

    文章目录 其它文章链接,独家吐血整理 实验现象 主程序 LWIP初始化程序 代码讲解 其它文章链接,独家吐血整理 (实验3)单片机,STM32F4学习笔记,代码讲解[按键输入实验][正点原子][原创] ...

  9. 分库分表:订单中心,多key业务如何进行数据库切分

    本篇将以"订单中心"为例,介绍"多key"类业务,随着数据量的逐步增大,数据库性能显著降低,数据库水平切分相关的架构实践. 什么是"多key" ...

  10. FPGA学习之状态机

    1. 理论学习 状态机简写为FSM,也称为同步有限状态机,我们简称为状态机.所以说同步时因为状态机中所有的状态跳转都是在时钟的作用下进行的,而有限则是说状态机的个数有限的.状态机分为两大类,即Moor ...

最新文章

  1. leetcode算法题--删除回文子序列
  2. Win11系统创建虚拟桌面的方法
  3. abp框架mysql连接配置,abp框架连接数据库
  4. BCrypt加密怎么存入数据库_dns污染怎么解决
  5. Intel Sandy Bridge/Ivy Bridge架构/微架构/流水线 (15) - L1数据缓存/读写地址转换
  6. windows平台下载编译好的webrtc代码vs2015
  7. [Linux 002]——Linux的常用命令
  8. 《深度学习Python实践》第20章——回归项目实例
  9. Atitit 提升战力眼光和组织能力的几大要点 目录 1. 成长金字塔模型 德雷福斯模型 1 2. 提升战略眼光, 3 2.1. 视野与格局 3 2.2. 未来预测 未来发展负责,判断未来趋势, 3
  10. 美洽消息推送 php,ThinkPHP内核仿美洽多商户多端接入无限客服系统支持PC+WAP+公众号接入...
  11. Logback使用总结
  12. 英语作文中常见的连接词
  13. MySQLyog的使用
  14. 研究生计算机专业笔记本配置要求,大学生买什么电脑好?电脑配置及选择方法全解析...
  15. 站内文案编辑seo关键词优化技巧
  16. 美团云深度学习平台-快速开始
  17. 【从FT到DFT和FFT】(一)从三角函数正交性到傅里叶变换的详细公式推导
  18. 华为畅享9 plus鸿蒙系统,华为鸿蒙系统支持的手机型号_鸿蒙系统支持华为哪几款手机...
  19. GIS开发:QGIS编辑矢量数据
  20. App Inventor 2 连接调试器的各种方式比较

热门文章

  1. 全纯函数导数的几何意义
  2. IE浏览器主页被劫持,如何解决主页被篡改问题?
  3. R语言深度学习GPU版本的下载
  4. 以太坊 私链 节点连接(window)
  5. 魔兽世界插件开发-暴雪插件源代码
  6. (Excel)如何使用Excel进行四舍六入数据修约
  7. mess组网 中继_想全屋覆盖还用中继器?out啦!Mesh组网才是最佳方案
  8. 关于ios的ipa包的分析之link map 文件的分析
  9. 五月份适合去哪旅游 国内15个旅游胜地
  10. ES查询结果全局高亮