Haberler:

Forum kuralları güncellendi LÜTFEN  okuyunuz:  https://bit.ly/2IjR3ME

Ana Menü

pic vs stm32

Başlatan mehmetali5454, 06 Kasım 2023, 15:45:04

mehmetali5454

Merhaba
3 faz bldc motor hall sensörlü Komutasyonda pic denetleyici kullanırsak Komutasyonu OVDCN registeri yapar, stm32 de motor kontrolü için kullanilan advanced time registerlerda OVDCN Regisyerinin karşılıği hangi registerdır,hangi bitlerdir?






sedronjames

ben register seviyesinde yapmamıştım. HAL kütüphanesini kullandım. HAL_TIMX_PWM_Start ve stop fonksiyonu var. komütasyona göre kanalları bu fonksiyonla start ve stop ederek motorumu sürmüştüm. stm32cube ide indiriseniz veya internetten HAL kütüphanesi incelerseniz bu fonksiyonlarda kullanılan registerlara rahatlıkla ulaşabilirsiniz.

mehmetali5454

Advanced timer 1 registerlerini reference manual dökümandan translate çevirisi ile anlamaya çalıştım, emin değilim ama capture/compare Enable Registerinin 0,2,4,6,8,10 nolu bitleri hall sensor komutasyonu için , 1,3,5,7,9,11 nolu bitleri encoder komutasyonu için , öyle anladım ; doğrulayacak veya yanlışa çıkaracak olursa seviniriz

mehmetali5454

Capture compare ENABLE registerindeki bitler; capture compare modulünün giriş mi yoksa çıkış mı olduğuna göre ayar yapmaya yarıyor
Bu haliyle,bldc moyor Komutasyon bitleri olduğuni sanmiyorum,bizi aydınlatacak yok mu?

mehmetali5454

Sanırim, ilk tespitim kismende olsa doğru,Ovdcon registetinin stm32 registerindrki karşiligi CCxE-CCxNE =TIMxCCER
diger bit e gelince
CC1P
CC1NP bunlarda , çikişin yiksrlen kenar veya düşen kenar olmasini sağliyor,
Konu kismende olsa bitmistir


Tagli

Doğru, büyük oranda öyle. Ancak o bitlerin davranışlarını etkileyen başka bitler de var. Olası kombinasyonlar açıklamaları ile birlikte bir tablo halinde verilmiş reference manual'de.
Ayrıca bu bitler'in shadow bitleri de var. Yani yeni değerler bitler yazıldığı zaman değil, komütasyon event'i oluştuğu zaman geçerli hale gelebiliyor. Bu da çıkışların atomik bir şekilde aynı anda güncellenmesine olanak veriyor.
Gökçe Tağlıoğlu

mehmetali5454

Tagli hocam ,
Kıymetli bilgileriniz için teşekkür ederim,
Diğer bitler,MOE,OSSRI,
OSSR,OISx OISxN olmalı
Evet,o tabloyu hatırladım
Shodow bit,register bu terimleri stm32G484 CE TIM1 registerleri üzerinden somut bir şekilde anlatmanız mümkün mü ,
Anladığım şu,ilk bilgiyi TIM1_CCER.CC1E ve CC1NE bitlerine yükledik,bu registerlere yüklenecek diğer bit tablosu,pic denetleyicidekideki karşiligi working olan bellekte bekler,komutasyon kesmesinden sonra,bu bellekten alinarak TIM1_CCER.CC1E ve CC1NE
Bitlerine atanir

Tagli

Alıntı yapılan: mehmetali5454 - 12 Kasım 2023, 20:41:16Anladığım şu,ilk bilgiyi TIM1_CCER.CC1E ve CC1NE bitlerine yükledik,bu registerlere yüklenecek diğer bit tablosu,pic denetleyicidekideki karşiligi working olan bellekte bekler,komutasyon kesmesinden sonra,bu bellekten alinarak TIM1_CCER.CC1E ve CC1NE
Bitlerine atanir
Evet, yaklaşık olarak öyle.

