当前位置:   article > 正文

初识C51单片机(STC89C52RC开发板)_c51单片机和stc89

c51单片机和stc89

一、学会创建新工程

(安装Keil 5 教程自行寻找)

选择好保存目录,取好名字

点击Add后即可开始编写第一个程序

二、点亮第一个LED灯

为什么不是高电平?看原理图

因此引脚设置为低电平,才能形成电势差,存在电压才能点亮led

下面讲一下怎么把程序刷入开发板

首先,用keil5将代码编译成hex文件

注意:如果在项目目录下的各个文件夹内都没生成hex文件,请看下面的操作步骤

点击ok即可,然后重新编译

再讲解一下将hex文件刷入开发板的方法:

1、使用PZ-ISP软件(打开板子电源开关即可自动下载)

2、使用STC-ISP(官方软件,推荐)需要冷启动

冷启动:板子在掉电状态下点击下载,打开电源后开始下载

三、LED闪烁实验

很简单,点亮,然后循环延时一会再熄灭,重复这个操作就是闪烁了

四、LED流水灯实验

五、蜂鸣器实验

蜂鸣器持续发声,即给蜂鸣器一个脉冲信号,使其不断开关,来持续稳定发声。它不是给一个高电平信号就可以持续响的!

六、数码管(静态/动态)

段码数据计算:

数码管原理图上,让哪根线亮,哪根就是1,按下面的顺序排列,转换成16进制即可,比如

dp g f e d c b a

0 1 1 1 1 1 1 1 -> 0x7F 即亮一个8

所谓动态数码管,本质上是让数码管一位一位点亮,利用人眼的余晖效应,只有30Hz,用高频率刷新,看起来就像同时点亮,在使用上面的nixie()函数时,后面建议加一句P0=0x00;消音以避免会有残影

七、 独立按键实验

我将四个独立按键进行封装,作为函数,便于后续调用。

使用方法:在主函数while(1)里写上if(key() == 1/2/3/4){}即可判断1-4哪个按键被按下

