搞了一块51实验板。考试周刚刚结束,修完了c艹,学了一点汇编和逻辑电路基础,这个东西变得很容易上手。找一片不大不小、不深不浅的泳池,每天做一些练习吧~
实验环境:
主控:STC 89C52
适合新手学习的单片机系统实验
集成usb电源和下载
编程环境:keil C51
下载器:stc-isp-15xx-v6.82
进行实验前需要知道的一些信息
- 振荡周期:时钟周期
- 机器周期:一个机器周期包含12个时钟周期,CPU完成一个独立操作
- 使用TTL电平
- 编译器选择Atmel AT89C51或者AT89C52,可以兼容
1.流水灯实验 2016.6.30
流水灯貌似是单片机学习的Hello World。
实验板连接信息:
- 正极接Vcc,负极接芯片引脚。RL2.RL3为排阻。
- 发光二极管工作电压1.6V~2.1V。每条支路可以使用电阻150Ω~3000Ω,使二极管正常发光。不接电阻烧毁二极管(我试过,一下就便当了)
- 在头文件reg52.h中已经申明了P接口地址
- 关键字sbit为位申明作用,使用方法如
[code] #include<reg52.h> sbit LED1 = P1^0; [/code]
- 由电路原理图,输出接口为低电平(0)时,LED点亮
实验目标:使LED0~8依次点亮,最后刷新全灭重复操作。
代码段:
#include <reg51.h>
void Delay_ms(unsigned int xms)
{
unsigned int i , j ;
for(i = xms ;i>0 ;i--)
for(j = 118; j>0 ;j--);
}
void main()
{
P1 = 0;
Delay_ms(100);
P1 = 0xff;
Delay_ms(100);
while(1)
{
unsigned int n ;
for(n=8 ; n>0 ; n--)
{
P1 = P1 << 1 ;
Delay_ms(1000);
}
P1 = 0xff ;
Delay_ms(1000);
}
}
在此程序段中,main函数使用了while循环函数。若是不使用while循环,keil程序会自动将main函数重复执行。可以在末尾使用while(1);停留在函数末尾。
2.数码管静态显示2016.7.01
8段式数码管,连接信息如下

需要了解的信息:
- 显示动态显示和静态显示。静态显示选入一直显示。而动态显示使用动态扫描。所有段选线并联。类似选择器。
- 共阳数码管,阳极共接Vcc,所以,字形库定义如下
unsigned char leddata[]={
0xC0, //"0"
0xF9, //"1"
0xA4, //"2"
0xB0, //"3"
0x99, //"4"
0x92, //"5"
0x82, //"6"
0xF8, //"7"
0x80, //"8"
0x90, //"9"
0x88, //"A"
0x83, //"B"
0xC6, //"C"
0xA1, //"D"
0x86, //"E"
0x8E, //"F"
0x89, //"H"
0xC7, //"L"
0xC8, //"n"
0xC1, //"u"
0x8C, //"P"
0xA3, //"o"
0xBF, //"-"
0xFF, //熄灭
0xA4 //自定义
};
推荐小工具:

