STM32 ile WS2812 programlanabilir RGB Led örneği

 


Programlanabilir ledler kendi üzerinden sürücüsünu bulunduran ledlerdir. daha önce reklamlarda veya sosyal medyada gördüğümüz Ambilight ledler, RGB klavye mause gibi donanımlarda tanıklık ettiğimiz bu ledler çalışma şekli olarak bir RGB led şeridi ve led sürücüsü ile ekran senkronizasyonu yakalamasıyla çalışır. karanlık ve loş bir aydınlatma ortamında oldukça hoş bir görünüm sağlar.

WS2812 ledi sürmek için yapılması yapısını inceleyelim. Datasheet linki






her ledin kendi üzerinde çipi bulunur. ve her led veri yolu iletimini bir sonraki ile seri olarak gerçekleştirir. yani bağlantıyı görselleştirmek gerekirse 

yukarıdaki görselde olduğu gibidir. mikro işlemciye ilk led seri olarak IN pinine bağlanır. ardından onun hemen arkasındaki 2. ledin IN pini ise 1. ledin OUT çıkışına bağlanır. bu sayede işlemci tek çıkış kanalından seri olarak bağlanan tüm ledleri programlayabilir.  veri iletimi ise işlemcinin kare dalgalar ile yani PWM ile sağlanır.




datasheette programlama Frekansın 800 Khz olduğu yazılmaktadır. 1 saniye / 800Khz olduğu vakit 
1 Periyot = 0,00000125 saniye olur. bu bizim her bir veri bitimiz için periyottur. bu duruma göre STM32 PWM çıkışımızı 800 Khz olacak şekilde konfigüre etmeliyiz. 
1 periyodumuz (10 üssü -9) 10^(-9) x 125 olur ki bu 125 nanosaniye yapar.

datasheet üzerinden anlatacağımız için mikrosaniyeye (uS) çevirelim. bir periyodumuz 1.25 mikrosaniyedir. tablonun en sağında bulunan +-150 nanosaniye değeri ise tolerans payıdır. yani 0.15 mikrosaniye tolerans payına sahip olur.


Frekans belirlendi, periyodumuz da 1.25uS (mikrosaniye) olarak hesapladık. çıkış kanalından verilerin nasıl iletildiğini inceleyelim. işlemci led sürücü ile 1 ve 0'lar üzerinden haberleşmektedir. bu 1 ve 0'lar için kare dalgaların genişliklerini kullanırız.

Low olarak tanımladığımız değerin karşılığı Sıfır Volttur.
High olarak tanımladığımız değerin karşılığı 3.3-5V Volttur.
bunun böyle olmasının sebebi kontrolcü olan işlemcinin çıkış değeridir.
 
eğer ki veri biti olarak '1' değeri göndereceksek T1H ve T1L değerlerine bakmamız gerek.
0.7uS + 0.6uS = 1.30 uS ile bir periyotta '1' değerinde tek bitlik verimiz iletilmiş olur. 0.5 uS'lik fazlalık payı da hata toleransı olarak gösterilebilir. tablo başlığının en üstünde 1.25uS +- 0.6uS olarak belirtilmiştir. 1 biti toplam süresi: 1.3us ± 300ns → [1.0µs, 1.6µs] aralığında olur

eğer ki veri biti olarak 0 değeri göndereceksek T0H ve T0L değerlerine bakmamız gerek.
0.35uS + 0.8uS = 1.15 uS ile bir periyotta '0' değerinde tek bitlik verimiz iletilmiş olur. 0.10 uS'lik eksiklik payı da hata toleransı olarak gösterilebilir. 0 biti toplam süresi: 1.15us ± 300ns → [0.85µs, 1.45µs] aralığında olur.

Squence chart bölmesinde PWM modülündeki kare dalga genişliklerinin '1' ve '0' için gösterilmiştir.

şimdi STM32 kısmına artık geçiş yapalım.








Harici osilatör kullanmamız daha iyi bir hassasiyet sağlayacaktır. seçtiğimiz TIMER kanalının değerini bilmemiz gerekmektedir. ben TIM3'ü kullanacağım. bu durumda STM32 datasheetine baktığımda TIM3 APB1 Timer Clocks kaynağına bağlı. clock configuration ekranında değere baktığımda 72 Mhz değerinde gözüküyor.

şimdi elimizde 72 Mhz hızında TIMER'ımız var. elde etmek istediğimiz PWM çıkışı 800 Khz.
72 000 000 / 800 000 = 90 değerini elde ederiz. bu durumda işlemcimizde  kendi belirleyeceğimiz olan Prescaler(PSC) ile Counter Period(ARR) değerinin çarpımı 90 etmelidir. Prescaler tanımı itibari ile ölçekleyici değerimizdir. frekans çıkışının ölçeklenebilmesini sağlar. bunu daha sonra belirleyelim.

ARR değerimiz yani Counter Period değerimiz. her bir periyodun kaç adımdan oluşacağını belirlememizi sağlar. örneğin 1 periyodu 100 adıma da 1000 adıma da istediğimiz değerde bölebiliyoruz. yukarıda hesaplamaları yaptık her period 1.25 uS sürmekte ve değerliklerimiz için gereken birimler şöyledir;

