当前位置:   article > 正文

单片机学习笔记——入门51单片机

单片机

一、单片机基础介绍

1.何为单片机

单片机,英文Micro Controller Unit,简称MCU 。内部集成了中央处理器CPU、随机存储器ROM、只读存储器RAM、定时器/计算器、中断系统和IO口等一系列电脑的常用硬件功能 单片机的任务是信息采集(依靠传感器)、处理(依靠CPU)和硬件设备(例如电机,LED等)的控制 。单片机跟计算机相比,单片机算是一个袖珍版计算机,一个芯片就能构成完整的计算机系统。但在性能上,与计算机相差甚远,但单片机成本低、体积小、结构简单,在生活和工业控制领域大有所用。 同时,学习使用单片机是了解计算机原理与结构的最佳选择。

单片机工作的基本时序

我们都知道在学校是通过铃声来控制所有班级的上下课时间,我们都知道单片机执行指令的过程就是从ROM取出一条指令执行来完成它在各个地方的作用,那它什么时候取指令这个是顺序呢?这里引入一个时序的周期,每访问一次ROM的时间,就是一个机器周期的时间

1个机器周期 = 6个状态周期 = 12个时钟(振荡)周期  

时钟周期:即单片机的基本时间单位,若晶体的频率=12MHZ,那时钟周期 = 1/12MHZ,一个时钟周期 = 1/12MHZ = 1/12000 000每秒

机器周期:即12x1/12 000 000 =0.000001s = 1us,访问一次ROM取指令的时间就是1us

2.单片机命名规则

3.单片机内部结构

重点需记:单片机管脚

1.电源:Vcc:正极     Gnd:负极             2.XTAL:单片机时钟引脚,外接晶振

3.RST:复位

4.开发板介绍

开发板原理图

二、单片机的一些基础项目

2-1、点亮一个led灯

  1. #include <REGX52.H>
  2. void main()
  3. {
  4. P2=0x7f;//1111 1110 d1 16 15
  5. //0111 1111 d8 7 16
  6. //1011 1111 d7 11 16
  7. }

通过高低电平控制led亮否

2-2、led闪烁

  1. #include <REGX52.H>
  2. #include <INTRINS.H>
  3. //延时函数
  4. void Delay500ms() //@11.0592MHz
  5. {
  6. unsigned char i, j, k;
  7. _nop_();
  8. i = 4;
  9. j = 129;
  10. k = 119;
  11. do
  12. {
  13. do
  14. {
  15. while (--k);
  16. } while (--j);
  17. } while (--i);
  18. }
  19. void main()
  20. {
  21. while(1){
  22. P2=0xfe;//亮
  23. Delay500ms();//延时500ms
  24. P2=0xff;//灭
  25. Delay500ms();//延时500ms
  26. }
  27. }

通过延时函数使led闪烁

2-3、流水灯

  1. #include <REGX52.H>
  2. #include <INTRINS.H>
  3. void Delay500ms() //@11.0592MHz
  4. {
  5. unsigned char i, j, k;
  6. _nop_();
  7. i = 4;
  8. j = 129;
  9. k = 119;
  10. do
  11. {
  12. do
  13. {
  14. while (--k);
  15. } while (--j);
  16. } while (--i);
  17. }
  18. void main()
  19. {
  20. while(1){
  21. P2=0xfe;//1111 1110
  22. Delay500ms();
  23. P2=0xfd;//1111 1101
  24. Delay500ms();
  25. P2=0xfb;//1111 1011
  26. Delay500ms();
  27. P2=0xf7;//1111 0111
  28. Delay500ms();
  29. P2=0xef;//1110 1111
  30. Delay500ms();
  31. P2=0xdf;//1101 1111
  32. Delay500ms();
  33. P2=0xbf;//1011 1111
  34. Delay500ms();
  35. P2=0x7f;//0111 1111
  36. Delay500ms();
  37. }
  38. }

位运算做法:

2-4、流水灯plus

  1. #include <REGX52.H>
  2. #include <INTRINS.H>
  3. //任意延时函数——1ms的延时函数执行x次循环
  4. void Delay1ms(unsigned int xms) //@11.0592MHz
  5. {
  6. unsigned char i, j;
  7. while(xms)
  8. {
  9. _nop_();
  10. i = 2;
  11. j = 199;
  12. do
  13. {
  14. while (--j);
  15. } while (--i);
  16. xms--;}
  17. }
  18. void main()
  19. {
  20. while(1){
  21. P2=0xfe;//1111 1110
  22. Delay1ms(100);
  23. P2=0xfd;//1111 1101
  24. Delay1ms(100);
  25. P2=0xfb;//1111 1011
  26. Delay1ms(100);
  27. P2=0xf7;//1111 0111
  28. Delay1ms(100);
  29. P2=0xef;//1110 1111
  30. Delay1ms(100);
  31. P2=0xdf;//1101 1111
  32. Delay1ms(100);
  33. P2=0xbf;//1011 1111
  34. Delay1ms(100);
  35. P2=0x7f;//0111 1111
  36. Delay1ms(100);
  37. }
  38. }

通过任意延时函数去简化步骤

3-1、通过独立按键控制led闪烁

  1. #include <REGX52.H>
  2. void main()
  3. {
  4. while(1)
  5. {
  6. if(P3_1==0)//低电平 按下按键接地为0
  7. {
  8. P2_0=0;//d1亮
  9. }
  10. else {
  11. P2_0=1;
  12. }
  13. }
  14. }

3-2、通过独立按键控制led状态

  1. #include <REGX52.H>
  2. #include <INTRINS.H>
  3. void Delay(unsigned int xms) //@11.0592MHz
  4. {
  5. unsigned char i, j;
  6. while(xms){
  7. _nop_();
  8. i = 2;
  9. j = 199;
  10. do
  11. {
  12. while (--j);
  13. } while (--i);
  14. xms--;
  15. }
  16. }
  17. void main()
  18. {
  19. while(1)
  20. {
  21. if(P3_1==0)
  22. {
  23. Delay(20);//取消前摇
  24. while(P3_1==0);//判断何时松手
  25. Delay(20);//取消后摇
  26. P2_0=~P2_0;//按位取反
  27. }
  28. }
  29. }

