51单片机的入门知识(献给初学者最易懂的文章)更新篇

举报
刘斯淇 发表于 2022/04/18 23:48:28 2022/04/18
【摘要】 51单片机按键锁功能以及LCD1602驱动C语言代码的实现

数码管介绍

LED数码管:数码管是一种简单,廉价的显示器,是由多个发光二极管封装在一起组成“8”字形的器件.

 74HC138译码器(这个74貌似已经停产了!)来个简称这个74其实可以叫做三八译码器

三位控制八位!

(提一提C语言的数组):
•数组:把相同类型的一系列数据统一编制到某一个组别中,可以通过数组名+索引号简单快捷的操作大量数据

intx[3];  //定义一组变量(3个)

intx[]={1,2,3};  //定义一组变量并初始化

x[0]   //引用数组的第0个变量

x[1]   //引用数组的第1个变量

x[2]  //引用数组的第2个变量

引用x[3]时,数组越界,读出的数值不确定,应避免这种操作(防止越界!!!)

还有一个函数的调用具体可以参考我C语言基础笔记.

这里有一个问题(数据串位)!!,这个问题其实很简单,中间加一个清零!

  1. #include <REGX52.H>  
  2. unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F}'  
  3. void delay(unsigned int xms){  
  4.     unsigned char i,j;  
  5.     while(xms--){  
  6.         i=2;  
  7.         j=239;  
  8.         do{  
  9.             while(--j);  
  10.         }while(--i);  
  11.     }  
  12. }  
  13. void Nixie(unsigned char Location ,Number){  
  14.     switch (Location){  
  15.         case 1:P2_4=1;P2_3=1;P2_2=1;break;  
  16.         case 2:P2_4=1;P2_3=1;P2_2=1 ;break;  
  17.         case 3:P2_4=1;P2_3=0;P2_2=0 ;break;  
  18.         case 4:P2_4=1;P2_3=0;P2_2=1 ;break;  
  19.         case 5:P2_4=0;P2_3=1;P2_2=1;break;  
  20.         case 6:P2_4=0;P2_3=1;P2_2=0 ;break;  
  21.         case 7:P2_4=0;P2_3=0;P2_2=1 ;break;  
  22.         case 8:P2_4=0;P2_3=0;P2_2=0 ;break;  
  23.     }  
  24.     P0= NixieTable[Number];  
  25.        delay(200);  
  26.     P0=0x00;  
  27. }         
  28. void main(){  
  29.       
  30.     while(1){  
  31.             Nixie(1,1);  
  32.             delay(200);  
  33.             Nixie(2,2);  
  34.             delay(200);  
  35.             Nixie(3,3);  
  36.             delay(200);  
  37.         }  

模块化编程:
这里提一提模块化编程!!!

1.传统方式编程:所有的函数均放在main.c里,若使用的模块比较多,则一个文件内会有很多的代码,不利于代码的组织和管理,而且很影响编程者的思路

2.模块化编程:把各个模块的代码放在不同的.c文件里,在.h文件里提供外部可调用函数的声明,其它.c文件想使用其中的代码时,只需要#include "XXX.h"文件即可。使用模块化编程可极大的提高代码的可阅读性、可维护性、可移植性等

总结来说就是:将其他.C文件(必须是有定义和申明的)用来调用过去!!这种方法更加节省时间!更有效果!

•.c文件:函数、变量的定义

•.h文件:可被外部调用的函数、变量的声明

•任何自定义的变量、函数在调用前必须有定义或声明(同一个.c)

•使用到的自定义函数的.c文件必须添加到工程参与编译

•使用到的.h文件必须要放在编译器可寻找到的地方(工程文件夹根目录、安装目录、自定义)

LCD1602基本操作

LCDLCD_Init(); _ShowChar(1,1,'A'); 初始化

LCD_ShowString(1,3,"Hello"); 显示一个字符

LCD_ShowNum(1,9,123,3); 显示字符串

LCD_ShowSignedNum(1,13,-66,2); 显示十进制数字

LCD_ShowHexNum(2,1,0xA8,2); 显示有符号十进制数字

LCD_ShowBinNum(2,4,0xAA,8); 显示二进制数字

这里矩阵键盘的使用与LCD1602的模块一起可以实现一个密码锁

矩阵键盘

矩阵键盘其实就是为了减少IO位置占用

矩阵键盘扫描(它是一种输入扫描),原理:读取第一行第二行,然后快速循环整个过程,最终实现按键的按键效果!

实现动态扫描矩阵键盘的扫描:

  1. #include <REGX52.H>  
  2.  #include "Delay.h"  
  3. unsigned char Matrixkey(){  
  4.     unsigned char KeyNumber=0;  
  5.       
  6.     P1=0xFF;  
  7.     P1_3=0;  
  8.     if(P1_7==0){Delay(20);while(P1_7==0);Delay(20); KeyNumber=1;}  
  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.           
  13.    
  14.     P1=0xFF;  
  15.     P1_2=0;  
  16.     if(P1_7==0){Delay(20);while(P1_7==0);Delay(20); KeyNumber=2;}  
  17.     if(P1_6==0){Delay(20);while(P1_6==0);Delay(20); KeyNumber=6;}  
  18.     if(P1_5==0){Delay(20);while(P1_5==0);Delay(20); KeyNumber=10;}  
  19.     if(P1_4==0){Delay(20);while(P1_4==0);Delay(20); KeyNumber=14;}  
  20.               
  21.         P1=0xFF;  
  22.     P1_1=0;  
  23.     if(P1_7==0){Delay(20);while(P1_7==0);Delay(20); KeyNumber=3;}  
  24.     if(P1_6==0){Delay(20);while(P1_6==0);Delay(20); KeyNumber=7;}  
  25.     if(P1_5==0){Delay(20);while(P1_5==0);Delay(20); KeyNumber=11;}  
  26.     if(P1_4==0){Delay(20);while(P1_4==0);Delay(20); KeyNumber=15;}    
  27.           
  28.     P1=0xFF;  
  29.     P1_0=0;  
  30.     if(P1_7==0){Delay(20);while(P1_7==0);Delay(20); KeyNumber=4;}  
  31.     if(P1_6==0){Delay(20);while(P1_6==0);Delay(20); KeyNumber=8;}  
  32.     if(P1_5==0){Delay(20);while(P1_5==0);Delay(20); KeyNumber=12;}  
  33.     if(P1_4==0){Delay(20);while(P1_4==0);Delay(20); KeyNumber=16;}        
  34.               
  35.               
  36.     return KeyNumber ;  
  37. }  

在使用两者结合的时候需要封装库的调用!(这里就凸显了模块化的重要行,不然你就一大堆代码挤在一起!!!)

LCD1602显示的调用!(注释已写)

调用成功后大概如下:

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

延迟函数的写法!!

  1. #ifndef __MATRIXKEY__H__  
  2. #define  __MATRIXKEY__H__  
  3. unsigned char Matrixkey();  
  4.    
  5.    
  6.    
  7. #endif  

封装如下! 

单片机

勾选这一步十分重要不然下面的项目没办法调用! 

需要将文件Browse information 将他勾选进去,不然你的程序马上就报错!!

实现动态密码锁!!

  1. #include <REGX52.H>  
  2. #include "LCD1602.h"  
  3. #include "Delay.h"  
  4. #include "MatrixKey.h"  
  5.    
  6. unsigned char KeyNum;  
  7. //unsigned int  Password;   
  8. unsigned int Password ,count;  
  9.    
  10. void main()  
  11. {  
  12.     LCD_Init();                     //LCD初始化  
  13.     //LCD_ShowChar(1,1,'A');            //在1行1列显示字符A  
  14.     LCD_ShowString(1,1,"Password");  
  15.    
  16.    
  17.    
  18.     while(1)  
  19.     {  
  20.           
  21.         KeyNum=Matrixkey();  
  22.         if(KeyNum){  
  23.               
  24.     if(KeyNum<=10)  //如果S1~S10按键按下,输入密码  
  25.     {  
  26.         if(count<4){//如果输入次数小于四次  
  27.         Password*=10; //密码左移一位  
  28.         Password+=KeyNum%10;//获取一位密码  
  29.         count++; //计次加1  
  30.         }  
  31.           
  32.     }  
  33.     LCD_ShowNum(2,1,Password,4); //更新显示器  
  34.     }  
  35.         if(KeyNum==11)  //他就是确定键  
  36.         {  
  37.             if(Password==2345){  
  38.                 LCD_ShowString(1,14,"OK ");  
  39.                 Password=0;  
  40.                 count=0;  
  41.             LCD_ShowNum(2,1,Password,4);  
  42.             }  
  43.             else { //否则  
  44.                 LCD_ShowString(1,14,"ERR");  
  45.                 Password=0;  
  46.                 count=0;  
  47.                 LCD_ShowNum(2,1,Password,4);  
  48. }  
  49. }  
  50.         if(KeyNum==12) {  
  51.             Password=0;  //密码清零  
  52.                 count=0;  //计次清零  
  53.                 LCD_ShowNum(2,1,Password,4); //更新显示  
  54.             //如果S12按键按下,取消  
  55. }  
  56.     }  
  57. }  

定时器
•定时器介绍:51单片机的定时器属于单片机的内部资源,其电路的连接和运转均在单片机内部完成

•定时器作用:

(1)用于计时系统,可实现软件计时,或者使程序每隔一固定时间完成一项操作

(2)替代长时间的Delay,提高CPU的运行效率和处理速度

(3) 你STC拆下,光剩下一个单片机他也能正常运行。他是在内部运行的,不影响!

•定时器个数:3个(T0、T1、T2),T0和T1与传统的51单片机兼容,T2是此型号单片机增加的资源

•注意:定时器的资源和单片机的型号是关联在一起的,不同的型号可能会有不同的定时器个数和操作方式,但一般来说,T0和T1的操作方式是所有51单片机所共有的

其实定时器就像一个小闹钟,每隔一个时间段,计数单元的值就回加+1,当计数到达设置的闹钟提醒时间时,技术单元就会像中断系统发生中断信号,产生所谓的闹钟提醒!

上图比较好理解!

实现一个项目:用按键控制LED流水灯模式和定时器时钟!

这个计时器的时间范围是————0~65535,基本上每隔一秒+1,总共定时为:65535us,

64535离计数器溢出差值1000,所以时间为1MS;

验证一下:123———123/100=1;

123%100=23;

65毫秒

中断器调用!

  1. #include <REGX52.H>  
  2. /** 
  3.     *@brief   ¶¨Ê±Æ÷0³õʼ»¯  1ºÁÃë@12.000khz 
  4.     *@param   ÎÞ 
  5.     *@retval  ÎÞ 
  6.  */  
  7. void Timer0Init(void){  
  8.       
  9.     //TMOD=0X01;  //0000 0001  
  10.     TMOD=TMOD&0xF0; // °ÑtmodµÄµÚËÄλÇåÁ㣬¸ßËÄλ±£³Ö²»±ä£¡  
  11.     TMOD=TMOD|0X01; //tmodµÄ×îµÍλÖÃ1£¬¸ßËÄλ±£³Ö²»±ä£¡  
  12.     TF0=0;  
  13.     TR0=1;  
  14.     TH0=0x18;  
  15.     TL0=0xFC;  
  16.     ET0=1;  
  17.     EA=1;  
  18.     PT0=0;  
  19.    
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。