Skocz do zawartości

Problem z przebiegiem PWM w STM32


JerzySz

Pomocna odpowiedź

Witam,

Jestem początkującym w programowaniu STM32F103.

Do tej pory programowałem Microchip PIC 8 bitowe w asm i C.

Zakupiłem zestaw STM32F103 Nucleo i uczę się programowania z kursu, jest bardzo ciekawy i pomocny.

Zgodnie z kursem uruchomiłem UART, przetwornik ADC na DMA, przerwanie na TIM2 z częstotliwością 4kHz, PWM 12kHz.

W przerwaniu przepisuję  wartość z przetwornika ADC (po przeskalowaniu od 0 - 1000) i zmieniam wypełnienie PWM.

Pojawia się problem z przebiegiem PWM. Proszę o pomoc w rozwiązaniu problemu.

Pozdrawiam. Jurek

/**
  ******************************************************************************
  * @file    main.c
  * @author  Ac6
  * @version V1.0
  * @date    01-December-2013
  * @brief   Default main function.
  ******************************************************************************
*/

#include <stdio.h>
#include <stdint.h>
#include "stm32f10x.h"
#include <math.h>

#define ADC_CHANNELS 2
TIM_OCInitTypeDef  channel;	// PWM

volatile uint32_t timer_ms = 0;

uint16_t adc_value[ADC_CHANNELS];
volatile int32_t PID;
volatile uint16_t PID_PWM;
volatile uint32_t old_adc_0, old_adc_1;

void SysTick_Handler()
{
 if (timer_ms)
 timer_ms--;
}

void TIM2_IRQHandler()
{

 if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
 {
 TIM_ClearITPendingBit(TIM2, TIM_IT_Update);

 GPIO_SetBits(GPIOA, GPIO_Pin_5);
 PID = (adc_value[0] / 4);
 PID_PWM = PID;

 channel.TIM_Pulse = PID_PWM;
 TIM_OC1Init(TIM4, &channel);

 GPIO_ResetBits(GPIOA, GPIO_Pin_5);

 //if (GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_5))
 //GPIO_ResetBits(GPIOA, GPIO_Pin_5);
 //else
 //GPIO_SetBits(GPIOA, GPIO_Pin_5);
 }
}


void delay_ms(int time)
{
 timer_ms = time;
 while (timer_ms);
}

void send_char(char c)
{
 while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
 USART_SendData(USART2, c);
}

int __io_putchar(int c)
{
 if (c=='\n')
 send_char('\r');
 send_char(c);
 return c;
}

int main(void)
{
 //int8_t i;
 GPIO_InitTypeDef gpio;
 USART_InitTypeDef uart;
 DMA_InitTypeDef dma;
 ADC_InitTypeDef adc;
 TIM_TimeBaseInitTypeDef tim;  	// Timer 2
 NVIC_InitTypeDef nvic;			// Timer 2
 //TIM_OCInitTypeDef  channel;	// PWM

 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
 RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);

 RCC_ADCCLKConfig(RCC_PCLK2_Div6);
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

 GPIO_StructInit(&gpio);
 gpio.GPIO_Pin = GPIO_Pin_5;
 gpio.GPIO_Mode = GPIO_Mode_Out_PP;
 GPIO_Init(GPIOA, &gpio);

 //*** konfiguracja pinow UART
 gpio.GPIO_Pin = GPIO_Pin_2;
 gpio.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_Init(GPIOA, &gpio);

 gpio.GPIO_Pin = GPIO_Pin_3;
 gpio.GPIO_Mode = GPIO_Mode_IN_FLOATING;
 GPIO_Init(GPIOA, &gpio);
 //***

 //*** PWM wyjscia
 gpio.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7|GPIO_Pin_8|GPIO_Pin_9;
 gpio.GPIO_Speed = GPIO_Speed_50MHz;
 gpio.GPIO_Mode = GPIO_Mode_AF_PP;
 GPIO_Init(GPIOB, &gpio);
 //***

 USART_StructInit(&uart);
 uart.USART_BaudRate = 115200;
 USART_Init(USART2, &uart);
 USART_Cmd(USART2, ENABLE);

 //*** ustawienie wejsc przetwornika A/D
 gpio.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1;
 gpio.GPIO_Mode = GPIO_Mode_AIN;
 GPIO_Init(GPIOA, &gpio);
 //***
