Mikrokontrolery STM32 - Obsługa portów IO, przerwań i timerów z wykorzystaniem funkcji API.pdf
(
1001 KB
)
Pobierz
104-108_stm32.indd
PODZESPOŁY
Obsługa portów we/wy,
przerwań i timerów
z wykorzystaniem funkcji API
Pierwszą umiejętnością, jaką trzeba nabyć, gdy rozpoczyna się
pracę z nową rodziną mikrokontrolerów jest zapanowanie nad
portami we/wy, przerwaniami i timerami, ponieważ stanowią
one pomost łączący MCU ze światem zewnętrznym. W artykule
przedstawiamy informacje, jak tego dokonać w przypadku
mikrokontrolerów STM32 w oparciu o płytę ewaluacyjną
STM3210B – EVAL/A.
stany swoich wejść na wyjściach, innymi słowy
diody sygnalizacyjne mają się zapalać w takt
zmian poziomu logicznego na wejściach. Jest
to zrealizowane przy pomocy joysticka znaj-
dującego się na płycie ewaluacyjnej. Biorąc po
uwagę budowę płyty uruchomieniowej, wej-
ścia z podłączonym joystickiem należy skon-
figurować jako „pływające” (
input floating
),
natomiast wyjścia jako
push–pull
. Praca wyjść
w konfiguracji
push–pull
oznacza, że dzięki
odpowiedniemu podłączeniu wewnętrznych
tranzystorów MOS, ustawienie wyjścia w stan
logicznej „1” spowoduje pojawienie się na
końcówce układu napięcia zasilania, nato-
miast ustawienie w programie logicznego „0”
będzie skutkowało podaniem na wyprowa-
dzenie układu potencjału masy.
By dobrze wykorzystać możliwości portów
we/wy, w pierwszej kolejności należy je odpo-
wiednio do określonego zadania skonfiguro-
wać. Najpierw jednak zostanie przedstawiony
mechanizm, jaki wykorzystują do pracy funk-
cje API.
Dla każdego urządzenia, czy jest to GPIO,
kontroler przerwań, czy jakikolwiek inny ele-
ment systemu, są stworzone odrębne typy da-
nych. W przypadku portów we/wy nazywają
się one GPIO_TypeDef, zaś do inicjacji jest wy-
korzystywany typ GPIO_InitTypeDef. Z punktu
widzenia programisty największe znaczenie
ma typ inicjujący, ponieważ to właśnie zmien-
ną tego typu jawnie tworzymy w pisanym
kodzie. Typ GPIO_TypeDef zapewnia dostęp
do poszczególnych rejestrów mikrokontrole-
ra i jest wykorzystywany przede wszystkim
przez funkcje API, natomiast zmienna typu
GPIO_InitTypeDef musi istnieć w każdej apli-
kacji wykorzystującej porty we/wy, ponieważ
jest wykorzystywana do inicjalizowania i kon-
figurowaniaportów.Na
list. 1
przedstawiono
kluczowy fragment kodu odpowiedzialny za
konfigurację portów oraz operacje na nich.
Utworzona na początku zmienna GPIO_Init-
Struct jest,
de facto
, strukturą. Inicjowanie
pinów lub w szczególnym przypadku całego
portu odbywa się w ten sposób, że wypełnia
się poszczególne pola struktury, a następnie
Operowanie bezpośrednio na rejestrach ja-
kiegokolwiek 32–bitowego procesora lub mi-
krokontrolera nie należy do zadań prostych.
Mimo, iż samo napisanie (stosunkowo zaawan-
sowanych) aplikacji przy użyciu nazw rejestrów
jest możliwe, to wprowadzanie zmian do ist-
niejącego kodu po upływie na przykład kilku
miesięcy, dodatkowo przez osobę, która nie
jest autorem programu, jest w zasadzie nie-
możliwe do wykonania w sensownym czasie.
Z tego powodu do takich operacji programiści
wykorzystują funkcje o mniej lub bardziej koja-
rzących się nazwach. Jeżeli mamy do czynienia
z bardzo skomplikowanym projektem wyko-
rzystującym wiele peryferiów mikrokontrolera,
to napisanie stosownych funkcji jest praco-
chłonne i wymaga dobrej znajomości architek-
tury mikrokontrolera. Firma STMicroelectronics
zauważyła ten problem i udostępnia komplet-
ne biblioteki API, które pozwalają w pełni kon-
trolować MCU. W pewnych przypadkach może
oczywiście zajść potrzeba bezpośredniego
odwołania się do rejestru, we wszystkich po-
zostałych funkcje API znacznie skracają czas po-
trzebny na napisanie i uruchomienie aplikacji.
Zasada wykorzystania biblioteki API zostanie
przedstawiona przy okazji omówienia obsługi
portów we/wy. Kompletne listingi przykładów
można znaleźć na stronie
http://paprocki.we-
mif.net
.
Porty wejścia/wyjścia
Porty we/wy mikrokontrolerów STM32
mogą pełnić do ośmiu funkcji. Oprócz pracy
jako alternatywne wejście lub
wyjście, określone wyprowa-
dzenie może być skonfiguro-
wane jako: wejście „pływa-
jące”,
pull–up
,
pull–down
,
lub jako wejście analogowe.
W konfiguracji wyjścia wy-
prowadzenie może pracować
w konfiguracji z otwartym
drenem lub
push–pull
. Uprosz-
czoną budowę takiego uniwer-
salnego portu przedstawiono
na
rys. 1
.
Sposób konfigurowania
i obsługiwania GPIO (
General
Purpose Input Output
) wyjaś-
nimy może na niezbyt wyrafi-
nowanym przykładzie, jednak
dzięki temu będzie on przej-
rzysty i czytelny.
Układ ma odzwierciedlać
Rys. 1. Uproszczona budowa portu uniwersalnego
104
ELEKTRONIKA PRAKTYCZNA 12/2008
Mikrokontrolery STM32
Obsługa portów we/wy, przerwań i timerów w mikrokontrolerach STM32
List. 1. Kluczowy fragment kodu odpowiedzialny za konfigurację portów oraz operacje na
nich
GPIO_InitTypeDef GPIO_InitStruct;
int main(void)
{
RCC_Conf();
NVIC_Conf();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 |
GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStruct);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_12 |
GPIO_Pin_14;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStruct);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOE, &GPIO_InitStruct);
while (1)
{
if(GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_14))
GPIO_ResetBits(GPIOC, GPIO_Pin_6);
else
GPIO_SetBits(GPIOC, GPIO_Pin_6);
if(GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_8))
GPIO_ResetBits(GPIOC, GPIO_Pin_9);
else
GPIO_SetBits(GPIOC, GPIO_Pin_9);
if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_1))
GPIO_ResetBits(GPIOC, GPIO_Pin_8);
else
GPIO_SetBits(GPIOC, GPIO_Pin_8);
if(GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_0))
GPIO_ResetBits(GPIOC, GPIO_Pin_7);
Tab. 1. Przykład remapowania pinów
Timera 3
całkowity
remap-
ping
TIM3_CH1 PA6 PB4 PC6
TIM3_CH2 PA7 PB5 PC7
TIM3_CH3 PB0
częściowy
remap-
ping
PC8
TIM3_CH4 PB1
PC9
Funkcje alternatywne
i „
remapping
”
W niektórych przypadkach fizyczne rozmiesz-
czenie elementów na docelowej płytce drukowa-
nej nie pozwala na podłączenie urządzenia, bądź
elementu zewnętrznego do wyprowadzenia, które
jest domyślnie powiązane z interesującą projek-
tanta alternatywną jego funkcją. W takich sytua-
cjach na ratunek przychodzi możliwość „przema-
powania” (
remapping
) GPIO. Jeśli więc przypisanie
danej funkcji alternatywnej, np. portu USART nie
odpowiada potrzebom projektowanej aplikacji, to
można ową funkcję przepisać do innego, bardziej
odpowiedniego dla aktualnego zastosowania wy-
prowadzenia. Takie zabiegi mogą być przeprowa-
dzane tylko dla ściśle określonych wyprowadzeń,
co jest szczegółowo podane w nocie katalogowej
każdego mikrokontrolera z rodziny STM32. Przy-
kłady możliwości zmiany funkcji różnych pinów
dla czterech kanałów Timera 3 są przedstawione
w
tab. 1
,
natomiast jedno z możliwych rozwią-
zań programowych przedstawiono na
list. 2
. Do
zmiany przypisania domyślnego funkcji alterna-
tywnej służy funkcja
GPIO_PinRemapConfig
. Infor-
macje, które należy przekazać funkcji to określenie
interesujących nas peryferiów, podanie czy prze-
mapowanie ma być tylko częściowe, czy całkowite
oraz czy włączamy, czy wyłączamy
remapping
.
Przedstawiony fragment kodu sprawi, że Timer
3 będzie sterował wyjściami od GPIO_Pin_6 do
GPIO_Pin_9. Należy oczywiście pamiętać o włą-
czeniu taktowania dla funkcji alternatywnej i sa-
mego portu (tutaj będzie to port GPIOC) w bloku
konfiguracji sygnałów zegarowych i zerowania
– funkcja
RCC_Conf
.
else
GPIO_SetBits(GPIOC, GPIO_Pin_7);
}
}
List. 2. Zmiana funkcji pinów Timera 3
void GPIO_Conf(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_PinRemapConfig(GPIO_FullRemap_TIM3, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7 |
GPIO_Pin_8 | GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStruct);
}
przekazuje tak przygotowaną zmienną przez
referencję do funkcji inicjującej. W przedsta-
wianej sytuacji, w naszym kręgu zaintereso-
wań leżą wszystkie trzy pola struktury GPIO_
InitStruct. W pierwszej kolejności ustalamy,
które z pinów będą konfigurowane, następ-
nie wybieramy żądany tryb pracy, w tym przy-
padku będzie to wyjście
push-pull
lub wejście
pływające (
input floating
). Następnie ustala-
my maksymalną prędkość, z jaką będą mogły
pracować wyprowadzenia układu. Tak przy-
gotowaną zmienną należy przekazać poprzez
referencję w argumencie do funkcji inicjującej
GPIO_Init
, podając przy tym również, do ja-
kiego portu mają być zastosowane wybrane
ustawienia. Odczytywanie stanu wyprowadze-
nia, dzięki zdefiniowanym przez firmę STMic-
roelectronics bibliotekom jest bardzo proste,
gdyż podajemy jedynie nazwę konkretnego
portu oraz pinu – instrukcje tego typu zawiera
nieskończona pętla
while(1)
z list. 1.
Producent mikrokontrolerów STM32 zale-
ca, aby wszystkie nieużywane wyprowadzenia
były skonfigurowane jako analogowe wejścia,
czego konsekwencją jest mniejsze zużycie
energii oraz większa odporność na EMI. O ile
w mniejszych jednostkach 8-bitowych miało
to mniejsze znaczenie, to należy pamiętać,
że tutaj mikrokontroler pracuje z dużo więk-
szą częstotliwością, ponadto znaczna część
rodziny STM32 posiada relatywnie dużą licz-
bę wyprowadzeń, zatem takie ich ustawienie
ma istotne znaczenie dla optymalizacji pracy
systemu.
Przerwania zewnętrzne
Jak wiadomo, aby system mikroprocesorowy
poprawnie radził sobie z przychodzącymi zda-
rzeniami, czy to ze świata zewnętrznego przez
porty we/wy, czy od wewnętrznych peryferiów,
w ogromnej większości przypadków muszą
być one obsługiwane przez przerwania. Ma to
szczególne znaczenia dla zadań krytycznych,
w których nie może być mowy o zbyt dużych
opóźnieniach w wykonaniu owego zadania, ani
tym bardziej o pominięciu zdarzenia. Często nie
zauważa się tego problemu, ponieważ wydaje
się, że na przykład cykliczne sprawdzanie w pętli
stanu danego wejścia jest wystarczające. Nieste-
ty takie podejście prędzej czy później powoduje
generowanie błędów w pracy urządzenia. Jeżeli
mamy do czynienia z projektem hobbistycz-
nym, to nie jest to specjalnie dotkliwe, jednakże
ELEKTRONIKA PRAKTYCZNA 12/2008
105
funkcja domyślnie
PODZESPOŁY
Rys. 2. Relacje pomiędzy przerwaniami
List. 3. Fragment programu wykorzystującego przerwanie zewnętrzne
int main(void)
{
RCC_Conf();
#ifdef VECT_TAB_RAM
NVIC_SetVectorTable(NVIC_VectTab_RAM, 0x0);
#else /* VECT_TAB_FLASH */
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
#endif
/* Wybranie grupy priorytetów */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
/* Konfiguracja NVIC i wlaczenie obslugi przerwania */
NVIC_InitStruct.NVIC_IRQChannel = EXTI9_5_IRQChannel;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
GPIO_Conf();
/* Poinformowanie uC o zrodle przerwania */
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource9);
/* Bedzie generowane przerwanie na zboczu opadajacym na EXTI_Line9 */
EXTI_InitStruct.EXTI_Line = EXTI_Line9;
EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStruct.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStruct);
/* Wygenerowanie przerwania EXTI_Line9 programowo */
EXTI_GenerateSWInterrupt(EXTI_Line9);
while (1);
}
/******* zawartosc pliku stm32f10x.it.c *******/
/* Funkcja oblugi przerwan zewnetrznych od 9 do 5 */
void EXTI9_5_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line9) != RESET)
{
/* Przerwanie wywoluje zmiane stanu wyprowadzenia */
GPIO_WriteBit(GPIOC, GPIO_Pin_6, (BitAction)
((1-GPIO_ReadOutputDataBit(GPIOC, GPIO_Pin_6))));
EXTI_ClearITPendingBit(EXTI_Line9);
}
}
w przypadku rozwiązań komercyjnych nie moż-
na już sobie na takie błędy pozwolić.
W mikrokontrolerach z rdzeniem Cortex M3
za obsługę przerwań odpowiada sprzętowy
kontroler przerwań (NVIC). Dzięki niemu pod-
program, który ma się wykonać po nadejściu
przerwania jest wywoływany szybciej, a sama
implementacja obsługi przerwań jest łatwiejsza,
niż miało to miejsce w przypadku rdzeni ARM7
i ARM9.
Fragment programu, który działa w oparciu
o zewnętrzne przerwanie przedstawiono na
list. 3
. Najważniejsza jest właściwa konfiguracja
MCU, natomiast program, jaki ma być docelowo
wywołany umieszcza się w pliku
stm32f10x_it.c
,
który, przy wykorzystaniu pakietu CrossWorks,
Keil lub Iar, znajduje się domyślnie w projekcie.
System ustalania priorytetów w mikrokon-
trolerach wyposażonych w rdzeń Cortex M3
jest rozdzielony na dwie części. Przerwania mają
przypisany priorytet główny, tzw.
Preemption
prioritet
, ponadto ustalany jest dodatkowy
poziom podpriorytetów (
subprioritet
). Rela-
cje pomiędzy nimi przedstawiono na
rys. 2
.
Taki podział ma uzasadnienie w działaniu: gdy
obsługiwane jest w danej chwili przerwanie
i nadejdzie zgłoszenie od innego przerwania, to
NVIC porównuje priorytety główne. Jeżeli nowe
przerwanie dysponuje wyższym priorytetem, na-
stępuje jego wywłaszczenie i ono będzie teraz
wykonywane. Wartości podpriorytetów mają
jedynie znaczenie w momencie, gdy wystąpią
dwa przerwania o takim samym priorytecie
List. 4. Odmierzanie czasu z wykorzystaniem timera SysTick
int main(void)
{
RCC_Conf();
GPIO_Conf();
NVIC_Conf();
/* SysTick bedzie taktowany z f = 72MHz/8 = 9MHz */
SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);
/* Przerwanie ma byc co 1ms, f = 9MHz czyli liczy od 9000 */
SysTick_SetReload(9000);
/* Odblokowanie przerwania od timera SysTick */
SysTick_ITConfig(ENABLE);
/* Wlaczenie timera */
SysTick_CounterCmd(SysTick_Counter_Enable);
while (1);
}
/******* zawartosc pliku stm32f10x.it.c *******/
/* Funkcja oblugi przerwania od SysTick timer */
void SysTickHandler(void)
{
if(TDelay == 500) //co 500ms
{
TDelay = 0;
/* zmienia stan portu na przeciwny */
GPIO_Write(GPIOC, (u16)~GPIO_ReadOutputData(GPIOC));
}
TDelay++;
}
głównym w tym samym czasie. W takiej sytu-
acji w pierwszej kolejności zostanie obsłużone
przerwanie o wyższym podpriorytecie. Progra-
mista wybierając tzw. grupy priorytetów (
Priori-
ty Group
) może ustalać relację pomiędzy liczbą
poziomów priorytetów głównych, a liczbą pod-
priorytetów w zależności od wymagań danej
aplikacji. Takich „zestawów” mikrokontrolery
STM32 obsługują pięć (od 0 do 4). Jeżeli w do-
celowym urządzeniu przewidujemy konieczność
106
ELEKTRONIKA PRAKTYCZNA 12/2008
Obsługa portów we/wy, przerwań i timerów w mikrokontrolerach STM32
List. 5. Fragment programu odpowiedzialny za odpowiednie skonfigurowanie układu liczni-
kowego
int main(void)
{
RCC_Conf();
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
NVIC_Conf();
GPIO_Conf();
Tab. 2. Przykładowe przypisanie prio-
rytetów i podpriorytetów
grupa
preempiton
priority
subpriority
NVIC_
PriorityGroup_0
0 bitów 4 bity
NVIC_
PriorityGroup_1
1 bit 3 bity
NVIC_
PriorityGroup_2
2 bity 2 bity
// Konfiguracja Timera 3
TIM_TimeBaseStruct.TIM_Period = 999;
TIM_TimeBaseStruct.TIM_Prescaler = 0;
TIM_TimeBaseStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStruct);
// Konfiguracja kanalu 1
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 950;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(TIM3, &TIM_OCInitStruct);
TIM_OC1PreloadConfig(TIM3, TIM_OCPreload_Enable);
// Konfiguracja kanalu 2
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 350;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC2Init(TIM3, &TIM_OCInitStruct);
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);
// Konfiguracja kanalu 3
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
NVIC_
PriorityGroup_3
3 bity 1 bit
NVIC_
PriorityGroup_4
4 bity 0 bitów
TIM_OCInitStruct.TIM_Pulse = 250;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC3Init(TIM3, &TIM_OCInitStruct);
TIM_OC3PreloadConfig(TIM3, TIM_OCPreload_Enable);
// Konfiguracja kanalu 4
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStruct.TIM_Pulse = 100;
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC4Init(TIM3, &TIM_OCInitStruct);
TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);
TIM_ARRPreloadConfig(TIM3, ENABLE);
// Wlaczenie Timera 3
TIM_Cmd(TIM3, ENABLE);
SysTick Timer
Zadaniem każdego systemu operacyjnego jest
takie zarządzenie uruchomionymi wątkami, aby
wszystkie zostały optymalnie obsłużone. Można
to zrealizować na kilka sposobów. Jednym z nich
jest cykliczne – w pewnych określonych ramach
czasowych – przełączanie kontekstu zadań. Im-
plementacje tego typu systemów operacyjnych
w mikrokontrolerach z rdzeniem Cortex M3 inży-
nierowie z firmy ARM znacznie uprościli wbudo-
wując w kontroler przerwań NVIC timer SysTick.
Jest to 24–bitowy układ licznikowy, który zliczając
w dół odmierza określone, zdefiniowane przez
programistę interwały czasowe. Każde przejście
licznika przez zero i rozpoczęcie nowego cyklu
odliczania powoduje wygenerowanie przerwa-
nia, które może zajmować się przełączaniem
kontekstów uruchomionych w systemie zadań.
Takie podejście ma też ważną zaletę, mianowicie
zawieszenie się któregoś z realizowanych zadań
nie spowoduje zawieszenia całego systemu, po-
nieważ zawieszony proces zostanie przerwany na
rzecz innych zadań przy najbliższym przepełnie-
niu timera SysTick.
Wbudowanie oddzielnego układu czasowe-
go w architekturę Cortex pozwala na dużo ła-
twiejsze przenoszenie aplikacji napisanych na
mikrokontrolery różnych producentów wyko-
rzystujących tę platformę. Dzieje się tak dlatego,
ponieważ w każdym takim mikrokontrolerze
znajduje się taki sam timer SysTick.
Oczywiście SysTick może być wykorzystywany
do różnych zadań. Konstruktor ustala wartość,
od jakiej timer ma liczyć w dół, tym samym wy-
znacza, jakie odcinki czasowe będą odmierzane.
Widać zatem, że może być on również wykorzy-
stywany do standardowego odmierzania czasu
i dla takiego przypadku, pokazanego na
list. 4
,
zostanie omówiona konfiguracja oraz urucho-
mienie timera SysTick. Aby jakikolwiek układ
czasowy w ogóle działał, musi być taktowany
jakimś sygnałem zegarowym. SysTick może zli-
czać impulsy z taką częstotliwością, z jaką pra-
cuje rdzeń mikrokontrolera, lub z tą częstotli-
wością podzieloną przez 8. Ta ostatnia opcja jest
zastosowana w omawianym przypadku, mamy
więc 9 MHz. Licznik liczy w dół, więc regulacja
odcinków czasowych, po których są generowa-
ne przerwania obywa się poprzez ustawienie
wartości początkowej, czym zajmuje się funkcja
while(1);
}
zastosowania większej liczby zagnieżdżonych
przerwań, to w takim wypadku należy wy-
brać odpowiednio wysoką grupę priorytetów.
Zasada jest taka, że im mniejszy numer grupy
priorytetów, tym mniej jest do dyspozycji prio-
rytetów głównych, a więcej podpriorytetów.
Jest to przedstawione w
tab. 2
. Wynika z niej,
że w omawianym przypadku z list. 3 jest wy-
brana opcja zawierająca dwa priorytety główne
(
preemption priority
), każdy dysponuje ośmio-
ma podpriorytetami (
subpriority
). Sumarycz-
nie otrzymujemy w ten sposób 16 możliwych
poziomów priorytetów, czyli tyle, ile obsługują
mikrokontrolery z rodziny STM32.
Po wybraniu grupy priorytetów należy odpo-
wiednio ustawić parametry wykorzystywanego
przerwania. W tym przykładzie będzie to prze-
rwanie pochodzące od przycisku użytkownika
na płycie uruchomieniowej, czyli wyprowadze-
nie PB9. Ponieważ jest to przerwanie zewnętrz-
ne o numerze 9, to trzeba poinformować NVIC
o tym, że będzie ono wykorzystywane. Doko-
nujemy tego modyfikując pole NVIC_IRQChan-
nel struktury inicjującej. Po tej czynności należy
ustalić priorytety dla tego konkretnego przerwa-
nia. Zarówno priorytet główny, jak i podprio-
rytet zostają ustawione na zera, a zatem, to
przerwanie ma pierwszeństwo w obsłużeniu.
Gdy wszystkie pola struktury są już wypełnione,
jest ona przekazywana, podobnie jak miało to
miejsce w przypadku konfiguracji GPIO, przez
referencję do funkcji inicjującej. W ten sposób
NVIC jest przygotowany na przyjęcie przerwa-
nia, należy jeszcze skonfigurować samo prze-
rwanie zgodnie z wymaganiami.
Wyprowadzenie PB9
jest przyporządkowa-
ne do zewnętrznego przerwania o numerze 9
(EXTI_Line9), stąd informujemy o tym MCU wy-
pełniając pole EXTI_Line. Kolejne dwie linie kodu
definiują zachowanie wyprowadzenia. Będzie
ono pracować w trybie przerwania oraz będzie
reagować na zbocze opadające. W chwili, gdy ta-
kie zdarzenie wystąpi, mikrokontroler przystępuje
do wykonywania funkcji
EXTI9_5_IRQHandler
.
ELEKTRONIKA PRAKTYCZNA 12/2008
107
PODZESPOŁY
SysTick_SetReload
. Do poprawnej pracy timera
pozostaje jeszcze odblokowanie jego przerwa-
nia i włączenie odliczania. Funkcja obsługująca
przerwanie to
SysTickHandler.
Jej cykliczne wy-
woływania powodują odliczenie żądanego cza-
su i okresową zmianę stanu portu GPIOC, a tym
samym miganie diodami LED.
na
list. 5
. Oprócz standardowej konfiguracji
sygnałów zegarowych, która jest wykonywa-
na przez funkcję
RCC_Conf
, należy włączyć
taktowanie timera i GPIO, natomiast funkcja
GPIO_Conf
, której zadaniem jest pełne prze-
mapowanie Timera 3 jest identyczna z przed-
stawioną wcześniej na list. 2. W następnych
krokach definiujemy podstawowe parametry
pracy licznika. Będzie on pracował jako licznik
w górę z częstotliwością taktowania 36 MHz
(nie dzielimy sygnału zegarowego i nie wy-
korzystujemy preskalera). Wartość, do jakiej
będzie liczył timer to 999, po czym nastąpi
przepełnienie i proces liczenia rozpocznie się
od nowa. Przy takich ustawieniach częstotli-
wość generowanego przebiegu PWM wyniesie:
f=36 [MHz]/(999+1)=36 [kHz]. Kolejnym kro-
kiem jest konfiguracja poszczególnych kanałów
timera. Informujemy MCU o tym, że poszcze-
gólne kanały będą pracować w trybie PWM,
długość impulsu będzie wynosiła odpowiednio
(licząc od kanału pierwszego): 950, 350, 250,
100 taktów licznika. Aktywnym stanem jest
stan wysoki, co oznacza, że np. dla kanału dru-
giego stan wysoki będzie panował na wyjściu
PC7 przez 350 taktów, a pozostałe 650 to stan
niski, zatem współczynnik wypełnienia wynie-
sie w tym przypadku 35%. Po zaprogramowa-
niu mikrokontrolera i uruchomieniu układu,
diody od LD1 do LD4 będą świecić z różną in-
tensywnością.
Na zakończenie
Z przedstawionych pobieżnie przykładów
widać, jak wielkie możliwości drzemią we
współczesnych mikrokontrolerach 32–bitowych.
Zarówno projektanci rdzenia Cortex M3, jak
i producent układów STM32, firma STMicro-
electronics dołożyli wszelkich starań, aby czas
potrzebny na napisanie i uruchomienie aplikacji
był możliwie krótki. Dzięki temu otrzymujemy
do dyspozycji świetnie wyposażone mikrokon-
trolery, które poza tym można stosunkowo ła-
two programować. Ponadto układy z rdzeniami
Cortex znajdują się w ofercie coraz większej licz-
by producentów, zatem należy się spodziewać
dalszych spadków ich cen i wzrostu możliwości.
Wyścig trwa...
Timery
Pod względem wyposażenia w układy
czasowo–licznikowe, mikrokontrolery STM32
prezentują się dość okazale. W sumie mamy
do dyspozycji aż siedem timerów (wliczając
SysTick), które mogą pracować w różnych
konfiguracjach. Ponieważ możliwość odmie-
rzania czasu została już przedstawiona przy
okazji omawiania timera SysTick, to teraz zaj-
miemy się generacją czterech sygnałów PWM
o różnym współczynniku wypełnienia. Do tego
celu wykorzystamy Timer 3 wraz z jego czte-
rema kanałami. Pozwoli to na wygenerowanie
żądanych sygnałów, z tym, że rzecz jasna będą
one miały taki sam okres. Fragment programu
odpowiedzialny za odpowiednie skonfiguro-
wanie układu licznikowego przedstawiono
Krzysztof Paprocki
paprocki.krzysztof@gmail.com
R
E
K
L
A
M
A
108
ELEKTRONIKA PRAKTYCZNA 12/2008
Plik z chomika:
kaczor1000
Inne pliki z tego folderu:
Peczarski M. - Mikrokontrolery STM 32 w sieci Ethernet w przykładach.pdf
(12152 KB)
The Insaider's Guide To The STM32.pdf
(6627 KB)
STM32Butterfly.pdf
(2632 KB)
Reference manual.pdf
(8589 KB)
Alternatywna metoda programowania pamięci Flash mikrokontrolerów STM32(1).pdf
(2108 KB)
Inne foldery tego chomika:
8051
Arduino
Bascom
CNC
DipTrace
Zgłoś jeśli
naruszono regulamin