STM32F411 sprzętowa zmiana polaryzacji 2 pinów co 64 s

Pytanie

Jak wygląda kod na STM32F411,sprzętowy , zmiana polaryzacji między 2 pinami ,LOW 64 sekund ,HIGH 64 sekund , okres 128 sekund ,DutyCycle 50%

Odpowiedź Sztucznej Inteligencji


Bezpośrednia odpowiedź na pytanie

Tak — na STM32F411 można to zrobić całkowicie sprzętowo, bez przerwań i bez pętli opóźniających.

Kluczowe punkty

  • Do okresu 128 s należy użyć 32-bitowego timera, czyli TIM2 albo TIM5.
  • Najprościej ustawić:
    • CH1 w trybie PWM mode 1
    • CH2 w trybie PWM mode 2
  • Wtedy dwa piny będą miały przeciwną polaryzację:
    • pierwszy pin: HIGH 64 s, LOW 64 s
    • drugi pin: LOW 64 s, HIGH 64 s
  • To jest rozwiązanie rzeczywiście sprzętowe — po inicjalizacji CPU nie musi nic robić.

Poniżej masz poprawny przykład dla:

  • TIM2
  • PA0 = TIM2_CH1
  • PA1 = TIM2_CH2

Bezpośrednia odpowiedź na pytanie Szczegółowa analiza problemu

1. Co dokładnie chcesz uzyskać

Zakładam, że chodzi o dwa piny pracujące przeciwsobnie:

Czas Pin 1 Pin 2
0…64 s HIGH LOW
64…128 s LOW HIGH
potem cykl od nowa

Czyli:

  • okres: 128 s
  • wypełnienie: 50%
  • przesunięcie/faza: 180°

To nie wymaga programu użytkownika w czasie pracy — wystarczy dobrze skonfigurować timer.


2. Dlaczego nie TIM3/TIM4/TIM1

W wielu odpowiedziach internetowych pojawia się pomysł użycia:

  • przerwań co 1 s
  • licznika programowego
  • timera 16-bitowego

To działa funkcjonalnie, ale:

  • nie jest czysto sprzętowe,
  • obciąża CPU,
  • zależy od kodu w ISR,
  • jest gorsze od rozwiązania opartego o TIM2/TIM5.

Dla okresu 128 s najlepszy wybór to:

  • TIM2 albo
  • TIM5

bo są 32-bitowe.


3. Obliczenia timera

Załóżmy typową konfigurację STM32F411:

  • zegar timera TIM2 = 84 MHz

Dobieramy wygodne taktowanie licznika:

\[
f_{CNT} = 2000\ \text{Hz}
\]

Wtedy jeden krok licznika trwa:

\[
T_{tick} = \frac{1}{2000} = 0.5\ \text{ms}
\]

Preskaler:

\[
PSC = \frac{84\,000\,000}{2000} - 1 = 41999
\]

Okres 128 s:

\[
ARR = 128 \cdot 2000 - 1 = 255999
\]

Połowa okresu, czyli 64 s:

\[
CCR = 64 \cdot 2000 = 128000
\]

Czyli finalnie:

  • PSC = 41999
  • ARR = 255999
  • CCR1 = 128000
  • CCR2 = 128000

4. Zasada działania dwóch kanałów

Ustawiamy:

  • CH1 = PWM mode 1
  • CH2 = PWM mode 2

Dla tej samej wartości CCR dostajesz automatycznie sygnały komplementarne:

  • PWM1: aktywne, gdy CNT < CCR
  • PWM2: aktywne, gdy CNT >= CCR

W praktyce:

  • przez pierwsze 64 s:
    • CH1 = 1
    • CH2 = 0
  • przez kolejne 64 s:
    • CH1 = 0
    • CH2 = 1

Dokładnie to, czego potrzebujesz.


5. Kod bare-metal / rejestrowy

Poniższy kod jest najbliższy pojęciu „sprzętowy”:

