Jeszcze nie tak dawno nie znałem języka C tak jak obecnie. Jeszcze nie tak dawno nie doceniałem tego jak wielką wagę trzeba przykładać do projektowania szkieletu programu. Jeszcze nie tak dawno utknąłbym w martwym punkcie potrzebując wykorzystać w projekcie więcej niż 3 timery - 3 akurat zaszyte są w Atmega8. 

Olśnienie przyszło po poznaniu moliwości tworzenia timerów programowych :). Na czym to polega? Wszystko sprowadza się do wybrania jednego timera, który będzie odmierzać dla nas "tyknięcia" co określony, z góry założony czas.  W procedurze przerwania od przepełnienia tego timera umieszczamy króciuteńki kod ustawiający globalną flagę "Tick" na wartość true. Następnie w głównej pętli programu umieszczamy funkcję, która cały czas sprawdza stan tej flagi. Po wykryciu "tyknięcia" funkcja realizuje dekrementację wszystkich zadeklarowanych przez nas timerów (tych, które mają wartość większą od 0) oraz kasuje flagę "tyknięcia". W taki sposób możemy uzyskać dowolną ilość timerów programowych potrzebnych nam w programie. Są różne szkoły tworzenia funkcji systick. Jedni zalecają, aby używać w niej bezpośrednio zmiennych uint 8 lub uint 16, inni proponują wykorzystanie tablic. Generalnie jak zwykle wszystko zależy od naszego konkretnego problemu - ilości potrzebnych nam timerów. W przykładzie, który za chwilę zaprezentuję mam utworzone 33 niezależne timery: 32 w formie 4 tablic po 8 elementów oraz jeden jako bezpośrednio  podana zmienna (była ona już używana tutaj)

Jeżeli zamierzasz korzystać z pozostałych udostępnionych przeze mnie kodów, musisz mieć na uwadze, że w większości z nich korzystam z timerów programowych które tutaj prezentuję. A więc do dzieła :)

- zawartość pliku timer2.h

 
// timer 2 ustawiony do generowania przerwania co 10 milisekund
#ifndef Timer2_H
#define Timer2_H
#include <util/delay.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <avr/interrupt.h>
#include <avr/wdt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>
void tajmer2(void);
//Liczniki do obslugi klawiszy
extern volatile uint8_t Ltab[8];
extern volatile uint8_t Ltabr[8];
//Liczniki programowe ogolnego przeznaczenia
extern volatile uint8_t Licznik_8[8];
extern volatile uint16_t Licznik_16[8];
//Pomocnicza zmienna wyzwalajaca tykniecie co 10ms
extern volatile bool Tick;
extern volatile uint8_t T_LCDrefresh;
inline void SysTick(void) __attribute__((always_inline));
inline void SysTick(void)
{
if(Tick)
 {
 for(uint8_t i=0;i<8;i++)
 {
 if(Ltab[i])Ltab[i]--;
 if(Ltabr[i])Ltabr[i]--;
 if(Licznik_8[i])Licznik_8[i]--;
 if(Licznik_16[i])Licznik_16[i]--;
 }
 if(T_LCDrefresh)T_LCDrefresh--;
 Tick=false;
 }
}
#endif

- zawartość pliku timer2.c

 
#include "tajmer2.h"
void tajmer2(void) {
 TCCR2 = _BV(WGM21) |_BV(CS22) |_BV(CS21) |_BV(CS20);
 OCR2 = 155;
 TIMSK |= _BV(OCIE2);
}
//Pomocnicza zmienna wyzwalajaca tykniecie co 10ms
volatile bool Tick=false;
//Liczniki do obslugi klawiszy
volatile uint8_t Ltab[8];
volatile uint8_t Ltabr[8];
//Liczniki programowe ogolnego przeznaczenia
volatile uint8_t Licznik_8[8];
volatile uint16_t Licznik_16[8];
volatile uint8_t T_LCDrefresh;
ISR(TIMER2_COMP_vect)
{
 Tick=true;
}

Objaśnienia wymagać może zapis

inline void SysTick(void) __attribute__((always_inline));

