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][AVR-GCC] Łatwiejsze konfigurowanie liczników?

ZbeeGin 23 Sie 2009 20:02 2229 4
REKLAMA
  • #1 6926405
    ZbeeGin
    Poziom 39  
    Właśnie pisałem jakiś kolejny program w GCC i jak pewnie większość z Was musiałem sobie skonfigurować liczniki by uzyskać... coś, nieważne co. Dziwię się, że środowisko związane z AVRGCC nie wpadło na pomysł by takie konfiguracje ludziom uprościć! Pewnie wielu z Was rozpisuje się w stylu:
    // **********************************************************
    // procedurki pomocnicze
    // **********************************************************
    
    void timer_setup(void)
    {
    	// generator podstawy czasu dla silnika
    	OCR0 = RELOAD;									// wpisz wartość okresu przerwań
    	TCCR0 = (0<<CS02) | (0<<CS01) | (1<<CS00) | (1<<WGM01) | (0<<WGM00);
    	// preskaler = 1, CTC = włączone, jeszcze nie generuj przerwań
    	
    	// generator podstawy czasu dla wyświetlacza
    	OCR1A = 0x1FFF;
    	TIMSK |= (1<<OCIE1A);
    	TCCR1A = (0<<WGM11) | (0<<WGM10);
    	TCCR1B = (0<<CS12) | (1<<CS11) | (0<<CS10) | (1<<WGM12);
    	// preskaler 8, CTC = włączone, generuj przerwania
    	// ok. 60Hz
    	
    	// sterownik PWMa
    	// konfiguracja timer2  (czy to się nie pogryzie z silnikiem?)
    	TCCR2 = (0<<CS02) | (1<<CS01) | (0<<CS00) | (1<<WGM20) | (1<<COM21);
    	// prescaler = 8, PWM phase mode = włączone, tryb = clear downcount
    	// Fpwm = 3,9kHz
    	
    } 

    Czyż nie byłoby prościej mieć plik nagłówkowy timerconf.h w którym wszelkie te łączenia i przesuwanie bitów zdefiniowne by były jako przyjazne i zrozumiałe definicje:

    (0<<CS02) | (0<<CS01) | (1<<CS00) -> TIMER0_PRESCALER_1
    (0<<CS12) | (1<<CS11) | (0<<CS10) -> TIMER1_PRESCALER_8

    (1<<WGM01) | (0<<WGM00) -> TIMER0_CTC_ON
    (1<<COM20) | (1<<COM21) -> TIMER2_PWM_CLEAR_DOWN

    itd.

    Wtedy możnaby przykładowo zapisać:
        TCCR0 = TIMER0_PRESCALER_1 | TIMER0_CTC_ON;

    Prawda, że prościej i czytelniej?


    A teraz pomarzmy dalej. Możnaby to usystematyzować i pójść na całego. Zaprządz do pracy preprocesor i za pomocą jednej funkcji - co prawda z dość skompilkowanymi warunkami - wykonać konfigurację licznika w najprostszy możliwy sposób:

        timer0_conf(PRESCALE_1|PWM_FAST|PWM_INVERT_MODE)



    Pewnie ktoś mógłby Mi zarzucić w tej chwili, że z GCC chciałbym zrobić GCCOMA (taki C-BASCOM). Lecz jak starałem się popełnić jeden program dla MSP430 to przykładowo w środowisku Code Composer były właśnie takie pliki nagłówkowe, które znacznie upraszczały konfigurację czegokolwiek.

    Pojawią się też głosy: "No to zrób!". Być może. Ale czy nie można by wspólnie takiego czegoś opracować? Co o tym myślą starzy wyjadacze GCC...?
  • REKLAMA
  • #2 6926495
    m.bartczak
    Poziom 16  
    Pewnie że można - wszystko co upraszcza Ci pracę to dobry pomysł :)

    Ale zamiast wyważać otwarte drzwi, czemu nie skorzystać z czegoś gotowego, np. Procyon AVR Lib

    Procyon AVRlib

    Jest to zrealizowane w formie kodu, nie tylko jako makra preprocesora, za to dużo bardziej zaawansowane:

    
    #include "timer.h"
    
    ...
    
    timerInit();
    timerAttach(TIMER0OVERFLOW_INT, doSomething);
    


    Proste i przyjemne :)
  • REKLAMA
  • #3 6926845
    mirekk36
    Poziom 42  
    ZbeeGin --> jakiś czas temu programowałem AVR'ki głównie w Bascomie z połączeniem asemblera. Ale też już jakiś czas temu zacząłem naukę GCC. Przyznam, że od dawna chodziło mi po głowie to co tu napisałeś - czyli przygotowanie takich swoich właśnie chociażby plików nagłówkowych z tego typu definicjami. Nie widziałem wcześniej wprawdzie na oczy tego o czym wspomniał kolega m.bartczak czyli o Proycon AVRlib, które nie zaprzeczam są SUPER ;) !!! - no ale fakt jest faktem, że to nie same nagłówki typu *.h ale i biblioteki *.c

    .... ja osobiście jestem za .... jeśli chodziłoby o przygotowanie takiego szkieletu najbardziej potrzebnych i koniecznych, najczęściej używanych makr .... sam kiedyś już się zabierałem za coś takiego - ale jak zwykle czas gonił i nigdy nie można było się za to zabrać - a nazwenictwo tego właśnie chciałem zapożyczyć prawie wprost z Bascoma ;)
  • REKLAMA
  • #4 8455753
    ZbeeGin
    Poziom 39  
    Odkopuję stary temat, ale jest on ciągle aktualny. A chwila wolnego od pracy pozwoliła na dalsze przemyślenia i jakieś proste - na razie - działania.

    W sumie to całą konfigurację licznika można by potraktować jako jeden bajt, albo dwa bajty. Oczywiście są też przypadki na więcej bajtów, ale jest ich mniejszość. Pozwoliłoby to na proste stworzenie funkcji, której parametrem była by wartość typu uint8_t albo uint16_t. Funkcja zaś sama by dzieliła części bajtów na TCCRxA i jeśli trzeba na TCCRxB.

    Pomyślałem zatem o utworzeniu kilku plików nagłówkowych - po jednym dla każdego tajmera, tak aby wszystko dalej uprościć i jakoś usystematyzować.

    Taki przykład mogę już podać, a starzy wyjadacze mogą wytknąć błędy lub zaproponować jeszcze inne uproszczenia.

    #ifndef _AVR_TIMER0_H_
    #define _AVR_TIMER0_H_
    
    #ifndef _AVR_IO_H_
    #include <avr/io.h>
    #endif
    
    #define lo8(x) ((int)(x)&0xff) 
    #define hi8(x) ((int)(x)>>8)
    
    #if defined (__AVR_AT94K__)
    /* soon */
    
    #elif defined (__AVR_AT90S2313__) || defined (__AVR_AT90S2323__) || defined (__AVR_AT90S2343__) || defined (__AVR_AT90S2333__) || defined (__AVR_AT90S4433__) || defined (__AVR_AT90S8535__) || defined (__AVR_AT90C8534__) || defined (__AVR_AT90S8515__) || defined (__AVR_AT90S4414__) || defined (__AVR_AT90S4434__)
    #define T0_STOP				0x00
    #define T0_PRESCALE_1		0x01
    #define T0_PRESCALE_8		0x02
    #define T0_PRESCALE_64		0x03
    #define T0_PRESCALE_256		0x04
    #define T0_PRESCALE_1024	0x05
    #define T0_EXT_RISING		0x06
    #define T0_EXT_FALLING		0x07
    
    void config_t0(uint8_t bits)
    {
    	TCCR0 = bits;
    }
    
    #elif defined (__AVR_ATtiny2313__) || defined (__AVR_ATtiny2313A__) || defined (__AVR_ATtiny4313__) || defined (__AVR_ATtiny13__) || defined (__AVR_ATtiny13A__) || defined (__AVR_ATtiny24__) || defined (__AVR_ATtiny24A__) || defined (__AVR_ATtiny44__) || defined (__AVR_ATtiny44A__) || defined (__AVR_ATtiny84__)
    #define T0_STOP						0x0000
    #define T0_PRESCALE_1				0x0001
    #define T0_PRESCALE_8				0x0002
    #define T0_PRESCALE_64				0x0003
    #define T0_PRESCALE_256				0x0004
    #define T0_PRESCALE_1024			0x0005
    #define T0_EXT_RISING				0x0006
    #define T0_EXT_FALLING				0x0007
    
    #define T0_FORCE_0A					0x0080
    #define T0_FORCE_0B					0x00C0
    
    #define T0_NORMAL					0x0000	/*0b0000 0000 0000 0000*/
    #define T0_PWM_PHASECORRECT			0x0100	/*0b0000 0001 0000 0000*/
    #define T0_CTC						0x0200	/*0b0000 0010 0000 0000*/
    #define T0_PWM_FAST					0x0300	/*0b0000 0011 0000 0000*/
    #define T0_PWM_PHASECORRECT_OCRTOP	0x0108	/*0b0000 0001 0000 1000*/
    #define T0_PWM_FAST_OCRTOP			0x0308	/*0b0000 0011 0000 1000*/
    
    #define	T0_OC0A_NORMAL				0x0000
    #define T0_OC0A_TOGGLE				0x6000
    #define T0_OC0A_CLEAR				0x8000
    #define T0_OC0A_SET					0xC000
    
    #define T0_OC0A_FPWM_TOGGLE			0x6000
    #define T0_OC0A_FPWM_CLEAR			0x8000
    #define T0_OC0A_FPWM_SET			0xC000
    
    #define T0_OC0A_PWM_TOGGLE			0x6000
    #define T0_OC0A_PWM_CLEAR_UP		0x8000
    #define T0_OC0A_PWM_SET_UP			0xC000
    #define T0_OC0A_PWM_CLEAR_DOWN		0xC000	/* for migrating Bascom users */
    
    #define	T0_OC0B_NORMAL				0x0000
    #define T0_OC0B_TOGGLE				0x1800
    #define T0_OC0B_CLEAR				0x2000
    #define T0_OC0B_SET					0x3000
    
    #define T0_OC0B_FPWM_CLEAR			0x2000
    #define T0_OC0B_FPWM_SET			0x3000
    
    #define T0_OC0B_PWM_CLEAR_UP		0x2000
    #define T0_OC0B_PWM_SET_UP			0x3000
    #define T0_OC0B_PWM_CLEAR_DOWN		0x3000	/* for migrating Bascom users */
    
    void config_t0(uint16_t bits)
    {
    	TCCR0A = hi8(bits);
    	TCCR0B = lo8(bits);
    }
    
    #else
    #  if !defined(__COMPILING_AVR_LIBC__)
    #    error "device type not defined, cannot determine registers to configure timer0"
    #  endif
    #endif
    
    #endif /* _TIMER0_H_ */
    


    I teraz na przykład sprawa ustawienia licznika Timer0 jako FastPwm stanie się w sumie prosta:

    config_t0(T0_PRESCALE_8 | T0_PWM_FAST | T0_OC0A_FPWM_SET);
REKLAMA