2-1 点亮一个LED
2023 04 21
单片机是如何控制LED亮灭的?
LED的一段连着固定电压的线路,另一端连着MCU的引脚,MCU只需要控制输出这根引脚的电压就可以控制灯的亮灭了
MCU中,CPU修改寄存器的值,驱动器读取寄存器的值提供高低电平
#include <REGX52.H>
int main(){
P2 = 0xEE;//1111 1110
return 0;
}
2-2 LED闪烁
2023 04 21
在STC-ISP中选择软件延时器生成一个1ms的延迟方法之后改成延迟任意时间的方法
记得#include <INTRINS.H>
#include <REGX52.H>
#include <INTRINS.H>
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--){
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
void Delay500ms(unsigned int xms) //@11.0592MHz
{
unsigned char i, j, k;
_nop_();
i = 4;
j = 129;
k = 119;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}
int main(){
while(1){
Delay(500);
P2 = 0xFE;//1111 1110
Delay(500);
P2 = 0xFF;//1111 1110
}
return 0;
}
2-3 LED流水灯
2023 04 21
#include <REGX52.H>
#include <INTRINS.H>
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--){
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
int main(){
unsigned int index = 0;// 闪烁灯的位置
while(1){
Delay(500);
P2 = ~(1 << index);
index++;
index %= 8;
}
return 0;
}
3-1 按键控制LED灯亮灭
2023 04 21
按键接驳的线路是低电平,所以在按下后会使连接的io口变成低电平,不按时是高电平(单片机常高电平)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1VmsNxWn-1682103045711)(51单片机.assets/image-20230421132036013.png)]
include <REGX52.H>
比 #include <REG52.H>
多了单位定义(P3_1)
#include <REGX52.H>
int main(){
while(1){
if(P3_1 == 1)
P2 = 0xFF;
else
P2 = 0xFE;
}
return 0;
}
3-2 消除按键抖动的影响
2023 04 21
#include <REGX52.H>
#include <INTRINS.H>
int main(){
int static1 = 0;
while(1){
if(P3_1 == 0){
// 没有消抖
if(P3_1 == 1){
static1 ^= 1;
}
}
if(static1 == 0)
P2 = 0xFF;
else
P2 = 0xFE;
}
return 0;
}
在按下按键时多数时候不起作用,因为对于一个机械开关,在连接和断开时的电流并不是瞬间就变为另一种,而是在抖动中接近.
解决办法有两种,硬件消抖和软件消抖
#include <REGX52.H>
#include <INTRINS.H>
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--){
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
int main(){
while(1){
if(P3_1 == 0){
Delay(20);// 通过等待20ms消除抖动
if(P3_1 == 1){
// 写逻辑写惯了没注意到P2_0的数据也是可以直接读取的
P2_0 = ~P2_0;
}
}
}
return 0;
}
3-3 独立按键控制LED显示二进制
2023 04 21
#include <REGX52.H>
#include <INTRINS.H>
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--){
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
int main(){
while(1){
if(P3_1 == 0){
Delay(20);// 通过等待20ms消除抖动
if(P3_1 == 1){
P2--;
}
}
}
return 0;
}
3-4 独立按键控制LED移位
2023 04 21
#include <REGX52.H>
#include <INTRINS.H>
void Delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--){
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
int main(){
int index = 0;
P2 = 0xFE;
while(1){
if(P3_0 == 0){
Delay(20);// 通过等待20ms消除抖动
if(P3_0 == 1){
index++;
index%=8;
P2 = ~(1 << index);
}
}
if(P3_1 == 0){
Delay(20);// 通过等待20ms消除抖动
if(P3_1 == 1){
index--;
if(index < 0) index = 7;
P2 = ~(1 << index);
}
}
}
return 0;
}
4-1 静态数码管显示
2023 04 21
数码管
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1YMXLhyq-1682103045712)(51单片机.assets/image-20230421161235244.png)]
数码管有单个数码管和四位一体数码管,也分为共阴极和共阳极.
51单片机的高电平驱动能力较弱,所以如果时共阴极的数码管,单片机控制高电平为亮时需要在中间续上中继器以提高驱动能力.之前LED不用是因为单片机提供的是低电平.
四位一体数码管的控制显示的数字是四个数码管公用的一个,但共阴极部分是分别单独控制的,只需要让同一时刻只有一个数码管是显示,利用人眼视觉暂留达到同时显示的效果.
74HC245在这里起到的是中继器的作用,但其实它还是缓冲器,给LE输出不同的电平,高电平将数据从左到右,低电平将数据送右到左.在这里LE直接接的高电平.
138译码器
四位一体数码管的引脚设计思路已经大大减少了所需的引脚数目,但138译码器可以将需要的引脚数目进一步降低.
8个数码管的亮灭需要单独8个引脚,但我们所用到的状态只有8种,就是各个数码管单独显示的8个状态,8种状态的信息是可以用3个io口传递的
代码
P2_2,P2_3,P2_4三位控制单个数码管的亮灭
P0_0-P0_7控制数码管内8个LED的亮灭
#include <REGX52.H>
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 = 0x7D;
}
int main(){
nixie(7, 2);
return 0;
}
效果为倒数第二位显示6
启用Number,用数组存数码管的显示
#include <REGX52.H>
unsigned char nixieTable[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
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];
}
void main() {
while (1) {
nixie(2, 0);
}
}
4-2 动态数码管显示
2023 04 21
#include <REGX52.H>
#include <INTRINS.H>
void delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--){
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
unsigned char nixieTable[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
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];
}
void main() {
while (1) {
unsigned char i = 0;
for(i = 1; i <= 8;i++){
nixie(i, i-1);
delay(2);
}
}
}
数码管的消影
如果没有delay(2)效果如下:
数码管显示的流程是:段选 位选 段选 位选 段选 位选
但因为单片机的运算速度很快,在位选到下一位后段选还没反应过来(硬件跟不上),所以会有上一位的残影
修改方法为每显示一位后等待一段时间后将段选清零,这样在下一次的位选->段选流程就不会有上一次的段选残留
#include <REGX52.H>
#include <INTRINS.H>
void delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--){
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
unsigned char nixieTable[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
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;// 如果没有这个的话还是会看见轻微的残留
}
void main() {
while (1) {
unsigned char i = 0;
for(i = 1; i <= 8;i++){
nixie(i, i-1);
}
}
}
数码管驱动方式
单片机直接扫描:硬件设备简单,但会消耗大量的单片机CPU时间
专用驱动芯片:内部自带显存、扫描线路,单片机只需要告诉他显示什么即可
5-1 模块化编程
2023 04 21
main.c
#include <REGX52.H>
#include "delay.h"
#include "nixie.h"
int main(){
while(1){
P2 = 0xFE;
delay(500);
P2 = 0xFF;
delay(500);
nixie(3,3);
}
}
delay.h
#ifndef __DELAY_H__
#define __DELAY_H__
void delay(unsigned int xms);
#endif
delay.c
#include <INTRINS.H>
void delay(unsigned int xms) //@11.0592MHz
{
unsigned char i, j;
while(xms--){
_nop_();
i = 2;
j = 199;
do
{
while (--j);
} while (--i);
}
}
nixie.h
#ifndef __NIXIE_H__
#define __NIXIE_H__
void nixie(unsigned char location,number);
#endif
nixie.c
#include <REGX52.H>
#include "delay.h"
unsigned char nixieTable[] = {0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F};
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调试工具
2023 04 21
使用LCD1602液晶屏作为调试窗口,提供类似printf函数的功能
函数 | 作用 |
---|---|
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); | 显示二进制数字 |