取消按键时的抖动,使单片机稳定判断状态。按一次亮,按一次灭。

3-3、通过独立按键控制led完成二进制

  1. #include <REGX52.H>
  2. #include <INTRINS.H>
  3. void Delay(unsigned int xms) //@11.0592MHz
  4. {
  5. unsigned char i, j;
  6. while(xms){
  7. _nop_();
  8. i = 2;
  9. j = 199;
  10. do
  11. {
  12. while (--j);
  13. } while (--i);
  14. xms--;
  15. }
  16. }
  17. void main()
  18. {
  19. unsigned int lednum=0;
  20. while(1)
  21. {
  22. if(P3_1==0)
  23. {
  24. Delay(20);
  25. while(P3_1==0);
  26. Delay(20);
  27. lednum++;
  28. P2=~lednum;
  29. }
  30. }
  31. }

也可以直接用P2--;代替最后两句,完成二进制运算。

3-4、用独立按键控制led灯移位

  1. #include <REGX52.H>
  2. #include <INTRINS.H>
  3. void Delay(unsigned int xms) //@11.0592MHz
  4. {
  5. unsigned char i, j;
  6. while(xms){
  7. _nop_();
  8. i = 2;
  9. j = 199;
  10. do
  11. {
  12. while (--j);
  13. } while (--i);
  14. xms--;
  15. }
  16. }
  17. void main()
  18. {
  19. unsigned int lednum=0;
  20. P2=~0x01;
  21. while(1)
  22. {
  23. if(P3_1==0)//k1
  24. {
  25. Delay(20);
  26. while(P3_1==0);
  27. Delay(20);
  28. lednum++;
  29. if(lednum>=8)
  30. lednum=0;
  31. P2=~(0x01<<lednum);
  32. }
  33. if(P3_0==0)//k2
  34. {
  35. Delay(20);
  36. while(P3_0==0);
  37. Delay(20);
  38. if(lednum==0)
  39. lednum=7;
  40. else lednum--;
  41. P2=~(0x01<<lednum);
  42. }
  43. }
  44. }

4-1 静态数码管

1.常见数码管

2.控制数码管显示的原理图

3.管脚定义(对应字母控制对应位置亮):上面的为共阴极、下面的为共阳极(可以理解为3,8管脚处为供电,三角形尖尖有一横的是负极,所有共阴极),两个图中的数字为引脚:

4.下面为多个数码管,PCB板的4个为一体,同样上面为共阴极、下面为共阳极的原理图:

5.STC89C52实现数字显示

①原理图是共阴极(上面给0、下给1亮)

②138译码器:输入3(ABC,读的时候是从下读 C B A )个口,控制输出8个口,输出口连接共阴极的,是0还是1,在这里控制:使能端连接(按下图给1和0就可以用了)

通过CBA给数字0和1二进制转换10进制(得到数字几)就控制Y几,Y0头上“—”是表示低电平有效(即给0):

③双向缓冲,高电平往低电平送数据

CC2电容作用:滤波电容,稳定电源,确定电路稳定性,提高电路工作性能可靠运行;

RP4:排阻,限流,防止电流过大

④这里的P01......P07,就是用P0口,后面代码就是通过P0口控制灯的

只有Y5为0,其他Y0...Y7都为1;

读取顺序都是从下到上

⑤代码控制公共端,从下往上写:

二进制101转换为1十进制为5,控制Y5,即公共端的LED6;

要显示下图的数字6

代码实现如下(P2控制共阴极,P0控制显示数字)及结果;

 ⑥优化操作代码:通过数组,子函数来优化代码
  1. #include <REGX52.H>
  2. #include <INTRINS.H>
  3. unsigned char NixieTable[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};
  4. void Nixie(unsigned char Location,Number)
  5. {
  6. switch(Location)
  7. {
  8. case 1:P2_4=1;P2_3=1;P2_2=1;break;
  9. case 2:P2_4=1;P2_3=1;P2_2=0;break;
  10. case 3:P2_4=1;P2_3=0;P2_2=1;break;
  11. case 4:P2_4=1;P2_3=0;P2_2=0;break;
  12. case 5:P2_4=0;P2_3=1;P2_2=1;break;
  13. case 6:P2_4=0;P2_3=1;P2_2=0;break;
  14. case 7:P2_4=0;P2_3=0;P2_2=1;break;
  15. case 8:P2_4=0;P2_3=0;P2_2=0;break;
  16. }
  17. P0=NixieTable[Number];
  18. }
  19. void main()
  20. {
  21. Nixie(7,10);
  22. while(1);
  23. }

要显示的数字对应的值

4-2、动态数码管 

  1. #include <REGX52.H>
  2. #include <INTRINS.H>
  3. unsigned char NixieTable[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};
  4. void Delay (unsigned int xms) //@11.0592MHz
  5. {
  6. unsigned char i, j;
  7. while(xms--){
  8. _nop_();
  9. i = 2;
  10. j = 199;
  11. do
  12. {
  13. while (--j);
  14. } while (--i);
  15. }
  16. }
  17. void Nixie(unsigned char Location,Number)
  18. {
  19. switch(Location)
  20. {
  21. case 1:P2_4=1;P2_3=1;P2_2=1;break;
  22. case 2:P2_4=1;P2_3=1;P2_2=0;break;
  23. case 3:P2_4=1;P2_3=0;P2_2=1;break;
  24. case 4:P2_4=1;P2_3=0;P2_2=0;break;
  25. case 5:P2_4=0;P2_3=1;P2_2=1;break;
  26. case 6:P2_4=0;P2_3=1;P2_2=0;break;
  27. case 7:P2_4=0;P2_3=0;P2_2=1;break;
  28. case 8:P2_4=0;P2_3=0;P2_2=0;break;
  29. }
  30. P0=NixieTable[Number];
  31. Delay(1);//保证亮度
  32. P0=0x00;//清零
  33. }
  34. void main()
  35. {
  36. while(1){
  37. Nixie(2,1);
  38. // Delay(20);
  39. Nixie(3,2);
  40. // Delay(20);
  41. Nixie(4,3);
  42. // Delay(20);
  43. }
  44. }