Yakın zamanda biraz da deneme amaçlı olarak STM32G431 için six-step komütasyon kodu yazmıştım. Kesme içinden hall sensörlerinin durumuna göre çağrılan fonksiyon aşağıda (0-5 arası bir argümanla çağrılıyor):
void drive_stage(uint8_t stage)
{
    static const uint32_t ccer[6] = {
        (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC2NE | TIM_CCER_CC3E), // 0
        (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC3NE), // 1
        (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC3NE), // 2
        (TIM_CCER_CC1E | TIM_CCER_CC1NE | TIM_CCER_CC2E | TIM_CCER_CC3E), // 3
        (TIM_CCER_CC1E | TIM_CCER_CC1NE | TIM_CCER_CC2E | TIM_CCER_CC3E), // 4
        (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC2NE | TIM_CCER_CC3E), // 5
    };
    static const uint8_t oc1m[6] = {OCMODE_PWM, OCMODE_PWM, OCMODE_LOW, OCMODE_LOW, OCMODE_LOW, OCMODE_LOW};
    static const uint8_t oc2m[6] = {OCMODE_LOW, OCMODE_LOW, OCMODE_PWM, OCMODE_PWM, OCMODE_LOW, OCMODE_LOW};
    static const uint8_t oc3m[6] = {OCMODE_LOW, OCMODE_LOW, OCMODE_LOW, OCMODE_LOW, OCMODE_PWM, OCMODE_PWM};

    TIM1->CCER = ccer[stage];
    uint32_t temp;
    temp = TIM1->CCMR1;
    temp &= ~TIM_CCMR1_OC1M & ~TIM_CCMR1_OC2M; // Clear the old values
    temp |= (oc1m[stage] << TIM_CCMR1_OC1M_Pos) | (oc2m[stage] << TIM_CCMR1_OC2M_Pos);
    TIM1->CCMR1 = temp;
    temp = TIM1->CCMR2;
    temp &= ~TIM_CCMR2_OC3M;
    temp |= (oc3m[stage] << TIM_CCMR2_OC3M_Pos);
    TIM1->CCMR2 = temp;
    TIM1->EGR |= TIM_EGR_COMG; // Trigger COM Event
}

Buradaki yöntem aslında çok da doğru değil, ama deneme amaçlı bir kod olduğu için kafamı çok yormamıştım. Her adımda/aşamada bir high-side'a PWM verilirken, bir low-side ise sabit olarak etkinleştiriliyor. High-side'ı tek sürmek yerine complimentary PWM vermek muhtemelen daha doğru olurdu.

Burada COM event'in elle oluşturulduğuna dikkat çekerim. Bunu belki bir şekilde otomatik olarak interrupt ile tetiklemenin de bir yolu olabilir. Ancak ben Hall sensörlerini klasik EXTI interruptlarına bağlayarak orada okudum ve hall konumlarına göre yukarıda verdiğim fonksiyonu çağırdım.

TIM1 konfigürasyonunu ise şöyle yapmıştım:

// TIM1 Configuration
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // Enable TIM1 clock
__DSB();
TIM1->ARR = 500 - 1; // 32 KHz PWM @ 16 MHz Timer Clock
TIM1->CR2 |= TIM_CR2_CCPC; // CCxE, CCxNE, OCxM are preloaded
TIM1->CCMR1 |= TIM_CCMR1_OC1PE | TIM_CCMR1_OC2PE; // CCR1 & CCR2 are preloaded
TIM1->CCMR2 |= TIM_CCMR2_OC3PE; // CCR3 is preloaded
TIM1->BDTR |= TIM_BDTR_OSSR // Disabled channels are driven low
        | TIM_BDTR_MOE; // Enable outputs
TIM1->CR1 |= TIM_CR1_CEN; // Start TIM
Gökçe Tağlıoğlu

mehmetali5454

OC1M,OC2M,OC3M , satirlari komutasyon sirasina göre aktif olacak güç anahtarlarini belirtiyor, değil mi?
Sonra temp degişkeni ile her komutasyon sonu,yeniden yüklemek için,eski degerleri silmişsiniz,Bu durumda OVDCON registerinin tam karşiliği, OC1M OC2M OC3M oluyor.
Kıymetli zamanlarınızı ayırdınız,çok sağolun hocam.

mehmetali5454

Gökçe Hocam,
yazdığınız  kodu anlayamadım;

        (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC2NE | TIM_CCER_CC3E), // 0
        (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC3NE), // 1
        (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC3NE), // 2
        (TIM_CCER_CC1E | TIM_CCER_CC1NE | TIM_CCER_CC2E | TIM_CCER_CC3E), // 3
        (TIM_CCER_CC1E | TIM_CCER_CC1NE | TIM_CCER_CC2E | TIM_CCER_CC3E), // 4
        (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC2NE | TIM_CCER_CC3E), // 5


CC1E /capture-Compare 1 Enable /
 hall sensörün bütün durumlarında  Logic 1 seviyesinde
