logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

AVR-GCC: Problem z wyświetlaniem liczb zmiennoprzecinkowych na wyświetlaczu

shg 18 Sie 2005 14:44 1841 2
REKLAMA
  • #1 1744191
    shg
    Poziom 35  
    Posty: 2289
    Pomógł: 339
    Ocena: 135
    Cuda się dzieją i to niesamowite :[

    Objawy: po kompilacji (nie wywala żadnych błędów, ani ostrzeżeń) na wyświetlaczach dostaję "----", co oznacza u mnie liczbę większą od 9999.
    Używam operacji na liczbach zmiennoprzecinkowych.
    Nie wiem, czy w koncu ta biblioteka libm jest linkowana, czy nie, z pliku .lss wynika, ze tak, z .lst ze nie :|

    Po wywaleniu z Watt.c funkcji z math.h (w kodzie napisałem, co trzeba zrobić, pod koniec main()), nie inkludowaniu math.h i wyłączeniu linkowania libm program działa i obliczenia zmiennoprzecinkowe wykonuje prawidłowo. zajmuje jakieś 3.6kB
    Po kompilacji tego, co dałem tu, z linkowaniem libm program zajmuje 2.2kB :|

    Przepraszam z góry, że tak bez ładu i składu, ale już mi się mózg od tego lasuje, 5 godzin i ciągle nie działa.... arghhhhhhhhh.

    Watt.c
    
    #define F_CPU 1000000UL  // 1 MHz
    
    #include <avr/delay.h>
    #include <avr/signal.h>
    #include <avr/interrupt.h>
    #include <avr/io.h>
    #include <inttypes.h>
    #include <stdlib.h>
    #include <math.h>
    #include "Watt.h"
    
    #define REP_ADC 64
    
    /*	first array element is leftmost display, last one - LEDs
    	Contents of this array will be presented on the display
    */
    uint8_t display[5] = {0xff, 0xff, 0xff, 0xff, 0xff};
    
    uint8_t chrtab[] = {
    	CHR_0, CHR_1, CHR_2, CHR_3, CHR_4,
    	CHR_5, CHR_6, CHR_7, CHR_8, CHR_9};
    
    SIGNAL(SIG_OVERFLOW0) {
    static uint8_t display_pos=0;	/* current display position */
    	
    	/* blank display to avoid "ghost characters" */
    	PORTB |= (unsigned char) DISPLAY_BLANK_MASK;
    	
    	/* Put character on display */
    	PORTD = display[display_pos];
    	
    	/* Turn on appropriate display */
    	PORTB &= (unsigned char) ~(1 << display_pos);
    	
    	/* increment position counter */
    	if(display_pos++ >= 4)
    		display_pos = 0;
    }
    
    /* display num (limited to 9999) an decimal point at position 'point' */
    void display_number(unsigned int num, unsigned char point) {
    char str[5];
    	if(num < 10000) {
    		utoa(num, str, 10);
    		{
    			uint8_t i=0, i2=0;
    			while(str[i])
    				i++;
    
    			i = 4 - i;					/* number of leading zeros */
    			for(i2 = 0; i2 < i; i2++)
    /* blank leading zeros */ 
    /*				display[i2] = 0xff;*/
    /* display leading zeros */
    				display[i2] = CHR_0;
    
    			for(; i2<4; i2++)
    				display[i2] = chrtab[str[i2-i]-'0'];
    			display[point] &= CHR_DOT;
    		}
    	} else {
    		display[0] = CHR_MI;
    		display[1] = CHR_MI;
    		display[2] = CHR_MI;
    		display[3] = CHR_MI;
    	}
    }
    
    int main(void) {
    /* Blank displays, and enable pull-up for button */
    	PORTB = DISPLAY_BLANK_MASK | 0X20;
    /* display position pins are outputs */
    	DDRB = DISPLAY_BLANK_MASK;
    /* LED display segments - outputs*/
    	DDRD = 0xff;
    	
    /* enable ADC */
    	ADC_ENABLE();
    	_delay_ms(1);
    
    /* operation mode, channel, and reference voltage */
    	ADMUX = ADC_CHN | 0;
    	
    /* Start a dummy conversion, and wait for result */
    	ADC_START();
    	ADC_WAIT();
    
    
    /* configure timer0 */
    	TCCR0 = 2;				/* prescaler = 8 */
    	TIMSK |= (1 << TOIE0);	/* enable interrupt on overflow */
    	sei();					/* enable interupts */
    	
    	{
    		unsigned int volt=0;
    		uint8_t	conv_cnt;
    		double vf;
    		while(1){
    			volt = 0;
    			for(conv_cnt = REP_ADC; conv_cnt; conv_cnt--) {
    				ADC_START();
    				ADC_WAIT();
    				volt += ADC;	/* read ADCL, and ADCH */
    			}
    /***********  TEN FRAGMENT ****************/
    /* ZMIENIC: ponizsze razy 1000.0 */
    			vf = ((double) volt) * (2.50 / (1024.0 * ((double) REP_ADC)));
    /* ZMIENIC: ponizsze wywalic */
    			vf = sqrt(0.05 * pow(10.0, 4.0 * (vf - 2.25))) * 1000.0;
    /*************** KONIEC :P *****************/
    			volt = (unsigned int) vf;
    			display_number(volt, 0);
    		}
    	}
    	return 0;
    }
    


    Makefile - ten przykładowy dołączony do WinAVR, poprawiłem tylko typ proca - ATmega8 i zegar - 1MHz (no i TARGET ofkoz ;) ).

    make all wypluwa to:
    avr-gcc (GCC) 3.4.3
    Copyright (C) 2004 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions.  There is NO
    warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    
    
    Compiling: Watt.c
    avr-gcc -c -mmcu=atmega8 -I. -gdwarf-2 -DF_CPU=1000000UL  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=Watt.lst  -std=gnu99 -MD -MP -MF .dep/Watt.o.d Watt.c -o Watt.o 
    
    Linking: Watt.elf
    avr-gcc -mmcu=atmega8 -I. -gdwarf-2 -DF_CPU=1000000UL  -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=Watt.o  -std=gnu99 -MD -MP -MF .dep/Watt.elf.d Watt.o --output Watt.elf -Wl,-Map=Watt.map,--cref    -lm
    
    Creating load file for Flash: Watt.hex
    avr-objcopy -O ihex -R .eeprom Watt.elf Watt.hex
    
    Creating load file for EEPROM: Watt.eep
    avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" \
    --change-section-lma .eeprom=0 -O ihex Watt.elf Watt.eep
    
    Creating Extended Listing: Watt.lss
    avr-objdump -h -S Watt.elf > Watt.lss
    
    Creating Symbol Table: Watt.sym
    avr-nm -n Watt.elf > Watt.sym
    
    Size after:
    Watt.elf  :
    section           size      addr
    .text             2252         0
    .data               16   8388704
    .bss                 1   8388720
    .noinit              0   8388721
    .eeprom              0   8454144
    .debug_aranges      20         0
    .debug_pubnames     84         0
    .debug_info        561         0
    .debug_abbrev      310         0
    .debug_line        509         0
    .debug_str         269         0
    Total             4022
    
    
    AVR Memory Usage:
    -----------------
    Device: atmega8
    
    Program:    2268 bytes (27.7% Full)
    (.text + .data + .bootloader)
    
    Data:         17 bytes (1.7% Full)
    (.data + .bss + .noinit)
    
    
    -------- end --------
    
    
    > Process Exit Code: 0
    
  • REKLAMA
  • #2 1744873
    zumek
    Poziom 39  
    Posty: 3352
    Pomógł: 695
    Ocena: 52
    Ustaw optymalizację na -O0 (w makefile OPT=0), czyli wyłącz :wink:

    Piotrek
  • #3 1745967
    shg
    Poziom 35  
    Posty: 2289
    Pomógł: 339
    Ocena: 135
    Nie działa :[
    Sprawdziłem w debugerze, syf totalny, są do tych funkcji (matematycznych) odwołania w kodzie (rzekomo te funkcje znajdują się za głównym programem), ale nie wiem, czy zostały "doklejone". Wygląda na to że tak, bo główny program ma około kilobajta (do końca main()), a sam wsad (.bin) jest większy, tylko że nie mam za bardzo jak tego potwierdzić.

    A żeby nie było, to przekroczenie zakresu nie jest możliwe, więc to co się wyświetla, to ewidentnie wynik jakiegoś błędu. Maksymalna wyświetlona wartość nie ma prawa przekraczać 0.707 (czyli wartość zmiennej volt = 707)

    Do kompletu Watt.h
    #define LED_NW	0b11111101
    #define LED_UW	0b11111011
    #define LED_MW	0b11110111
    #define LED_W	0b11101111
    #define LDE_DBM	(LED_NW | LED_UW | LED_MW)
    
    #define CHR_0	0b10000001
    #define CHR_1	0b11110011
    #define CHR_2	0b01001001
    #define CHR_3	0b01100001
    #define CHR_4	0b00110011
    #define CHR_5	0b00100101
    #define CHR_6	0b00000101
    #define CHR_7	0b11110001
    #define CHR_8	0b00000001
    #define CHR_9	0b00100001
    
    /* 	special character - decimal dot, usage example:
    		display[2] = CHR_7 & CHR_DOT;
    	This wil put "7." on third display position
    */
    #define	CHR_DOT	0b11111110	/* decimal dot */
    #define CHR_MI	0b01111111	/* minus sign */
    #define CHR_BL	0b11111111	/* blank */
    
    /* mask for display blanking */
    #define DISPLAY_BLANK_MASK	0x1f
    
    /* ADC config, and channel selection */
    /* usage: ADMUX = ADCHN | channel_number */
    #define	ADC_CHN 0			/* external reference voltage, right adjusted result */
    
    /* ADCSRA */
    /* ADC enabled, single mode, no interrupts, prescaler = 8  (125kHz) */
    #define ADC_CONFIG (_BV(ADEN) + 3)
    #define ADC_ENABLE() {ADCSRA = ADC_CONFIG;}
    
    #define ADC_START() {ADCSRA |= _BV(ADSC);}
    
    #define ADC_WAIT() {while(ADCSRA & _BV(ADSC)) {};}
    


    -----------------------------------
    Miało być sto punktów, ale problem się po części rozwiązał.

    Program działa

    Problem okazał się leżeć po stronie symulatora (Proteus).

    Nie wiem dlaczego, ale po załadowaniu wsadu do proca rusza normalnie, a na symulatorze wywala przekroczony zakres. I jak tu teraz polegać na takim narzędziu... echh
    Dziwne i to bardzo... A może ktoś ma jakiś sposób na uruchomienie tego w Proteusie?
    Schemat:
    Do portu D wyświetlacze 7-segmentowe, bity i segmenty odpowiednio:
    0 - DP, 1-a, 2-b, itd.
    Do porty B tranzystory sterujące katodami (aktywny stan niski na porcie), ja dałem w Proteusie inwertery i wyświetlacz multipleksowany (koniecznie, bo na innym będą śmieci) katody kolejno od lewej, zaczynając od bitu 0.
    Napięcie wejściowe do ADC0.

    BTW, z włączoną optymalizacją też działa.
REKLAMA