Skocz do zawartości

[Kurs] Kurs programowania ARM cz.08 - PWM c.d.


Elvis

Pomocna odpowiedź

Poprzednia lekcja

Lekcja 8 - PWM ciąg dalszy

Następna lekcja

Rejestry cienie

Poprzedni program był bardzo prosty, niestety miał kilka mankamentów.

Pierwszy to problem zmiany wartości zmiennej pwm_led_0.

Jeśli zmienimy wartość tej zmiennej podczas działania PWM to możemy uzyskać dość dziwne działanie - zmienimy wartość na mniejszą, a na w efekcie na chwilę dioda zapali się na bardzo długo (cały okres PWM).

Wyobraźmy sobie następującą sytuację. Nasz PWM miał wypełnienie 50%, zmieniamy na 10%.

Jeśli zmiany dokonamy, gdy licznik jest pomiędzy 10%, a 50% to procedura PWM nie wygasi diody!

Przy następnym okresie będzie już działać poprawnie, ale jeśli często będziemy zmieniać wartość wypełnienia, nasz PWM będzie działać niepoprawnie.

Problem taki pojawia się, gdy zmieniamy wypełnianie PWM podczas pracy. Najlepiej byłoby zmieniać wypełnianie dopiero po zakończeniu okresu PWM.

Możemy to zrealizować tworząc rejestr cień (ang. shadow). Rejestr ten będzie przechowywał aktualnie używaną wartość PWM, po zakończeniu okresu PWM wartość zwykłego rejestru będzie kopiowana do rejestru cienia.

Nowa procedura obsługi PWM wygląda następująco:

void pwm_proc()
{
static unsigned int pwm_led_0_shadow = 0;

if (++pwm_counter>=PWM_PERIOD) {
	pwm_led_0_shadow = pwm_led_0;
	pwm_counter = 0;
	GPIO1_IOSET = LED_0;
}
if (pwm_counter==pwm_led_0_shadow)
	GPIO1_IOCLR = LED_0;
}

Zmienna pwm_led_0_shadow została zadeklarowana jako static. Zachowuje się jak zmienna globalna, ale poza procedurą nie ma do niej dostępu - dzięki temu "przypadkiem" nie zmienimy jej wartości.

Warto przeanalizować ten program - większość procesorów ma wbudowaną obsługę rejestrów cieni w sprzętowych modułach PWM.

program12.zip zawiera przykładowy kod płynnie zapalający i wygaszający diodę.

Poniżej filmik z rezultatem działania:

Więcej linii PWM

Gdy potrafimy już sterować jedną linią (i diodą) możemy rozbudować program o obsługę wielu (w naszym przypadku 8) diod.

Moglibyśmy wykorzystać metodę „kopiuj-wklej” i 8 razy skopiować poprzedni program.

Nie jest to oczywiście dobra metoda, więc my postąpimy inaczej. Wykorzystamy tablice.

Procedura obsługi PWM wygląda tak:

void pwm_proc()
{
static unsigned int pwm_counter = 0;
static unsigned int pwm_led_shadow[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

int i;

if (++pwm_counter>=PWM_PERIOD) {
	for (i=0;i<8;i++)
		pwm_led_shadow[i] = pwm_led[i];
	pwm_counter = 0;
	GPIO1_IOSET = LED_MASK;
}
for (i=0;i<8;i++) 
	if (pwm_counter==pwm_led_shadow[i])
		GPIO1_IOCLR = LED_PIN[i];
}

Od poprzedniej różni się głównie dodaniem pętli oraz wykorzystaniem tablic zamiast zmiennych.

Nowy program będzie wykorzystywał wartości pwm_led[0] ... pwm_led[7] do ustawiania jasności diod D0...D7.

Program program13.zip zapala i gasi na zmianę diody D0 i D1.

Stała:

#define LIGHT_N			20

definiuje w ilu krokach będziemy zapalać/gasić diody.

Funkcja set_led_light(int value) realizuje niejako przechodzenie świecenia od D0 do D1. Dla parametru value=0 zapalana jest D0, następnie zwiększając value przygaszana jest D0, a rozjaśniana D1. Dla value=LIGHT_N w pełni świeci D1.

Kod jest następujący:

void set_led_light(int value)
{
pwm_led[0] = (LIGHT_N-value)*PWM_PERIOD/LIGHT_N;
pwm_led[1] = value*PWM_PERIOD/LIGHT_N;
}

Działanie programu pokazuje film:

W pliku program14.zip znajdziemy jeszcze jeden przykład zastosowania naszego PWM - kolejne zapalanie wszystkich 8 diod.

Program jest nieco bardziej rozbudowany, diody zamiast liniowo, wygaszane i zapalane są stablicowaną funkcją sinus - takie sterowanie daje nieco ładniejszy efekt i jest bardzo ważne gdybyśmy sterowali silnik za pomocą PWM.

Film z rezultatem działania:

Program14.zip

Program13.zip

Program12.zip

Link do komentarza
Share on other sites

Mam pytanie odnośnie do tablicowania, pamięci itd. 🙂 Czy deklarując stałą np.:

const unsigned int[20] = {...};

zostanie ona umieszczona w pamięci RAM, czy w pamięci programu? Jeżeli jest przechowywana w pamięci RAM, wpisanie jej jako stałej ma średni sens, jedyna zaleta takiego rozwiązania jest taka, że ją chroni przed zmainami. Czy w WinARMie można sprawdzić jaka jest przewidywana zajętość pamięci RAM? Rozumiem, że pamięć flash jest podawana jako "image size", tak?

Link do komentarza
Share on other sites

Aby sprawdzić, gdzie są przechowywane zmienne funkcje itd. najlepiej otworzyć plik z rozszerzeniem .map (znajduje się w katalogu z plikiem wynikowym).

Przykładowo w program14.map znajdziemy fragment:

Memory Configuration

Name             Origin             Length             Attributes
ROM              0x00000000         0x0001e000         xr
RAM              0x40000000         0x00004000         rw
*default*        0x00000000         0xffffffff

Tutaj zdefiniowana jest mapa pamięci naszego mikrokontrolera. Od adresu 0 mamy Flash, od 0x40000000 pamięć RAM.

Dalej odnajdujemy adresy zmiennych, czy funkcji. Przykładowo stała:

const unsigned int SIN_TABLE[] = { 0, 15, 31, 46, 61, 76, 90, 104, 117, 129, 141, 152, 161, 170, 178, 184, 190, 194, 197, 199, 200 };

Daje wynikowo:

 *(.rodata)
.rodata        0x00000418       0x74 program14.o
               0x00000438                SIN_TABLE
               0x00000418                LED_PIN

Więc jak widać const działa zgodnie z oczekiwaniami, stała umieszczana jest w pamięci Flash.

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.