概要
大三在读工科女生,分享学习成果,望有同频者可互相学习。
本实验使用单片机实现一个具备密码验证、温度检测以及多种显示功能的综合系统。该系统能够通过密码验证来控制直流电机的运转,实现开锁与关锁功能,还集成了多种辅助模块,提升用户体验和系统的安全性与可靠性。通过LCD显示模块为用户提供直观的操作提示和信息反馈;利用LED指示模块展示系统的工作状态;蜂鸣器报警模块在密码错误等异常情况下持续发出警报;单总线通信实现温度传感器的数据采集,为系统增添更多功能维度;数码管模块用于显示密码输入正确与否的判断、温度等信息;矩阵按键模块则作为用户输入密码的接口。
整体架构流程
整体框架图
整体流程图
Proteus仿真图
硬件模块
温度传感器代码:
// 温度传感器相关函数
void dsreset(void) {
uint i;
DS = 0;
i = 103;
while (i > 0) i--;
DS = 1;
i = 4;
while (i > 0) i--;
}
bit tmpreadbit(void) {
uint i;
bit dat;
DS = 0; i++;
DS = 1; i++; i++;
dat = DS;
i = 8;
while (i > 0) i--;
return (dat);
}
uchar tmpread(void) {
uchar i, j, dat;
dat = 0;
for (i = 1; i <= 8; i++) {
j = tmpreadbit();
dat = (j << 7) | (dat >> 1);
}
return (dat);
}
void tmpwritebyte(uchar dat) {
uint i;
uchar j;
bit testb;
for (j = 1; j <= 8; j++) {
testb = dat & 0x01;
dat = dat >> 1;
if (testb) {
DS = 0;
i++; i++;
DS = 1;
i = 8;
while (i > 0) i--;
} else {
DS = 0;
i = 8;
while (i > 0) i--;
DS = 1;
i++; i++;
}
}
}
void tmpchange(void) {
dsreset();
delay(1);
tmpwritebyte(0xcc);
tmpwritebyte(0x44);
}
uint tmp() {
uchar a, b;
dsreset();
delay(1);
tmpwritebyte(0xcc);
tmpwritebyte(0xbe);
a = tmpread();
b = tmpread();
temp_value = b;
temp_value <<= 8;
temp_value = temp_value | a;
f_temp = temp_value * 0.0625;
temp_value = f_temp * 10 + 0.5;
return temp_value;
}
// 显示温度后三位的函数
void DisplayTemperatureLastThree() {
uint t = tmp();
uchar digit1 = (t % 1000) / 100; // 百位
uchar digit2 = (t % 100) / 10; // 十位
uchar digit3 = t % 10; // 个位
Nixie(2, digit1);
Nixie(3, digit2);
Nixie(4, digit3);
}
温度传感器实时显示当前温度值。
LCD代码:
void write_com(uchar com) {
lcdrs = 0; // 选择命令寄存器
P0 = com; // 将命令写入P0口
delay(5); // 短暂延时
lcden = 1; // 使能LCD
delay(5); // 短暂延时
lcden = 0; // 关闭使能
}
// LCD写数据函数
void write_data(uchar date) {
lcdrs = 1; // 选择数据寄存器
P0 = date; // 将数据写入P0口
delay(5); // 短暂延时
lcden = 1; // 使能LCD
delay(5); // 短暂延时
lcden = 0; // 关闭使能
}
// LCD初始化函数
void lcd_init() {
write_com(0x38); // 设置16x2显示,5x7点阵,8位数据接口
delay(5);
write_com(0x0c); // 显示开,关闭光标
delay(5);
write_com(0x06); // 文字不动,地址自动+1
delay(5);
write_com(0x01); // 清屏
delay(5); // 延时等待清屏完成
}
// LCD显示字符串函数
void lcd_display(uchar *str, uchar line) {
uchar i;
write_com(0x80 + line); // 设置显示位置
for (i = 0; str[i] != '\0'; i++) {
write_data(str[i]); // 写入字符
delay(5); // 短暂延时
}
}
当密码正确时,LCD 显示屏显示 “THANK YOU!!!”,当密码错误时,LCD 显示屏显示 “ERROR!” 。
矩阵键盘扫描代码:
// 矩阵键盘扫描函数
unsigned char MatrixKey() {
unsigned char KeyNumber = 0;
P3 = 0XFF;
P3_0 = 0;
if (P3_3 == 0) { KeyNumber = 1; return KeyNumber; }
if (P3_2 == 0) { KeyNumber = 2; return KeyNumber; }
if (P3_1 == 0) { KeyNumber = 3; return KeyNumber; }
if (P3_6 == 0) { KeyNumber = 4; return KeyNumber; }
P3 = 0XFF;
P3_7 = 0;
if (P3_3 == 0) { KeyNumber = 5; return KeyNumber; }
if (P3_2 == 0) { KeyNumber = 6; return KeyNumber; }
if (P3_1 == 0) { KeyNumber = 7; return KeyNumber; }
if (P3_6 == 0) { KeyNumber = 8; return KeyNumber; }
P3 = 0XFF;
P3_0 = 0;
if (P3_3 == 0) { KeyNumber = 9; return KeyNumber; }
if (P3_2 == 0) { KeyNumber = 10; return KeyNumber; }
if (P3_1 == 0) { KeyNumber = 11; return KeyNumber; }
if (P3_6 == 0) { KeyNumber = 12; return KeyNumber; }
P3 = 0XFF;
P3_7 = 0;
if (P3_3 == 0) { KeyNumber = 13; return KeyNumber; }
if (P3_2 == 0) { KeyNumber = 14; return KeyNumber; }
if (P3_1 == 0) { KeyNumber = 15; return KeyNumber; }
if (P3_6 == 0) { KeyNumber = 16; return KeyNumber; }
return KeyNumber;
}
本次实验使用到矩阵键盘,设定正确密码为:s17。
主函数分析:
// 主函数
void main() {
Timer0_Init();
lcd_init(); // 初始化LCD
PassLen = my_strlen(Password); // 使用自定义的 strlen 函数
while (1) {
// 上电即开始测试温度并显示后三位温度值
tmpchange();
DisplayTemperatureLastThree();
if (isShow) {
// 初始状态下,数码管显示提示输入密码相关信息,这里简单显示一个横线(可根据需求改更合适提示)
ShowChar(0, 0);
ShowChar(1, 0);
ShowChar(3, 0);
ShowChar(4, 0);
ShowChar(5, 0);
ShowChar(6, 0);
} else {
if (isTrue == 1) {
// 密码验证成功后的数码管显示逻辑(已在中断函数里实现部分,这里可保持或添加更多)
} else if (isTrue == 2) {
// 密码验证失败后的数码管显示逻辑(已在中断函数里实现部分,这里可保持或添加更多)
}
}
}
完整代码:
#include "reg52.h"
#include <stdio.h>
#define uchar unsigned char
#define uint unsigned int
// 自定义 strlen 函数
unsigned char my_strlen(char *str) {
unsigned char len = 0;
while (*str!= '\0') {
len++;
str++;
}
return len;
}
// 定义引脚
sbit dula = P2^6; // P2.6
sbit wela = P2^7; // P2.7
sbit dianji = P1^7; // 电机控制引脚
sbit led1 = P1^0; // LED 控制引脚
sbit beep = P2^3; // 蜂鸣器控制引脚
sbit DS = P2^2; // 温度传感器引脚
// LCD引脚定义
sbit lcden = P3^4; // LCD使能引脚 (使用P3^4)
sbit lcdrs = P3^5; // LCD数据/命令选择引脚 (使用P3^5)
// 定义 P3 口的位变量
sbit P3_0 = P3^0;
sbit P3_1 = P3^1;
sbit P3_2 = P3^2;
sbit P3_3 = P3^3;
sbit P3_6 = P3^6;
sbit P3_7 = P3^7;
// 定义 P2 口的位变量
sbit P2_6 = P2^6;
sbit P2_7 = P2^7;
// 定义变量
uchar num = 0, show_num = 1;
uchar code table[] = {0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07, 0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71};
unsigned char i = 0;
unsigned char NixieTable[] = {0x3F, 0x06, 0x5B, 0x4F, 0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, 0x77, 0x7C, 0x39, 0x5E, 0x79, 0x71, 0x00};
unsigned char code Locations[] = {0x7e, 0x7d, 0x7b, 0x77, 0x2f, 0x1f, 0x3f};
unsigned char code ShowC[] = {0x40, 0x73, 0x79, 0x77, 0x7c, 0x39, 0x5e, 0x71, 0x76};
unsigned char PassLen = 0;
bit isShow = 1;
unsigned char isTrue = 0;
char Password[] = "6";
char temp[20] = {0};
// 温度相关变量
uint temp_value; // 温度值
float f_temp; // 浮点温度值
// LCD显示相关变量
uchar code lcd_table[] = " THANK YOU!!! ";
uchar code lcd_table1[] = " DXT XX XFQ ";
uchar code lcd_error[] = " ERROR! ";
// 延时函数
void delay(uint ms) {
uint i, j;
for (i = ms; i > 0; i--)
for (j = 110; j > 0; j--);
}
// 显示字符函数
void ShowChar(unsigned char Location, unsigned char Number) {
P2_7 = 1;
P0 = Locations[Location];
P2_7 = 0;
P2_6 = 1;
P0 = ShowC[Number];
P2_6 = 0;
delay(1);
P0 = 0X0;
}
// 在指定数码管位置显示数字(0 - F)的函数
void Nixie(unsigned char Location, unsigned char Number) {
P2_7 = 1;
P0 = Locations[Location];
P2_7 = 0;
P2_6 = 1;
P0 = NixieTable[Number];
P2_6 = 0;
delay(1);
P0 = 0X0;
}
// 字符串比较函数
bit MyStrCmp(char *a, char *b) {
while (*a!= '\0' || *b!= '\0') {
if (*a == *b) {
a++;
b++;
} else {
return 0;
}
}
return 1;
}
// 矩阵键盘扫描函数
unsigned char MatrixKey() {
unsigned char KeyNumber = 0;
P3 = 0XFF;
P3_0 = 0;
if (P3_3 == 0) { KeyNumber = 1; return KeyNumber; }
if (P3_2 == 0) { KeyNumber = 2; return KeyNumber; }
if (P3_1 == 0) { KeyNumber = 3; return KeyNumber; }
if (P3_6 == 0) { KeyNumber = 4; return KeyNumber; }
P3 = 0XFF;
P3_7 = 0;
if (P3_3 == 0) { KeyNumber = 5; return KeyNumber; }
if (P3_2 == 0) { KeyNumber = 6; return KeyNumber; }
if (P3_1 == 0) { KeyNumber = 7; return KeyNumber; }
if (P3_6 == 0) { KeyNumber = 8; return KeyNumber; }
P3 = 0XFF;
P3_0 = 0;
if (P3_3 == 0) { KeyNumber = 9; return KeyNumber; }
if (P3_2 == 0) { KeyNumber = 10; return KeyNumber; }
if (P3_1 == 0) { KeyNumber = 11; return KeyNumber; }
if (P3_6 == 0) { KeyNumber = 12; return KeyNumber; }
P3 = 0XFF;
P3_7 = 0;
if (P3_3 == 0) { KeyNumber = 13; return KeyNumber; }
if (P3_2 == 0) { KeyNumber = 14; return KeyNumber; }
if (P3_1 == 0) { KeyNumber = 15; return KeyNumber; }
if (P3_6 == 0) { KeyNumber = 16; return KeyNumber; }
return KeyNumber;
}
// 定时器初始化函数
void Timer0_Init() {
TMOD = 0x01;
TF0 = 0;
TR0 = 1;
TH0 = 64535 / 256;
TL0 = 64535 % 256;
ET0 = 1;
EA = 1;
}
// LED 流水灯从上往下流动
void LED_Flow() {
uchar i;
for (i = 0; i < 8; i++) {
P1 = ~(0x01 << i); // 从上往下流动
delay(200); // 延时控制流动速度
}
}
// LED 全部闪烁
void LED_Blink() {
P1 = 0x00; // 全部亮
delay(500);
P1 = 0xFF; // 全部灭
delay(500);
}
// 温度传感器相关函数
void dsreset(void) {
uint i;
DS = 0;
i = 103;
while (i > 0) i--;
DS = 1;
i = 4;
while (i > 0) i--;
}
bit tmpreadbit(void) {
uint i;
bit dat;
DS = 0; i++;
DS = 1; i++; i++;
dat = DS;
i = 8;
while (i > 0) i--;
return (dat);
}
uchar tmpread(void) {
uchar i, j, dat;
dat = 0;
for (i = 1; i <= 8; i++) {
j = tmpreadbit();
dat = (j << 7) | (dat >> 1);
}
return (dat);
}
void tmpwritebyte(uchar dat) {
uint i;
uchar j;
bit testb;
for (j = 1; j <= 8; j++) {
testb = dat & 0x01;
dat = dat >> 1;
if (testb) {
DS = 0;
i++; i++;
DS = 1;
i = 8;
while (i > 0) i--;
} else {
DS = 0;
i = 8;
while (i > 0) i--;
DS = 1;
i++; i++;
}
}
}
void tmpchange(void) {
dsreset();
delay(1);
tmpwritebyte(0xcc);
tmpwritebyte(0x44);
}
uint tmp() {
uchar a, b;
dsreset();
delay(1);
tmpwritebyte(0xcc);
tmpwritebyte(0xbe);
a = tmpread();
b = tmpread();
temp_value = b;
temp_value <<= 8;
temp_value = temp_value | a;
f_temp = temp_value * 0.0625;
temp_value = f_temp * 10 + 0.5;
return temp_value;
}
// 显示温度后三位的函数
void DisplayTemperatureLastThree() {
uint t = tmp();
uchar digit1 = (t % 1000) / 100; // 百位
uchar digit2 = (t % 100) / 10; // 十位
uchar digit3 = t % 10; // 个位
Nixie(2, digit1);
Nixie(3, digit2);
Nixie(4, digit3);
}
// LCD写命令函数
void write_com(uchar com) {
lcdrs = 0; // 选择命令寄存器
P0 = com; // 将命令写入P0口
delay(5); // 短暂延时
lcden = 1; // 使能LCD
delay(5); // 短暂延时
lcden = 0; // 关闭使能
}
// LCD写数据函数
void write_data(uchar date) {
lcdrs = 1; // 选择数据寄存器
P0 = date; // 将数据写入P0口
delay(5); // 短暂延时
lcden = 1; // 使能LCD
delay(5); // 短暂延时
lcden = 0; // 关闭使能
}
// LCD初始化函数
void lcd_init() {
write_com(0x38); // 设置16x2显示,5x7点阵,8位数据接口
delay(5);
write_com(0x0c); // 显示开,关闭光标
delay(5);
write_com(0x06); // 文字不动,地址自动+1
delay(5);
write_com(0x01); // 清屏
delay(5); // 延时等待清屏完成
}
// LCD显示字符串函数
void lcd_display(uchar *str, uchar line) {
uchar i;
write_com(0x80 + line); // 设置显示位置
for (i = 0; str[i] != '\0'; i++) {
write_data(str[i]); // 写入字符
delay(5); // 短暂延时
}
}
// 定时器中断服务函数
void Timer0_ISR() interrupt 1 {
unsigned char Key;
TR0 = 0;
if (isShow!= 1) {
isShow = 1;
isTrue = 0;
i = 0;
delay(3000);
}
Key = MatrixKey();
if (Key!= 0) {
switch (Key) {
case 1: temp[i] = '1'; break;
case 2: temp[i] = '2'; break;
case 3: temp[i] = '3'; break;
case 4: temp[i] = '4'; break;
case 5: temp[i] = '5'; break;
case 6: temp[i] = '6'; break;
case 7: temp[i] = '7'; break;
case 8: temp[i] = '8'; break;
case 9: temp[i] = '9'; break;
case 10: temp[i] = 'a'; break;
case 11: temp[i] = 'b'; break;
case 12: temp[i] = 'c'; break;
case 13: temp[i] = 'd'; break;
case 14: temp[i] = 'e'; break;
case 15: temp[i] = 'f'; break;
case 16: temp[i] = 'g'; break;
}
i++;
if (i <= 6) {
Nixie(0, i);
}
if (i >= PassLen) {
bit isEq = MyStrCmp(temp, Password);
if (isEq) {
isShow = 0;
isTrue = 1;
beep = 0; // 蜂鸣器滴一声
delay(1000);
beep = 1;
dianji = 0; // 电机停止
ShowChar(0, 7);
ShowChar(1, 8);
lcd_display(lcd_table, 0x00); // 显示"THANK YOU!!!"
lcd_display(lcd_table1, 0x40); // 显示"DXT XX XFQ"
while (isTrue == 1) {
LED_Flow(); // 流水灯从上往下流动
}
} else {
isShow = 0;
isTrue = 2;
beep = 0; // 蜂鸣器长响
dianji = 1; // 电机启动
ShowChar(0, 4);
ShowChar(1, 7);
ShowChar(2, 7);
lcd_display(lcd_error, 0x00); // 显示"ERROR!"
while (isTrue == 2) {
LED_Blink(); // 流水灯全部闪烁
}
}
}
}
TR0 = 1;
}
// 主函数
void main() {
Timer0_Init();
lcd_init(); // 初始化LCD
PassLen = my_strlen(Password); // 使用自定义的 strlen 函数
while (1) {
// 上电即开始测试温度并显示后三位温度值
tmpchange();
DisplayTemperatureLastThree();
if (isShow) {
// 初始状态下,数码管显示提示输入密码相关信息,这里简单显示一个横线(可根据需求改更合适提示)
ShowChar(0, 0);
ShowChar(1, 0);
ShowChar(3, 0);
ShowChar(4, 0);
ShowChar(5, 0);
ShowChar(6, 0);
} else {
if (isTrue == 1) {
// 密码验证成功后的数码管显示逻辑(已在中断函数里实现部分,这里可保持或添加更多)
} else if (isTrue == 2) {
// 密码验证失败后的数码管显示逻辑(已在中断函数里实现部分,这里可保持或添加更多)
}
}
}
}
技术细节
本次实验采用郭天祥STC89C52单片机完成,此代码用在其他单片机上需要更改引脚配置,仅用于学习,
小结
项目成果:
数码管上显示实时温度,通过矩阵键盘输入密码,输入过程中数码管实时显示已输入的密码位数,密码输入完成后,系统能够准确比对输入密码与预设密码,当密码正确时,数码管显示“H”,蜂鸣器发出一声提示音,电机跟随流水灯频率转动,LCD 显示屏显示 “THANK YOU!!!”,同时 LED 灯从上往下流动展示流水灯提示效果;当密码错误时,数码管显示“F”,蜂鸣器报警长响,LCD 显示屏显示 “ERROR!” ,LED 灯进行全部亮灭闪烁的报警提示效果。