智能风扇设计
项目概述
本设计是一个基于 C51 单片机的智能风扇控制系统,该系统通过温度传感器(DS18B20)采集环境温度信息,根据温度值自动调节风扇的转速,并且会通过热释电红外传感器检测附近是否有人在,如果有人才启动风扇,否则风扇转速为0。同时在 (LCD12864) 显示屏上实时显示温度和风扇风速信息。系统具有自动模式和手动模式,在自动模式下,风扇转速根据温度自动调节;在手动模式下,用户可以通过按键手动调节风扇转速。该实验通过仿真软件来实现。
课程设计目的
1. 深入理解和掌握 C51 单片机的原理和应用,包括单片机的引脚功能、内部结构、指令系统、定时器、中断等,综合应用这些功能。
2. 熟悉温度传感器(DS18B20)的工作原理和使用方法,能够准确地采集环境温度数据;使用双刀开关来模拟热释电红外传感器。
3. 掌握 (LCD12864) 液晶显示屏的驱动原理和编程方法,能够实现温度和风扇风速等信息的实时显示。
4. 学会使用 PWM(脉冲宽度调制)技术来控制风扇的转速,实现风速的无级调节。
5. 个人能力方面提升:培养自己的硬件设计能力,能够根据项目需求设计合理的电路原理图;提高软件编程能力,能够使用C语言编写单片机的控制程序,实现对温度传感器、LCD显示器和风扇的控制;增强自己的系统调试和故障排除能力,能够结合硬件和软件联合调试,增强代码的模块化;以及提升自己的创新能力和时间能力。
6. 增强环保意识和节能意识,通过智能风扇的设计,实现根据环境温度自动调节风扇转速,达到节能减排的目的。
课程设计内容
硬件设计
- C51单片机:作为系统的控制核心,负责处理温度传感器采集的数据、控制风扇转速和驱动LCD12864显示屏。
- 温度传感器:采用DS18B20数字温度传感器,用于采集环境温度信息,并将数据传输给单片机。
- 风扇驱动电路:使用L298芯片来组成简单驱动电路,通过单片机输出的PWM信号控制风扇的转速。
- LCD12864显示屏:用于显示温度的风扇风速信息。
- 按键电路:用于设置系统的工作模式和手动调节风扇转速,采用独立按键与单片机连接。
- 使用双刀开关来模拟热释电红外传感器模块检测附近是否有人活动。同时加一个LED来提示信息。
详细设计原理图如下:
课程设计的要求与数据
- 温度检测与风扇转速自动控制
利用DS18B20温度传感器准确采集环境温度信息,温度测量范围至少为 - 10℃ - +50℃,精度为±0.5℃。
根据采集到的温度值自动调节风扇转速。例如,当温度低于25℃时,风扇转速为0;当温度在25℃ - 28℃之间时,风扇以1档转速(如占空比为20%)运行;当温度在28℃ - 30℃之间时,风扇以2转速(如占空比为40%)运行;当温度高于32℃时,风扇以更高档转速(如占空比为90%)运行,以此类推。
- 人体检测与风扇启停控制
采用热释电红外传感器检测附近是否有人存在。检测范围为正前方3 - 5米扇形区域,角度范围为120°左右。只有检测到有人时,风扇才会根据温度情况启动并调节转速;若未检测到有人,风扇转速保持为0。
- 显示功能
通过LCD12864模块将环境温度和风扇转速准确的显示出来,温度保留整数(因为这里对温度的精度要求不高),风扇转速分为 1 -> 5 多挡位调节。
- 工作模式的切换
打开风扇默认是以自动模式工作,用户可以自动通过MODE按键,将模式切换为手动。
课程设计应完成的工作
- 项目选题:根据个人兴趣,选择风扇作为课程设计的对象。每到夏天(可能是合师滨湖校区教室没有空调的缘故),总能看到一些同学带着自己的小风扇去上课,但是那些小风扇往往只有开关、调速的功能,下课去上厕所的话可能还要手动关闭,不关闭可能会浪费电能,我就想能不能通过热释电红外传感器来解决这个痛点。着手做这件事情以后,刚好学了DS18B20传感器,一步到位!再加装一个温度传感器来智能调节转速,也减少了电资源的浪费,同时兼顾手动模式。
- 系统设计:
硬件方面:选择合适的C51单片机型号,传感器接口电路, 了解DS18B20的工作原理和通信协议,与单片机连接,实现温度采集,使用热释电红外传感器(使用一个开关来模拟该装置)来准确检测附近是否有人存在,风扇采用直流电机驱动,驱动直流电机采用L298芯片,通过LCD12864来显示相关状态。
软件方面:传感器数据的采集,风扇控制程序,显示程序和模式切换程序。
- 硬件与软件联调:在vscode中编写嵌入式程序(vscode中下载的keil插件,个人感觉keil软件有点老旧),分模块进行编写,与搭建好的硬件电路进行联合调试。检测各个模块之间通信是否正常,传感器采集数据是否准确,风扇转速控制是否有效,以及显示屏显示是否正确等。
- 功能测试:对系统的各项功能进行测试,如在不同温度环境下测试自动模式下风扇转速的自动调节功能;在手动模式下测试按键对风扇转速的调节功能;测试热释电红外传感器检测是否有人存在的准确性以及在有人和无人情况下风扇的启动和停止情况等。
- 仿真实现:使用proteus软件对整个系统进行仿真。在仿真环境中设置合适的温度输入、模拟人体红外信号以及按键操作等,验证系统在各种情况下的功能正确性。同时,通过仿真可以观察系统内部的信号变化、程序执行流程等,有助于发现潜在的问题并进行改进。
- 文字报告和演示:撰写设计报告,展示实验结果和成果,对设计过程进行总结与评价。
总体设计
1. 温度传感器模块
在AT89C51单片机的P1.7引脚接到DS18B20传感器的DQ接口上,用来传感器与单片机数据的传输,获取环境实时温度。
2. 电机驱动模块
AT89C51单片机的P3.0和P3.1引脚连接L298芯片,将单片机输出的弱电信号转换为能够驱动风扇电机的强电信号,来驱动直流电机。
3. 显示模块
将LCD12864的引脚连接在AT89C51单片机的P0口和P2部分口上,(P0口需要上拉电阻),LCD12863显示屏,能够显示汉字、字符和图形等信息,可清晰地显示温度和风扇转速等数据。
4. 热释电红外传感器模块
此传感器用一个开关来模拟实现,连接到AT89C51的P1.0口,用于检测是否有人靠近单片机,以决定风扇是否启动。
5. 按键模块
在AT89C51单片机的P1口接五个按钮,用来切换自动、手动模式或者手动模式下对风扇的操控(转速,暂停开始等)。
详细设计
源程序:
main.c
#include <regx52.h>
#include "LCD12864.h"
#include "DS18B20.h"
#define uint unsigned int
#define uchar unsigned char
#define START 100
#define STOP 101
#define ADD 102
#define SUB 103
#define MODE 104
#define ERO 105
//控制风扇转向的引脚
sbit IN1=P3^0;
sbit IN2=P3^1;
sbit ENA=P3^4;
//热释电红外传感器模块
sbit pir_detect = P1^0; //高电平表示检测到有人,低电平表示无人
uchar Counter=0,Compare=0; //用来控制PWM调速的占空比
unsigned char T = 0; //环境的温度
//智
unsigned char code arr1[] = {
0x10,0x94,0x53,0x32,0x1E,0x32,0x52,0x10,0x00,0x7E,0x42,0x42,0x42,0x7E,0x00,0x00,
0x00,0x00,0x00,0xFF,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0xFF,0x00,0x00,0x00,0x00,
};
//能
unsigned char code arr2[] = {
0x08,0xCC,0x4A,0x49,0x48,0x4A,0xCC,0x18,0x00,0x7F,0x88,0x88,0x84,0x82,0xE0,0x00,
0x00,0xFF,0x12,0x12,0x52,0x92,0x7F,0x00,0x00,0x7E,0x88,0x88,0x84,0x82,0xE0,0x00,
};
//普
unsigned char code pu[] = {
0x40,0x44,0x54,0x64,0x45,0x7E,0x44,0x44,0x44,0x7E,0x45,0x64,0x54,0x44,0x40,0x00,
0x00,0x00,0x00,0xFF,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0xFF,0x00,0x00,0x00,0x00,
};
//通
unsigned char code tong[] = {
0x40,0x42,0xCC,0x00,0x00,0xE2,0x22,0x2A,0x2A,0xF2,0x2A,0x26,0x22,0xE0,0x00,0x00,
0x80,0x40,0x3F,0x40,0x80,0xFF,0x89,0x89,0x89,0xBF,0x89,0xA9,0xC9,0xBF,0x80,0x00,
};
//风
unsigned char code arr3[] = {
0x00,0x00,0xFE,0x02,0x12,0x22,0xC2,0x02,0xC2,0x32,0x02,0xFE,0x00,0x00,0x00,0x00,
0x80,0x60,0x1F,0x00,0x20,0x10,0x0C,0x03,0x0C,0x30,0x00,0x0F,0x30,0x40,0xF8,0x00,
};
//扇
unsigned char code arr4[] = {
0x00,0x00,0xFC,0x24,0x24,0x24,0x25,0x26,0x24,0x24,0x24,0x24,0x24,0x3C,0x00,0x00,
0x40,0x30,0x0F,0x21,0x15,0x49,0x81,0x7F,0x00,0x21,0x15,0x49,0x81,0x7F,0x00,0x00,
};
//温
unsigned char code wen[] = {
0x10,0x60,0x02,0x8C,0x00,0x00,0xFE,0x92,0x92,0x92,0x92,0x92,0xFE,0x00,0x00,0x00,
0x04,0x04,0x7E,0x01,0x40,0x7E,0x42,0x42,0x7E,0x42,0x7E,0x42,0x42,0x7E,0x40,0x00,
};
//度
unsigned char code du[] = {
0x00,0x00,0xFC,0x24,0x24,0x24,0xFC,0x25,0x26,0x24,0xFC,0x24,0x24,0x24,0x04,0x00,
0x40,0x30,0x8F,0x80,0x84,0x4C,0x55,0x25,0x25,0x25,0x55,0x4C,0x80,0x80,0x80,0x00,
};
//转
unsigned char code zhuan[] = {
0xC8,0xB8,0x8F,0xE8,0x88,0x88,0x40,0x48,0x48,0xE8,0x5F,0x48,0x48,0x48,0x40,0x00,
0x08,0x18,0x08,0xFF,0x04,0x04,0x00,0x02,0x0B,0x12,0x22,0xD2,0x0A,0x06,0x00,0x00,
};
//速
unsigned char code su[] = {
0x40,0x40,0x42,0xCC,0x00,0x04,0xF4,0x94,0x94,0xFF,0x94,0x94,0xF4,0x04,0x00,0x00,
0x00,0x40,0x20,0x1F,0x20,0x48,0x44,0x42,0x41,0x5F,0x41,0x42,0x44,0x48,0x40,0x00,
};
//8 * 16 数字显示
unsigned char code num[] = {
0x00,0x00,0xF0,0xF8,0x08,0x68,0xF8,0xF0,0x00,0x00,0x07,0x0F,0x0B,0x08,0x0F,0x07,
0x00,0x20,0x20,0x30,0xF8,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x0F,0x0F,0x00,0x00,
0x00,0x30,0x38,0x08,0x88,0xF8,0x70,0x00,0x00,0x0C,0x0E,0x0B,0x09,0x08,0x08,0x00,
0x00,0x30,0x38,0x88,0x88,0xF8,0x70,0x00,0x00,0x06,0x0E,0x08,0x08,0x0F,0x07,0x00,
0x00,0x00,0xF8,0xF8,0x00,0xE0,0xE0,0x00,0x00,0x03,0x03,0x02,0x02,0x0F,0x0F,0x02,
0x00,0xF8,0xF8,0x88,0x88,0x88,0x08,0x00,0x00,0x08,0x08,0x08,0x0C,0x07,0x03,0x00,
0x00,0xC0,0xE0,0x78,0x58,0xC8,0x80,0x00,0x00,0x07,0x0F,0x08,0x08,0x0F,0x07,0x00,
0x00,0x08,0x08,0x88,0xE8,0x78,0x18,0x00,0x00,0x00,0x0E,0x0F,0x01,0x00,0x00,0x00,
0x00,0x70,0xF8,0xC8,0x88,0xF8,0x70,0x00,0x00,0x07,0x0F,0x08,0x09,0x0F,0x07,0x00,
0x00,0xF0,0xF8,0x08,0x08,0xF8,0xF0,0x00,0x00,0x00,0x09,0x0D,0x0F,0x03,0x01,0x00,
};
void Timer0_Init(void);
void Delay(unsigned char count);
unsigned char get_P1_Button(void);
void Manual(void);
unsigned char get_grade(unsigned char Compare);
void show_wind_speed(void);
void show_temperature(void);
void main(void) {
unsigned char Button;
Timer0_Init();
LCD12864_Init();
//显示智能风扇
show_ch(1, 0, 2*16, arr1);
show_ch(1, 0, 3*16, arr2);
show_ch(2, 0, 0*16, arr3);
show_ch(2, 0, 1*16, arr4);
//显示温度
show_ch(1, 3, 0*16, wen);
show_ch(1, 3, 1*16, du);
//显示转速
show_ch(1, 6, 0*16, zhuan);
show_ch(1, 6, 1*16, su);
IN2 = 1; //将电机的一个引脚固定为高电平 - 通过控制IN1来调速
while (1) {
DS18B20_ConvertT(); //启动温度转换
T = DS18B20_ReadT(); //读取温度的数据
if (T > 35) {
Compare = 100;
} else if (T > 33) {
Compare = 80;
} else if (T > 30) {
Compare = 60;
} else if (T > 28) {
Compare = 40;
} else if (T > 26) {
Compare = 20;
} else { //温度低于阈值,不需要风扇
Compare = 0;
}
//检测用户是否按下模式切换按钮
Button = get_P1_Button();
if (Button == MODE) {
Manual();
show_ch(1, 0, 2*16, arr1);
show_ch(1, 0, 3*16, arr2);
}
//对显示的风速数据进行更新
if (pir_detect == 0) {
Compare = 0;
}
//显示风速
show_wind_speed();
//显示温度
show_temperature();
}
}
//每隔100us - 0.1ms,触发中断一次
void Timer0_Init(void) {
TMOD &= 0xf0;
TMOD |= 0x01;
TH0 = 0xff;
TL0 = 0x9c;
EA = 1;
ET0 = 1;
TR0 = 1;
}
//延时函数1ms
void Delay(unsigned char count) //@12.000MHz
{
unsigned char i, j;
while(count--) {
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
//获取P1口的按钮信息
unsigned char get_P1_Button(void) {
if (P1_1 == 0) { Delay(10); while(P1_1 == 0); return START; }
if (P1_2 == 0) { Delay(10); while(P1_2 == 0); return STOP; }
if (P1_3 == 0) { Delay(10); while(P1_3 == 0); return ADD; }
if (P1_4 == 0) { Delay(10); while (P1_4 == 0); return SUB; }
if (P1_5 == 0) { Delay(10); while (P1_5 == 0); return MODE; }
return ERO;
}
//手动控制风扇函数
void Manual(void) {
unsigned char Button;
unsigned char num;
show_ch(1, 0, 2*16, pu);
show_ch(1, 0, 3*16, tong);
while (1) {
Button = get_P1_Button();
switch(Button)
{
case START: Compare = 20; break;
case STOP : Compare = 0; break;
case ADD : Compare += 20;
if (Compare > 100) Compare = 100;
break;
case SUB : Compare -= 20;
if (Compare < 0) Compare = 0;
break;
case MODE : return;
default : break;
}
num = get_grade(Compare);
show_wind_speed();
}
}
//返回此时风扇的转速
unsigned char get_grade(unsigned char Compare) {
switch (Compare)
{
case 100: return 5;
case 80 : return 4;
case 60 : return 3;
case 40 : return 2;
case 20 : return 1;
default: return 0;
}
}
//将风速显示到lcd12864屏幕
void show_wind_speed(void) {
show_num(2, 6, 0*8, num + get_grade(Compare)*16);
}
//将温度显示到lcd12864屏幕
void show_temperature(void) {
show_num(2, 3, 0*8, num + (T/10)*16);
show_num(2, 3, 1*8, num + (T%10)*16);
}
void Timer0(void) interrupt 1 {
TMOD &= 0xf0;
TMOD |= 0x01;
Counter++;
Counter %= 100; // 10ms是一个周期
//表示检测到有人存在
if (pir_detect == 1) {
if (Counter < Compare) {
IN1 = 0;
} else {
IN1 = 1;
}
} else {
IN1 = 1; //将电机停止
}
}
LCD12864.h
#ifndef _LCD12168_H_
#define _LCD12168_H_
#define uint8 unsigned char
void show_im(uint8 screen,uint8 page,uint8 column,uint8 *p);
void LCD12864_Init(void);
void show_ch(uint8 screen,uint8 page,uint8 column,uint8 *p);
void show_num(uint8 screen, uint8 page, uint8 column, uint8 *p);
void ClearScreen(unsigned int screen);
void SelectScreen(unsigned int screen);
void lcd12864_WriteCommed(unsigned char commed);
#endif
LCD12864.c
由于keil和vscode的编码不统一,导致我的注释变成了乱码
有看不懂的地方,可以自行将代码拉取到gpt进行翻译解释
#include <regx52.h>
#define uint8 unsigned char
#define out P0
sbit LCD_CS1 = P2 ^ 0;
sbit LCD_CS2 = P2 ^ 1;
sbit RS = P2 ^ 2;
sbit RW = P2 ^ 3;
sbit E = P2 ^ 4;
void lcd12864_delay(unsigned int t) //@12.000MHz
{
unsigned char i, j;
while (t--) {
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
void lcd12864_Busy(void) // 12864 ??
{
out = 0X00; // ???????? 0x00
RS = 0; // ? RS ????????
RW = 1; // ? RW ????????
E = 1; // ? EN ????????,????
while (out & 0x80); // ????? 1 ?,?????????,?????
E = 0; // ? EN ????????
}
void lcd12864_WriteCommed(unsigned char commed) // 12864 ???
{
unsigned char i = 0;
lcd12864_Busy(); // 12864 ??,??????????????
RS = 0; // ? RS ????????,?????
RW = 0; // ? RW ????????,?????
out = commed; // ???????????
E = 1; // ? EN ????????,????
i = 2; while(i--); //????
E = 0; // ? EN ????????,?????????
}
void lcd12864_WriteData(unsigned char dat) // 12864 ???
{
unsigned char i = 0;
lcd12864_Busy(); // 12864 ??
RS = 1; // ? RS ????????,?????
RW = 0; // ? RW ????????,?????
out = dat; // ?????????
E = 1; // ? EN ????????,????
i = 2; while(i--);
E = 0; // ? EN ????????,?????????
}
void Set_page(unsigned int page) { //???????
page = 0xB8 | page; //??????0xB8
lcd12864_WriteCommed(page);
}
void Set_line(unsigned int startline) { //????????
startline = 0xC0 | startline;
lcd12864_WriteCommed(startline);
}
void Set_column(unsigned int column) { //??????
column &= 0x3F; //??????64
column |= 0x40; //??????0x40
lcd12864_WriteCommed(column);
}
void SetOnOff(unsigned onoff) { //?????? 0x3E?,0x3F??
onoff = 0x3F | onoff; //onoff?0?????,?1?????
lcd12864_WriteCommed(onoff);
}
void SelectScreen(unsigned int screen) {
switch(screen) {
case 0:LCD_CS1 = 0; LCD_CS2 = 0; break;
case 1:LCD_CS1 = 0; LCD_CS2 = 1; break;
case 2:LCD_CS1 = 1; LCD_CS2 = 0; break;
default: break;
}
}
void ClearScreen(unsigned int screen) { //??
unsigned int i, j;
SelectScreen(screen);
for (i = 0; i < 8; i++) {
Set_page(i);
Set_column(0);
for (j = 0; j < 64; j++) {
lcd12864_WriteData(0x00); //??0,???????1
}
}
}
void LCD12864_Init(void) { //?????
lcd12864_Busy();
SelectScreen(0);
SetOnOff(0);
SelectScreen(0);
SetOnOff(1);
SelectScreen(0);
ClearScreen(0);
Set_line(0);
}
//????16*16???
void show_ch(uint8 screen,uint8 page,uint8 column,uint8 *p)
{
uint8 i;
SelectScreen(screen);
Set_page(page);
Set_column(column);
for(i=0;i<16;i++) //??16*16???
{
lcd12864_WriteData(p[i]);
}
Set_page(page+1);
Set_column(column);
for(i=0;i<16;i++) //??16*16???,"????"
{
lcd12864_WriteData(p[i+16]);
}
}
//显示8*16的数字函数
void show_num(uint8 screen, uint8 page, uint8 column, uint8 *p)
{
uint8 i;
SelectScreen(screen);
Set_page(page);
Set_column(column);
for (i = 0; i < 8; i++)
{
lcd12864_WriteData(p[i]);
}
Set_page(page + 1);
Set_column(column);
for (i = 0; i < 8; i++)
{
lcd12864_WriteData(p[i + 8]);
}
}
//????64*64???
void show_im(uint8 screen,uint8 page,uint8 column,uint8 *p)
{
uint8 i;
SelectScreen(screen);
Set_page(page);
Set_column(column);
for(i=0;i<32;i++) //??16*16???
{
lcd12864_WriteData(p[i]);
}
Set_page(page+1);
Set_column(column);
for(i=0;i<32;i++) //??16*16???,"????"
{
lcd12864_WriteData(p[i+32]);
}
Set_page(page+2);
Set_column(column);
for(i=0;i<32;i++) //??16*16???,"????"
{
lcd12864_WriteData(p[i+64]);
}
Set_page(page+3);
Set_column(column);
for(i=0;i<32;i++) //??16*16???,"????"
{
lcd12864_WriteData(p[i+96]);
}
}
DS18B20.h
#ifndef _DS18B20_H_
#define _DS18B20_H_
void DS18B20_ConvertT(void);
float DS18B20_ReadT(void);
#endif
DS18B20.c
#include <regx52.h>
#include "OneWire.h"
#define DS18B20_SKIP_ROM 0xCC //跳过rom指令
#define DS18B20_CONVERT_T 0x44 //启动温度转换的指令
#define DS18B20_READ_SCRATCHPAD 0xBE //读取温度传感器内部暂存器(Scratchpad)数据
void Delay1ms(unsigned int t)//t毫秒延时函数
{
unsigned char a,b;
unsigned int i;
for(i=0;i<t;i++)
for(b=199;b>0;b--)
for(a=1;a>0;a--);
}
//用于启动DS18B20温度传感器的温度转换
void DS18B20_ConvertT(void) {
unsigned char i = 0;
OneWire_Init(); // 初始化
Delay1ms(1); //延时1ms
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_CONVERT_T); //延时1ms
Delay1ms(1000);//延时1s等待转化
}
//读取DS18B20内部暂存器里面的数据
float DS18B20_ReadT(void) {
unsigned char LSB = 0, MSB = 0; //L表示低位,M表示高位
int temp = 0;
float T = 0;
OneWire_Init(); // 初始化
Delay1ms(1);
OneWire_SendByte(DS18B20_SKIP_ROM);
OneWire_SendByte(DS18B20_READ_SCRATCHPAD);
LSB = OneWire_ReceiveByte(); //读取16位的低位
MSB = OneWire_ReceiveByte(); //读取16位的高位
temp = (MSB << 8) | LSB;
T = temp / 16.0;
return T;
}
OneWire.h
#ifndef _ONEWIRE_H_
#define _ONEWIRE_H_
unsigned char OneWire_Init(void);
void OneWire_SendBit(unsigned char Bit);
unsigned char OneWire_ReceiveBit(void);
void OneWire_SendByte(unsigned char Byte);
unsigned char OneWire_ReceiveByte(void);
#endif
OneWire.c
#include <regx52.h>
#include "LCD.h"
sbit OneWire_DQ = P1^7; //数据总线
void Delay1000ms() //@12.000MHz
{
unsigned char i, j, k;
i = 8;
j = 154;
k = 122;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
void Delay600us(void)//600us延时函数
{
unsigned char a,b;
for(b=119;b>0;b--)
for(a=1;a>0;a--);
}
//初始化,返回1表示成功
unsigned char OneWire_Init(void) {
// unsigned int i = 0;
// unsigned char AckBit;
// OneWire_DQ = 1;
// OneWire_DQ = 0;
// i = 260; while (--i); //延时500us
// OneWire_DQ = 1;
// i = 32; while (--i); //延时70us
// AckBit = OneWire_DQ;
// i = 247; while (--i); //延时500us - 走完这个时序
OneWire_DQ=0;//主动拉低480-960us(此处选择600us)
Delay600us();
OneWire_DQ=1;//释放总线,传感器15-60us后拉低总线
while(OneWire_DQ);//等待传感器拉低;
while(!OneWire_DQ);//度过传感器被拉低的时间(60-240us)后主动拉高
OneWire_DQ=1;//主动拉高
return 1;
}
//主机向从机发送一位数据
void OneWire_SendBit(unsigned char Bit) {
unsigned char i = 0;
OneWire_DQ = 0;//拉低电平
i = 3; while (--i); //延时6us - 告诉从机主机要发送数据
// OneWire_DQ = Bit; -------------------------------------- 更改逻辑
if (Bit != 0) {
OneWire_DQ = 1;
} else {
OneWire_DQ = 0;
}
// LCD_ShowNum(2, 5, OneWire_DQ, 2);
// Delay1000ms();
i = 30; while (--i); //延时60us - 等待从机读取数据
i = 3; while (--i); //延时6us - 告诉从机主机要发送数据
OneWire_DQ = 1; //将数据线恢复
i = 2; while (--i); //延时4us
}
//主机接收从机发送的一位数据
unsigned char OneWire_ReceiveBit(void) {
unsigned char i = 0;
unsigned char ACK = 0;
OneWire_DQ = 0;
i = 1; while (--i); //延时2us
OneWire_DQ = 1; //释放总线,等待
i = 3; while (--i); //延时6us
// ACK = OneWire_DQ; //获取从机发送发的数据
if (OneWire_DQ == 1) {
ACK = 1;
} else {
ACK = 0;
}
i = 30; while (--i); //延时60us,凑齐时间切片
i = 2; while (--i); //延时4us
return ACK;
}
//主机向从机发送一个字节的数据
void OneWire_SendByte(unsigned char Byte) {
unsigned char i = 0;
for (i = 0; i < 8; i++) {
OneWire_SendBit(Byte & (0x01 << i));
}
}
//主机接收一个字节的数据
unsigned char OneWire_ReceiveByte(void) {
unsigned char ACK = 0;
unsigned char i = 0;
for (i = 0; i < 8; i++) {
if (OneWire_ReceiveBit()) {ACK |= (0x01 << i); }
}
return ACK;
}
总结
通过本次课程设计,对C51单片机的内部结构、定时器、中断、I/O口等功能有了更深入的理解。学会了如何利用单片机的这些功能来实现复杂的控制逻辑,如通过定时器产生PWM信号来控制风扇转速,利用中断来处理传感器的实时数据等。
掌握了DS18B20温度传感器和热释电红外传感器的工作原理、接口电路设计以及驱动程序的编写。了解了如何根据传感器的特性来采集和处理数据,并且能够将传感器的数据与单片机的控制逻辑相结合,实现智能化的系统功能。
学会了LCD12864显示屏的使用方法,包括其初始化设置、数据显示格式的定义以及如何将采集到的系统信息准确地显示在屏幕上。这为今后在其他嵌入式系统中实现显示功能提供了宝贵的经验。
在课程设计过程中,遇到了许多硬件和软件方面的问题,需要同时从硬件电路和软件程序两个方面进行分析和排查。通过不断地调试和优化,逐渐掌握了硬件与软件协同调试的方法,能够快速定位问题所在,并采取有效的解决措施。这种能力的培养对于今后从事嵌入式系统开发工作具有重要意义。
由于自身水平有限或时间方面原因,课程设计存在很多不足与可以改进之处:1. 虽然在设计过程中采取了一些措施来提高系统的稳定性,如数据滤波、防抖动处理等,但在实际运行过程中,仍然存在一些小概率的异常情况,如风扇转速在临界值附近波动或者显示器乱码现象; 2. 本设计实现了基本的温度控制和人体检测功能,但在功能的丰富性方面还有所欠缺,不能处理复杂情况;3. LCD12864显示屏仅简单地显示温度和风扇转速信息,显示界面比较单一,缺乏交互性。用户无法通过显示屏进行更多的操作,如查看历史温度数据、设置风扇转速的默认值等。
解决方案:在软件方面,优化算法,提高数据处理的准确性;优化PWM 控制算法,使风扇转速的调节更加平滑稳定;增加功能模块,根据空气湿度或者空气质量,多级调节风扇转速,更能准确捕获环境情况;改进用户界面设计,重新设计LCD12864的显示界面,加入一些小图案或者动态显示,增加更多显示内容,例如菜单以及选项等交互功能、与手机APP远程联动,实时获取环境信息和风扇工作状态(通过增加无线通信模块)等。
本课程设计的初衷来源于日常生活,在教室或者实验室,总是被忘记关灯或者关空调的事情困扰,又在学校学习了单片机,想着通过单片机来解决这个痛点,通过摄像头或者红外对射模块记录实验室人员数量,当实验室有人员时,自动检测温度和湿度、以及光线是否适宜,并且自动调节空调温度和湿度,以及是否打开电灯,同时链接手机APP实时查看实验室情况;检测到实验室没人时,会自动打开定时器,十分钟没人进出会自动将所有电器断电,实现节能的同时,解放实验室成员的双手(懒人专属)。而后将这个项目简化,而来的“智能风扇”项目。
- 参考资料
- 单片机原理
- 单片机设计与教程
结束语:笔者能力尚有不足,此次为初次撰写博客,若存在不当之处,恳请读者予以谅解。