本节主要内容为基础IO口驱动跑马灯及按键控制LED与蜂鸣器实验,对应课本第5章第1、2小结,另课本例5-2代码也非常经典,运用了与运算、switch-case、do-while语句
基础实验
1、利用单片机及8个发光二极管等器件,构成一个单片机控制的流水灯系统
基础代码 实现由上至下循环点亮
#include <reg51.h>
#include <intrins.h>
#define uchar unsigned char
#define uint unsigned int
void delay(uint i){
uchar j;
while(i--){
for(j=0;j<255;j++)
}
}
void main(){
P1 = 0xfe; //向P1.0送点亮数据
while(1){
delay(500);
P1 = _crol_(P1,1); //向左循环移1位
}
}
知识点1
_crol_:循环左移(left)
_cror_:循环右移(right)
使用循环移位符时需要 #include <intrins.h>
拓展代码 实现由上至下,再由下至上反复循环点亮显示的流水灯
(1)数组的字节操作实现 用tab[ ]实现
uchar tab[] = {0xfe~0xfe}; //前8个数据为由上至下点亮数据,后8个为由下至上点亮数据
//内容需自己补充完整
void delay(){
uchar i,j;
for(i=0;i<255;i++);
for(j=0;j<255;j++);
}
void main(){
while(1){
for(i=0;i<16;i++){
P1 = tab[i];
delay();
}
}
}
(2)字节运算符实现 用>>和<<符号实现
void main(){
uchar temp,i;
while(1){
temp = 0x01;
for(i=0;i<8;i++){
P1 = ~temp;
delay();
temp<<1;
}
temp = 0x80;
for(i=0;i<8;i++){
P1 = ~temp;
delay();
temp>>1;
}
}
}
Q:为什么在这段代码中要先赋给变量temp=0x01后再取反?
因为>>和<<运算符与循环移位函数不同,<<会在移位时将高位丢弃,低位置0,而P1口送0时LED灯才会亮
(3)通过循环移位符实现
void main(){
while(1){
temp = 0xfe;
for(i=0;i<7;i++){
P1 = temp;
delay();
temp = _crol_(temp,1);
}
for(i=0;i<7;i++){
P1 = temp;
delay();
temp = _cror_(temp,1);
}
}
}
注意:这里的循环中i<7,因为一开始就已经置temp为第一个灯亮了
拓展实验一
按键控制LED灯
#include "reg51.h"
#include "intrins.h"
#define uchar unsigned char
#define uint unsigned int
#define in P3
#define out P1
// 延时函数,延时大约 10 毫秒
void Delay10ms()
{
uint i, j;
for (i = 100; i > 0; i--)
for (j = 120; j > 0; j--);
}
void main(void)
{
uchar i;
bit m;
while (1)
{
for (i = 0; i < 8; i++) // 扫描 P3 口的按键
{
m = (in >> i) & 0x01; // 获取 P3 的第 i 位输入状态
if (m == 0) // 检测按键是否按下
{
Delay10ms(); // 延时 10 毫秒消抖
if (m == 0) // 再次确认按键状态
{
out = _crol_(0xFE, i); // P1 口输出点亮对应位置 LED 灯
}
}
}
}
}
拓展实验二
按键控制蜂鸣器
工作原理:
当单片机P2口输出高电平时,基极电流流入三极管,使其进入饱和导通状态。
三极管导通后,蜂鸣器负极接地,电流通过蜂鸣器形成回路,蜂鸣器因此发声。
当P2口输出低电平时,三极管截止,蜂鸣器无电流流过,因而不发声
#include <reg51.h>
#define uchar unsigned char
#define uint unsigned int
sbit key=P3^2 ;
sbit BUZZER=P2^3;
void delayms(uchar ms)
{
uchar i;
while(ms--)
{
for(i=0;i<120;i++);
}
}
void beep()
{
BUZZER=1;
delayms(2000);
BUZZER=0;
delayms(2000);
}
void check_key()
{
if (key==0){
beep();
beep();
beep();
while(key==0);
}
}
void main()
{
BUZZER=0;
while(1)
{
check_key();
}
}
延时消抖优化
void check_key()
{
if (key == 0) {
delayms(20); // 延时去抖
if (key == 0) {
beep();
beep();
beep();
while (key == 0); // 等待按键松开
}
}
}
Q:如果不加while (key == 0); // 等待按键松开 会发生什么?
- 蜂鸣器会因按键持续按下而重复鸣叫,功能表现不符合设计预期。
- 主程序循环的效率会降低,逻辑可能陷入重复执行。
拓展实验三
按键控制流水灯
4个按键
#include <reg51.h>
#define uchar unsigned char
#define LED P2
sbit key1 = P1^4;
sbit key2 = P1^5;
sbit key3 = P1^6;
sbit key4 = P1^7;
uchar mode = 0;
void delayms(uchar ms) {
uchar i;
while(ms--) {
for(i = 0; i < 120; i++);
}
}
void check_button() {
if(key1 == 0) {
delayms(10); // 按键消抖
if(key1 == 0) {
mode = 1;
while(key1 == 0);
}
}
if(key2 == 0) {
delayms(10); // 按键消抖
if(key2 == 0) {
mode = 2;
while(key2 == 0);
}
}
if(key3 == 0) {
delayms(10); // 按键消抖
if(key3 == 0) {
mode = 3;
while(key3 == 0);
}
}
if(key4 == 0) {
delayms(10); // 按键消抖
if(key4 == 0) {
mode = 4;
while(key4 == 0);
}
}
}
void led_mode()
{
switch(mode)
{
case 1:
LED=0x00;
break;
case 2:
LED=0x55;
delayms(2000);
LED=0xAA;
delayms(2000);
break;
case 3:
LED=0xF0;
break;
case 4:
LED=0x0F;
break;
}
}
void main() {
LED = 0xFF; // 初始化LED状态
while(1) {
check_button();
led_mode();
}
}
1个按键
#include <reg51.h>
#define uchar unsigned char
sbit S = P1^4;
#define LED P2
uchar mode = 0;
void delayms(uchar ms)
{
uchar i;
while(ms--)
{
for(i = 0; i < 120; i++);
}
}
void check_button()
{
if(S == 0) {
delayms(10); // 按键消抖
if(S == 0) { // 确认按键状态
mode++;
if(mode > 4) {
mode = 0;
}
while(S == 0); // 等待按键释放
}
}
}
void led_mode()
{
switch(mode)
{
case 0:
LED = 0xFF; // 所有灯灭
break;
case 1:
LED = 0x00; // 所有灯亮
break;
case 2:
LED = 0x55; // LED 闪烁
delayms(2000);
LED = 0xAA;
delayms(2000);
break;
case 3:
LED = 0xF0; // 前四灭,后四亮
break;
case 4:
LED = 0x0F; // 前四亮,后四灭
break;
}
}
void main()
{
LED = 0xFF; // 初始化为所有灯灭
while(1) {
check_button();
led_mode();
}