八、 矩阵键盘实验

  1. #include <REGX52.H>
  2. #include "delay.h"
  3. #define KEY_MATRIX_PORT P1
  4. /*******************************************************************************
  5. * 函 数 名 : key_matrix_ranks_scan
  6. * 函数功能 : 使用行列式扫描方法,检测矩阵按键是否按下,按下则返回对应键值
  7. * 输 入 : 无
  8. * 输 出 : key_value:1-16,对应S1-S16键,
  9. 0:按键未按下
  10. ====实现原理====
  11. 逐列扫描
  12. 低八位
  13. ————> 1、先拉低某一列
  14. 高| 2、再检测GPIO 1寄存器中的电平信号来判断某一行
  15. 八| 3、不断循环这个过程
  16. 位|
  17. V
  18. *******************************************************************************/
  19. int scankey()
  20. {
  21. char key_value=0;
  22. KEY_MATRIX_PORT=0xf7;//给第一列赋值0,其余全为1
  23. if(KEY_MATRIX_PORT!=0xf7)//判断第一列按键是否按下
  24. {
  25. delay(10);//消抖
  26. switch(KEY_MATRIX_PORT)//保存第一列按键按下后的键值
  27. {
  28. case 0x77: key_value=1;break;
  29. case 0xb7: key_value=5;break;
  30. case 0xd7: key_value=9;break;
  31. case 0xe7: key_value=13;break;
  32. }
  33. }
  34. while(KEY_MATRIX_PORT!=0xf7);//等待按键松开
  35. KEY_MATRIX_PORT=0xfb;//给第二列赋值0,其余全为1
  36. if(KEY_MATRIX_PORT!=0xfb)//判断第二列按键是否按下
  37. {
  38. delay(10);//消抖
  39. switch(KEY_MATRIX_PORT)//保存第二列按键按下后的键值
  40. {
  41. case 0x7b: key_value=2;break;
  42. case 0xbb: key_value=6;break;
  43. case 0xdb: key_value=10;break;
  44. case 0xeb: key_value=14;break;
  45. }
  46. }
  47. while(KEY_MATRIX_PORT!=0xfb);//等待按键松开
  48. KEY_MATRIX_PORT=0xfd;//给第三列赋值0,其余全为1
  49. if(KEY_MATRIX_PORT!=0xfd)//判断第三列按键是否按下
  50. {
  51. delay(10);//消抖
  52. switch(KEY_MATRIX_PORT)//保存第三列按键按下后的键值
  53. {
  54. case 0x7d: key_value=3;break;
  55. case 0xbd: key_value=7;break;
  56. case 0xdd: key_value=11;break;
  57. case 0xed: key_value=15;break;
  58. }
  59. }
  60. while(KEY_MATRIX_PORT!=0xfd);//等待按键松开
  61. KEY_MATRIX_PORT=0xfe;//给第四列赋值0,其余全为1
  62. if(KEY_MATRIX_PORT!=0xfe)//判断第四列按键是否按下
  63. {
  64. delay(10);//消抖
  65. switch(KEY_MATRIX_PORT)//保存第四列按键按下后的键值
  66. {
  67. case 0x7e: key_value=4;break;
  68. case 0xbe: key_value=8;break;
  69. case 0xde: key_value=12;break;
  70. case 0xee: key_value=16;break;
  71. }
  72. }
  73. while(KEY_MATRIX_PORT!=0xfe);//等待按键松开
  74. return key_value;
  75. }
  76. /*******************************************************************************
  77. * 函 数 名 : key_matrix_flip_scan
  78. * 函数功能 : 使用线翻转扫描方法,检测矩阵按键是否按下,按下则返回对应键值
  79. * 输 入 : 无
  80. * 输 出 : key_value:1-16,对应S1-S16键,
  81. 0:按键未按下
  82. ====原理说明====
  83. 先扫出行,再扫出列
  84. 依然是分高八位低八位
  85. 先扫描列数据,行为高八位,置0,按键按下后就会将列的低八位拉低为0,此时可以检测出哪一列被按下
  86. 再扫描行,低八位置0,按键按下后就会将高八位拉低,检测出哪一行被按下,判断哪个按键即可
  87. 第一行,直接输出列
  88. 第二行,列+4即可
  89. 第三行,列+8
  90. 依此类推
  91. *******************************************************************************/
  92. char key_matrix_flip_scan()
  93. {
  94. static char key_value=0;
  95. KEY_MATRIX_PORT=0x0f;//给所有行赋值0,列全为1
  96. if(KEY_MATRIX_PORT!=0x0f)//判断按键是否按下
  97. {
  98. delay(10);//消抖
  99. if(KEY_MATRIX_PORT!=0x0f)
  100. {
  101. //测试列
  102. KEY_MATRIX_PORT=0x0f;
  103. switch(KEY_MATRIX_PORT)//保存行为0,按键按下后的列值
  104. {
  105. case 0x07: key_value=1;break;
  106. case 0x0b: key_value=2;break;
  107. case 0x0d: key_value=3;break;
  108. case 0x0e: key_value=4;break;
  109. }
  110. //测试行
  111. KEY_MATRIX_PORT=0xf0;
  112. switch(KEY_MATRIX_PORT)//保存列为0,按键按下后的键值
  113. {
  114. case 0x70: key_value=key_value;break;
  115. case 0xb0: key_value=key_value+4;break;
  116. case 0xd0: key_value=key_value+8;break;
  117. case 0xe0: key_value=key_value+12;break;
  118. }
  119. while(KEY_MATRIX_PORT!=0xf0);//等待按键松开
  120. }
  121. }
  122. else
  123. key_value=0;
  124. return key_value;
  125. }

代码及原理解释直接给出,方法二效率更高一些,因为扫描次数更少,讲解看不懂的建议去b站看讲解动画。

封装函数使用方法:在主函数while(1)里面写if(scankey() == 1/2/……16){}与独立按键相同

九、点阵屏实验

与数码管类似,但这里多了74HC595芯片(串行传并行,极大减少io使用)

74HC595是一个8位串行输入、并行输出的位移缓存器:并行输出为三态输出。在SCK 的上升沿,串行数据由SDL输入到内部的8位位移缓存器,并由Q7'输出,而并行输出则是在LCK的上升沿将在8位位移缓存器的数据存入到8位并行输出缓存器。当串行数据输入端OE的控制信号为低使能时,并行输出端的输出值等于并行输出缓存器所存储的值。

看不懂b站上有动画说明

