Timer ARR Değeri Güncelleme Problemi

Başlatan SB7, 20 Mayıs 2023, 18:45:12

SB7

Selamlar,

STM32f103 blue pill ile belirli frekanslarda PWM üretmeye çalışıyorum.Bir dizi içindeki sayılar, ilgili Timer ARR  registerine yazılıyor. Yazdığım kod 2 cycle da bir doğru çalışıyor ve program her başa dönünce 65ms bir ölü zaman oluşuyor.

Amacım bu şekilde olan;
uint32_t period[5] = {5000,1000,2000,8000,4000};
period değerlerini ARR registerine yüklemek.

Sonsuz döngüde yapılan işlem;
while (1)
	{   
		for (i=0;i<5;i++){
		GPIOC->ODR ^= GPIO_Pin_13;
		TIM2->ARR = period[i];
		DelayMs(100);
		}
}
(C13 pininin toggle edilmesi debug için)


Timer ayarlarım bu şekilde;

void PWM_Init()
{
	//TIMER AYARLARI 
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_OCInitTypeDef TIM_OCInitStruct;
	GPIO_InitTypeDef GPIO_InitStruct;

	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
	TIM_TimeBaseInitStruct.TIM_Period = 25000;
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);

	TIM_Cmd(TIM2, ENABLE);
	
// PWM AYARLARI
	
	TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1;
	TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable;
	TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High;
	
	TIM_OCInitStruct.TIM_Pulse = 0;
	TIM_OC1Init(TIM2, &TIM_OCInitStruct);
	TIM_OC1PreloadConfig(TIM2, TIM_OCPreload_Enable);
	
	// PA0 PWM CIKIS PINI OLARAK AYARLANDI
	
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);
}


 Lojik analizör çıktısı da bu şekilde;




Bir taşma olabilir düşüncesiyle Timer CCR registerine en küçük ARR değerinin yarısını yüklüyorum ancak program başa dönünce tekrar ölü zamanlar gerçekleşiyor. Sebebi nedir sizce?
-SB7

Z80

void TIMER2_AYAR()
{

    RCC->APB1ENR |=  RCC_APB1ENR_TIM2EN;    // TIMER2 clock aç       
    TIM2->PSC     =  0;             // 72 MHz 
    TIM2->ARR     =  1024-1;                // 72MHz/1024=70KHz(1024 resolution)                
    TIM2->CCMR1  |=  TIM_CCMR1_OC1M_1;            // PWM mode 1
    TIM2->CCMR1  |=  TIM_CCMR1_OC1M_2; 
    TIM2->CCMR1  |=  TIM_CCMR1_OC1PE;            // Preload register TIMx_CCR1 aç
    TIM2->CCER   |=  TIM_CCER_CC1E;             // OC1 PWM çikisi aç
    TIM2->CR1    &= ~TIM_CR1_CMS;        // PWM kenar mode
    TIM2->CCR1    =  PWM_DEGERI;        // PWM degeri yükle        
    TIM2->CR1    |=  TIM_CR1_CEN;        // Timer2 aç
    
}   

Bu TIMER2 PWM ayar koduyla olması gerekir. 

SB7

@Z80 hocam cevabınız için teşekkür ederim, mesajınızı görünce hemen hızlıca denedim. Aynı sorun devam ediyor. 65ms ölü zamanlar yine oluşuyor.

İlk peryot sorunsuz istediğim frekanslar üretiliyor sonrasında da bu şekilde;



Normalde C13 toggle olduğu süre boyunca aynı frekansta sinyal üretmesi gerekiyor ama burda sağında ve solunda 65ms beklemeler oluyor.


-SB7

SB7

#3
uint32_t period[7] = {25000,25000,10000,[b]1000[/b],10000,25000,25000};

Bu şekilde denedim ortada 1.000 değerinde sadece sorun oluyor bu haliyle, 1.000 değerini 7.000 ve üzeri yapınca sorun olmuyor. Sanırım ani yüksek değer değişimlerinde saçmalıyor MCU.

Bu iş için bir işlem kısıtı mı var? Bilginiz var mı?
-SB7

Z80

TIM2->CR1    &= ~TIM_CR1_CEN;        // Timer2 kapat
TIM2->ARR     =  period[i];                          
TIM2->CR1    |=  TIM_CR1_CEN;        // Timer2 aç

