当前位置:   article > 正文

C语言:51单片机看这一篇就够了_51单片机c语言

51单片机c语言

前言

本篇文章属于学习笔记,来源于B站教学视频,相关代码工程请从源地址自行下载。这位Up讲解得很好,适合同学们一起学习,在这里推荐给大家。本文为个人学习笔记,只能做参考,细节方面建议观看视频,肯定受益匪浅。

51单片机入门教程-2020版 程序全程纯手打 从零开始入门_哔哩哔哩_bilibilihttps://www.bilibili.com/video/BV1Mb411e7re?p=1

一、环境搭建

1、开发软件Keil5C51

2、烧录软件stc-isp

3、普中51单片机开发板

二、单片机介绍

1、单片机(Micro Controller Unit ,即MCU)

2、本篇所使用的单片机为STC89C52单片机

所属系列为8051,即51单片机。8051最开始指在80年代生产的8051内核的单片机。后延伸为只要是8051内核的单片机,都统称为51单片机。

3、51单片机命名规则

4、51单片机结构

三、单片机实操一:点亮一个LED

1、打开STC-ISP添加芯片包到开发环境

2、创建工程

 

3、创建文件

 4、LED的硬件原理图

在下面的图中,我们可以看到LED最终连上了单片机的管脚。而单片机需要通过CPU控制寄存器的值,进而通过驱动器加大控制力度,由控制电路输出高低电平(对应寄存器1/0)。因此,程序需要在对应的寄存器上写1或0,即可控制LED的亮灭。

5、编写程序

根据硬件原理图和寄存器定义,来对操作寄存器地址,实现灯的点亮

  1. #include <STC89C5xRC.H>
  2. void main()
  3. {
  4. P2 = 0xFE; //1111 1110
  5. }

6、实验过程与代码解释

(1)点击F7编译单个文件,编译失败,给出P2不认识的提示

(2)添加H文件,继续编译,编译通过 ,说明头文件定义了P2

(3)为什么控制的是P2?因为硬件原理图以及头文件的定义,拉低电平即可点亮一盏灯!

7、生成文件,下载到板子上,重新上电之后可运行该程序

 

 

 

 

四、单片机实操二:LED闪烁

1、在指定路径下创建工程并添加新文件

2、通过STC-ISP拷贝延时代码

 3、编写程序

加入延时,方便观察灯的闪烁

  1. #include <STC89C5xRC.H>
  2. #include <INTRINS.H>
  3. void Delay500ms() //@12.000MHz
  4. {
  5. unsigned char i, j, k;
  6. _nop_(); //An empty function, from INTRINS.H
  7. i = 4;
  8. j = 205;
  9. k = 187;
  10. do
  11. {
  12. do
  13. {
  14. while (--k);
  15. } while (--j);
  16. } while (--i);
  17. }
  18. void main()
  19. {
  20. P2=0xFE;
  21. Delay500ms();
  22. P2=0xFF;
  23. Delay500ms();
  24. }

4、实验过程

 

五、单片机实操三:LED流水灯

1、在指定路径下创建工程并添加新文件

2、编写程序

让LED循环点亮,像流水一样

  1. #include <STC89C5xRC.H>
  2. #include <INTRINS.H>
  3. void Delay500ms() //@12.000MHz
  4. {
  5. unsigned char i, j, k;
  6. _nop_();
  7. i = 4;
  8. j = 205;
  9. k = 187;
  10. do
  11. {
  12. do
  13. {
  14. while (--k);
  15. } while (--j);
  16. } while (--i);
  17. }
  18. void main()
  19. {
  20. while(1)
  21. {
  22. P2=0xFE; //1111 1110
  23. Delay500ms();
  24. P2=0xFD; //1111 1101
  25. Delay500ms();
  26. P2=0xFB; //1111 1011
  27. Delay500ms();
  28. P2=0xF7; //1111 0111
  29. Delay500ms();
  30. P2=0xEF; //1110 1111
  31. Delay500ms();
  32. P2=0xDF; //1101 1111
  33. Delay500ms();
  34. P2=0xBF; //1011 1111
  35. Delay500ms();
  36. P2=0x7F; //0111 1111
  37. Delay500ms();
  38. }
  39. }

六、单片机实操四:LED流水灯Plus

1、在指定路径下创建工程并添加新文件

2、编写程序

将延时函数参数化。

  1. #include <STC89C5xRC.H>
  2. #include <INTRINS.H>
  3. void Delayxms(unsigned int xms) //@12.000MHz
  4. {
  5. unsigned char i, j;
  6. while(xms)
  7. {
  8. i = 2;
  9. j = 239;
  10. do
  11. {
  12. while (--j);
  13. } while (--i);
  14. xms--; //xms=xms-1
  15. }
  16. }
  17. void main()
  18. {
  19. while(1)
  20. {
  21. P2=0xFE; //1111 1110
  22. Delayxms(500);
  23. P2=0xFD; //1111 1101
  24. Delayxms(500);
  25. P2=0xFB; //1111 1011
  26. Delayxms(500);
  27. P2=0xF7; //1111 0111
  28. Delayxms(500);
  29. P2=0xEF; //1110 1111
  30. Delayxms(500);
  31. P2=0xDF; //1101 1111
  32. Delayxms(500);
  33. P2=0xBF; //1011 1111
  34. Delayxms(500);
  35. P2=0x7F; //0111 1111
  36. Delayxms(500);
  37. }
  38. }

3、51单片机的数据类型

我们在上面的程序中使用到了数据类型unsigned int xms,即xms在单片机中能表示0~65535

七、单片机实操五:独立按键控制LED灯亮灭

1、在指定路径下创建工程并添加新文件

2、独立按键原理图

3、编写程序

按下按键亮灯,松开按键灭灯。

  1. #include <STC89C5xRC.H>
  2. void main()
  3. {
  4. while(1)
  5. {
  6. if(P30==0)
  7. {
  8. P20=0;
  9. }
  10. else
  11. {
  12. P20=1;
  13. }
  14. }
  15. }

4、51单片机数据运算

上述代码中,我们使用到了==这个运算符,表示等于。

 5、51单片机基本语句

上述程序用到了if语句,通过判断按键的动作来实现灯的亮灭

 6、注意事项

程序写的是P30独立按键,根据原理图,需要按下K2才能点亮第一盏灯。(原理图设计者并没有按照顺序一一对应按键,这是设计者埋下的小坑)

P30是H文件中定义的,如果是其他H文件可能没有或者用另一个变量表示,这个时候需要你自己去写或者更改变量

八、单片机实操六:独立按键控制LED灯状态

1、在指定路径下创建工程并添加新文件

2、编写程序

按键消抖,检测松手,才改变LED灯的状态,使其松开按键后长亮或长灭。

  1. #include <STC89C5xRC.H>
  2. void Delay(unsigned int xms) //@12.000MHz
  3. {
  4. unsigned char i, j;
  5. while(xms)
  6. {
  7. i = 2;
  8. j = 239;
  9. do
  10. {
  11. while (--j);
  12. } while (--i);
  13. xms--;
  14. }
  15. }
  16. void main()
  17. {
  18. while(1)
  19. {
  20. if(P31==0)
  21. {
  22. Delay(20); // Keys away shaking
  23. while(P31==0);
  24. Delay(20); // Detection of let go
  25. P20=~P20;
  26. }
  27. }
  28. }

九、单片机实操七:独立按键控制LED显示二进制

1、在指定路径下创建工程并添加新文件

2、编写程序

通过不停的按下按键,使得灯以二进制的方式,不断往上加一,达到用灯来表示按键次数

  1. #include <STC89C5xRC.H>
  2. void Delay(unsigned int xms) //@12.000MHz
  3. {
  4. unsigned char i, j;
  5. while(xms)
  6. {
  7. i = 2;
  8. j = 239;
  9. do
  10. {
  11. while (--j);
  12. } while (--i);
  13. xms--;
  14. }
  15. }
  16. void main()
  17. {
  18. unsigned char LEDNum=0; // char max num is 255
  19. while(1)
  20. {
  21. if(P31==0)
  22. {
  23. Delay(20);
  24. while(P31==0);
  25. Delay(20);
  26. LEDNum++;
  27. P2=~LEDNum;
  28. }
  29. }
  30. }

十、单片机实操八:独立按键控制LED移位

1、在指定路径下创建工程并添加新文件

2、编写程序

按下P31,往左边移一位;按下P30,往右边移一位,以LED灯来展示。

  1. #include <STC89C5xRC.H>
  2. void Delay(unsigned int xms); // must statement
  3. unsigned char LEDNum; // The global variable
  4. void main()
  5. {
  6. P2=~0x01; //int P2
  7. while(1)
  8. {
  9. if(P31==0)
  10. {
  11. Delay(20);
  12. while(P31==0);
  13. Delay(20);
  14. LEDNum++;
  15. if(LEDNum>=8)
  16. LEDNum=0;
  17. P2=~(0x01<<LEDNum); // 0x01 of P2 need shift to the left LEDNum, and get the not
  18. }
  19. if(P30==0)
  20. {
  21. Delay(20);
  22. while(P30==0);
  23. Delay(20);
  24. if(LEDNum==0)
  25. LEDNum=7;
  26. else
  27. LEDNum--;
  28. P2=~(0x01<<LEDNum);
  29. }
  30. }
  31. }
  32. void Delay(unsigned int xms) //@12.000MHz
  33. {
  34. unsigned char i, j;
  35. while(xms)
  36. {
  37. i = 2;
  38. j = 239;
  39. do
  40. {
  41. while (--j);
  42. } while (--i);
  43. xms--;
  44. }
  45. }

十一、单片机实操九:静态数码管显示

1、在指定路径下创建工程并添加新文件

2、单个数码管引脚定义

数码管的接法,有共阳和共阴之分。共阴时,拉高电压即可点亮。共阳时,拉低电平点亮。

 3、开发板四位一体的数码管引脚定义

4、硬件原理图

138译码器控制数码管使能(使用3个单片机输入控制8个数码管显示),每个数字的一个笔画由双向数据缓存器245(单片机输出能力有限,需要该芯片提高输入能力)控制亮灭。

因此,首先控制P22~P24来选中数码管,然后选中数码管的笔画,最终呈现数据。

5、C51单片机数组

 6、C51单片机子函数

 7、数码管段码表(共阴极电路,共阳极则不一样) 

8、编写程序

  1. #include <STC89C5xRC.H>
  2. unsigned char NixieTable[]={
  3. 0x3f,0x06,0x5b,0x4f,
  4. 0x66,0x6d,0x7d,0x07,
  5. 0x7f,0x6f,0x77,0x7c,
  6. 0x39,0x5e,0x79,0x71, 0x00};
  7. void Nixie(unsigned char Location,Number)
  8. {
  9. switch(Location)
  10. {
  11. case 1:
  12. P24=1;P23=1;P22=1;break;
  13. case 2:
  14. P24=1;P23=1;P22=0;break;
  15. case 3:
  16. P24=1;P23=0;P22=1;break;
  17. case 4:
  18. P24=1;P23=0;P22=0;break;
  19. case 5:
  20. P24=0;P23=1;P22=1;break;
  21. case 6:
  22. P24=0;P23=1;P22=0;break;
  23. case 7:
  24. P24=0;P23=0;P22=1;break;
  25. case 8:
  26. P24=0;P23=0;P22=0;break;
  27. }
  28. P0=NixieTable[Number];
  29. }
  30. void main()
  31. {
  32. // P24=1; //Controls a nixie tube
  33. // P23=0;
  34. // P22=1;
  35. // P0=0x7D; //value is 6
  36. Nixie(2,3);
  37. while(1)
  38. {
  39. }
  40. }

十二、单片机实操十:动态数码管显示

1、在指定路径下创建工程并添加新文件

2、编写程序

动态清零,数字消影,来实现动态数码管显示,这是单片机不断扫描来成像的,将耗费大量CPU资源。因此,硬件电路一般会加1640的芯片来驱动,将大量减少扫描带来的CPU损耗。

  1. #include <STC89C5xRC.H>
  2. unsigned char NixieTable[]={
  3. 0x3f,0x06,0x5b,0x4f,
  4. 0x66,0x6d,0x7d,0x07,
  5. 0x7f,0x6f,0x77,0x7c,
  6. 0x39,0x5e,0x79,0x71, 0x00};
  7. void Delay(unsigned int xms) //@12.000MHz
  8. {
  9. unsigned char i, j;
  10. while(xms)
  11. {
  12. i = 2;
  13. j = 239;
  14. do
  15. {
  16. while (--j);
  17. } while (--i);
  18. xms--;
  19. }
  20. }
  21. void Nixie(unsigned char Location,Number)
  22. {
  23. switch(Location)
  24. {
  25. case 1:
  26. P24=1;P23=1;P22=1;break;
  27. case 2:
  28. P24=1;P23=1;P22=0;break;
  29. case 3:
  30. P24=1;P23=0;P22=1;break;
  31. case 4:
  32. P24=1;P23=0;P22=0;break;
  33. case 5:
  34. P24=0;P23=1;P22=1;break;
  35. case 6:
  36. P24=0;P23=1;P22=0;break;
  37. case 7:
  38. P24=0;P23=0;P22=1;break;
  39. case 8:
  40. P24=0;P23=0;P22=0;break;
  41. }
  42. P0=NixieTable[Number];
  43. Delay(1); // Shadow elimination
  44. P0=0x00; // reset
  45. }
  46. void main()
  47. {
  48. while(1)
  49. {
  50. Nixie(1,1);
  51. Nixie(2,2);
  52. Nixie(3,3);
  53. }
  54. }

十三、单片机实操十一:模块化编程

1、在指定路径下创建工程并添加新文件

2、模块化编程

 3、模块化编程框图

4、模块化编程注意事项

 5、C预编译

 6、增加中文注释

7、编写代码

