PIC单片机——电压检测器

·核心原理

  采集最初采用一个半波采集100个数据的方法,这种方法理论上拥有最准确的精度和最稳定的效果,但是经过仿真跑表测试,单独循环AD函数将要花费442us,远超过要求的100us,删除掉AD计算电压值的这种花费时间的浮点数计算,也只能降到113us,然而这种计算是必不可少的。同时UART和LCD的实时显示都将占用不少的时间。最后全程序测试,发现在定时器设定最短的情况下,跑完一次半波采样只能进行1-3次不等的转换,这是远远无法满足要求的,更不用说实际硬件存在的不可避免的延时与误差。同时定时器中断将无法退出,间隔时长将短于定时器中断代码运行时长,意味着主循环内的任何代码都不可能运行。

  所以最后使用了单周期单采样的方法,从0度开始,每次采集相对于上一次对半波产生180*0.01电角度的移相,经过100个周期就会均匀采集满半波的100个点,理论上的效果是一致的,数据更新周期将为原来的1/100,定时器需要的间隔将变为20.2ms,这个时长对于代码周期是十分充裕的。

·硬件

资源

单片机使用的资源:
  AD模块及模拟口1个
  UART串口
  定时器TMR1
  RD引脚(普通电平口)

LCD1602

  基本原理,电平控制162个显示位(每个位57个液晶像素点)不同的字符。模块内自带HD44780U显示芯片(主控,80个地址)用于控制像素显示,以及HD44100H芯片(可扩展)用于辅助驱动。不同位地址分配不同的DRAM,地址和位数据通过2个口和8个口(ASCII码对应显示内容)输入。这两个通用性高的芯片使得编程思路变得相对方便且适用于各种单片机。

仿真电路

·软件

AD部分

unsigned int AD;
float OUT;      //AD转出来的电压值

void AD_CSH(void){      //初始化
    T1CON=0;
    TRISA|=0B01110000;		//模拟接收口、参考电压口
    TMR1H=T1_20MS>>8;
    TMR1L=T1_20MS;
    TMR1ON=1;   
}
unsigned int AD_SUB(unsigned int k){
        //ad操作函数,k是采样通道,本次取1
    ADCON1=0B10000000;   //参考电压,0、5V
    ADCON0=0B01000001;   //选择分频,使能AD
    ADCON0=ADCON0|(k<<2); 	//将通道赋值在中间四位
    __delay_us(20);      //延时等电容充电
    GO=1;           	//开始AD转换
    while(GO==1);       //等待转换完成
    return ((ADRESH<<8)+ADRESL);      
            //拼接结果并输出
}

串口部分

volatile unsigned char X;        //发送数据
volatile unsigned char Y[20];       //存放收到数据
volatile unsigned char idata;
//接受测试变量
void UART_CSH(void){     //初始化
    OPTION_REG=0B11010110;                
    INTCON=0B11000000; //中断使能
    RCIE=1;        //接收中断使能
    SPBRG=25;  		//8位下配置波特率因子1
    RCSTA=0B10010000;
    TXSTA=0B00100100;    
}
void UART_Send(char *str){          
//发送字符串,输入为所发字符串
    for(int i=0;*str!='\0';i++){
        TXREG=*str;
        while(TRMT==0);
        str++;
    }
}

LCD上层函数

#define LCD_E RD6     //读写使能
#define LCD_RW RD5//读1/写0控制线
#define LCD_RS RD4   //寄存器选择
#define COM 0       //0表示写命令
#define DATA 1      //1表示写数据
extern char DD[16];      
     //定义数组用来存放整行显示数据
void DISP_C(char line){         //显示某整行
    char i;
    LCD_WRITE(line,COM);//写信号
    for(i=0;i<16;i++)  //遍历行写入
        LCD_WRITE(DD[i],DATA);
}
void LCD_WRITE(char R1,char FLAG){      //写八位
    char R2;
    LCD_BUSY();
    R2=R1&0XF0;
    R2=R2>>4;
    LCD_WRITE_4(R2,FLAG);
    R2=R1&0X0F;
    LCD_WRITE_4(R2,FLAG);
    __delay_us(10);
}

装置主逻辑

变量和初始化

