Proste pytania o programowanie STM32 w języku C.

Tu możesz pisać o swoich problemach z pisaniem programów w języku C/C++ dla STM.
Awatar użytkownika
0110101101101101
Posty: 18
Rejestracja: sobota 12 maja 2018, 14:55

Proste pytania o programowanie STM32 w języku C.

Postautor: 0110101101101101 » czwartek 07 cze 2018, 07:47

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. :lol:

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?

Awatar użytkownika
inż.wielki
User
User
Posty: 307
Rejestracja: niedziela 20 gru 2015, 23:11

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: inż.wielki » czwartek 07 cze 2018, 07:51

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 :D

Awatar użytkownika
0110101101101101
Posty: 18
Rejestracja: sobota 12 maja 2018, 14:55

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: 0110101101101101 » czwartek 07 cze 2018, 08:17

Tyle czasu tego szukałem w Reference Manual'u i dopiero teraz znalazłem.... :mrgreen:

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... :lol:

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!

Awatar użytkownika
xor
User
User
Posty: 169
Rejestracja: poniedziałek 05 wrz 2016, 21:44

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: xor » czwartek 07 cze 2018, 08:27

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.

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ń.

Awatar użytkownika
0110101101101101
Posty: 18
Rejestracja: sobota 12 maja 2018, 14:55

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: 0110101101101101 » piątek 22 cze 2018, 10:44

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:

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?

Awatar użytkownika
0110101101101101
Posty: 18
Rejestracja: sobota 12 maja 2018, 14:55

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: 0110101101101101 » piątek 22 cze 2018, 11:47

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..

Awatar użytkownika
squeez
User
User
Posty: 211
Rejestracja: czwartek 04 lut 2016, 10:13

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: squeez » piątek 22 cze 2018, 13:56

A może masz PLL ustawione przy taktowaniu procesora?

Awatar użytkownika
0110101101101101
Posty: 18
Rejestracja: sobota 12 maja 2018, 14:55

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: 0110101101101101 » piątek 22 cze 2018, 14:52

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!

Awatar użytkownika
mokrowski
User
User
Posty: 190
Rejestracja: czwartek 08 paź 2015, 20:50
Lokalizacja: Tam gdzie Centymetro

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: mokrowski » piątek 22 cze 2018, 21:06

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

Awatar użytkownika
inż.wielki
User
User
Posty: 307
Rejestracja: niedziela 20 gru 2015, 23:11

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: inż.wielki » sobota 23 cze 2018, 13:02

mokrowski pisze:Zamiast while(1) standard MISRA dla embedded zaleca for(;;)

Ma to jakaś przewagę nad while(1)

Awatar użytkownika
0110101101101101
Posty: 18
Rejestracja: sobota 12 maja 2018, 14:55

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: 0110101101101101 » sobota 23 cze 2018, 14:37

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);

Awatar użytkownika
mokrowski
User
User
Posty: 190
Rejestracja: czwartek 08 paź 2015, 20:50
Lokalizacja: Tam gdzie Centymetro

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: mokrowski » sobota 23 cze 2018, 22:03

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!
,,Myślenie nie jest łatwe, ale można się do niego przyzwyczaić" - Alan Alexander Milne: Kubuś Puchatek

Awatar użytkownika
inż.wielki
User
User
Posty: 307
Rejestracja: niedziela 20 gru 2015, 23:11

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: inż.wielki » sobota 23 cze 2018, 22:40

Ma to sens co piszesz. Będę o tym pamiętać przy następnych implementacjach :)

Awatar użytkownika
0110101101101101
Posty: 18
Rejestracja: sobota 12 maja 2018, 14:55

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: 0110101101101101 » środa 11 lip 2018, 13:55

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:

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?

Awatar użytkownika
0110101101101101
Posty: 18
Rejestracja: sobota 12 maja 2018, 14:55

Re: Proste pytania o programowanie STM32 w języku C.

Postautor: 0110101101101101 » czwartek 12 lip 2018, 07:11

Co się naszukałem, to moje, ale znalazłem i radość z tego powodu jest przeogromna. :D

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. :lol:


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 7 gości