main.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h" // 使用双引号,优先查询工程目录
  3. #include "Nixie.h"
  4. void main()
  5. {
  6. while(1)
  7. {
  8. Nixie(1,1);
  9. Nixie(2,2);
  10. Nixie(3,3);
  11. }
  12. }

Delay.c

  1. void Delay(unsigned int xms) //@12.000MHz
  2. {
  3. unsigned char i, j;
  4. while(xms)
  5. {
  6. i = 2;
  7. j = 239;
  8. do
  9. {
  10. while (--j);
  11. } while (--i);
  12. xms--;
  13. }
  14. }

Delay.h

  1. #ifndef __DELAY_H__
  2. #define __DELAY_H__
  3. void Delay(unsigned int xms);
  4. #endif

Nixie.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h"
  3. unsigned char NixieTable[]={
  4. 0x3f,0x06,0x5b,0x4f,
  5. 0x66,0x6d,0x7d,0x07,
  6. 0x7f,0x6f,0x77,0x7c,
  7. 0x39,0x5e,0x79,0x71, 0x00};
  8. void Nixie(unsigned char Location,Number)
  9. {
  10. switch(Location)
  11. {
  12. case 1:
  13. P24=1;P23=1;P22=1;break;
  14. case 2:
  15. P24=1;P23=1;P22=0;break;
  16. case 3:
  17. P24=1;P23=0;P22=1;break;
  18. case 4:
  19. P24=1;P23=0;P22=0;break;
  20. case 5:
  21. P24=0;P23=1;P22=1;break;
  22. case 6:
  23. P24=0;P23=1;P22=0;break;
  24. case 7:
  25. P24=0;P23=0;P22=1;break;
  26. case 8:
  27. P24=0;P23=0;P22=0;break;
  28. }
  29. P0=NixieTable[Number];
  30. Delay(1); // Shadow elimination
  31. P0=0x00; // reset
  32. }

Nixie.h

  1. #ifndef __NIXIE_H__
  2. #define __NIXIE_H__
  3. void Nixie(unsigned char Location,Number);
  4. #endif

十四、单片机实操十二:LCD1602调试工具

1、在指定路径下创建工程并添加新文件

2、LCD1602调试工具

3、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "LCD1602.h"
  3. void main()
  4. {
  5. LCD_Init();
  6. LCD_ShowChar(1,1,'B');
  7. LCD_ShowString(1,3,"Hello");
  8. LCD_ShowNum(1,9,123,3);
  9. LCD_ShowSignedNum(1,13,-66,2);
  10. LCD_ShowHexNum(2,1,0xA8,2);
  11. LCD_ShowBinNum(2,4,0xAA,8);
  12. LCD_ShowChar(2,13,'A');
  13. while(1)
  14. {
  15. }
  16. }

 LCD1602.c

  1. #include <STC89C5xRC.H>
  2. //引脚配置:
  3. sbit LCD_RS=P2^6;
  4. sbit LCD_RW=P2^5;
  5. sbit LCD_EN=P2^7;
  6. #define LCD_DataPort P0
  7. //函数定义:
  8. /**
  9. * @brief LCD1602延时函数,12MHz调用可延时1ms
  10. * @param 无
  11. * @retval 无
  12. */
  13. void LCD_Delay()
  14. {
  15. unsigned char i, j;
  16. i = 2;
  17. j = 239;
  18. do
  19. {
  20. while (--j);
  21. } while (--i);
  22. }
  23. /**
  24. * @brief LCD1602写命令
  25. * @param Command 要写入的命令
  26. * @retval 无
  27. */
  28. void LCD_WriteCommand(unsigned char Command)
  29. {
  30. LCD_RS=0;
  31. LCD_RW=0;
  32. LCD_DataPort=Command;
  33. LCD_EN=1;
  34. LCD_Delay();
  35. LCD_EN=0;
  36. LCD_Delay();
  37. }
  38. /**
  39. * @brief LCD1602写数据
  40. * @param Data 要写入的数据
  41. * @retval 无
  42. */
  43. void LCD_WriteData(unsigned char Data)
  44. {
  45. LCD_RS=1;
  46. LCD_RW=0;
  47. LCD_DataPort=Data;
  48. LCD_EN=1;
  49. LCD_Delay();
  50. LCD_EN=0;
  51. LCD_Delay();
  52. }
  53. /**
  54. * @brief LCD1602设置光标位置
  55. * @param Line 行位置,范围:1~2
  56. * @param Column 列位置,范围:1~16
  57. * @retval 无
  58. */
  59. void LCD_SetCursor(unsigned char Line,unsigned char Column)
  60. {
  61. if(Line==1)
  62. {
  63. LCD_WriteCommand(0x80|(Column-1));
  64. }
  65. else if(Line==2)
  66. {
  67. LCD_WriteCommand(0x80|(Column-1+0x40));
  68. }
  69. }
  70. /**
  71. * @brief LCD1602初始化函数
  72. * @param 无
  73. * @retval 无
  74. */
  75. void LCD_Init()
  76. {
  77. LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
  78. LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
  79. LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
  80. LCD_WriteCommand(0x01);//光标复位,清屏
  81. }
  82. /**
  83. * @brief 在LCD1602指定位置上显示一个字符
  84. * @param Line 行位置,范围:1~2
  85. * @param Column 列位置,范围:1~16
  86. * @param Char 要显示的字符
  87. * @retval 无
  88. */
  89. void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
  90. {
  91. LCD_SetCursor(Line,Column);
  92. LCD_WriteData(Char);
  93. }
  94. /**
  95. * @brief 在LCD1602指定位置开始显示所给字符串
  96. * @param Line 起始行位置,范围:1~2
  97. * @param Column 起始列位置,范围:1~16
  98. * @param String 要显示的字符串
  99. * @retval 无
  100. */
  101. void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
  102. {
  103. unsigned char i;
  104. LCD_SetCursor(Line,Column);
  105. for(i=0;String[i]!='\0';i++)
  106. {
  107. LCD_WriteData(String[i]);
  108. }
  109. }
  110. /**
  111. * @brief 返回值=X的Y次方
  112. */
  113. int LCD_Pow(int X,int Y)
  114. {
  115. unsigned char i;
  116. int Result=1;
  117. for(i=0;i<Y;i++)
  118. {
  119. Result*=X;
  120. }
  121. return Result;
  122. }
  123. /**
  124. * @brief 在LCD1602指定位置开始显示所给数字
  125. * @param Line 起始行位置,范围:1~2
  126. * @param Column 起始列位置,范围:1~16
  127. * @param Number 要显示的数字,范围:0~65535
  128. * @param Length 要显示数字的长度,范围:1~5
  129. * @retval 无
  130. */
  131. void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
  132. {
  133. unsigned char i;
  134. LCD_SetCursor(Line,Column);
  135. for(i=Length;i>0;i--)
  136. {
  137. LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
  138. }
  139. }
  140. /**
  141. * @brief 在LCD1602指定位置开始以有符号十进制显示所给数字
  142. * @param Line 起始行位置,范围:1~2
  143. * @param Column 起始列位置,范围:1~16
  144. * @param Number 要显示的数字,范围:-32768~32767
  145. * @param Length 要显示数字的长度,范围:1~5
  146. * @retval 无
  147. */
  148. void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
  149. {
  150. unsigned char i;
  151. unsigned int Number1;
  152. LCD_SetCursor(Line,Column);
  153. if(Number>=0)
  154. {
  155. LCD_WriteData('+');
  156. Number1=Number;
  157. }
  158. else
  159. {
  160. LCD_WriteData('-');
  161. Number1=-Number;
  162. }
  163. for(i=Length;i>0;i--)
  164. {
  165. LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
  166. }
  167. }
  168. /**
  169. * @brief 在LCD1602指定位置开始以十六进制显示所给数字
  170. * @param Line 起始行位置,范围:1~2
  171. * @param Column 起始列位置,范围:1~16
  172. * @param Number 要显示的数字,范围:0~0xFFFF
  173. * @param Length 要显示数字的长度,范围:1~4
  174. * @retval 无
  175. */
  176. void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
  177. {
  178. unsigned char i,SingleNumber;
  179. LCD_SetCursor(Line,Column);
  180. for(i=Length;i>0;i--)
  181. {
  182. SingleNumber=Number/LCD_Pow(16,i-1)%16;
  183. if(SingleNumber<10)
  184. {
  185. LCD_WriteData(SingleNumber+'0');
  186. }
  187. else
  188. {
  189. LCD_WriteData(SingleNumber-10+'A');
  190. }
  191. }
  192. }
  193. /**
  194. * @brief 在LCD1602指定位置开始以二进制显示所给数字
  195. * @param Line 起始行位置,范围:1~2
  196. * @param Column 起始列位置,范围:1~16
  197. * @param Number 要显示的数字,范围:0~1111 1111 1111 1111
  198. * @param Length 要显示数字的长度,范围:1~16
  199. * @retval 无
  200. */
  201. void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
  202. {
  203. unsigned char i;
  204. LCD_SetCursor(Line,Column);
  205. for(i=Length;i>0;i--)
  206. {
  207. LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
  208. }
  209. }

 LCD1602.h

  1. #ifndef __LCD1602_H__
  2. #define __LCD1602_H__
  3. //用户调用函数:
  4. void LCD_Init();
  5. void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
  6. void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
  7. void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
  8. void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
  9. void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
  10. void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
  11. #endif

4、注意事项

单引号和双引号都有全角和半角之分,全角的又叫中文字符,半角的又称英文字符。在c语言中,全角字符没有任何意义,它就是一个普通字符,没有含义;半角字符才有不同的意义:
        双引号用来括起一个字符串,如"China";
        单引号原来括起一个字符,如'f'。
二者含义不同,不能混用。

十五、单片机实操十三:矩阵键盘

1、在指定路径下创建工程并添加新文件

2、矩阵键盘介绍

3、扫描的概念

4、硬件连线

我们想选取1、5、9、13,即第一列,可以先把P1全部置为高,即全部拉高;接着让P13拉低,则选中了第一列。其他也这样操作。

 5、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "LCD1602.h"
  3. #include "Delay.h"
  4. #include "MatrixKey.h"
  5. unsigned char KeyNum;
  6. void main()
  7. {
  8. LCD_Init();
  9. // LCD_ShowChar(1,1,'B');
  10. LCD_ShowString(1,3,"MatrixKey:");
  11. // LCD_ShowNum(1,9,123,3);
  12. // LCD_ShowSignedNum(1,13,-66,2);
  13. // LCD_ShowHexNum(2,1,0xA8,2);
  14. // LCD_ShowBinNum(2,4,0xAA,8);
  15. // LCD_ShowChar(2,13,'A');
  16. while(1)
  17. {
  18. KeyNum=MatrixKey();
  19. if(KeyNum)
  20. {
  21. LCD_ShowNum(2,3,KeyNum,2);
  22. }
  23. }
  24. }

MatrixKey.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h"
  3. unsigned char MatrixKey()
  4. {
  5. unsigned char KeyNumber=0;
  6. P1=0xFF;
  7. P13=0;
  8. if(P17==0){Delay(20);while(P17==0);Delay(20);KeyNumber=1;}
  9. if(P16==0){Delay(20);while(P16==0);Delay(20);KeyNumber=5;}
  10. if(P15==0){Delay(20);while(P15==0);Delay(20);KeyNumber=9;}
  11. if(P14==0){Delay(20);while(P14==0);Delay(20);KeyNumber=13;}
  12. P1=0xFF;
  13. P12=0;
  14. if(P17==0){Delay(20);while(P17==0);Delay(20);KeyNumber=2;}
  15. if(P16==0){Delay(20);while(P16==0);Delay(20);KeyNumber=6;}
  16. if(P15==0){Delay(20);while(P15==0);Delay(20);KeyNumber=10;}
  17. if(P14==0){Delay(20);while(P14==0);Delay(20);KeyNumber=14;}
  18. P1=0xFF;
  19. P11=0;
  20. if(P17==0){Delay(20);while(P17==0);Delay(20);KeyNumber=3;}
  21. if(P16==0){Delay(20);while(P16==0);Delay(20);KeyNumber=7;}
  22. if(P15==0){Delay(20);while(P15==0);Delay(20);KeyNumber=11;}
  23. if(P14==0){Delay(20);while(P14==0);Delay(20);KeyNumber=15;}
  24. P1=0xFF;
  25. P10=0;
  26. if(P17==0){Delay(20);while(P17==0);Delay(20);KeyNumber=4;}
  27. if(P16==0){Delay(20);while(P16==0);Delay(20);KeyNumber=8;}
  28. if(P15==0){Delay(20);while(P15==0);Delay(20);KeyNumber=12;}
  29. if(P14==0){Delay(20);while(P14==0);Delay(20);KeyNumber=16;}
  30. return KeyNumber;
  31. }

MatrixKey.h

  1. #ifndef __MATRIXKEY_H__
  2. #define __MATRIXKEY_H__
  3. unsigned char MatrixKey();
  4. #endif

其它文件:复制之前写的模块即可。

6、软件使用技巧:配置模板

 

 

十六、单片机实操十四:矩阵键盘密码锁

1、复制工程后打开

