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]!='