基于51单片机的灯盘检测(PCF8591+CD4051 )
基于51单片机的灯盘检测系统使用,AT89C51作为系统主控,采用PCF8591+CD4051进行循环检测,本次的led灯一共有90个并在每个led上做了一个断路模拟开关,部分led上还设置了击穿模拟开关,led点亮使用PNP三极管进行导通,低电平的导通方式。
一、参考代码
1、扫描灯盘
本次设计使用PCF8591和CD4051配合进行90个led的扫描,首先使用传入的变量进行通道选择DA、DB、DC分别使用的引脚是P22、P23、P24进行CD4051通道的选择,ADC读取采取统一通道,即AN0通道。接着再使用成对的CD4051进行扫描,以此类推最后进行了90个led的扫描。
void ScanLed(unsigned char n)
{
//选择灯盘 1110 0011
P2 &= 0xE3;
//选择灯盘
P2 |= (n << 2);
//清除低四位
P1 &= 0xF0;
j=0;
while(j<16){
if(led_state == n+1){
P1 &= 0xf0;
//选择通道
P1 |= j;
//获取ADC的值
Invalue = read_AD_data(0x40);
if(Invalue==0 || Invalue>=250){
if(n==0){err1_led[num1] = n*16+j-1;num1++;}
if(n==1){err2_led[num2] = n*16+j-1;num2++;}
if(n==2){err3_led[num3] = n*16+j-1;num3++;}
if(n==3){err4_led[num4] = n*16+j-1;num4++;}
if(n==4){err5_led[num5] = n*16+j-1;num5++;}
}
}
j++;
}
}
2、主函数
下面是本次设计的主函数,实现了灯盘控制,检测灯一些逻辑,利用串口中断进行灯盘异常数据的获取并控制led灯。
#include <REGX52.H>
#include "LCD1602.H"
#include "delay.h"
#include "PCF8591.h"
#include "UART.h"
sbit led1 = P3^3;
sbit led2 = P3^4;
sbit led3 = P3^5;
sbit led4 = P3^6;
sbit led5 = P3^7;
//轮询芯片的IO口
sbit DA = P2^2;
sbit DB = P2^3;
sbit DC = P2^4;
sbit DA0 = P1^0;
sbit DB0 = P1^1;
sbit DC0 = P1^2;
sbit DD0 = P1^3;
typedef unsigned char u8;
u8 Invalue=100;
u8 i=0,j=0;
u8 num1=0;
u8 num2=0;
u8 num3=0;
u8 num4=0;
u8 num5=0;
u8 err1_led[10] = {0};
u8 err2_led[10] = {0};
u8 err3_led[10] = {0};
u8 err4_led[10] = {0};
u8 err5_led[10] = {0};
u8 pos=0;
u8 close=0;
u8 cmd=0;
u8 led_state=0;
void ScanLed(unsigned char n)
{
//选择灯盘 1110 0011
P2 &= 0xE3;
//选择灯盘
P2 |= (n << 2);
//清除低四位
P1 &= 0xF0;
j=0;
while(j<16){
if(led_state == n+1){
P1 &= 0xf0;
//选择通道
P1 |= j;
//获取ADC的值
Invalue = read_AD_data(0x40);
if(Invalue==0 || Invalue>=250){
if(n==0){err1_led[num1] = n*16+j-1;num1++;}
if(n==1){err2_led[num2] = n*16+j-1;num2++;}
if(n==2){err3_led[num3] = n*16+j-1;num3++;}
if(n==3){err4_led[num4] = n*16+j-1;num4++;}
if(n==4){err5_led[num5] = n*16+j-1;num5++;}
}
}
j++;
}
}
void main()
{
LCD_Init();
UART_Init();
//led灯开启
led1=1;
led2=1;
led3=1;
led4=1;
led5=1;
while(1)
{
Invalue = read_AD_data(0x40);
//清空坏灯的数组
for(i=0; i<5; i++){
err1_led[i] = 0;
err2_led[i] = 0;
err3_led[i] = 0;
err4_led[i] = 0;
err5_led[i] = 0;
}
num1 = 0;
if(led_state == 1){
ScanLed(0);
}
num2 = 0;
if(led_state == 2){
ScanLed(1);
}
num3 = 0;
if(led_state == 3){
ScanLed(2);
}
num4 = 0;
if(led_state == 4){
ScanLed(3);
}
num5 = 0;
if(led_state == 5){
ScanLed(4);
}
//红灯故障超过了4个,红灯灭掉
if(num1 >= 5){
led_state=6;
led1=1;
}
else{
}
//其它四个灯盘,灭了超过4个,点亮红灯
if(num2 >= 5 || num3 >= 5 || num4 >= 5 || num5 >= 5 ){
led_state=1;
led1=0;
led2=1;
led3=1;
led4=1;
led5=1;
}
LCD_ShowNum(2, 1, num1+num2+num3+num4+num5, 2);
if(num1 != 0 || num2 != 0 || num3 != 0 || num4 != 0 || num5 != 0)
{
//发送数据包过去
UART_SendByte(0x5A);
//显示坏灯的数据
if(num1 != 0){
//显示灯坏的数量
for(i=0; i<num1; i++){
UART_SendByte(err1_led[i]);
}
}
if(num2 != 0){
//显示灯坏的数量
for(i=0; i<num2; i++){
UART_SendByte(err2_led[i]);
}
}
if(num3 != 0){
//显示灯坏的数量
for(i=0; i<num3; i++){
UART_SendByte(err3_led[i]);
}
}
if(num4 != 0){
//显示灯坏的数量
for(i=0; i<num4; i++){
UART_SendByte(err4_led[i]);
}
}
if(num5 != 0){
//显示灯坏的数量
for(i=0; i<num5; i++){
UART_SendByte(err5_led[i]);
}
}
UART_SendByte(0xA5);
}
}
}
void UATR_Routine() interrupt 4
{
if(RI==1)
{
cmd = SBUF;
if(cmd=='a'){led_state=1;}
if(cmd=='b'){led_state=2;}
if(cmd=='c'){led_state=3;}
if(cmd=='d'){led_state=4;}
if(cmd=='e'){led_state=5;}
if(cmd=='f'){led_state=6;}
if(led_state==1){
led1=0;
led2=1;
led3=1;
led4=1;
led5=1;
}
if(led_state==2){
led1=1;
led2=0;
led3=1;
led4=1;
led5=1;
}
if(led_state==3){
led1=1;
led2=1;
led3=0;
led4=1;
led5=1;
}
if(led_state==4){
led1=1;
led2=1;
led3=1;
led4=0;
led5=1;
}
if(led_state==5){
led1=1;
led2=1;
led3=1;
led4=1;
led5=0;
}
if(led_state==6){
led1=1;
led2=1;
led3=1;
led4=1;
led5=1;
}
RI=0;
}
}
二、功能演示
1、串口控制亮灯
串口分别发送a、b、c、d、e、f,分别是点亮这5个led灯盘和关闭所有灯盘,如图,发送指令a可以点亮第一个灯盘。如下,当我发送a的时候灯盘1灯被点亮。
2、故障检测
如下图,利用串口助手发送c指令,第三个灯盘被点亮,同时串口循环打印检测到的故障灯,数据格式是0x5A+数据+0xA5这样的格式,如下图数据大致是 22、20、2d(0为开始数),表示第三个灯盘的第3个灯、第三个灯盘的第10个灯、第三个灯盘的第13个灯,同时lcd显示故障灯数量,这里是3个。
三、项目总结
本次项目使用51单片机加PCF8591加CD4051实现了串口无线灯盘控制及检测,同时串口可以定位到具体得到故障灯盘,LCD显示灯盘故障数量。