2、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "LCD1602.h"
  3. #include "Delay.h"
  4. #include "MatrixKey.h"
  5. unsigned char KeyNum;
  6. unsigned int Password, Count;
  7. void main()
  8. {
  9. LCD_Init();
  10. // LCD_ShowChar(1,1,'B');
  11. LCD_ShowString(1,1,"Password:");
  12. // LCD_ShowNum(1,9,123,3);
  13. // LCD_ShowSignedNum(1,13,-66,2);
  14. // LCD_ShowHexNum(2,1,0xA8,2);
  15. // LCD_ShowBinNum(2,4,0xAA,8);
  16. // LCD_ShowChar(2,13,'A');
  17. while(1)
  18. {
  19. KeyNum=MatrixKey();
  20. if(KeyNum)
  21. {
  22. if(KeyNum<=10) // 如果S1~S10按键按下,输入密码
  23. {
  24. if(Count<4) // 密码四位数
  25. {
  26. Password*=10; // 密码左移一位,这样就可以依次输入密码
  27. Password+=KeyNum%10; // 获取一位密码,加入到原密码,此时10为0
  28. Count++;
  29. }
  30. }
  31. LCD_ShowNum(2,1,Password,4);
  32. if(KeyNum==11) // 确认
  33. {
  34. if(Password==1234)
  35. {
  36. LCD_ShowString(1,14,"OK ");
  37. Password=0;
  38. Count=0;
  39. LCD_ShowNum(2,1,Password,4);
  40. }
  41. else
  42. {
  43. LCD_ShowString(1,14,"ERR");
  44. Password=0;
  45. Count=0;
  46. LCD_ShowNum(2,1,Password,4);
  47. }
  48. }
  49. if(KeyNum==12) //取消
  50. {
  51. Password=0;
  52. Count=0;
  53. LCD_ShowString(1,14," ");
  54. LCD_ShowNum(2,1,Password,4);
  55. }
  56. }
  57. }
  58. }

其它文件:复制之前写的模块即可。

十七、单片机实操十五:定时器与按键控制LED流水灯模式

1、定时器介绍

2、STC89C52定时器资源

3、定时器狂框图 

 4、定时器工作模式

 5、中断系统

 6、中断程序流程

 7、STC89C52中断资源

 8、定时器和中断系统

 9、定时器相关寄存器

10、编写程序

通过独立按键控制流水灯模式,并由定时器执行流水灯。

main.c

  1. #include <STC89C5xRC.H>
  2. #include "Timer0.h"
  3. #include "Key.h"
  4. #include <INTRINS.H>
  5. unsigned char KeyNum,LEDMode;
  6. void main()
  7. {
  8. P2=0xFE;
  9. Timer0Init();
  10. while(1)
  11. {
  12. KeyNum=Key(); //获取独立按键键码
  13. if(KeyNum) //如果按键按下
  14. {
  15. if(KeyNum==1) //如果K1按键按下
  16. {
  17. LEDMode++; //模式切换,按1下按键是模式1,按2下是模式0,默认模式0
  18. if(LEDMode>=2)LEDMode=0;
  19. }
  20. }
  21. }
  22. }
  23. void Timer0_Routine() interrupt 1 //中断函数标识,含优先级
  24. {
  25. static unsigned int T0Count; //静态变量,拥有局部作用域,全局生命周期
  26. TL0 = 0x18; //设置定时初值
  27. TH0 = 0xFC; //设置定时初值
  28. T0Count++; //T0Count计次,对中断频率进行分频
  29. if(T0Count>=500)//分频500次,500ms
  30. {
  31. T0Count=0;
  32. if(LEDMode==0) //模式判断
  33. P2=_crol_(P2,1); //LED输出(循环左移函数,即使流水灯循环左移)
  34. if(LEDMode==1)
  35. P2=_cror_(P2,1);
  36. }
  37. }

Timer0.c

  1. #include <STC89C5xRC.H>
  2. /**
  3. * @brief 定时器0初始化,1毫秒@12.000MHz
  4. * @param 无
  5. * @retval 无
  6. */
  7. void Timer0Init(void)
  8. {
  9. TMOD &= 0xF0; //设置定时器模式,只改变T0,避免T1改变
  10. TMOD |= 0x01; //设置定时器模式
  11. TL0 = 0x18; //高位设置定时初值 65535/256
  12. TH0 = 0xFC; //低位设置定时初值 65535%256
  13. TF0 = 0; //清除TF0标志
  14. TR0 = 1; //定时器0开始计时
  15. ET0=1;
  16. EA=1;
  17. PT0=0;
  18. }
  19. /*定时器中断函数模板
  20. void Timer0_Routine() interrupt 1
  21. {
  22. static unsigned int T0Count; //静态变量,拥有局部作用域,全局生命周期
  23. TL0 = 0x18; //设置定时初值,像沙漏,重置沙漏时间
  24. TH0 = 0xFC; //设置定时初值
  25. T0Count++;
  26. if(T0Count>=1000)
  27. {
  28. T0Count=0;
  29. }
  30. }
  31. */

Timer0.h

  1. #ifndef __TIMER0_H__
  2. #define __TIMER0_H__
  3. void Timer0Init(void);
  4. #endif

Key.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h"
  3. /**
  4. * @brief 获取独立按键键码
  5. * @param 无
  6. * @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
  7. */
  8. unsigned char Key()
  9. {
  10. unsigned char KeyNumber=0;
  11. if(P31==0){Delay(20);while(P31==0);Delay(20);KeyNumber=1;}
  12. if(P30==0){Delay(20);while(P30==0);Delay(20);KeyNumber=2;}
  13. if(P32==0){Delay(20);while(P32==0);Delay(20);KeyNumber=3;}
  14. if(P33==0){Delay(20);while(P33==0);Delay(20);KeyNumber=4;}
  15. return KeyNumber;
  16. }

Key.h

  1. #ifndef __KEY_H__
  2. #define __KEY_H__
  3. unsigned char Key();
  4. #endif

其它文件:复制之前写的模块即可。

11、定时器代码获取

十八、单片机实操十六:定时器时钟

1、编写程序

制作一个一天时间的计时器。

main.c

  1. #include <STC89C5xRC.H>
  2. #include "Timer0.h"
  3. #include "Delay.h"
  4. #include "LCD1602.h"
  5. unsigned char Sec, Min=59, Hour=23;
  6. void main()
  7. {
  8. Timer0Init();
  9. LCD_Init();
  10. LCD_ShowString(1,1,"Clock:");
  11. while(1)
  12. {
  13. LCD_ShowNum(2,1,Hour,2);
  14. LCD_ShowString(2,3,":");
  15. LCD_ShowNum(2,4,Min,2);
  16. LCD_ShowString(2,6,":");
  17. LCD_ShowNum(2,7,Sec,2);
  18. }
  19. }
  20. void Timer0_Routine() interrupt 1 //中断函数标识,含优先级
  21. {
  22. static unsigned int T0Count; //静态变量,拥有局部作用域,全局生命周期
  23. TL0 = 0x18; //设置定时初值
  24. TH0 = 0xFC; //设置定时初值
  25. T0Count++; //T0Count计次,对中断频率进行分频
  26. if(T0Count>=1000)//1000ms
  27. {
  28. T0Count=0;
  29. Sec++;
  30. if(Sec>=60)
  31. {
  32. Sec=0;
  33. Min++;
  34. if(Min>=60)
  35. {
  36. Min=0;
  37. Hour++;
  38. if(Hour>=24)
  39. {
  40. Hour=0;
  41. }
  42. }
  43. }
  44. }
  45. }

其它文件:复制之前写的模块即可。

十九、单片机实操十七:串口通信与串口向电脑发送数据

1、串口介绍

2、接口及引脚定义

 3、硬件电路

 4、电平标准

 5、常见通信接口比较

 6、相关术语

 7、51单片机的UART

 8、串口参数与时序图

 9、串口模式图

 10、串口相关寄存器

 11、串口和中断系统

11、使用工具生成串口配置

 12、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h"
  3. #include "UART.h"
  4. unsigned char Sec;
  5. void main()
  6. {
  7. UartInit();
  8. while(1)
  9. {
  10. UART_SendByte(Sec);
  11. Sec++;
  12. Delay(1); // 必要的延时,避免误差导致乱码,没误差的时候可以不需要
  13. }
  14. }

UART.c

  1. #include <STC89C5xRC.H>
  2. void UartInit(void) //4800bps@11.0592MHz
  3. {
  4. PCON &= 0x80; //波特率不倍速
  5. SCON = 0x40; //8位数据,可变波特率
  6. TMOD &= 0x0F; //设置定时器模式
  7. TMOD |= 0x20; //设置定时器模式
  8. TL1 = 0xFA; //设置定时初始值
  9. TH1 = 0xFA; //设置定时重载值
  10. ET1 = 0; //禁止定时器%d中断
  11. TR1 = 1; //定时器1开始计时
  12. }
  13. void UART_SendByte(unsigned char Byte)
  14. {
  15. SBUF=Byte; // 根据硬件原理,操作寄存器
  16. while(TI==0); // 操作寄存器,检测是否完成
  17. TI=0; // 按要求重新赋值为0
  18. }

UART.h

  1. #ifndef __UART_H__
  2. #define __UART_H__
  3. void UartInit(void);
  4. void UART_SendByte(unsigned char Byte);
  5. #endif

其他文件:复制之前写的模块即可。

13、调试程序

二十、单片机实操十八:电脑通过串口控制LED

1、编写程序

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h"
  3. #include "UART.h"
  4. unsigned char Sec;
  5. void main()
  6. {
  7. UartInit();
  8. while(1)
  9. {
  10. }
  11. }
  12. void UART_Routine() interrupt 4
  13. {
  14. if(RI==1)
  15. {
  16. P2=~SBUF; // 读寄存器的数据
  17. UART_SendByte(SBUF);
  18. RI=0; // 说明书要求手动复位
  19. }
  20. }

其他文件:复制之前写的模块即可。

2、波特率计算

(1)内部逻辑图

(2)配置的定时初值

 (3)计算方式

 F3=243;

256-243=13us;

每记13次数,溢出1次;

1/13=0.0769230769230769MHz;

0.0769230769230769/16=0.0048076923076923MHz=4,807.692307692308HZ

3、ASCII码编码表

文本模式与Hex模式对应情况。

二十一、单片机实操十九:LED点阵屏与显示图形

1、LED点阵屏介绍

 2、显示原理

 3、74HC595模块原理图

4、74HC595介绍

 5、接线

OE要通过跳线帽与GND相连,而开发板默认OE与VCC相连,因此需要操作。

6、开发板引脚对应关系

7、C51的sfr、sbit 

8、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.H"
  3. sbit RCK=P3^5; //RCLK
  4. sbit SCK=P3^6; //SRCLK
  5. sbit SER=P3^4; //SER
  6. #define MATRIX_LED_PORT P0
  7. void _74HC595_WriteByte(unsigned char Byte)
  8. {
  9. // SER=Byte&0x80; //一般是0、1赋值,不过,如果非0,都会当作1
  10. // SCK=1;
  11. // SCK=0;
  12. // SER=Byte&0x60;
  13. // SCK=1;
  14. // SCK=0;
  15. unsigned char i;
  16. for(i=0;i<8;i++)
  17. {
  18. SER=Byte&(0x80>>i);
  19. SCK=1;
  20. SCK=0;
  21. }
  22. RCK=1;
  23. RCK=0;
  24. }
  25. void MatrixLED_ShowColumn(unsigned char Column, Data)
  26. {
  27. _74HC595_WriteByte(Data);
  28. // if(Column==0){P0=~0x80;}
  29. // if(Column==1){P0=~0x40;}
  30. MATRIX_LED_PORT=~(0x80>>Column);
  31. Delay(1);
  32. MATRIX_LED_PORT=0xFF;
  33. }
  34. void main()
  35. {
  36. SCK=0;
  37. RCK=0;
  38. while(1)
  39. {
  40. // _74HC595_WriteByte(0xAA);
  41. MatrixLED_ShowColumn(0,0x80);
  42. MatrixLED_ShowColumn(1,0x40);
  43. MatrixLED_ShowColumn(2,0x20);
  44. MatrixLED_ShowColumn(3,0x10);
  45. }
  46. }

二十二、单片机实操二十:LED点阵屏显示动画

1、字模提取软件

 

 

 

 

 取出数据如下,将作为程序数组使用:

0xFF,0x10,0x10,0x10,0xFF,0x00,0x1E,0x29,0x29,0x29,0x18,0x00,0xFE,0x01,0x02,0x00,
0xFE,0x01,0x02,0x00,0x0E,0x11,0x11,0x0E,0x00,0x7D,0x7D,0x00,0x00,0x00,0x00,0x00,

2、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.H"
  3. #include "MatrixLED.H"
  4. unsigned char Animation[]={
  5. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 为了显示更好看,避免第一列直接显示字符本身
  6. 0xFF,0x10,0x10,0x10,0xFF,0x00,0x1E,0x29,0x29,0x29,0x18,0x00,0xFE,0x01,0x02,0x00,
  7. 0xFE,0x01,0x02,0x00,0x0E,0x11,0x11,0x0E,0x00,0x7D,0x7D,0x00,0x00,0x00,0x00,0x00,
  8. 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 为了显示更好看
  9. };
  10. void main()
  11. {
  12. unsigned char i, Offset=1, Count=0;
  13. while(1)
  14. {
  15. for(i=0;i<8;i++)
  16. {
  17. MatrixLED_ShowColumn(i,Animation[i+Offset]);
  18. }
  19. Count++;
  20. if(Count>10)
  21. {
  22. Count=0;
  23. Offset++;
  24. if(Offset>40)
  25. {
  26. Offset=0; //防止数组溢出
  27. }
  28. }
  29. }
  30. }

MatrixLED.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.H"
  3. sbit RCK=P3^5; //RCLK
  4. sbit SCK=P3^6; //SRCLK
  5. sbit SER=P3^4; //SER
  6. #define MATRIX_LED_PORT P0
  7. void _74HC595_WriteByte(unsigned char Byte)
  8. {
  9. // SER=Byte&0x80; //一般是0、1赋值,不过,如果非0,都会当作1
  10. // SCK=1;
  11. // SCK=0;
  12. // SER=Byte&0x60;
  13. // SCK=1;
  14. // SCK=0;
  15. unsigned char i;
  16. for(i=0;i<8;i++)
  17. {
  18. SER=Byte&(0x80>>i);
  19. SCK=1;
  20. SCK=0;
  21. }
  22. RCK=1;
  23. RCK=0;
  24. }
  25. void MatrixLED_Init()
  26. {
  27. SCK=0;
  28. RCK=0;
  29. }
  30. void MatrixLED_ShowColumn(unsigned char Column, Data)
  31. {
  32. _74HC595_WriteByte(Data);
  33. // if(Column==0){P0=~0x80;}
  34. // if(Column==1){P0=~0x40;}
  35. MATRIX_LED_PORT=~(0x80>>Column);
  36. Delay(1);
  37. MATRIX_LED_PORT=0xFF;
  38. }

