PWM mit den HAL-Bibliotheken von ST auf STM32F4 zum Laufen bringen

Ich versuche, zu den ST HAL-Bibliotheken zu wechseln, und kann die PWM anscheinend nicht zum Laufen bringen. Lässt sich gut kompilieren, startet nur nicht.

In meinem main() rufe ich die Timer-Initialisierungsfunktion auf:

/* TIM3 init function */
void MX_TIM3_Init(void)
{

  TIM_MasterConfigTypeDef sMasterConfig;
  TIM_OC_InitTypeDef sConfigOC;

  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 0;
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim3.Init.Period = 1300;
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  HAL_TIM_PWM_Init(&htim3);

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig);

  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.OCIdleState = TIM_OCIDLESTATE_SET;
  sConfigOC.Pulse = 650;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_ENABLE;

  HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_1);
  HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_2);
  HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3);
  HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_4);
  HAL_TIM_PWM_MspInit(&htim3);

} 

Der GPIO wird in der HAL_TIM_PWM_MspInit()Funktion initialisiert:

void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef* htim_pwm)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(htim_pwm->Instance==TIM3)
  {
    /* Peripheral clock enable */
    __TIM3_CLK_ENABLE();

    /**TIM3 GPIO Configuration    
    PC9     ------> TIM3_CH4
    PC8     ------> TIM3_CH3
    PC7     ------> TIM3_CH2
    PC6     ------> TIM3_CH1 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_9|GPIO_PIN_8|GPIO_PIN_7|GPIO_PIN_6;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF2_TIM3;
    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

  }

}

Schließlich sieht mein main() so aus: (Ich rufe SystemInit() von main auf, weil ich STCube-generierte Dateien mit coocox coide verwende)

int main(void)
{

    SystemInit() ;

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_TIM3_Init();
  MX_GPIO_Init();
  MX_LWIP_Init();
  while (1)
  {

  }

}
Am Ende kehrte ich zu den alten Bibliotheken zurück, weil ich sie problemlos zum Laufen brachte - ich arbeitete an einem Proto-Board mit dem stm32F417IG und wollte es einfach wieder zum Laufen bringen! Hoffentlich habe ich Zeit, darauf zurückzukommen und das HAL-Zeug zum Laufen zu bringen. Danke für alle Antworten!

Antworten (7)

Ich komme zu spät zur Party, aber ich befand mich in einer ähnlichen Situation und habe die richtige Antwort, oder zumindest die Antwort, die ich persönlich verifiziert habe, um mit meinem eigenen STM32-Board und den STM32CubeMx-Treibern zu funktionieren.

  1. Stellen Sie sicher, dass Sie Ihre Stack-zugeordneten Strukturen initialisieren (auf Null setzen). zB memset(&sConfigOC, 0, sizeof(sConfigOC));Lokale Variablen werden nicht automatisch initialisiert und Sie werden eine unerwartete/unbeabsichtigte Konfiguration haben, weil die HAL basierend auf diesen Handle-Variablen sehr grundlegende Annahmen über den Peripheriezustand macht. Besonders ärgerlich ist es, wenn sich das Verhalten von Kompilierung zu Kompilierung oder von Funktionsaufruf zu Funktionsaufruf ändert, wenn sich der Stapelspeicher ändert.
  2. Rufen Sie nicht HAL_TIM_PWM_MspInit()manuell an. Es wird automatisch aufgerufen, wenn Sie aufrufen HAL_TIM_PWM_Init(), aber Sie müssen sicherstellen, dass Ihre Handle-Variablen richtig initialisiert sind (siehe Nr. 1 oben).
  3. Sie müssen nicht anrufen, HAL_TIMEx_MasterConfigSynchronization()wenn Sie nicht von der Standardkonfiguration (nicht verkettet/synchronisiert) wechseln. Es schadet nichts, ist aber auch nicht nötig.
  4. Ihre MspInit()Funktion ist gut, außer Sie sollten auch die Uhr des GPIOC-Peripheriegeräts aktivieren.

Und die Magie, die verhindert, dass es funktioniert:

  1. Sie MÜSSENHAL_TIM_PWM_Start() nach jedem Aufruf von aufrufen -- HAL_TIM_PWM_ConfigChannel()das erste, was Sie tun HAL_TIM_PWM_ConfigChannel()müssen, ist, die Timer-Ausgänge (die CCxEBits in TIMx_CCER) zu deaktivieren. HAL_TIM_PWM_ConfigChannel()schaltet die Timer-Ausgänge nicht wieder ein!

Da die einzige HAL-Methode zum manuellen Aktualisieren des PWM-Tastverhältnisses durch ist HAL_TIM_PWM_ConfigChannel(), müssen Sie immer anrufen HAL_TIM_PWM_Start()oder Sie haben keinen PWM-Ausgang. Ich denke, dies geschieht, weil die normale Arbeitsweise mit PWM darin besteht, die _IT()oder _DMA()-Varianten von zu verwenden HAL_TIM_PWM_Start(), und Sie aktualisieren den Arbeitszyklus sozusagen "im Hintergrund".

Einige der anderen Antworten (einschließlich der ursprünglich als akzeptiert markierten) sind falsch:

  1. Rufen Sie weder , HAL_TIM_Base_Start()noch HAL_TIM_Base_Init(), noch einen der anderen Nicht-PWM-Aufrufe an; Erstens ruft die HAL Ihre PWM_MspInit()Funktion nicht auf, da sich das Peripheriehandle nicht mehr im Reset-Zustand befindet, aber was noch wichtiger ist, die PWM (und OC und andere Varianten) rufen alle internen Funktionen auf niedriger Ebene ordnungsgemäß auf, ohne dass Sie dies manuell tun. Der springende Punkt bei der HAL ist, dass Sie sich nicht so viele Gedanken über die Details machen müssen, aber Sie müssen einen guten Überblick darüber haben, was die HAL tut und was sie erwartet. Die vollständige Quelle ist verfügbar und ziemlich gut dokumentiert. Sie müssen es nur lesen.
  2. Sie müssen HAL_TIMEx_PWMN_Start()keine der anderen TIMExFunktionen aufrufen, es sei denn, Sie verwenden die kostenlosen Ausgänge. Auch dies ist in der HAL-Quelle ziemlich gut dokumentiert.
Ich habe andere Vorschläge ausprobiert, aber keiner hat funktioniert. Dies hat bei mir funktioniert, da ich Olimex H103-Board verwendet habe. dies basiert auf dem STM32F103RBT6. Ich habe einfach die folgende Zeile zwischen USER CODE BEGIN 2 UND USER CODE END 2 in main.c eingefügt HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
Als Hinweis - um komplementäre PWM-Ausgänge zu starten, muss jeder von ihnen separat gestartet werden. zB HAL_TIMEx_PWMN_Start(&htim8, TIM_CHANNEL_2); HAL_TIM_PWM_Start(&htim8, TIM_CHANNEL_2);
Ich habe Stunden damit verbracht, dies zum Laufen zu bringen. In der Version von HAL, die ich habe (V1.27.1 Mitte 2022), stellte ich fest, dass ich zuerst die PWM stoppen, dann die Konfiguration ändern und dann neu starten musste:HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_1); HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1);

Ich verwende STCubeMX und die generierten HAL-Initialisierungsdateien. Prozess auf meinem F302 Nucleo Board verifiziert. Ich habe den erweiterten Timer 1 (TIM1) mit einem normalen und komplementären Ausgang ohne Totzeit eingerichtet.

So habe ich PWM in CubeMX konfiguriert:

  1. In der Pinbelegungsansicht habe ich zwei Pins als TIM1_CH- und TIM1_CHN-Pins ausgewählt. Stellen Sie im linken Bereich TIM1-Kanal 1 als „PWM Generation CH1 CH1N“ ein.
  2. In der Konfigurationsregisterkarte habe ich die folgende Einstellung vorgenommen (TIM1 clk ist 64 MHz)CubeMX TIM1 Konfigurationseinstellungen
  3. Nachdem der Code generiert wurde, müssen wir noch die PWM starten. Dies geschieht durch Aufrufen der folgenden Funktionen im Benutzercodebereich vor while(1) innerhalb von main():

    HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1); //starts PWM on CH1 pin HAL_TIMEx_PWMN_Start(&htim1, TIM_CHANNEL_1); //starts PWM on CH1N pin

  4. Um die Konfigurationseinstellungen zu ändern, verwenden Sie die folgenden Makros, die in stm32f3xx_hal_tim.h bereitgestellt werden. HAL_TIM_PWM_ConfigChannel()ist nicht die einzige Möglichkeit, die PWM-Einstellung manuell zu aktualisieren, wie von @akohlsmith gesagt.

    __HAL_TIM_GET_AUTORELOAD(&htim1); //gets the Period set for PWm __HAL_TIM_SET_COMPARE(&htim1, TIM_CHANNEL_1, dutyCycle); //sets the PWM duty cycle (Capture Compare Value)

Kein erneuter Anruf nötig HAL_TIM_PWM_Start(). Diese Makros ändern die Einstellung während der Laufzeit.

Bei Verwendung von Cube Mx initialisiert der generierte Code das Timer-Peripheriegerät, startet es jedoch nicht. Die Lösung ist wie von anderen vorgeschlagen: Fügen Sie die Startfunktionen hinzu.

HAL_TIM_Base_Start(&htim3); 
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_ALL); 

Platzieren Sie diesen Code im Abschnitt „BENUTZERCODE“ nach dem generierten Code, der aufruft MX_TIM3_Init().

Ich habe dies für einen Nucleo-32 (Mikrocontroller STM32F042K6) ausprobiert, auch für Timer 3. Es funktionierte nicht mit TIM_CHANNEL_ALLdem zweiten Aufruf, sondern mit TIM_CHANNEL1 stattdessen (es startete die tatsächliche PWM-Ausgabe am Ausgangspin). Ähnlich für die anderen 3 Kanäle, indem Sie TIM_CHANNEL2, TIM_CHANNEL3bzw. TIM_CHANNEL14im Anruf an verwenden HAL_TIM_PWM_Start()(also insgesamt 4 Anrufe an HAL_TIM_PWM_Start()).
Der Aufruf an HAL_TIM_Base_Start(&htim3);wird nicht benötigt (empirisch ermittelt und auch in der Antwort von akohlsmith ).
@PeterMortensen das ist genial. Erstaunlich, wie so wenig Informationen so viel bedeuten können. Vielen Dank für das Teilen, dies war auch auf einem benutzerdefinierten STM32F412-Board von entscheidender Bedeutung.

Sie müssen zuerst den Timer starten. Fügen Sie die nächste Zeile zum main()Start von timer3 hinzu CH1:

HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_1);
        void PWM_Output(void)
        {


          TimerPeriod = 1000;



          /* TIM1 clock enable */
          RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 , ENABLE);

          /* Time Base configuration */
          TIM_TimeBaseStructure.TIM_Prescaler = 48;
          TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
          TIM_TimeBaseStructure.TIM_Period = TimerPeriod;
          TIM_TimeBaseStructure.TIM_ClockDivision = 1;
          TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

          TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

          /* Channel 1, 2, 3 and 4 Configuration in PWM mode */
          TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
          TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
          TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;



          TIM_OCInitStructure.TIM_Pulse = 899;
          TIM_OC1Init(TIM1, &TIM_OCInitStructure);

          TIM_OCInitStructure.TIM_Pulse = 899;
          TIM_OC2Init(TIM1, &TIM_OCInitStructure);

          TIM_OCInitStructure.TIM_Pulse = 899;
          TIM_OC3Init(TIM1, &TIM_OCInitStructure);

          TIM_OCInitStructure.TIM_Pulse = 899;
          TIM_OC4Init(TIM1, &TIM_OCInitStructure);

          /* TIM1 counter enable */
          TIM_Cmd(TIM1, ENABLE);

          /* TIM1 Main Output Enable */
          TIM_CtrlPWMOutputs(TIM1, DISABLE);

        }

    void GPIO_init(){
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);


              GPIO_PWMEnPins.GPIO_Pin = GPIO_Pin_8|GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_11;
              GPIO_PWMEnPins.GPIO_Mode = GPIO_Mode_AF;
              GPIO_PWMEnPins.GPIO_Speed = GPIO_Speed_Level_1;
              GPIO_PWMEnPins.GPIO_OType = GPIO_OType_PP;
              GPIO_PWMEnPins.GPIO_PuPd = GPIO_PuPd_NOPULL;
              GPIO_Init(GPIOA, &GPIO_PWMEnPins);

 /*SMT32F0 has different config for Alternate Function assignment; below is the example. STM32F4 might have less code footprint to configure alternate func.*/
              GPIO_PinAFConfig(GPIOA, GPIO_PinSource8, GPIO_AF_2);
              GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_2);
              GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_2);
              GPIO_PinAFConfig(GPIOA, GPIO_PinSource11, GPIO_AF_2);

    }

Dieser Code funktioniert in CooCox mit Standard-STM32f0xx_yyy-Bibliotheken. Sie müssen auch die Uhr von GPIOC in GPIO init aktivieren.

Das mag funktionieren, aber die Frage war die Verwendung der ST HAL-Bibliotheken.

Ich denke, Sie müssen die GPIO-Uhr durch Aufrufen initialisieren__GPIOC_CLK_ENABLE();

Ich habe ähnlichen Code, der funktioniert - aber ich habe das Master-Config-Zeug ausgelassen.

Es gibt ein Beispiel im Ordner stm32g-eval, das an das Discovery Board angepasst werden kann (falls das das Board ist, das Sie verwenden).

Sie müssen dazu:

HAL_TIM_Base_Start_IT(&htim3);
HAL_TIM_PWM_Start(&htim3,TIM_CHANNEL_ALL);
HAL_TIMEx_PWMN_Start(&htim3,TIM_CHANNEL_ALL);
Erklären Sie bitte, wo und warum.
Das "_IT" bezieht sich auf etwas Interrupt-bezogenes. Warum sollte das nur benötigt werden, um eine PWM-Ausgabe zu erhalten?