当前位置:首页 » 《随便一记》 » 正文

51单片机入门代码(上篇)

29 人参与  2023年05月05日 12:49  分类 : 《随便一记》  评论

点击全文阅读


前言

必读

因为一些图片不显示,可以点击以下链接
个人博客文章地址:51单片机入门教程(上篇)(代码+个人理解) – Echo (liveout.cn)

这篇文章是记录我粗略学习51单片机的一些代码,我会加些个人理解以及注释在里面。

因为是囫囵吞枣式学习,所以质量不是很好,后期我会慢慢优化 ?

如果你想要学习单片机,可以观看下面的B站教程并配合本文档学习

本文章使用的51单片机是 普中STC89C52RC

教程

推荐B站视频: 【51单片机入门教程-2020版 程序全程纯手打 从零开始入门】 https://www.bilibili.com/video/BV1Mb411e7re/?share_source=copy_web&vd_source=55024add0415795a359bd7b29ca21142(应该都知道吧)。

资源

B站江科大资源 链接:https://pan.baidu.com/s/1dLED_1VqL66qYItLl5ic4A?pwd=1111 提取码:1111

普中 链接:https://pan.baidu.com/s/1dNCHm9lLMP8pe3rZu3ktZQ?pwd=1111 提取码:1111


1. 入门

原理: 单片机核心

cpu通过配置寄存器来控制驱动器,来控制硬件电路

