搞了一块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 我现在课设考试还感冒都要狗带了(手动再见)