注释掉上面的延时调用,旁边的管会有些影响,需要消影,段选、位选影响造成串位,如下代码消除

了解

5-1、模块化编程 

1)驱动,先会用,后续有详细内容:

2)模块化,功能函数用点C文件写,点H文件声明函数,在main函数文件引入头文件直接调用:

3)注意事项

4)预编译

5)延时函数文件

6)头文件延迟

7)主函数文件程序入口:

8)数码管模块,用到的头文件要引用:

9)数码管模块头文件

10)函数调用

11)显示

5-2 、LCD1602调试工具-------

1)调试工具原理图

2)模块化代码,下完放到自己工程目录下:



3)将下好的两个文件添加到工程:

4)文件主要内容如下:

5)main函数调用:

6)显示其他管脚冲突,所有会一起显示:

7)其他函数的调用及功能,可以设置显示位置和范围:

8)需要用到延迟函数:可以直接将前面模块化文件复制到工程目录下,添加进来引用即可;

9)娱乐:小计时器

  1. #include <REGX52.H>
  2. #include "LCD1602.h"
  3. #include "Delay.h"
  4. int Result=0;
  5. void main()
  6. {
  7. LCD_Init();//初始化
  8. while(1)
  9. {
  10. Result++;
  11. Delay(1000);
  12. LCD_ShowNum(1,1,Result,3);
  13. }//计时器



6-1.矩阵键盘

1)基础介绍:

P14-P17给0就代表扫描,其他给1(没选中),一次只能扫描一行;P10-P13给0表示按下,给1表示没按下;(逐列扫描)

2.代码实现:

①:创建工程并把“Delay”与“LCD1602”的模块加入此工程中。
②:编写MatrixKey(矩阵)代码
  1. #include <REGX52.H>
  2. #include "Delay.h"
  3. unsigned char MatrixKey()
  4. {
  5. unsigned char KeyNumber=0;
  6. P1=0xff; //按列扫描
  7. P1_3=0; //控制扫描的列
  8. if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=1;} //while判断何时松手
  9. if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=5;}
  10. if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=9;}
  11. if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=13;}
  12. P1=0xff;
  13. P1_2=0;
  14. if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=2;}
  15. if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=6;}
  16. if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=10;}
  17. if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=14;}
  18. P1=0xff;
  19. P1_1=0;
  20. if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=3;}
  21. if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=7;}
  22. if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=11;}
  23. if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=15;}
  24. P1=0xff;
  25. P1_0=0;
  26. if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=4;}
  27. if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=8;}
  28. if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=12;}
  29. if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=16;}
  30. return KeyNumber;
  31. }
③.编写主函数
  1. #include <REGX52.H>
  2. #include "Delay.h"
  3. #include "LCD1602.h"
  4. #include "matrixKey.h"
  5. unsigned char KeyNum;
  6. void main()
  7. {
  8. LCD_Init(); //LCD上电初始化
  9. LCD_ShowString(1,1,"Helloworld");
  10. while(1)
  11. {
  12. KeyNum=MatrixKey();
  13. if(KeyNum){
  14. LCD_ShowNum(2,1,KeyNum,2);
  15. }
  16. }
  17. }
④.软件使用小技巧

快速生成常用格式代码:

设置,完成后双击就可以生成了:

6-2.矩阵键盘密码锁

1):把6-1文件全部cv到6-2工程文件中

2):代码实现

  1. #include <REGX52.H>
  2. #include "Delay.h"
  3. #include "LCD1602.h"
  4. #include "matrixKey.h"
  5. unsigned char KeyNum;
  6. unsigned int Password,count;
  7. void main()
  8. {
  9. LCD_Init();
  10. LCD_ShowString(1,1,"Helloworld");
  11. while(1)
  12. {
  13. KeyNum=MatrixKey();
  14. if(KeyNum){
  15. if(KeyNum<=10){ //s1~s10按下密码
  16. if(count<4){
  17. Password*=10;
  18. Password+=KeyNum%10; //这两句是用来实现四位密码的显示
  19. count++; //计数 防止按下的密码数超过四位
  20. }
  21. LCD_ShowNum(2,1,Password,4);//更新显示
  22. }
  23. if(KeyNum==11){ //s11设置为确认键
  24. if(Password==2345){
  25. LCD_ShowString(1,14,"OK ");
  26. Password=0; //密码清零
  27. count=0; //计数清零
  28. LCD_ShowNum(2,1,Password,4);//更新显示
  29. }
  30. else {LCD_ShowString(1,14,"ERR");
  31. Password=0; //密码清零
  32. count=0; //计数清零
  33. LCD_ShowNum(2,1,Password,4);//更新显示
  34. }
  35. }
  36. if(KeyNum==12){ //定义S12为取消键
  37. Password=0;
  38. count=0;
  39. LCD_ShowNum(2,1,Password,4);
  40. }
  41. }
  42. }
  43. }

7.定时器介绍

1)介绍,Delay前面CPU是一直在等的,用定时器在Delay时可以去检测按键,提高CPU利用率:

2)模式1最常用:

3)模式:时钟--计数最大65535(计数系统TL0\TH0:每来一个脉冲+1方法计数)-TF0(标志位,到最大了回到0)-中断:

4)非门与门图形为控制部分(TR0是否启动暂定)

5)定时器部分:

6)时钟可以由系统提供(上图,晶振),也可以由外部T0P提供(如下图引脚)

7)C/T,给1连上面为控制器,给0连接下面为定时器(如下图):

8)中断系统:

9)中断资源

10)定时器和中断系统

11)定时器相关寄存器

7-1.独立按键控制流水灯的模式

1.TMOD寄存器工作模式,定时器0配置使用(不可位寻址,只能整体赋值)

