Proste pytania o programowanie STM32 w języku C.
- 0110101101101101
- Posty: 18
- Rejestracja: sobota 12 maja 2018, 14:55
Proste pytania o programowanie STM32 w języku C.
Witajcie, jako, że dopiero zaczynam poznawać rodzinę STM to w związku z tym nasuwa mi się na język mnóstwo pytań i nieścisłości.
Pozwoliłem sobie założyć temat dotyczący takich prostych pytań nowicjusza o różne peryferia. Nie chcę pytać na czacie, żeby te pytania z odpowiedziami zostały dla potomnych, a zakładanie osobnego tematu dla każdego tematu mogłoby Was szybko wyprowadzić z równowagi.
Więc, żeby nie zakładać pustego tematu, to już mam jedno pytanie.
[PYTANIE 1]
Jak to jest z obsługą przerwań zewnętrznych? Dla testów i nauki chciałem na przerwaniu EXTI generowanym przez przycisk zmieniać stan diody.
Gdy ręcznie w przerwaniu nie kasowałem bitu "Pending Bit", to przerwanie wykonało się tylko jeden, jedyny raz, a kolejne naciśnięcia przycisku nic nie zmieniały.
Gdy w obsłudze przerwania dodałem linijkę EXTI -> PR = EXTI_PR_PR13;, to za każdym naciśnięciem przycisku prawidłowo zmienia się stan diody.
Czy automatycznie po wejściu do obsługi tego przerwania ten bit nie powinien się automatycznie kasować? Bo jak dobrze rozumiem, to jest to flaga zajętości, która blokuje przerwania z danej linii, tak?
Pozwoliłem sobie założyć temat dotyczący takich prostych pytań nowicjusza o różne peryferia. Nie chcę pytać na czacie, żeby te pytania z odpowiedziami zostały dla potomnych, a zakładanie osobnego tematu dla każdego tematu mogłoby Was szybko wyprowadzić z równowagi.
Więc, żeby nie zakładać pustego tematu, to już mam jedno pytanie.
[PYTANIE 1]
Jak to jest z obsługą przerwań zewnętrznych? Dla testów i nauki chciałem na przerwaniu EXTI generowanym przez przycisk zmieniać stan diody.
Gdy ręcznie w przerwaniu nie kasowałem bitu "Pending Bit", to przerwanie wykonało się tylko jeden, jedyny raz, a kolejne naciśnięcia przycisku nic nie zmieniały.
Gdy w obsłudze przerwania dodałem linijkę EXTI -> PR = EXTI_PR_PR13;, to za każdym naciśnięciem przycisku prawidłowo zmienia się stan diody.
Czy automatycznie po wejściu do obsługi tego przerwania ten bit nie powinien się automatycznie kasować? Bo jak dobrze rozumiem, to jest to flaga zajętości, która blokuje przerwania z danej linii, tak?
- inż.wielki
- User
- Posty: 307
- Rejestracja: niedziela 20 gru 2015, 23:11
Re: Proste pytania o programowanie STM32 w języku C.
W zależności od procka taka flaga może ale nie musi być kasowana. Zazwyczaj trzeba o to zadbać samemu. Nie pamiętam dokładnie, bo STM'ów nie ruszałem dłuższą chwilę, ale tam jest jeden uchwyt dla danego typu przerwań. Tzn dla wszystkich pinów o indeksie 0, 1, 2 itd... Więc w przerwaniu możesz też sprawdzać, który pin to wywołał. Dlatego nie jest kasowane. Ale mogę się mylić, poczekajmy na wypowiedź mądrzejszych
- 0110101101101101
- Posty: 18
- Rejestracja: sobota 12 maja 2018, 14:55
Re: Proste pytania o programowanie STM32 w języku C.
Tyle czasu tego szukałem w Reference Manual'u i dopiero teraz znalazłem....
W opisie działania EXTI jest zapis:
No. Czasami się czegoś nie zauważy i kilka godzin się szuka po dokumentacji...
Dokładnie jest tak jak piszesz - np. piny 10..15 są obsługiwane przez jeden wektor przerwania. Dzięki za rozjaśnienie umysłu!
W opisie działania EXTI jest zapis:
When the selected edge occurs on the external interrupt line, an interrupt request is
generated. The pending bit corresponding to the interrupt line is also set. This request is
reset by writing a ‘1’ in the pending register
No. Czasami się czegoś nie zauważy i kilka godzin się szuka po dokumentacji...
inż.wielki pisze:Nie pamiętam dokładnie, bo STM'ów nie ruszałem dłuższą chwilę, ale tam jest jeden uchwyt dla danego typu przerwań. Tzn dla wszystkich pinów o indeksie 0, 1, 2 itd... Więc w przerwaniu możesz też sprawdzać, który pin to wywołał. Dlatego nie jest kasowane.
Dokładnie jest tak jak piszesz - np. piny 10..15 są obsługiwane przez jeden wektor przerwania. Dzięki za rozjaśnienie umysłu!
Re: Proste pytania o programowanie STM32 w języku C.
Niemal wszystkie przerwania w STM32F103 (zdaje się, że takim prockiem się bawisz?) trzeba kasować Jedyne które w tej chwili pamiętam, gdzie nie jest to potrzebne to przerwanie USARTa, tam kasowanie następuje automatycznie wraz z przeczytaniem/zapisem rejestru danych.
Linie 5-9 też są zgrupowane, ale już 0-4 mają osobne wektory przerwań.
Dokładnie jest tak jak piszesz - np. piny 10..15 są obsługiwane przez jeden wektor przerwania.
Linie 5-9 też są zgrupowane, ale już 0-4 mają osobne wektory przerwań.
- 0110101101101101
- Posty: 18
- Rejestracja: sobota 12 maja 2018, 14:55
Re: Proste pytania o programowanie STM32 w języku C.
No to znowu mam jakiś problem z ogarnięciem Timera.
Tym razem timer numer 2 ma odliczać 2 sekundy i po tym czasie jednorazowo uruchomić pomiar za pomocą ADC. Drugą część związaną z ADC ogarnąłem bez żadnego problemu i działa, ale timer wysyła sygnał TRGO na ADC od razu po jego uruchomieniu ( " TIM2->CR1 = TIM_CR1_CEN") i tym sposobem pomiar na ADC następuje od razu a nie po 2s.
Zacząłem modyfikować program, bo myślałem, że zamieszałem coś z tym ADC, ale jednak problem jest w konfiguracji timera.
Przedstawiam kod dla stm32f103rb:
Gdzie popełniam błąd? Co powinienem zmienić, żeby pierwsze (i w sumie jedyne, bo to one pulse mode) przerwanie uruchomiło się po tych 2 sekundach?
Tym razem timer numer 2 ma odliczać 2 sekundy i po tym czasie jednorazowo uruchomić pomiar za pomocą ADC. Drugą część związaną z ADC ogarnąłem bez żadnego problemu i działa, ale timer wysyła sygnał TRGO na ADC od razu po jego uruchomieniu ( " TIM2->CR1 = TIM_CR1_CEN") i tym sposobem pomiar na ADC następuje od razu a nie po 2s.
Zacząłem modyfikować program, bo myślałem, że zamieszałem coś z tym ADC, ale jednak problem jest w konfiguracji timera.
Przedstawiam kod dla stm32f103rb:
Kod: Zaznacz cały
int main(void)
{
RCC->APB1ENR = RCC_APB1ENR_TIM2EN;
TIM2->PSC = 8000-1;
TIM2->ARR = 2000-1;
TIM2->DIER = TIM_DIER_UIE;
NVIC_ClearPendingIRQ(TIM2_IRQn);
NVIC_EnableIRQ(TIM2_IRQn);
TIM2->CR1 = TIM_CR1_CEN | TIM_CR1_OPM;
while(1);
}
void TIM2_IRQHandler(void)
{
TO PRZERWANIE URUCHAMIA SIĘ OD RAZU PO WŁĄCZENIU TIMERA.
}
Gdzie popełniam błąd? Co powinienem zmienić, żeby pierwsze (i w sumie jedyne, bo to one pulse mode) przerwanie uruchomiło się po tych 2 sekundach?
- 0110101101101101
- Posty: 18
- Rejestracja: sobota 12 maja 2018, 14:55
Re: Proste pytania o programowanie STM32 w języku C.
Chyba doszedłem gdzie jest błąd, ale kompletnie nie wiem dlaczego.
Jak zmieniłem TIM2->ARR z 1999 na 19999, to kolejne przerwania występują co 2 sekundy.
Skoro taktowanie mikroprocesora to 8MHZ, to dla timera taktowanie bazowe to też 8MHz, tak? Preskaler ustawiony na 8000, wartość przeładowania na 2000, więc teoretycznie:
częstotliwość przerwania = 8MHZ / 8000 * 2000 = 0,5HZ, więc okres to 1/f = 1/0,5HZ = 2s.
Istnieje jeszcze jakiś bit, który ma wpływ na preskaler timera? bo mój wygląda jakby był podzielony przez 10..
Jak zmieniłem TIM2->ARR z 1999 na 19999, to kolejne przerwania występują co 2 sekundy.
Skoro taktowanie mikroprocesora to 8MHZ, to dla timera taktowanie bazowe to też 8MHz, tak? Preskaler ustawiony na 8000, wartość przeładowania na 2000, więc teoretycznie:
częstotliwość przerwania = 8MHZ / 8000 * 2000 = 0,5HZ, więc okres to 1/f = 1/0,5HZ = 2s.
Istnieje jeszcze jakiś bit, który ma wpływ na preskaler timera? bo mój wygląda jakby był podzielony przez 10..
Re: Proste pytania o programowanie STM32 w języku C.
A może masz PLL ustawione przy taktowaniu procesora?
- 0110101101101101
- Posty: 18
- Rejestracja: sobota 12 maja 2018, 14:55
Re: Proste pytania o programowanie STM32 w języku C.
Właśnie to było problemem.
Rejestr RCC_CFGR i bity SW ustawione na 10 - więc PLL, a powinno być 00.
Tylko nie wiem, dlaczego to się zmieniło, skoro nigdy tego rejestru nie używałem.
Dzięki!
Rejestr RCC_CFGR i bity SW ustawione na 10 - więc PLL, a powinno być 00.
Tylko nie wiem, dlaczego to się zmieniło, skoro nigdy tego rejestru nie używałem.
Dzięki!
- mokrowski
- User
- Posty: 190
- Rejestracja: czwartek 08 paź 2015, 20:50
- Lokalizacja: Tam gdzie Centymetro
Re: Proste pytania o programowanie STM32 w języku C.
Zamiast while(1) standard MISRA dla embedded zaleca for(;;)
,,Myślenie nie jest łatwe, ale można się do niego przyzwyczaić" - Alan Alexander Milne: Kubuś Puchatek
- inż.wielki
- User
- Posty: 307
- Rejestracja: niedziela 20 gru 2015, 23:11
Re: Proste pytania o programowanie STM32 w języku C.
mokrowski pisze:Zamiast while(1) standard MISRA dla embedded zaleca for(;;)
Ma to jakaś przewagę nad while(1)
- 0110101101101101
- Posty: 18
- Rejestracja: sobota 12 maja 2018, 14:55
Re: Proste pytania o programowanie STM32 w języku C.
Okej, zwrócę na to uwagę i zacznę używać for(;;), ale macie jakieś wytłumaczenie dlaczego tak jest?
Samo środowisko attolic po stworzeniu nowego projektu tworząc szkielet kodu w funkcji main używa while(1);
Samo środowisko attolic po stworzeniu nowego projektu tworząc szkielet kodu w funkcji main używa while(1);
- mokrowski
- User
- Posty: 190
- Rejestracja: czwartek 08 paź 2015, 20:50
- Lokalizacja: Tam gdzie Centymetro
Re: Proste pytania o programowanie STM32 w języku C.
Wg. standardu while(1) może powstać przez przypadek w trakcie modyfikowania kodu albo jak ja to nazywam "macanda kodu" (ciekawe co będzie jak tu poprawę i _na_razie_ tu to wpiszę żeby coś robiło). Poza tym poprawniej było by while(true). Z kolei idiota może zdefiniować makro dla true na jakieś "wesołe"
W efekcie lepiej jawnie powiedzieć: "chcę mieć pętlę z brakiem warunków startu i stopu i bez inkrementacji czegokolwiek" niż "oto while z warunkiem true". To drugie jednak mniej mówi o intencjach. Ja się nad tym raz zastanowiłem i wybrałem for(;;) także ze względu na narzędzia które nie zawracają gitary taką błahostką. No i o 1 znak mniej od while(1)
Podobnie z while(jakiś_fajny_warunek_sprawdzany_w_kółko_aż_niespełniony). Wpisanie tam średnika na końcu to jednak szukanie problemu. Lepiej klepnąć pusty blok przez { i } i wiedzieć że ktoś to zrobił celowo. I tak kompilator to spłaszczy.
Z takich drobnostek później szybciej czyta się kod i łatwiej go modyfikuje.
Kod jest dla ludzi a nie maszyn!
W efekcie lepiej jawnie powiedzieć: "chcę mieć pętlę z brakiem warunków startu i stopu i bez inkrementacji czegokolwiek" niż "oto while z warunkiem true". To drugie jednak mniej mówi o intencjach. Ja się nad tym raz zastanowiłem i wybrałem for(;;) także ze względu na narzędzia które nie zawracają gitary taką błahostką. No i o 1 znak mniej od while(1)
Podobnie z while(jakiś_fajny_warunek_sprawdzany_w_kółko_aż_niespełniony). Wpisanie tam średnika na końcu to jednak szukanie problemu. Lepiej klepnąć pusty blok przez { i } i wiedzieć że ktoś to zrobił celowo. I tak kompilator to spłaszczy.
Z takich drobnostek później szybciej czyta się kod i łatwiej go modyfikuje.
Kod jest dla ludzi a nie maszyn!
,,Myślenie nie jest łatwe, ale można się do niego przyzwyczaić" - Alan Alexander Milne: Kubuś Puchatek
- inż.wielki
- User
- Posty: 307
- Rejestracja: niedziela 20 gru 2015, 23:11
Re: Proste pytania o programowanie STM32 w języku C.
Ma to sens co piszesz. Będę o tym pamiętać przy następnych implementacjach
- 0110101101101101
- Posty: 18
- Rejestracja: sobota 12 maja 2018, 14:55
Re: Proste pytania o programowanie STM32 w języku C.
No to mam kolejny mały problem, nad którym dłuższy czas już główkuję i nic nie mogę wymyślić.
Tym razem chodzi o obsługę zegara czasu rzeczywistego RTC. Taktowany jest on u mnie za pomocą LSI, ustawione przerwania "sekundowe" i w obsłudze przerwania wyrzucam zawartość rejestru RTC -> CNTL na wyświetlacz LCD 2x16.
Wszystko działa dobrze, ale tylko przy pierwszym uruchomieniu, to znaczy jeśli w środowisku Attolic kliknę Debug, wgram program - wszystko działa. Wyjdę z trybu Debug, to również wszystko działa jak należy. Zresetuję przyciskiem, wszystko jest okej, ale wystarczy na chwilkę wyłączyć zasilanie i RTC już nie rusza.
Nie używam zewnętrznego zasilania dla nóżki VBAT, bo na razie sobie to testuję i chciałbym, żeby po każdym odłączeniu i podłączeniu zasilania wszystko się inicjalizowało od nowa.
Przedstawiam kod programu:
Podpowiecie co by tu zmienić, żeby po odłączeniu i podłączeniu zasilania rtc ruszał od zera?
Tym razem chodzi o obsługę zegara czasu rzeczywistego RTC. Taktowany jest on u mnie za pomocą LSI, ustawione przerwania "sekundowe" i w obsłudze przerwania wyrzucam zawartość rejestru RTC -> CNTL na wyświetlacz LCD 2x16.
Wszystko działa dobrze, ale tylko przy pierwszym uruchomieniu, to znaczy jeśli w środowisku Attolic kliknę Debug, wgram program - wszystko działa. Wyjdę z trybu Debug, to również wszystko działa jak należy. Zresetuję przyciskiem, wszystko jest okej, ale wystarczy na chwilkę wyłączyć zasilanie i RTC już nie rusza.
Nie używam zewnętrznego zasilania dla nóżki VBAT, bo na razie sobie to testuję i chciałbym, żeby po każdym odłączeniu i podłączeniu zasilania wszystko się inicjalizowało od nowa.
Przedstawiam kod programu:
Kod: Zaznacz cały
#include "main.h"
#include "lcd4bit.h"
#include <stdlib.h>
#include <stdio.h>
void RCC_Conf(void);
void GPIO_Conf(void);
int main(void)
{
RCC->APB1ENR |= (RCC_APB1ENR_PWREN | RCC_APB1ENR_BKPEN);
RCC->CSR |= RCC_CSR_LSION;
while ((RCC->CSR & RCC_CSR_LSIRDY) == 0);
RCC->BDCR |= RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL_LSI;
PWR->CR = PWR_CR_DBP;
RTC->CRH = RTC_CRH_SECIE;
while(!(RTC_CRL_RTOFF));
RTC->CRL = RTC_CRL_CNF;
RTC->PRLL = 40000-1;
RTC->PRLH = 0;
RTC->CNTL = 40;
RTC->CNTH = 0;
RTC->ALRL = 0;
RTC->ALRH = 0;
RTC->CRL &=~ RTC_CRL_CNF;
while(!(RTC_CRL_RTOFF));
RCC_Conf();
NVIC_Conf();
GPIO_Conf();
LCD_Init();
LCD_Clear();
LCD_SendText("TEST TEST TEST");
NVIC_ClearPendingIRQ(RTC_IRQn);
NVIC_EnableIRQ(RTC_IRQn);
LCD_Clear();
LCD_GoTo(0,0);
LCD_SendText("GODZ: ");
while (1)
{
}
}
void RCC_Conf(void)
{
ErrorStatus HSEStartUpStatus;
// Reset ustawien RCC
RCC_DeInit();
// Wlacz HSE
RCC_HSEConfig(RCC_HSE_ON);
// Czekaj za HSE bedzie gotowy
HSEStartUpStatus = RCC_WaitForHSEStartUp();
if(HSEStartUpStatus == SUCCESS)
{
FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable);
// zwloka dla pamieci Flash
FLASH_SetLatency(FLASH_Latency_2);
// HCLK = SYSCLK
RCC_HCLKConfig(RCC_SYSCLK_Div1);
// PCLK2 = HCLK
RCC_PCLK2Config(RCC_HCLK_Div1);
// PCLK1 = HCLK/2
RCC_PCLK1Config(RCC_HCLK_Div2);
// PLLCLK = 8MHz * 9 = 72 MHz
RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
// Wlacz PLL
RCC_PLLCmd(ENABLE);
// Czekaj az PLL poprawnie sie uruchomi
while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);
// PLL bedzie zrodlem sygnalu zegarowego
RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
// Czekaj az PLL bedzie sygnalem zegarowym systemu
while(RCC_GetSYSCLKSource() != 0x08);
}
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
}
void GPIO_Conf(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// Wlacz obsluge wszyskich diod LED (LD1 do LD8)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_ResetBits(GPIOB, GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15);
}
__attribute__((interrupt)) void RTC_IRQHandler(void)
{
itoa((RTC->CNTL), sekundy, 10);
LCD_GoTo(0,9);
LCD_SendText(sekundy);
}
Podpowiecie co by tu zmienić, żeby po odłączeniu i podłączeniu zasilania rtc ruszał od zera?
- 0110101101101101
- Posty: 18
- Rejestracja: sobota 12 maja 2018, 14:55
Re: Proste pytania o programowanie STM32 w języku C.
Co się naszukałem, to moje, ale znalazłem i radość z tego powodu jest przeogromna.
Ustawienie bitu:
Jest on odpowiedzialny za wyłączenie dostępu do rejestrów RTC i Backup, żeby (podejrzewam) przypadkowo czegoś nie nadpisać i musi się znajdować przed modyfikacjami rejestrów z domeny Backup, więc:
U mnie było odwrotnie, więc najpierw ustawiałem 2 bity w domenie Backup, a dopiero później włączałem dostęp do tych rejestrów.
Ustawienie bitu:
Kod: Zaznacz cały
PWR->CR = PWR_CR_DBP;
Jest on odpowiedzialny za wyłączenie dostępu do rejestrów RTC i Backup, żeby (podejrzewam) przypadkowo czegoś nie nadpisać i musi się znajdować przed modyfikacjami rejestrów z domeny Backup, więc:
Kod: Zaznacz cały
RCC->BDCR |= RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL_LSI;
U mnie było odwrotnie, więc najpierw ustawiałem 2 bity w domenie Backup, a dopiero później włączałem dostęp do tych rejestrów.
Wróć do „Programowanie STM w C/C++”
Kto jest online
Użytkownicy przeglądający to forum: Obecnie na forum nie ma żadnego zarejestrowanego użytkownika i 0 gości