//*** ustawienie DMA do odczytu wejsc przetwornika A/D
 DMA_StructInit(&dma);
 dma.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
 dma.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
 dma.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
 dma.DMA_MemoryBaseAddr = (uint32_t)adc_value;
 dma.DMA_MemoryInc = DMA_MemoryInc_Enable;
 dma.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
 dma.DMA_DIR = DMA_DIR_PeripheralSRC;
 dma.DMA_BufferSize = ADC_CHANNELS;
 dma.DMA_Mode = DMA_Mode_Circular;
 DMA_Init(DMA1_Channel1, &dma);
 DMA_Cmd(DMA1_Channel1, ENABLE);
 //***

 ADC_StructInit(&adc);
 adc.ADC_ScanConvMode = ENABLE;
 adc.ADC_ContinuousConvMode = ENABLE;
 adc.ADC_NbrOfChannel = ADC_CHANNELS;
 adc.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
 ADC_Init(ADC1, &adc);

 ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 2, ADC_SampleTime_239Cycles5);
 ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_239Cycles5);

 ADC_DMACmd(ADC1, ENABLE);
 ADC_Cmd(ADC1, ENABLE);

 ADC_ResetCalibration(ADC1);
 while (ADC_GetResetCalibrationStatus(ADC1));

 ADC_StartCalibration(ADC1);
 while(ADC_GetCalibrationStatus(ADC1));

 ADC_SoftwareStartConvCmd(ADC1, ENABLE);

 SysTick_Config(SystemCoreClock / 1000);

 // *** Timer4 jako PWM
 //RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
 TIM_TimeBaseStructInit(&tim);

 tim.TIM_ClockDivision = 0;
 tim.TIM_RepetitionCounter = 0;

 tim.TIM_CounterMode = TIM_CounterMode_Up;
 tim.TIM_Prescaler = 5 - 1;
 tim.TIM_Period = 1000 - 1;
 TIM_TimeBaseInit(TIM4, &tim);
 //***

 //*** ustawienia kanalow PWM
 TIM_OCStructInit(&channel);
 channel.TIM_OCMode = TIM_OCMode_PWM1;
 channel.TIM_OutputState = TIM_OutputState_Enable;
 channel.TIM_OCPolarity = TIM_OCPolarity_High;
 TIM_OC1PreloadConfig(TIM4, TIM_OCPreload_Enable);


 //channel.TIM_Pulse = PID_PWM;
 //TIM_OC1Init(TIM4, &channel);

 channel.TIM_Pulse = 200;
 TIM_OC2Init(TIM4, &channel);
 channel.TIM_Pulse = 500;
 TIM_OC3Init(TIM4, &channel);
 channel.TIM_Pulse = 900;
 TIM_OC4Init(TIM4, &channel);
 TIM_Cmd(TIM4, ENABLE);
 //***


 //*** Timer 2
 TIM_TimeBaseStructInit(&tim);
 tim.TIM_CounterMode = TIM_CounterMode_Up;
  tim.TIM_Prescaler = 640 - 1;
  tim.TIM_Period = 25 - 1;
  TIM_TimeBaseInit(TIM2, &tim);

  TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
  TIM_Cmd(TIM2, ENABLE);

  nvic.NVIC_IRQChannel = TIM2_IRQn;
  nvic.NVIC_IRQChannelPreemptionPriority = 0;
  nvic.NVIC_IRQChannelSubPriority = 0;
  nvic.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&nvic);
//***

 while (1) {

 //GPIO_SetBits(GPIOA, GPIO_Pin_5);
 //printf("ADC%d = %d\n", 0, adc_value[0]);
 //printf("A = %d (%.3fV)\n", PID);
	 //channel.TIM_Pulse = PID_PWM;
	  //TIM_OC1Init(TIM4, &channel);

 printf("%d\n",PID);

 //GPIO_ResetBits(GPIOA, GPIO_Pin_5);
 delay_ms(1);
 }
}

 

Przechwytywanie.JPG

Przechwytywanie1.JPG

Link do komentarza
Share on other sites

Od czego jest pin PA5 ?
Problem masz raczej w przerwaniu od TIM2 skoro ono działa z f = 4 kHz a PWM ma 12 kHz i na wykresie masz co 3 (okres ?) szpilkę w dół (12/4)

Chyba korzystasz z kursu dotyczącego biblioteki SPL już nie rozwijanej, ale to nie problem 😉

https://forbot.pl/blog/kurs-stm32-f1-hal-liczniki-timery-w-praktyce-pwm-id24334

Edytowano przez ps19
Link do komentarza
Share on other sites

PA5 pozostał do odczytu czasu obliczeń które wcześniej pisałem i usunąłem z powodu błędnego przebiegu PWM.

Ten sam efekt mam w przypadku zmiany wartości wypełnienia w pętli while(1), nawet jeśli zmienię na stałą wartość.

 while (1) {

 //GPIO_SetBits(GPIOA, GPIO_Pin_5);
 //printf("ADC%d = %d\n", 0, adc_value[0]);
 //printf("A = %d (%.3fV)\n", PID);
	 channel.TIM_Pulse = 720;		//PID_PWM;
	 TIM_OC1Init(TIM4, &channel);

 printf("%d\n",PID);

 //GPIO_ResetBits(GPIOA, GPIO_Pin_5);
 delay_ms(1);
 }

 

