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

Dołącz do dyskusji, napisz odpowiedź!

Jeśli masz już konto to zaloguj się teraz, aby opublikować wiadomość jako Ty. Możesz też napisać teraz i zarejestrować się później.
Uwaga: wgrywanie zdjęć i załączników dostępne jest po zalogowaniu!

Anonim
Dołącz do dyskusji! Kliknij i zacznij pisać...

×   Wklejony jako tekst z formatowaniem.   Przywróć formatowanie

  Dozwolonych jest tylko 75 emoji.

×   Twój link będzie automatycznie osadzony.   Wyświetlać jako link

×   Twoja poprzednia zawartość została przywrócona.   Wyczyść edytor

×   Nie możesz wkleić zdjęć bezpośrednio. Prześlij lub wstaw obrazy z adresu URL.

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