1. Şunu bir dene. Timer2 kapat, ARR yükle, tekrar aç.
2. TIM2->ARR     =  period; bunu peşpeşe iki sefer yap.

SB7

Hocam ikisini de denedim sonuç aynı, yakın değerlerde düzgün ama ani düşük değer gidince 65ms beklemeler oluyor.

Kodların tamamı;
#include "stm32f10x.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_tim.h"
#include "delay.h"

// FONKSIYON TANIMLAMALARI

void PWM_Init(void);
void GPIO_Configuration(void);


uint8_t i = 0;
uint32_t period[7] = {25000,25000,10000,1000,10000,25000,25000};
int main(void)
{
    DelayInit();
    PWM_Init();
  GPIO_Configuration();
    TIM2->CCR1 = 200;
    while (1)
    {  
        for (i=0;i<7;i++){
        GPIOC->ODR ^= GPIO_Pin_13;    
            TIM2->CR1    &= ~TIM_CR1_CEN;        // Timer2 kapat
            TIM2->ARR    =  period[i];
            TIM2->ARR    =  period[i];
            TIM2->CR1    |=  TIM_CR1_CEN;        // Timer2 aç
            
        DelayMs(100);
        }
        
}
}

void PWM_Init()
{
    //TIMER AYARLARI 
    
    GPIO_InitTypeDef GPIO_InitStruct;

TIM2->PSC = 71;
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN; // TIMER2 clock aç      
TIM2->PSC = 71;// 72 MHz 
TIM2->ARR =1024-1;// 72MHz/1024=70KHz(1024 resolution)                  
TIM2->CCMR1 |= TIM_CCMR1_OC1M_1; // PWM mode 1
TIM2->CCMR1 |= TIM_CCMR1_OC1M_2; 
TIM2->CCMR1 |= TIM_CCMR1_OC1PE; // Preload register TIMx_CCR1 aç
TIM2->CCER |= TIM_CCER_CC1E; // OC1 PWM çikisi aç
TIM2->CR1 &= ~TIM_CR1_CMS; // PWM kenar mode
TIM2->CCR1 = 500;
TIM2->CR1 |= TIM_CR1_CEN; // Timer2 aç
    
    // PA0 PWM CIKIS PINI OLARAK AYARLANDI
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
    GPIO_Init(GPIOA, &GPIO_InitStruct);
    
    
}


//GPIO AYARLARI
void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
  RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

  //C13 CIKIS
    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_13;
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOC, &GPIO_InitStruct);
}

Sonuç bu;


-SB7

Z80

Yarın bir deneyeyim bakalım nolacak.

ahmet2015

İşlemci frekans ayarını 72mhz olarak ayarla düzelir.

while döngüsünde bu yeterli

      for (i=0;i<7;i++){
      GPIOC->ODR ^= GPIO_Pin_13;
      TIM2->ARR    =  period[i];
      DelayMs(100);
      }

ahmet2015

Bu değerlere göre osilaskop çıktısı aşağıda.



ahmet2015

Buda stm32cubeide proje dosyası çalışmak isteyenler için.

https://disk.yandex.com.tr/d/m1b_2-6qiGvPuQ

Tagli

Yazılanlara alıcı gözüyle bakmadım ama ilk aklıma gelen şey ARR'nin yanlış zamanda güncellenmesi oldu. Örnek vereyim: ARR 1000 olsun, ve belli bir anda CNT 750 olsun mesela. Tam bu anda, ARR'yi 500 yaptığını düşün. Bu burumda CNT'nin mevcut değeri ARR'den büyük kaldığı için, update event oluşana kadar CNT önce bir 65535'e kadar gidecek, sonra sıfırlanacak ve ancak tekrar 500 olunca update event üretilecek.

Buna çare olarak, ARR sadece bir update event olduğunda, yani timer sıfırlandığında güncellenmeli. Bu işlemi kolaylaştırmak için CR1'in ARPE bitini 1 yapabilirsin. Bu durumda ARR'ye yazdığın değer, yazdığın anda değil, timer taşmasından (update event) sonra geçerli olur ancak. Gerekmesi durumda update event'i EGR'nin UG bitini 1 yaparak da tetikleyebilirsin. Genelde ilk ayarlar yapıldıktan sonra ARR ve PSC'nin güncellenmesi için kullanıyorum bunu.
Gökçe Tağlıoğlu

