使用AVR atmega8在C中出现意外的浮动行为

我一直试图弄清楚为什么我不能通过将unsigned int乘以浮点值来获得合理的值.

像65535 * 0.1这样的东西按预期工作,但是浮点数与来自内存的uint相乘会产生疯狂的值.我有一个函数读取ADC并返回一个uin16_t.有了这个值,我将它打印到一个4位数的LED显示屏上,工作正常.
将相同的值与1.0相乘会返回完全不同的东西(对于我的显示来说太大了,所以我真的不知道它是什么).

我的代码在下面,但争用的区域在main()的底部.任何帮助都会很棒.谢谢

main.c中:

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdint.h>
#define BAUD 9600
#include <util/setbaud.h>
#define DISP_BRIGHT_CMD     'z'
#define DISP_RESET          'v'

#define ADC_AVG            3 

volatile uint8_t  hi,lo;
volatile uint16_t result; 

ISR(ADC_vect)
{ 
    lo = ADCL; 
    hi = ADCH; 
    MCUCR &= ~_BV(SE); //Clear enable sleep 
} 


void initSerial(void)
{
    // set baud rate
    UBRR0H = UBRRH_VALUE;
    UBRR0L = UBRRL_VALUE;
    // set frame format
    UCSR0C |= (0x3 << UCSZ00); // 8n1
    // set enable tx/rx
    UCSR0B = _BV(RXEN0) | _BV(TXEN0);
}

void initADC(void)
{
    // AVCC and ADC0
    ADMUX   = _BV(REFS0); 
    // Enable, div128, + 1st setup
    ADCSRA  |= _BV(ADEN)|_BV(ADSC)|_BV(ADPS2)|_BV(ADPS1)|_BV(ADPS0)|_BV(ADIE);
}

uint16_t readADC(void)
{
    uint16_t average=0;
    // Start Conversion
    ADCSRA |= _BV(ADSC);

    for (char i=0;i<ADC_AVG;i++) {
        MCUCR   |= _BV(SE);
        ADCSRA  |= _BV(ADSC);
        __asm volatile("sleep");
        MCUCR   &= ~_BV(SE);
        result  = (hi<<8);
        result  |= lo;
        average += result;
    }
    average /= ADC_AVG;
    return average;    
}

void sendByte(char val)
{
    while (! (UCSR0A & (1<<UDRE0)) ); //wait until tx is complete
    UDR0 = val;
}

/*
 * Convert voltage to temperature based on a negative coefficient for MAX6613
 */
uint16_t analogToTemp(uint16_t val)
{
  uint16_t temp;
  //v     = 5 * (val/1023.0);
  //temp  = (1.8455 - (5.0*(val/1023.0)))/0.01123;
  temp  = (1.8455 - (5.0*(val/1023.0)))*89;
  //temp = val * M_PI;
  //v     = 5 * ( val/1024);
  //temp  = (2 - v) * 89;

  return temp;
}

void initDisplay()
{
    sendByte(DISP_RESET);
    sendByte(DISP_BRIGHT_CMD);
    sendByte(0);
}

void serialSegments(uint16_t val) 
{  
  // 4 digit display
  sendByte(val / 1000);
  sendByte((val / 100) % 10);
  sendByte((val / 10) % 10);
  sendByte(val % 10);  
}

int main(void)
{
    uint16_t calc=0,sense=0;

    DDRB    |= _BV(DDB5);
    PORTB   |= _BV(PORTB5);
    initSerial();
    initADC();
    initDisplay();
    sei();
    MCUCR   |= (1 << SM0); // Setting sleep mode to "ADC Noise Reduction" 
    MCUCR   |= (1 << SE);  // Sleep enable 
    for(;;) {
        //PORTB   ^= _BV(PORTB5);
        if (calc>=9999){ // I can't see the real value. Max val on display is 9999
        //if (sense>=330){
            PORTB |= _BV(PORTB5);
        } else {
            PORTB &= ~_BV(PORTB5);
        }

        sense   = readADC();
        //calc    = sense*1.0;      // refuses to calculate properly
    calc    = analogToTemp(sense);  // a bunch of zeroes
        //calc = 65535*0.1;         // a-ok

        serialSegments(calc);
        _delay_ms(500);
        serialSegments(sense);
        _delay_ms(500);
    }
    return 0;
}

Makefile文件:

# AVR-GCC Makefile
PROJECT=Temp_Display
SOURCES=main.c
CC=avr-gcc
OBJCOPY=avr-objcopy
MMCU=atmega328p
OSC_HZ=16000000UL
OPTIMISATION=2
PORT=/dev/ttyUSB0

CFLAGS=-mmcu=${MMCU} -std=gnu99 -Wall -O${OPTIMISATION}  -DF_CPU=${OSC_HZ} -lm -lc 

${PROJECT}.hex: ${PROJECT}.out
    ${OBJCOPY} -j .text -O ihex ${PROJECT}.out ${PROJECT}.hex
    avr-size ${PROJECT}.out

$(PROJECT).out: $(SOURCES)
    ${CC} ${CFLAGS} -I./ -o ${PROJECT}.out ${SOURCES}

program: ${PROJECT}.hex
    stty -F ${PORT} hupcl
    avrdude -V -F -c arduino -p m168 -b 57600 -P ${PORT} -U flash:w:${PROJECT}.hex

clean:
    rm -f ${PROJECT}.out
    rm -f ${PROJECT}.hex

编辑:
好的,我在某种程度上简化了代码

#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
#define BAUD 9600
#include <util/setbaud.h>
#define DISP_BRIGHT_CMD     'z'
#define DISP_RESET          'v'


void initSerial(void)
{
    // set baud rate
    UBRR0H = UBRRH_VALUE;
    UBRR0L = UBRRL_VALUE;
    // set frame format
    UCSR0C |= (0x3 << UCSZ00); // 8n1
    // set enable tx/rx
    UCSR0B = _BV(TXEN0);
}


void sendByte(char val)
{
    while (! (UCSR0A & (1<<UDRE0)) ); //wait until tx is complete
    UDR0 = val;
}


void initDisplay()
{
    sendByte(DISP_RESET);
    sendByte(DISP_BRIGHT_CMD);
    sendByte(0);
}

void serialSegments(uint16_t val) {  
  // 4 digit display
  sendByte(val / 1000);
  sendByte((val / 100) % 10);
  sendByte((val / 10) % 10);
  sendByte(val % 10);  
}

int main(void)
{
    uint16_t i=0,val;

    DDRB    |= _BV(DDB5);
    initSerial();
    initDisplay();
    for(;;) {
        val = (uint16_t)(i++ * 1.5);
        serialSegments(i);
        _delay_ms(500);
        serialSegments(val);
        _delay_ms(500);
        if (val > 9999){
            PORTB |= _BV(PORTB5);
        } else {
            PORTB &= ~_BV(PORTB5);
        }
    }
    return 0;
}

最佳答案 非浮点浮点常量是double而不是float类型.

使用f后缀具有浮点字面值,例如0.1f

这可能会产生巨大的开销,因为像atmega8这样的MCU没有浮点单元,并且所有浮点运算都必须通过实现在固件中实现.

对于像atmega8这样的小型设备,通常会尝试避免使用浮动操作,因为没有FPU,它们在CPU周期中非常昂贵.

现在没有理由实现不能正确翻译表达式,如:

calc = sense * 1.0;

当calc和sense是uint16_t类型时.

点赞