ÖRNEK OLARAK: ARR değeri 3 olsun. yani PWM genişliğimiz 3 adımlık olsun. yani 1.25uS'lik periyodu 3 dilime böldük

 '0' biti yapacak olursak 1/3 birim doluluk ve 2/3 birim boşluk olur.
  • 1/3 birim doluluk da 0.41uS High
  • 2/3 birim boşluk da 0.83uS Low

 1' biti yapacak olursak 2/3 birim doluluk ve 1/3 birim boşluk olur.
  • 2/3 birim doluluk da 0.83uS High
  • 1/3 birim boşluk da 0.41uS Low

değeri olur. bu değerlerin hata payına uyum gösterip gösteremeyeceğini ele aldığımız takdirde ARR değerini uygulayabiliriz.
-----------------------------------------------
Tamamen varsayımsal olarak daha hassas bir ayarlama yapmak isteniyorsa diyelim ki ARR değerimiz 45 olsun. bu durumda 1.25uS'lik periyodu 45 dilime bölmüş olduk. ve her dilim 0.027 uS zaman dilimine sahip oluyor.

 '0' biti yapacak olursak 15/45 birim doluluk ve 30/45 birim boşluk olur. (aslında 1/3 ve 2/3 yakınsar fakat daha hassas olur)
  • 15/45 birim doluluk da 0.41uS High -> fakat daha hassas bir ayar çekmişsek daha hassas olalım; 13/45 birim doluluk 0.361uS doluluk yapar 
  • 32/45 birim boşluk da 0.88uS Low yapar.
'1' biti yapacak olursak 27/45 birim doluluk ve 18/45 birim boşluk olur.
  • 27/45 birim doluluk da 0.75uS High
  • 18/45 birim boşluk da 0.44uS Low
-------------------------------------------

ARR'yi 3 seçtiğimiz takdirde PSC değerimiz de otomatik olarak 30 olur. formül yapısı gereği sağına -1 eklenir ki derlenirken zaten 1 çıkartılarak değeri koyulmuş olur.



DMA açık olmalıdır. Direction;  Memory To Peripheral olmalıdır. bu işin DMA'ya bırakılması CPU yükünü hafifletir. 




Her led için ayrı ayrı RGB değerlikleri olur. Red Green Blue değerleri ayrı ayrı 0 ila 255 arasında değişebilir. bu da RGB için ayrı ayrı 8 bitlik bir veridir. 8 bit Red, 8 bit Green ve 8 Bit Blue olmak üzere her bir led için 24 bitlik veri tamponda saklanır. eğer ki 8 adet led seri dizilmişse 8*24 adet biti bellekte saklayabilmemiz gerekmektedir.


Main.C kod içeriğinde noOfLEDs define tanımlı bir değerdir. elinizdeki modülde kaç tane led var ise sayıyı girmeniz gerekmektedir.


Main.C kodları




  
/* USER CODE BEGIN 0 */
#define noOfLEDs 8
uint8_t pwmData[24*noOfLEDs];

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{
    HAL_TIM_PWM_Stop_DMA(&htim3, TIM_CHANNEL_1);
    htim3.Instance->CCR1 = 0;
}

void resetAllLED (void)
{
    for (int i=0; i<24 1="" 2="" blue="" for="" green="" i="" int="" ledposition="" noofleds="" pwmdata="" red="" setallled="" setled="" void="">=0; i--) // Set the first 8 out of 24 to green
    {
        pwmData[24*LEDposition + 7 - i] = ((Green >> i) & 1) + 1;
    }
    for (int i=7; i>=0; i--) // Set the second 8 out of 24 to red
    {
        pwmData[24*LEDposition + 15 - i] = ((Red >> i) & 1) + 1;
    }
    for (int i=7; i>=0; i--) // Set the third 8 out of 24 to blue
    {
        pwmData[24*LEDposition + 23 - i] = ((Blue >> i) & 1) + 1;
    }
}

void ws2812Send(void)
{
    HAL_TIM_PWM_Start_DMA(&htim3, TIM_CHANNEL_1, (uint32_t *)pwmData, 24*noOfLEDs);
}
/* USER CODE END 0 */




  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
	  resetAllLED();
	      ws2812Send();
	      setLED(0, 255, 0, 0);     // Red
	      ws2812Send();
	      HAL_Delay (500);
	      setLED(1, 0, 255, 0);     // Green
	      ws2812Send();
	      HAL_Delay (500);
	      setLED(2, 0, 0, 255);     // Blue
	      ws2812Send();
	      HAL_Delay (500);
	      setLED(3, 255, 255, 0);   // Yellow
	      ws2812Send();
	      HAL_Delay (500);
	      setLED(4, 0, 255, 255);   // Magenta
	      ws2812Send();
	      HAL_Delay (500);
	      setLED(5, 255, 0, 255);   // Cyan
	      ws2812Send();
	      HAL_Delay (500);
	      resetAllLED();
	      ws2812Send();
	      setLED(6, 255, 255, 255); // Specific color
	      ws2812Send();
	      HAL_Delay (3000);
	      resetAllLED();
	      ws2812Send();
	      for (int i=255; i>0; i--) // Set brighness for red (Dimm)
	      {
	          setLED(7, i, 0, 0);
	          ws2812Send();
	          HAL_Delay (20);
	      }
	      for (int i=0; i<255 0="" brighness="" for="" green="" hal_delay="" i="" int="" p="" set="" setled="" ws2812send="">0; i--) // Set brighness for blue (Dimm)
	      {
	          setLED(7, 0, 0, i);
	          ws2812Send();
	          HAL_Delay (20);
	      }
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
  
  

Yorumlar