PIC16F877 定时采集电压 LCD1602显示 Proteus仿真图
功能:定时器间隔50us采集一次AD电压,一共采集30次,并通过LCD1602显示。
软件:MPLAB X IDE v5.35,ISIS 7 Professional
注意事项:系统时钟配置为外部时钟8Mhz,由于间隔50us采集一次,所以AD采集一次时间应该小于50us。可通过调整定时器的数值,间隔不同的时间采集AD。
代码如下:
#include "pic.h"
#define RS RC1 //command or data
#define RW RC2 //read write io
#define EN RC3 //enable
__CONFIG(FOSC_HS & WDTE_OFF & PWRTE_ON & CP_OFF & BOREN_ON & LVP_ON & CPD_OFF);
void LCD1602_Init(void);
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str); //display
void Time_Init(void);
void ADC_Init(char k);
int AD_CollectVolt(void);
char AD_Complete_Flag = 0;
unsigned int arr[30];
unsigned int ad_volt = 0;
unsigned long sum = 0;
unsigned char num = 0,j = 0;
unsigned int Data = 0;
unsigned char volt_num[5];
void main(void) //Fosc = 8Mhz
{
PEIE = 1;
GIE = 1;
TRISC = 0X00; //OUTPUT
TRISD = 0X00; //OUTPUT
Time_Init(); //50us interrupt
ADC_Init(0); //Channel 0 RA0
LCD1602_Init();
while(1)
{
AD_CollectVolt();
volt_num[0] = ad_volt/1000 + 48;
volt_num[1] = ad_volt/100%10 + 48;
volt_num[2] = ad_volt/10%10 + 48;
volt_num[3] = ad_volt%10 + 48;
volt_num[4] = '\0';
LcdShowStr(0, 0, &volt_num);
LcdShowStr(5, 0, "MV");
}
}
void interrupt ISR(void)
{
static unsigned char test = 0;
if((TMR1IE == 1) && (TMR1IF == 1))
{
TMR1IF = 0;
TMR1 = TMR1 + 0xffcd;
if(GO != 1)
{
Data = ADRESH;
Data = Data << 8;
Data = Data | ADRESL;
arr[num]= Data;
num ++;
}
GO = 1;
if(test == 0) //test 50us time GPIO
{
RC4 = 0;
test = 1;
}
else
{
RC4 = 1;
test = 0;
}
if(num >= 30)
{
TMR1ON = 0;
TMR1IE = 0;
for(j = 0;j < 30;j ++)
{
sum = sum + arr[j];
}
sum = sum/30;
ad_volt = sum*5000/1024;
sum = 0;
num = 0;
AD_Complete_Flag = 1;
}
}
}
void LCD_WaitReady(void)
{
unsigned char sta;
PORTD = 0xff;
RS = 0;
RW = 1;
do
{
EN = 1;
sta = PORTD;
EN = 0;
}
while(sta&0x80);
}
void Write_Command(unsigned char Command)
{
LCD_WaitReady();
PORTD = Command;
RW = 0;
RS = 0;
EN = 1;
EN = 0;
}
void Write_Data(unsigned char Data)
{
LCD_WaitReady();
PORTD = Data;
RW = 0;
RS = 1;
EN = 1;
EN = 0;
}
void LcdSetCursor(unsigned char x, unsigned char y)
{
unsigned char addr;
if (y == 0)
{
addr = 0x00 + x;
}
else
{
addr = 0x40 + x;
}
Write_Command(addr | 0x80);
}
void LcdShowStr(unsigned char x, unsigned char y, unsigned char *str)
{
LcdSetCursor(x, y);
while (*str != '\0')
{
Write_Data(*str++);
}
}
void LCD1602_Init(void)
{
Write_Command(0x38);
Write_Command(0x0c);
Write_Command(0x06);
Write_Command(0x01);
}
void Time_Init(void)
{
TMR1CS = 0; //FOSC/4
T1CKPS1 = 0;
T1CKPS0 = 1; //1:2
TMR1 = 0xffcd; //50us
TMR1IF = 0;
TMR1IE = 1;
}
void ADC_Init(char k)
{
TRISA = 0XFF; //input
ADCON1 = 0X80;
ADCON0 = 0b00000001; //FOSC/2
ADCON0 |= (k << 3);
}
int AD_CollectVolt(void)
{
TMR1ON = 1;
TMR1IE = 1;
GO = 1;
do
{
}
while(AD_Complete_Flag == 0); //delay collect volt complete
AD_Complete_Flag = 0;
}
硬件图:
在RA1中连接外部3V电源,LCD显示2998mv;
注意:如何验证是否真的50us采集一次,在定时器中断函数里通过IO口电平翻转,然后用示波器来观察;
如上图所示,50us翻转一次,说明是50us采集一次AD值。