C语言:51单片机看这一篇就够了
Posted Lion King
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言:51单片机看这一篇就够了相关的知识,希望对你有一定的参考价值。
前言
本篇文章属于学习笔记,来源于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、编写程序
根据硬件原理图和寄存器定义,来对操作寄存器地址,实现灯的点亮
#include <STC89C5xRC.H>
void main()
P2 = 0xFE; //1111 1110
6、实验过程与代码解释
(1)点击F7编译单个文件,编译失败,给出P2不认识的提示
(2)添加H文件,继续编译,编译通过 ,说明头文件定义了P2
(3)为什么控制的是P2?因为硬件原理图以及头文件的定义,拉低电平即可点亮一盏灯!
7、生成文件,下载到板子上,重新上电之后可运行该程序
四、单片机实操二:LED闪烁
1、在指定路径下创建工程并添加新文件
2、通过STC-ISP拷贝延时代码
3、编写程序
加入延时,方便观察灯的闪烁
#include <STC89C5xRC.H>
#include <INTRINS.H>
void Delay500ms() //@12.000MHz
unsigned char i, j, k;
_nop_(); //An empty function, from INTRINS.H
i = 4;
j = 205;
k = 187;
do
do
while (--k);
while (--j);
while (--i);
void main()
P2=0xFE;
Delay500ms();
P2=0xFF;
Delay500ms();
4、实验过程
五、单片机实操三:LED流水灯
1、在指定路径下创建工程并添加新文件
2、编写程序
让LED循环点亮,像流水一样
#include <STC89C5xRC.H>
#include <INTRINS.H>
void Delay500ms() //@12.000MHz
unsigned char i, j, k;
_nop_();
i = 4;
j = 205;
k = 187;
do
do
while (--k);
while (--j);
while (--i);
void main()
while(1)
P2=0xFE; //1111 1110
Delay500ms();
P2=0xFD; //1111 1101
Delay500ms();
P2=0xFB; //1111 1011
Delay500ms();
P2=0xF7; //1111 0111
Delay500ms();
P2=0xEF; //1110 1111
Delay500ms();
P2=0xDF; //1101 1111
Delay500ms();
P2=0xBF; //1011 1111
Delay500ms();
P2=0x7F; //0111 1111
Delay500ms();
六、单片机实操四:LED流水灯Plus
1、在指定路径下创建工程并添加新文件
2、编写程序
将延时函数参数化。
#include <STC89C5xRC.H>
#include <INTRINS.H>
void Delayxms(unsigned int xms) //@12.000MHz
unsigned char i, j;
while(xms)
i = 2;
j = 239;
do
while (--j);
while (--i);
xms--; //xms=xms-1
void main()
while(1)
P2=0xFE; //1111 1110
Delayxms(500);
P2=0xFD; //1111 1101
Delayxms(500);
P2=0xFB; //1111 1011
Delayxms(500);
P2=0xF7; //1111 0111
Delayxms(500);
P2=0xEF; //1110 1111
Delayxms(500);
P2=0xDF; //1101 1111
Delayxms(500);
P2=0xBF; //1011 1111
Delayxms(500);
P2=0x7F; //0111 1111
Delayxms(500);
3、51单片机的数据类型
我们在上面的程序中使用到了数据类型unsigned int xms,即xms在单片机中能表示0~65535
七、单片机实操五:独立按键控制LED灯亮灭
1、在指定路径下创建工程并添加新文件
2、独立按键原理图
3、编写程序
按下按键亮灯,松开按键灭灯。
#include <STC89C5xRC.H>
void main()
while(1)
if(P30==0)
P20=0;
else
P20=1;
4、51单片机数据运算
上述代码中,我们使用到了==这个运算符,表示等于。
5、51单片机基本语句
上述程序用到了if语句,通过判断按键的动作来实现灯的亮灭
6、注意事项
程序写的是P30独立按键,根据原理图,需要按下K2才能点亮第一盏灯。(原理图设计者并没有按照顺序一一对应按键,这是设计者埋下的小坑)
P30是H文件中定义的,如果是其他H文件可能没有或者用另一个变量表示,这个时候需要你自己去写或者更改变量
八、单片机实操六:独立按键控制LED灯状态
1、在指定路径下创建工程并添加新文件
2、编写程序
按键消抖,检测松手,才改变LED灯的状态,使其松开按键后长亮或长灭。
#include <STC89C5xRC.H>
void Delay(unsigned int xms) //@12.000MHz
unsigned char i, j;
while(xms)
i = 2;
j = 239;
do
while (--j);
while (--i);
xms--;
void main()
while(1)
if(P31==0)
Delay(20); // Keys away shaking
while(P31==0);
Delay(20); // Detection of let go
P20=~P20;
九、单片机实操七:独立按键控制LED显示二进制
1、在指定路径下创建工程并添加新文件
2、编写程序
通过不停的按下按键,使得灯以二进制的方式,不断往上加一,达到用灯来表示按键次数
#include <STC89C5xRC.H>
void Delay(unsigned int xms) //@12.000MHz
unsigned char i, j;
while(xms)
i = 2;
j = 239;
do
while (--j);
while (--i);
xms--;
void main()
unsigned char LEDNum=0; // char max num is 255
while(1)
if(P31==0)
Delay(20);
while(P31==0);
Delay(20);
LEDNum++;
P2=~LEDNum;
十、单片机实操八:独立按键控制LED移位
1、在指定路径下创建工程并添加新文件
2、编写程序
按下P31,往左边移一位;按下P30,往右边移一位,以LED灯来展示。
#include <STC89C5xRC.H>
void Delay(unsigned int xms); // must statement
unsigned char LEDNum; // The global variable
void main()
P2=~0x01; //int P2
while(1)
if(P31==0)
Delay(20);
while(P31==0);
Delay(20);
LEDNum++;
if(LEDNum>=8)
LEDNum=0;
P2=~(0x01<<LEDNum); // 0x01 of P2 need shift to the left LEDNum, and get the not
if(P30==0)
Delay(20);
while(P30==0);
Delay(20);
if(LEDNum==0)
LEDNum=7;
else
LEDNum--;
P2=~(0x01<<LEDNum);
void Delay(unsigned int xms) //@12.000MHz
unsigned char i, j;
while(xms)
i = 2;
j = 239;
do
while (--j);
while (--i);
xms--;
十一、单片机实操九:静态数码管显示
1、在指定路径下创建工程并添加新文件
2、单个数码管引脚定义
数码管的接法,有共阳和共阴之分。共阴时,拉高电压即可点亮。共阳时,拉低电平点亮。
3、开发板四位一体的数码管引脚定义
4、硬件原理图
138译码器控制数码管使能(使用3个单片机输入控制8个数码管显示),每个数字的一个笔画由双向数据缓存器245(单片机输出能力有限,需要该芯片提高输入能力)控制亮灭。
因此,首先控制P22~P24来选中数码管,然后选中数码管的笔画,最终呈现数据。
5、C51单片机数组
6、C51单片机子函数
7、数码管段码表(共阴极电路,共阳极则不一样)
8、编写程序
#include <STC89C5xRC.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:
P24=1;P23=1;P22=1;break;
case 2:
P24=1;P23=1;P22=0;break;
case 3:
P24=1;P23=0;P22=1;break;
case 4:
P24=1;P23=0;P22=0;break;
case 5:
P24=0;P23=1;P22=1;break;
case 6:
P24=0;P23=1;P22=0;break;
case 7:
P24=0;P23=0;P22=1;break;
case 8:
P24=0;P23=0;P22=0;break;
P0=NixieTable[Number];
void main()
// P24=1; //Controls a nixie tube
// P23=0;
// P22=1;
// P0=0x7D; //value is 6
Nixie(2,3);
while(1)
十二、单片机实操十:动态数码管显示
1、在指定路径下创建工程并添加新文件
2、编写程序
动态清零,数字消影,来实现动态数码管显示,这是单片机不断扫描来成像的,将耗费大量CPU资源。因此,硬件电路一般会加1640的芯片来驱动,将大量减少扫描带来的CPU损耗。
#include <STC89C5xRC.H>
unsigned char NixieTable[]=
0x3f,0x06,0x5b,0x4f,
0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,
0x39,0x5e,0x79,0x71, 0x00;
void Delay(unsigned int xms) //@12.000MHz
unsigned char i, j;
while(xms)
i = 2;
j = 239;
do
while (--j);
while (--i);
xms--;
void Nixie(unsigned char Location,Number)
switch(Location)
case 1:
P24=1;P23=1;P22=1;break;
case 2:
P24=1;P23=1;P22=0;break;
case 3:
P24=1;P23=0;P22=1;break;
case 4:
P24=1;P23=0;P22=0;break;
case 5:
P24=0;P23=1;P22=1;break;
case 6:
P24=0;P23=1;P22=0;break;
case 7:
P24=0;P23=0;P22=1;break;
case 8:
P24=0;P23=0;P22=0;break;
P0=NixieTable[Number];
Delay(1); // Shadow elimination
P0=0x00; // reset
void main()
while(1)
Nixie(1,1);
Nixie(2,2);
Nixie(3,3);
十三、单片机实操十一:模块化编程
1、在指定路径下创建工程并添加新文件
2、模块化编程
3、模块化编程框图
4、模块化编程注意事项
5、C预编译
6、增加中文注释
7、编写代码
main.c
#include <STC89C5xRC.H>
#include "Delay.h" // 使用双引号,优先查询工程目录
#include "Nixie.h"
void main()
while(1)
Nixie(1,1);
Nixie(2,2);
Nixie(3,3);
Delay.c
void Delay(unsigned int xms) //@12.000MHz
unsigned char i, j;
while(xms)
i = 2;
j = 239;
do
while (--j);
while (--i);
xms--;
Delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void Delay(unsigned int xms);
#endif
Nixie.c
#include <STC89C5xRC.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:
P24=1;P23=1;P22=1;break;
case 2:
P24=1;P23=1;P22=0;break;
case 3:
P24=1;P23=0;P22=1;break;
case 4:
P24=1;P23=0;P22=0;break;
case 5:
P24=0;P23=1;P22=1;break;
case 6:
P24=0;P23=1;P22=0;break;
case 7:
P24=0;P23=0;P22=1;break;
case 8:
P24=0;P23=0;P22=0;break;
P0=NixieTable[Number];
Delay(1); // Shadow elimination
P0=0x00; // reset
Nixie.h
#ifndef __NIXIE_H__
#define __NIXIE_H__
void Nixie(unsigned char Location,Number);
#endif
十四、单片机实操十二:LCD1602调试工具
1、在指定路径下创建工程并添加新文件
2、LCD1602调试工具
3、编写程序
main.c
#include <STC89C5xRC.H>
#include "LCD1602.h"
void main()
LCD_Init();
LCD_ShowChar(1,1,'B');
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);
LCD_ShowChar(2,13,'A');
while(1)
LCD1602.c
#include <STC89C5xRC.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');
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
4、注意事项
单引号和双引号都有全角和半角之分,全角的又叫中文字符,半角的又称英文字符。在c语言中,全角字符没有任何意义,它就是一个普通字符,没有含义;半角字符才有不同的意义:
双引号用来括起一个字符串,如"China";
单引号原来括起一个字符,如'f'。
二者含义不同,不能混用。
十五、单片机实操十三:矩阵键盘
1、在指定路径下创建工程并添加新文件
2、矩阵键盘介绍
3、扫描的概念
4、硬件连线
我们想选取1、5、9、13,即第一列,可以先把P1全部置为高,即全部拉高;接着让P13拉低,则选中了第一列。其他也这样操作。
5、编写程序
main.c
#include <STC89C5xRC.H>
#include "LCD1602.h"
#include "Delay.h"
#include "MatrixKey.h"
unsigned char KeyNum;
void main()
LCD_Init();
// LCD_ShowChar(1,1,'B');
LCD_ShowString(1,3,"MatrixKey:");
// LCD_ShowNum(1,9,123,3);
// LCD_ShowSignedNum(1,13,-66,2);
// LCD_ShowHexNum(2,1,0xA8,2);
// LCD_ShowBinNum(2,4,0xAA,8);
// LCD_ShowChar(2,13,'A');
while(1)
KeyNum=MatrixKey();
if(KeyNum)
LCD_ShowNum(2,3,KeyNum,2);
MatrixKey.c
#include <STC89C5xRC.H>
#include "Delay.h"
unsigned char MatrixKey()
unsigned char KeyNumber=0;
P1=0xFF;
P13=0;
if(P17==0)Delay(20);while(P17==0);Delay(20);KeyNumber=1;
if(P16==0)Delay(20);while(P16==0);Delay(20);KeyNumber=5;
if(P15==0)Delay(20);while(P15==0);Delay(20);KeyNumber=9;
if(P14==0)Delay(20);while(P14==0);Delay(20);KeyNumber=13;
P1=0xFF;
P12=0;
if(P17==0)Delay(20);while(P17==0);Delay(20);KeyNumber=2;
if(P16==0)Delay(20);while(P16==0);Delay(20);KeyNumber=6;
if(P15==0)Delay(20);while(P15==0);Delay(20);KeyNumber=10;
if(P14==0)Delay(20);while(P14==0);Delay(20);KeyNumber=14;
P1=0xFF;
P11=0;
if(P17==0)Delay(20);while(P17==0);Delay(20);KeyNumber=3;
if(P16==0)Delay(20);while(P16==0);Delay(20);KeyNumber=7;
if(P15==0)Delay(20);while(P15==0);Delay(20);KeyNumber=11;
if(P14==0)Delay(20);while(P14==0);Delay(20);KeyNumber=15;
P1=0xFF;
P10=0;
if(P17==0)Delay(20);while(P17==0);Delay(20);KeyNumber=4;
if(P16==0)Delay(20);while(P16==0);Delay(20);KeyNumber=8;
if(P15==0)Delay(20);while(P15==0);Delay(20);KeyNumber=12;
if(P14==0)Delay(20);while(P14==0);Delay(20);KeyNumber=16;
return KeyNumber;
MatrixKey.h
#ifndef __MATRIXKEY_H__
#define __MATRIXKEY_H__
unsigned char MatrixKey();
#endif
其它文件:复制之前写的模块即可。
6、软件使用技巧:配置模板
十六、单片机实操十四:矩阵键盘密码锁
1、复制工程后打开
2、编写程序
main.c
#include <STC89C5xRC.H>
#include "LCD1602.h"
#include "Delay.h"
#include "MatrixKey.h"
unsigned char KeyNum;
unsigned int Password, Count;
void main()
LCD_Init();
// LCD_ShowChar(1,1,'B');
LCD_ShowString(1,1,"Password:");
// LCD_ShowNum(1,9,123,3);
// LCD_ShowSignedNum(1,13,-66,2);
// LCD_ShowHexNum(2,1,0xA8,2);
// LCD_ShowBinNum(2,4,0xAA,8);
// LCD_ShowChar(2,13,'A');
while(1)
KeyNum=MatrixKey();
if(KeyNum)
if(KeyNum<=10) // 如果S1~S10按键按下,输入密码
if(Count<4) // 密码四位数
Password*=10; // 密码左移一位,这样就可以依次输入密码
Password+=KeyNum%10; // 获取一位密码,加入到原密码,此时10为0
Count++;
LCD_ShowNum(2,1,Password,4);
if(KeyNum==11) // 确认
if(Password==1234)
LCD_ShowString(1,14,"OK ");
Password=0;
Count=0;
LCD_ShowNum(2,1,Password,4);
else
LCD_ShowString(1,14,"ERR");
Password=0;
Count=0;
LCD_ShowNum(2,1,Password,4);
if(KeyNum==12) //取消
Password=0;
Count=0;
LCD_ShowString(1,14," ");
LCD_ShowNum(2,1,Password,4);
其它文件:复制之前写的模块即可。
十七、单片机实操十五:定时器与按键控制LED流水灯模式
1、定时器介绍
2、STC89C52定时器资源
3、定时器狂框图
4、定时器工作模式
5、中断系统
6、中断程序流程
7、STC89C52中断资源
8、定时器和中断系统
9、定时器相关寄存器
10、编写程序
通过独立按键控制流水灯模式,并由定时器执行流水灯。
main.c
#include <STC89C5xRC.H>
#include "Timer0.h"
#include "Key.h"
#include <INTRINS.H>
unsigned char KeyNum,LEDMode;
void main()
P2=0xFE;
Timer0Init();
while(1)
KeyNum=Key(); //获取独立按键键码
if(KeyNum) //如果按键按下
if(KeyNum==1) //如果K1按键按下
LEDMode++; //模式切换,按1下按键是模式1,按2下是模式0,默认模式0
if(LEDMode>=2)LEDMode=0;
void Timer0_Routine() interrupt 1 //中断函数标识,含优先级
static unsigned int T0Count; //静态变量,拥有局部作用域,全局生命周期
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++; //T0Count计次,对中断频率进行分频
if(T0Count>=500)//分频500次,500ms
T0Count=0;
if(LEDMode==0) //模式判断
P2=_crol_(P2,1); //LED输出(循环左移函数,即使流水灯循环左移)
if(LEDMode==1)
P2=_cror_(P2,1);
Timer0.c
#include <STC89C5xRC.H>
/**
* @brief 定时器0初始化,1毫秒@12.000MHz
* @param 无
* @retval 无
*/
void Timer0Init(void)
TMOD &= 0xF0; //设置定时器模式,只改变T0,避免T1改变
TMOD |= 0x01; //设置定时器模式
TL0 = 0x18; //高位设置定时初值 65535/256
TH0 = 0xFC; //低位设置定时初值 65535%256
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0=1;
EA=1;
PT0=0;
/*定时器中断函数模板
void Timer0_Routine() interrupt 1
static unsigned int T0Count; //静态变量,拥有局部作用域,全局生命周期
TL0 = 0x18; //设置定时初值,像沙漏,重置沙漏时间
TH0 = 0xFC; //设置定时初值
T0Count++;
if(T0Count>=1000)
T0Count=0;
*/
Timer0.h
#ifndef __TIMER0_H__
#define __TIMER0_H__
void Timer0Init(void);
#endif
Key.c
#include <STC89C5xRC.H>
#include "Delay.h"
/**
* @brief 获取独立按键键码
* @param 无
* @retval 按下按键的键码,范围:0~4,无按键按下时返回值为0
*/
unsigned char Key()
unsigned char KeyNumber=0;
if(P31==0)Delay(20);while(P31==0);Delay(20);KeyNumber=1;
if(P30==0)Delay(20);while(P30==0);Delay(20);KeyNumber=2;
if(P32==0)Delay(20);while(P32==0);Delay(20);KeyNumber=3;
if(P33==0)Delay(20);while(P33==0);Delay(20);KeyNumber=4;
return KeyNumber;
Key.h
#ifndef __KEY_H__
#define __KEY_H__
unsigned char Key();
#endif
其它文件:复制之前写的模块即可。
11、定时器代码获取
十八、单片机实操十六:定时器时钟
1、编写程序
制作一个一天时间的计时器。
main.c
#include <STC89C5xRC.H>
#include "Timer0.h"
#include "Delay.h"
#include "LCD1602.h"
unsigned char Sec, Min=59, Hour=23;
void main()
Timer0Init();
LCD_Init();
LCD_ShowString(1,1,"Clock:");
while(1)
LCD_ShowNum(2,1,Hour,2);
LCD_ShowString(2,3,":");
LCD_ShowNum(2,4,Min,2);
LCD_ShowString(2,6,":");
LCD_ShowNum(2,7,Sec,2);
void Timer0_Routine() interrupt 1 //中断函数标识,含优先级
static unsigned int T0Count; //静态变量,拥有局部作用域,全局生命周期
TL0 = 0x18; //设置定时初值
TH0 = 0xFC; //设置定时初值
T0Count++; //T0Count计次,对中断频率进行分频
if(T0Count>=1000)//1000ms
T0Count=0;
Sec++;
if(Sec>=60)
Sec=0;
Min++;
if(Min>=60)
Min=0;
Hour++;
if(Hour>=24)
Hour=0;
其它文件:复制之前写的模块即可。
十九、单片机实操十七:串口通信与串口向电脑发送数据
1、串口介绍
2、接口及引脚定义
3、硬件电路
4、电平标准
5、常见通信接口比较
6、相关术语
7、51单片机的UART
8、串口参数与时序图
9、串口模式图
10、串口相关寄存器
11、串口和中断系统
11、使用工具生成串口配置
12、编写程序
main.c
#include <STC89C5xRC.H>
#include "Delay.h"
#include "UART.h"
unsigned char Sec;
void main()
UartInit();
while(1)
UART_SendByte(Sec);
Sec++;
Delay(1); // 必要的延时,避免误差导致乱码,没误差的时候可以不需要
UART.c
#include <STC89C5xRC.H>
void UartInit(void) //4800bps@11.0592MHz
PCON &= 0x80; //波特率不倍速
SCON = 0x40; //8位数据,可变波特率
TMOD &= 0x0F; //设置定时器模式
TMOD |= 0x20; //设置定时器模式
TL1 = 0xFA; //设置定时初始值
TH1 = 0xFA; //设置定时重载值
ET1 = 0; //禁止定时器%d中断
TR1 = 1; //定时器1开始计时
void UART_SendByte(unsigned char Byte)
SBUF=Byte; // 根据硬件原理,操作寄存器
while(TI==0); // 操作寄存器,检测是否完成
TI=0; // 按要求重新赋值为0
UART.h
#ifndef __UART_H__
#define __UART_H__
void UartInit(void);
void UART_SendByte(unsigned char Byte);
#endif
其他文件:复制之前写的模块即可。
13、调试程序
二十、单片机实操十八:电脑通过串口控制LED
1、编写程序
#include <STC89C5xRC.H>
#include "Delay.h"
#include "UART.h"
unsigned char Sec;
void main()
UartInit();
while(1)
void UART_Routine() interrupt 4
if(RI==1)
P2=~SBUF; // 读寄存器的数据
UART_SendByte(SBUF);
RI=0; // 说明书要求手动复位
其他文件:复制之前写的模块即可。
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
#include <STC89C5xRC.H>
#include "Delay.H"
sbit RCK=P3^5; //RCLK
sbit SCK=P3^6; //SRCLK
sbit SER=P3^4; //SER
#define MATRIX_LED_PORT P0
void _74HC595_WriteByte(unsigned char Byte)
// SER=Byte&0x80; //一般是0、1赋值,不过,如果非0,都会当作1
// SCK=1;
// SCK=0;
// SER=Byte&0x60;
// SCK=1;
// SCK=0;
unsigned char i;
for(i=0;i<8;i++)
SER=Byte&(0x80>>i);
SCK=1;
SCK=0;
RCK=1;
RCK=0;
void MatrixLED_ShowColumn(unsigned char Column, Data)
_74HC595_WriteByte(Data);
// if(Column==0)P0=~0x80;
// if(Column==1)P0=~0x40;
MATRIX_LED_PORT=~(0x80>>Column);
Delay(1);
MATRIX_LED_PORT=0xFF;
void main()
SCK=0;
RCK=0;
while(1)
// _74HC595_WriteByte(0xAA);
MatrixLED_ShowColumn(0,0x80);
MatrixLED_ShowColumn(1,0x40);
MatrixLED_ShowColumn(2,0x20);
MatrixLED_ShowColumn(3,0x10);
二十二、单片机实操二十: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
#include <STC89C5xRC.H>
#include "Delay.H"
#include "MatrixLED.H"
unsigned char Animation[]=
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 为了显示更好看,避免第一列直接显示字符本身
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,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // 为了显示更好看
;
void main()
unsigned char i, Offset=1, Count=0;
while(1)
for(i=0;i<8;i++)
MatrixLED_ShowColumn(i,Animation[i+Offset]);
Count++;
if(Count>10)
Count=0;
Offset++;
if(Offset>40)
Offset=0; //防止数组溢出
MatrixLED.c
#include <STC89C5xRC.H>
#include "Delay.H"
sbit RCK=P3^5; //RCLK
sbit SCK=P3^6; //SRCLK
sbit SER=P3^4; //SER
#define MATRIX_LED_PORT P0
void _74HC595_WriteByte(unsigned char Byte)
// SER=Byte&0x80; //一般是0、1赋值,不过,如果非0,都会当作1
// SCK=1;
// SCK=0;
// SER=Byte&0x60;
// SCK=1;
// SCK=0;
unsigned char i;
for(i=0;i<8;i++)
SER=Byte&(0x80>>i);
SCK=1;
SCK=0;
RCK=1;
RCK=0;
void MatrixLED_Init()
SCK=0;
RCK=0;
void MatrixLED_ShowColumn(unsigned char Column, Data)
_74HC595_WriteByte(Data);
// if(Column==0)P0=~0x80;
// if(Column==1)P0=~0x40;
MATRIX_LED_PORT=~(0x80>>Column);
Delay(1);
MATRIX_LED_PORT=0xFF;
MatrixLED.h
#ifndef __MATRIXLED_H__
#define __MATRIXLED_H__
void MatrixLED_ShowColumn(unsigned char Column, Data);
void MatrixLED_Init();
#endif
3、注意事项
将数据放在flash里面,用来避免内存被消耗过多,但这种数据是不能更改的,方法如下:
将
unsigned char Animation[]
改为
unsigned char code Animation[]
二十三、单片机实操二十一:DS1302实时时钟
1、DS1302介绍
2、引脚定义和应用电路
3、内部结构图
4、寄存器定义
5、时序定义
6、BCD码
7、编写代码
main.c
#include <STC89C5xRC.H>
#include "LCD1602.h"
#include "DS1302.h"
// unsigned char Second, Minute;
void main()
LCD_Init();
DS1302_Init();
// DS1302_WriteByte(0x8E, 0x00); // 解除芯片写保护,避免数值不动
// DS1302_WriteByte(0x80, 0x54);
DS1302_SetTime();
while(1)
DS1302_ReadTime();
LCD_ShowNum(1,1,DS1302_TIME[0],2);
LCD_ShowNum(1,4,DS1302_TIME[1],2);
LCD_ShowNum(1,7,DS1302_TIME[2],2);
LCD_ShowNum(2,1,DS1302_TIME[3],2);
LCD_ShowNum(2,4,DS1302_TIME[4],2);
LCD_ShowNum(2,7,DS1302_TIME[5],2);
// Second=DS1302_ReadByte(0x81);
// Minute=DS1302_ReadByte(0x83);
// LCD_ShowNum(2,1,Second/16*10+Second%16,2);
// LCD_ShowNum(2,3,Minute/16*10+Minute%16,2);
DS1302.c
#include <STC89C5xRC.H>
sbit DS1302_SCLK=P3^6;
sbit DS1302_IO=P3^4;
sbit DS1302_CE=P3^5;
#define DS1302_SECOND 0x80
#define DS1302_MINUTE 0x82
#define DS1302_HOUR 0x84
#define DS1302_DATE 0x86
#define DS1302_MONTH 0x88
#define DS1302_DAY 0x8A
#define DS1302_YEAR 0x8C
#define DS1302_WP 0x8E
unsigned char DS1302_TIME[]=19,11,16,12,59,55,6;
void DS1302_Init(void)
DS1302_CE=0;
DS1302_SCLK=0;
void DS1302_WriteByte(unsigned char Command, Data)
unsigned char i;
DS1302_CE=1;
// DS1302_IO=Command&0x01;
// DS1302_SCLK=1; // 速度慢可以不加延时,有些速度快的芯片需要增加延时
// DS1302_SCLK=0;
//
// DS1302_IO=Command&0x02;
// DS1302_SCLK=1;
// DS1302_SCLK=0;
for(i=0;i<8;i++)
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
for(i=0;i<8;i++)
DS1302_IO=Data&(0x01<<i);
DS1302_SCLK=1;
DS1302_SCLK=0;
DS1302_CE=0; //完成一次操作,释放IO
unsigned char DS1302_ReadByte(unsigned char Command)
unsigned char i,Data=0x00;
Command|=0x01; //
DS1302_CE=1;
for(i=0;i<8;i++)
DS1302_IO=Command&(0x01<<i);
DS1302_SCLK=0; //根据时序操作
DS1302_SCLK=1;
// DS1302_SCLK=0;
// DS1302_SCLK=1;
// if(DS1302_IO)
//
// Data=Data|0x01;
//
// DS1302_SCLK=0;
// DS1302_SCLK=1;
// if(DS1302_IO)
//
// Data=Data|0x02;
//
for(i=0;i<8;i++)
DS1302_SCLK=1; //重复置1是去掉一个周期,为的是满足时序
DS1302_SCLK=0;
if(DS1302_IO)Data|=(0x01<<i);
DS1302_CE=0;
DS1302_IO=0; // 如果不加这一行,将显示全0
return Data;
void DS1302_SetTime(void)
DS1302_WriteByte(DS1302_WP,0x00);
DS1302_WriteByte(DS1302_YEAR,DS1302_TIME[0]/10*16+DS1302_TIME[0]%10);
DS1302_WriteByte(DS1302_MONTH,DS1302_TIME[1]/10*16+DS1302_TIME[1]%10);
DS1302_WriteByte(DS1302_DATE,DS1302_TIME[2]/10*16+DS1302_TIME[2]%10);
DS1302_WriteByte(DS1302_HOUR,DS1302_TIME[3]/10*16+DS1302_TIME[3]%10);
DS1302_WriteByte(DS1302_MINUTE,DS1302_TIME[4]/10*16+DS1302_TIME[4]%10);
DS1302_WriteByte(DS1302_SECOND,DS1302_TIME[5]/10*16+DS1302_TIME[5]%10);
DS1302_WriteByte(DS1302_DAY,DS1302_TIME[6]/10*16+DS1302_TIME[6]%10);
DS1302_WriteByte(DS1302_WP,0x00);
void DS1302_ReadTime(void)
unsigned char Temp;
Temp=DS1302_ReadByte(DS1302_YEAR);
DS1302_TIME[0]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_MONTH);
DS1302_TIME[1]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DATE);
DS1302_TIME[2]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_HOUR);
DS1302_TIME[3]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_MINUTE);
DS1302_TIME[4]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_SECOND);
DS1302_TIME[5]=Temp/16*10+Temp%16;
Temp=DS1302_ReadByte(DS1302_DAY);
DS1302_TIME[6]=Temp/16*10+Temp%16;
DS1302.h
#ifndef __DS1302_H__
#define __DS1302_H__
extern unsigned char DS1302_TIME[]; // 声明数组
void DS1302_Init(void);
void DS1302_WriteByte(unsigned char Command, Data);
unsigned char DS1302_ReadByte(unsigned char Command);
void DS1302_SetTime(void);
void DS1302_ReadTime(void);
#endif
二十四、单片机实操二十二:DS1302可调时钟
1、编写程序
main.c
#include <STC89C5xRC.H>
#include "LCD1602.h"
#include "DS1302.h"
#include "Key.h"
#include "Timer0.h"
unsigned char KeyNum,MODE,TimeSetSelect,TimeSetFlashFlag;
void TimeShow(void)//时以上是关于C语言:51单片机看这一篇就够了的主要内容,如果未能解决你的问题,请参考以下文章
[建议速看] 学习Python的小白了解 Python看这一篇就够了!