You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于Atmega16的LM35温度传感器Proteus仿真:负温度显示异常求助

基于Atmega16的LM35温度传感器Proteus仿真:负温度显示异常求助

看起来你已经把正温度的显示逻辑跑通了,唯独卡在负温度显示上——明明手动模拟负温度时能正常显示,但真实采样时只会出0.0℃对吧?我帮你梳理下核心问题,再给你调整后的完整代码方案。


核心问题分析

1. 偏置电压的软件处理缺失

你提到硬件上加了2.5V偏置电路来测负温度,但代码里完全没做对应的处理!LM35本身是单极性输出(0℃对应0V,正温度输出正电压),加2.5V偏置后,0℃对应2.5V,-25℃对应0V,25℃对应5V。但你原来的代码直接把ADC采样的电压除以0.01转温度,相当于把2.5V当成25℃,完全没减去偏置,所以真实采样负温度时,得到的是25℃以下的正数,自然显示不出负号。

2. LCD显示逻辑可简化

原来的代码手动判断正负号再拼接字符串,其实sprintf可以自动处理负号,没必要额外取反处理,这点可以简化。


调整后的完整代码

我标注了所有修改的地方(// MODIFIED 注释):

#include <mega16.h>
#include <delay.h>
#include <alcd.h>
#include <stdio.h>

#define ADC_VREF_TYPE ((0<<REFS1)|(1<<REFS0)|(0<<ADLAR))

const unsigned char seg_patterns[11] = {
    0xC0, // 0
    0xF9, // 1
    0xA4, // 2
    0xB0, // 3
    0x99, // 4
    0x92, // 5
    0x82, // 6
    0xF8, // 7
    0x80, // 8
    0x90, // 9
    0xBF // Minus sign '-'
};

volatile unsigned char display_digit = 0;
volatile unsigned char digits[4] = {0xFF, 0xFF, 0xFF, 0xFF};
volatile unsigned char decimal_points[4] = {0, 0, 1, 0};

interrupt [TIM0_OVF] void timer0_ovf_isr(void) {
    TCNT0 = 240;
    PORTB &= ~0xF0;
    if (digits[display_digit] != 0xFF) {
        PORTC = seg_patterns[digits[display_digit]];
        if (decimal_points[display_digit])
            PORTC &= ~0x80;
    } else {
        PORTC = 0xFF;
    }
    PORTB |= (1 << (display_digit + 4));
    display_digit = (display_digit + 1) % 4;
}

unsigned int read_adc(unsigned char ch) {
    ADMUX = ch | ADC_VREF_TYPE;
    delay_us(10);
    ADCSRA |= (1 << ADSC);
    while (ADCSRA & (1 << ADSC));
    return ADCW;
}

void update_display(float temp) {
    int temp_int;
    unsigned char is_negative = 0;

    // Check if temperature is negative
    if (temp < 0) {
        is_negative = 1;
        temp_int = (int)((-temp) * 10); // Make positive for digit extraction
    } else {
        temp_int = (int)(temp * 10);
    }

    // For negative numbers: -26.0 should show as "-26.0"
    // digits[0] = minus, digits[1] = 2, digits[2] = 6, digits[3] = 0
    if (is_negative) {
        digits[0] = 10; // Minus sign
        if (temp_int >= 100) { // Two digits before decimal (e.g., -26.0)
            digits[1] = (temp_int / 100) % 10; // Tens digit
            digits[2] = (temp_int / 10) % 10; // Units digit
            digits[3] = temp_int % 10; // Decimal digit
        } else { // One digit before decimal (e.g., -5.2)
            digits[1] = 0xFF; // Blank
            digits[2] = (temp_int / 10) % 10; // Units digit
            digits[3] = temp_int % 10; // Decimal digit
        }
    } else {
        // Positive numbers
        if (temp_int >= 1000) { // Three digits (e.g., 125.4)
            digits[0] = (temp_int / 1000) % 10;
            digits[1] = (temp_int / 100) % 10;
            digits[2] = (temp_int / 10) % 10;
            digits[3] = temp_int % 10;
        } else if (temp_int >= 100) { // Two digits (e.g., 25.5)
            digits[0] = 0xFF; // Blank
            digits[1] = (temp_int / 100) % 10;
            digits[2] = (temp_int / 10) % 10;
            digits[3] = temp_int % 10;
        } else { // One digit (e.g., 5.2)
            digits[0] = 0xFF; // Blank
            digits[1] = 0xFF; // Blank
            digits[2] = (temp_int / 10) % 10; // Units digit
            digits[3] = temp_int % 10; // Decimal digit
        }
    }

    // Set decimal point position (after digit 2)
    decimal_points[0] = 0;
    decimal_points[1] = 0;
    decimal_points[2] = 1; // Decimal point here
    decimal_points[3] = 0;
}

void main(void) {
    DDRB = 0xF1;
    DDRC = 0xFF;
    PORTC = 0xFF;

    TCCR0 = (1 << CS01);
    TIMSK |= (1 << TOIE0);
    TCNT0 = 240;

    ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0);

    lcd_init(16);
    #asm("sei")
    lcd_clear();

    // Test counter for simulating different temperatures
    int test_counter = 0;

    while (1) {
        unsigned int adc_value = read_adc(0);
        float voltage = (adc_value * 5.0) / 1024.0;
        // MODIFIED: 减去2.5V偏置电压,转换为真实温度
        float temp = (voltage - 2.5) / 0.01; // LM35 output 10mV per °C (with 2.5V bias)

        // TEMPORARY: Simulate negative temperatures for testing
        // Remove this section once you confirm negative display works
        if (test_counter < 10) {
            temp = -26.5; // Test negative temperature
        } else if (test_counter < 20) {
            temp = -5.2; // Test single digit negative
        } else if (test_counter < 30) {
            temp = 25.5; // Test positive temperature
        } else {
            test_counter = 0; // Reset counter
        }
        test_counter++;
        // END OF TEST SECTION

        char lcd_text[16];
        // MODIFIED: 直接用sprintf自动处理负号,简化代码
        sprintf(lcd_text, "Temp: %.1fC", temp);
        lcd_clear();
        lcd_puts(lcd_text);

        update_display(temp);
        delay_ms(500);
    }
}

测试与验证步骤

  1. 先保留测试代码段,手动设置负温度,确认7段码和LCD都能正确显示负号与数值;
  2. 移除测试代码,在Proteus中搭建好LM35+2.5V偏置的电路,调整仿真温度到负数区间,检查ADC采样后的温度转换是否正确;
  3. 确认硬件位选(PORTB高四位)与段选(PORTC)的连线和代码逻辑完全对应,避免接线错误导致显示异常。

内容来源于stack exchange

火山引擎 最新活动