#include "stm32f4xx.h"
static void TIM2_Complementary_128s_Init(void)
{
    /* 1. Zegary dla GPIOA i TIM2 */
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    RCC->APB1ENR |= RCC_APB1ENR_TIM2EN;
    /* Krótka zwłoka po włączeniu zegara */
    (void)RCC->AHB1ENR;
    (void)RCC->APB1ENR;
    /* 2. PA0 = TIM2_CH1, PA1 = TIM2_CH2, AF1 */
    GPIOA->MODER &= ~((3U << (0 * 2)) | (3U << (1 * 2)));
    GPIOA->MODER |=  ((2U << (0 * 2)) | (2U << (1 * 2)));   // Alternate Function
    GPIOA->OTYPER &= ~((1U << 0) | (1U << 1));              // Push-pull
    GPIOA->OSPEEDR &= ~((3U << (0 * 2)) | (3U << (1 * 2))); // Low speed wystarczy
    GPIOA->PUPDR &= ~((3U << (0 * 2)) | (3U << (1 * 2)));   // No pull
    GPIOA->AFR[0] &= ~((0xFU << (0 * 4)) | (0xFU << (1 * 4)));
    GPIOA->AFR[0] |=  ((0x1U << (0 * 4)) | (0x1U << (1 * 4))); // AF1 = TIM2
    /* 3. Wyłączenie timera na czas konfiguracji */
    TIM2->CR1 = 0;
    TIM2->CR2 = 0;
    TIM2->SMCR = 0;
    TIM2->DIER = 0;   // bez przerwań
    /* 4. Parametry czasu:
       TIM2CLK = 84 MHz
       PSC     = 41999  => licznik 2000 Hz
       ARR     = 255999 => okres 128 s
       CCRx    = 128000 => 64 s */
    TIM2->PSC  = 41999;
    TIM2->ARR  = 255999;
    TIM2->CCR1 = 128000;
    TIM2->CCR2 = 128000;
    /* 5. Kanał 1 = PWM1, kanał 2 = PWM2 */
    TIM2->CCMR1 = 0;
    /* CH1: PWM mode 1 + preload */
    TIM2->CCMR1 |= (6U << TIM_CCMR1_OC1M_Pos);
    TIM2->CCMR1 |= TIM_CCMR1_OC1PE;
    /* CH2: PWM mode 2 + preload */
    TIM2->CCMR1 |= (7U << TIM_CCMR1_OC2M_Pos);
    TIM2->CCMR1 |= TIM_CCMR1_OC2PE;
    /* 6. Polaryzacja normalna, włączenie wyjść CH1 i CH2 */
    TIM2->CCER = 0;
    TIM2->CCER |= TIM_CCER_CC1E;
    TIM2->CCER |= TIM_CCER_CC2E;
    /* 7. Preload ARR */
    TIM2->CR1 |= TIM_CR1_ARPE;
    /* 8. Załadowanie rejestrów do aktywnych shadow registers */
    TIM2->EGR |= TIM_EGR_UG;
    /* 9. Start timera */
    TIM2->CR1 |= TIM_CR1_CEN;
}
int main(void)
{
    /* Zakładam, że zegar systemowy jest już skonfigurowany
       np. przez startup / SystemInit / CubeMX */
    TIM2_Complementary_128s_Init();
    while (1)
    {
        /* CPU nic nie musi robić */
        __NOP();
    }
}

6. Co otrzymasz na pinach

Po uruchomieniu:

  • PA0 / TIM2_CH1:
    • HIGH przez 64 s
    • LOW przez 64 s
  • PA1 / TIM2_CH2:
    • LOW przez 64 s
    • HIGH przez 64 s

Okres:
\[
128\ \text{s}
\]

Wypełnienie:
\[
50\%
\]


7. Jeśli chcesz odwrotny stan początkowy

Jeżeli chcesz, aby na starcie było:

  • PA0 = LOW
  • PA1 = HIGH

to masz kilka możliwości:

  1. zamienić role PWM1/PWM2 między kanałami,
  2. odwrócić polaryzację kanału przez CCxP,
  3. zamienić fizycznie piny w projekcie.

Najczyściej:

  • CH1 ustaw jako PWM2
  • CH2 ustaw jako PWM1

8. Wersja HAL — jeśli pracujesz w CubeMX / CubeIDE

Jeżeli wolisz HAL, logika jest identyczna:

  • timer: TIM2
  • kanał 1: TIM_OCMODE_PWM1
  • kanał 2: TIM_OCMODE_PWM2
  • Prescaler = 41999
  • Period = 255999
  • Pulse = 128000

Minimalny szkic:

htim2.Instance = TIM2;
htim2.Init.Prescaler = 41999;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 255999;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_PWM_Init(&htim2);
TIM_OC_InitTypeDef sConfigOC = {0};
/* CH1 */
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 128000;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1);
/* CH2 */
sConfigOC.OCMode = TIM_OCMODE_PWM2;
sConfigOC.Pulse = 128000;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_2);

Do tego oczywiście GPIO:

  • PA0 i PA1
  • tryb AF_PP
  • GPIO_AF1_TIM2

Aktualne informacje i trendy

Najważniejsza korekta względem typowych odpowiedzi

W praktyce spotyka się dwie błędne sugestie:

  • użycie przerwań co 1 s i przełączanie GPIO programowo,
  • użycie 16-bitowych timerów jako rozwiązania „sprzętowego”.

Z punktu widzenia inżynierskiego:

  • przerwania to już nie jest rozwiązanie całkowicie sprzętowe,
  • dla tak długiego okresu lepszy jest TIM2/TIM5 32-bit.

Obecne dobre praktyki

  • dla bardzo wolnych przebiegów sterujących stosować:
    • duży timer 32-bitowy,
    • ewentualnie RTC, jeśli ważniejsza jest długoterminowa stabilność czasu niż funkcja PWM.
  • jeśli sygnał steruje mocą lub mostkiem:
    • nie używać „zwykłych dwóch GPIO” jako zamiennika kanałów komplementarnych z dead-time.

Potencjalne przyszłe kierunki rozwoju

