初学单片机,基于51单片机的电子琴PCB图可以更简单
前面介绍了我的小目标“单片机电子琴”的设计思路和原理图,今天继续PCB图的制作。
出于对节省时间和费用的考虑,采用“洞洞板”制作比较易行。
由于单片机STC15W408AS为贴片封装,大体积的只有SOP28封装,比较适合。我们在程序没有设计完成之前,没有必要专门制作PCB蚀刻电路板。用洞洞板的话先要解决贴片转直插的问题,用排针来转换。两排插针焊装在洞洞板上,把贴片封装的单片机管脚用细导线连接到插针上,如图所示:
基于51单片机的电子琴PCB图
当然,如果手边有转换小板的话,就不用手工焊接连线了。
下面是3D展示图:
基于51单片机的电子琴PCB立体图
背面连线图(从背面看到的,不是正面看到的):
基于51单片机的电子琴PCB背面图
为了更简单的制作电路,这里把按键省略,双排插针省略,只连接用到的连线。如下图:
基于51单片机的电子琴精简PCB图
不用按键怎么使用呢?可以用金属尖镊子、万用表笔、自制金属头的笔等方法,只要连线到R1就可以。使用的时候用笔尖接触单片机管脚即可。
图中用到的元器件说明:
P2 :电源接口。如果用USB供电,可换成USB插座;如果用杜邦线取电自下载小板,可以换成插针;
S13 :电源开关。
LED1, R4 :电源指示。可以不用。
C2 :电源滤波电容。耐压>6V,例如16V;容量100~470uF。供电良好的情况下也可以省略。
C1 :高频滤波电容。一般采用独石电容,也可用瓷片电容。
Q1 :C8050,9013均可。
LS1 :蜂鸣器。无源型,5v的,直径12mm。
R3 :限流电阻。1~10欧。
R1 :限流电阻。防止对单片机管脚直接接地(在I/O口设置错误的情况下)造成损坏。阻值可改变。
P1 :串口下载程序代码的接口。
R2 :限流电阻。阻值可根据供电电压调整。
U1 :STC15W408AS系列单片机。401、402、404、408均可,建议容量大点的。
最后说下什么是“洞洞板”?看图吧:
采用洞洞板焊接的线路图片
注:图中的绿色元件同上文中的P2(电源接线端子),适合连接电池盒的导线。
以上介绍了单片机电子琴的材料和PCB布局连线图,如果您感兴趣,欢迎在评论里交流讨论,也欢迎关注头条号/春天说,我们将继续单片机的学习之旅。。。
51单片机超声波水位控制器设计 (C源码+PCB+原理图与实物制作)
最近几天总是下雨,搞的人心情也很差,昨天看到有粉丝留言说,导师让他们组做一个关于超声波控制液体水位的项目,但是无从下手,希望我帮帮他,可以有偿。他是我的铁粉,下面我就把我前段时间帮人家设计的一个超声波水位自动控制系统 分享出来,希望可以帮到他们。
51单片机超声波水位控制器设计
设计说明:
本设计中液晶显示有4个字母,分别为
H------容器的最高水位设定值(不能高于实际高度) L------容器的最低水位设定值
D-----容器实际高度(可以设置)
C-----容器内液体的高度(在实际演示中,障碍物离探头越近,液晶C显示越大,因为障碍物好比液面,离探头近了说明水位高了)
特别提醒:如果容器实际高度D你设置为1米,那么C液体的高度最高能测到98cm,因为探头的盲区在2cm左右。如果D设为2米,那么最高能测到1.98m.
按键功能分别为:设置键 增加键 减小键 复位键
三个指示灯的分别功能为:红色----超过设定的最高水位H 黄色-----低于设定的最低水位L
绿色----最高H和最低L中间
本文采用AT89C52单片机系统实现了水塔水位的自动控制,设计出一种低成本、高实用价值的水塔水位控制器。该系统具有水位检测、水位高度LCD显示、低水位高水位报警以及自动加水等功能。
本设计过程中主要采用了传感技术、单片机技术、光报警技术以及弱电控制强电的技术。本设计传感器使用了超声波模块,并且详细阐述了超声波测距测的原理,给出了系统构成框图。此系统具有易控制、工作可靠、测量精度高的优点,可实时监控液位。并采用52单片机系统控制整个电路的信号处理以及采用光电耦合和继电器来实现弱电控制强电来实现加水系统的自动控制。它能自动完成水位检测、光报警、上水停水的全部工作循环,保证液面高度始终处于较理想的范围内,它结构简单,制造成本低,灵敏度高,节约能源显著,是用于各种高层液体储存的理想设备。
为了大家更好地理解,请如下看示意图
制作出来的实物图如下:
AD的设计图如下:
超声波水位控制器元件清单
1) 9*15万用板 1
2) AT89C51单片机 1
3) 超声波探头 0
4) 40脚IC座 1
5) 4脚排针 0
6) 杜邦线4根 0
7) 继电器*2 0
8) LCD1602液晶 1
9) 103电位器 0
10) 16脚IC座 0
11) 16脚排针 1
12) 蜂鸣器 0
13) 8550三极管*3 0
14) 1k电阻*8 0
15) 10k电阻 0
16) 10uf电容 0
17) 30pf电容*2 0
18) 12M晶振 1
19) 3mmLED(红、绿各2个,黄1个) 0
20) 轻触按键*4 1
21) 自锁开关 1
22) DC电源插口 1
23) USB电源线(电池盒)
24) 直流水泵*2(根据客户自选)
单片机程序源码如下:
/***************************************************************
名称:基于51单片机的超声波水位监测报警系统
单片机型号:AT89C51
单片机设置:时钟12T,晶体12MHZ
作者:从零开始学单片机
注:修改增加水泵控制和排水控制,即双继电器
***************************************************************/
#include <reg51.h>
#include <intrins.h> // 包含循环移位:_cror_
#include "main.h"
//----------------------------------------------------------------------
uchar code TabNumASCII[10] = {'0','1','2','3','4','5','6','7','8','9'};
bool g_flag = isNo; //用于标记超时(65.536ms)
bool g_flag05s = isNo; //用于标记0.52秒
uchar ucCount = 0; //用于计数0.52秒
uint uiH = 80; //设定的最高报警水位 H
uint uiL = 30; //设定的最低报警水位 L
uint uiD = 100; //检测探头到水库底部的距离 D
bool g_flagSwitch = isNo; //控制阀门连续开启间隔延时(保护)标志
bool g_flagBeepTimer = isNo; //定时提醒标志
//-----------------------------------------------------------------------
// 延时10us
void delay10us(void) //@12MHz
{
unsigned char i;
_nop_();
i = 2;
while (--i);
}
// 延时100us
void delay100us(void) //@12MHz
{
uchar i;
_nop_();
i = 47;
while (--i);
}
// 延时125us
void delay125us(void) //@12MHz
{
unsigned char i;
i = 60;
while (--i);
}
// 延时5ms
void delay5ms(void) //@12.000MHz
{
unsigned char i, j;
i = 10;
j = 183;
do
{
while (--j);
} while (--i);
}
// 延时500ms
void delay500ms(void) //@12MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 205;
k = 187;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
//-----------------------------------------------------------------------
//初始化IO端口
void initIO(void)
{
P0 = 0xff;
P1 = 0xff;
P2 = 0xff;
P3 = 0xff;
}
// 初始化定时器0,定时器时钟12T模式 模式1,16位 @12.000MHz
void initTimer0(void)
{
TMOD &= 0xF0; //设置定时器模式
TMOD |= 0x01; //设置定时器模式
TL0 = 0; //定时器初值清零
TH0 = 0; //定时器初值清零
//TR0 = 1; //开定时器0
ET0 = 1; //开定时器0中断
EA = 1; //开总中断
}
// 初始化定时器1,定时器时钟12T模式 模式1,16位 @12.000MHz
void initTimer1(void) //50毫秒@12.000MHz
{
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x10; //设置定时器模式
TL1 = 0xB0; //设置定时初值
TH1 = 0x3C; //设置定时初值
TR1 = 1; //定时器1开始计时
ET1 = 1; //开定时器0中断
}
//-----------------------------------------------------------------------
//定时器0中断
void zd0(void) interrupt 1
{
g_flag = isYes; //中断溢出标志,g_flag = isYes超过测距范围
if(++ucCount >= 8)
{
ucCount = 0;
g_flag05s = isYes; //g_flag05s = isYes定时0.52秒到,用于测量周期延时
}
TL0 = 0; //设置定时初值
TH0 = 0; //设置定时初值
}
//定时器1中断 定时50ms
void tm1_isr() interrupt 3 using 1
{
static uchar count = DATA_switchTime; //50ms的200倍 = 10S
static uchar uiCount = 1200; // = 1分钟
static uint uiCount_BeepTimer = DATA_BeepTimer;
TL1 = 0xB0; //设置定时初值
TH1 = 0x3C; //设置定时初值
if (g_flagSwitch == isNo)
{
if (count-- == 0) //50ms * 200 -> 10s
{
count = DATA_switchTime;
g_flagSwitch = isYes;
// TR1 = 0;
}
}
if(g_flagBeepTimer == isNo)
{
if (uiCount-- == 0) //= 1分钟
{
uiCount = 1200;
if(uiCount_BeepTimer-- == 0)
{
uiCount_BeepTimer = DATA_BeepTimer;
g_flagBeepTimer = isYes;
// TR1 = 0;
}
}
}
}
//-----------------------------------------------
//外部中断1
void exint1() interrupt 2
{
EX1 = 0; //关闭当前中断
TR0 = 0; //关闭时器0
}
//-----------------------------------------------------------------------
//读LCD忙状态并等待忙状态结束
void LCD_waitNotBusy(void)
{
IO_LCD_Data = 0xFF;
io_LCD_RS = 0;
io_LCD_RW = 1;
io_LCD_E = 0;
_nop_();
_nop_();
io_LCD_E = 1;
while(IO_LCD_Data & 0x80); //检测如果是忙信号,一直等到不忙
}
//给LCD写指令
void LCDWriteCommand(uchar command,bool ifReadBusy) //ifReadBusy = 1 时先进行忙检测
{
if (ifReadBusy == isReadBusy) LCD_waitNotBusy(); //根据需要检测忙
IO_LCD_Data = command;
io_LCD_RS = 0;
io_LCD_RW = 0;
io_LCD_E = 0;
_nop_();
_nop_();
io_LCD_E = 1;
}
//给LCD写数据
void LCDWriteData(uchar dat)
{
LCD_waitNotBusy(); //等到不忙
IO_LCD_Data = dat;
io_LCD_RS = 1;
io_LCD_RW = 0;
io_LCD_E = 0;
_nop_();
_nop_();
io_LCD_E = 1;
}
// 初始化LCD1602液晶显示屏
void initLCD1602(void)
{
uchar i;
IO_LCD_Data = 0; // 数据端口清零
for(i = 0; i < 3; i++) // 设置三次显示模式
{
LCDWriteCommand(0x38,isNotReadBusy); // 不检测忙信号
delay5ms();
}
LCDWriteCommand(0x38,isReadBusy); // 设置显示模式,检测忙信号
LCDWriteCommand(0x08,isReadBusy); // 关闭显示
LCDWriteCommand(0x01,isReadBusy); // 显示清屏
LCDWriteCommand(0x06,isReadBusy); // 显示光标移动设置
LCDWriteCommand(0x0F,isReadBusy); // 显示开及光标设置
}
//按指定位置显示一个字符
void putOneCharToLCD1602(uchar line, uchar position, uchar ucData)
{
line &= DATA_LineMax;
position &= DATA_PositionMax;
if (line == DATA_LineTow) position |= 0x40; //当要显示第二行时地址码+0x40;
position |= 0x80; //设置两行显示格式 D7 = 1;
LCDWriteCommand(position, isReadBusy); //发送命令 设置字符地址
LCDWriteData(ucData); //写入字符的数据
}
//按指定位置显示一串字符
void putLineCharsToLCD1602(uchar line, uchar position, uchar count, uchar code *ucData)
{
uchar i;
for(i = 0; i < count; i++) //连续显示单个字符
{
putOneCharToLCD1602(line, position + i, ucData[i]);
}
}
//按指定位置连续显示三个字符(三位数字)
void putThreeCharToLCD1602(uchar line, uchar position, uint uiNumber)
{
uiNumber %= 1000;
putOneCharToLCD1602(line, position, TabNumASCII[uiNumber / 100]);
putOneCharToLCD1602(line, ++position, TabNumASCII[uiNumber % 100 / 10]);
putOneCharToLCD1602(line, ++position, TabNumASCII[uiNumber % 100 % 10]);
}
// 按键检测子程序,有键按下返回键端口数据,无键返回0
uchar GetKey(void)
{
uchar KeyTemp = (IO_KEY | DATA_KEY_ORL); //获取按键端口数据
if( KeyTemp != DATA_KEY_Null ) // 如果不为空
{
uchar CountTemp = 0;
do
{
delay125us();
if(KeyTemp != (IO_KEY | DATA_KEY_ORL)) return 0; //在延时期间检测键,如果不稳定保持则退出
} while(++CountTemp > Data_Key20msCountMax); // 延时20ms去抖动
while((IO_KEY | DATA_KEY_ORL) != DATA_KEY_Null); //等键释放
return KeyTemp; // 有键按下返回键端口数据
}
return 0; // 无有效键返回0
}
//加一
uchar INC_Number(uchar Number, uchar Min, uchar Max)
{
if(Number >= Max) return Min; else return (++ Number);
}
//减一
uchar DEC_Number(uchar Number, uchar Min, uchar Max)
{
if(Number <= Min) return Max; else return (-- Number);
}
// 检测到有按键后 这里执行按键任务
void execute_key_task(uchar ucKeyValue)
{
uchar state = 0; //定义调整数据的状态变量
uchar keyValue = 0; //定义键值得临时变量
if(ucKeyValue != DATA_KEY_Set) return; //不是设置键退出
//是设置键继续-----------------------------------------------------
putLineCharsToLCD1602(lineTow, 8, 8, "C:000cm "); //清零显示当前距离CURRENT
putThreeCharToLCD1602(lineOne, 8 + 2, uiD); //光标调整到调整总距离(检测探头到水库底部的距离“D:000cm”)
while(1)
{
keyValue = GetKey();
if(keyValue == 0) continue;
switch(keyValue)
{
case DATA_KEY_Set:
{
// 如果按的是设置键,顺序设置总距离D——高水位H——低水位L——退出
switch(state)
{
case 0: // 如果是设置总距离状态,改变为设置高水位状态,并显示高水位,实现移动光标到高水位后面
{
state = 1;
putThreeCharToLCD1602(lineOne, 0 + 2, uiH);
}
break;
case 1:
{
uchar tempMax = uiD - DATA_uiD_Min;
if(tempMax < 2 + 2) tempMax = 2 + 2;
if(uiH > tempMax)
{
uiH = tempMax;
putThreeCharToLCD1602(lineOne, 0 + 2, uiH);
}
else if(uiH < 2 + 2)
{
uiH = 2 + 2;
putThreeCharToLCD1602(lineOne, 0 + 2, uiH);
}
state = 2;
putThreeCharToLCD1602(lineTow, 0 + 2, uiL);
}
break;
case 2:
{
if(uiL > uiH - 2)
{
uiL = uiH - 2;
putThreeCharToLCD1602(lineTow, 0 + 2, uiL);
}
return;
}
break;
}
}
break;
// 如果按的是增加键,改变相应数据并显示
case DATA_KEY_INC:
{
switch(state)
{
case 0:
{
uiD = INC_Number(uiD, DATA_uiD_Min, DATA_uiD_Max);
putThreeCharToLCD1602(lineOne, 8 + 2, uiD);
}
break;
case 1:
{
uchar tempMax = uiD - DATA_uiD_Min;
if(tempMax < 2 + 2) tempMax = 2 + 2;
uiH = INC_Number(uiH, 2, tempMax);
putThreeCharToLCD1602(lineOne, 0 + 2, uiH);
}
break;
case 2:
{
uiL = INC_Number(uiL, 0, uiH - 2);
putThreeCharToLCD1602(lineTow, 0 + 2, uiL);
}
break;
}
}
break;
// 如果按的是减少键,改变相应数据并显示
case DATA_KEY_DEC:
{
switch(state)
{
case 0:
{
uiD = DEC_Number(uiD, DATA_uiD_Min, DATA_uiD_Max);
putThreeCharToLCD1602(lineOne, 8 + 2, uiD);
}
break;
case 1:
{
uchar tempMax = uiD - DATA_uiD_Min;
if(tempMax < 2 + 2) tempMax = 2 + 2;
uiH = DEC_Number(uiH, 2, tempMax);
putThreeCharToLCD1602(lineOne, 0 + 2, uiH);
}
break;
case 2:
{
uiL = DEC_Number(uiL, 0, uiH - 2);
putThreeCharToLCD1602(lineTow, 0 + 2, uiL);
}
break;
}
}
break;
}
}
}
// 蜂鸣器
void buzzerCall(void)
{
uchar i;
for(i = 0; i < 90; i++)
{
io_Buzzer = 0;
delay100us();
io_Buzzer = 1;
delay100us();
delay100us();
}
delay100us();
delay100us();
}
//计算水位
bool CalculatedWaterLevel(void)
{
uchar i = 8 + 2; //当前水位的数字在LCD屏显示的起点位置
uint uiTime; //声波传播时间
ulong ulDis; //实时测量到距离
uiTime = TH0 << 8 | TL0;
ulDis = (uiTime * 3.40) / 200; //计算当前测量的距离,单位cm
TH0 = 0;
TL0 = 0;
if((ulDis > uiD) || (g_flag == isYes )) // ulDis > uiD 超出测量范围;g_flag == isYes超时;
{
g_flag = isNo;
TR0 = 0;
putLineCharsToLCD1602(lineTow, i, 3, "Err"); // 显示Err
//阀门动作:
// if(g_flagSwitch == isYes)
// {
// io_Control_Inlet = isio_Control_Inlet_OFF;
// io_Control_Outlet = isio_Control_Outlet_ON;
// g_flagSwitch = isNo;
// }
//指示灯:
ioLed_Red = ! ioLed_Red; // 三个灯同时快速闪亮
ioLed_Green = ! ioLed_Green;
ioLed_Yellow = ! ioLed_Yellow;
// 蜂鸣器叫:
if(buzzerCallFlag == isCall)
{
buzzerCall(); // 蜂鸣器叫
}
return isNo; // 返回错误信息
}
else
{
ulDis = uiD - ulDis; // 当前水位C = 总距离 - 当前检测到的距离
if(ulDis > uiH) // 如果水位超高
{
//阀门动作:
io_Control_Inlet = isio_Control_Inlet_OFF;
io_Control_Outlet = isio_Control_Outlet_ON;
g_flagSwitch = isNo;
//指示灯:
ioLed_Red = ! ioLed_Red; // 红灯闪
ioLed_Green = isLedOFF;
ioLed_Yellow = isLedOFF;
// 蜂鸣器叫:
if(ulDis - uiH > (uiD - uiH) / DATA_alarmCoefficient) //当“当前水位”超出最高水位“ ((“总高度减高水位)除以2的值”)时报警
{
buzzerCall(); // 蜂鸣器叫
}
}
else if(ulDis < uiL) // 如果水位超低
{
//阀门动作:
if(g_flagSwitch == isYes)
{
io_Control_Outlet = isio_Control_Outlet_OFF;
io_Control_Inlet = isio_Control_Inlet_ON;
g_flagSwitch = isNo;
}
//指示灯:
ioLed_Red = isLedOFF;
ioLed_Green = isLedOFF;
ioLed_Yellow = ! ioLed_Yellow; //黄灯闪
// 蜂鸣器叫:
if( uiL - ulDis > uiL / DATA_alarmCoefficient)//uiL / 2 当“当前水位”低于“低水位” “低水位除以2的值”时报警
{
buzzerCall(); // 蜂鸣器叫
}
}
else // 水位在正常范围
{
ioLed_Red = isLedOFF;
ioLed_Green = ! ioLed_Green;
ioLed_Yellow = isLedOFF;
}
putThreeCharToLCD1602(lineTow, i, ulDis);
return isYes;
}
return isYes;
}
void main(void)
{
initIO(); //初始化IO端口
delay500ms(); //启动延时,给器件进入正常工作状态留够时间
initLCD1602(); //LCD初始化
putLineCharsToLCD1602(lineOne, 8, 8, "D:000cm "); //显示distance (总)距离(检测探头到水库底部的距离)D
putThreeCharToLCD1602(lineOne, 8 + 2, uiD); //显示三位数值
putLineCharsToLCD1602(lineOne, 0, 8, "H:000cm "); //显示设定的最高报警水位H
putThreeCharToLCD1602(lineOne, 0 + 2, uiH); //显示三位数值
putLineCharsToLCD1602(lineTow, 0, 8, "L:000cm "); //显示设定的最低报警水位L
putThreeCharToLCD1602(lineTow, 0 + 2, uiL); //显示三位数值
putLineCharsToLCD1602(lineTow, 8, 8, "C:000cm "); //显示当前CURRENT水位C
initTimer0(); //初始化定时器0
initTimer1();
//阀门动作:初始先排水
io_Control_Inlet = isio_Control_Inlet_OFF;
io_Control_Outlet = isio_Control_Outlet_ON;
g_flagSwitch = isNo;
while(1)
{
io_US_TX = 1; //启动超声波模块信号
delay10us();
io_US_TX = 0;
while(io_US_RX == 0); //等待计时开始
TR0 = 1; //开启定时器0,计时开始
IT1 = 1; //设置外中断INT1输入信号模式(1:Falling only仅下降沿有效 0:Low level低电平有效)
EX1 = 1; //使能外中断INT1
while(EX1 == 1 && g_flag == isNo)//等待中断或超时退出
{
uchar ucKeyValue = GetKey(); //在等待中检测按键
if(ucKeyValue) execute_key_task(ucKeyValue); //如果有键按下则执行按键任务
}
if(CalculatedWaterLevel() == isNo) continue; //计算水位,如果超出范围返回isNo并重新循环
TR0 = 0; //暂时关闭定时器0
//清零定时器和计数变量以及标志
TL0 = 0;
TH0 = 0;
g_flag = isNo;
ucCount = 0;
g_flag05s = isNo;
TR0 = 1; //打开定时器0
鉴于篇幅限制,只能写部分代码
最后,如果有什么意见或者建议欢迎您留言给我,让我们共同学习一起进步,
如果需要 完整代码或设计文件,请在下方留言或者私信我,看到后会第一时间回复。
谢谢!
感谢你的阅读,希望您有所收获,喜欢请点赞评论加关注!
相关问答
51单片机 可以带到高铁上去吗?可以带。乘坐高铁严禁携带易燃、易爆、化学危险物品和管制刀具等物品。单片机不属于上述物品范围,可以带上高铁,但需要妥善放置。51单片机完全没问题电子元器...
单片机 的毕业论文怎么写?一、毕业设计题目及要求(2个)1、基于单片机控制的电动机Y-△启动的设计要求:1)控制器为单片机,电动机为三相异步电动机;2)启动时间为3秒;3)由按键设...51...
想学习电子电路,请问各位大神们哪里有模拟软件学习呢?我这里使用过很多电子模拟软件。至于选择哪一个,这个要看你对这个软件的期望了。说一下我使用过的电子模拟软件:Proteus、multisim、Tina和iCircuit。接下...
pcb 和 单片机 哪个有前途?二者应该说没有轻重,如果有可能最好都取,而且就你目前仅有的一点的数码管点阵键盘都不能叫经验,只能说是基础知识,而且单片机是一门应用学,动手能力要比较强...
怎么样学好 51单片机 ?单片机的学习绝不仅仅是对一项知识的掌握。想要学好单片机,需要从硬件结构、内部资源、外设应用等几个方面多方位入手。而要想成为一名嵌入式工程师,就要对单片...
PCB 中怎样找 单片机 ?有些PCB上有标记的一般芯片用U?表示,单片机也是的再看单片机的封装,如果知道单片机型号,就可以查封装再找有些PCB上有标记的一般芯片用U?表示,单片机也是的再...
proteus主要有4大结构体系即是什么?具有4大功能模块(1)智能原理图设计(ISIS)丰富的器件库:超过27000种元器件,可方便地创建新元件;智能的器件搜索:通过模糊搜索可以快速定位所需要的器件;智...
什么是嵌入式系统?嵌入式系统是指由硬件和软件组成并且能够移植到硬件设备的操作系统。而相比较于普通的PC操作系统,嵌入式操作系统不能存储大容量的内存和程序。所以嵌入式操作系...
想学习怎么自己制作电路板100分 - 小红薯85942C59 的回答 - 懂得谬论一:高中老师常对我们说,大家现在好好学,考上了大学就轻松了,爱怎么玩怎么玩。这真是狗屁。别的专业我不好说,电气、电子、电力、通信、自动化等...
单片机 密码锁需要的零件?元件清单:1、PCB板1片2、AT89C51单片机+IC座1套3、1602液晶显示屏1个4、按键17只5、12M晶振1只6、22P、104瓷片电容2只、1只7、470uF电容...