【问题标题】:AVR programming, displaying wrong value on 7 seg. LEDAVR 编程,7 段显示错误值。引领
【发布时间】:2018-01-23 12:06:29
【问题描述】:

我正在将 LM35 与 Atmega8 连接。为了显示数字,我使用 7 段 LED 阳极显示器,我将其连接到 AVR 的两端(它在没有晶体管的情况下处理它,所以为什么不呢)。奇怪的事情发生了: 从 adc 分配后的 res 值为 237(23.7 度)。我想在我的显示器上打印第一个数字 (2)。 如果我在注释掉的时候留下最后一行,显示器首先正确显示数字 2,但在第一次延迟后显示 1 而不是 2。否则我得到正确的数字 2。为什么会发生这种情况?

#ifndef F_CPU
#define F_CPU 1000000UL
#endif // F_CPU

#include <avr/io.h>
#include <util/delay.h>

#define DELAY_IN_MS 500 /* 0.5 sec */

int numbers[] = {
    0b01000000,
    0b01110011,
    0b00100100,
    0b00100001,
    0b00010011,
    0b00001001,
    0b00001000,
    0b01100011,
    0b00000000,
    0b00000001,
    0b11111111 // off
};

uint8_t digits[3];

void initADC()
{
    ADMUX=(1<<REFS1)|(1<<REFS0);
    ADCSRA=(1<<ADEN)|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);
}

uint16_t ReadADC(uint8_t ch)
{
    //Select ADC Channel ch must be 0-7
    ch=ch&0b00000111;
    ADMUX|=ch;

    //Start Single conversion

    ADCSRA|=(1<<ADSC);

    //Wait for conversion to complete
    while(!(ADCSRA & (1<<ADIF)));

    //Clear ADIF by writing one to it
    ADCSRA|=(1<<ADIF);

    return(ADC);
}

int main()
{
    DDRD = 0xFF;
    PORTD = 0xFF;

    DDRB = 0b00000001;
    PORTB = 1;

    initADC();

    uint16_t adc_value;
    uint16_t res;
    while(1)
    {
        adc_value = 0;
        for (int i = 0; i < 250; i++)
        {
            adc_value += ReadADC(0);
        }

        adc_value=(adc_value/25)/4;
        res = adc_value;

        for(int j = 2; j >= 0; j--) {
            digits[j] = res%10;
            res /= 10;
        }

        uint8_t dig = digits[0];
        PORTD = numbers[dig];
        _delay_ms(DELAY_IN_MS);

        // if following is uncommented there blinks digit two correctly
        // if commented there is unblinking digit 1 
        PORTD = numbers[10]; // display off
    }

    return 0;
}

【问题讨论】:

  • hmmm 可能是错误的数组索引或 MSB 或 LSB 第一种情况?
  • 不确定是什么问题,但我会将数字数组更改为 uint8_t 而不是 int。你有任何可能调试你的代码吗?
  • @Maku 好的。可悲的是我无法调试它(或不知道如何)。我唯一可以“调试”的是LM35输出,我可以用电压表读取。我想知道,是否可能是显示器耗电过多导致 ADC 计算不正确?
  • 给定 ADC 的分辨率在这里很重要。除非它是 8 位,否则此代码将崩溃并烧毁,因为您无法将 250 次 ADC 读取的总和存储在 16 位变量中。微控制器的行业最低标准是 10 位 ADC。 12 位越来越普遍。你真的在使用 8 位 ADC 吗?
  • @Lundin 为什么要“崩溃和燃烧”?确实 250*16bit 不适合 16bit 变量,但它不会崩溃 - 如果我没记错的话它只会翻转。

标签: c embedded avr atmega


【解决方案1】:

问题是归纳法。

我的电路在非焊接区有很多电线。当显示器打开时,有很多感应会改变 ADC 输入/LM35 输出上的结果电压。 解决方案不止一种。

1) 软件:我将 ADC 转换移到了中断功能中。它关闭显示器,从 lm35 转换值并在正确的显示器上显示数字。它发生得如此之快,以至于眼睛无法察觉。 我现在更喜欢这个,因为它使我的电路更简单。

2) 硬件:将 L/C 或 R/C 滤波器添加到 adc 引脚应该可以解决问题。

1) 的完整代码

#ifndef F_CPU
#define F_CPU 1000000UL
#endif // F_CPU

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>

#define DELAY_IN_MS 5000 /* ms */
#define NUM_OF_MEASUREMENTS 100
#define NUM_DISPLAYS 3

int numbers[] = {
    0b10000001,
    0b10011111,
    0b10100100,
    0b10010100,
    0b10011010,
    0b11010000,
    0b11000000,
    0b10011101,
    0b10000000,
    0b10010000,
    0b11111111 // off
};

int display = 0;
uint8_t digits[NUM_DISPLAYS];
volatile uint16_t adc_values[NUM_OF_MEASUREMENTS];
int adc_read_cycle_index = 0;
uint32_t res;

void initADC()
{
    ADMUX=(1<<REFS1)|(1<<REFS0);
    ADCSRA=(1<<ADEN)|(1<<ADPS2);
}

uint16_t ReadADC(uint8_t ch)
{
    //Select ADC Channel ch must be 0-7
    ch=ch&0b00000111;
    ADMUX|=ch;

    //Start Single conversion
    ADCSRA|=(1<<ADSC);

    //Wait for conversion to complete
    while (ADCSRA & (1<<ADSC));

    return(ADC);
}

void readDegrees()
{
    adc_values[adc_read_cycle_index] = (ReadADC(0)*10)/4;

    if(adc_read_cycle_index + 1 == NUM_OF_MEASUREMENTS) {
        adc_read_cycle_index = 0;
    } else {
        adc_read_cycle_index++;
    }
}

void fetchTemperatureDigits() {
    res = 0;
    for(int i = 0; i < NUM_OF_MEASUREMENTS; i++) {
        res += adc_values[i];
    }
    res /= NUM_OF_MEASUREMENTS;

    for(int j = 2; j >= 0; j--) {
        digits[j] = res%10;
        res = res / 10;
    }
}

void initTimer0()
{
      // Prescaler = FCPU/64
      TCCR0|=(1<<CS01);//|(1<<CS00);

      //Enable Overflow Interrupt Enable
      TIMSK|=(1<<TOIE0);

      //Initialize Counter
      TCNT0=0;
}

ISR(TIMER0_OVF_vect)
{
    // turn off displays
    PORTD = numbers[10];

    // read ADC and convert to degrees
    readDegrees();

    // turn on proper anode
    PORTB &= 0b11111000;
    PORTB |= (1<<display);

    // show digit
    PORTD = numbers[digits[display]];
    // show decimal point for second display (21.5 - second display shows "1.")
    if(display == 1) {
        PORTD &= 0b01111111;
    }
    // next display for next interruption
    display++;
    if(display == NUM_DISPLAYS) {
        display = 0;
    }
}

int main()
{

    initADC();
    for(int i = 0; i < NUM_OF_MEASUREMENTS; i++) {
        readDegrees();
    }

    DDRD = 0xFF;
    PORTD = 0;

    DDRB |= 0b00000111;
    PORTB |= 1;

    initTimer0();

    sei();

    while(1) {
        fetchTemperatureDigits();
        _delay_ms(DELAY_IN_MS);
    }

    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-01-11
    • 1970-01-01
    • 1970-01-01
    • 2012-10-22
    • 1970-01-01
    • 1970-01-01
    • 2012-03-10
    • 2018-02-05
    相关资源
    最近更新 更多