根据74HC595芯片的工作原理,我们知道,最关键的引脚其实只有三个,分别是串行数据输入引脚DS、移位寄存器时钟输入引脚SHCP、储存寄存器时钟输入引脚STCP;所以代码怎么写,无非就是配置这几个引脚,定义串行数据输入的子函数,以及运行所需要的主函数

下面直接给出代码

  1. /**************************************************************************************
  2. 实验名称:LED点阵实验(显示图像)
  3. 实验现象:下载程序后,8*8LED点阵显示心形
  4. 注意事项:LED点阵旁的J24黄色跳线帽短接到GND一端
  5. ***************************************************************************************/
  6. #include "reg51.h"
  7. //定义74HC595控制管脚
  8. sbit SRCLK=P3^6; //移位寄存器时钟输入
  9. sbit RCLK=P3^5; //存储寄存器时钟输入
  10. sbit SER=P3^4; //串行数据输入
  11. #define LEDDZ_COL_PORT P0 //点阵列控制端口
  12. char gled_row[8]={0x38,0x7C,0x7E,0x3F,0x3F,0x7E,0x7C,0x38};//LED点阵显示图像的行数据
  13. char gled_col[8]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//LED点阵显示图像的列数据
  14. /*******************************************************************************
  15. * 函 数 名 : hc595_write_data(char dat)
  16. * 函数功能 : 向74HC595写入一个字节的数据
  17. * 输 入 : dat:数据
  18. * 输 出 : 无
  19. *******************************************************************************/
  20. void hc595_write_data(char dat)
  21. {
  22. char i=0;
  23. for(i=0;i<8;i++)//循环8次即可将一个字节写入寄存器中
  24. {
  25. SER=dat>>7;//优先传输一个字节中的高位
  26. dat<<=1;//将低位移动到高位
  27. SRCLK=0;
  28. delay(1);
  29. SRCLK=1;
  30. delay(1);//移位寄存器时钟上升沿将端口数据送入寄存器中
  31. }
  32. RCLK=1;
  33. delay(1);
  34. RCLK=0;//存储寄存器时钟上升沿将前面写入到寄存器的数据输出
  35. }
  36. void main()
  37. {
  38. char i=0;
  39. while(1)
  40. {
  41. for(i=0;i<8;i++)//循环8次扫描8行、列
  42. {
  43. LEDDZ_COL_PORT=gled_col[i];//传送列选数据
  44. hc595_write_data(gled_row[i]);//传送行选数据
  45. delay(1);//延时一段时间,等待显示稳定
  46. hc595_write_data(0x00);//消影
  47. }
  48. }
  49. }

 十、定时器

        1.CPU时序

                 1) 震荡周期:为单片机提供定时信号的震荡源的周期(晶振周期或外加震荡周期)。

                 2) 状态周期:2个震荡周期为1个状态周期,用S表示。震荡周期又称S周期或时钟周期。

                 3) 机器周期:1个机器周期含6个状态周期,12个震荡周期

                 4) 指令周期:完成1条指令所占用的全部时间,他以机器周期为单位。

  • 例如:外界晶振为12MHz时,51单片机相关周期的具体值为:
  • 震荡周期=1/12us
  • 状态周期=1/6us
  • 机器周期=1us
  • 指令周期=1~4us

        2.定时器初步认识

                ① 51单片机有两组定时器/计数器,因为既可以定时,又可以计数,故称之为定时器/计数器   

                ② 定时器/计数器和单片机的CPU是相互独立的。定时器/计数器工作的过程自动完成的,不需要CPU的参与。

                ③ 51单片机中的定时器/计数器是根据机器内部的时钟或者外部的脉冲信号对寄存器中的数据加1

        3.定时器原理

                STC89C5X单片机内有两个可编程的定时/计数器 T0,T1 和一个特殊功能定时器T2。定时/计数的是指是加 1 计数器(16位),由高 8 位到低 8 位两个寄存器 THx 和 TLx 组成。它随着计数器的输入脉冲进行自加 1 ,也就是每来一个脉冲,计数器就自动加 1 ,当加到计数器为全 1 时,再输入一个脉冲就使计数器回零,且计数器的益出相应的中断标志位置 1 ,向CPU发出中断请求(定时/计数器中断允许时)。如果定时/计数器工作于定时模式,则表示定时时间已到;如果工作于计数模式,则表示计数值已满。可见,由溢出时计算器的值减去计数初值才是加 1 计数器的计算值。

        4.定时器/计数器控制寄存器TCON