MatrixLED.h

  1. #ifndef __MATRIXLED_H__
  2. #define __MATRIXLED_H__
  3. void MatrixLED_ShowColumn(unsigned char Column, Data);
  4. void MatrixLED_Init();
  5. #endif

3、注意事项

将数据放在flash里面,用来避免内存被消耗过多,但这种数据是不能更改的,方法如下:

unsigned char Animation[]

改为

unsigned char code Animation[]

二十三、单片机实操二十一:DS1302实时时钟

1、DS1302介绍

2、引脚定义和应用电路

3、内部结构图

4、寄存器定义

 5、时序定义

 6、BCD码

7、编写代码

main.c

  1. #include <STC89C5xRC.H>
  2. #include "LCD1602.h"
  3. #include "DS1302.h"
  4. // unsigned char Second, Minute;
  5. void main()
  6. {
  7. LCD_Init();
  8. DS1302_Init();
  9. // DS1302_WriteByte(0x8E, 0x00); // 解除芯片写保护,避免数值不动
  10. // DS1302_WriteByte(0x80, 0x54);
  11. DS1302_SetTime();
  12. while(1)
  13. {
  14. DS1302_ReadTime();
  15. LCD_ShowNum(1,1,DS1302_TIME[0],2);
  16. LCD_ShowNum(1,4,DS1302_TIME[1],2);
  17. LCD_ShowNum(1,7,DS1302_TIME[2],2);
  18. LCD_ShowNum(2,1,DS1302_TIME[3],2);
  19. LCD_ShowNum(2,4,DS1302_TIME[4],2);
  20. LCD_ShowNum(2,7,DS1302_TIME[5],2);
  21. // Second=DS1302_ReadByte(0x81);
  22. // Minute=DS1302_ReadByte(0x83);
  23. // LCD_ShowNum(2,1,Second/16*10+Second%16,2);
  24. // LCD_ShowNum(2,3,Minute/16*10+Minute%16,2);
  25. }
  26. }

DS1302.c

  1. #include <STC89C5xRC.H>
  2. sbit DS1302_SCLK=P3^6;
  3. sbit DS1302_IO=P3^4;
  4. sbit DS1302_CE=P3^5;
  5. #define DS1302_SECOND 0x80
  6. #define DS1302_MINUTE 0x82
  7. #define DS1302_HOUR 0x84
  8. #define DS1302_DATE 0x86
  9. #define DS1302_MONTH 0x88
  10. #define DS1302_DAY 0x8A
  11. #define DS1302_YEAR 0x8C
  12. #define DS1302_WP 0x8E
  13. unsigned char DS1302_TIME[]={19,11,16,12,59,55,6};
  14. void DS1302_Init(void)
  15. {
  16. DS1302_CE=0;
  17. DS1302_SCLK=0;
  18. }
  19. void DS1302_WriteByte(unsigned char Command, Data)
  20. {
  21. unsigned char i;
  22. DS1302_CE=1;
  23. // DS1302_IO=Command&0x01;
  24. // DS1302_SCLK=1; // 速度慢可以不加延时,有些速度快的芯片需要增加延时
  25. // DS1302_SCLK=0;
  26. //
  27. // DS1302_IO=Command&0x02;
  28. // DS1302_SCLK=1;
  29. // DS1302_SCLK=0;
  30. for(i=0;i<8;i++)
  31. {
  32. DS1302_IO=Command&(0x01<<i);
  33. DS1302_SCLK=1;
  34. DS1302_SCLK=0;
  35. }
  36. for(i=0;i<8;i++)
  37. {
  38. DS1302_IO=Data&(0x01<<i);
  39. DS1302_SCLK=1;
  40. DS1302_SCLK=0;
  41. }
  42. DS1302_CE=0; //完成一次操作,释放IO
  43. }
  44. unsigned char DS1302_ReadByte(unsigned char Command)
  45. {
  46. unsigned char i,Data=0x00;
  47. Command|=0x01; //
  48. DS1302_CE=1;
  49. for(i=0;i<8;i++)
  50. {
  51. DS1302_IO=Command&(0x01<<i);
  52. DS1302_SCLK=0; //根据时序操作
  53. DS1302_SCLK=1;
  54. }
  55. // DS1302_SCLK=0;
  56. // DS1302_SCLK=1;
  57. // if(DS1302_IO)
  58. // {
  59. // Data=Data|0x01;
  60. // }
  61. // DS1302_SCLK=0;
  62. // DS1302_SCLK=1;
  63. // if(DS1302_IO)
  64. // {
  65. // Data=Data|0x02;
  66. // }
  67. for(i=0;i<8;i++)
  68. {
  69. DS1302_SCLK=1; //重复置1是去掉一个周期,为的是满足时序
  70. DS1302_SCLK=0;
  71. if(DS1302_IO){Data|=(0x01<<i);}
  72. }
  73. DS1302_CE=0;
  74. DS1302_IO=0; // 如果不加这一行,将显示全0
  75. return Data;
  76. }
  77. void DS1302_SetTime(void)
  78. {
  79. DS1302_WriteByte(DS1302_WP,0x00);
  80. DS1302_WriteByte(DS1302_YEAR,DS1302_TIME[0]/10*16+DS1302_TIME[0]%10);
  81. DS1302_WriteByte(DS1302_MONTH,DS1302_TIME[1]/10*16+DS1302_TIME[1]%10);
  82. DS1302_WriteByte(DS1302_DATE,DS1302_TIME[2]/10*16+DS1302_TIME[2]%10);
  83. DS1302_WriteByte(DS1302_HOUR,DS1302_TIME[3]/10*16+DS1302_TIME[3]%10);
  84. DS1302_WriteByte(DS1302_MINUTE,DS1302_TIME[4]/10*16+DS1302_TIME[4]%10);
  85. DS1302_WriteByte(DS1302_SECOND,DS1302_TIME[5]/10*16+DS1302_TIME[5]%10);
  86. DS1302_WriteByte(DS1302_DAY,DS1302_TIME[6]/10*16+DS1302_TIME[6]%10);
  87. DS1302_WriteByte(DS1302_WP,0x00);
  88. }
  89. void DS1302_ReadTime(void)
  90. {
  91. unsigned char Temp;
  92. Temp=DS1302_ReadByte(DS1302_YEAR);
  93. DS1302_TIME[0]=Temp/16*10+Temp%16;
  94. Temp=DS1302_ReadByte(DS1302_MONTH);
  95. DS1302_TIME[1]=Temp/16*10+Temp%16;
  96. Temp=DS1302_ReadByte(DS1302_DATE);
  97. DS1302_TIME[2]=Temp/16*10+Temp%16;
  98. Temp=DS1302_ReadByte(DS1302_HOUR);
  99. DS1302_TIME[3]=Temp/16*10+Temp%16;
  100. Temp=DS1302_ReadByte(DS1302_MINUTE);
  101. DS1302_TIME[4]=Temp/16*10+Temp%16;
  102. Temp=DS1302_ReadByte(DS1302_SECOND);
  103. DS1302_TIME[5]=Temp/16*10+Temp%16;
  104. Temp=DS1302_ReadByte(DS1302_DAY);
  105. DS1302_TIME[6]=Temp/16*10+Temp%16;
  106. }

DS1302.h

  1. #ifndef __DS1302_H__
  2. #define __DS1302_H__
  3. extern unsigned char DS1302_TIME[]; // 声明数组
  4. void DS1302_Init(void);
  5. void DS1302_WriteByte(unsigned char Command, Data);
  6. unsigned char DS1302_ReadByte(unsigned char Command);
  7. void DS1302_SetTime(void);
  8. void DS1302_ReadTime(void);
  9. #endif

二十四、单片机实操二十二:DS1302可调时钟

1、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "LCD1602.h"
  3. #include "DS1302.h"
  4. #include "Key.h"
  5. #include "Timer0.h"
  6. unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;
  7. void TimeShow(void)//时间显示功能
  8. {
  9. DS1302_ReadTime();//读取时间
  10. LCD_ShowNum(1,1,DS1302_Time[0],2);//显示年
  11. LCD_ShowNum(1,4,DS1302_Time[1],2);//显示月
  12. LCD_ShowNum(1,7,DS1302_Time[2],2);//显示日
  13. LCD_ShowNum(2,1,DS1302_Time[3],2);//显示时
  14. LCD_ShowNum(2,4,DS1302_Time[4],2);//显示分
  15. LCD_ShowNum(2,7,DS1302_Time[5],2);//显示秒
  16. }
  17. void TimeSet(void)//时间设置功能
  18. {
  19. if(KeyNum==2)//按键2按下
  20. {
  21. TimeSetSelect++;//设置选择位加1
  22. TimeSetSelect%=6;//越界清零
  23. }
  24. if(KeyNum==3)//按键3按下
  25. {
  26. DS1302_Time[TimeSetSelect]++;//时间设置位数值加1
  27. if(DS1302_Time[0]>99){DS1302_Time[0]=0;}//年越界判断
  28. if(DS1302_Time[1]>12){DS1302_Time[1]=1;}//月越界判断
  29. if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 ||
  30. DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判断
  31. {
  32. if(DS1302_Time[2]>31){DS1302_Time[2]=1;}//大月
  33. }
  34. else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
  35. {
  36. if(DS1302_Time[2]>30){DS1302_Time[2]=1;}//小月
  37. }
  38. else if(DS1302_Time[1]==2)
  39. {
  40. if(DS1302_Time[0]%4==0)
  41. {
  42. if(DS1302_Time[2]>29){DS1302_Time[2]=1;}//闰年2月
  43. }
  44. else
  45. {
  46. if(DS1302_Time[2]>28){DS1302_Time[2]=1;}//平年2月
  47. }
  48. }
  49. if(DS1302_Time[3]>23){DS1302_Time[3]=0;}//时越界判断
  50. if(DS1302_Time[4]>59){DS1302_Time[4]=0;}//分越界判断
  51. if(DS1302_Time[5]>59){DS1302_Time[5]=0;}//秒越界判断
  52. }
  53. if(KeyNum==4)//按键3按下
  54. {
  55. DS1302_Time[TimeSetSelect]--;//时间设置位数值减1
  56. if(DS1302_Time[0]<0){DS1302_Time[0]=99;}//年越界判断
  57. if(DS1302_Time[1]<1){DS1302_Time[1]=12;}//月越界判断
  58. if( DS1302_Time[1]==1 || DS1302_Time[1]==3 || DS1302_Time[1]==5 || DS1302_Time[1]==7 ||
  59. DS1302_Time[1]==8 || DS1302_Time[1]==10 || DS1302_Time[1]==12)//日越界判断
  60. {
  61. if(DS1302_Time[2]<1){DS1302_Time[2]=31;}//大月
  62. if(DS1302_Time[2]>31){DS1302_Time[2]=1;}
  63. }
  64. else if(DS1302_Time[1]==4 || DS1302_Time[1]==6 || DS1302_Time[1]==9 || DS1302_Time[1]==11)
  65. {
  66. if(DS1302_Time[2]<1){DS1302_Time[2]=30;}//小月
  67. if(DS1302_Time[2]>30){DS1302_Time[2]=1;}
  68. }
  69. else if(DS1302_Time[1]==2)
  70. {
  71. if(DS1302_Time[0]%4==0)
  72. {
  73. if(DS1302_Time[2]<1){DS1302_Time[2]=29;}//闰年2月
  74. if(DS1302_Time[2]>29){DS1302_Time[2]=1;}
  75. }
  76. else
  77. {
  78. if(DS1302_Time[2]<1){DS1302_Time[2]=28;}//平年2月
  79. if(DS1302_Time[2]>28){DS1302_Time[2]=1;}
  80. }
  81. }
  82. if(DS1302_Time[3]<0){DS1302_Time[3]=23;}//时越界判断
  83. if(DS1302_Time[4]<0){DS1302_Time[4]=59;}//分越界判断
  84. if(DS1302_Time[5]<0){DS1302_Time[5]=59;}//秒越界判断
  85. }
  86. //更新显示,根据TimeSetSelect和TimeSetFlashFlag判断可完成闪烁功能
  87. if(TimeSetSelect==0 && TimeSetFlashFlag==1){LCD_ShowString(1,1," ");}
  88. else {LCD_ShowNum(1,1,DS1302_Time[0],2);}
  89. if(TimeSetSelect==1 && TimeSetFlashFlag==1){LCD_ShowString(1,4," ");}
  90. else {LCD_ShowNum(1,4,DS1302_Time[1],2);}
  91. if(TimeSetSelect==2 && TimeSetFlashFlag==1){LCD_ShowString(1,7," ");}
  92. else {LCD_ShowNum(1,7,DS1302_Time[2],2);}
  93. if(TimeSetSelect==3 && TimeSetFlashFlag==1){LCD_ShowString(2,1," ");}
  94. else {LCD_ShowNum(2,1,DS1302_Time[3],2);}
  95. if(TimeSetSelect==4 && TimeSetFlashFlag==1){LCD_ShowString(2,4," ");}
  96. else {LCD_ShowNum(2,4,DS1302_Time[4],2);}
  97. if(TimeSetSelect==5 && TimeSetFlashFlag==1){LCD_ShowString(2,7," ");}
  98. else {LCD_ShowNum(2,7,DS1302_Time[5],2);}
  99. }
  100. void main()
  101. {
  102. LCD_Init();
  103. DS1302_Init();
  104. Timer0Init();
  105. LCD_ShowString(1,1," - - ");//静态字符初始化显示
  106. LCD_ShowString(2,1," : : ");
  107. DS1302_SetTime();//设置时间
  108. while(1)
  109. {
  110. KeyNum=Key();//读取键码
  111. if(KeyNum==1)//按键1按下
  112. {
  113. if(MODE==0){MODE=1;TimeSetSelect=0;}//功能切换
  114. else if(MODE==1){MODE=0;DS1302_SetTime();}
  115. }
  116. switch(MODE)//根据不同的功能执行不同的函数
  117. {
  118. case 0:TimeShow();break;
  119. case 1:TimeSet();break;
  120. }
  121. }
  122. }
  123. void Timer0_Routine() interrupt 1
  124. {
  125. static unsigned int T0Count;
  126. TL0 = 0x18; //设置定时初值
  127. TH0 = 0xFC; //设置定时初值
  128. T0Count++;
  129. if(T0Count>=500)//每500ms进入一次
  130. {
  131. T0Count=0;
  132. TimeSetFlashFlag=!TimeSetFlashFlag;//闪烁标志位取反
  133. }
  134. }