Przechwytywanie2.JPG

Link do komentarza
Share on other sites

W obsłudze przerwania masz wywołanie TIM_OC1Init, po pierwsze nie jestem pewien, czy zmienna channel jest poprawnie ustawiona, ale co ważniejsze Init służy do inicjalizacji - a z tego co rozumiem chciałeś tylko zmienić wypełnienie PWM. Do tego lepiej użyć TIM_SetCompare4.

  • Lubię! 1
Link do komentarza
Share on other sites

Zarejestruj się lub zaloguj, aby ukryć tę reklamę.
Zarejestruj się lub zaloguj, aby ukryć tę reklamę.

jlcpcb.jpg

jlcpcb.jpg

Produkcja i montaż PCB - wybierz sprawdzone PCBWay!
   • Darmowe płytki dla studentów i projektów non-profit
   • Tylko 5$ za 10 prototypów PCB w 24 godziny
   • Usługa projektowania PCB na zlecenie
   • Montaż PCB od 30$ + bezpłatna dostawa i szablony
   • Darmowe narzędzie do podglądu plików Gerber
Zobacz również » Film z fabryki PCBWay

Aby sobie życia nie komplikować proponuję jednak jak @ps19 zmienić podejście do tematu i skorzystać z CubeMX i HAL i nie powinno być więcej tego typu problemów, pozostanie jedynie kwestia nauki posługiwania się HALem.

Link do komentarza
Share on other sites

1 godzinę temu, atMegaTona napisał:

Aby sobie życia nie komplikować proponuję jednak jak @ps19 zmienić podejście do tematu i skorzystać z CubeMX i HAL i nie powinno być więcej tego typu problemów, pozostanie jedynie kwestia nauki posługiwania się HALem.

To nie problem biblioteki jak napisałem - nowa biblioteka nie zapobiegnie użyci zlej funkcji/metody podczas pisania programu. Równie dobrze mogłem zaproponować pisanie na rejestrach w w C czy nawet asemblerze, ale nie o to chodzi.

OFFTOP: Wyklikiwanie konfiguracji w Cube niekoniecznie uczy programowania mikroprocesorów bardziej pomaga zacząć nie do końca wiedząc jak działa procesor, ale nie chce wszczynać dyskusji o wyższości bibliotek czy rejestrów bo sam korzystam czasami różnie od potrzeb z różnych kombinacji HAL lub rejestry czy HAL + rejestry.

 

 

Edytowano przez ps19
Link do komentarza
Share on other sites

Nie każdy chce się uczyć na blachę mikrokontrolerów  a większość sięgających po nie amatorów chce je sobie po prostu zaprogramować dla własnych celów tak aby nie było tego typu problemów z jakim boryka się autor tego wątku. Wykorzystanie profesjonalnie napisanych i sprawdzonych rozwiązań na ogół pozwala uniknąć tego typu przypadków kiedy się godzinami, dniami, miesiącami zmaga z jakimś irytującym potykaczem, który w rezultacie może okazać się banalny. Uważam, że jeśli producent dostarcza najnowsze rozwiązania dla swojego produktu to powinno się z nich korzystać bo pozwala to oszczędzić mnóstwo czasu i nerwów i skupić się na własnych pomysłach a nie zmagać z konfiguracją konfiguracji przez inicjalizowanie inicjalizacji bo można się w końcu doczekać real-life-przerwania albo.. albo jakiś głupi jestem. 

W mx wystarczy wyklikać, dodać funkcję w projekcie i działa, i tak ma być ( u mnie działa bez problemu). Nie trzeba być mechanikiem samochodowym, żeby sprawnie jeździć samochodem i nie każdy mechanik jest świetnym kierowcą.

Co innego kiedy ma się do czynienia z czymś typu avr 8 bit gdzie stopień komplikacji jest przyswajalny dla większości średnio rozgarniętych księgowych a co innego STMy z miesiąca na miesiąc coraz bardziej rozbudowane. Zwyczajnie szkoda życia na wnikanie w szczegóły, które nie mają żadnego znaczenia dla realizacji własnych pomysłów.

Link do komentarza
Share on other sites

Bądź aktywny - zaloguj się lub utwórz konto!

Tylko zarejestrowani użytkownicy mogą komentować zawartość tej strony

Utwórz konto w ~20 sekund!

Zarejestruj nowe konto, to proste!

Zarejestruj się »

Zaloguj się

Posiadasz własne konto? Użyj go!

Zaloguj się »
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.