Stosuję go, ponieważ funkcja SysTick wywoływana jest TYLKO I WYŁĄCZNIE w jednym miejscu programu. Nie ma zatem potrzeby, aby kompilator tworzył dla niej odpowiednią procedurę skoku wraz z odłożeniem na stos wartości niektórych rejestrów. Taki zapis powoduje, że kod funkcji jest przez kompilator "wklejany" dokładnie w miejscu, gdzie znajduje się jej wywołanie.

Jak widać, oprócz 17 liczników wykorzystywanych przez inne moduły programu, do dyspozycji jest również 16 liczników "ogólnego przeznaczenia":

Licznik_8[8];
Licznik_16[8];
 

Jednak posługiwanie się takimi nazwami liczników w programie głównym byłoby kłopotliwe ze względu na ich ilość. Jak temu zaradzić? Oto rozwiązanie:

/******************************************************************************/
#define L_a Licznik_16[0]
#define L_b Licznik_16[1]
#define L_kasujWszystkieLiczniki Licznik_16[2]
#define L_przeliczNominal Licznik_16[3]
#define L_kasujKredyt Licznik_16[4]
#define L_wyswNominaly Licznik_16[5]
#define L_wejdzDoMenu Licznik_16[6]
#define L_autoWyjscieZmenu Licznik_16[7]
/******************************************************************************/
#define L_buzz Licznik_8[0]
#define L_odliczanie Licznik_8[1]
#define L_zwloka Licznik_8[2]
#define L_oblicz_czasy Licznik_8[3]
#define L_nawiasy Licznik_8[4]
#define L_8f Licznik_8[5]
#define L_led Licznik_8[6]
#define L_errBlink Licznik_8[7]

Proste i skuteczne :) Teraz praca z tak nazwanymi timerami programowymi staje się czystą przyjemnościa. Jak widać pozostały mi jeszcze 2 wolne liczniki 16-to bitowe. Jeżeli będę potrzebował więcej to poprostu dodam kolejną tablicę :) Dlaczego tablice są 8-io elementowe? Niestety specyfika funkcji obsługującej wejścia (którą również prezentuję w tym dziale) zakłada obecność 8-iu niezależnych wejść. Gdybym miał potrzebę rozbudowy tej funkcji o więcej niż 8 wejść, to również i struktura tablic uległaby zmianie.

Od razu pragnę przestrzec przed błędem, jaki ja popełniłem na początku przygody z timerami programowymi. Mianowicie: ustaliłem rozdzielczość na 1ms, cały kod obsługi "tyknięcia" umieściłem w przerwaniu. Efekt? Program, pomimo kompilacji bez ani jednego warninga działał niestabilnie. Ratunkiem i jak się okazało jedyną słuszną drogą rozwoju było wykorzystanie systemu flag. Dzięki temu przerywam wykonywanie programu przerwaniem od timera na zaledwie kilkanaście mikrosekund potrzebnych na obsłużenie zmiany stanu flagi i powrót z przerwania. Po wielu testach rozdzielczość 10ms okazała się w 100% wystarczająca do realizacji wszystkich potrzebnych mi funkcji opartych nawet o timery 16-to bitowe :).

 

Do pobrania:

Użyj opcji uploadu plików, aby przekazać mi kod do sprawdzenia. Postaram się zajrzeć do niego tak szybko jak będzie to możliwe. Paczkę z poprawionym kodem umieszczę w tym samym miejscu.

 

 

Jeżeli wystąpi sytuacja, w której zostanie wyświetlona zawartość folderu z innego działu - należy wyczyścić cache przeglądarki! Następnie przeładować stronę.


Dodaj komentarz

Kod antyspamowy
Odśwież

Licznik odwiedzin

Mapa odwiedzin



 

Copyright © ST WebSite 2017

Stronę hostuje FutureHost. []

Strona korzysta z plików cookie. Dane przechowywane na Twoim komputerze służą wyłą…cznie do poprawienia funkcjonalnoś›ci witryny. Jeżeli tego nie akceptujesz - powinieneś› ją… opuś›cić‡.

Akceptujesz ciasteczka z tej witryny?