DS1302.c

  1. #include <STC89C5xRC.H>
  2. //引脚定义
  3. sbit DS1302_SCLK=P3^6;
  4. sbit DS1302_IO=P3^4;
  5. sbit DS1302_CE=P3^5;
  6. //寄存器写入地址/指令定义
  7. #define DS1302_SECOND 0x80
  8. #define DS1302_MINUTE 0x82
  9. #define DS1302_HOUR 0x84
  10. #define DS1302_DATE 0x86
  11. #define DS1302_MONTH 0x88
  12. #define DS1302_DAY 0x8A
  13. #define DS1302_YEAR 0x8C
  14. #define DS1302_WP 0x8E
  15. //时间数组,索引0~6分别为年、月、日、时、分、秒、星期,设置为有符号的便于<0的判断
  16. char DS1302_Time[]={19,11,16,12,59,55,6};
  17. /**
  18. * @brief DS1302初始化
  19. * @param 无
  20. * @retval 无
  21. */
  22. void DS1302_Init(void)
  23. {
  24. DS1302_CE=0;
  25. DS1302_SCLK=0;
  26. }
  27. /**
  28. * @brief DS1302写一个字节
  29. * @param Command 命令字/地址
  30. * @param Data 要写入的数据
  31. * @retval 无
  32. */
  33. void DS1302_WriteByte(unsigned char Command,Data)
  34. {
  35. unsigned char i;
  36. DS1302_CE=1;
  37. for(i=0;i<8;i++)
  38. {
  39. DS1302_IO=Command&(0x01<<i);
  40. DS1302_SCLK=1;
  41. DS1302_SCLK=0;
  42. }
  43. for(i=0;i<8;i++)
  44. {
  45. DS1302_IO=Data&(0x01<<i);
  46. DS1302_SCLK=1;
  47. DS1302_SCLK=0;
  48. }
  49. DS1302_CE=0;
  50. }
  51. /**
  52. * @brief DS1302读一个字节
  53. * @param Command 命令字/地址
  54. * @retval 读出的数据
  55. */
  56. unsigned char DS1302_ReadByte(unsigned char Command)
  57. {
  58. unsigned char i,Data=0x00;
  59. Command|=0x01; //将指令转换为读指令
  60. DS1302_CE=1;
  61. for(i=0;i<8;i++)
  62. {
  63. DS1302_IO=Command&(0x01<<i);
  64. DS1302_SCLK=0;
  65. DS1302_SCLK=1;
  66. }
  67. for(i=0;i<8;i++)
  68. {
  69. DS1302_SCLK=1;
  70. DS1302_SCLK=0;
  71. if(DS1302_IO){Data|=(0x01<<i);}
  72. }
  73. DS1302_CE=0;
  74. DS1302_IO=0; //读取后将IO设置为0,否则读出的数据会出错
  75. return Data;
  76. }
  77. /**
  78. * @brief DS1302设置时间,调用之后,DS1302_Time数组的数字会被设置到DS1302中
  79. * @param 无
  80. * @retval 无
  81. */
  82. void DS1302_SetTime(void)
  83. {
  84. DS1302_WriteByte(DS1302_WP,0x00);
  85. DS1302_WriteByte(DS1302_YEAR,DS1302_Time[0]/10*16+DS1302_Time[0]%10);//十进制转BCD码后写入
  86. DS1302_WriteByte(DS1302_MONTH,DS1302_Time[1]/10*16+DS1302_Time[1]%10);
  87. DS1302_WriteByte(DS1302_DATE,DS1302_Time[2]/10*16+DS1302_Time[2]%10);
  88. DS1302_WriteByte(DS1302_HOUR,DS1302_Time[3]/10*16+DS1302_Time[3]%10);
  89. DS1302_WriteByte(DS1302_MINUTE,DS1302_Time[4]/10*16+DS1302_Time[4]%10);
  90. DS1302_WriteByte(DS1302_SECOND,DS1302_Time[5]/10*16+DS1302_Time[5]%10);
  91. DS1302_WriteByte(DS1302_DAY,DS1302_Time[6]/10*16+DS1302_Time[6]%10);
  92. DS1302_WriteByte(DS1302_WP,0x80);
  93. }
  94. /**
  95. * @brief DS1302读取时间,调用之后,DS1302中的数据会被读取到DS1302_Time数组中
  96. * @param 无
  97. * @retval 无
  98. */
  99. void DS1302_ReadTime(void)
  100. {
  101. unsigned char Temp;
  102. Temp=DS1302_ReadByte(DS1302_YEAR);
  103. DS1302_Time[0]=Temp/16*10+Temp%16;//BCD码转十进制后读取
  104. Temp=DS1302_ReadByte(DS1302_MONTH);
  105. DS1302_Time[1]=Temp/16*10+Temp%16;
  106. Temp=DS1302_ReadByte(DS1302_DATE);
  107. DS1302_Time[2]=Temp/16*10+Temp%16;
  108. Temp=DS1302_ReadByte(DS1302_HOUR);
  109. DS1302_Time[3]=Temp/16*10+Temp%16;
  110. Temp=DS1302_ReadByte(DS1302_MINUTE);
  111. DS1302_Time[4]=Temp/16*10+Temp%16;
  112. Temp=DS1302_ReadByte(DS1302_SECOND);
  113. DS1302_Time[5]=Temp/16*10+Temp%16;
  114. Temp=DS1302_ReadByte(DS1302_DAY);
  115. DS1302_Time[6]=Temp/16*10+Temp%16;
  116. }

DS1302.h

  1. #ifndef __DS1302_H__
  2. #define __DS1302_H__
  3. //外部可调用时间数组,索引0~6分别为年、月、日、时、分、秒、星期,设置为有符号的便于<0的判断
  4. extern char DS1302_Time[];
  5. void DS1302_Init(void);
  6. void DS1302_WriteByte(unsigned char Command,Data);
  7. unsigned char DS1302_ReadByte(unsigned char Command);
  8. void DS1302_SetTime(void);
  9. void DS1302_ReadTime(void);
  10. #endif

2、遇到问题

*** FATAL ERROR L250: CODE SIZE LIMIT IN RESTRICTED VERSION EXCEEDED
    MODULE:  D:\KEIL5\C51\LIB\C51S.LIB (-----)
    LIMIT:   0800H BYTES
Target not created.
Build Time Elapsed:  00:00:01

 3、解决方式

 

二十五、单片机实操二十三:蜂鸣器与播放提示音

1、蜂鸣器介绍

 2、驱动电路

 3、ULN2003

 4、硬件原理图

无源蜂鸣器,使用的控制引脚是P25

 5、钢琴键盘与音符对照

6、 简谱

 

 7、C调音符与频率对照表

8、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h"
  3. #include "Key.h"
  4. #include "Nixie.h"
  5. #include "Buzzer.h"
  6. unsigned char KeyNum;
  7. void main()
  8. {
  9. Nixie(1,0);
  10. while(1)
  11. {
  12. KeyNum=Key();
  13. if(KeyNum)
  14. {
  15. Buzzer_Time(1000);
  16. Nixie(1,KeyNum);
  17. }
  18. }
  19. }

Buzzer.c

  1. #include <STC89C5xRC.H>
  2. #include <INTRINS.H>
  3. sbit Buzzer=P2^5;
  4. void Buzzer_Delay500us() //@11.0592MHz
  5. {
  6. unsigned char i;
  7. _nop_();
  8. i = 227;
  9. while (--i);
  10. }
  11. void Buzzer_Time(unsigned int ms)
  12. {
  13. unsigned int i;
  14. for(i=0;i<ms*2;i++)
  15. {
  16. Buzzer=!Buzzer;
  17. Buzzer_Delay500us();
  18. }
  19. }

Buzzer.h

  1. #ifndef __BUZZER_H__
  2. #define __BUZZER_H__
  3. void Buzzer_Time(unsigned int ms);
  4. #endif

二十六、单片机实操二十四:蜂鸣器与播放提示音

1、音符对应的重载值计算方式

 2、编写天空之城音乐程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h"
  3. #include "Timer0.h"
  4. //蜂鸣器端口定义
  5. sbit Buzzer=P2^5;
  6. //播放速度,值为四分音符的时长(ms)
  7. #define SPEED 500
  8. //音符与索引对应表,P:休止符,L:低音,M:中音,H:高音,下划线:升半音符号#
  9. #define P 0
  10. #define L1 1
  11. #define L1_ 2
  12. #define L2 3
  13. #define L2_ 4
  14. #define L3 5
  15. #define L4 6
  16. #define L4_ 7
  17. #define L5 8
  18. #define L5_ 9
  19. #define L6 10
  20. #define L6_ 11
  21. #define L7 12
  22. #define M1 13
  23. #define M1_ 14
  24. #define M2 15
  25. #define M2_ 16
  26. #define M3 17
  27. #define M4 18
  28. #define M4_ 19
  29. #define M5 20
  30. #define M5_ 21
  31. #define M6 22
  32. #define M6_ 23
  33. #define M7 24
  34. #define H1 25
  35. #define H1_ 26
  36. #define H2 27
  37. #define H2_ 28
  38. #define H3 29
  39. #define H4 30
  40. #define H4_ 31
  41. #define H5 32
  42. #define H5_ 33
  43. #define H6 34
  44. #define H6_ 35
  45. #define H7 36
  46. //索引与频率对照表
  47. unsigned int FreqTable[]={
  48. 0,
  49. 63628,63731,63835,63928,64021,64103,64185,64260,64331,64400,64463,64528,
  50. 64580,64633,64684,64732,64777,64820,64860,64898,64934,64968,65000,65030,
  51. 65058,65085,65110,65134,65157,65178,65198,65217,65235,65252,65268,65283,
  52. };
  53. //乐谱
  54. unsigned char code Music[]={
  55. //音符,时值,
  56. //1
  57. P, 4,
  58. P, 4,
  59. P, 4,
  60. M6, 2,
  61. M7, 2,
  62. H1, 4+2,
  63. M7, 2,
  64. H1, 4,
  65. H3, 4,
  66. M7, 4+4+4,
  67. M3, 2,
  68. M3, 2,
  69. //2
  70. M6, 4+2,
  71. M5, 2,
  72. M6, 4,
  73. H1, 4,
  74. M5, 4+4+4,
  75. M3, 4,
  76. M4, 4+2,
  77. M3, 2,
  78. M4, 4,
  79. H1, 4,
  80. //3
  81. M3, 4+4,
  82. P, 2,
  83. H1, 2,
  84. H1, 2,
  85. H1, 2,
  86. M7, 4+2,
  87. M4_,2,
  88. M4_,4,
  89. M7, 4,
  90. M7, 8,
  91. P, 4,
  92. M6, 2,
  93. M7, 2,
  94. //4
  95. H1, 4+2,
  96. M7, 2,
  97. H1, 4,
  98. H3, 4,
  99. M7, 4+4+4,
  100. M3, 2,
  101. M3, 2,
  102. M6, 4+2,
  103. M5, 2,
  104. M6, 4,
  105. H1, 4,
  106. //5
  107. M5, 4+4+4,
  108. M2, 2,
  109. M3, 2,
  110. M4, 4,
  111. H1, 2,
  112. M7, 2+2,
  113. H1, 2+4,
  114. H2, 2,
  115. H2, 2,
  116. H3, 2,
  117. H1, 2+4+4,
  118. //6
  119. H1, 2,
  120. M7, 2,
  121. M6, 2,
  122. M6, 2,
  123. M7, 4,
  124. M5_,4,
  125. M6, 4+4+4,
  126. H1, 2,
  127. H2, 2,
  128. H3, 4+2,
  129. H2, 2,
  130. H3, 4,
  131. H5, 4,
  132. //7
  133. H2, 4+4+4,
  134. M5, 2,
  135. M5, 2,
  136. H1, 4+2,
  137. M7, 2,
  138. H1, 4,
  139. H3, 4,
  140. H3, 4+4+4+4,
  141. //8
  142. M6, 2,
  143. M7, 2,
  144. H1, 4,
  145. M7, 4,
  146. H2, 2,
  147. H2, 2,
  148. H1, 4+2,
  149. M5, 2+4+4,
  150. H4, 4,
  151. H3, 4,
  152. H3, 4,
  153. H1, 4,
  154. //9
  155. H3, 4+4+4,
  156. H3, 4,
  157. H6, 4+4,
  158. H5, 4,
  159. H5, 4,
  160. H3, 2,
  161. H2, 2,
  162. H1, 4+4,
  163. P, 2,
  164. H1, 2,
  165. //10
  166. H2, 4,
  167. H1, 2,
  168. H2, 2,
  169. H2, 4,
  170. H5, 4,
  171. H3, 4+4+4,
  172. H3, 4,
  173. H6, 4+4,
  174. H5, 4+4,
  175. //11
  176. H3, 2,
  177. H2, 2,
  178. H1, 4+4,
  179. P, 2,
  180. H1, 2,
  181. H2, 4,
  182. H1, 2,
  183. H2, 2+4,
  184. M7, 4,
  185. M6, 4+4+4,
  186. P, 4,
  187. 0xFF //终止标志
  188. };
  189. unsigned char FreqSelect,MusicSelect;
  190. void main()
  191. {
  192. Timer0Init();
  193. while(1)
  194. {
  195. if(Music[MusicSelect]!=0xFF) //如果不是停止标志位
  196. {
  197. FreqSelect=Music[MusicSelect]; //选择音符对应的频率
  198. MusicSelect++;
  199. Delay(SPEED/4*Music[MusicSelect]); //选择音符对应的时值
  200. MusicSelect++;
  201. TR0=0;
  202. Delay(5); //音符间短暂停顿
  203. TR0=1;
  204. }
  205. else //如果是停止标志位
  206. {
  207. TR0=0;
  208. while(1);
  209. }
  210. }
  211. }
  212. void Timer0_Routine() interrupt 1
  213. {
  214. if(FreqTable[FreqSelect]) //如果不是休止符
  215. {
  216. /*取对应频率值的重装载值到定时器*/
  217. TL0 = FreqTable[FreqSelect]%256; //设置定时初值
  218. TH0 = FreqTable[FreqSelect]/256; //设置定时初值
  219. Buzzer=!Buzzer; //翻转蜂鸣器IO口
  220. }
  221. }