2.定时器模式1:门控端给0,就是tr0单独控制:C/T,T这里有一横表示低电平有效,就给0是用T(定时器模式),给1用C(控制器模式),M1,M0工作模式选择

3.TCON控制寄存器(可位寻址,可以单独每一位赋值)

中断溢出标志位:

TF=0(等于1产生中断);

TR0=1(定时器是否开启,给1开始,电机开始工作);

IE0、IT0:控制外部中断引脚,可以不配置

4.定时器配置  TH0\TL0

TH0\TL0 分开储存

代码优化,TMOD问题(不可位寻址)配置两个的时候,后面的会把前面的覆盖;

因此,可采用“与或”法设定TMOD

 

5.中断配置T0-->ET0=1--EA=1,PT0=0

6.定时器配置完成,模块化编程

  1. #include <REGX52.H>
  2. void Timer0_Init(void) //1ms@11.0592MHz
  3. {
  4. TMOD &= 0xF0; //设置定时器模式
  5. TMOD |= 0x01; //设置定时器模式
  6. TL0 = 0x66; //设置定时器初值
  7. TH0 = 0xFC; //设置定时器初值
  8. TF0 = 0; //清除TF0标志
  9. TR0 = 1; //定时器0开始计时
  10. ET0=1; //下面三行为中断的配置
  11. EA=1;
  12. PT0=0;
  13. }

 7.定时器中断函数模板

  1. void Timer0_Routine() interrupt 1 //中断函数
  2. {
  3. static unsigned int T0Count;
  4. TL0 = 0x66; //设置定时器初值
  5. TH0 = 0xFC; //设置定时器初值
  6. T0Count++;
  7. if(T0Count>=1000) //每隔1s
  8. {
  9. T0Count=0;
  10. }
  11. }

8.独立按键模块

  1. #include <REGX52.H>
  2. #include "Delay.h"
  3. unsigned char Key()
  4. {
  5. unsigned char KeyNumber=0;
  6. if(P3_1==0){Delay(20);while(P3_1==0);Delay(20);KeyNumber=1;}
  7. if(P3_0==0){Delay(20);while(P3_0==0);Delay(20);KeyNumber=2;}
  8. if(P3_2==0){Delay(20);while(P3_2==0);Delay(20);KeyNumber=3;}
  9. if(P3_3==0){Delay(20);while(P3_3==0);Delay(20);KeyNumber=4;}
  10. return KeyNumber;
  11. }

9.主函数

  1. #include <REGX52.H>
  2. #include "Timer0.h"
  3. #include "key.h"
  4. #include <INTRINS.H>
  5. unsigned char KeyNumber,LEDmode;
  6. void main()
  7. {
  8. P2=0xfe; //与INTRINS.H中的循环左右移函数共同实现流水灯
  9. Timer0_Init(); //上电初始化
  10. while(1)
  11. {
  12. KeyNumber=Key();
  13. if(KeyNumber)
  14. {
  15. if(KeyNumber==1)
  16. {
  17. LEDmode++;
  18. if(LEDmode>=2) LEDmode=0;
  19. }
  20. }
  21. }
  22. }
  23. void Timer0_Routine() interrupt 1 //中断函数
  24. {
  25. static unsigned int T0Count;
  26. TH0=64535/256;
  27. TL0=64535%256;
  28. T0Count++;
  29. if(T0Count>=1000) //每隔1s
  30. {
  31. T0Count=0;
  32. if(LEDmode==0) P2=_crol_(P2,1);
  33. if(LEDmode==1) P2=_cror_(P2,1);
  34. }
  35. }

7-2.时钟 

1.把LCD1602液晶显示、延迟、定时器、的代码复制到工程目录下,导入;

2.主函数包含其他模块头文件并初始化;

3.复制定时器中断函数到main函数下:

4.定义变量,秒计数、分、小时并显示:

5.代码综合

  1. #include <REGX52.H>
  2. #include "Delay.h"
  3. #include "LCD1602.h"
  4. #include "Timer0.h"
  5. unsigned char Sec,Min,Hour;
  6. void main()
  7. {
  8. LCD_Init();//³õʼ»¯
  9. Timer0_Init();
  10. LCD_ShowString(1,1,"CLOCK:");
  11. LCD_ShowString(2,1," : :");
  12. while(1)
  13. {
  14. LCD_ShowNum(2,1,Hour,2);
  15. LCD_ShowNum(2,4,Min,2);
  16. LCD_ShowNum(2,7,Sec,2);
  17. }
  18. }
  19. void Timer0_Routine() interrupt 1
  20. {
  21. static unsigned int T0Count;
  22. TL0 = 0x66;
  23. TH0 = 0xFC;
  24. T0Count++;
  25. if(T0Count>=1000)
  26. {
  27. T0Count=0;
  28. Sec++;
  29. if(Sec>=60)
  30. {
  31. Sec=0;
  32. Min++;
  33. if(Min>=60)
  34. {
  35. Min=0;
  36. Hour++;
  37. if(Hour>=24)
  38. {
  39. Hour=0;}
  40. }
  41. }
  42. }
  43. }

8-1串口介绍

1)介绍

2)向单片机发送数据(下面框),返回(上框)

3)DB9串口传输数据(注意使用的电压是否一致)使用RS232或RS485电平

4)知识点

①硬件电路

注:最少需要三根线实现双向通信:TXD,RXD,GND。VCC不一定需要,可独立供电。

②电平标准 

 

 差分信号优点:传送距离远(1km+)   TTL与RS232(10m)

③常见通信接口比较

 CAN总线:常用于汽车领域,因为使用的是差分信号传输,传输距离远、稳定。

通信方式的相关术语:

④51单片机里的UART串口 

 

 

   中间部分用来控制波特率,依靠定时器来约定速率,T1的溢出率通过分频后来控制收发器的采样时间。

  SBUF:收发数据后,会产生相应的TI(发送中断)/RI(接收中断),继而进入中断函数,进行相应的中断函数内部的操作。

 配置ES、EA,PS此时不需要配置,因为只有一个中断,不需要进行优先级判断。

 配置好SCON和PCON,读SBUF,配置定时器T1,打开EA和ES,即串口可以开始工作。