W bardziej złożonych projektach taki generator często łączy się z:

  • trybami niskiego poboru mocy,
  • RTC do synchronizacji długich interwałów,
  • DMA / triggerami timerów do sterowania sekwencjami bez CPU.

Wspierające wyjaśnienia i detale

Dlaczego PWM1 + PWM2 jest lepsze od ręcznego przełączania GPIO

Bo:

  • stan wyjść wynika bezpośrednio z porównania CNT z CCR,
  • nie ma jitteru programowego,
  • nie ma ryzyka opóźnienia ISR,
  • nie ma obciążenia CPU.

Dlaczego CCR = 128000, a nie 127999

W STM32 tryb PWM działa na relacji:

  • CNT < CCR

Przy:

  • ARR = 255999
  • liczba zliczeń w okresie = 256000

Połowa z tego to:

  • 128000

To daje idealne 50%.


Aspekty etyczne i prawne

W tym zagadnieniu aspekty etyczne i prawne są zwykle drugorzędne, ale istnieją kwestie bezpieczeństwa technicznego:

  • jeśli te dwa piny sterują:
    • tranzystorami MOSFET,
    • półmostkiem,
    • H-mostkiem,
    • przekaźnikami mocy,

to nie wolno zakładać, że „przeciwsobność logiczna” wystarczy.

Kwestie bezpieczeństwa

  • zwykłe kanały TIM2/TIM5 nie dają dead-time,
  • nie są to klasyczne wyjścia komplementarne do energoelektroniki,
  • przy sterowaniu mocą należy użyć advanced timer i mechanizmów ochronnych.

Praktyczne wskazówki

Najlepsza metoda implementacji

  1. Wybierz TIM2.
  2. Użyj pinów:
    • PA0 = TIM2_CH1
    • PA1 = TIM2_CH2
  3. Ustaw:
    • PSC = 41999
    • ARR = 255999
    • CCR1 = CCR2 = 128000
  4. Skonfiguruj:
    • CH1 = PWM1
    • CH2 = PWM2

Jak sprawdzić poprawność

  • oscyloskop:
    • sprawdź, że okres wynosi 128 s,
    • przejście następuje po 64 s,
    • piny są wzajemnie odwrócone.
  • analizator logiczny:
    • wygodniejszy przy tak długich czasach.

Typowe błędy

  • użycie TIM3/TIM4 i oczekiwanie czysto sprzętowego 128 s,
  • zła częstotliwość zegara timera,
  • pomylenie:
    • zegara APB1
    • zegara samego timera,
  • brak konfiguracji GPIO w Alternate Function,
  • zły numer AF dla TIM2.

Ewentualne zastrzeżenia lub uwagi dodatkowe

Ograniczenia

  • rozwiązanie jest bardzo dobre dla wolnego sygnału logicznego,
  • ale nie jest przeznaczone do precyzyjnych układów mocy wymagających dead-time.

Obszary niepewności

Nie podałeś:

  • jakie dokładnie piny chcesz użyć,
  • czy korzystasz z HAL czy rejestrów,
  • jaki masz zegar systemowy.

To ma znaczenie, bo jeśli TIM2 nie jest taktowany 84 MHz, trzeba przeliczyć PSC.

Ogólny wzór:

\[
f{CNT} = \frac{f{TIM}}{PSC+1}
\]

\[
ARR = T{okres} \cdot f{CNT} - 1
\]

\[
CCR = T{HIGH} \cdot f{CNT}
\]


Sugestie dalszych badań

Jeżeli chcesz rozwinąć temat, warto sprawdzić:

  • różnicę między:
    • PWM mode 1
    • PWM mode 2
    • Output Compare Toggle
  • preload rejestrów ARR i CCR,
  • synchronizację wielu timerów,
  • użycie RTC do bardzo długich i stabilnych okresów,
  • tryby niskiego poboru mocy przy pracy timera.

Krótkie podsumowanie

Najpoprawniejsze rozwiązanie dla STM32F411 to:

  • TIM2 lub TIM5
  • dwa kanały jednego timera
  • CH1 = PWM1
  • CH2 = PWM2
  • PSC = 41999
  • ARR = 255999
  • CCR1 = CCR2 = 128000

To daje:

  • okres 128 s
  • 64 s HIGH / 64 s LOW
  • 50% duty
  • dwa piny o przeciwnej polaryzacji
  • bez udziału CPU po starcie

Jeżeli chcesz, mogę od razu przygotować Ci:

  1. pełny projekt HAL/CubeIDE, albo
  2. wersję LL, albo
  3. wersję dokładnie pod Twoje konkretne piny.

Oceń odpowiedź:
Zastrzeżenie: Odpowiedzi udzielone przez sztuczną inteligencję (model językowy) mogą być niedokładne i wprowadzające w błąd. Elektroda nie ponosi odpowiedzialności za dokładność, rzetelność ani kompletność prezentowanych informacji. Wszystkie odpowiedzi powinny być zweryfikowane przez użytkownika.

Zadaj dodatkowe pytanie Sztucznej Inteligencji

Czekaj (2min)...