二十七、单片机实操二十五:AT24C02(I2C总线)与数据存储

1、存储器介绍

 2、存储器简化模型

 3、AT24C02介绍

 4、引脚与芯片电路

 5、内部结构框图

 6、I2C总线介绍

 7、I2C电路规范

 8、I2C时序结构

 

 

 

 9、I2C数据帧

 

 10、AT24C02数据帧

 

11、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "LCD1602.h"
  3. #include "Key.h"
  4. #include "AT24C02.h"
  5. #include "Delay.h"
  6. unsigned char KeyNum;
  7. unsigned int Num;
  8. void main()
  9. {
  10. LCD_Init();
  11. LCD_ShowNum(1,1,Num,5);
  12. while(1)
  13. {
  14. KeyNum=Key();
  15. if(KeyNum==1) //K1按键,Num自增
  16. {
  17. Num++;
  18. LCD_ShowNum(1,1,Num,5);
  19. }
  20. if(KeyNum==2) //K2按键,Num自减
  21. {
  22. Num--;
  23. LCD_ShowNum(1,1,Num,5);
  24. }
  25. if(KeyNum==3) //K3按键,向AT24C02写入数据
  26. {
  27. AT24C02_WriteByte(0,Num%256);
  28. Delay(5);
  29. AT24C02_WriteByte(1,Num/256);
  30. Delay(5);
  31. LCD_ShowString(2,1,"Write OK");
  32. Delay(1000);
  33. LCD_ShowString(2,1," ");
  34. }
  35. if(KeyNum==4) //K4按键,从AT24C02读取数据
  36. {
  37. Num=AT24C02_ReadByte(0);
  38. Num|=AT24C02_ReadByte(1)<<8;
  39. LCD_ShowNum(1,1,Num,5);
  40. LCD_ShowString(2,1,"Read OK ");
  41. Delay(1000);
  42. LCD_ShowString(2,1," ");
  43. }
  44. }
  45. }

I2C.c

  1. #include <STC89C5xRC.H>
  2. sbit I2C_SCL=P2^1;
  3. sbit I2C_SDA=P2^0;
  4. /**
  5. * @brief I2C开始
  6. * @param 无
  7. * @retval 无
  8. */
  9. void I2C_Start(void)
  10. {
  11. I2C_SDA=1;
  12. I2C_SCL=1;
  13. I2C_SDA=0;
  14. I2C_SCL=0;
  15. }
  16. /**
  17. * @brief I2C停止
  18. * @param 无
  19. * @retval 无
  20. */
  21. void I2C_Stop(void)
  22. {
  23. I2C_SDA=0;
  24. I2C_SCL=1;
  25. I2C_SDA=1;
  26. }
  27. /**
  28. * @brief I2C发送一个字节
  29. * @param Byte 要发送的字节
  30. * @retval 无
  31. */
  32. void I2C_SendByte(unsigned char Byte)
  33. {
  34. unsigned char i;
  35. for(i=0;i<8;i++)
  36. {
  37. I2C_SDA=Byte&(0x80>>i);
  38. I2C_SCL=1;
  39. I2C_SCL=0;
  40. }
  41. }
  42. /**
  43. * @brief I2C接收一个字节
  44. * @param 无
  45. * @retval 接收到的一个字节数据
  46. */
  47. unsigned char I2C_ReceiveByte(void)
  48. {
  49. unsigned char i,Byte=0x00;
  50. I2C_SDA=1;
  51. for(i=0;i<8;i++)
  52. {
  53. I2C_SCL=1;
  54. if(I2C_SDA){Byte|=(0x80>>i);}
  55. I2C_SCL=0;
  56. }
  57. return Byte;
  58. }
  59. /**
  60. * @brief I2C发送应答
  61. * @param AckBit 应答位,0为应答,1为非应答
  62. * @retval 无
  63. */
  64. void I2C_SendAck(unsigned char AckBit)
  65. {
  66. I2C_SDA=AckBit;
  67. I2C_SCL=1;
  68. I2C_SCL=0;
  69. }
  70. /**
  71. * @brief I2C接收应答位
  72. * @param 无
  73. * @retval 接收到的应答位,0为应答,1为非应答
  74. */
  75. unsigned char I2C_ReceiveAck(void)
  76. {
  77. unsigned char AckBit;
  78. I2C_SDA=1;
  79. I2C_SCL=1;
  80. AckBit=I2C_SDA;
  81. I2C_SCL=0;
  82. return AckBit;
  83. }

I2C.h

  1. #ifndef __I2C_H__
  2. #define __I2C_H__
  3. void I2C_Start(void);
  4. void I2C_Stop(void);
  5. void I2C_SendByte(unsigned char Byte);
  6. unsigned char I2C_ReceiveByte(void);
  7. void I2C_SendAck(unsigned char AckBit);
  8. unsigned char I2C_ReceiveAck(void);
  9. #endif

AT21C02.c

  1. #include <STC89C5xRC.H>
  2. #include "I2C.h"
  3. #define AT24C02_ADDRESS 0xA0
  4. /**
  5. * @brief AT24C02写入一个字节
  6. * @param WordAddress 要写入字节的地址
  7. * @param Data 要写入的数据
  8. * @retval 无
  9. */
  10. void AT24C02_WriteByte(unsigned char WordAddress,Data)
  11. {
  12. I2C_Start();
  13. I2C_SendByte(AT24C02_ADDRESS);
  14. I2C_ReceiveAck();
  15. I2C_SendByte(WordAddress);
  16. I2C_ReceiveAck();
  17. I2C_SendByte(Data);
  18. I2C_ReceiveAck();
  19. I2C_Stop();
  20. }
  21. /**
  22. * @brief AT24C02读取一个字节
  23. * @param WordAddress 要读出字节的地址
  24. * @retval 读出的数据
  25. */
  26. unsigned char AT24C02_ReadByte(unsigned char WordAddress)
  27. {
  28. unsigned char Data;
  29. I2C_Start();
  30. I2C_SendByte(AT24C02_ADDRESS);
  31. I2C_ReceiveAck();
  32. I2C_SendByte(WordAddress);
  33. I2C_ReceiveAck();
  34. I2C_Start();
  35. I2C_SendByte(AT24C02_ADDRESS|0x01);
  36. I2C_ReceiveAck();
  37. Data=I2C_ReceiveByte();
  38. I2C_SendAck(1);
  39. I2C_Stop();
  40. return Data;
  41. }

AT21C02.h

  1. #ifndef __AT24C02_H__
  2. #define __AT24C02_H__
  3. void AT24C02_WriteByte(unsigned char WordAddress,Data);
  4. unsigned char AT24C02_ReadByte(unsigned char WordAddress);
  5. #endif

二十八、单片机实操二十六:秒表(定时器扫描按键数码管)

1、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "Timer0.h"
  3. #include "Key.h"
  4. #include "Nixie.h"
  5. #include "Delay.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) //K1按键按下
  17. {
  18. RunFlag=!RunFlag; //启动标志位翻转
  19. }
  20. if(KeyNum==2) //K2按键按下
  21. {
  22. Min=0; //分秒清0
  23. Sec=0;
  24. MiniSec=0;
  25. }
  26. if(KeyNum==3) //K3按键按下
  27. {
  28. AT24C02_WriteByte(0,Min); //将分秒写入AT24C02
  29. Delay(5);
  30. AT24C02_WriteByte(1,Sec);
  31. Delay(5);
  32. AT24C02_WriteByte(2,MiniSec);
  33. Delay(5);
  34. }
  35. if(KeyNum==4) //K4按键按下
  36. {
  37. Min=AT24C02_ReadByte(0); //读出AT24C02数据
  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. * @brief 秒表驱动函数,在中断中调用
  53. * @param 无
  54. * @retval 无
  55. */
  56. void Sec_Loop(void)
  57. {
  58. if(RunFlag)
  59. {
  60. MiniSec++;
  61. if(MiniSec>=100)
  62. {
  63. MiniSec=0;
  64. Sec++;
  65. if(Sec>=60)
  66. {
  67. Sec=0;
  68. Min++;
  69. if(Min>=60)
  70. {
  71. Min=0;
  72. }
  73. }
  74. }
  75. }
  76. }
  77. void Timer0_Routine() interrupt 1
  78. {
  79. static unsigned int T0Count1,T0Count2,T0Count3;
  80. TL0 = 0x18; //设置定时初值
  81. TH0 = 0xFC; //设置定时初值
  82. T0Count1++;
  83. if(T0Count1>=20)
  84. {
  85. T0Count1=0;
  86. Key_Loop(); //20ms调用一次按键驱动函数
  87. }
  88. T0Count2++;
  89. if(T0Count2>=2)
  90. {
  91. T0Count2=0;
  92. Nixie_Loop();//2ms调用一次数码管驱动函数
  93. }
  94. T0Count3++;
  95. if(T0Count3>=10)
  96. {
  97. T0Count3=0;
  98. Sec_Loop(); //10ms调用一次数秒表驱动函数
  99. }
  100. }

Key.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h"
  3. unsigned char Key_KeyNumber;
  4. /**
  5. * @brief 获取按键键码
  6. * @param 无
  7. * @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
  8. */
  9. unsigned char Key(void)
  10. {
  11. unsigned char Temp=0;
  12. Temp=Key_KeyNumber;
  13. Key_KeyNumber=0;
  14. return Temp;
  15. }
  16. /**
  17. * @brief 获取当前按键的状态,无消抖及松手检测
  18. * @param 无
  19. * @retval 按下按键的键码,范围:0,1~4,0表示无按键按下
  20. */
  21. unsigned char Key_GetState()
  22. {
  23. unsigned char KeyNumber=0;
  24. if(P31==0){KeyNumber=1;}
  25. if(P30==0){KeyNumber=2;}
  26. if(P32==0){KeyNumber=3;}
  27. if(P33==0){KeyNumber=4;}
  28. return KeyNumber;
  29. }
  30. /**
  31. * @brief 按键驱动函数,在中断中调用
  32. * @param 无
  33. * @retval 无
  34. */
  35. void Key_Loop(void)
  36. {
  37. static unsigned char NowState,LastState;
  38. LastState=NowState; //按键状态更新
  39. NowState=Key_GetState(); //获取当前按键状态
  40. //如果上个时间点按键按下,这个时间点未按下,则是松手瞬间,以此避免消抖和松手检测
  41. if(LastState==1 && NowState==0)
  42. {
  43. Key_KeyNumber=1;
  44. }
  45. if(LastState==2 && NowState==0)
  46. {
  47. Key_KeyNumber=2;
  48. }
  49. if(LastState==3 && NowState==0)
  50. {
  51. Key_KeyNumber=3;
  52. }
  53. if(LastState==4 && NowState==0)
  54. {
  55. Key_KeyNumber=4;
  56. }
  57. }

Key.h

  1. #ifndef __KEY_H__
  2. #define __KEY_H__
  3. unsigned char Key(void);
  4. void Key_Loop(void);
  5. #endif

Nixie.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h"
  3. //数码管显示缓存区
  4. unsigned char Nixie_Buf[9]={0,10,10,10,10,10,10,10,10};
  5. //数码管段码表
  6. unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40};
  7. /**
  8. * @brief 设置显示缓存区
  9. * @param Location 要设置的位置,范围:1~8
  10. * @param Number 要设置的数字,范围:段码表索引范围
  11. * @retval 无
  12. */
  13. void Nixie_SetBuf(unsigned char Location,Number)
  14. {
  15. Nixie_Buf[Location]=Number;
  16. }
  17. /**
  18. * @brief 数码管扫描显示
  19. * @param Location 要显示的位置,范围:1~8
  20. * @param Number 要显示的数字,范围:段码表索引范围
  21. * @retval 无
  22. */
  23. void Nixie_Scan(unsigned char Location,Number)
  24. {
  25. P0=0x00; //段码清0,消影
  26. switch(Location) //位码输出
  27. {
  28. case 1:P24=1;P23=1;P22=1;break;
  29. case 2:P24=1;P23=1;P22=0;break;
  30. case 3:P24=1;P23=0;P22=1;break;
  31. case 4:P24=1;P23=0;P22=0;break;
  32. case 5:P24=0;P23=1;P22=1;break;
  33. case 6:P24=0;P23=1;P22=0;break;
  34. case 7:P24=0;P23=0;P22=1;break;
  35. case 8:P24=0;P23=0;P22=0;break;
  36. }
  37. P0=NixieTable[Number]; //段码输出
  38. }
  39. /**
  40. * @brief 数码管驱动函数,在中断中调用
  41. * @param 无
  42. * @retval 无
  43. */
  44. void Nixie_Loop(void)
  45. {
  46. static unsigned char i=1;
  47. Nixie_Scan(i,Nixie_Buf[i]);
  48. i++;
  49. if(i>=9){i=1;}
  50. }

