Main主程序
#include "sys.h"
#include "delay.h"
#include "adc.h"
#include "gpio.h"
#include "OLED_I2C.h"
#include "stmflash.h"
#include "ds18b20.h"
#include "timer.h"
#include "usart1.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#define FLASH_SAVE_ADDR ((u32)0x0800F000) //设置FLASH 保存地址(必须为偶数)
#define STM32_RX1_BUF Usart1RecBuf
#define STM32_Rx1Counter RxCounter
#define STM32_RX1BUFF_SIZE USART1_RXBUFF_SIZE
extern const unsigned char BMP1[];
extern const unsigned char BMP2[];
char display[16]; //显示缓存区
short temperature=0; //温度
u8 setTempValue = 40; //温度上限
u8 setSmokeValue = 60; //烟雾上限
u8 sendSmsFlag = 0; //发送短信标志
u8 alarmFlag = 0x00; //蜂鸣器报警标志
u16 smoke=0; //烟雾
u8 setn=0; //记录设置按键按下的次数
bool shanshuo=0;
bool shuaxin=0;
bool fangdao=0;
char PhoneNumber[11];//手机号码
char uniPhoneNum[44];//手机号码转码后存放数组
void UsartRx1BufClear(void)
{
memset(STM32_RX1_BUF, 0, STM32_RX1BUFF_SIZE);//清除缓存
STM32_Rx1Counter = 0;
}
/***********************************************************************************************/
/****************************** 以下为SIM800相关部分 ****************************************/
/***********************************************************************************************/
void PhoneNumChangeUnicode(char *str1,char *str2) //手机号转码
{
u8 i;
char *buf = str1;
for(i = 0;i < 11;i ++) //发送中文短信手机号必须转码,前面需要加上003
{
str2[i*4+0] = '0';
str2[i*4+1] = '0';
str2[i*4+2] = '3';
str2[i*4+3] = buf[i];
}
}
void gsm_atcmd_send(char *at)//GSM AT指令发送函数
{
unsigned short waittry;//延时变量
do
{
gsm_rev_start = 0;//接收开始标志清零
gsm_rev_okflag = 0;//接收完成标志清零
waittry = 0;//延时变量清零
uart1_send((unsigned char *)at,0xFF);//串口发送内容
while(waittry ++ < 3000)//进入while延时
{
if (gsm_rev_okflag == 1)//等待GSM返回OK
{
return;//返回出去
}
delay_ms(1);
}
}
while(gsm_rev_okflag == 0);
}
void gsm_init(void)//gsm初始化
{
gsm_atcmd_send("AT\r\n");//测试用的
delay_ms(1000);
gsm_atcmd_send("AT+CSCS=\"UCS2\"\r\n");//设置为unicode编码
delay_ms(1000);
gsm_atcmd_send("AT+CMGF=1\r\n");//设置为文本模式
delay_ms(1000);
gsm_atcmd_send("AT+CNMI=2,1\r\n");//来短信时提示,并存储到模块内存
delay_ms(1000);
gsm_atcmd_send("AT+CMGD=1,4\r\n");//清除短信
delay_ms(1000);
gsm_atcmd_send("AT+CSMP=17,0,2,25\r\n");//设置短信保留为5分钟,发送中文
}
/*
*number 为对方手机号
*/
void gsm_send_msg(const char*number,char * content)
{
u8 len;
unsigned char gsm_at_txbuf[60];//GSM AT命令缓存区
memset(gsm_at_txbuf, 0, 60);//缓存清零
strncpy((char *)gsm_at_txbuf,"AT+CMGS=\"",9);//将AT+CMGS=\",复制到gsm_at_txbuf
memcpy(gsm_at_txbuf + 9, number, 44);//将手机号码复制到AT+CMGS=\"之后
len = strlen((char *)gsm_at_txbuf);//获取gsm_at_txbuf字符串长度
gsm_at_txbuf[len] = '"'; // AT+CMGS=\"12345678901\"
gsm_at_txbuf[len + 1] = '\r';
gsm_at_txbuf[len + 2] = '\n';//gsm_at_txbuf最终的格式"AT+CMGS=\"手机号码\"\r\n"
uart1_send(gsm_at_txbuf,0xFF);//发送需要接受短信的手机号码
delay_ms(1000);
uart1_send((unsigned char *)content,0xFF); //发短信内容
delay_ms(100);
printf("%c",0x1a); //发送结束符号
delay_ms(10);
}
/*
*content 为短信内容
*/
void sim800_send_sms(char *content)
{
bool send_error = 0;
u16 send_count = 0;
gsm_rev_okflag = 0;
OLED_ShowStr(32,2,"Send Sms... ",2,0);
PhoneNumChangeUnicode(PhoneNumber,uniPhoneNum); //在发送短信前,先将手机号转码
gsm_send_msg(uniPhoneNum,content);//发送短信
delay_ms(1000);//延时1秒
while(gsm_rev_okflag == 0)//等待返回OK指令
{
if(send_count++ > 8000)
{
send_count = 0;
send_error = 1;
break;
}
delay_ms(1);
};
gsm_rev_okflag = 0;
if(send_error == 1)
OLED_ShowStr(32,2,"Send Fail! ",2,0);//显示发送超时
else
OLED_ShowStr(32,2," Send OK! ",2,0);
UsartRx1BufClear();
delay_ms(1000);//延时1秒
OLED_ShowStr(32,2," ",2,0);
}
/***********************************************************************************************/
/****************************** end ****************************************/
/***********************************************************************************************/
void STM32_FlashCheck(void) // 检查是否是新的单片机,是的话清空存储区,否则保留
{
u8 comper_str[6],i;
STMFLASH_Read(FLASH_SAVE_ADDR + 0x10,(u16*)comper_str,5);
comper_str[5] = '\0';
if(strstr((char *)comper_str,"FDYDZ") == NULL) //新的单片机
{
STMFLASH_Write(FLASH_SAVE_ADDR + 0x10,(u16*)"FDYDZ",5); //写入“FDYDZ”,方便下次校验
delay_ms(50);
STMFLASH_Write(FLASH_SAVE_ADDR + 0x40,(u16*)"12345678910",11);//存入初始手机号
delay_ms(50);
}
STMFLASH_Read(FLASH_SAVE_ADDR + 0x40,(u16*)PhoneNumber,11); //读出手机号
for(i = 0; i < 11 ; i++)
{
if(PhoneNumber[i]<'0' || PhoneNumber[i]>'9')
{
break;
}
}
if(i != 11)
{
memset(PhoneNumber, 0 , 11); //清除缓存
sprintf(PhoneNumber,"12345678910");
}
delay_ms(100);
}
void display_mode(void)
{
u8 i;
//显示中文: 防盗模式
if(fangdao==1){for(i = 0;i < 4;i ++)OLED_ShowCN(i*16+32,0,i+0,1);}else {for(i = 0;i < 4;i ++)OLED_ShowCN(i*16+32,0,i+24,1);}
}
void displayInitInterface(void) //显示初始页面
{
u8 i;
for(i = 0;i < 2;i ++)OLED_ShowCN(i*16,4,i+6,0); //显示中文: 温度
for(i = 0;i < 2;i ++)OLED_ShowCN(i*16,6,i+8,0); //显示中文: 烟雾
OLED_ShowChar(32,0,':',2,0);
OLED_ShowChar(32,4,':',2,0);
OLED_ShowChar(32,6,':',2,0);
display_mode();
}
void Get_Temperature(void) //获取温度
{
temperature=ReadTemperature();
if(temperature>=setTempValue)
{
if(!(alarmFlag&0x01))
{
alarmFlag|=0x01;
shanshuo = 0;
sendSmsFlag = 2; //发送短信标志
}
}
else
{
alarmFlag&=0xFE;
}
if(temperature>=setTempValue && shanshuo)
{
OLED_ShowStr(40, 4," ", 2,0);
}
else
{
sprintf(display," %d",temperature);
OLED_ShowStr(40, 4, (u8*)display, 2,0);//显示温度
OLED_ShowCentigrade(68, 4); //显示摄氏度
}
}
void Get_Smoke(void) //获取烟雾浓度
{
u16 test_adc=0;
/* 获取烟雾浓度 */
test_adc = Get_Adc_Average(ADC_Channel_9,10);//读取通道9的10次AD平均值
smoke = test_adc*99/4096;//转换成0-99百分比
if(smoke>=setSmokeValue)
{
if(!(alarmFlag&0x02))
{
alarmFlag|=0x02;
shanshuo = 0;
sendSmsFlag = 3; //发送短信标志
}
}
else
{
alarmFlag&=0xFD;
}
if(smoke>=setSmokeValue && shanshuo)
{
OLED_ShowStr(40, 6," ", 2,0);
}
else
{
sprintf(display," %02d %%",smoke);
OLED_ShowStr(40, 6, (u8*)display, 2,0);//显示温度
}
}
void displaySetValue(void) //显示设置值
{
u8 add=2,i;
if(setn==1)
{
OLED_ShowChar(56,4,setTempValue%100/10+'0',2,0);//显示
OLED_ShowChar(64,4,setTempValue%10+'0',2,0);//显示
}
if(setn==2)
{
OLED_ShowChar(56,4,setSmokeValue%100/10+'0',2,0);//显示
OLED_ShowChar(64,4,setSmokeValue%10+'0',2,0);//显示
OLED_ShowChar(72,4,'%',2,0);
}
if(setn>=3)
{
for(i = 0;i < 11;i ++)
{
OLED_ShowChar((add++)*8,4,PhoneNumber[i],2,(setn+1)-(3+i));//显示手机号码
}
}
}
void keyscan(void) //按键扫描
{
u8 i;
if(KEY1 == 0) //设置键
{
delay_ms(20);
if(KEY1 == 0)
{
while(KEY1 == 0);
setn ++;
if(setn == 1)
{
OLED_CLS();//清屏
for(i = 0;i < 4;i ++)OLED_ShowCN(i*16+32,0,i+10,0);//显示中文:设置温度
OLED_ShowCentigrade(75, 4); //显示摄氏度
}
if(setn == 2)
{
for(i = 0;i < 4;i ++)OLED_ShowCN(i*16+32,0,i+14,0);//显示中文:设置烟雾
OLED_ShowChar(80,4,' ',2,0);
}
if(setn == 3)
{
for(i = 0;i < 6;i ++)OLED_ShowCN(i*16+16,0,i+18,0);//显示中文:设置手机号码
}
if(setn >= 14)
{
setn = 0;
OLED_CLS();//清屏
displayInitInterface();
STMFLASH_Write(FLASH_SAVE_ADDR + 0x40,(u16*)PhoneNumber,11); //退出设置,存入设置的手机号
}
displaySetValue();
}
}
if(KEY2 == 0) //加键
{
delay_ms(80);
if(KEY2 == 0)
{
if(setTempValue < 99 && setn==1)setTempValue++;
if(setSmokeValue < 99 && setn==2)setSmokeValue++;
if(setn>=3)
{
PhoneNumber[setn-3]++;
if(PhoneNumber[setn-3]>'9')PhoneNumber[setn-3]='0';
}
displaySetValue();
}
}
if(KEY3 == 0) //减键
{
delay_ms(80);
if(KEY3 == 0)
{
if(setn==0)
{
fangdao=!fangdao;
display_mode();
}
if(setTempValue > 0 && setn==1)setTempValue--;
if(setSmokeValue > 0 && setn==2)setSmokeValue--;
if(setn>=3)
{
PhoneNumber[setn-3]--;
if(PhoneNumber[setn-3]<'0')PhoneNumber[setn-3]='9';
}
displaySetValue();
}
}
}
int main(void)
{
bool flameFlag=0;
bool SomebodyFlag=0;
char SEND_BUF[400]; //发送短信缓存
delay_init(); //延时函数初始化
NVIC_Configuration(); //中断优先级配置
I2C_Configuration(); //IIC初始化
STM32_FlashCheck(); //FLASH初始化
delay_ms(200);
OLED_Init(); //OLED液晶初始化
OLED_CLS(); //清屏
OLED_ShowStr(0,2," GSM Init... ",2,0);
uart1_Init(9600);
gsm_init();//gsm初始化
OLED_CLS();//清屏
Adc_Init(); //adc初始化
KEY_GPIO_Init(); //按键引脚初始化
SR501_GPIO_Init(); //人体红外初始化
DS18B20_GPIO_Init(); //温度初始化
DS18B20_Init(); //初始化显示
displayInitInterface(); //显示初始界面
TIM3_Init(99,719); //定时器初始化,定时1ms
//Tout = ((arr+1)*(psc+1))/Tclk ;
//Tclk:定时器输入频率(单位MHZ)
//Tout:定时器溢出时间(单位us)
while(1)
{
keyscan(); //按键扫描
if(setn == 0)
{
if(shuaxin == 1) //大概300ms刷新一次数据
{
Get_Temperature(); //获取温度
Get_Smoke(); //获取烟雾
shuaxin = 0;
}
if(FLAME == 0) //检测到火焰
{
delay_ms(10);
if(FLAME == 0)
{
if(flameFlag == 0)
{
OLED_DrawBMP(88,4,120,8,(unsigned char *)BMP1); //显示火焰图片
sendSmsFlag = 1; //发送短信标志
}
flameFlag = 1;
}
}
else
{
if(flameFlag == 1)
{
OLED_ShowStr(88, 4, " ", 2,0);
OLED_ShowStr(88, 6, " ", 2,0);
}
flameFlag = 0;
}
if(fangdao==1&&SR501==1) //在防盗模式下检测到有人
{
if(SomebodyFlag==0)
{
OLED_DrawBMP(0,0,32,4,(unsigned char *)BMP2); //图显示
sendSmsFlag = 4; //发送短信标志
SomebodyFlag = 1;
}
}
else
{
if(SomebodyFlag==1)
{
OLED_ShowStr(0, 0, " ", 2,0);
OLED_ShowStr(0, 2, " ", 2,0);
SomebodyFlag = 0;
}
}
if(temperature>=setTempValue || smoke>=setSmokeValue || flameFlag || SomebodyFlag)BEEP=1;else BEEP=0; //检测到温度烟雾超标火焰蜂鸣器报警
if(temperature>=setTempValue)FAN=1;else FAN=0; //温度超标都开启风扇
if(smoke>=setSmokeValue || flameFlag)RELAY = 1; else RELAY = 0; //检测到有火或者烟雾超标,开启水泵
if(sendSmsFlag != 0) //发送短信
{
char TEMP_BUF[100];
/*******************************************************************************************/
/*******************以下为短信内容处理部分,发送中文短信必须转换为Unicode码**************/
/******************************************************************************************/
memset(SEND_BUF,0,sizeof(SEND_BUF)); //清空缓冲区
switch(sendSmsFlag)
{
case(1): strcat(SEND_BUF,"8B66544AFF0168C06D4B5230706B7130FF01"); break; //警告!检测到火焰!
case(2): strcat(SEND_BUF,"8B66544AFF016E295EA68FC79AD8FF01"); break; //警告!温度过高!
case(3): strcat(SEND_BUF,"8B66544AFF0170DF96FE6D535EA68FC79AD8FF01"); break; //警告!烟雾浓度过高!
case(4): strcat(SEND_BUF,"8B66544AFF0168C06D4B523067094EBAFF01"); break; //警告!检测到有人!
default: break;
}
if(sendSmsFlag!=4)
{
memset(TEMP_BUF,0,sizeof(TEMP_BUF)); //清空缓冲区
sprintf(TEMP_BUF,"6E295EA6003A003%1d003%1d2103FF0C70DF96FE003A003%1d003%1d0025",temperature/10,temperature%10,smoke/10,smoke%10);
strcat(SEND_BUF,TEMP_BUF);
}
sim800_send_sms((char *)SEND_BUF);//发送短信
sendSmsFlag = 0;
/*******************************************************************************************/
/*************************** end *****************************/
/******************************************************************************************/
}
}
delay_ms(10);
}
}
void TIM3_IRQHandler(void)//定时器3中断服务程序,用于记录时间
{
static u16 timeCount1 = 0;
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) //检查指定的TIM中断发生与否:TIM 中断源
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除中断标志位
timeCount1++;
if(timeCount1 >= 300) //300ms
{
timeCount1 = 0;
shanshuo = !shanshuo;
shuaxin = 1;
}
}
}
ADC初始化程序
#include "adc.h"
#include "delay.h"
//初始化ADC
//这里我们仅以规则通道为例
//我们默认将开启通道0~3
void Adc_Init(void)
{
ADC_InitTypeDef ADC_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB |RCC_APB2Periph_ADC1 , ENABLE ); //使能ADC1通道时钟
RCC_ADCCLKConfig(RCC_PCLK2_Div8); //设置ADC分频因子6 72M/8=9,ADC最大时间不能超过14M
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1|GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; //模拟输入引脚
GPIO_Init(GPIOB, &GPIO_InitStructure);
ADC_DeInit(ADC1); //复位ADC1,将外设 ADC1 的全部寄存器重设为缺省值
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent; //ADC工作模式:ADC1和ADC2工作在独立模式
ADC_InitStructure.ADC_ScanConvMode = DISABLE; //模数转换工作在单通道模式
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE; //模数转换工作在单次转换模式
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; //转换由软件而不是外部触发启动
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right; //ADC数据右对齐
ADC_InitStructure.ADC_NbrOfChannel = 1; //顺序进行规则转换的ADC通道的数目
ADC_Init(ADC1, &ADC_InitStructure); //根据ADC_InitStruct中指定的参数初始化外设ADCx的寄存器
ADC_Cmd(ADC1, ENABLE); //使能指定的ADC1
ADC_ResetCalibration(ADC1); //使能复位校准
while(ADC_GetResetCalibrationStatus(ADC1)); //等待复位校准结束
ADC_StartCalibration(ADC1); //开启AD校准
while(ADC_GetCalibrationStatus(ADC1)); //等待校准结束
}
//获得ADC值
//ch:通道值 9
u16 Get_Adc(u8 ch)
{
//设置指定ADC的规则组通道,一个序列,采样时间
ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_71Cycles5 ); //ADC1,ADC通道,采样时间为13.5周期
ADC_SoftwareStartConvCmd(ADC1, ENABLE); //使能指定的ADC1的软件转换启动功能
while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束
return ADC_GetConversionValue(ADC1); //返回最近一次ADC1规则组的转换结果
}
u16 Get_Adc_Average(u8 ch,u8 times)
{
u32 temp_val=0;
u8 t;
for(t=0;t<times;t++)
{
temp_val+=Get_Adc(ch);
delay_ms(5);
}
return temp_val/times;
}
#ifndef __ADC_H
#define __ADC_H
#include "sys.h"
void Adc_Init(void);
u16 Get_Adc(u8 ch);
u16 Get_Adc_Average(u8 ch,u8 times);
#endif
GPIO引脚初始化程序
#include "gpio.h"
//////////////////////////////////////////////////////////////////////////////////
//按键舵机的GPIO设置
//////////////////////////////////////////////////////////////////////////////////
void KEY_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC|RCC_APB2Periph_AFIO, ENABLE); //使能PABC端口时钟
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //关闭JTAG模式 使PB3,PB4变成普通IO口
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //上拉输入
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_ResetBits(GPIOC,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //输出0
}
void SR501_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PA端口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; //下拉输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
#ifndef __GPIO_H
#define __GPIO_H
#include "sys.h"
#define KEY1 PBin(12)
#define KEY2 PBin(13)
#define KEY3 PBin(14)
#define FLAME PBin(15)
#define BEEP PCout(13)
#define FAN PCout(14)
#define RELAY PCout(15)
#define SR501 PAin(0)
void KEY_GPIO_Init(void);//引脚初始化
void SR501_GPIO_Init(void);
#endif
OLED驱动程序
/************************************************************************************
*
* Description:128*64点阵的OLED显示屏驱动文件SD1306驱动IIC通信方式显示屏
*
* Others: none;
*
* Function List:
* 1. void I2C_Configuration(void) -- 配置CPU的硬件I2C
* 2. void I2C_WriteByte(uint8_t addr,uint8_t data) -- 向寄存器地址写一个byte的数据
* 3. void WriteCmd(unsigned char I2C_Command) -- 写命令
* 4. void WriteDat(unsigned char I2C_Data) -- 写数据
* 5. void OLED_Init(void) -- OLED屏初始化
* 6. void OLED_SetPos(unsigned char x, unsigned char y) -- 设置起始点坐标
* 7. void OLED_Fill(unsigned char fill_Data) -- 全屏填充
* 8. void OLED_CLS(void) -- 清屏
* 9. void OLED_ON(void) -- 唤醒
* 10. void OLED_OFF(void) -- 睡眠
* 11. void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize) -- 显示字符串(字体大小有6*8和8*16两种)
* 12. void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N) -- 显示中文(中文需要先取模,然后放到codetab.h中)
* 13. void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]) -- BMP图片
*
* History: none;
*
*************************************************************************************/
#include "OLED_I2C.h"
#include "delay.h"
#include "codetab.h"
/* 定义I2C总线连接的GPIO端口, 用户只需要修改下面4行代码即可任意改变SCL和SDA的引脚 */
#define RCC_I2C_PORT RCC_APB2Periph_GPIOB /* GPIO端口时钟 */
#define PORT_I2C_SCL GPIOB /* GPIO端口 */
#define PIN_I2C_SCL GPIO_Pin_6 /* GPIO引脚 */
#define PORT_I2C_SDA GPIOB /* GPIO端口 */
#define PIN_I2C_SDA GPIO_Pin_7 /* GPIO引脚 */
#define I2C_SCL_PIN GPIO_Pin_6 /* 连接到SCL时钟线的GPIO */
#define I2C_SDA_PIN GPIO_Pin_7 /* 连接到SDA数据线的GPIO */
/* 定义读写SCL和SDA的宏 */
#define I2C_SCL_1() PORT_I2C_SCL->BSRR = I2C_SCL_PIN /* SCL = 1 */
#define I2C_SCL_0() PORT_I2C_SCL->BRR = I2C_SCL_PIN /* SCL = 0 */
#define I2C_SDA_1() PORT_I2C_SDA->BSRR = I2C_SDA_PIN /* SDA = 1 */
#define I2C_SDA_0() PORT_I2C_SDA->BRR = I2C_SDA_PIN /* SDA = 0 */
#define I2C_SDA_READ() ((PORT_I2C_SDA->IDR & I2C_SDA_PIN) != 0) /* 读SDA口线状态 */
#define I2C_SCL_READ() ((PORT_I2C_SCL->IDR & I2C_SCL_PIN) != 0) /* 读SCL口线状态 */
/*
*********************************************************************************************************
* 函 数 名: bsp_InitI2C
* 功能说明: 配置I2C总线的GPIO,采用模拟IO的方式实现
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void bsp_InitI2C_2(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_I2C_PORT, ENABLE); /* 打开GPIO时钟 */
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; /* 开漏输出模式 */
GPIO_InitStructure.GPIO_Pin = PIN_I2C_SCL;
GPIO_Init(PORT_I2C_SCL, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = PIN_I2C_SDA;
GPIO_Init(PORT_I2C_SDA, &GPIO_InitStructure);
/* 给一个停止信号, 复位I2C总线上的所有设备到待机模式 */
i2c_Stop_2();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Start
* 功能说明: CPU发起I2C总线启动信号
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Start_2(void)
{
/* 当SCL高电平时,SDA出现一个下跳沿表示I2C总线启动信号 */
I2C_SDA_1();
I2C_SCL_1();
delay_us(4);
I2C_SDA_0();
delay_us(4);
I2C_SCL_0();
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Start
* 功能说明: CPU发起I2C总线停止信号
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Stop_2(void)
{
/* 当SCL高电平时,SDA出现一个上跳沿表示I2C总线停止信号 */
I2C_SDA_0();
I2C_SCL_1();
delay_us(4);
I2C_SDA_1();
delay_us(4);
}
/*
*********************************************************************************************************
* 函 数 名: i2c_SendByte
* 功能说明: CPU向I2C总线设备发送8bit数据
* 形 参: _ucByte : 等待发送的字节
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_SendByte_2(uint8_t _ucByte)
{
uint8_t i;
/* 先发送字节的高位bit7 */
for (i = 0; i < 8; i++)
{
if (_ucByte & 0x80)
{
I2C_SDA_1();
}
else
{
I2C_SDA_0();
}
delay_us(2);
I2C_SCL_1();
delay_us(2);
I2C_SCL_0();
_ucByte <<= 1; /* 左移一个bit */
delay_us(2);
}
}
/*
*********************************************************************************************************
* 函 数 名: i2c_ReadByte
* 功能说明: CPU从I2C总线设备读取8bit数据
* 形 参: 无
* 返 回 值: 读到的数据
*********************************************************************************************************
*/
uint8_t i2c_ReadByte_2(void)
{
uint8_t i;
uint8_t value;
/* 读到第1个bit为数据的bit7 */
value = 0;
for (i = 0; i < 8; i++)
{
value <<= 1;
I2C_SCL_1();//SCL在高电平期间,数据必须保持稳定
delay_us(2);
if (I2C_SDA_READ())
{
value++;
}
I2C_SCL_0();
delay_us(1);
}
return value;
}
/*
*********************************************************************************************************
* 函 数 名: i2c_WaitAck
* 功能说明: CPU产生一个时钟,并读取器件的ACK应答信号
* 形 参: 无
* 返 回 值: 返回0表示正确应答,1表示无器件响应
*********************************************************************************************************
*/
uint8_t i2c_WaitAck_2(void)
{
uint8_t tempTime;
I2C_SDA_1(); /* CPU释放SDA总线 */
delay_us(1);
I2C_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
delay_us(1);
while(I2C_SDA_READ())
{
tempTime++;
if(tempTime>250)
{
i2c_Stop_2();
return 1;
}
}
I2C_SCL_0();
return 0;
}
/*
*********************************************************************************************************
* 函 数 名: i2c_Ack
* 功能说明: CPU产生一个ACK信号
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_Ack_2(void)
{
I2C_SDA_0(); /* CPU驱动SDA = 0 */
delay_us(5);
I2C_SCL_1(); /* CPU产生1个时钟 */
delay_us(5);
I2C_SCL_0();
delay_us(5);
I2C_SDA_1(); /* CPU释放SDA总线 */
}
/*
*********************************************************************************************************
* 函 数 名: i2c_NAck
* 功能说明: CPU产生1个NACK信号
* 形 参: 无
* 返 回 值: 无
*********************************************************************************************************
*/
void i2c_NAck_2(void)
{
I2C_SDA_1(); /* CPU驱动SDA = 1 */
delay_us(5);
I2C_SCL_1(); /* CPU产生1个时钟 */
delay_us(5);
I2C_SCL_0();
delay_us(5);
}
void I2C_Configuration(void)
{
bsp_InitI2C_2();
}
void I2C_WriteByte(uint8_t addr,uint8_t data)
{
/* 第1步:发起I2C总线启动信号 */
i2c_Start_2();
/* 第2步:发起控制字节,高7bit是地址,bit0是读写控制位,0表示写,1表示读 */
i2c_SendByte_2(OLED_ADDRESS | I2C_WR); /* 此处是写指令 */
/* 第3步:发送ACK */
i2c_WaitAck_2();
/* 第4步:发送字节地址 */
i2c_SendByte_2(addr);
i2c_WaitAck_2();
/* 第5步:开始写入数据 */
i2c_SendByte_2(data);
/* 第6步:发送ACK */
i2c_WaitAck_2();
/* 发送I2C总线停止信号 */
i2c_Stop_2();
}
void WriteCmd(unsigned char I2C_Command)//写命令
{
I2C_WriteByte(0x00, I2C_Command);
}
void WriteDat(unsigned char I2C_Data)//写数据
{
I2C_WriteByte(0x40, I2C_Data);
}
void OLED_Init(void)
{
delay_ms(100); //这里的延时很重要
WriteCmd(0xAE); //display off
WriteCmd(0x20); //Set Memory Addressing Mode
WriteCmd(0x10); //00,Horizontal Addressing Mode;01,Vertical Addressing Mode;10,Page Addressing Mode (RESET);11,Invalid
WriteCmd(0xb0); //Set Page Start Address for Page Addressing Mode,0-7
WriteCmd(0xc8); //Set COM Output Scan Direction
WriteCmd(0x00); //---set low column address
WriteCmd(0x10); //---set high column address
WriteCmd(0x40); //--set start line address
WriteCmd(0x81); //--set contrast control register
WriteCmd(0xff); //亮度调节 0x00~0xff
WriteCmd(0xa1); //--set segment re-map 0 to 127
WriteCmd(0xa6); //--set normal display
WriteCmd(0xa8); //--set multiplex ratio(1 to 64)
WriteCmd(0x3F); //
WriteCmd(0xa4); //0xa4,Output follows RAM content;0xa5,Output ignores RAM content
WriteCmd(0xd3); //-set display offset
WriteCmd(0x00); //-not offset
WriteCmd(0xd5); //--set display clock divide ratio/oscillator frequency
WriteCmd(0xf0); //--set divide ratio
WriteCmd(0xd9); //--set pre-charge period
WriteCmd(0x22); //
WriteCmd(0xda); //--set com pins hardware configuration
WriteCmd(0x12);
WriteCmd(0xdb); //--set vcomh
WriteCmd(0x20); //0x20,0.77xVcc
WriteCmd(0x8d); //--set DC-DC enable
WriteCmd(0x14); //
WriteCmd(0xaf); //--turn on oled panel
}
void OLED_SetPos(unsigned char x, unsigned char y) //设置起始点坐标
{
WriteCmd(0xb0+y);
WriteCmd(((x&0xf0)>>4)|0x10);
WriteCmd((x&0x0f)|0x01);
}
void OLED_Fill(unsigned char fill_Data)//全屏填充
{
unsigned char m,n;
for(m=0;m<8;m++)
{
WriteCmd(0xb0+m); //page0-page1
WriteCmd(0x00); //low column start address
WriteCmd(0x10); //high column start address
for(n=0;n<128;n++)
{
WriteDat(fill_Data);
}
}
}
void OLED_CLS(void)//清屏
{
OLED_Fill(0x00);
}
//--------------------------------------------------------------
// Prototype : void OLED_ON(void)
// Calls :
// Parameters : none
// Description : 将OLED从休眠中唤醒
//--------------------------------------------------------------
void OLED_ON(void)
{
WriteCmd(0X8D); //设置电荷泵
WriteCmd(0X14); //开启电荷泵
WriteCmd(0XAF); //OLED唤醒
}
//--------------------------------------------------------------
// Prototype : void OLED_OFF(void)
// Calls :
// Parameters : none
// Description : 让OLED休眠 -- 休眠模式下,OLED功耗不到10uA
//--------------------------------------------------------------
void OLED_OFF(void)
{
WriteCmd(0X8D); //设置电荷泵
WriteCmd(0X10); //关闭电荷泵
WriteCmd(0XAE); //OLED休眠
}
//在指定位置显示一个字符,包括部分字符
//x:0~127
//y:0~7
//chr:显示的字符
//TextSize:字符大小(1:6*8 ; 2:8*16)
//mode:1,反白显示;0,正常显示
void OLED_ShowChar(unsigned char x,unsigned char y,unsigned char chr,unsigned char TextSize,u8 mode)
{
unsigned char c=0,i=0;
c=chr-' ';//得到偏移后的值
if(TextSize == 2)
{
if(x>120){x=0;y++;}
OLED_SetPos(x,y);
for(i=0;i<8;i++)
if(mode==1)WriteDat(~(F8X16[c*16+i]));else WriteDat(F8X16[c*16+i]);
OLED_SetPos(x,y+1);
for(i=0;i<8;i++)
if(mode==1)WriteDat(~(F8X16[c*16+i+8]));else WriteDat(F8X16[c*16+i+8]);
}
else
{
if(x>126){x=0;y++;}
OLED_SetPos(x,y);
for(i=0;i<6;i++)
if(mode==1)WriteDat(~(F6x8[c][i])); else WriteDat(F6x8[c][i]);
}
}
//--------------------------------------------------------------
// Prototype : void OLED_ShowChar(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
// Calls :
// Parameters : x,y -- 起始点坐标(x:0~127, y:0~7); ch[] -- 要显示的字符串; TextSize -- 字符大小(1:6*8 ; 2:8*16)
// Description : 显示codetab.h中的ASCII字符,有6*8和8*16可选择
//--------------------------------------------------------------
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize,u8 mode)
{
unsigned char c = 0,i = 0,j = 0;
switch(TextSize)
{
case 1:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 126)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<6;i++)
if(mode==1)WriteDat(~(F6x8[c][i]));
else WriteDat(F6x8[c][i]);
x += 6;
j++;
}
}break;
case 2:
{
while(ch[j] != '\0')
{
c = ch[j] - 32;
if(x > 120)
{
x = 0;
y++;
}
OLED_SetPos(x,y);
for(i=0;i<8;i++)
if(mode==1)WriteDat(~(F8X16[c*16+i]));
else WriteDat(F8X16[c*16+i]);
OLED_SetPos(x,y+1);
for(i=0;i<8;i++)
if(mode==1)WriteDat(~(F8X16[c*16+i+8]));
else WriteDat(F8X16[c*16+i+8]);
x += 8;
j++;
}
}break;
}
}
//--------------------------------------------------------------
// Prototype : void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N)
// Calls :
// Parameters : x,y -- 起始点坐标(x:0~127, y:0~7); N:汉字在codetab.h中的索引
// Description : 显示codetab.h中的汉字,16*16点阵
//mode:1,反白显示;0,正常显示
//--------------------------------------------------------------
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N,u8 mode)
{
unsigned char wm=0;
unsigned int adder=32*N;
OLED_SetPos(x , y);
for(wm = 0;wm < 16;wm++)
{
if(mode==1)WriteDat(~(F16x16[adder]));else WriteDat(F16x16[adder]);
adder += 1;
}
OLED_SetPos(x,y + 1);
for(wm = 0;wm < 16;wm++)
{
if(mode==1)WriteDat(~(F16x16[adder]));else WriteDat(F16x16[adder]);
adder += 1;
}
}
//--------------------------------------------------------------
// Prototype : void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]);
// Calls :
// Parameters : x0,y0 -- 起始点坐标(x0:0~127, y0:0~7); x1,y1 -- 起点对角线(结束点)的坐标(x1:1~128,y1:1~8)
// Description : 显示BMP位图
//--------------------------------------------------------------
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[])
{
unsigned int j=0;
unsigned char x,y;
if(y1%8==0)
y = y1/8;
else
y = y1/8 + 1;
for(y=y0;y<y1;y++)
{
OLED_SetPos(x0,y);
for(x=x0;x<x1;x++)
{
WriteDat(BMP[j++]);
}
}
}
void OLED_ShowCentigrade(unsigned char x, unsigned char y)//显示℃
{
unsigned char wm=0;
unsigned char BUF[]={
0x10,0x28,0x10,0xC0,0x20,0x10,0x10,0x10,0x20,0x70,0x00,0x00,0x00,0x00,0x00,0x07,
0x08,0x10,0x10,0x10,0x10,0x08,0x04,0x00,/*"℃"*/
};
OLED_SetPos(x , y);
for(wm = 0;wm < 12;wm++)
{
WriteDat(BUF[wm]);
}
OLED_SetPos(x,y + 1);
for(wm = 0;wm < 12;wm++)
{
WriteDat(BUF[wm+12]);
}
}
#ifndef __OLED_I2C_H
#define __OLED_I2C_H
#include "stm32f10x.h"
#define OLED_ADDRESS 0x78 //通过调整0R电阻,屏可以0x78和0x7A两个地址 -- 默认0x78
#define I2C_WR 0 /* 写控制bit */
#define I2C_RD 1 /* 读控制bit */
void bsp_InitI2C_2(void);
void i2c_Start_2(void);
void i2c_Stop_2(void);
void i2c_SendByte_2(uint8_t _ucByte);
uint8_t i2c_ReadByte_2(void);
uint8_t i2c_WaitAck_2(void);
void i2c_Ack_2(void);
void i2c_NAck_2(void);
void I2C_Configuration(void);
void I2C_WriteByte(uint8_t addr,uint8_t data);
void WriteCmd(unsigned char I2C_Command);
void WriteDat(unsigned char I2C_Data);
void OLED_Init(void);
void OLED_SetPos(unsigned char x, unsigned char y);
void OLED_Fill(unsigned char fill_Data);
void OLED_CLS(void);
void OLED_ON(void);
void OLED_OFF(void);
void OLED_ShowChar(unsigned char x,unsigned char y,unsigned char chr,unsigned char TextSize,u8 mode);
void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize,u8 mode);
void OLED_ShowCN(unsigned char x, unsigned char y, unsigned char N,u8 mode);
void OLED_DrawBMP(unsigned char x0,unsigned char y0,unsigned char x1,unsigned char y1,unsigned char BMP[]);
void OLED_ShowCentigrade(unsigned char x, unsigned char y);
#endif
TIME定时器程序
#include "timer.h"
//通用定时器中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Init(u16 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值 计数到5000为500ms
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值 10Khz的计数频率
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位
TIM_ITConfig( //使能或者失能指定的TIM中断
TIM3, //TIM3
TIM_IT_Update ,
ENABLE //使能
);
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //先占优先级0级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //从优先级2级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
TIM_Cmd(TIM3, ENABLE); //使能定时器3
}
#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"
void TIM3_Init(u16 arr,u16 psc);
#endif
DS18B20温度程序
#include "ds18b20.h"
#include "delay.h"
/*******************************************************************************
函数名:DS18B20_GPIO_Init
功能:初始化DS18B20引脚
输入:
输出:
返回值:
备注:
*******************************************************************************/
void DS18B20_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_DS18B20_PORT, ENABLE); //使能PORTA口时钟
GPIO_InitStructure.GPIO_Pin = DS18B20_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DS18B20_GPIO_PORT, &GPIO_InitStructure);
GPIO_SetBits(DS18B20_GPIO_PORT,DS18B20_GPIO_PIN); //输出1
}
/*******************************************************************************
函数名:DS18B20_Init
功能:初始化DS18B20
输入:
输出:
返回值:初始化成功为0,不成功为1
备注:
*******************************************************************************/
u8 DS18B20_Init(void)
{
unsigned char wait=0;
DS18B20_IO_OUT(); //输出模式
DS18B20_OUT_0; //拉低
delay_us(750); //至少延时480us
DS18B20_OUT_1; //拉高
delay_us(15); //15us
DS18B20_IO_IN(); //输入模式
while(READ_DS18B20_IO && wait++<200)delay_us(1);//等待高电平结束
if(wait>=200)return 1;
else wait=0;
while(!READ_DS18B20_IO && wait++<240)delay_us(1);//等待低电平结束
if(wait>=240)return 1;
else return 0;
}
/*******************************************************************************
函数名:DS18B20_ReadByte
功能:从DS18B20读一个字节
输入:
输出:
返回值:读取到的字节
备注:
*******************************************************************************/
unsigned char DS18B20_ReadByte(void)
{
unsigned char i;
unsigned char dat = 0;
for (i=0; i<8; i++) //8位计数器
{
dat >>= 1;
DS18B20_IO_OUT(); //输出模式
DS18B20_OUT_0; //开始时间片
delay_us(2); //延时等待
DS18B20_OUT_1; //准备接收
DS18B20_IO_IN(); //输入模式
delay_us(12); //接收延时
if(READ_DS18B20_IO) dat |= 0x80; //读取数据
delay_us(60); //等待时间片结束
}
return dat;
}
/*******************************************************************************
函数名:DS18B20_WriteByte
功能:写一个字节
输入:unsigned char dat
输出:
返回值:
备注:
*******************************************************************************/
void DS18B20_WriteByte(unsigned char dat)
{
unsigned char i;
unsigned char temp;
DS18B20_IO_OUT();//输出模式
for (i=1; i<=8; i++)
{
temp = dat & 0x01;
dat = dat >> 1;
if (temp)
{
DS18B20_OUT_0;
delay_us(2);
DS18B20_OUT_1; //写1
delay_us(60);
}
else
{
DS18B20_OUT_0;//写0
delay_us(60);
DS18B20_OUT_1;
delay_us(2);
}
}
}
/**************************************
从DS18B20中获取温度值得浮点值
参数: 空
返回值: 读取到的温度值(有效范围-55.0~125.0)
**************************************/
float ReadTemperature(void)
{
unsigned char TPH; //存放温度值的高字节
unsigned char TPL; //存放温度值的低字节
short i16=0;
float f32=0;
DS18B20_Init();
DS18B20_WriteByte(0xCC); //跳过ROM命令
DS18B20_WriteByte(0x44); //开始转换命令
DS18B20_Init();
DS18B20_WriteByte(0xCC); //跳过ROM命令
DS18B20_WriteByte(0xBE); //读暂存存储器命令
TPL = DS18B20_ReadByte(); //读温度低字节
TPH = DS18B20_ReadByte(); //读温度高字节
i16 = 0;
i16 = (TPH<<8) |TPL; // 将高位(MSB)与低位(LSB)合并
f32 = i16 * 0.0625; // 12bit精度时温度值计算
return(f32); // 返回读取到的温度数值(float型)
}
#ifndef __DS18B20_H
#define __DS18B20_H
#include "sys.h"
//如果想要修改引脚,只需修改下面的宏
#define RCC_DS18B20_PORT RCC_APB2Periph_GPIOA /* GPIO端口时钟 */
#define DS18B20_GPIO_PIN GPIO_Pin_11
#define DS18B20_GPIO_PORT GPIOA
//IO方向设置(CRL寄存器对应引脚0~7,CRH寄存器对应引脚8~15)
//DS18B20_GPIO_PORT->CRH&=0xFFFFFFF0为PA8引脚输出模式对应的寄存器清空
//DS18B20_GPIO_PORT->CRH|=0x00000008将(CNF8[1:0]设置为10:上拉/下拉输入模式,MODE8[1;0]设置为00:输入模式)
//DS18B20_GPIO_PORT->CRH|=0x00000003将(CNF8[1:0]设置为00:通用推挽输出模式 ,MODE8[1;0]设置为11:最大50MHZ)
#define DS18B20_IO_IN() {DS18B20_GPIO_PORT->CRH&=0xFFFF0FFF;DS18B20_GPIO_PORT->CRH|=0x00008000;}
#define DS18B20_IO_OUT() {DS18B20_GPIO_PORT->CRH&=0xFFFF0FFF;DS18B20_GPIO_PORT->CRH|=0x00003000;}
#define DS18B20_OUT_0 GPIO_ResetBits(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN)//IO为低电平
#define DS18B20_OUT_1 GPIO_SetBits(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN)//IO为高电平
#define READ_DS18B20_IO GPIO_ReadInputDataBit(DS18B20_GPIO_PORT, DS18B20_GPIO_PIN)//读取IO电平
void DS18B20_GPIO_Init(void);
u8 DS18B20_Init(void); //初始化DS18B20
float ReadTemperature(void); //获取温度值
#endif
修改代码中使用OLED的部分,改为使用LCD1602实现
最新发布