可位寻址:可以对寄存器的某一位进行修改操作,而其它位数据保持不变

上图中常用的几个数据位为:

TF0 / TF1  ——  定时器/计数器 T1溢出标志。 当最高位产生溢出时由硬件置“1”,向CPU请求中断。 一直保持到 CPU 响应中断时,才由硬件清 “0 ”。
TR0 / TR1 ——   定时器 T0的运行控制位。 TRX=1 时就允许TX 开始计数。

        5.定时器/计数器工作模式寄存器TMOD

不可位寻址 : 必须对整个寄存器的每一位数据进行覆盖。此处可以利用“TMOD &= 0xF0”来保持高位不变,低位清0,也可以利用“TMOD |= 0xXX”来实现更多的修改操作

这里用定时器0的16位模式演示

按照手册,TMOD = 0x01;

  1. #include <REGX52.H>
  2. sbit LED1 = P2^0;//将P2寄存器按位寻址第一位赋给LED1
  3. void t0_init(void)
  4. {
  5. TMOD=0x10; //设置定时器0模式为16位
  6. TF0=0; // 溢出位 置零
  7. TH0=0xFC; // 高位 1111 1100
  8. TL0=0x18; // 低位 0001 1000
  9. //1ms FC18(16) -> 64536(10) 距离65536差1000(us) 即1ms后中断一次
  10. ET0=1;//打开定时器0中断允许
  11. EA=1;//打开总中断
  12. TR0=1;//定时器开始计时
  13. }
  14. void main()
  15. {
  16. t0_init();//初始化
  17. while(1)
  18. {
  19. }
  20. }
  21. void t0() interrupt 1 //定时器0中断函数 interrupt 中断次序号 越小优先级越高
  22. {
  23. static int i;//定义静态变量i
  24. //重新赋初值,为下次计时准备
  25. TH0=0XFC;
  26. TL0=0X18;
  27. i++;
  28. if(i==1000)//1000ms = 1s
  29. {
  30. LED1=!LED1; //电平反转
  31. i=0;
  32. }
  33. }

实验效果:第一个LED灯先暗1s然后亮1s,依次循环 

注意:上面代码中的注释“次序号越小优先级越高”有误,手册中说的是相同优先级情况下,次序号越小优先级越高,中断优先级是可以通过寄存器控制的,具体解释看十一节

十一、外部中断

        STC89C51RC/RD+系列单片机提供了8个中断请求源,它们分别是:外部中断0(INT0)、
定时器0中断、外部中断1(INT1)、定时器1中断、串口(UART)中断、定时器2中断、外部中断
2(INT2)、外部中断3(INT3)。所有的中断都具有4个中断优先级。

此处对上一节做出解释:中断优先级控制寄存器相同情况下(即同一中断优先级下),优先级取决于中断查询次序。

具体外部中断的寄存器的基本操作步骤为:

1、打开中断允许控制寄存器(总中断开关EA=1,子中断开关比如EX0=1)

2、调整ITx寄存器来控制中断触发方式(比如低电平触发IT0=0,下降沿触发IT0=1)

3、配置中断执行函数 void func(void) interrupt 次序号 { //要执行的内容 }

4、其他高级操作比如修改中断优先级请自行研究手册

  1. #include "reg52.h"
  2. //定义LED1管脚
  3. sbit LED1=P2^0;
  4. //定义独立按键K2控制脚
  5. sbit KEY2=P3^0;
  6. void exti1_init(void)
  7. {
  8. IT0=1;//跳变沿触发方式(下降沿)
  9. EX0=1;//打开INT0的中断允许
  10. EA=1;//打开总中断
  11. }
  12. void main()
  13. {
  14. exti1_init();//外部中断0配置
  15. while(1)
  16. {
  17. }
  18. }
  19. void exti1() interrupt 1 //外部中断0中断函数
  20. {
  21. if(KEY2==0)//判断K3键是否按下
  22. LED1=!LED1;//LED1状态翻转
  23. }

实验现象:下载程序后,当按下K3键可控制D1指示灯亮灭

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/531282
推荐阅读
相关标签
  

闽ICP备14008679号