Nixie.h

  1. #ifndef __NIXIE_H__
  2. #define __NIXIE_H__
  3. void Nixie_SetBuf(unsigned char Location,Number);
  4. void Nixie_Scan(unsigned char Location,Number);
  5. void Nixie_Loop(void);
  6. #endif

二十九、单片机实操二十七:DS18B20温度传感器与温度读取

1、DS18B20介绍

2、 引脚及应用电路

 3、内部结构图

 4、存储器结构

 5、单总线介绍

 6、单总线电路规范

 7、单总线时序结构

 

 

 

 8、DS18B20操作流程

9、DS18B20数据帧 

 10、温度存储格式

 11、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "LCD1602.h"
  3. #include "DS18B20.h"
  4. #include "Delay.h"
  5. #include "AT24C02.h"
  6. #include "Key.h"
  7. #include "Timer0.h"
  8. float T,TShow;
  9. char TLow,THigh;
  10. unsigned char KeyNum;
  11. void main()
  12. {
  13. DS18B20_ConvertT(); //上电先转换一次温度,防止第一次读数据错误
  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_ConvertT(); //转换温度
  34. T=DS18B20_ReadT(); //读取温度
  35. if(T<0) //如果温度小于0
  36. {
  37. LCD_ShowChar(1,3,'-'); //显示负号
  38. TShow=-T; //将温度变为正数
  39. }
  40. else //如果温度大于等于0
  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) //K1按键,THigh自增
  52. {
  53. THigh++;
  54. if(THigh>125){THigh=125;}
  55. }
  56. if(KeyNum==2) //K2按键,THigh自减
  57. {
  58. THigh--;
  59. if(THigh<=TLow){THigh++;}
  60. }
  61. if(KeyNum==3) //K3按键,TLow自增
  62. {
  63. TLow++;
  64. if(TLow>=THigh){TLow--;}
  65. }
  66. if(KeyNum==4) //K4按键,TLow自减
  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); //写入到At24C02中保存
  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
  87. {
  88. LCD_ShowString(1,13," ");
  89. }
  90. }
  91. }
  92. void Timer0_Routine() interrupt 1
  93. {
  94. static unsigned int T0Count;
  95. TL0 = 0x18; //设置定时初值
  96. TH0 = 0xFC; //设置定时初值
  97. T0Count++;
  98. if(T0Count>=20)
  99. {
  100. T0Count=0;
  101. Key_Loop(); //每20ms调用一次按键驱动函数
  102. }
  103. }

OneWrite.c

  1. #include <STC89C5xRC.H>
  2. //引脚定义
  3. sbit OneWire_DQ=P3^7;
  4. /**
  5. * @brief 单总线初始化
  6. * @param 无
  7. * @retval 从机响应位,0为响应,1为未响应
  8. */
  9. unsigned char OneWire_Init(void)
  10. {
  11. unsigned char i;
  12. unsigned char AckBit;
  13. OneWire_DQ=1;
  14. OneWire_DQ=0;
  15. i = 247;while (--i); //Delay 500us
  16. OneWire_DQ=1;
  17. i = 32;while (--i); //Delay 70us
  18. AckBit=OneWire_DQ;
  19. i = 247;while (--i); //Delay 500us
  20. return AckBit;
  21. }
  22. /**
  23. * @brief 单总线发送一位
  24. * @param Bit 要发送的位
  25. * @retval 无
  26. */
  27. void OneWire_SendBit(unsigned char Bit)
  28. {
  29. unsigned char i;
  30. OneWire_DQ=0;
  31. i = 4;while (--i); //Delay 10us
  32. OneWire_DQ=Bit;
  33. i = 24;while (--i); //Delay 50us
  34. OneWire_DQ=1;
  35. }
  36. /**
  37. * @brief 单总线接收一位
  38. * @param 无
  39. * @retval 读取的位
  40. */
  41. unsigned char OneWire_ReceiveBit(void)
  42. {
  43. unsigned char i;
  44. unsigned char Bit;
  45. OneWire_DQ=0;
  46. i = 2;while (--i); //Delay 5us
  47. OneWire_DQ=1;
  48. i = 2;while (--i); //Delay 5us
  49. Bit=OneWire_DQ;
  50. i = 24;while (--i); //Delay 50us
  51. return Bit;
  52. }
  53. /**
  54. * @brief 单总线发送一个字节
  55. * @param Byte 要发送的字节
  56. * @retval 无
  57. */
  58. void OneWire_SendByte(unsigned char Byte)
  59. {
  60. unsigned char i;
  61. for(i=0;i<8;i++)
  62. {
  63. OneWire_SendBit(Byte&(0x01<<i));
  64. }
  65. }
  66. /**
  67. * @brief 单总线接收一个字节
  68. * @param 无
  69. * @retval 接收的一个字节
  70. */
  71. unsigned char OneWire_ReceiveByte(void)
  72. {
  73. unsigned char i;
  74. unsigned char Byte=0x00;
  75. for(i=0;i<8;i++)
  76. {
  77. if(OneWire_ReceiveBit()){Byte|=(0x01<<i);}
  78. }
  79. return Byte;
  80. }

OneWrite.h

  1. #ifndef __ONEWIRE_H__
  2. #define __ONEWIRE_H__
  3. unsigned char OneWire_Init(void);
  4. void OneWire_SendBit(unsigned char Bit);
  5. unsigned char OneWire_ReceiveBit(void);
  6. void OneWire_SendByte(unsigned char Byte);
  7. unsigned char OneWire_ReceiveByte(void);
  8. #endif

三十、单片机实操二十八:温度报警器

1、编写代码

main.c

  1. #include <STC89C5xRC.H>
  2. #include "LCD1602.h"
  3. #include "DS18B20.h"
  4. #include "Delay.h"
  5. #include "AT24C02.h"
  6. #include "Key.h"
  7. #include "Timer0.h"
  8. float T,TShow;
  9. char TLow,THigh;
  10. unsigned char KeyNum;
  11. void main()
  12. {
  13. DS18B20_ConvertT(); //上电先转换一次温度,防止第一次读数据错误
  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_ConvertT(); //转换温度
  34. T=DS18B20_ReadT(); //读取温度
  35. if(T<0) //如果温度小于0
  36. {
  37. LCD_ShowChar(1,3,'-'); //显示负号
  38. TShow=-T; //将温度变为正数
  39. }
  40. else //如果温度大于等于0
  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) //K1按键,THigh自增
  52. {
  53. THigh++;
  54. if(THigh>125){THigh=125;}
  55. }
  56. if(KeyNum==2) //K2按键,THigh自减
  57. {
  58. THigh--;
  59. if(THigh<=TLow){THigh++;}
  60. }
  61. if(KeyNum==3) //K3按键,TLow自增
  62. {
  63. TLow++;
  64. if(TLow>=THigh){TLow--;}
  65. }
  66. if(KeyNum==4) //K4按键,TLow自减
  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); //写入到At24C02中保存
  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
  87. {
  88. LCD_ShowString(1,13," ");
  89. }
  90. }
  91. }
  92. void Timer0_Routine() interrupt 1
  93. {
  94. static unsigned int T0Count;
  95. TL0 = 0x18; //设置定时初值
  96. TH0 = 0xFC; //设置定时初值
  97. T0Count++;
  98. if(T0Count>=20)
  99. {
  100. T0Count=0;
  101. Key_Loop(); //每20ms调用一次按键驱动函数
  102. }
  103. }

三十一、单片机实操二十九:LCD1602与功能函数代码

1、LCD1602介绍

 2、引脚及应用电路

3、内部结构框图

 4、存储器结构

 5、时序结构

 6、LS1602指令集

 7、LCD1602操作流程

 8、编写代码

详见LCD1602模块。

三十二、单片机实操三十:直流电机驱动(PWM)与LED呼吸灯

1、直流电机介绍

2、电机驱动电路

 3、PWM介绍

4、产生PWM的方法

5、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. sbit LED=P2^0;
  3. void Delay(unsigned int t)
  4. {
  5. while(t--);
  6. }
  7. void main()
  8. {
  9. unsigned char Time,i;
  10. while(1)
  11. {
  12. for(Time=0;Time<100;Time++) //改变亮灭时间,由暗到亮
  13. {
  14. for(i=0;i<20;i++) //计次延时
  15. {
  16. LED=0; //LED亮
  17. Delay(Time); //延时Time
  18. LED=1; //LED灭
  19. Delay(100-Time); //延时100-Time
  20. }
  21. }
  22. for(Time=100;Time>0;Time--) //改变亮灭时间,由亮到暗
  23. {
  24. for(i=0;i<20;i++) //计次延时
  25. {
  26. LED=0; //LED亮
  27. Delay(Time); //延时Time
  28. LED=1; //LED灭
  29. Delay(100-Time); //延时100-Time
  30. }
  31. }
  32. }
  33. }

三十三、单片机实操三十一:直流电机调速

1、编写程序

main.c

  1. #include <STC89C5xRC.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; //计数值和比较值,用于输出PWM
  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;} //设置比较值,改变PWM占空比
  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 = 0x9C; //设置定时初值
  30. TH0 = 0xFF; //设置定时初值
  31. Counter++;
  32. Counter%=100; //计数值变化范围限制在0~99
  33. if(Counter<Compare) //计数值小于比较值
  34. {
  35. Motor=1; //输出1
  36. }
  37. else //计数值大于比较值
  38. {
  39. Motor=0; //输出0
  40. }
  41. }

三十四、单片机实操三十二:AD/DA之AD模数转换

1、AD/DA介绍

 2、硬件电路模型

 3、硬件电路

 

 4、运算放大器

 5、运放电路

 

 6、DA原理

 7、AD原理

 8、AD/DA性能指标

9、XPT2046功能说明

10、 XPT2046时序

 11、开发板原理图

8、编写代码

main.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h"
  3. #include "LCD1602.h"
  4. #include "XPT2046.h"
  5. unsigned int ADValue;
  6. void main(void)
  7. {
  8. LCD_Init();
  9. LCD_ShowString(1,1,"ADJ NTC GR");
  10. while(1)
  11. {
  12. ADValue=XPT2046_ReadAD(XPT2046_XP); //读取AIN0,可调电阻
  13. LCD_ShowNum(2,1,ADValue,3); //显示AIN0
  14. ADValue=XPT2046_ReadAD(XPT2046_YP); //读取AIN1,热敏电阻
  15. LCD_ShowNum(2,6,ADValue,3); //显示AIN1
  16. ADValue=XPT2046_ReadAD(XPT2046_VBAT); //读取AIN2,光敏电阻
  17. LCD_ShowNum(2,11,ADValue,3); //显示AIN2
  18. Delay(100);
  19. }
  20. }

XPT2046.c

  1. #include <STC89C5xRC.H>
  2. #include <INTRINS.H>
  3. //引脚定义
  4. sbit XPY2046_DIN=P3^4;
  5. sbit XPY2046_CS=P3^5;
  6. sbit XPY2046_DCLK=P3^6;
  7. sbit XPY2046_DOUT=P3^7;
  8. /**
  9. * @brief ZPT2046读取AD值
  10. * @param Command 命令字,范围:头文件内定义的宏,结尾的数字表示转换的位数
  11. * @retval AD转换后的数字量,范围:8位为0~255,12位为0~4095
  12. */
  13. unsigned int XPT2046_ReadAD(unsigned char Command)
  14. {
  15. unsigned char i;
  16. unsigned int Data=0;
  17. XPY2046_DCLK=0;
  18. XPY2046_CS=0;
  19. for(i=0;i<8;i++)
  20. {
  21. XPY2046_DIN=Command&(0x80>>i);
  22. XPY2046_DCLK=1;
  23. XPY2046_DCLK=0;
  24. }
  25. for(i=0;i<16;i++)
  26. {
  27. XPY2046_DCLK=1;
  28. XPY2046_DCLK=0;
  29. if(XPY2046_DOUT){Data|=(0x8000>>i);}
  30. }
  31. XPY2046_CS=1;
  32. return Data>>8;
  33. }

XPT2046.h

  1. #ifndef __XPT2046_H__
  2. #define __XPT2046_H__
  3. #define XPT2046_VBAT 0xAC
  4. #define XPT2046_AUX 0xEC
  5. #define XPT2046_XP 0x9C //0xBC
  6. #define XPT2046_YP 0xDC
  7. unsigned int XPT2046_ReadAD(unsigned char Command);
  8. #endif

三十五、单片机实操三十三:DA数模转换

1、编写代码

main.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h"
  3. #include "Timer0.h"
  4. sbit DA=P2^1;
  5. unsigned char Counter,Compare; //计数值和比较值,用于输出PWM
  6. unsigned char i;
  7. void main()
  8. {
  9. Timer0_Init();
  10. while(1)
  11. {
  12. for(i=0;i<100;i++)
  13. {
  14. Compare=i; //设置比较值,改变PWM占空比
  15. Delay(10);
  16. }
  17. for(i=100;i>0;i--)
  18. {
  19. Compare=i; //设置比较值,改变PWM占空比
  20. Delay(10);
  21. }
  22. }
  23. }
  24. void Timer0_Routine() interrupt 1
  25. {
  26. TL0 = 0x9C; //设置定时初值
  27. TH0 = 0xFF; //设置定时初值
  28. Counter++;
  29. Counter%=100; //计数值变化范围限制在0~99
  30. if(Counter<Compare) //计数值小于比较值
  31. {
  32. DA=1; //输出1
  33. }
  34. else //计数值大于比较值
  35. {
  36. DA=0; //输出0
  37. }
  38. }