8-2.实际配置串口 

1)将延迟函数复制过来并导入工程里面;

2)配置串口控制寄存器,配置模式1最常用,REN允许接收给1,不允许接收给0(也可以给1外面不给发就行);

3)TI、RI发送完置1(硬件只负责),但必须软件复位置0;

 故SCON=0x40

PCON

4)配置定时器

这里定时器1,没有定时器0,所有要把高位修改(不影响高低位配置用“”& |“”这两个方式)

选择8位自动重载模式

5)可以直接用STC-isp来配置串口 

系统频率根据板子选择,波特率4800,波特率发生器选择8位自动重载,时钟12T

6)发送数据的函数

7)发送单项数据

8)模块化

9)数据显示模式 

8-3.串口实例实现 

1)每一秒发送一个递增的数字

 2)电脑通过串口控制LED灯,并且返回电脑读入的数据

1.需要打开串口的中断功能

SCON = 0x50;  EA=1;ES=1; 

注:要分清这里是禁止了定时器1的中断功能,只是让它的溢出率去开启串口收发的功能,中断的产生是由于串口收发数据产生的中断。

2.编写串口中断函数

串口中断函数模板:

  1. void UART_Routine() interrupt 4
  2. {
  3. if(RI==1)
  4. {
  5. RI=0; //复位清0
  6. }
  7. }

3.整体代码

9-1LED点阵屏

1.介绍:


2.显示原理

3.相关图

①:led矩阵图——经测试,P0控制列,D控制行

②:开发版引脚对应关系:

③:74HC595 

 加-,低电平有效

OE: 使能,output enable,接低电平工作,高电平不工作。

RCLK:寄存器时钟,register clock

SRCLR:串行清零端

SRCLK:串行时钟 

SER:串行数据

运行方式:类似队列

④:总结:使用步骤:进行行列的选择——列由P0口控制,因此给P0赋值就能控制列。行需通过74HC595来间接控制。

9-2.点阵屏的驱动代码 

示例代码1-点阵屏显示图形

1)sfr与sbit,可位寻址与不可位寻址。

2) 进行位声明,方便操作。

3)编写子函数——控制74HC595把字节数据输出给D的8个引脚。

4) 编写LED矩阵显示的子函数

5)  自己设计图形,调用函数输入数据,即可显示;

示例代码2-点阵屏显示动画

1)把示例1的代码模块化
2)定义要显示的动画的数组,并初步检测是否显示正常

3)实现动态显示

 

上述代码实现的是流水式的动画,也可以改变offset去实现逐帧动画。

4)定义的Animation是放在RAM中的,有空间限制,浪费RAM空间。所以可以加个code,把它放在flash里,但此时不可在主函数里改变数组内容。

10-1 DS1302实时时钟介绍

1.介绍

定时器计时的缺点:1.精度不高  2.消耗单片机的CPU  3.断电不能继续计时

而时钟芯片精度高,且有备用电源,掉电可以用备用电源继续计时。

2.时钟芯片的引脚定义和应用电路

①:两种封装模式:直插封装和贴片封装  ②:分为三部分:电源部分,时钟部分,数据交互部分

3. 内部结构框图

4.内部寄存器的定义

秒,分,时,日,月,年,星期,年,wp(write protect,写保护),涓流充电

前两列的为 :命令字——

5. 时序图

前八为指定读还是写,后八位读出或者写入指定的数据。

上升沿写入,下降沿读出。

6.BCD码

10-2 实例代码

1.时钟

1)创建工程,cvLCD1602的文件,并开始模块化编写DS1302的模块

2) DS1302模块的子函数编写

3)初步测试 

结果会发现,数字到9后会直接到16……,这是因为寄存器数据是以BCD码存储的,只需将Second改为Second/16*10+Second%16,就可以转化成十进制,正常显示。

4) 定义一下各个寄存器的地址,方便操作;再定义一个存储时间的数组;
5) 编写DS1302的另外两个子函数——①:设定时间 ②:读出时间     并声明为外部可调用的函数和数组;

6) 编写主函数,调用函数在LCD显示屏上显示时间

2.功能化时钟——可以用独立按键设置要显示的时间

1)需要用到的模块

DS1302模块,LCD1602模块,独立按键key模块,定时器Timer0模块,延时函数模块;

2)编写主函数