SB7

@ahmet2015 hocam uğraşıp osiloskop görüntüsüne kadar atmışsınız teşekkür ederim. Sorun çalışıp çalışmaması değil, arada düzgün çalışması. Ben de skobu küçük bir alanda tutsam doğru çalıştığı anlar oluyor, her 2-3 peryotta 1 kez yanlış çalışması problem.

@Tagli hocam nokta atışı oldu, ARPE bitini 1 yaptığım anda düzeldi. Üstelik CCR ARR den büyük olsa bile çalışıyor, önceden korkarak değer gönderiyordum çok teşekkür ederim hocam çok makbule geçti.

@Z80 hocam siz de çok uğraştınız tekrar teşekkür ederim size de.
-SB7

Tagli

Bu preload özelliği ARR ve CCRn register'ları için opsiyonel olarak etkinleştirilebiliyor. Varsayılan olarak kapalı. PSC için ise hep etkin, kapatmak mümkün değil.
Gökçe Tağlıoğlu

SB7

Chat GPT ye de sordum, cevap inanılmaz : )

STM32F103 gibi bazı mikrodenetleyicilerde, sürekli olarak otomatik yeniden yükleme kaydına (ARR) değer yüklemeniz durumunda bir ölü zaman oluşabilir. Bu durum, bir güncelleme olayının meydana gelmesini beklemek zorunda olmanızdan kaynaklanır.

TIMx_CR1 kaydındaki ARPE (Otomatik Yeniden Yükleme Ön Yükleme Etkinleştirme) bitinin ayarlı olması durumunda, otomatik yeniden yükleme kaydına yapılan her yazma işlemi, güncelleme olayı gerçekleştiğinde veya gölge kayıtların güncellenmesini sağlamak için yapılan diğer bir işlem sırasında aktarılır. Bu durumda, otomatik yeniden yükleme kaydına sürekli olarak yeni değerler yüklemeniz, bir güncelleme olayının tamamlanmasını beklemeniz gerektiği anlamına gelir. Bu bekleme süresi, ölü zamanların oluşmasına neden olabilir.

Ölü zamanı azaltmak veya ortadan kaldırmak için aşağıdaki yöntemleri düşünebilirsiniz:

1.TIMx_CR1 kaydındaki ARPE bitini sıfıra ayarlayarak otomatik yeniden yükleme ön yükleme işlemini devre dışı bırakın. Bu durumda, otomatik yeniden yükleme kaydına her değer yükleme işlemi, hemen etkili olur.

2.Ölü zamanları minimize etmek için DMA (Direct Memory Access) kullanmayı düşünebilirsiniz. DMA, yazılımın müdahalesi olmadan bellekten periferiklere veya periferiklerden belleğe veri transferi yapabilen bir mekanizmadır. Bu şekilde, otomatik yeniden yükleme kaydına yapılan yazma işlemleri DMA aracılığıyla gerçekleştirilebilir ve işlemci ölü zamanlarını en aza indirebilir.

3.Ölü zamanları en aza indirmek için kesme tabanlı bir yöntem kullanabilirsiniz. TIMx_SR kaydındaki UIF (Update Interrupt Flag) bayrağını izleyerek güncelleme olayını tespit edebilir ve bu bayrak ayarlandığında uygun işlemleri gerçekleştirebilirsiniz. Bu sayede, otomatik yeniden yükleme kaydına yeni değerler yükledikten sonra beklemek zorunda kalmadan devam edebilirsiniz.

Bu yöntemler, ölü zamanları azaltmaya yardımcı olabilir. Hangi yöntemi tercih edeceğiniz, uygulamanızın gereksinimlerine ve kullanım senaryonuza bağlı olarak değişebilir.
-SB7

Tagli

Alıntı yapılan: SB7 - 02 Haziran 2023, 14:38:23Chat GPT ye de sordum, cevap inanılmaz : )
Gömülü sistemler üzerine bu kadar bilgi sahibi olabileceğini ummamıştım açıkçası  :o
Acaba verileri nereden topluyor? Reference manual'leri de tarıyor olabilir mi?
Gökçe Tağlıoğlu