下面的实验是让学生初步掌握如何使用单片机,并配合基本电路元件实现一些有趣的功能。
单片机控制模块选用的是Arduino UNO R3,其集成开发环境Arduino IDE将很多复杂的底层操作都封装成函数,供初学者调用,大大降低了学习门槛,简化了编程工作量,非常适合零基础入门学习。
Arduino UNO R3开发板
注意,在Arduino环境下开发时,一定要善用串口监视器,这是一个好东西。
相关资源链接:
火花空间 http://www.oursparkspace.cn/
Arduino官网 https://www.arduino.cc/
开源软件托管平台 https://github.com/
另外,大家必须习惯于自己解决问题,习惯于利用搜索引擎去寻找问题的答案。
展开全文
很多事情就像是旅行一样,当你决定要出发的时候,最困难的部分其实就已经完成了。
基础实验
1. 自动控制路灯
要求:白天光照较强时,路灯关闭;晚上光照较弱,路灯开启。
设计思路:通过光敏元件感知光强,并通过mCookie控制路灯。
自动控制路灯原理图和接线图
注:按照左边的原理图来连接电路!不要按实物图。
路灯用发光二极管代替,发光二极管必须串联限流电阻。
参考代码:
int threshold =400; //光强值,根据实际情况调整
void setup ( )
{
Serial.begin(115200);
pinMode(10, OUTPUT); //设置输出端口
}
void loop( )
{
int n = analogRead(A3); //读取模拟口A3
Serial.println(n);
if (n>threshold ) //晚上光线暗,n值变大
digitalWrite(10, HIGH); //点亮路灯
else
digitalWrite(10, LOW); //关闭路灯
delay(200);
}
注意:程序中的端口需根据实际电路连接修改,一定要跟电路连接的端口对应上。
思考题:
(1) 如何改成光强警示电路,光照较强时发光二极管点亮?
(2) 如何让发光二极管变成呼吸灯? (提示:采用PWM方式控制LED)
(3) 如何设计自动调光台灯? (提示:引入光敏电阻)
(4) 如何设计炫彩台灯?即控制彩色发光二极管,根据不同光强显示不同颜色的光。
2.数字温度计
要求:利用温度传感器检测环境温度,并通过IDE的串口监视器观察环境温度变化,环境温度越高发光二极管越亮,以适当方式呈现给用户。
数字温度计原理图和接线图
注:(1) 温度传感器采用LM35D,外形类似三极管,输出为模拟电压值,采集时每升高10mv电压代表温度上升1℃,两者成线性关系。
(2) 该电路用的是内部基准源进行ADC采样,如果要实现精准测温,模数转换的基准电压必须稳定,mCookie的REF引脚应该外接精准电压源。
(3) 实物图上未接发光二极管和蜂鸣器。
参考代码:
unsigned int tempMin = 29; //亮灯温度
unsigned int tempMax = 33; //报警温度
void setup( ) {
Serial.begin(115200); //串口初始化
analogReference(INTERNAL); //调用板载1.1V基准源
pinMode(11, OUTPUT);
digitalWrite(11, LOW);
}
void loop( ) {
double analogVotage = 1.1*(double)analogRead(A3)/1023;
double temp = 100*analogVotage; //计算温度
unsigned int dutyCycle;//占空比
if (temp <= tempMin) { //小于亮灯门限值
dutyCycle = 0; digitalWrite(11, LOW);
}
else if (temp < tempMax) { //小于报警门限
dutyCycle = (temp-tempMin)*255/(tempMax-tempMin);
digitalWrite(11, LOW);
}
else{ //发光二极管亮度最大值,并启动声音报警
dutyCycle = 255; digitalWrite(11, HIGH);
}
analogWrite(10, dutyCycle);//控制发光二极管发光
Serial.print("Temp: "); Serial.print(temp);
Serial.print(" Degrees Duty cycle: ");
Serial.println(dutyCycle);
delay(100);// 控制刷新速度
}
思考题:
(1) 如果通过彩色发光二极管显示环境温度变化?
(2) 如何利用七段数码管显示环境温度?
(3) 如何利用OLED屏显示环境温度?
3.智能温控风扇
要求:根据环境温度自动调节风扇转速。
智能温控风扇原理图和接线图
注:马达一般需要电流较大,单片机IO口驱动能力有限,所以要经过三极管进行电流放大,由于采用PWM方式,三极管工作在开关状态。
参考代码:
double analogVotage; //模拟电压值
double temp; //温度
unsigned int dutyCycle; //占空比
unsigned int tempMin = 25; //零速温度,设为串口观察到的环境温度
unsigned int tempMax = 35; //满速温度,设为串口观察到的手握元件温度
void setup() {
Serial.begin(115200); //设置串口波特率
analogReference(INTERNAL); //调用板载1.1V基准源
}
void loop() {
analogVotage = 1.1*(float)analogRead(A3)/1024; //把ADC读取值换算成电压
temp = 100*analogVotage; //电压换算成温度
if (temp < tempMin){
dutyCycle = 0;
}
else if (temp < tempMax){
dutyCycle = (temp-tempMin)*255/(tempMax-tempMin);
}
else {
dutyCycle = 255;
}
analogWrite(10,dutyCycle); //产生PWM
Serial.print("Temp: "); Serial.print(temp);
Serial.print(" Degrees Duty cycle: ");
Serial.println(dutyCycle);
delay(100); //等待0.1秒,控制刷新速度
}
思考题:
(1) 为什么没有用单片机IO口直接驱动马达?
(2) 如何让风扇感应到有人时才启动,并自动根据环境温度调节转速?
(3) 增加OLED屏,实时显示环境温度和风扇转速。
补充:热释电红外传感器前面装有菲涅尔透镜,可以很灵敏地检测到人体活动,这种传感器淘宝有很多,大家可根据需要购买。
4.同向比例运算电路
要求:利用集成运放,实现输入信号的线性放大。
同向比例运算原理图和接线图
注:输入可以采用电阻或电位器分压,淘宝上碳膜电位器很便宜,一元钱能买几个。如果输入信号过大,输出进入饱和。输入和输出的电压值用mCookie采集,通过IDE的串口观察器查看。
参考代码:
void setup( ) {
Serial.begin(115200);
}
void loop( ) {
float vi=analogRead(A2)/1023.0*5;
float vo=analogRead(A3)/1023.0*5;
Serial.print("Input voltage is ");
Serial.print(vi); Serial.println("V");
Serial.print("Output voltage is ");
Serial.print(vo); Serial.println("V");
delay(1000);
}
//在此程序中,用Arduino开发板的A2口采集输入模拟电压值,用A3口采集输出模拟电压值。
思考题:
(1) 如何更改放大倍数?放大倍数是否可以很大?
(2) 理论上,如何实现反向比例放大?
5.触摸电子琴
要求:通过手指触摸不同按键,弹奏不同的乐曲。
设计思路:利用Arduino的readCapacitivePin()函数,读取端口电容值;利用tone(pin, frequency)函数,产生不同的音调。
先做一个指尖开关实验
指尖开关也称为触摸开关。 本实验是利用人体电阻做开关,控制晶体三极管的导通,并以此控制发光二极管。
指尖开关原理图
用万用表测量,人体两只手的手指之间的电阻约在200千欧到2兆欧左右,与皮肤干燥程度有关。若手指未触摸开关,三极管的基极悬空,发射结be截止,三极管截止,A3点的电压由图中10k电阻拉低至0V。
用两手手指触摸开关时,等于将5V电压通过约1M欧的电阻加到三极管的基极,使三极管的发射结be因正偏而导通,三极管开启,虽然此时基极电流很小,但由于三极管的电流放大作用,流出发射极e的电流相对较大,在10k的发射极电阻上产生电压差,A3点为高电平。当Arduino UNO检测到A3点的电平为高时,将D13脚置为高电平,发光二极管被点亮。
参考代码
void setup()
{
Serial.begin(115200);
pinMode(10,OUTPUT);
}
void loop()
{
int n=analogRead(A3); //读取模拟口数据
if(n>50) //有电压反应就运行以下程序
digitalWrite(10,HIGH); //点亮led
else
digitalWrite(10,LOW); //熄灭led
Serial.println(n);//串口监视
delay(100);//延时,控制刷新速率。
}
如何测量电容值
Arduino除了接收数字端口的数字信号,唯一能检测的模拟物理量就是电压。任何模拟传感器的检测值几乎都要通过相关电路转化成电压值,再输入Arduino的模拟端口进行模数转换。电容值就需要更复杂的电路转化为电压值,才能被Arduino检测。这里介绍一个除了一段导线和一个端口,不需要任何元器件的电容检测方法。
这个方法的思路是,首先把一个数字端口设成低电位,并打开Arduino的内部上拉电阻,开始计算这个端口到达高电位所需要的时间。而这个时间与此端口的对地电容值有关,电容越大,时间越长。在硬件上只需要在一个端口上连一根导线即可。用手指触摸这段导线的裸露端,就会导致电容变化,Arduino可以通过上述方法检测这个变化。如果要增加灵敏度,可以在导线上连 一片锡箔。
注意:为防止静电造成Arduino核心模块的损坏,建议先触摸暖气等释放人体静电,或者在导线上盖一层绝缘板。
触摸开关
我们先通过触摸开关测试一下利用电容值检测端口是否被触摸,如果检测到手指碰触端口引线,则点亮LED灯。 实验的时候,触摸按键由D8口引出,用D11引脚控制LED灯。
参考代码
int ledPin = 11; //用D11引脚控制LED灯
int capval;
void setup()
{
pinMode(ledPin, OUTPUT);
Serial.begin(115200);
Serial.println("Touch senser");
}
void loop ()
{
digitalWrite(ledPin,LOW);
capval = readCapacitivePin(8); //通过D8口读取电容值
Serial.println(capval, DEC);
if (capval > 2) {
// turn LED on:
digitalWrite(ledPin, HIGH);
delay(10);
}
}
uint8_t readCapacitivePin(int pinToMeasure) {
// Variables used to translate from Arduino to AVR pin naming
volatile uint8_t* port;
volatile uint8_t* ddr;
volatile uint8_t* pin;
// Here we translate the input pin number from
// Arduino pin number to the AVR PORT, PIN, DDR,
// and which bit of those registers we care about.
byte bitmask;
port = portOutputRegister(digitalPinToPort(pinToMeasure));
ddr = portModeRegister(digitalPinToPort(pinToMeasure));
bitmask = digitalPinToBitMask(pinToMeasure);
pin = portInputRegister(digitalPinToPort(pinToMeasure));
// Discharge the pin first by setting it low and output
*port &= ~(bitmask);
*ddr |= bitmask;
delay(1);
// Make the pin an input with the internal pull-up on
*ddr &= ~(bitmask);
*port |= bitmask;
// Now see how long the pin to get pulled up. This manual unrolling of the loop
// decreases the number of hardware cycles between each read of the pin,
// thus increasing sensitivity.
uint8_t cycles = 17;
if (*pin & bitmask) { cycles = 0;}
else if (*pin & bitmask) { cycles = 1;}
else if (*pin & bitmask) { cycles = 2;}
else if (*pin & bitmask) { cycles = 3;}
else if (*pin & bitmask) { cycles = 4;}
else if (*pin & bitmask) { cycles = 5;}
else if (*pin & bitmask) { cycles = 6;}
else if (*pin & bitmask) { cycles = 7;}
else if (*pin & bitmask) { cycles = 8;}
else if (*pin & bitmask) { cycles = 9;}
else if (*pin & bitmask) { cycles = 10;}
else if (*pin & bitmask) { cycles = 11;}
else if (*pin & bitmask) { cycles = 12;}
else if (*pin & bitmask) { cycles = 13;}
else if (*pin & bitmask) { cycles = 14;}
else if (*pin & bitmask) { cycles = 15;}
else if (*pin & bitmask) { cycles = 16;}
// Discharge the pin again by setting it low and output
// It's important to leave the pins low if you want to
// be able to touch more than 1 sensor at a time - if
// the sensor is left pulled high, when you touch
// two sensors, your body will transfer the charge between
// sensors.
*port &= ~(bitmask);
*ddr |= bitmask;
return cycles;
}
触摸电子琴
将Arduino UNO的A3口接无源蜂鸣器的正极,蜂鸣器的负极接地;然后将D2-D9口引出来作为触摸按键,烧入下面的程序,即可实现触摸电子琴的功能。
参考代码:
int ledPin = A3;//此处接无源蜂鸣器正极
int capval1,capval2,capval3,capval4,capval5,capval6,capval7,capval8;
void setup()
{
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
Serial.println("Touch senser");
}
void loop ()
{
digitalWrite(ledPin,LOW);
capval1 = readCapacitivePin(2); //D2管脚
capval2 = readCapacitivePin(3);
capval3 = readCapacitivePin(4);
capval4 = readCapacitivePin(5);
capval5 = readCapacitivePin(6);
capval6 = readCapacitivePin(7);
capval7 = readCapacitivePin(8);
capval8 = readCapacitivePin(9); //D9管脚
if (capval1 > 2) tone(ledPin, 262, 10);
if (capval2 > 2) tone(ledPin, 294, 10);
if (capval3 > 2) tone(ledPin, 330, 10);
if (capval4 > 2) tone(ledPin, 350, 10);
if (capval5 > 2) tone(ledPin, 393, 10);
if (capval6 > 2) tone(ledPin, 441, 10);
if (capval7 > 2) tone(ledPin, 495, 10);
if (capval8 > 2) tone(ledPin, 525, 10);
}
uint8_t readCapacitivePin(int pinToMeasure) {
// Variables used to translate from Arduino to AVR pin naming
volatile uint8_t* port;
volatile uint8_t* ddr;
volatile uint8_t* pin;
// Here we translate the input pin number from
// Arduino pin number to the AVR PORT, PIN, DDR,
// and which bit of those registers we care about.
byte bitmask;
port = portOutputRegister(digitalPinToPort(pinToMeasure));
ddr = portModeRegister(digitalPinToPort(pinToMeasure));
bitmask = digitalPinToBitMask(pinToMeasure);
pin = portInputRegister(digitalPinToPort(pinToMeasure));
// Discharge the pin first by setting it low and output
*port &= ~(bitmask);
*ddr |= bitmask;
delay(1);
// Make the pin an input with the internal pull-up on
*ddr &= ~(bitmask);
*port |= bitmask;
// Now see how long the pin to get pulled up. This manual unrolling of the loop
// decreases the number of hardware cycles between each read of the pin,
// thus increasing sensitivity.
uint8_t cycles = 17;
if (*pin & bitmask)
cycles = 0;
else if (*pin & bitmask)
cycles = 1;
else if (*pin & bitmask)
cycles = 2;
else if (*pin & bitmask)
cycles = 3;
else if (*pin & bitmask)
cycles = 4;
else if (*pin & bitmask)
cycles = 5;
else if (*pin & bitmask)
cycles = 6;
else if (*pin & bitmask)
cycles = 7;
else if (*pin & bitmask)
cycles = 8;
else if (*pin & bitmask)
cycles = 9;
else if (*pin & bitmask)
cycles = 10;
else if (*pin & bitmask)
cycles = 11;
else if (*pin & bitmask)
cycles = 12;
else if (*pin & bitmask)
cycles = 13;
else if (*pin & bitmask)
cycles = 14;
else if (*pin & bitmask)
cycles = 15;
else if (*pin & bitmask)
cycles = 16;
// Discharge the pin again by setting it low and output
// It's important to leave the pins low if you want to
// be able to touch more than 1 sensor at a time - if
// the sensor is left pulled high, when you touch
// two sensors, your body will transfer the charge between
// sensors.
*port &= ~(bitmask);
*ddr |= bitmask;
return cycles;
}
呼吸灯
利用脉冲宽度调制PWM可以控制发光二极管逐渐亮暗,实现呼吸等效果。电路连接很简单,将程序下载到板子上后,可以看到LED灯由暗慢慢变亮,再由亮渐暗到灭,依次循环,像是在呼吸一样。
参考代码
int brightness = 0; //表示LED的亮度
int fadeAmount = 5; //LED亮度变化增量
void setup()
{
pinMode(11, OUTPUT); // 设置11号口为输出端口
}
void loop()
{
analogWrite(11, brightness); //把brightness值写入端口
brightness = brightness + fadeAmount; //使亮度在下一次循环发生改变
if (brightness <= 0 || brightness >= 255)
fadeAmount = -fadeAmount ; //在亮度最高或最低时进行翻转
delay(30); //延时30毫秒
光控电子琴
利用光敏电阻检测光照强度,并以此控制无源蜂鸣器发生不同的音调,便成了光控电子琴。
电路原理图
实物图
为方便起见,用Arduino UNO R3代替mCookie。
参考程序
//定义音阶常量
#define Do 262
#define Re 294
#define Mi 330
#define Fa 349
#define Sol 392
#define La 440
#define Si 494
int val=0;
int buzzerPin=7; //定义蜂鸣器针脚
void setup(){
Serial.begin(115200);
pinMode(buzzerPin,OUTPUT);
}
void loop()
{
int n = analogRead(A0); //读取模拟口A0,获取光强
Serial.println(n); // 用于IDE串口观察窗
val=0;
if(n<=200)
val=Do;
else if(n<=220)
val=Re;
else if(n<=240)
val=Mi;
else if (n<=300)
val=Fa;
else if (n <=350)
val=Sol;
else if (n<=400)
val=La;
else if (n<=500)
val=Si;
if (val)
{
tone(buzzerPin,val,1000);
delay(1000);
}
else
delay(100); //防止写串口速度过快
}
进阶实验
焦耳小偷电路
LM386音频放大电路
基于LM386的音频放大电路
基于LM386的音频放大电路
炫彩闪光棒
激光PM2.5检测仪
手把手教你做PM2.5检测仪
手把手教你做PM2.5检测仪
口袋实验仪器AD2
Arduino UNO板的使用