138译码器相关,基础模块的必要工具
//HC138端口选择
//通过前三位按位与,其他位数按位或的原理
//省去了部分HC138选端口的代码 //最好分开写
void InitHC138(unsigned char n)
{
switch(n)
{
case 4:
P2=P2&0x1f;
P2=P2|0x80; //LED
break;
case 5:
P2=P2&0x1f;
P2=P2|0xa0; //蜂鸣器继电器
break;
case 6:
P2=P2&0x1f;
P2=P2|0xc0; //数码管位选
break;
case 7:
P2=P2&0x1f;
P2=P2|0xe0; //数码管段选
break;
case 0:
P2=P2&0x1f;
P2=P2|0x00; //关闭锁存器
break;
}
}
//初始化
void InitSystem()
{
InitHC138(5);
P0=0x00; //初始关闭LED与蜂鸣器,防止上电出现错误
InitHC138(4);
P0=0xFF;
}
LED相关代码(解决了LED闪烁与数码管冲突的问题)
默认变量定义
// LED状态缓存(0:灭,1:亮)
unsigned char Led_Status = 0xFF; // 默认全灭(共阳极)
// LED模式定义(0:常亮/常灭,1:闪烁)
unsigned char Led_mode[8] = {0}; // 8个LED的模式
// 闪烁计数器(控制闪烁频率)
unsigned int Blink_counter = 0;
unsigned char Blink_Time=100 // 闪烁间隔(单位:ms)
功能代码
将上段代码放入定时器定期扫描,下段代码则是真正起到设置作用的代码
void UpdateLEDs()
{
// 仅在需要时操作硬件(避免频繁调用138)
static unsigned char Last_Led_Status = 0xFF;
if (Led_Status != Last_Led_Status)
{
InitHC138(4); // 选择Y4(LED锁存器)
P0 = Led_Status; // 写入LED状态
InitHC138(0); // 关闭锁存器(Y0) //不关闭锁存器就会与数码管冲突
Last_Led_Status = Led_Status;
}
}
// 设置LED模式(led_num: 0~7,mode: 0=常亮/常灭,1=闪烁)
void SetLedMode(unsigned char Led_Num, unsigned char mode, unsigned char state) {
if (Led_Num > 7) return;
Led_mode[Led_Num] = mode;
// 初始化状态(若为常亮/常灭)
if (mode == 0)
{
if (state)
{
Led_Status |= (1 << Led_Num); // 灭
}
else
{
Led_Status &= ~(1 << Led_Num); // 亮
}
}
UpdateLEDs(); // 立即更新硬件
}
//闪烁功能由此实现,定期翻转亮灭状态,控制对应LED位置亮灭
if (++ms_counter >= Blink_Time)
{
ms_counter = 0;
Blink_counter ^= 1; // 翻转闪烁状态(0或1)
for (i = 0; i < 8; i++)
{
if (Led_mode[i] == 1) // 遍历所有LED,更新闪烁模式的状态
{ // 闪烁模式
if (Blink_counter)
{
Led_Status |= (1 << i); // 灭
}
else
{
Led_Status &= ~(1 << i); // 亮
}
}
}
UpdateLEDs(); // 统一更新硬件
}
实例演示
SetLedMode(0,0,0);
SetLedMode(7,1,0);
//亮灭需自主控制,不会自动关闭
独立按键扫描
unsigned char Key()
{
unsigned char temp=0;
if(P33==0)
{
temp=4;
}
if(P32==0)
{
temp=5;
}
if(P31==0)
{
temp=6;
}
if(P30==0)
{
temp=7;
}
return temp;
}
矩阵按键扫描
unsigned char MartixScan()
{
unsigned char temp=0;
P44=0;P42=1;P35=1;P34=1;
if(P33==0) temp=4;
if(P32==0) temp=5;
if(P31==0) temp=6;
if(P30==0) temp=7;
P44=1;P42=0;P35=1;P34=1;
if(P33==0) temp=8;
if(P32==0) temp=9;
if(P31==0) temp=10;
if(P30==0) temp=11;
P44=1;P42=1;P35=0;P34=1;
if(P33==0) temp=12;
if(P32==0) temp=13;
if(P31==0) temp=14;
if(P30==0) temp=15;
P44=1;P42=1;P35=1;P34=0;
if(P33==0) temp=16;
if(P32==0) temp=17;
if(P31==0) temp=18;
if(P30==0) temp=19;
return temp;
}
按键下降沿与上升沿判断
void Key_Proc()
{
KeyN=MartixScan(); //按键下降沿与上升沿的判断
Key_Down=KeyN&(Key_Old^KeyN);
Key_Up=~KeyN&(Key_Old^KeyN);
Key_Old=KeyN;
switch(Key_Down)
{
case 4://此处填写作用
break;
case 5:...
}
switch(Key_Up)
{
同上
}
切记不能忘记break
数码管
void SMG(unsigned char num,plc)
{
SMGBuff[plc]=num; //对数码管缓存数组进行改变
}
void SMG_Set(unsigned char ad1,ad2,ad3,ad4,ad5,ad6,ad7,ad8)
{
if(SMG_Flag) //此为减速标志位
{
SMG(ad1,1);
SMG(ad2,2); //一次性对所有数码管缓存数组进行改变
SMG(ad3,3);
SMG(ad4,4);
SMG(ad5,5);
SMG(ad6,6);
SMG(ad7,7);
SMG(ad8,8);
SMG_Flag=0;
}
}
void SMG_Show() //放在定时器里扫描,显示的是八个数码管缓存数组
{
InitHC138(7); //消隐
P0 = 0xff;
InitHC138(6);
P0=0x01<<(sign-1);
InitHC138(0); //消隐
P0 = 0xff;
InitHC138(7);
P0=SMGNB[SMGBuff[sign]];
sign++;
if(sign>8)
{
sign=0;
}
}
蜂鸣器继电器
void Buzzer_Proc()
{
InitHC138(5);
P0=0x50;
InitHC138(0);
}
//比较简单,0x50是蜂鸣器与继电器,0x10是继电器,0x40是蜂鸣器
//0x00是全关
定时器(可直接在STC-ISP中生成)
这是定时器0(处理一般功能)
void Timer0_Working() interrupt 1 //定时器0的端口
{
static unsigned int SMG_Count=0; //static代表局部变量
static unsigned int Key_Pos=0; //注意int与char的限制大小
static unsigned char SMG_Delay=0;
TL0=0x18; //非自动重载,需要进入中断时重设定时器初值
TH0=0xFC;
if(++Key_Pos == 10)
{
Key_Pos=0; //按键扫描
Key_Flag=1; //当Key_Flag为1时执行按键相关
}
if(++SMG_Count==2)
{
SMG_Show(); //数码管扫描
SMG_Count=0;
}
if(++SMG_Delay==80) //数码管减速,防止刷新过快
{
SMG_Flag=1;
SMG_Delay=0;
}
如上图演示为部分定时器功能(不要忘记打开总中断与定时器中断)
DS1302(时钟模块)
赛点资源包中缺少的部分定义
sbit SCK=P1^7;
sbit SDA=P2^3;
sbit RST=P1^3;
实际使用
xdata unsigned char Write_Ds1302_ADD[7]={0x80,0x82,0x84,0x86,0x88,0x8a,0x8c}; // DS1302写地址
xdata unsigned char Read_Ds1302_ADD[7]={0x81,0x83,0x85,0x87,0x89,0x8b,0x8d}; // DS1302读地址
xdata unsigned char Time[7]={0x00,0x00,0x00,0x04,0x03,0x06,0x25}; // 时间数组,格式为秒、分、时、日、月、星期、年
// DS1302初始化函数
void Init_Ds1302() //需要放在主函数的while循环前,进行初始化
{
char i;
Write_Ds1302_Byte(0x8e,0x00); // 关闭写保护
for(i=0;i<7;i++)
{
Write_Ds1302_Byte(Write_Ds1302_ADD[i],Time[i]); // 写入初始时间
}
Write_Ds1302_Byte(0x8e,0x80); // 打开写保护
}
//DS1302读取时间,存入Time数组
void Read_Ds1302_Time()
{
char i;
for(i=0;i<7;i++)
{
Time[i]=Read_Ds1302_Byte(Read_Ds1302_ADD[i]); // 读取时间
}
}
DS18B20(温度读取模块)
赛点资源包中缺少的端口定义
sbit DQ=P1^4;
功能代码
/* DS18B20温度读取函数 */
int Read_DS18B20_temp(unsigned int Temp) //int型数据,不是char型数据!!!
{
unsigned char LSB, MSB;
init_ds18b20(); // 初始化DS18B20
Write_DS18B20(0xCC); // 跳过ROM指令
Write_DS18B20(0x44); // 启动温度转换
init_ds18b20(); // 重新初始化
Write_DS18B20(0xCC);
Write_DS18B20(0xBE); // 读取暂存器
LSB = Read_DS18B20(); // 读取温度低字节
MSB = Read_DS18B20(); // 读取温度高字节
init_ds18b20(); // 释放总线
Temp = (MSB << 8) | LSB; // 合成16位温度数据
return (Temp/16*10);
}
//需注意是int型数据,不要搞错了
主函数中如此写
Temp=Read_DS18B20_temp(Temp);
PCF8591(光敏电阻读取,与电压输入输出)
赛点资源包缺少的端口定义
sbit scl=P2^0;
sbit sda=P2^1;
功能代码
void PCF8591_DAout(unsigned char Data)
{
I2CStart();
I2CSendByte(0x90);
I2CWaitAck(); //电压输出
I2CSendByte(0x40);
I2CWaitAck();
I2CSendByte(Data);
I2CWaitAck();
I2CStop();
}
unsigned char PCF8591_ADin(unsigned char addr)
{
unsigned char ad;
I2CStart();
I2CSendByte(0x90);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck();
//电压或光敏电阻数据采集
I2CStart();
I2CSendByte(0x91);
I2CWaitAck();
ad=I2CReceiveByte();
I2CSendAck(1);
I2CStop();
return ad;
}
实际使用
Light=PCF8591_ADin(0x01); //光敏电阻电压采集
ad=PCF8591_ADin(0x03); //可控电阻电压采集
0-255对应0-5V
PCF8591_DAout(0);
PCF8591_DAout(51);
PCF8591_DAout(102); //输出电压值
//近似255/5的每单位电压输出值
//0x41通道可能是混合采集
//只AD时,可只用0x01,0x03。
//只DA时,可只用0x40。
//既要AD又要DA时,需用0X41,0X43。
AT24C02(存储模块)
赛点资源包中少的端口定义
sbit scl=P2^0;
sbit sda=P2^1;
功能代码(做到断电存储)
void AT24C02_Write(unsigned char addr,unsigned char Data)
{
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck(); //AT24C02写地址代码
I2CSendByte(addr);
I2CWaitAck();
I2CSendByte(Data);
I2CWaitAck();
I2CStop();
Delay(5);
}
unsigned char AT24C02_Read(unsigned char addr)
{
unsigned char Data;
I2CStart();
I2CSendByte(0xa0);
I2CWaitAck();
I2CSendByte(addr);
I2CWaitAck(); //AT24C02读地址代码
I2CStart();
I2CSendByte(0xa1);
I2CWaitAck();
Data=I2CReceiveByte();
I2CSendAck(1);//不应答
I2CStop();
Delay(5);
return Data;
}
超声波模块
超声波发送数据与接收数据定义
sbit Trig=P1^0;
sbit Echo=P1^1;
超声波专用定时器初始化
void Wave_timer_init(void) //12.000MHz
{
CMOD=0x00;
CL=0;
CH=0;
CF=0;
CR=0;
}
超声波初始化
void Wave_Init()
{
unsigned char i;
for(i=0;i<8;i++)
{
Trig=1; //超声波初始化 产生八个方波信号
Delay12us();
Trig=0;
Delay12us(); //延时一定时间,方波频率计算得到
}
}
超声波读取数据
unsigned char Wave_Read() //超声波读取数据
{
unsigned int Time;
Wave_timer_init(); //超声波所使用的定时器的重启
Wave_Init(); //发送信号
CR=1; //开始计时
while((Echo==1)&&(CF==0)); //接收到数据之后停止计时(且数据未溢出)
CR=0;
if(CF==0) //如果计时停止
{
Time=CH<<8|CL; //得到距离数据
return(Time*0.017); //音速与频率的计算得到距离
}
else
{
CF=0; //错误数据(定时器溢出)
return 0;
}
}
使用
Wave_Data=Wave_Read();
NE555(频率模块)
初始值
int Count_f,Dat_f;
数据较大,别忘了是int
频率定时器初始化(八位自动重载)
void Timer_Init(void)
{
TMOD=0x16;
ET0=1; //打开定时器0开关
ET1=1; //打开定时器1开关
EA=1; //打开EA总开关
TH0=0XFF; //设置初值,使P3^4每产生一个脉冲,触发一次定时器0中断
TL0=0XFF;
TH1=0XFC; //设置初值,定时器1每1ms产生一次中断
TL1=0X18;
TR0=1; //定时器0开始计时
TR1=1; //定时器1开始计时
}
void Timer0_Working() interrupt 1
{
Count_f++;
}
void Timer1_Working() interrupt 3
{
TL1=0x18;
TH1=0xFC;
if(++Count_t==1000)
{ //此为频率值的产生
Dat_f=Count_f; //时间不同,频率的大小也不同
Count_f=0;
Count_t=0;
}
//其他无关功能此处省略
}
此处使用定时器0产生频率,定时器1处理功能,相反应该不行
另一个定时器仍为非自动重载,所以别忘记设置初值
串口通讯模块(难点)
串口使用的数组与索引
unsigned char Uart_Data[10]={0};//串口接收数组
unsigned char Uart_Index;//串口接收索引
定时器2初始化
void UartInit(void) //9600bps@12.000MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x01; //串口1选择定时器2为波特率发生器
AUXR &= 0xFB; //定时器2时钟为Fosc/12,即12T
T2L = 0xE6; //设定定时初值
T2H = 0xFF; //设定定时初值
AUXR |= 0x10; //启动定时器2
EA=1; //总中断
ES=1; //串口中断
}
中断存入数组
void Uart_Server() interrupt 4
{ //没有中断则无法起到串口作业
if(RI==1) //因为进入中断会卡死
{
Uart_Data[Uart_Index]=SBUF;
Uart_Index++;
RI=0;
}
}
串口发送字符与字符串
void SendByte(unsigned char Dat)
{
SBUF=Dat;
while(TI==0); //发送完会变成1
TI=0; //手动清零
}
void SendString(unsigned char *str)
{
while(*str !='\0')
{ //发送字符串
SendByte(*str++);
}
}
使用例子
void Uart_Proc()
{
if(Uart_Flag==1) //这是逻辑判断,根据情况设置使用
{
SendString("Data:"); //此处为例子,发送字符串与发送数字数据的方法
SendByte(0x30+Light%10);
SendString("\r\n"); //换行符
Uart_Flag=0;
}
else if(Uart_Flag2==1) //另一个逻辑
{
SendString("Warn\r\n");
Uart_Flag2=0;
}
if(Uart_Index!=0) //如果索引不为零(开始接收字符)
{
if(Uart_Index==5)
{
if(Uart_Data[0]=='C' && Uart_Data[1]=='l' && Uart_Data[2]=='o' && Uart_Data[3]=='s' && Uart_Data[4]=='e') //判断存入缓存数组的字符
{
Uart_Time=0;
Warn_Flag=0; //起到作用
}
}
Uart_Index=0; //索引归零
}
}
此为蓝桥杯单片机所有考察模块,记录以便日后查看