一、题目要求
1-硬件框图
2-功能描述
2-1 基本描述
通过单片机控制 8 个 LED 指示灯按照特定的顺序亮灭。
- 指示灯的流转间隔可通过按键调整,亮度可由电位器 RB2 进行控制。
各工作模式的流转间隔时间需在 E2PROM 中保存,并可在硬件重新上电后,自动载入。
2-2 设计说明
- 关闭蜂鸣器、继电器等与本试题程序设计无关的外设资源。
- 设备上电后默认数码管、LED 指示灯均为熄灭状态。
- 流转间隔可调整范围为 400ms-1200ms。
设备固定按照模式 1、模式 2、模式 3、模式 4 的次序循环往复运行。
2-3 LED 指示灯工作模式
- 模式 1:按照 L1、L2…L8 的顺序,从左到右单循环点亮。
- 模式 2:按照 L8、L7…L1 的顺序,从右到左单循环点亮。
模式 3:
模式4:
2-4 亮度等级控制
检测电位器RB2的输出电压,控制8个LED指示灯的亮度,要求在0V-5V
的可调区间内,实现 4 个均匀分布的 LED 指示灯亮度等级。
2-5 按键功能
二、程序源码
#include "stc15.h"
#include "iic.h"
#include <stdio.h>
#define uchar unsigned char
#define uint unsigned int
#define KEYCOM P3 //按键IO
#define KEY_S7 0x01
#define KEY_S6 0x02
#define KEY_S5 0x04
#define KEY_S4 0x08
#define PWM_MAX 19 //pwm周期 <20
uchar code smg_dis[]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,0x7D,0x07,0x7F,0x6F,0x00,0x40};
uchar code LED_Max[]={7,7,3,3};//模式轮转时 数组最大下标
uchar code LED_MODE1[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
uchar code LED_MODE2[]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
uchar code LED_MODE3[]={0x81,0x42,0x24,0x18};
uchar code LED_MODE4[]={0x18,0x24,0x42,0x81};
uint code PWM_Val[]={0,5,10,15,20};//亮度调节PWM值
uchar Smg_Buf[8]={10,10,10,10,10,10,10,10};
uchar Led_Time[4]={4,4,4,4};//轮转时间
bit LED_Switch=0;//led轮转开关
bit KEY_Flag=0;
bit ADC_Flag=0;
bit SET_Flag=0;
bit Bilik_Flag=0;//0.8s闪烁
uchar Trg , Cont;//键值
uchar Rank=1;//光照等级
void Delay2ms();//写入延时
void UartInit(void);
void Timer0Init(void) ;
void KeyRead ();
void KEY_Dispose (void);//按键处理
void Set_Show (uchar mode);
void Write_AT24 (uchar add,uchar dat);
uchar Read_AT24 (uchar add);
uchar Read_ADC (uchar add);//读取adc
uchar KEY_Resize (uchar sum,uchar Max,uchar Min);//按键调整数值
void main ()
{
uchar ADC_Val;
P0=0x00;P2=0xa0;P2=0x00;
P0=0xff,P2=0x80,P2=0x00;
UartInit();
Timer0Init();
Led_Time[0]=Read_AT24(0x01);
Led_Time[1]=Read_AT24(0x02);
Led_Time[2]=Read_AT24(0x03);
Led_Time[3]=Read_AT24(0x04);
if( Led_Time[0]>=4 && Led_Time[1]>=4 && Led_Time[2]>=4 && Led_Time[3]>=4 );//判断读取到的间隔是否小于预设值 小于则为失败
else
{
Led_Time[0]=4;
Led_Time[1]=4;
Led_Time[2]=4;
Led_Time[3]=4;
}
while(1)
{
if(KEY_Flag)
{
KEY_Flag=0;
KEY_Dispose();//处理按键事件
if(!SET_Flag && (Trg & KEY_S4 || Cont & KEY_S4))//普通状态显示亮度
{
Smg_Buf[6]=11;
Smg_Buf[7]=Rank;
}
else if (!SET_Flag)
{
Smg_Buf[6]=10;
Smg_Buf[7]=10;
}
}
if(ADC_Flag)//亮度判断
{
ADC_Flag=0;
ADC_Val=Read_ADC(0x03);
if(ADC_Val<=69)
Rank=1;
else if (ADC_Val>69&&ADC_Val<=131)
Rank=2;
else if (ADC_Val>131&&ADC_Val<=193)
Rank=3;
else if (ADC_Val>193&&ADC_Val<=255)
Rank=4;
}
}
}
void Timer0Init(void) //[email protected]
{
AUXR |= 0x80;
TMOD &= 0xF0;
TL0 = 0xCD;
TH0 = 0xD4;
TF0 = 0;
TR0 = 1;
ET0 = 1;
EA = 1;
}
void TIME0() interrupt 1
{
static uint LedCount =0;
static uchar KeyCount =0;
static uchar SmgCount =0;
static uchar SmgLen=0;
static uchar AdcCount=0;
static uint BilikCount=0;
static uchar i =0;
static uchar PWM;
static uchar Run_Mode =1;
if(++KeyCount>10)
{
KeyCount=0;
KEY_Flag=1;
}
if(++AdcCount>100)
{
AdcCount=0;
ADC_Flag=1;
}
if(SET_Flag)//设置下闪烁所选位
{
if( ++BilikCount>800)
{
BilikCount=0;
Bilik_Flag=~Bilik_Flag;//不可手动清0
}
}
if(++SmgCount>1)
{
SmgCount=0;
P0=~smg_dis[Smg_Buf[SmgLen]];P2=0xe0;P2=0x00;
P0=1<<SmgLen;P2=0xc0;P2=0x00;
if(++SmgLen>7)SmgLen=0;
}
/*****************/
PWM++;
if(PWM <= PWM_Val[Rank])
{
switch(Run_Mode)//模式
{
case 1 :P0=~LED_MODE1[i]; break;
case 2 :P0=~LED_MODE2[i]; break;
case 3 :P0=~LED_MODE3[i]; break;
case 4 :P0=~LED_MODE4[i]; break;
}
P2=0x80;P2=0x00;
}
else if(PWM < PWM_MAX&&PWM>PWM_Val[Rank])//pwm周期小于20ms 视觉残留
{
P0=~0x00;
P2=0x80;P2=0x00;
}
else
{
PWM=0;
}
/*****************/
if(!LED_Switch)//暂停时 不影响亮度
{
++LedCount;
if(LedCount > Led_Time[Run_Mode-1]*100)
{
LedCount=0;
++i;
if( i > LED_Max[Run_Mode-1])
{
i=0;
Run_Mode++;//模式切换
if(Run_Mode==5)Run_Mode=1;
}
}
}
}
void KeyRead (void)
{
uchar ReadData = KEYCOM ^ 0xff;
Trg = ReadData & (ReadData ^ Cont);
Cont = ReadData;
}
void KEY_Dispose (void)
{
static uchar SET_MODE=0,mode1=0;
KeyRead();
if( Trg & KEY_S7 )
{
LED_Switch=~LED_Switch;
}
if( Trg & KEY_S6 )
{
if(!SET_Flag)
{
SET_MODE=0;
SET_Flag=1;
mode1=0;
}
else
SET_MODE++;
if(SET_MODE>=2)//退出设置 清空显示缓存 便于亮度显示 向EEPROM写入间隔
{
SET_MODE=0;
SET_Flag=0;
Smg_Buf[0]=10;
Smg_Buf[1]=10;
Smg_Buf[2]=10;
Smg_Buf[3]=10;
Smg_Buf[4]=10;
Smg_Buf[5]=10;
Smg_Buf[6]=10;
Smg_Buf[7]=10;
Write_AT24(0x01,Led_Time[0]);//写入间隔
Delay2ms();//必须延时
Write_AT24(0x02,Led_Time[1]);
Delay2ms();
Write_AT24(0x03,Led_Time[2]);
Delay2ms();
Write_AT24(0x04,Led_Time[3]);
Delay2ms();
}
}
if(SET_Flag)//设置模式
{
if(SET_MODE==0)
{
mode1=KEY_Resize(mode1,3,0);
Set_Show(mode1);
if(Bilik_Flag)//闪烁模式位
{
Smg_Buf[1]=10;
}
}
else
{
Led_Time[mode1]=KEY_Resize(Led_Time[mode1],12,4);
Set_Show(mode1);
if(Bilik_Flag)//闪烁轮转间隔
{
Smg_Buf[4]=10;
Smg_Buf[5]=10;
Smg_Buf[6]=10;
Smg_Buf[7]=10;
}
}
}
}
uchar KEY_Resize (uchar sum,uchar Max,uchar Min)
{
char Temp;//无字符型uchar <0时会超过取值范围
Temp=sum;
if(Trg & KEY_S5)
{
if( ++Temp > Max ) Temp=Max;
}
if(Trg & KEY_S4)
{
if( --Temp < Min ) Temp=Min;
}
return Temp;
}
void UartInit(void) //[email protected]
{
SCON = 0x50;
AUXR |= 0x01;
AUXR |= 0x04;
T2L = 0xE0;
T2H = 0xFE;
AUXR |= 0x10;
TI=1;
}
void Set_Show (uchar mode)
{
Smg_Buf[0]=11;
Smg_Buf[1]=mode+1;
Smg_Buf[2]=11;
Smg_Buf[3]=10;
if(Led_Time[mode]<10)//当轮转时间小于1000时 即<10 不显示千位
Smg_Buf[4]=10;
else
Smg_Buf[4]=Led_Time[mode]/10;//1000位
Smg_Buf[5]=Led_Time[mode]%10;//100位
Smg_Buf[6]=0;
Smg_Buf[7]=0;//个位与十位一直为0
}
uchar Read_ADC (uchar add)//读取adc
{
uchar Temp;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
Temp=IIC_RecByte();
IIC_WaitAck();
IIC_Stop();
return Temp;
}
void Write_AT24 (uchar add,uchar dat)
{
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Stop();
}
uchar Read_AT24 (uchar add)
{
uchar Temp;
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(add);
IIC_WaitAck();
IIC_Start();
IIC_SendByte(0xa1);
IIC_WaitAck();
Temp=IIC_RecByte();
IIC_WaitAck();
IIC_Stop();
return Temp;
}
void Delay2ms() //@11.0592MHz
{
unsigned char i, j;
_nop_();
_nop_();
i = 22;
j = 128;
do
{
while (--j);
} while (--i);
}