- 本接线方法中,P20~P21用于选择,P00~P07用于指示字形。
- 本实验不使用锁存器
实验要求:使用四位数码管,使其从“0000”每次每位加1变成“9999”
代码段:
#include <reg51.h>
sbit P2_0 = P2^0;
sbit P2_1 = P2^1;
sbit P2_2 = P2^2;
sbit P2_3 = P2^3;
int count ;
/*类型为code时,存储在程序段,节省内存空间。但是后期不可改变。若定义char类型,则存储在内存中。*/
unsigned code leddata[]={
0xC0, //"0"
0xF9, //"1"
0xA4, //"2"
0xB0, //"3"
0x99, //"4"
0x92, //"5"
0x82, //"6"
0xF8, //"7"
0x80, //"8"
0x90, //"9"
0x88, //"A"
0x83, //"B"
0xC6, //"C"
0xA1, //"D"
0x86, //"E"
0x8E, //"F"
0x89, //"H"
0xC7, //"L"
0xC8, //"n"
0xC1, //"u"
0x8C, //"P"
0xA3, //"o"
0xBF, //"-"
0xFF, //熄灭
0xA4 //自定义
};
/*延时函数*/
void Delay_ms(unsigned int xms)
{
unsigned int i , j ;
for(i = xms ;i>0 ;i--)
for(j = 118; j>0 ;j--);
}
void LED_ON()
{
P2_0 = 0 ;
P2_1 = 0 ;
P2_2 = 0 ;
P2_3 = 0 ;
}
void LED_OFF()
{
P2_0 = 1 ;
P2_1 = 1 ;
P2_2 = 1 ;
P2_3 = 1 ;
}
void main()
{
do{
P0 = 0xff;
LED_ON();
for(count=0;count<10;count++)
{
// LED_OFF();
P0=leddata[count];
// Delay_ms(5); 个人做了一些实验验证其特性的冗余代码
// LED_ON();
Delay_ms(1000);
}
}while(1) ;
}
备注:
- 其中计数变量conut写在主函数中就会迷之报错,最后只好写成全局变量。
- P2输出低电位时,使该段数码管点亮
- 静态显示不必强制刷新显示。如果想节省io口,可使用锁存器和选择器
3.定时计数器和动态数显 2016.7.3
需要知道的信息:
- 51有2个16位定时计数器,T0T1,之前使用的软件计数器占用CPU资源,而且不准确。
- 定时器每经过一个*机器周期*刷新一次
- 简单地,计数器计数范围为0~65535,实际上,即达到65536溢出。
- 快速公式:65536*1.085us
- 定时器的控制寄存器TCON地址0x88
- 7:TF1 6:TR1 5:TF0 4:TR0
- TF:定时器溢出标志。溢出时为1.中断执行时硬件清零或者软件清零。
- TR:定时器控制位:1开始计时
- 定时器工作模式寄存TMOD 0x89 。默认初值为0
使用定时器的步骤:
1.设置定时器工作模式寄存器TMOD
2.设置计数寄存器的初值
3.设置定时寄存器TCON
TCON能被8整除,可以进行位操作。而不能被8整除,不能进行位操作
实验要求:做一个秒表,0到60循环,使用1个计数器即可,使用软件复位。
代码段
#include <reg51.h>
sbit P2_0 = P2^0;
sbit P2_1 = P2^1;
sbit P2_2 = P2^2;
sbit P2_3 = P2^3;
int count ;
int time_counter ;
unsigned code leddata[]={
0xC0, //"0"
0xF9, //"1"
0xA4, //"2"
0xB0, //"3"
0x99, //"4"
0x92, //"5"
0x82, //"6"
0xF8, //"7"
0x80, //"8"
0x90, //"9"
0x88, //"A"
0x83, //"B"
0xC6, //"C"
0xA1, //"D"
0x86, //"E"
0x8E, //"F"
0x89, //"H"
0xC7, //"L"
0xC8, //"n"
0xC1, //"u"
0x8C, //"P"
0xA3, //"o"
0xBF, //"-"
0xFF, //熄灭
0xA4 //自定义
};
void Delay_ms(unsigned int xms)
{
unsigned int i , j ;
for(i = xms ;i>0 ;i--)
for(j = 118; j>0 ;j--);
}
void boot()
{
TMOD = 0x10; //定时器1工作模式1 16位计时器
}
void display(unsigned char t)
{
unsigned char t0,t1 ;
t0= t%10;
t1= t/10;
P2_3=0;
P0=leddata[t0];
Delay_ms(1);
P2_3=1;
P2_2=0;
P0=leddata[t1];
Delay_ms(1);
P2_2=1;
}
void main()
{
boot();
TH1 = 0x4b ;
TL1 = 0xfe ;//送50ms
TR1 = 1;//启动定时器
do
{
if(TF1 == 1)
{
TF1 = 0;//软件复位
TH1 =0x4b;
TL1 = 0xfe;//重新送入50ms;
count++;
}
if(count == 20)
{
count = 0;
time_counter++;
}
if(time_counter == 60)
{
time_counter = 0;
}
display(time_counter);
}while(1) ;
}
- 注意数码管显示的位数位置
2016.7.4
头文件:
4.独立键盘 2014.7.5

- 有硬件进行编码的键盘称为编码键盘。
- 由软件编程识别按键的称为非编码键盘。
- 所有IO口上电后默认是高电平
- 另一端接地,按键接通后为0,可以检测io口输入为0
暂时先不使用中断。使用软件循环检测
实验目标:计数器,s3+1,s4-1,注意别溢出
#include <reg51.h>
sbit S1 = P3^5;
sbit S2 = P3^4;
sbit S3 = P3^3;
sbit S4 = P3^2;
sbit P2_3 = P2^3 ;
sbit P2_2 = P2^2 ;
int count ;
int time_counter ;
unsigned code leddata[]={
0xC0, //"0"
0xF9, //"1"
0xA4, //"2"
0xB0, //"3"
0x99, //"4"
0x92, //"5"
0x82, //"6"
0xF8, //"7"
0x80, //"8"
0x90, //"9"
0x88, //"A"
0x83, //"B"
0xC6, //"C"
0xA1, //"D"
0x86, //"E"
0x8E, //"F"
0x89, //"H"
0xC7, //"L"
0xC8, //"n"
0xC1, //"u"
0x8C, //"P"
0xA3, //"o"
0xBF, //"-"
0xFF, //熄灭
0xA4 //自定义
};
void Delay_ms(unsigned int xms)
{
unsigned int i , j ;
for(i = xms ;i>0 ;i--)
for(j = 118; j>0 ;j--);
}
void display(unsigned char t)
{
unsigned char t0,t1 ;
t0= t%10;
t1= t/10;
P2_3=0;
P0=leddata[t0];
Delay_ms(1);
P2_3=1;
P2_2=0;
P0=leddata[t1];
Delay_ms(1);
P2_2=1;
}
void main()
{
do //大循环
{
display(time_counter); //数码管显示函数
if(S3 == 0) //检测s3被按下
{
Delay_ms(30);//延时消抖
if(S3 == 0)
{
time_counter++;
while(!S3); //若不弹起,则暂停
}
}
if(S4 == 0&& time_counter!=0) //检测s4被按下,并防止负向溢出
{
Delay_ms(30);//延时消抖
if(S4 == 0)
{
time_counter--;
while(!S4); //若不弹起,则暂停
}
}
}while(1) ;
}
5.矩阵键盘 2016.7.6
连接:
板子无内置,使用了某套件盒里的矩阵键盘