Komutasyon sırasına göre durumunun diğerleri gibi değişmesi gerekmez mi,
yoksa nasıl oluyor,bilemedim?

Halbuki pwm OCM1,2,3 değiştirmişsiniz

             {OCMODE_PWM, OCMODE_PWM, OCMODE_LOW, OCMODE_LOW, OCMODE_LOW, OCMODE_LOW};
            
             {OCMODE_LOW, OCMODE_LOW, OCMODE_PWM, OCMODE_PWM, OCMODE_LOW, OCMODE_LOW};

             {OCMODE_LOW, OCMODE_LOW, OCMODE_LOW, OCMODE_LOW, OCMODE_PWM, OCMODE_PWM};
 

Kırambor

Alıntı yapılan: Tagli - 12 Kasım 2023, 23:13:47Evet, yaklaşık olarak öyle.

Yakın zamanda biraz da deneme amaçlı olarak STM32G431 için six-step komütasyon kodu yazmıştım. Kesme içinden hall sensörlerinin durumuna göre çağrılan fonksiyon aşağıda (0-5 arası bir argümanla çağrılıyor):
void drive_stage(uint8_t stage)
{
    static const uint32_t ccer[6] = {
        (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC2NE | TIM_CCER_CC3E), // 0
        (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC3NE), // 1
        (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC3NE), // 2
        (TIM_CCER_CC1E | TIM_CCER_CC1NE | TIM_CCER_CC2E | TIM_CCER_CC3E), // 3
        (TIM_CCER_CC1E | TIM_CCER_CC1NE | TIM_CCER_CC2E | TIM_CCER_CC3E), // 4
        (TIM_CCER_CC1E | TIM_CCER_CC2E | TIM_CCER_CC2NE | TIM_CCER_CC3E), // 5
    };
    static const uint8_t oc1m[6] = {OCMODE_PWM, OCMODE_PWM, OCMODE_LOW, OCMODE_LOW, OCMODE_LOW, OCMODE_LOW};
    static const uint8_t oc2m[6] = {OCMODE_LOW, OCMODE_LOW, OCMODE_PWM, OCMODE_PWM, OCMODE_LOW, OCMODE_LOW};
    static const uint8_t oc3m[6] = {OCMODE_LOW, OCMODE_LOW, OCMODE_LOW, OCMODE_LOW, OCMODE_PWM, OCMODE_PWM};

    TIM1->CCER = ccer[stage];
    uint32_t temp;
    temp = TIM1->CCMR1;
    temp &= ~TIM_CCMR1_OC1M & ~TIM_CCMR1_OC2M; // Clear the old values
    temp |= (oc1m[stage] << TIM_CCMR1_OC1M_Pos) | (oc2m[stage] << TIM_CCMR1_OC2M_Pos);
    TIM1->CCMR1 = temp;
    temp = TIM1->CCMR2;
    temp &= ~TIM_CCMR2_OC3M;
    temp |= (oc3m[stage] << TIM_CCMR2_OC3M_Pos);
    TIM1->CCMR2 = temp;
    TIM1->EGR |= TIM_EGR_COMG; // Trigger COM Event
}

Buradaki yöntem aslında çok da doğru değil, ama deneme amaçlı bir kod olduğu için kafamı çok yormamıştım. Her adımda/aşamada bir high-side'a PWM verilirken, bir low-side ise sabit olarak etkinleştiriliyor. High-side'ı tek sürmek yerine complimentary PWM vermek muhtemelen daha doğru olurdu.

Burada COM event'in elle oluşturulduğuna dikkat çekerim. Bunu belki bir şekilde otomatik olarak interrupt ile tetiklemenin de bir yolu olabilir. Ancak ben Hall sensörlerini klasik EXTI interruptlarına bağlayarak orada okudum ve hall konumlarına göre yukarıda verdiğim fonksiyonu çağırdım.

TIM1 konfigürasyonunu ise şöyle yapmıştım:

// TIM1 Configuration
RCC->APB2ENR |= RCC_APB2ENR_TIM1EN; // Enable TIM1 clock
__DSB();
TIM1->ARR = 500 - 1; // 32 KHz PWM @ 16 MHz Timer Clock
TIM1->CR2 |= TIM_CR2_CCPC; // CCxE, CCxNE, OCxM are preloaded
TIM1->CCMR1 |= TIM_CCMR1_OC1PE | TIM_CCMR1_OC2PE; // CCR1 & CCR2 are preloaded
TIM1->CCMR2 |= TIM_CCMR2_OC3PE; // CCR3 is preloaded
TIM1->BDTR |= TIM_BDTR_OSSR // Disabled channels are driven low
        | TIM_BDTR_MOE; // Enable outputs