寄存器:连接软硬件的媒介

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oEYash0v-1670833987844)(https://yy.liveout.cn/article/Learn/cmicrocpu/22_12/%E5%8D%95%E7%89%87%E6%9C%BA%E6%A0%B8%E5%BF%83.png)]

数据类型

C51数据类型

2进制----16进制转换

16base

2. 点灯

原理

LED模块

2.1 点亮第一个LED

#include <REGX52.H> //右键添加头文件void main(){   //2进制转16进制,前缀为 0x 87654321 8个灯序号对应进制    // 8个1分别为8个led,0则是负极点亮。最后一个0是D1,第一个是D8    P2 = 0xFE; //1111 1110 点亮第1个led    P2 = 0x7F; //0111 1111 点亮第8个led    P2 = 0x5F; //0101 1111 led 6,8亮    P2 = 0xAA; //1010 1010 led 1,3,5,7亮    P2 = 0x55; //0101 0101 led 2,4,6,8亮while (1) //程序一直运行{}}

2.2 LED闪烁

#include <REGX52.H>#include <INTRINS.H>void Delay500ms()//@11.0592MHz 500ms延迟函数{unsigned char i, j, k;    _nop_(); //去掉nop不用加头文件#include <INTRINS.H>i = 4;j = 129;k = 119;do{do{while (--k);} while (--j);} while (--i);}void main(){while(1){P2 = 0xFE; Delay500ms(); //延迟500ms    P2 = 0xFF;  Delay500ms();} }

2.3 LED流水灯

#include <REGX52.H>void Delay500ms()//@11.0592MHz 延迟500ms{unsigned char i, j, k;    i = 4;j = 129;k = 119;do{do{while (--k);} while (--j);} while (--i);}void main(){while(1){P2 = 0xFE; //1111 1110Delay500ms();P2 = 0xFD; //1111 1101Delay500ms();P2 = 0xFB; //1111 1011Delay500ms();P2 = 0xF7; //1111 0111Delay500ms();P2 = 0xEF; //1110 1111Delay500ms();P2 = 0xDF; //1101 1111Delay500ms();P2 = 0xBF; //1011 1111Delay500ms();P2 = 0x7F; //0111 1111Delay500ms();}}

2.4 LED流水灯Plus

#include <REGX52.H>void Delay1ms(unsigned int xms)//@11.0592MHz{ //xms 为延迟秒数unsigned char i, j;  while(xms){i = 2;j = 199;do{while (--j);} while (--i);xms--;}}void main(){while(1){P2 = 0xFE; //1111 1110Delay1ms(500); //延迟500msP2 = 0xFD; //1111 1101Delay1ms(100); //100msP2 = 0xFB; //1111 1011Delay1ms(1000); //1000msP2 = 0xF7; //1111 0111Delay1ms(500);P2 = 0xEF; //1110 1111Delay1ms(200);P2 = 0xDF; //1101 1111Delay1ms(500);P2 = 0xBF; //1011 1111Delay1ms(900);P2 = 0x7F; //0111 1111Delay1ms(500);}Delay1ms(500);}

3.独立按键控制LED

原理(高电平、低电平)

电平和电压是有差别的,高电平指的是与低电平相对的高电压,是电工程上的一种说法。

在逻辑电平中,保证逻辑门的输入为高电平时所允许的最小输入高电平,当输入电平高于输入高电压(Vih)时,则认为输入电平为高电平。

在电子和自动化控制中,分为模拟信号和数字信号,在数字逻辑电子电路中,数字信号是二进制的,即只有0信号和1信号。

高电平表示电压高的状态,记为1,一般规定高电平为3.5~5V
低电平表示电压低的状态, 记为0,一般规定低电平为0~0.25V

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o21HlK7Z-1670833987845)(https://yy.liveout.cn/article/Learn/cmicrocpu/22_12/%E7%8B%AC%E7%AB%8B%E6%8C%89%E9%94%AE%E6%A8%A1%E5%9D%97.png)]

按键松开 高电平 1

按键按下 低电平 0

3.1 独立按键控制LED灯灭

#include <REGX52.H>//P2_0就是单片机上面的一个端口,这个端口就是链接右边第一个led灯的//等于左边七个LED直接不给信号了,只给右边第一个一个亮的信号void main(){//P2 = 0xFE;P2_0 = 1;while(1){ // 按下亮,松开灭if(P3_1==0) //P3_1控制第一个按键,即P31{P2_0 = 0; //P2_0是第一个LED,0则亮}else{P2_0=1;}                if (P3_0==0){P2_1 = 0;}        else{P2_1 = 1;}}}

3.2 独立按键控制LED状态

#include <REGX52.H>void Delay1ms(unsigned int xms)//@11.0592MHz{unsigned char i, j;while(xms){i = 2;j = 199;do{while (--j);} while (--i);xms--;}}void main(){  //按一次亮,再按一次灭while(1){if(P3_1==0){Delay1ms(20); //按下时消抖while(P3_1==0); //松开跳出while循环,不松开一直循环,所以灯不会有反应Delay1ms(20); //松开时消抖P2_0=~P2_0; //取反,灭变亮,亮变灭}}}

3.3 独立按键控制LED显示二进制

#include <REGX52.H>void Delay1ms(unsigned int xms)//@11.0592MHz{unsigned char i, j;while(xms){i = 2;j = 199;do{while (--j);} while (--i);xms--;}}void main(){  unsigned char LEDNum = 0; while(1){if(P3_1==0){Delay1ms(20);while(P3_1==0)Delay1ms(20);LEDNum++; //0000 0001P2 = ~LEDNum; //取反, 1111 1110,第一个LED灯亮}}}

3.4 独立按键控制LED移位

0000 0001 -> 0000 0010 -> 0000 0100 …

0x01<<0 0x01<<1 0x01<<2 …

#include <REGX52.H>void Delay1ms(unsigned int xms)//@11.0592MHz{unsigned char i, j;while(xms){i = 2;j = 199;do{while (--j);} while (--i);xms--;}} void main(){unsigned char LEDNum = 0;P2 = ~0x01; //LED初始化,不然第一次led1不亮while(1){        //按键1右移if(P3_1==0) //按键1按下{Delay1ms(20);while(P3_1==0);Delay1ms(20);LEDNum++; //右移if (LEDNum>=8)LEDNum=0; //重置P2=~(0x01<<LEDNum);}        //按键2左移if(P3_0==0) //按键1按下{Delay1ms(20);while(P3_0==0);Delay1ms(20);if(LEDNum==0)LEDNum=7; //重置elseLEDNum--; //左移P2=~(0x01<<LEDNum);}}}

4.数码管

LED数码管:由多个发光二极管封装在一起组成“8”字型

输出扫描:显示第一位->显示第二位->显示第三位…,然后快速循环这个过程,最终实现所有数码管同时显示的效果

原理

138验码器
//这里灯的顺序排列我的理解有错误,还没仔细研究。我的单片机111时是Y0,即LED1亮C(P24)  B(P23)    A(P22)  Y LED 8 -> 10(4)0(2)0(1)Y70 1 1 1 1 1 1 1  LED800 1    Y6 1 0 1 1 1 1 1 1  LED710 1 Y2 1 1 1 1 1 0 1 1  LED311 1 Y0 1 1 1 1 1 1 1 0  LED1    
动态数码管

上面是共阴极,公共端为0则亮。如LED7(引脚9)为0,其他为1。即 1011 1111,则LED7亮,其他不亮。

下面是阴码。LED显示7,则b、c为1,其他0。即 0110 0000

4.1 静态数码管显示

点亮一个

#include <REGX52.H>void main(){    //100,即4,LED4亮P2_4 = 1;P2_3 = 0;P2_2 = 0;    //P07->P00  0000 0111 ,即0x07,    //a、b、c为1,即a、b、c亮,显示为7P0 = 0x07;while(1){}}

模块化

#include <REGX52.H>//显示数字的段码存储在数组中//0~9 -> 0~9unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00};void Nixie(unsigned char Location,Number){   //Location为LED灯序号,Number为显示数字switch(Location){case 1:P2_4=1;P2_3=1;P2_2=1;break;case 2:P2_4=1;P2_3=1;P2_2=0;break;case 3:P2_4=1;P2_3=0;P2_2=1;break;case 4:P2_4=1;P2_3=0;P2_2=0;break;case 5:P2_4=0;P2_3=1;P2_2=1;break;case 6:P2_4=0;P2_3=1;P2_2=0;break;case 7:P2_4=0;P2_3=0;P2_2=1;break;case 8:P2_4=0;P2_3=0;P2_2=0;break;}P0=NixieTable[Number];}void main(){Nixie(6,2); //第6个LED显示2while(1){}}

4.2 动态数码管显示

个人理解:就像早期电影一样,通过视觉残留动态显示。实际上只能有一个LED灯亮

消影:位选 段选 清零 位选 段选 清零

利用延时来抵消人的视觉暂留现象达到消影

#include <REGX52.H>void Delay1ms(unsigned int xms)//@11.0592MHz{unsigned char i, j;while(xms){i = 2;j = 199;do{while (--j);} while (--i);xms--;}} unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00};void Nixie(unsigned char Location,Number){   switch(Location){case 1:P2_4=1;P2_3=1;P2_2=1;break;case 2:P2_4=1;P2_3=1;P2_2=0;break;case 3:P2_4=1;P2_3=0;P2_2=1;break;case 4:P2_4=1;P2_3=0;P2_2=0;break;case 5:P2_4=0;P2_3=1;P2_2=1;break;case 6:P2_4=0;P2_3=1;P2_2=0;break;case 7:P2_4=0;P2_3=0;P2_2=1;break;case 8:P2_4=0;P2_3=0;P2_2=0;break;}P0=NixieTable[Number]; //段选Delay1ms(1); P0=0x00; //清零}void main(){while(1){Nixie(1,1); //Delay1ms(500);  //此延迟用于从第一个led1依次显示数字Nixie(2,2);//Delay1ms(500); Nixie(3,3);//Delay1ms(500); Nixie(4,4);//Delay1ms(500);   Nixie(5,5);//   Delay1ms(500);Nixie(6,6);//Delay1ms(500);Nixie(7,7);//Delay1ms(500);Nixie(8,8);//Delay1ms(500);}}

5 .1模块化编程

视频

https://www.bilibili.com/video/BV1Mb411e7re?p=13&vd_source=a1234589a3616351986bc6d13bcbd8f8

代码

头文件

//Delay.h#ifndef __DELAY_H_#define __DELAY_H_void Delay(unsigned int xms);#endif
//Nixie.h#ifndef __NIXIE_H___#define __NIXIE_H__void Nixie(unsigned char Location,Number);#endif

函数

//main.c#include <REGX52.H>#include "Delay.h"#include "Nixie.h"void main(){while(1){Nixie(1,1);Nixie(2,2);Nixie(3,3);}}

其他函数

//Delay.cvoid Delay(unsigned int xms)//@11.0592MHz{unsigned char i, j;while(xms){i = 2;j = 199;do{while (--j);} while (--i);xms--;}} 
//Nixe.c#include <REGX52.H>#include "Delay.h"unsigned char NixieTable[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x39,0x5E,0x79,0x71,0x00};void Nixie(unsigned char Location,Number){   switch(Location){case 1:P2_4=1;P2_3=1;P2_2=1;break;case 2:P2_4=1;P2_3=1;P2_2=0;break;case 3:P2_4=1;P2_3=0;P2_2=1;break;case 4:P2_4=1;P2_3=0;P2_2=0;break;case 5:P2_4=0;P2_3=1;P2_2=1;break;case 6:P2_4=0;P2_3=1;P2_2=0;break;case 7:P2_4=0;P2_3=0;P2_2=1;break;case 8:P2_4=0;P2_3=0;P2_2=0;break;}P0=NixieTable[Number]; Delay(1); P0=0x00; }

5.2 LCD1602调试工具

LCD与数码管引脚冲突

原理

LCD1602接口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2stC76sm-1670833987845)(https://yy.liveout.cn/article/Learn/cmicrocpu/22_12/LCD%E8%B0%83%E8%AF%95.png)]

代码

记得添加之前定义过的函数和头文件

//LCD1602.h  分别为显示各种进制数组以及字符#ifndef __LCD1602_H__#define __LCD1602_H__void LCD_Init();void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);#endif
#include <REGX52.H>//引脚配置:sbit LCD_RS=P2^6;sbit LCD_RW=P2^5;sbit LCD_EN=P2^7;#define LCD_DataPort P0//函数定义:/**  * @brief  LCD1602延时函数,12MHz调用可延时1ms  * @param  无  * @retval 无  */void LCD_Delay(){unsigned char i, j;i = 2;j = 239;do{while (--j);} while (--i);}/**  * @brief  LCD1602写命令  * @param  Command 要写入的命令  * @retval 无  */void LCD_WriteCommand(unsigned char Command){LCD_RS=0;LCD_RW=0;LCD_DataPort=Command;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();}/**  * @brief  LCD1602写数据  * @param  Data 要写入的数据  * @retval 无  */void LCD_WriteData(unsigned char Data){LCD_RS=1;LCD_RW=0;LCD_DataPort=Data;LCD_EN=1;LCD_Delay();LCD_EN=0;LCD_Delay();}/**  * @brief  LCD1602设置光标位置  * @param  Line 行位置,范围:1~2  * @param  Column 列位置,范围:1~16  * @retval 无  */void LCD_SetCursor(unsigned char Line,unsigned char Column){if(Line==1){LCD_WriteCommand(0x80|(Column-1));}else if(Line==2){LCD_WriteCommand(0x80|(Column-1+0x40));}}/**  * @brief  LCD1602初始化函数  * @param  无  * @retval 无  */void LCD_Init(){LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动LCD_WriteCommand(0x01);//光标复位,清屏}/**  * @brief  在LCD1602指定位置上显示一个字符  * @param  Line 行位置,范围:1~2  * @param  Column 列位置,范围:1~16  * @param  Char 要显示的字符  * @retval 无  */void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char){LCD_SetCursor(Line,Column);LCD_WriteData(Char);}/**  * @brief  在LCD1602指定位置开始显示所给字符串  * @param  Line 起始行位置,范围:1~2  * @param  Column 起始列位置,范围:1~16  * @param  String 要显示的字符串  * @retval 无  */void LCD_ShowString(unsigned char Line,unsigned char Column,char *String){unsigned char i;LCD_SetCursor(Line,Column);for(i=0;String[i]!='\0';i++){LCD_WriteData(String[i]);}}/**  * @brief  返回值=X的Y次方  */int LCD_Pow(int X,int Y){unsigned char i;int Result=1;for(i=0;i<Y;i++){Result*=X;}return Result;}/**  * @brief  在LCD1602指定位置开始显示所给数字  * @param  Line 起始行位置,范围:1~2  * @param  Column 起始列位置,范围:1~16  * @param  Number 要显示的数字,范围:0~65535  * @param  Length 要显示数字的长度,范围:1~5  * @retval 无  */void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length){unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');}}/**  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字  * @param  Line 起始行位置,范围:1~2  * @param  Column 起始列位置,范围:1~16  * @param  Number 要显示的数字,范围:-32768~32767  * @param  Length 要显示数字的长度,范围:1~5  * @retval 无  */void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length){unsigned char i;unsigned int Number1;LCD_SetCursor(Line,Column);if(Number>=0){LCD_WriteData('+');Number1=Number;}else{LCD_WriteData('-');Number1=-Number;}for(i=Length;i>0;i--){LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');}}/**  * @brief  在LCD1602指定位置开始以十六进制显示所给数字  * @param  Line 起始行位置,范围:1~2  * @param  Column 起始列位置,范围:1~16  * @param  Number 要显示的数字,范围:0~0xFFFF  * @param  Length 要显示数字的长度,范围:1~4  * @retval 无  */void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length){unsigned char i,SingleNumber;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){SingleNumber=Number/LCD_Pow(16,i-1)%16;if(SingleNumber<10){LCD_WriteData(SingleNumber+'0');}else{LCD_WriteData(SingleNumber-10+'A');}}}/**  * @brief  在LCD1602指定位置开始以二进制显示所给数字  * @param  Line 起始行位置,范围:1~2  * @param  Column 起始列位置,范围:1~16  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111  * @param  Length 要显示数字的长度,范围:1~16  * @retval 无  */void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length){unsigned char i;LCD_SetCursor(Line,Column);for(i=Length;i>0;i--){LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');}}
//输出固定值#include <REGX52.H>#include "LCD1602.h"void main(){LCD_Init();LCD_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);while(1){}}
//输出变量值#include <REGX52.H>#include "LCD1602.h"int Result;void main(){LCD_Init();Result = 1+1;LCD_ShowNum(1,1,Result,3);while(1){}}
//输出变量值Plus 从0开始显示秒数#include <REGX52.H>#include "LCD1602.h"#include "Delay.h"int Result=0;void main(){LCD_Init();while(1){Result++;Delay(1000); //1000ms == 1sLCD_ShowNum(1,1,Result,3);}}

6. 矩阵键盘

输入扫描:读取第一行(列)->读取第二(列)->读取第三行(列)…,然后快速循环这个过程,最终实现所有按键同时检测的效果

原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LhsevxtI-1670833987846)(https://yy.liveout.cn/article/Learn/cmicrocpu/22_12/%E7%9F%A9%E9%98%B5%E6%8C%89%E9%94%AE.png)]

6.1矩阵键盘

记得添加之前定义过的函数和头文件

头文件

//MatrixKey.h#ifndef __MATRIXKEY_H_#define __MATRIXKEY_H_unsigned char MatrixKey();#endif

函数

//main.c#include <REGX52.H>#include "Delay.h"#include "LCD1602.h"#include "MatrixKey.h"unsigned char KeyNum;void main(){LCD_Init();LCD_ShowString(1,1,"Hello Echo"); //第一行显示字符串while(1){KeyNum=MatrixKey();if (KeyNum) //如何没有if,按下一瞬间显示数字,但是松手会重置为0{LCD_ShowNum(2,1,KeyNum,2);//第2行显示KeyNum,即按下按键键码值}}}                      
//MatrixKey.c#include <REGX52.H>#include "Delay.h"//Function Definition:/**  * @brief 矩阵键盘读取按键码  * @param  无  * @retval KeyNumber 按下按键的键码值  如果按键按下不放,程序会停留在此函数,松手一瞬间,返回按键键码。没有按键按下时返回0  */unsigned char MatrixKey(){unsigned char KeyNumber=0;P1 = 0xFF; //初始化P1_3=0;  //第1列if(P1_7==0) {Delay(20);while(P1_7==0);Delay(20);KeyNumber=1;}//第1行if(P1_6==0) {Delay(20);while(P1_6==0);Delay(20);KeyNumber=5;}//第2行if(P1_5==0) {Delay(20);while(P1_5==0);Delay(20);KeyNumber=9;}//第3行if(P1_4==0) {Delay(20);while(P1_4==0);Delay(20);KeyNumber=13;}//第4行P1 = 0xFF;P1_2=0;  //第2列if(P1_7==0) {Delay(20);while(P1_7==0);Delay(20);KeyNumber=2;}if(P1_6==0) {Delay(20);while(P1_6==0);Delay(20);KeyNumber=6;}if(P1_5==0) {Delay(20);while(P1_5==0);Delay(20);KeyNumber=10;}if(P1_4==0) {Delay(20);while(P1_4==0);Delay(20);KeyNumber=14;}P1 = 0xFF;P1_1=0;  //第3列if(P1_7==0) {Delay(20);while(P1_7==0);Delay(20);KeyNumber=3;}if(P1_6==0) {Delay(20);while(P1_6==0);Delay(20);KeyNumber=7;}if(P1_5==0) {Delay(20);while(P1_5==0);Delay(20);KeyNumber=11;}if(P1_4==0) {Delay(20);while(P1_4==0);Delay(20);KeyNumber=15;}P1 = 0xFF;P1_0=0;  //第4列if(P1_7==0) {Delay(20);while(P1_7==0);Delay(20);KeyNumber=4;}if(P1_6==0) {Delay(20);while(P1_6==0);Delay(20);KeyNumber=8;}if(P1_5==0) {Delay(20);while(P1_5==0);Delay(20);KeyNumber=12;}if(P1_4==0) {Delay(20);while(P1_4==0);Delay(20);KeyNumber=16;}return KeyNumber; //返回按下按键键码值}                    

6.2 密码锁

#include <REGX52.H>#include "Delay.h"#include "LCD1602.h"#include "MatrixKey.h"//unsigned char没有符号位,因此能表示0~255,但是密码肯定不能这么短。//B站视频教程是unsigned char类型,但是我输入时只能到256,改为int类型就行了int KeyNum;int Password,Count;void main(){LCD_Init();LCD_ShowString(1,1,"PassWord");while(1){KeyNum=MatrixKey();if (KeyNum){if(KeyNum<=10) //如果S1~S2按键按下,输入密码            {                 if(Count<4)Password*=10; //密码左移一位Password+=KeyNum%10; //获取一位密码            }             Count++; 。//计次加一LCD_ShowNum(2,1,Password,4); //更新显示}        if(KeyNum==11)   //按下S11按键确认{if (Password==2345) //密码正确{LCD_ShowString(1,12,"OK"); //显示OK                  Password=0; //密码清理Count=0;  //计数清理                  LCD_ShowNum(2,1,Password,4); //更新显示}            else            {                 LCD_ShowString(1,12,"ERROR");//Delay(2000); //延时2s,然后空格替代,不然OK后面会显示RORPassword=0;Count=0;LCD_ShowNum(2,1,Password,4);//LCD_ShowString(1,12,"     "); //空格替代            }}        if (KeyNum==12) //按键S12手动清零{   Password=0; //密码清零Count=0;   //计次清零             LCD_ShowString(1,12,"     "); //空格替代LCD_ShowNum(2,1,Password,4); //更新显示                  }}}                      

7. 定时器

原理

这章比较难,得多看几遍视频

视频教程:https://www.bilibili.com/video/BV1Mb411e7re/?p=17&spm_id_from=333.1007.top_right_bar_window_history.content.click&vd_source=a1234589a3616351986bc6d13bcbd8f8


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

SySclk:系统时钟,即晶振周期,本开发板上的晶振为11.0592MHz

时钟:给0定时器,给1计数器


基础理解代码:实现led1灯每1s闪烁一次

0~65535 每隔1us计数加一 总共定时时间65535us

给初值6435 离计数器65535 差1000us 所以计数时间为1ms

#include <REGX52.H>//定时器0初始化void Timer0_Init() //这个函数可以通过软件直接生成的!!!,但是没有中断,即最后三个{    //定时器//TMOD=0x01; //0000 0001  高四位  低四位    TMOD=TMOD&0xF0; //把TMOD的低四位清0,高四位保持不变    TMOD=TMOD|0x01; //把TMOD的最低位 置为1,高四位保持不变TF0=0; //TF0是进入中断程序后,硬件自动清零TR0=1; //定时器从0开始计时        //TH0,TL0为二进制8位,单独可计256次   低8位计满256后高8位进1    //设置定时初值TH0=64535/256; //高8位次数 TL0=64535%256; //低8位次数        //中断    ET0=1;    EA=1;    PT0=0; //设置优先级}//主程序void main(){Timer0_Init();while(1){}}//中断服务程序Bunsigned int T0Count;void Timer0_Routine() interrupt 1{TH0=64535/256;TL0=64535%256; //重新赋值T0Count++; //每次加1us,1000次就是1sif (T0Count>=1000){  T0Count=0; //重新赋值  P2_0=~P2_0; //每1s闪烁一次}}

7.1 按键控制LED流水灯模式

头文件

//Delay.h#ifndef __DELAY_H_#define __DELAY_H_void Delay(unsigned int xms);#endif
//Key.h#ifndef __KEY_H_#define __KEY_H_usigned char Key();#endif
//Timer0.h#ifndef __TIMER0_H_#define __TIMER0_H_void Timer0_Init();#endif

函数

//Delay.c//Function Definition/**  * @brief 延迟函数  * @param  无  * @retval 1ms  */void Delay(unsigned int xms)//@11.0592MHz{unsigned char i, j;while(xms){i = 2;j = 199;do{while (--j);} while (--i);xms--;}} 
//Key.c#include <REGX52.H>#include "Delay.h"//Function Definition/**  * @brief 获取独立按键键码  * @param  无  * @retval 按下按键的键码,范围0~4,无按键按下的返回值为0  */unsigned char Key(){unsigned char KeyNum=0;if(P2_1==0) {Delay(20);while(P2_1==0);Delay(20);KeyNum=1;}if(P2_0==0) {Delay(20);while(P2_0==0);Delay(20);KeyNum=2;}if(P2_3==0) {Delay(20);while(P2_3==0);Delay(20);KeyNum=3;}if(P2_4==0) {Delay(20);while(P2_4==0);Delay(20);KeyNum=4;}return KeyNum;}
//Timer0.c#include <REGX52.H>//Function Definition/**  * @brief 定时器0初始化  * @param  无  * @retval 1ms  */void Timer0_Init()//1毫秒@12.000MHz{TMOD &= 0xF0;//设置定时器模式TMOD |= 0x01;//设置定时器模式TL0 = 0x66;//设置定时初值TH0 = 0xFC;//设置定时初值TF0 = 0;        //清除TF0标志 TR0 = 1;//定时器0开始计时ET0=1;EA=1;PT0=0;}
//mian.c (测试) 按键控制灯亮#include <REGX52.H>#include "Timer0.h"#include "Key.h"unsigned char KeyNum;void main(){    Timer0_Init(); //定时器0初始化while(1){KeyNum=Key();if (KeyNum){if(KeyNum==1) P2_0=~P2_0; //按K1,led1亮,再按一次则熄灭if(KeyNum==2) P2_1=~P2_1;if(KeyNum==3) P2_2=~P2_2;if(KeyNum==4) P2_3=~P2_3;}}}
//main.c  流水灯,按下K1改变方向#include <REGX52.H>#include "Timer0.h"#include "Key.h"#include "INTRINS.H"unsigned char KeyNum,LEDMode;void main(){P2=0xFE;    Timer0_Init(); //定时器0初始化while(1){KeyNum=Key();if (KeyNum){if(KeyNum==1) //按下K1,改变方向{LEDMode++;if(LEDMode>=2) LEDMode=0;}}}}void Timer0_Routine() interrupt 1{    static unsigned int T0Count;TL0 = 0x18; //重新赋值1usTH0 = 0xFC;T0Count++;if (T0Count>=500) //0.5s{  T0Count=0;  if(LEDMode==0)  P2=_crol_(P2,1);  //向左移位,并且循环  if(LEDMode==1)  P2=_cror_(P2,1);  //向右移位,并且循环}}

7.2 定时器时钟

头文件之前都定义过了

#include <REGX52.H>#include "Delay.h"#include "LCD1602.h"#include "Timer0.h"unsigned char Sec,Min,Hou;void main(){LCD_Init();Timer0_Init();LCD_ShowString(1,1,"Clock:");LCD_ShowString(2,1,"  :  :");while(1){        //LCD显示LCD_ShowNum(2,1,Hour,2);LCD_ShowNum(2,4,Min,2);LCD_ShowNum(2,7,Sec,2);}}void Timer0_Routine() interrupt 1{    static unsigned int T0Count;TL0 = 0x18; //重新赋值1usTH0 = 0xFC;T0Count++;if (T0Count>=1000) //1s{    T0Count=0;Sec++;  //每秒递增if(Sec>=60){Sec=0;Min++;if(Min>=60){Min=0;Hour++;if(Hour>=24){Hour=0;}}}}}

8. 串口

串口是一种应用十分广泛的通讯接口,成本低,容易使用,通讯线路简单,可以实现两个设备的互通性。

51单片机内部自带UART(通用异步收发器),可实现单片机的串口通信。串口接口是DB9接口

STC89C52有一个UART,STC89C52URAT有四种工作模式

原理

USBTTl

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A8nbd6de-1670833987846)(https://yy.liveout.cn/article/Learn/cmicrocpu/22_12/%E4%B8%B2%E5%8F%A3%E5%AF%84%E5%AD%98%E5%99%A8.png)]

数据显示模式

HEX模式/十六进制模式/二进制模式:以原始数据的形式显示

文本模式/字符模式:以原始数据编码后的形式显示

8.1 串口向电脑发送数据

头文件

//UART.h#ifndef __UART_H_#define __UART_H_void UART_Init(); //初始化void UART_SendByte(unsigned char Byte);//只发送,不接收#endif

函数

//UART.c#include <REGX52.H>//Function Definition/**  * @brief 串口初始化 4800bps@11.0592MHz  * @param  无  * @retval 无  */void UART_Init()  //初始化 4800bps@11.0592MHz{SCON = 0x40; //0100 0000PCON |= 0x80; TMOD &= 0x0F;//设置定时器1模式  0000 1111TMOD |= 0x20;//设置定时器1模式  0010 0000TL1 = 0xF4;//设定定时初值TH1 = 0xF4;//设定定时器重装值ET1 = 0;//禁止定时器1中断TR1 = 1;//启动定时器1}//Function Definition/**  * @brief 串口发送一个字节数据  * @param  Byte 要发送一个字节数字  * @retval 无  */void UART_SendByte(unsigned char Byte) //只发送,不接收 {SBUF = Byte;  while(TI==0); //发送成功跳出循环,TI变为1TI = 0;    //重置为0}//串口中断函数模板(用在下一个例子中)//void UART_Routine() interrupt 4 //串口中断号为4//{ //RI:接收中断请求标志位 //if(RI==1) //RI=1向主机请求中断响//{//RI=0;//应中断后必须用软件复位,即RI=0  //}//}
//main.c//每一秒发送一个字节#include <REGX52.H>#include "Delay.h"#include "UART.h"unsigned char Sec;void main(){UART_Init(); //初始化while(1){UART_SendByte(Sec); //发送 16进制格式Sec++;Delay(1000);}}

8.2 电脑通过串口控制LED

#include <REGX52.H>#include "Delay.h"#include "UART.h"void main(){UART_Init();while(1){}}void UART_Routine() interrupt 4 //串口中断号为4{ //RI:接收中断请求标志位 if(RI==1) //RI=1向主机请求中断响{P2=~SBUF; UART_SendByte(SBUF);RI=0;//应中断后必须用软件复位,即RI=0  }}


点击全文阅读


本文链接:http://zhangshiyu.com/post/60882.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1