#define TMR1_20MS 45663
   //定时器时长,保证采样个数的关键
#define LINE1 0b10000000
#define LINE2 0b11000000
int last_OUT;       //上一个电压值
int last_AD;       //上一个电压值
long int sum;        //半波电压和
int data_index;     //数组索引
int data_ave;       //半波平均值
int data_val;       //半波有效值
int dataSave;       //是否存储数据(正半周),0不存  
char data[5];       //发送串口的字符串暂存

void main(void) {
    idata=0;
    UART_CSH();   //各个模块初始化
    AD_CSH();
    LCD_CSH();
    __delay_ms(1000);
    TMR1IE=1;		//定时器使能
    TMR1ON=1; 
}

定时器中断采样

void __interrupt() INT_SER(void){
    if(TMR1IF==1){
        TMR1IF=0;
        TMR1H=TMR1_20MS>>8;
        TMR1L=TMR1_20MS;
        last_AD=AD;
        last_OUT=OUT;      //存放上一个OUT
        AD=AD_SUB(1);	//AD采样操作
        OUT=AD*5000.0/1023.0;  //0到+5参考电压下的电压(mV)
        if( last_AD==0 && AD>0){	//负到正过零点,进入正半周
            dataSave=1;	//开始收集数据
        }
        else if (last_AD>0 && AD==0){ //正到负过零点,不存数据
            dataSave=0;
            ClearData(); //正半周结束进行数据处理
        }
        if(dataSave==1 && AD>0){  //只有正半周存储数据
            sum+=OUT; 	//求和的方式存数据不占据内存
            if(data_val<OUT) 	//找到最大值,用于算有效值
                data_val=OUT;
            data_index++;       //记录采样点个数
        }
    }
}

数据处理

void ClearData(void){  		//正半周数据每100次处理一次,获得平均值和有效值,并且发送串口
    data_ave=sum/data_index;             	//求平均值
    data_val=data_val/sqrt(2);      		//求有效值
    UART_Send(BCD(data_index));    			//BCD转换 采样点数 生成字符串并发送
    UART_Send(BCD(data_ave));       		//BCD转换 平均值 生成字符串并发送
    LCD_SHOW(0,BCD(data_ave));            	//屏幕显示第一行
    UART_Send(BCD(data_val));               //BCD转换 有效值 生成字符串并发送
    LCD_SHOW(1,BCD(data_val));            	//屏幕显示第二行
    TXREG=13;           		//ENTER
    while(TRMT==0);
    data_index=0;    		//发送一次三数归零
    sum=0;
    data_val=0;
}

LCD显示

void LCD_SHOW(int line,char* show){		//LCD显示,输入为行号(0或1)、内容的字符串
    if(line==0){    //格式:  AVE=x.xxxV
        BCD(data_ave);
        DD[0]='A';
        DD[1]='V';
        DD[2]='E';
        DD[3]='=';
        DD[4]=*show;
        show++;
        DD[5]='.';
        DD[6]=*show;
        show++;
        DD[7]=*show;
        show++;
        DD[8]=*show;
        DD[9]='V';
        DISP_C(LINE1);
    }
    else if (line==1){      //格式:  VAL=x.xxxV
        BCD(data_val);
        DD[0]='V';
        DD[1]='A';
        DD[2]='L';
        DD[3]='=';
        DD[4]=*show;
        show++;
        DD[5]='.';
        DD[6]=*show;
        show++;
        DD[7]=*show;
        show++;
        DD[8]=*show;
        DD[9]='V';
        DISP_C(LINE2);
    }
}

·结果

仿真

实物

创新

  在本套装置使用过程中,发现PC端可以利用串口实现数据接收,便可以利用该优势,对数据进行实时可视化显示和存储,在实际过程中对电压的检测势必也需要使用者对数据有直观的接收,以便对情况高效作出判断,存储数据也利于对历史情况进行分析。于是基于python,在电脑端通过Pyqt5实现能够数据可视化的串口接收程序,利用强大的组件我设计了一个可方便使用的界面,按钮组件实现数据保存,折线图动态更新,使用自己编写的图表模块实现折线图显示。采样点、平均值、有效值也能清晰地显示在界面当中。

  • Copyrights © 2023-2025 LegendLeo Chen
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信