TIM1->CR1 |= TIM_CR1_CEN; // Start TIM


Ben bir türlü şu komütasyon işini tam anlayamıyorum. 3 tane hall sensörüm olacak mantıken her biri 2 fazın arasında olacak şekilde. Başta bu uygulama notunu takip ettim.

Aklımdaki o komütasyon mantığını koda nasıl yansıtacağımı bir türlü bulamadım. Siz burada drive_stage fonksiyonunu 3 interrupt fonksiyonunda da çağırıyor musunuz?

mehmetali5454

Kırambor, 3 kesme ile 6 duruüm elde ediliyor,her bir durumda 0-Sıfırdan 5-Beşe kadar olan satırlar sırası ile işliyor,
komutasyon demek sırasıyla güç anahtarlarını iletim-kesim yapması anlamına geliyor

Tagli

Tablo ve kontroller gerçekten de kafa karıştırıcı. İlgili tablo bu:



Örnek olması için 0 durumunu inceleyelim.

Bu durumda koddan CCER'e bakacak olursak:
Faz 1 : Sadece H
Faz 2 : H ve L
Faz 3 : Sadece H
çıkışları aktive ediliyor gibi gözüküyor. Ama işin aslı öyle değil. TIM_BDTR_OSSR bitini 1 olarak ayarladığımız için, aslında Faz 1 ve Faz 3'ün L bacakları 0 olarak sürülüyor (MOE = 1 olduğunu hatırlayın). Yani Faz 1 & Faz 3 için tablonun 6. satırı geçerli. Buraya kadar olan bilgiler ile 6 bacağın 2'sini belirledik: Faz 1 ve Faz 3'ün L'lerinin 0 olduğunu biliyoruz.

Şimdi OC1M, OC2M ve OC3M'e bakalım. Bunlara sırasıyla PWM, LOW, LOW demişim. Bunlar "PWM Mode 1" ve "Force inactive level" ayarlarına denk geliyor. Buradan yola çıkarak H'ları da bulabiliyoruz. Demek ki Faz 1'in H'sı PWM, Faz 2 ve Faz 3'ün H'sı ise 0.

Geriye sadece Faz 2'nin L'si kaldı. Bu fazda hem H hem de L kumanda edilecek şekilde seçilmişti CCER ayarı ile. Bu durumda Faz 2'nin H'sı 0 olduğu için (yukarıdaki paragraf), L'si de mecburen 1 olmaya zorlanmış olacak. (Tablonun 4. satırı)

Yani "Stage 0"da, 2 adet MOSFET aktif: Faz 1'in H'sından PWM verilirken, Faz 2'nin L'si sürekli iletimde kalarak akımı toprağa götürüyor. Faz 3 ise tamamen devre dışı.

Burada aslında Faz 1 sadece H yerine, H ve L beraber etkinleştirilerek complementary moduna alınsa galiba daha iyi olurdu. Önceki mesajımda bahsettiğim durum buydu.
Gökçe Tağlıoğlu

mehmetali5454

Gökçe hocam
Low side dediğimiz anahtarlara pwm sinyali uygulanmıyor
sizden öğrendiğim Break- dead time Registerindeki moe biti ve
off state selection run mode  OSSR biti ile low side anahtarları Lojik 1 -Lojik 0 seviyesinde kontrol ediliyor
işte bu kısım işi karıştırıyor,
BDTR registerinde bir adet MOe biti var, zaten Logik 1 olmak zorunda (motor kontrolü için)
geriye OSSR biti kalıyor, o da bir bit,ya 1 ya 0 olacak,
OSSR biti ile low side anahtar grubundan 1 i iletimde olurken,diğer High side anahtar grubundan birine pwm uygulanıyor

mehmetali5454

#14
Gökçe hocam.
Tabloda 1X010 durumunda ocx=ocxref xor CCxP ifadesinde  ocxref ile CCxP bitinin Logic xor işleminden sonra çikiş degerinin oluştuğunu mu anlamaliyim.
Birde registerler bitwise işlemiyle set reset edilir ama registerin bit degerinin X olarak ifadesi nasil yazilir,tabloda OSSR Bitin degeri bazı durumda  X olarak ifade edilmiş BDTR &=~1 <<3
yazsak bu X olmaz,0 olur,
X değerini nasıl ifade ederim.