主要分为四大部分:时钟模式选择,显示时间,设置时间(选择设置的时间位置,调整时间大小,利用定时器中断去实现选择的时间的位置的动态显示),更新显示新的时钟。

  1. #include <REGX52.H>
  2. #include "LCD1602.h"
  3. #include "DS1302.h"
  4. #include "Key.h"
  5. #include "Timer0.h"
  6. #include "Delay.h"
  7. unsigned char KeyNum,MODE,TimeSetSelect,TimeFlashFlag;
  8. /**
  9. * @brief 显示时间
  10. * @param 无
  11. * @retval 无
  12. */
  13. void TimeShow(void)
  14. {
  15. DS1302_ReadTime();
  16. LCD_ShowNum(1,1,DS1302_Time[0],2);
  17. LCD_ShowNum(1,4,DS1302_Time[1],2);
  18. LCD_ShowNum(1,7,DS1302_Time[2],2);
  19. LCD_ShowNum(2,1,DS1302_Time[3],2);
  20. LCD_ShowNum(2,4,DS1302_Time[4],2);
  21. LCD_ShowNum(2,7,DS1302_Time[5],2);
  22. }
  23. /**
  24. * @brief 设置时间,按键2选择设置的时间位置,按键3让时间++,按键4让时间--
  25. * @param 无
  26. * @retval 无
  27. */
  28. void TimeSet(void)
  29. {
  30. if(KeyNum==2)
  31. {
  32. TimeSetSelect++;
  33. TimeSetSelect %= 6;
  34. }
  35. if(KeyNum==3)
  36. {
  37. DS1302_Time[TimeSetSelect]++;
  38. if(DS1302_Time[0]>99){DS1302_Time[0]=0;}
  39. if(DS1302_Time[1]>12){DS1302_Time[1]=1;}
  40. if(DS1302_Time[1]==1 || DS1302_Time[1]==3 ||DS1302_Time[1]==5 ||DS1302_Time[1]==7
  41. || DS1302_Time[1]==8 || DS1302_Time[1]==10 ||DS1302_Time[1]==12)
  42. {
  43. if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
  44. }else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 ||DS1302_Time[1]==5 ||
  45. DS1302_Time[1]==9 || DS1302_Time[1]==11)
  46. {
  47. if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
  48. }else if(DS1302_Time[1]==2)
  49. { if(DS1302_Time[0]%100!=0&&DS1302_Time[0]%4==0||DS1302_Time[0]%100==0&&DS1302_Time[0]%400==0)
  50. {
  51. if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
  52. }else if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
  53. }
  54. if(DS1302_Time[3]>23){DS1302_Time[3]=0;}
  55. if(DS1302_Time[4]>59){DS1302_Time[4]=0;}
  56. if(DS1302_Time[5]>59){DS1302_Time[5]=0;}
  57. }
  58. if(KeyNum==4)
  59. {
  60. DS1302_Time[TimeSetSelect]--;
  61. if(DS1302_Time[0]<0){DS1302_Time[0]=99;}
  62. if(DS1302_Time[1]<0){DS1302_Time[1]=12;}
  63. if(DS1302_Time[1]==1 || DS1302_Time[1]==3 ||DS1302_Time[1]==5 || DS1302_Time[1]==7
  64. || DS1302_Time[1]==8 || DS1302_Time[1]==10 ||DS1302_Time[1]==12)
  65. {
  66. if(DS1302_Time[2]<1){DS1302_Time[2]=31;}
  67. if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
  68. }else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 ||DS1302_Time[1]==5 || DS1302_Time[1]==9
  69. || DS1302_Time[1]==11)
  70. {
  71. if(DS1302_Time[2]<1){DS1302_Time[2]=30;}
  72. if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
  73. }else if(DS1302_Time[1]==2)
  74. {
  75. if(DS1302_Time[0]%100!=0&&DS1302_Time[0]%4==0||DS1302_Time[0]%100==0&&DS1302_Time[0]%400==0)
  76. {
  77. if(DS1302_Time[2]<1){DS1302_Time[2]=29;}
  78. if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
  79. }else
  80. {
  81. if(DS1302_Time[2]<1){DS1302_Time[2]=28;}
  82. if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
  83. }
  84. }
  85. if(DS1302_Time[3]<0){DS1302_Time[3]=23;}
  86. if(DS1302_Time[4]<0){DS1302_Time[4]=59;}
  87. if(DS1302_Time[5]<0){DS1302_Time[5]=59;}
  88. }
  89. //动态显示选择位
  90. if(TimeSetSelect==0 && TimeFlashFlag==1 ){LCD_ShowString(1,1," ");}
  91. else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
  92. if(TimeSetSelect==1 && TimeFlashFlag==1 ){LCD_ShowString(1,4," ");}
  93. else {LCD_ShowNum(1,4,DS1302_Time[1],2);}
  94. if(TimeSetSelect==2 && TimeFlashFlag==1 ){LCD_ShowString(1,7," ");}
  95. else {LCD_ShowNum(1,7,DS1302_Time[2],2);}
  96. if(TimeSetSelect==3 && TimeFlashFlag==1 ){LCD_ShowString(2,1," ");}
  97. else {LCD_ShowNum(2,1,DS1302_Time[3],2);}
  98. if(TimeSetSelect==4 && TimeFlashFlag==1 ){LCD_ShowString(2,4," ");}
  99. else {LCD_ShowNum(2,4,DS1302_Time[4],2);}
  100. if(TimeSetSelect==5 && TimeFlashFlag==1 ){LCD_ShowString(2,7," ");}
  101. else {LCD_ShowNum(2,7,DS1302_Time[5],2);}
  102. }
  103. void main()
  104. {
  105. Timer0_Init();
  106. LCD_Init();
  107. DS1302_Init();
  108. DS1302_WriteByte(0x8e,0x00);
  109. LCD_ShowString(1,1," - - ");
  110. LCD_ShowString(2,1," : : ");
  111. DS1302_SetTime();
  112. while(1)
  113. {
  114. //通过按键1控制时钟的模式,模式0为显示时钟,模式1为设置时间
  115. KeyNum=key();
  116. if(KeyNum==1)
  117. {
  118. if(MODE==0){MODE=1;TimeSetSelect=0;}
  119. else if(MODE==1) {MODE=0;DS1302_SetTime();}
  120. }
  121. switch(MODE)
  122. {
  123. case 0:TimeShow();break;
  124. case 1:TimeSet();break;
  125. }
  126. }
  127. }
  128. //利用定时器中断来动态显示选择的时间位置
  129. void Timer0_Routine() interrupt 1
  130. {
  131. static unsigned int T0Count;
  132. TL0 = 0x66;
  133. TH0 = 0xFC;
  134. T0Count++;
  135. if(T0Count>=1000)
  136. {
  137. T0Count=0;
  138. TimeFlashFlag=!TimeFlashFlag;
  139. }
  140. }

11.蜂鸣器

1)介绍

 NPN:高电平导通     PNP:低电平导通

由P15的高低电平取反控制BZ的高低电平

 2)乐理知识

①:介绍C1-C2升高8度。往右升高,往左降低:相邻半音黑白,1对应中央C1,i表示升即C2部分,降低8度下面加一个点,降低2个8度,下面加两个点(白键);相邻两个键为半音#:升高半音,b:降低半音。

②:-表示时长,如图中的665-,其中5占两个节拍,

③:音符

 一般以四分音符为一个基准

④:音符频率对照

⑤:以a440hz为基准,到另一个a,中间等比12频分,
 ⑥:单片机晶振芯片每秒震荡的次数称为时钟频率,震荡一次所需时间称为振荡周期。12个震荡周期是一个机器周期,机器频率=晶振频率/12,计时周期=1/机器周期,即每过一个计时周期,定时器计数+1;