P30第一行P31第二行P32第三行P33第四行
P34第一列P35第二列P36第三列P37第四列
逻辑上跟这个一样

- IO 之间如果连接,最终的信号可以用并运算判断
- 矩阵键盘没有引用外部地线或vcc,用软件编程判断。
实验目标:P3接外接矩阵键盘。用数码管显示其16进制键位码,同时用LED指示其按键位置
#include <reg51.h>
sbit S1 = P3^5;
sbit S2 = P3^4;
sbit S3 = P3^3;
sbit S4 = P3^2;
sbit P2_3 = P2^3 ;
sbit P2_2 = P2^2 ;
int count ;
int time_counter ;
unsigned char temp;
unsigned code leddata[]={
0xC0, //"0"
0xF9, //"1"
0xA4, //"2"
0xB0, //"3"
0x99, //"4"
0x92, //"5"
0x82, //"6"
0xF8, //"7"
0x80, //"8"
0x90, //"9"
0x88, //"A"
0x83, //"B"
0xC6, //"C"
0xA1, //"D"
0x86, //"E"
0x8E, //"F"
0x89, //"H"
0xC7, //"L"
0xC8, //"n"
0xC1, //"u"
0x8C, //"P"
0xA3, //"o"
0xBF, //"-"
0xFF, //熄灭
0xA4 //自定义
};
void Delay_ms(unsigned int xms)
{
unsigned int i , j ;
for(i = xms ;i>0 ;i--)
for(j = 118; j>0 ;j--);
}
void display(unsigned char t)
{
unsigned char t0,t1 ;
t0= t%0xf;
t1= t/0xf;
P2_3=0;
P0=leddata[t0];
Delay_ms(1);
P2_3=1;
P2_2=0;
P0=leddata[t1];
Delay_ms(1);
P2_2=1;
}
unsigned char keyscan()
{
unsigned char key_x, key_y;//坐标变量
P3 =0xf0;//初始化,等待行线全为1,列线全为0。1111 0000
if((P3 & 0xf0) != 0xf0) //若发生变化
{
Delay_ms(5); //软件消抖
if((P3 & 0xf0) != 0xf0) //仍然按下
{
key_y = (P3 & 0xf0); //保存列坐标(y坐标)
P3 = 0x0f;
key_x = (P3 & 0x0f); //保存行坐标(x坐标)
while((P3 & 0x0f) != 0x0f);//松手检测
return (key_x+key_y); //返回键值
}
}
return temp;
}
void main()
{
do //大循环
{
temp=keyscan();
P1=temp;
display(temp);
}while(1) ;
}
1.不知为啥 我接上了P3的外接键盘后,开发板开始迷之一股青烟,然后某个器件以肉眼可见的速度变黑
2.但是功能似乎没有受到影响
3.冒青烟后,一段时间内编译器无法正常工作。后来洗了脸,竟然又能正常工作了
4.希望这个板子能撑过我的实验折腾……
存活图:

阿门!世界真是艰难
(出青烟那瞬间我以为我还得买个新的)
2016.7.6
六 中断系统 2016.7.9
对之前定时器计数器的补充
- 计数器用于计数, P3.4对应T0,P3.5对应T1
- 工作方式寄存器TMOD用于设置定时器的工作模式和方式
- 控制寄存器 TCON 用于控制开始停止
- TH0,8位,TH1,8位
- 溢出可触发中断
- 快速计算: 初值=T0溢出值-需求的计数
另外TMOD和TCON的一些注意:
- TMOD地址89H,只有能被8整除才能位选(^)。

- C/~T,0计时器,1计数器
- 00:方式0:13位定时计数器,TH0用低5位,TL0用8位:
- 方式0溢出后,TH0TL0都被置0.需要软件重新装载
- 01方式1:16位,TH0TL0都用8位
- 10方式2:8位自动重装
- TCON控制方法



太狠了!!
必须支持一下!我也想假期学学单片机看时间能不能安排开吧
@fandy 7月1日0点01的评论!另我觉得你学起来更快啊…这些c语言方式写的代码看看就懂了~
@fandy 你的承诺呢,你的担架呢
@SPtuan 我现在课设考试还感冒都要狗带了(手动再见)