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;
}
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
Vous devez vous connecter pour publier un commentaire.
Unsuffixed virgule flottante constante sont de type
double
pasfloat
.Utilisation
f
suffixe d'avoir unfloat
littérale, par exemple,0.1f
Cela peut faire une énorme surcharge comme Mcu comme atmega8 n'ont pas d'unité à virgule flottante et toutes les opérations en virgule flottante doivent être mises en œuvre dans le firmware par la mise en œuvre.
Avec de petits appareils comme un atmega8 généralement essayez d'éviter d'utiliser
float
opérations sans FPU ils sont très chers dans les cycles de PROCESSEUR.Maintenant il n'ya aucune raison pour une mise en œuvre ne serait pas correctement traduire une expression comme:
calc = sense * 1.0;
quand
calc
etsense
sont deuint16_t
type.calc = sens * 1.0 est exactement pourquoi je suis assez perplexe ici. Je l'ai essayé sur 2 différents compilateurs créées par le biais de différents moyens (Ubuntu et mon propre script) sur différentes machines.
65535 * 0.1
sera fait lors de la compilation, mais pas(uint16_t) val * 1.0
qui sera exécutée lors de l'exécution sur l'atmega. Sinon d'opérations en virgule flottante sont pris en charge paravr-gcc
mais à l'aide de point fixe des opérations, c'est mieux.OriginalL'auteur ouah
Pas exactement votre code, peut-être assez proche, peut-être pas.
Tout d'abord quand je l'affiche de la sortie et de les comparer aux lignes:
le résultat est le même. Démontage montre un peu les choses ainsi. Tout d'abord avr-gcc
utilise des flotteurs pas de doubles, de sorte que les observations d'environ 1,5 F vs 1.5 sont tout à fait valable en général, mais ne sont pas pertinentes ici. La deuxième c'est de produire des valeurs de virgule flottante simple précision, et fait de calcul en virgule flottante, le compilateur de ne pas prendre un raccourci, il convertit à flotteur, le fait de multiplier les convertit ensuite en arrière.
À l'aide de mon hex routine d'affichage et de votre affichage décimal de routine (modifié en sortie sur un terminal série, ici encore, il produit le même résultat, le calcul en virgule flottante ne semble pas être le problème.
J'ai commencé cette tâche pour voir si la performance en virgule flottante était un tueur, et il est, mais le timing des changements en fonction de la façon dont je l'ai tester. il a fallu autant que 157 fois plus longtemps pour le code de la virgule flottante par rapport à des points fixes. Si je pars dans l'appel à serialSegments(), mais l'appel à un mannequin de routine au lieu de le port série, il est 3 fois plus lente pour le flotteur. Aussi, j'ai construit de deux manières différentes, et a tiré dans une libc/m qui utilise un ensemble différent de virgule flottante de routines, de la virgule flottante routines choisie par le compilateur C étaient 7 fois plus lente que la libc/libm.une séance dans le répertoire /usr/lib64/avr/lib/. Une fois que vous ajoutez de l'attente sur le port série et d'autres retards, vous pouvez ne pas remarquer la différence de timing donc cette expérience, tout en démontrant que le flotteur est très douloureuse, n'est probablement pas l'arme, même si votre code est sensibles au temps, nous parlons de quelques millisecondes.
En plus le code ci-dessous j'ai essayé ceci:
for(i=0;i<9999;i++)
{
vala = (unsigned int)(i * 1.5);
valb = i+(i>>1); i++;
si(vala!=valb)
{
hexstring16(i);
hexstring16(vala);
hexstring16(valb);
}
}
Pas échouer. - Je limité à 9999 parce que serialSegments() seulement côtelettes décimales de 0 à 9999. Maintenant, la boucle va au-delà de 65535, mais vous verrez que les problèmes de cause sans le flotteur bien que, droite?
avr.c
factice.s
vecteurs.s
Makefile
C'était tout de l'exécution sur un arduino pro mini (atmega328p).
a prendre des instantanés de la 16 bits de la minuterie, puis à la fin combiné les deux 8 bits pièces en 16 bits.
J'ai été en utilisant la minuterie pour le temps de la différence entre la solution de flottaison et le point fixe de la solution pour le multiplicateur, qui est l'endroit où l'157 fois plus de temps pour utiliser les float sont venus.
OriginalL'auteur old_timer
Vous êtes mal avec float<=>int conversion et de la coulée. Déposer l'ensemble du projet, de faire de nouveaux test de l'un et de l'utilisation simple de conversion de base et de la coulée de commandes avec étape par étape de débogage dans AVR Studio et vous trouverez votre point de malentendu, le concept. Vous disposez également d'un mauvais concept sur Stackoverflow. Ce n'est pas un forum de discussion, et les gens de l'aide pour les récompenses. Jusqu'à présent, je ne vois pas qui vous a donné +1 à personne. Vous n'avez pas à, mais je vois que certaines personnes ont donné beaucoup d'efforts pour rien. Lire la FAQ.
J'apprécie qui utilise un système de points, mais comme Reddit du karma, il semble un peu ridicule, surtout une fois que vous avez les permissions de base. Obtenir OT, je l'ai déjà découper n'importe quel float-ops et à l'échelle de mes valeurs pour effectuer math entier. Je pense que je vais garder mon float-ops sur mon Bras projets.
Je ne suis pas ici pour les points, ici pour aider les gens de partager ce que je sais et de l'apprentissage de ce que les autres savent. Si le problème nécessite alors une discussion peut être utile, peut-être à la fin tout cela est édité dans un concis question et réponse pour la préservation à long terme. Parfois, l'affiche doit autant s'empêcher de demander à leur question comme ils le font avec la question elle-même et qui peut nécessiter plusieurs allers et retours. La possibilité d'ajouter des commentaires dans les questions ou les réponses ne seraient pas ici si la discussion n'a pas été autorisé.
OriginalL'auteur avra