给定时器的TL和TH赋重装载值,使定时器每过所需周期的一半时计数+1(因为需要给震荡信号,故计数周期=原周期的二分之一),从而使蜂鸣器发出对应频率的声音。

3)实例代码

1.蜂鸣器鸣响
①:蜂鸣器模块函数

②:主函数——按下独立按键,在数码管上显示按的第几个键,并且蜂鸣器鸣响。

 

2.蜂鸣器演奏音乐—有时间再补

12.AT24C02,I2C总线

1.存储器介绍

RAM:存储速度快,但是掉电丢失   ROM:掉电不丢失,但是速度慢。

2.AT24C02介绍

引脚及应用电路

 

3. I2C总线

 

I2C通信 时序结构:

 

 

 

 

 

 

4.示例程序

1——用独立按键设置想要写入的数据,并读出显示在LCD上,断电不丢失。
①:程序编写整体思路:编写I2C模块,AT24C02模块,main模块

②:编写I2C模块

先进行位声明,再分别编写子函数,开始,结束,发送和接受一个字节,发送和接收应答位

 

③:编写AT24C02模块

先定义一下AT24C02的地址,写是0xA0,读是0xA1;

再用I2C里的函数去编写写入一个字节和读出一个字节的函数

 

④:编写main模块

用独立按键控制想要写入的数据,并读出显示在LCD上,断电不丢失。

 

2——秒表(用定时器扫描数码管)
①:先创建新的工程,包含所需的模块——独立按键,数码管,延迟函数,定时器,AT,I2C
②:改造独立按键模块——用定时器扫描独立按键

这样就不用像原来一样在检测时停在while死循环里,影响主函数进程

 

③:改造数码管模块——改造成用定时器扫描数码管

先在main模块的中断函数里加上一个计数定时器的T0Count2,这样每隔一段时间就会调用一下NixieLoop函数,而NixieLoop函数会不断扫描并在数码管上显示每一位数字。NixieSetBuf函数会根据输入的位置和数字改变Buf数组里的值,继而让NixieLoop扫描显示对应的值。

 

④: 编写主函数
  1. #include <REGX52.H>
  2. #include "Timer0.h"
  3. #include "key.h"
  4. #include "Delay.h"
  5. #include "Nixie.h"
  6. #include "AT24C02.h"
  7. unsigned char KeyNum;
  8. unsigned char Min,Sec,Minisec;
  9. unsigned char Runflag;
  10. void main()
  11. {
  12. Timer0_Init();
  13. while(1)
  14. {
  15. KeyNum=Key();
  16. if(KeyNum==1) //按键1控制开始和停止计时
  17. {
  18. Runflag=!Runflag;
  19. }
  20. if(KeyNum==2) //按键2清0
  21. {
  22. Min=0;
  23. Sec=0;
  24. Minisec=0;
  25. }
  26. if(KeyNum==3) //按键3将数据写入AT
  27. {
  28. AT24C02_WriteByte(0,Min);
  29. Delay(5);
  30. AT24C02_WriteByte(1,Sec);
  31. Delay(5);
  32. AT24C02_WriteByte(2,Minisec);
  33. Delay(5);
  34. }
  35. if(KeyNum==4) //按键4读出数据
  36. {
  37. Min=AT24C02_ReadByte(0);
  38. Sec=AT24C02_ReadByte(1);
  39. Minisec=AT24C02_ReadByte(2);
  40. }
  41. Nixie_SetBuf(1,Min/10);
  42. Nixie_SetBuf(2,Min%10);
  43. Nixie_SetBuf(3,11);
  44. Nixie_SetBuf(4,Sec/10);
  45. Nixie_SetBuf(5,Sec%10);
  46. Nixie_SetBuf(6,11);
  47. Nixie_SetBuf(7,Minisec/10);
  48. Nixie_SetBuf(8,Minisec%10);
  49. }
  50. }
  51. //计时中断函数
  52. void Sec_Loop()
  53. {
  54. if(Runflag)
  55. {
  56. Minisec++;
  57. if(Minisec>=100)
  58. {
  59. Minisec=0;
  60. Sec++;
  61. if(Sec>=60)
  62. {
  63. Sec=0;
  64. Min++;
  65. if(Min>=60)
  66. {
  67. Min=0;
  68. }
  69. }
  70. }
  71. }
  72. }
  73. void Timer0_Routine() interrupt 1 //ÖжϺ¯Êý
  74. {
  75. static unsigned int T0Count1,T0Count2,T0Count3;
  76. TL0 = 0x66; //ÉèÖö¨Ê±Æ÷³õÖµ
  77. TH0 = 0xFC; //ÉèÖö¨Ê±Æ÷³õÖµ
  78. T0Count1++;
  79. if(T0Count1>=20)
  80. {
  81. T0Count1=0;
  82. Key_Loop();
  83. }
  84. T0Count2++;
  85. if(T0Count2>=2)
  86. {
  87. T0Count2=0;
  88. Nixie_Loop();
  89. }
  90. T0Count3++;
  91. if(T0Count3>=10)
  92. {
  93. T0Count3=0;
  94. Sec_Loop();
  95. }
  96. }

13.1 DS18B20——温度传感器

1.介绍

 2.引脚及应用电路

3.内部结构框图

最后一列分别是:温度传感器(内部的模拟温度传感器,能进行数据的转换),存储高报警位的EEPROM,存储低报警位的EEPROM,调节精度,校验码

前两个字节分别存储数据的低位和高位

总体思路:先发送温度转换的指令,再发送读数据的指令。因此,我们接下来要学习,如何通过单总线来发送数据和接收数据。

4.单总线介绍

①:介绍

  ②:时序结构

 

5.DS18B20操作流程

 本节需要的指令SKIP ROM,CONVERT T:让温度转换器进行数据的转换,READ SCRATCHPAD:读暂存器,依次读出每一个字节。

 

 

