You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PIC16F877A嵌入式系统LCD电压显示及延时卡顿问题求助

问题:PIC16F877A LCD显示中延时阻塞ADC采样的解决方法

我用mikroC编译器编写PIC16F877A的LCD电压数据显示代码,需要5秒延时来校准输入电压,但调用delay_ms()会导致系统卡顿,调节电位器时数值在延时期间无法更新——因为延时阻塞了ADC采样。附上代码:

// Lcd pinout settings
sbit LED1 at RB4_bit;
sbit LCD_RS at RD2_bit;
sbit LCD_EN at RD3_bit;
sbit LCD_D4 at RD4_bit;
sbit LCD_D5 at RD5_bit;
sbit LCD_D6 at RD6_bit;
sbit LCD_D7 at RD7_bit;
//sbit SW at RB1_bit;
// Pin direction
sbit LCD_RS_Direction at TRISD2_bit;
sbit LCD_EN_Direction at TRISD3_bit;
sbit LCD_D4_Direction at TRISD4_bit;
sbit LCD_D5_Direction at TRISD5_bit;
sbit LCD_D6_Direction at TRISD6_bit;
sbit LCD_D7_Direction at TRISD7_bit;


unsigned short count,pls;
unsigned char ch,bh;
long tlong,blong;

void adc1_config();
void adc2_config();
int adc1_prcs();
int adc2_prcs();

void main()
{
adc1_config();
adc2_config();
ADC_Init();
Lcd_Init();
Lcd_Cmd(_LCD_CLEAR);
Lcd_Cmd(_LCD_CURSOR_OFF);
adc1_prcs();
adc2_prcs();
Lcd_Out(1,1,"VAC-IN : ");
Lcd_Out(2,1,"VAC-OUT: ");
delay_ms(5000);
// here i want delay to adjust input voltage and calibrate.
// delay does not work here bcoz adc value stuck due to delay function.
// suugest proven technique to avoid delay.
TRISB.F4 = 0;
while(1)
{
Lcd_Out(1,1,"info page");
Lcd_Out(2,4,"123456789");
delay_ms(5000);
//same scenario occur here
Lcd_Cmd(_LCD_CLEAR);
adc1_prcs();
adc2_prcs();
Lcd_Out(1,1,"VAC-IN : ");
Lcd_Out(2,1,"VAC-OUT: ");
delay_ms(5000);
Lcd_Cmd(_LCD_CLEAR);
 // and here also

 }
}

解决方案

方法1:软件计时轮询法(无需定时器)

核心逻辑是用全局变量累计计时,主循环中不断检查是否达到延时时长,同时穿插执行ADC采样,保证数值实时更新。

修改后的主函数示例:

unsigned long timer_count = 0;
unsigned char display_state = 0; // 0:校准界面, 1:信息页面, 2:电压显示页面

void main()
{
    adc1_config();
    adc2_config();
    ADC_Init();
    Lcd_Init();
    Lcd_Cmd(_LCD_CLEAR);
    Lcd_Cmd(_LCD_CURSOR_OFF);
    TRISB.F4 = 0;

    // 初始校准界面
    Lcd_Out(1,1,"VAC-IN : ");
    Lcd_Out(2,1,"VAC-OUT: ");
    timer_count = 0;
    display_state = 0;

    while(1)
    {
        // 持续执行ADC采样,实时更新数值
        adc1_prcs();
        adc2_prcs();
        // 此处可添加实时刷新LCD电压值的代码

        // 1ms计时累加
        delay_ms(1);
        timer_count++;

        switch(display_state)
        {
            case 0: // 校准界面等待5秒
                if(timer_count >= 5000)
                {
                    timer_count = 0;
                    display_state = 1;
                    Lcd_Cmd(_LCD_CLEAR);
                    Lcd_Out(1,1,"info page");
                    Lcd_Out(2,4,"123456789");
                }
                break;

            case 1: // 信息页面等待5秒
                if(timer_count >= 5000)
                {
                    timer_count = 0;
                    display_state = 2;
                    Lcd_Cmd(_LCD_CLEAR);
                    Lcd_Out(1,1,"VAC-IN : ");
                    Lcd_Out(2,1,"VAC-OUT: ");
                }
                break;

            case 2: // 电压显示页面等待5秒
                if(timer_count >= 5000)
                {
                    timer_count = 0;
                    display_state = 1;
                    Lcd_Cmd(_LCD_CLEAR);
                    Lcd_Out(1,1,"info page");
                    Lcd_Out(2,4,"123456789");
                }
                break;
        }
    }
}

方法2:定时器中断法(精准高效)

利用PIC16F877A的Timer0产生1ms中断,在中断中累计计时,主循环仅检查计时变量,同时持续执行ADC采样。

配置Timer0和中断的示例代码:

unsigned long timer_ticks = 0;
unsigned char display_state = 0;

// 中断服务函数
void interrupt()
{
    if(TMR0IF_bit) // Timer0溢出中断触发
    {
        TMR0 = 0x63; // 预加载值(8MHz晶振下对应1ms中断,需根据实际晶振调整)
        TMR0IF_bit = 0; // 清除中断标志
        timer_ticks++;
    }
}

void main()
{
    // 配置Timer0
    OPTION_REG = 0b00000111; // 预分频器1:256,内部时钟源
    TMR0 = 0x63;
    TMR0IE_bit = 1; // 使能Timer0中断
    GIE_bit = 1; // 全局中断使能

    adc1_config();
    adc2_config();
    ADC_Init();
    Lcd_Init();
    Lcd_Cmd(_LCD_CLEAR);
    Lcd_Cmd(_LCD_CURSOR_OFF);
    TRISB.F4 = 0;

    // 初始校准界面
    Lcd_Out(1,1,"VAC-IN : ");
    Lcd_Out(2,1,"VAC-OUT: ");
    timer_ticks = 0;
    display_state = 0;

    while(1)
    {
        // 持续执行ADC采样,实时更新数值
        adc1_prcs();
        adc2_prcs();
        // 此处可添加实时刷新LCD电压值的代码

        switch(display_state)
        {
            case 0:
                if(timer_ticks >= 5000)
                {
                    timer_ticks = 0;
                    display_state = 1;
                    Lcd_Cmd(_LCD_CLEAR);
                    Lcd_Out(1,1,"info page");
                    Lcd_Out(2,4,"123456789");
                }
                break;

            case 1:
                if(timer_ticks >= 5000)
                {
                    timer_ticks = 0;
                    display_state = 2;
                    Lcd_Cmd(_LCD_CLEAR);
                    Lcd_Out(1,1,"VAC-IN : ");
                    Lcd_Out(2,1,"VAC-OUT: ");
                }
                break;

            case 2:
                if(timer_ticks >= 5000)
                {
                    timer_ticks = 0;
                    display_state = 1;
                    Lcd_Cmd(_LCD_CLEAR);
                    Lcd_Out(1,1,"info page");
                    Lcd_Out(2,4,"123456789");
                }
                break;
        }
    }
}

关键说明

  • 两种方法均替代了阻塞式delay_ms(),主循环始终运行,ADC采样可持续进行,调节电位器时数值会实时更新
  • 方法1实现简单,无需配置中断,但delay_ms(1)会占用少量CPU资源;方法2计时精准,主循环可专注处理其他任务

内容的提问来源于stack exchange,提问作者Afzal Ansari

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 21:40:27