三十六、单片机实操三十四:红外遥控与外部中断

1、红外遥控简介

2、 硬件电路

 3、基本发送与接收

4、NEC编码

5、 遥控器键码

 6、51单片机的外部中断

 7、外部中断寄存器

8、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h"
  3. #include "LCD1602.h"
  4. #include "IR.h"
  5. unsigned char Num;
  6. unsigned char Address;
  7. unsigned char Command;
  8. void main()
  9. {
  10. LCD_Init();
  11. LCD_ShowString(1,1,"ADDR CMD NUM");
  12. LCD_ShowString(2,1,"00 00 000");
  13. IR_Init();
  14. while(1)
  15. {
  16. if(IR_GetDataFlag() || IR_GetRepeatFlag()) //如果收到数据帧或者收到连发帧
  17. {
  18. Address=IR_GetAddress(); //获取遥控器地址码
  19. Command=IR_GetCommand(); //获取遥控器命令码
  20. LCD_ShowHexNum(2,1,Address,2); //显示遥控器地址码
  21. LCD_ShowHexNum(2,7,Command,2); //显示遥控器命令码
  22. if(Command==IR_VOL_MINUS) //如果遥控器VOL-按键按下
  23. {
  24. Num--; //Num自减
  25. }
  26. if(Command==IR_VOL_ADD) //如果遥控器VOL+按键按下
  27. {
  28. Num++; //Num自增
  29. }
  30. LCD_ShowNum(2,12,Num,3); //显示Num
  31. }
  32. }
  33. }

IR.c

  1. #include <STC89C5xRC.H>
  2. #include "Timer0.h"
  3. #include "Int0.h"
  4. unsigned int IR_Time;
  5. unsigned char IR_State;
  6. unsigned char IR_Data[4];
  7. unsigned char IR_pData;
  8. unsigned char IR_DataFlag;
  9. unsigned char IR_RepeatFlag;
  10. unsigned char IR_Address;
  11. unsigned char IR_Command;
  12. /**
  13. * @brief 红外遥控初始化
  14. * @param 无
  15. * @retval 无
  16. */
  17. void IR_Init(void)
  18. {
  19. Timer0_Init();
  20. Int0_Init();
  21. }
  22. /**
  23. * @brief 红外遥控获取收到数据帧标志位
  24. * @param 无
  25. * @retval 是否收到数据帧,1为收到,0为未收到
  26. */
  27. unsigned char IR_GetDataFlag(void)
  28. {
  29. if(IR_DataFlag)
  30. {
  31. IR_DataFlag=0;
  32. return 1;
  33. }
  34. return 0;
  35. }
  36. /**
  37. * @brief 红外遥控获取收到连发帧标志位
  38. * @param 无
  39. * @retval 是否收到连发帧,1为收到,0为未收到
  40. */
  41. unsigned char IR_GetRepeatFlag(void)
  42. {
  43. if(IR_RepeatFlag)
  44. {
  45. IR_RepeatFlag=0;
  46. return 1;
  47. }
  48. return 0;
  49. }
  50. /**
  51. * @brief 红外遥控获取收到的地址数据
  52. * @param 无
  53. * @retval 收到的地址数据
  54. */
  55. unsigned char IR_GetAddress(void)
  56. {
  57. return IR_Address;
  58. }
  59. /**
  60. * @brief 红外遥控获取收到的命令数据
  61. * @param 无
  62. * @retval 收到的命令数据
  63. */
  64. unsigned char IR_GetCommand(void)
  65. {
  66. return IR_Command;
  67. }
  68. //外部中断0中断函数,下降沿触发执行
  69. void Int0_Routine(void) interrupt 0
  70. {
  71. if(IR_State==0) //状态0,空闲状态
  72. {
  73. Timer0_SetCounter(0); //定时计数器清0
  74. Timer0_Run(1); //定时器启动
  75. IR_State=1; //置状态为1
  76. }
  77. else if(IR_State==1) //状态1,等待Start信号或Repeat信号
  78. {
  79. IR_Time=Timer0_GetCounter(); //获取上一次中断到此次中断的时间
  80. Timer0_SetCounter(0); //定时计数器清0
  81. //如果计时为13.5ms,则接收到了Start信号(判定值在12MHz晶振下为13500,在11.0592MHz晶振下为12442)
  82. if(IR_Time>12442-500 && IR_Time<12442+500)
  83. {
  84. IR_State=2; //置状态为2
  85. }
  86. //如果计时为11.25ms,则接收到了Repeat信号(判定值在12MHz晶振下为11250,在11.0592MHz晶振下为10368)
  87. else if(IR_Time>10368-500 && IR_Time<10368+500)
  88. {
  89. IR_RepeatFlag=1; //置收到连发帧标志位为1
  90. Timer0_Run(0); //定时器停止
  91. IR_State=0; //置状态为0
  92. }
  93. else //接收出错
  94. {
  95. IR_State=1; //置状态为1
  96. }
  97. }
  98. else if(IR_State==2) //状态2,接收数据
  99. {
  100. IR_Time=Timer0_GetCounter(); //获取上一次中断到此次中断的时间
  101. Timer0_SetCounter(0); //定时计数器清0
  102. //如果计时为1120us,则接收到了数据0(判定值在12MHz晶振下为1120,在11.0592MHz晶振下为1032)
  103. if(IR_Time>1032-500 && IR_Time<1032+500)
  104. {
  105. IR_Data[IR_pData/8]&=~(0x01<<(IR_pData%8)); //数据对应位清0
  106. IR_pData++; //数据位置指针自增
  107. }
  108. //如果计时为2250us,则接收到了数据1(判定值在12MHz晶振下为2250,在11.0592MHz晶振下为2074)
  109. else if(IR_Time>2074-500 && IR_Time<2074+500)
  110. {
  111. IR_Data[IR_pData/8]|=(0x01<<(IR_pData%8)); //数据对应位置1
  112. IR_pData++; //数据位置指针自增
  113. }
  114. else //接收出错
  115. {
  116. IR_pData=0; //数据位置指针清0
  117. IR_State=1; //置状态为1
  118. }
  119. if(IR_pData>=32) //如果接收到了32位数据
  120. {
  121. IR_pData=0; //数据位置指针清0
  122. if((IR_Data[0]==~IR_Data[1]) && (IR_Data[2]==~IR_Data[3])) //数据验证
  123. {
  124. IR_Address=IR_Data[0]; //转存数据
  125. IR_Command=IR_Data[2];
  126. IR_DataFlag=1; //置收到连发帧标志位为1
  127. }
  128. Timer0_Run(0); //定时器停止
  129. IR_State=0; //置状态为0
  130. }
  131. }
  132. }

IR.h

  1. #ifndef __IR_H__
  2. #define __IR_H__
  3. #define IR_POWER 0x45
  4. #define IR_MODE 0x46
  5. #define IR_MUTE 0x47
  6. #define IR_START_STOP 0x44
  7. #define IR_PREVIOUS 0x40
  8. #define IR_NEXT 0x43
  9. #define IR_EQ 0x07
  10. #define IR_VOL_MINUS 0x15
  11. #define IR_VOL_ADD 0x09
  12. #define IR_0 0x16
  13. #define IR_RPT 0x19
  14. #define IR_USD 0x0D
  15. #define IR_1 0x0C
  16. #define IR_2 0x18
  17. #define IR_3 0x5E
  18. #define IR_4 0x08
  19. #define IR_5 0x1C
  20. #define IR_6 0x5A
  21. #define IR_7 0x42
  22. #define IR_8 0x52
  23. #define IR_9 0x4A
  24. void IR_Init(void);
  25. unsigned char IR_GetDataFlag(void);
  26. unsigned char IR_GetRepeatFlag(void);
  27. unsigned char IR_GetAddress(void);
  28. unsigned char IR_GetCommand(void);
  29. #endif

Int0.c

  1. #include <STC89C5xRC.H>
  2. /**
  3. * @brief 外部中断0初始化
  4. * @param 无
  5. * @retval 无
  6. */
  7. void Int0_Init(void)
  8. {
  9. IT0=1;
  10. IE0=0;
  11. EX0=1;
  12. EA=1;
  13. PX0=1;
  14. }
  15. /*外部中断0中断函数模板
  16. void Int0_Routine(void) interrupt 0
  17. {
  18. }
  19. */

Int0.h

  1. #ifndef __INT0_H__
  2. #define __INT0_H__
  3. void Int0_Init(void);
  4. #endif

Timer0.c

  1. #include <STC89C5xRC.H>
  2. /**
  3. * @brief 定时器0初始化
  4. * @param 无
  5. * @retval 无
  6. */
  7. void Timer0_Init(void)
  8. {
  9. TMOD &= 0xF0; //设置定时器模式
  10. TMOD |= 0x01; //设置定时器模式
  11. TL0 = 0; //设置定时初值
  12. TH0 = 0; //设置定时初值
  13. TF0 = 0; //清除TF0标志
  14. TR0 = 0; //定时器0不计时
  15. }
  16. /**
  17. * @brief 定时器0设置计数器值
  18. * @param Value,要设置的计数器值,范围:0~65535
  19. * @retval 无
  20. */
  21. void Timer0_SetCounter(unsigned int Value)
  22. {
  23. TH0=Value/256;
  24. TL0=Value%256;
  25. }
  26. /**
  27. * @brief 定时器0获取计数器值
  28. * @param 无
  29. * @retval 计数器值,范围:0~65535
  30. */
  31. unsigned int Timer0_GetCounter(void)
  32. {
  33. return (TH0<<8)|TL0;
  34. }
  35. /**
  36. * @brief 定时器0启动停止控制
  37. * @param Flag 启动停止标志,1为启动,0为停止
  38. * @retval 无
  39. */
  40. void Timer0_Run(unsigned char Flag)
  41. {
  42. TR0=Flag;
  43. }

Timer0.h

  1. #ifndef __TIMER0_H__
  2. #define __TIMER0_H__
  3. void Timer0_Init(void);
  4. void Timer0_SetCounter(unsigned int Value);
  5. unsigned int Timer0_GetCounter(void);
  6. void Timer0_Run(unsigned char Flag);
  7. #endif

三十七、单片机实操三十五:红外遥控电机调速

1、编写程序

main.c

  1. #include <STC89C5xRC.H>
  2. #include "Delay.h"
  3. #include "Key.h"
  4. #include "Nixie.h"
  5. #include "Motor.h"
  6. #include "IR.h"
  7. unsigned char Command,Speed;
  8. void main()
  9. {
  10. Motor_Init();
  11. IR_Init();
  12. while(1)
  13. {
  14. if(IR_GetDataFlag()) //如果收到数据帧
  15. {
  16. Command=IR_GetCommand(); //获取遥控器命令码
  17. if(Command==IR_0){Speed=0;} //根据遥控器命令码设置速度
  18. if(Command==IR_1){Speed=1;}
  19. if(Command==IR_2){Speed=2;}
  20. if(Command==IR_3){Speed=3;}
  21. if(Speed==0){Motor_SetSpeed(0);} //速度输出
  22. if(Speed==1){Motor_SetSpeed(50);}
  23. if(Speed==2){Motor_SetSpeed(75);}
  24. if(Speed==3){Motor_SetSpeed(100);}
  25. }
  26. Nixie(1,Speed); //数码管显示速度
  27. }
  28. }

Time1.c

  1. #include <STC89C5xRC.H>
  2. /**
  3. * @brief 定时器1初始化,100us@12.000MHz
  4. * @param 无
  5. * @retval 无
  6. */
  7. void Timer1_Init(void)
  8. {
  9. TMOD &= 0x0F; //设置定时器模式
  10. TMOD |= 0x10; //设置定时器模式
  11. TL1 = 0x9C; //设置定时初值
  12. TH1 = 0xFF; //设置定时初值
  13. TF1 = 0; //清除TF1标志
  14. TR1 = 1; //定时器1开始计时
  15. ET1=1;
  16. EA=1;
  17. PT1=0;
  18. }
  19. /*定时器中断函数模板
  20. void Timer1_Routine() interrupt 3
  21. {
  22. static unsigned int T1Count;
  23. TL1 = 0x9C; //设置定时初值
  24. TH1 = 0xFF; //设置定时初值
  25. T1Count++;
  26. if(T1Count>=1000)
  27. {
  28. T1Count=0;
  29. }
  30. }
  31. */

Time1.h

  1. #ifndef __TIMER1_H__
  2. #define __TIMER1_H__
  3. void Timer1_Init(void);
  4. #endif

Motor.c

  1. #include <STC89C5xRC.H>
  2. #include "Timer1.h"
  3. //引脚定义
  4. sbit Motor=P1^0;
  5. unsigned char Counter,Compare;
  6. /**
  7. * @brief 电机初始化
  8. * @param 无
  9. * @retval 无
  10. */
  11. void Motor_Init(void)
  12. {
  13. Timer1_Init();
  14. }
  15. /**
  16. * @brief 电机设置速度
  17. * @param Speed 要设置的速度,范围0~100
  18. * @retval 无
  19. */
  20. void Motor_SetSpeed(unsigned char Speed)
  21. {
  22. Compare=Speed;
  23. }
  24. //定时器1中断函数
  25. void Timer1_Routine() interrupt 3
  26. {
  27. TL1 = 0x9C; //设置定时初值
  28. TH1 = 0xFF; //设置定时初值
  29. Counter++;
  30. Counter%=100; //计数值变化范围限制在0~99
  31. if(Counter<Compare) //计数值小于比较值
  32. {
  33. Motor=1; //输出1
  34. }
  35. else //计数值大于比较值
  36. {
  37. Motor=0; //输出0
  38. }
  39. }

Motor.h

  1. #ifndef __MOTOR_H__
  2. #define __MOTOR_H__
  3. void Motor_Init(void);
  4. void Motor_SetSpeed(unsigned char Speed);
  5. #endif

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

闽ICP备14008679号