包括符号,整数部分,小数部分,其中负数以补码形式存储

13.2示例代码

1-DS18B20温度读取

①:整体思路——先写OneWire模块(包括初始化,发送一位,读出一位,发送一个字节,接收一个字节),再写DS18B20模块(包括温度转换指令函数,读取数据函数),最后编写main函数。

②:OneWire模块编写

③:DS18B20模块编写

④:main函数

实例代码2-DS18B20温度报警器

①:要用到的模块:AT24C02,Delay,DS18B20,LCD1602,OneWire,I2C,key,Timer0
②:main函数
  1. #include <REGX52.H>
  2. #include "DS18B20.h"
  3. #include "key.h"
  4. #include "LCD1602.h"
  5. #include "Delay.h"
  6. #include "AT24C02.h"
  7. #include "Timer0.h"
  8. float T,TShow;
  9. char TLow,THigh;
  10. unsigned char KeyNum;
  11. void main()
  12. {
  13. DS18B20_ConverT();
  14. Delay(1000);
  15. THigh=AT24C02_ReadByte(0);
  16. TLow=AT24C02_ReadByte(1);
  17. if(THigh>125 || TLow<-55 || THigh<=TLow)
  18. {
  19. THigh=20;
  20. TLow=15;
  21. }
  22. LCD_Init();
  23. LCD_ShowString(1,1,"T:");
  24. LCD_ShowString(2,1,"TH:");
  25. LCD_ShowString(2,9,"TL:");
  26. LCD_ShowSignedNum(2,4,THigh,3);
  27. LCD_ShowSignedNum(2,12,TLow,3);
  28. Timer0_Init();
  29. while(1)
  30. {
  31. KeyNum=Key();
  32. /*温度读取及显示*/
  33. DS18B20_ConverT();
  34. T=DS18B20_ReadT();
  35. if(T<0)
  36. {
  37. LCD_ShowChar(1,3,'-');
  38. TShow=-T;
  39. }
  40. else
  41. {
  42. LCD_ShowChar(1,3,'+');
  43. TShow=T;
  44. }
  45. LCD_ShowNum(1,4,TShow,3);
  46. LCD_ShowChar(1,7,'.');
  47. LCD_ShowNum(1,8,(unsigned long)(TShow*100)%100,2);
  48. /*阈值判断及显示*/
  49. if(KeyNum)
  50. {
  51. if(KeyNum==1)
  52. {
  53. THigh++;
  54. if(THigh>125){THigh=125;}
  55. }
  56. if(KeyNum==2)
  57. {
  58. THigh--;
  59. if(THigh<=TLow){THigh++;}
  60. }
  61. if(KeyNum==3)
  62. {
  63. TLow++;
  64. if(TLow>=THigh){TLow--;}
  65. }
  66. if(KeyNum==4)
  67. {
  68. TLow--;
  69. if(TLow<-55){TLow=-55;}
  70. }
  71. LCD_ShowSignedNum(2,4,THigh,3);
  72. LCD_ShowSignedNum(2,12,TLow,3);
  73. AT24C02_WriteByte(0,THigh);
  74. Delay(5);
  75. AT24C02_WriteByte(1,TLow);
  76. Delay(5);
  77. }
  78. if(T>THigh)
  79. {
  80. LCD_ShowString(1,13,"OV:H");
  81. }
  82. else if(T<TLow)
  83. {
  84. LCD_ShowString(1,13,"OV:L");
  85. }
  86. else {LCD_ShowString(1,13,"OV: ");}
  87. }
  88. }
  89. void Timer0_Routine() interrupt 1 //中断函数
  90. {
  91. static unsigned int T0Count;
  92. TL0 = 0x66;
  93. TH0 = 0xFC;
  94. T0Count++;
  95. if(T0Count>=20)
  96. {
  97. T0Count=0;
  98. Key_Loop();
  99. }
  100. }

为防止定时器扫描按键时会打断OneWire的传送与接收

可以在OneWire里先关闭EA,再打开EA。

14-1 LCD1602液晶显示屏

1.介绍

 2.引脚及应用电路

   3.内部结构框图

 4.存储器结构

 5.时序结构

 6.LCD1602指令集

 

7.LCD1602操作流程

14-2 功能代码编写

之后可以在main函数里调用流动的指令,实现流动字幕

15-1 直流电机

1.介绍

2.电机驱动电路

大功率器件直接驱动:只能使电机朝着一个方向转,不具备调换电机正反方向的功能。

H桥驱动:可以控制电机正反转。

3.电机调速——PWM介绍

给电全速转,不给电不转,利用惯性,可以设置一个在周期内高电平与低电平的时间,使之呈现中间速度

 

15-2 示例代码

1.LED呼吸灯——用来理解PWM

2.电机调速 

①:创建新工程,需要用到的模块:Delay,Timer0,Nixie,key
②:main函数编写
  1. #include <REGX52.H>
  2. #include "Delay.h"
  3. #include "key.h"
  4. #include "Nixie.h"
  5. #include "Timer0.h"
  6. sbit Motor=P1^0; //位声明
  7. unsigned char Counter,Compare;
  8. unsigned char KeyNum,Speed;
  9. void main()
  10. {
  11. Timer0_Init();
  12. while(1)
  13. {
  14. KeyNum=Key();
  15. if(KeyNum==1)
  16. {
  17. Speed++;
  18. Speed%=4;
  19. if(Speed==0){Compare=0;}
  20. if(Speed==1){Compare=50;}
  21. if(Speed==2){Compare=75;}
  22. if(Speed==3){Compare=100;}
  23. }
  24. Nixie(1,Speed);
  25. }
  26. }
  27. void Timer0_Routine() interrupt 1
  28. {
  29. TL0 = 0xA4;
  30. TH0 = 0xFF;
  31. Counter++;
  32. if(Counter>=100) {Counter=0;}
  33. if(Counter<Compare)
  34. {
  35. Motor=1;
  36. }
  37. else
  38. {
  39. Motor=0;
  40. }
  41. }

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

闽ICP备14008679号