目录
1.LED介绍
发光二极管也具有二极管普遍的特性单向导电性,有阳极和阴极之分 ,上图左侧式插件式LED ,长的引脚是阳极;左侧是贴片式的带色的一端是阴极,箭头指向是阴极,也就是电流的流向。二极管阳极接电源,阴极接地。电流大小一般在3-20ma之间,若是电流再大容易烧坏二极管。二极管的亮度和电流的大小有关,电流越大,发光二极管就越亮,自身带有导通压降1.7V 。
2.硬件电路
为防止发光二级管因为电流而烧毁,需要串联一个电阻来限制电流的大小,硬件连接电路如下
图中的电阻是排阻内有四个电阻并,阳极接在电源处,阴极经过排阻和单片机的I/0相连接,这样只有单片机输出的是低电平的时候,电路导通,二极管才能发光。
3.程序设计
3.1.点亮一颗LED
假设控制是D1发光二极管,那么P20引脚只有输出低电平的情况下,电路导通。我们直接将P20口置为0,这样端口就是低电平了。
sbit LED1 = P2^0;
void main()
{
LED1 = 0;
while(1)
{
}
}
如何编程 P2.0管脚输出低电平
1.先定义管脚 :使用一个关键字 sbit 是特殊功能寄存器的某一个位的位定义 P2寄存器 ,语句加分号结束
2.直接将定义的引脚置为0 。
程序从main函数执行,单片机不断的循环执行程序 ,如果有while就会一直执行while循环。
code 程序flash大小 data ram 数据占用的大小 xdata 外部的ram 没有的话就不用调用了
编译之后才会出现的上述的单位是字节
3.2.LED闪烁
使用循环语句让D1二极管一亮一灭,想要实现可以让P2.0GPIO口一会输出低电平,一会儿输出高电平,当然还要有延时,人肉眼的识别要大于20-50ms。这里的延时程序是不准确的,想要精确的定时是要有定时器的。u16的最大值是65535。这里的延时时长和单片机的晶振频率有关。
#include <REGX52.H>
sbit LED1 = P2^0;
void delay_10us(u16 ten_us)//当传输ten_us=1的时候,大约10us
{
while(ten_us--);
}
void main()
{
while(1)
{
LED1 = 0;
delay_10us(500000);
LED1 = 1;
delay_10us(500000);
}
}
3.3.LED流水灯实现
D1-D8一次点亮,移位操作和循环(while)的结合 ,对8个I/O进行操作,
宏定义可以定义一组寄存器 ,定义P2端口 PORT端口,在宏定义的时候不用加分号
P2.7是高位 P2.0是低位 左移之后,低位补0可以使用0x01移动之后取反,这样就会有一个二极管是常量的,总共有8个二极管,循环体要循环八次才能让每一个二极管亮一遍。
#include <REGX52.H>
typedef unsigned char u8;
typedef unsigned int u16;
#define LED_PORT P2
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void main()
{
while(1)
{
u8 i = 0;
for(i = 9; i < 8;i++)
{
LED_PORT = ~(0X01<<i);
delay_10us(50000);
}
}
}
一个来回从D1-D8,再从D8-D1;使用的是 <intrins.H>头文件中包含的函数
#include <REGX52.H>
#include <intrins.H>
typedef unsigned char u8;
typedef unsigned int u16;
#define LED_PORT P2 //
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
void main()
{
u8 i = 0;
LED_PORT = ~(0x01);
delay_10us(50000);
while(1)
{
for(i = 0; i < 7;i++)//这里循环了7次,因为刚开始D1灯已经亮了
{
LED_PORT = _crol_ (LED_PORT,1);//向左移动
delay_10us(50000);
}
for(i = 0; i < 7;i++)
{
LED_PORT = _cror_(LED_PORT,1);//向右移动
delay_10us(50000);
}
}
}
_crol_和_cror_和C语言中的位移符号是不一样的。
使用函数进行位移情况,二级制的补码是首位相接的,如下
_crol_(11111110,1);
1111 1110 比如每一次移动一次 左移为例
1111 1101
1111 1011 //首位相连 _crol_(11111110,1);
使用位移操作符<< 左移为例 移动一位,空白的区域填上0。
1111 1110
1111 1100
1111 1000 //每一次移动一位 <<1
4.按键介绍
图片来自开发板开源教程,上图就是轻触按键了,初始状态按键的长边是导通的,短边不导通;当按下按键的时候,四角均导通;可以控制外部设备和调节参数。当按下按键的时候会有抖动,我们采用的是软件消抖的方式,来精确的读出按键所接引脚的高低电平。
为什么要消抖呢?
当按下按键的时候,按键会有抖动,这个时候的电压不稳定可能会读取到高电平或者低电平,因此我们直接不读取这段时间的数值,直接延时2ms-10ms(抖动时间)来完成消抖。
一般我们不会考虑释放抖动之后再读取数值。
5.按键电路
图片中 按键一脚接单片机I/O口,一端接地,当按下高低电平时候,可以实现高电平到低电平的转换。
6.代码实现
u8 key_scan(mode)按键函数思路,
1.根据创建的枚举变量作为返回值来确定按个按键的按下
2.当mode=0的时候单次扫描,mode=1的时候是多次扫描。例如音量的加加和减减;
#include <REGX52.H>
#include <stdio.h>
#define SMG_A_DP_PORT P1
sbit KEY1=P0^0;
sbit KEY2=P0^1;
sbit KEY3=P0^2;
sbit KEY4=P0^3;
sbit LED1=P2^0;
sbit LED2=P2^1;
sbit LED3=P2^2;
sbit LED4=P2^3;
enum KEY
{
KEY_UNPRESS=0,
KEY1_PRESS=1,
KEY2_PRESS=2,
KEY3_PRESS=3,
KEY4_PRESS=4,
};//枚举
typedef unsigned char u8;
typedef unsigned int u16;
//ÑÓʱº¯Êý µ±1=ten_us,ÑÓʱ10us
void delay_10us(u16 ten_us)
{
while(ten_us--);
}
u8 key_scan(mode)
{
static u8 key=1;
if(mode)
{
key=1;
}
if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0))
{
key=0;
delay_10us(1000);
if(KEY1==0)
return KEY1_PRESS;
else if(KEY2==0)
return KEY2_PRESS;
else if(KEY3==0)
return KEY3_PRESS;
else if(KEY4==0)
return KEY4_PRESS;
}
else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1)
{
key=1;
}
return KEY_UNPRESS;
}
void main()
{
u8 ret=0;
while(1)
{
ret=key_scan(0);
switch(ret)
{
case KEY1_PRESS:
{
LED1=!LED1;
break;
}
case KEY2_PRESS:
{
LED2=!LED2;
break;
}
case KEY3_PRESS:
{
LED3=!LED3;
break;
}
case KEY4_PRESS:
{
LED4=!LED4;
break;
}
}
}
}