Inattendu float comportement en C avec AVR atmega8

J'ai été à essayer de comprendre pourquoi je ne peux pas obtenir un bon de la valeur de la multiplication d'un unsigned int avec une valeur flottante.

De faire quelque chose comme 65535*0.1 fonctionne comme prévu, mais la multiplication d'un flotteur avec un uint à partir de la mémoire crée mad valeurs. J'ai une fonction qui lit un ADC et renvoie un uin16_t. Avec cette valeur, je suis d'impression à 4 chiffres led-écran, qui fonctionne bien.
La multiplication de la même valeur avec la 1.0 renvoie quelque chose de différent (elle est trop grande pour mon écran donc je ne sais pas vraiment ce que c'est).

Mon code est ci-dessous, mais la zone de conflit est au fond dans le main(). Toute aide serait super. Grâce

principal.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

EDIT:
OK, j'ai simplifié le code un peu

#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;
}
Depuis je n'ai pas accès à un atmega328p , je ne peux que prendre une deviner les questions possibles, par exemple, êtes-vous sûr de l'ordre des octets de l'ADC? je suppose que je pourrais prendre simavr 😛
putain je dois construire le régulateur de la chaîne? hhaha
Ahmed, j'ai un script
Êtes-vous sûr readADC retourne raisonnable de la valeur en premier lieu? Je ne suis pas convaincu que cette fonction n'quelque chose d'utile...
en utilisant 1,5 aime que les forces d'une double opération, utilisez 1.5 F, pour en faire un (unique) float opération. Double est un peu excessif. il serait beaucoup plus facile de dire simplement " val=i+(i>>1); i++; que d'essayer d'obtenir un double float opérations en cours sur un processeur 8 bits. même val=(i++*15)/10; est beaucoup plus susceptibles de travailler.

OriginalL'auteur regomodo | 2012-02-05