最近在搞单片机,整理一下。
目录
更新日志:5/10 在尝试这些模块的时候我以为我已经了解了单片机,但实际自己操作的时候才发现根本不是这回事。我想外接一个DHT11测温湿度,但是VCC和GND模块根本就不知道要插哪。后来链接上后我又不知道到底测没测数据,于是又接了一个LCD显示屏模块,运行时显示屏虽然亮了但是根本不显示数据,我现在不知道到底是我连线有误还是程序有错,还是测温模块根本就没有工作。
只能说我这些程序只能帮助大家初步了解这些模块都能干些什么,实际上连单片机的皮毛都没摸到。
前言
最近在学习单片机,刚入门也不是很会,就将一些简单的例子整理下来以供参考。如果哪些地方理解的有问题,欢迎大家积极留言,互相交流学习。
一、51单片机简介
我使用的这个单片机其内核是 STC 公司生产的经典 51 内核芯片 STC89C52,这是一 款拥有 64KB FLASH 超大存储器的 51 单片机,可容纳更大更复杂的程序。
80C51芯片管脚图
其内部资源:
- 8位CPU
- 4kbytes 程序存储器(ROM)(52为8K)
- 128bytes 的数据存储器(RAM) (52有256bytes 的RAM)
- 32条 I/O口线
- 111条指令,大部分为单字节指令
- 21个专用寄存器
- 2个可编程定时/计数器
- 5个中断源,2个优先级(52有6个)
- 一个全双工串行通信口
- 外部数据存储器寻址空间为64kB
- 外部程序存储器寻址空间为64kB
- 逻辑操作位寻址功能
- 双列直插40PinDIP封装
- 单一+5V电源供电
二、点亮第一个LED灯
1.LED模块
8个LED链接单片机P2 IO口上
2.软件设计
#include <reg52.h>
sbit led = P2^0;
void main()
{
led = 0;
while(1)
{
}
}
通过将D1端口设置为低电平使其常亮
而main函数中设置一个while循环,在其中执行我们想实现的功能(这个程序中无用)
3.设计更新
#include <reg52.h>
sbit led1 = P2^0;
sbit led2 = P2^2;
sbit led3 = P2^4;
sbit led4 = P2^6;
void main()
{
led1 = 0;
led2 = 0;
led3 = 0;
led4 = 0;
while(1)
{
}
}
使D1,D3,D5,D7四个灯常亮
三、LED流水灯
1.流水灯原理
要实现LED的闪烁才能达到流水灯效果。需要设置延时使指示灯先亮一会后熄灭。
2.软件设计
#include <reg52.h>
#include <intrins.h>
typedef unsigned int uint;
typedef unsigned char uchar;
#define led P2
void delay(uint i)
{
while(i--);
}
void main()
{
uchar j;
led = ~0x01;
delay(50000);
while(1)
{
for(j=0;j<8;j++)
{
P2 = ~(0x01<<j);
delay(50000);
}
}
}
因为首先将P2口定义为led
而初始赋值是:
led = ~0x01;
就是0xFE
对应二进制 1111 1110,相当于D1=0(赋值低电平,所以会被点亮)
P2 = ~(0x01<<j);
0000 0001 左移一位就是 0000 0010,然后取反就是 1111 1101,这样D2=0(第二个灯就会被点亮)
以此类推达到流水灯的效果,而每次循环都要延时一段时间,这样我们肉眼才能分辨出来。
3.软件改进
上面这种形式虽然简单但是很难理解
因此还提供了移位库函数,_crol_() 左移 ,_cror_() 右移
从D1到D8流水灯
#include <reg52.h>
#include <intrins.h>
typedef unsigned int uint;
typedef unsigned char uchar;
#define led P2
void delay(uint i)
{
while(i--);
}
void main()
{
uchar j;
led = ~0x01;
delay(50000);
while(1)
{
for(j=0;j<8;j++)
{
led = _crol_(led,1);
delay(50000);
}
}
}
从D8到D1流水灯
#include <reg52.h>
#include <intrins.h>
typedef unsigned int uint;
typedef unsigned char uchar;
#define led P2
void delay(uint i)
{
while(i--);
}
void main()
{
uchar j;
led = 0x7F;
delay(50000);
while(1)
{
for(j=0;j<8;j++)
{
led = _cror_(led,1);
delay(50000);
}
}
}
4.灯光设计
根据前面所学的种种知识,可以设计出一个灯光秀了。
#include <reg52.h>
#include <intrins.h>
typedef unsigned int uint;
typedef unsigned char uchar;
#define led P2
sbit led1 = P2^0;
sbit led2 = P2^1;
sbit led3 = P2^2;
sbit led4 = P2^3;
sbit led5 = P2^4;
sbit led6 = P2^5;
sbit led7 = P2^6;
sbit led8 = P2^7;
void delay(uint i)
{
while(i--);
}
void main()
{
uchar j;
while(1)
{
led1 = 0;
delay(20000);
led3 = 0;
delay(40000);
led5 =0;
delay(60000);
led7 =0;
delay(80000);
led2 = 0;
delay(20000);
led4 = 0;
delay(40000);
led6 = 0;
delay(60000);
led8 = 0;
delay(80000);
led = 0xFF;
delay(50000);
led = 0xFE;
delay(50000);
for(j=0;j<7;j++)
{
led = _crol_(led,1);
delay(50000);
}
for(j=0;j<7;j++)
{
led =_cror_(led,1);
delay(50000);
}
led = 0x00;
delay(50000);
led = 0xFF;
delay(50000);
}
}
四、蜂鸣器
1.蜂鸣器模块
我用的这款单片机,蜂鸣器控制管脚接到P1^5 上
2.软件设计
使蜂鸣器发声
#include <reg52.h>
#include <intrins.h>
typedef unsigned int uint;
typedef unsigned char uchar;
sbit beep = P1^5;
void delay(uint i)
{
while(i--);
}
void main()
{
while(1)
{
beep = ~beep;
delay(100);
}
}
beep = ~beep;
delay(500);
在循环内对beep进行取反,然后延时一定时间,就是P1^5间隔一定时间输出高低电平,以此产生脉冲信号。更改延时时间能达到不同的发生效果。
3.演奏乐曲
因为不太清楚音调和节拍应该怎么设置,没法实践,这个例子是我网上找的
大家如果感兴趣可以自己试试,也可以教教我。
《生日快乐》歌
#include <reg51.h>
typedef unsigned int uint;
typedef unsigned char uchar;
sbit beep = P1^5;
uchar code SONG_TONE[]={212,212,190,212,159,169,212,212,190,212,142,159,212,212,106,126,159,169,190,119,119,126,159,142,159,0};
uchar code SONG_LONG[]={9,3,12,12,12,24,9,3,12,12,12,24,9,3,12,12,12,12,12,9,3,12,12,12,24,0};
void DelayMS(uint x)
{
uchar t;
while(x--)
{
for(t=0;t<120;t++);
}
}
void PlayMusic()
{
uint i=0,j,k;
while(SONG_LONG[i]!=0||SONG_TONE[i]!=0)
{
for(j=0;j<SONG_LONG[i]*20;j++)
{
beep=~beep;
for(k=0;k<SONG_TONE[i]/3;k++);
}
DelayMS(10);
i++;
}
}
void main()
{
beep=0;
while(1)
{
PlayMusic();
DelayMS(500);
}
}
五、静态数码管
1.如何控制数码管显示字符
对于每一个晶体管,当给其赋值1就是点亮,然后再以16进制输出就好。
2.软件设计
使一个数码管输出0
#include <reg52.h>
#include <intrins.h>
typedef unsigned char uchar;
typedef unsigned int uint;
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
//0-F
uchar code smgduan[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
void main()
{
LSA = 0;
LSB = 0;
LSC = 0;
P0 = smgduan[0];
while(1)
{
}
}
六、动态数码管
1.动态数码管模块
动态数码管就是在静态的基础上,输出多个字符
2.软件设计
因为我们采用了138译码器实现
因此根据下图真值表可以控制如何点亮数码管(低电平时点亮)
数码管输出0-7
#include <reg52.h>
#include <intrins.h>
typedef unsigned char uchar;
typedef unsigned int uint;
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
uchar code smgduan[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40};
void delay(uint i)
{
while(i--);
}
void Display()
{
uchar j;
for(j=0;j<8;j++)
{
switch(j)
{
case(0):
LSA = 0;LSB = 0;LSC = 0;break;
case(1):
LSA = 1;LSB = 0;LSC = 0;break;
case(2):
LSA = 0;LSB = 1;LSC = 0;break;
case(3):
LSA = 1;LSB = 1;LSC = 0;break;
case(4):
LSA = 0;LSB = 0;LSC = 1;break;
case(5):
LSA = 1;LSB = 0;LSC = 1;break;
case(6):
LSA = 0;LSB = 1;LSC = 1;break;
case(7):
LSA = 1;LSB = 1;LSC = 1;break;
}
P0=smgduan[j];
delay(100);
P0=0x00;
}
}
void main()
{
while(1)
{
Display();
}
}
3.使用数码管告白
#include <reg52.h>
#include <intrins.h>
typedef unsigned char uchar;
typedef unsigned int uint;
sbit LSA = P2^2;
sbit LSB = P2^3;
sbit LSC = P2^4;
uchar code smgduan[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x40};
void delay(uint i)
{
while(i--);
}
void Display()
{
uchar j;
for(j=0;j<8;j++)
{
switch(j)
{
case(0):
LSA = 0;LSB = 0;LSC = 0;break;
case(1):
LSA = 1;LSB = 0;LSC = 0;break;
case(2):
LSA = 0;LSB = 1;LSC = 0;break;
case(3):
LSA = 1;LSB = 1;LSC = 0;break;
case(4):
LSA = 0;LSB = 0;LSC = 1;break;
case(5):
LSA = 1;LSB = 0;LSC = 1;break;
case(6):
LSA = 0;LSB = 1;LSC = 1;break;
case(7):
LSA = 1;LSB = 1;LSC = 1;break;
}
switch(j)
{
case(0):
P0=smgduan[0];break;
case(1):
P0=smgduan[2];break;
case(2):
P0=smgduan[5];break;
case(3):
P0=smgduan[16];break;
case(4):
P0=smgduan[4];break;
case(5):
P0=smgduan[1];break;
case(6):
P0=smgduan[3];break;
case(7):
P0=smgduan[1];break;
}
delay(100);
P0=0x00;
}
}
void main()
{
while(1)
{
Display();
}
}
七、独立按键
1.独立按键模块
可以看出其管脚为P30-P33
2.软件设计
使用不同按键控制不同小灯泡点亮与熄灭
#include <reg52.h>
#include <intrins.h>
typedef unsigned char uchar;
typedef unsigned int uint;
sbit K1 = P3^1;
//sbit K2 = P3^0;
sbit K3 = P3^2;
sbit K4 = P3^3;
sbit Led1 = P2^0;
//sbit Led2 = P2^1;
sbit Led3 = P2^2;
sbit Led4 = P2^3;
void delay(uint i)
{
while(i--);
}
void keypros()
{
if(K1 == 0)
{
delay(1000);
if(K1 == 0)
{
Led1 =~Led1;
}
while(!K1);
}
/*if(K2 == 0)
{
delay(1000);
if(K2 == 0)
{
Led2 =~Led2;
}
while(!K2);
}
*/
if(K3 == 0)
{
delay(1000);
if(K3 == 0)
{
Led3 =~Led3;
}
while(!K3);
}
if(K4 == 0)
{
delay(1000);
if(K4 == 0)
{
Led4 =~Led4;
}
while(!K4);
}
}
void main()
{
Led1 = 1;
//Led2 = 1;
Led3 = 1;
Led4 = 1;
while(1)
{
keypros();
}
}
实验的时候发现,如果联通K2按键,会对其他按键造成短路,而其他按键之间不会受到影响。
为什么对按键的判断需要双重if?
if(K1 == 0)
{
delay(1000);
if(K1 == 0)
以为按下和释放的时候波形都是不稳定的,需要判断其稳定后的情况,避免不准确
八、矩阵按键
1.矩阵按键模块
无论是独立键盘还是矩阵键盘,单片机检测其是否被按下的依据都是一样 的,也就是检测与该键对应的 I/O 口是否为低电平。独立键盘有一端固定为低 电平,单片机写程序检测时比较方便。而矩阵键盘两端都与单片机 I/O 口相连, 因此在检测时需编程通过单片机 I/O 口送出低电平。
参考博客链接
1.单片机——蜂鸣器(生日快乐歌)_liu1910260504的博客-CSDN博客_蜂鸣器生日快乐
2. 蜂鸣器奏乐:https://www.cnblogs.com/xiaowuyi/p/3343757.html