C:\Users\Administrator\Downloads\2019-10-14-11-5-48-1182922812500-Steins;Lab - 某团的自留研究所-采集的数据-后羿采集器.html
标题【学习手记】51单片机基础实验(C语言)←2016.6.30~7.06 链接https://steinslab.io/archives/561 导语搞了一块51实验板。考试周刚刚结束,修完了c艹,学了一点汇编和逻辑电路基础,这个东西变得很容易上手。找一片不大不小、不深不浅的泳池,每天做一些练习吧~ 正文
搞了一块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依次点亮,最后刷新全灭重复操作。
代码段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
#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,所以,字形库定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
|
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”
代码段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
|
#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个计数器即可,使用软件复位。
代码段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
|
#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
头文件:
intrins.h
在 C51单片机 编程中,头文件INTRINS.H的函数使用起来,就会让你像在用 汇编时一样简便.
_crol_ 字符循环左移
_cror_ 字符循环右移
_irol_ 整数循环左移
_iror_ 整数循环右移
_lrol_ 长整数循环左移
_lror_ 长整数循环右移
_nop_ 空操作 (相当于8051 NOP 指令)
函数名: _crol_,_irol_,_lrol_
原 型:
unsigned char _crol_(unsigned char val,unsigned char n);
unsigned int _irol_(unsigned int val,unsigned char n);
unsigned int _lrol_(unsigned int val,unsigned char n);
举例:
_crol_,_cror_:将char型变量循环向左(右)移动指定位数后返回
_ testbit_: 相当于JBC bitvar测试该位变量并跳转同时清除。
_chkfloat_: 测试并返回源点数状态。
_crol_,_cror_:如果二进制数为01010101 那么_crol_(1) 左移1位后将高位补低位。
结果10101010。
功 能:_crol_,_irol_,_lrol_以位形式将val 左移n 位,该函数与8051“RLA”指令相关,上面几个函数不同于参数类型。
4.独立键盘 2014.7.5
硬件独立键盘接法图:
S1接P35,S2接P34,S3接P33,S4接P32,另一极共地
实验前需要知道的信息:
- 有硬件进行编码的键盘称为编码键盘。
- 由软件编程识别按键的称为非编码键盘。
- 所有IO口上电后默认是高电平
- 另一端接地,按键接通后为0,可以检测io口输入为0
暂时先不使用中断。使用软件循环检测
实验目标:计数器,s3+1,s4-1,注意别溢出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
|
#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指示其按键位置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
|
#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控制方法
相关
|