Picproje Elektronik Sitesi

PICPROJE PROGRAMLAMA DERSLERİ => STM32 Örnekleri => Konuyu başlatan: Klein - 17 Kasım 2012, 07:30:15

Başlık: Örnek: STM32Fxx USART TX interrupt + RS485
Gönderen: Klein - 17 Kasım 2012, 07:30:15
Eğer USART ile gönderilecek yüklü miktarda verimiz var ise, bu veriyi döngü içerisinde göndermek, işlemci zamanının önemli bir bölümünü harcayacaktır. Baud hızımız ne kadar az ise, gönderme hızımız o kadar düşecek, dolayısyla bekleme hızımız da o kadar artacaktır.
Gönderme işleminin bitmesini beklemeden işimize devam edebilmek için ya DMA kullanırız,ya da TX kesmesi.
DMA kullanmak çok daha avantajlı olmasına karşın, kurulumu kesme kurmaya nispeten daha karışıktır.

Bu örneğimiz TX(Gönderme) için kesmenin kurulmasını  ve  işletilmesini nasıl yapacağımızı gösteriyor.
Örneğimiz, veriyi binary olarak gönderiyor.  Bu text olarak gönderemeyeceğimiz anlamına gelmiyor. Göndermek için verinin uzunluğunu fonksiyona bildirmemiz gerekiyor.

Aynı zamanda RS485 için DE ve RE pinlerini kontrol etmemizi sağlıyor. RS485 DE pinini kontrol etmenin ince bir ayrıntısı var. Eğer bir baytlık verinin gönderilip gönderilmediğini anlamak için TXE bayrağına bakarsak, tüm verinin son baytını kaybedebiliriz.
Çünkü; DR registerine veriyi yerleştirdiğimizde bu veri TX donanımının shift registerine aktarılır. Bu aktarma tamamlandığında, TXE bayrağı çekilir. Yani verinin tüm bitleri gönderilmemiştir. Sadece DR registerimiz boşalmıştır o kadar. Eğer bu esnada RS485 DE pinini 0 yaparsak , veri karşı tarafa ulaşmadan RS485 TX modundan çıkarız.
Bu sorunu çözmek için çoğunlukla  delay kullanılır. Ama biz delay kullanmayacağız. Bunun yerine  dizimizdeki tüm elemanların DR registerine yazılması bittikten sonra, son baytı gönderirken TC (Gönderme tamamlandı) kesmesini kurup, bu bayrağı gözleyeceğiz.
TC bayrağı,  shift registerdeki verinin son biti de çıkışa aktarıldıktan sonra çekilir. 

Benim devremde Rs485 çipinin RE ve DE pinleri ayrı ayrı çıktığı için ben iki pini birden kontrol ettim. Sizin devrenizde ortak ise tek pinikontrol edeceksiniz.

Öncelikle USART kanalımızın kullanacağı pinlerin init edilmiş olması gerek.

Artık USART ayarlarımızı yapabiliriz.  init_usart(...) yerine kendi başlatma rutininizi de yazabilirsiniz. usart ayarlarını doğru yapalım yeter.

void init_usart(USART_TypeDef *Usart, uint32_t BaudRate, uint16_t DataBits, uint16_t Parity, uint16_t StopBits){
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStruct;

  USART_InitStructure.USART_BaudRate = BaudRate;
  USART_InitStructure.USART_WordLength = DataBits;
  USART_InitStructure.USART_StopBits = StopBits;
  USART_InitStructure.USART_Parity = Parity;
  USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
  USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
  USART_Init(Usart, &USART_InitStructure);

  NVIC_InitStruct.NVIC_IRQChannel = USART3_IRQn;
  NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init(&NVIC_InitStruct);
  USART_Cmd(Usart,ENABLE);
}

// init_usart fonksiyonunu çağırma
// Usart3'ü 9600 baud, 8 bit veri uzunluğu, 1 bit stop biti ve parite biti olmadan init ediyoruz.
init_usart(USART3,9600,USART_WordLength_8b,USART_Parity_No,USART_StopBits_1);


Usartımızı yapılandırdığımıza göre artık send ve interrupt fonksiyonlarımızı yazabiliriz.

char txt_buf[100];
char bin_buf[]= {'b','i','n','a','r','y',':',0,1,2,3,4,0x30,0x31,0x32,65,66,67,13,10};
uint16_t tx_len=0;
char *tx_ptr=0;
uint8_t send_ok=1;


void Usart_SendBuf(USART_TypeDef *USART,  uint16_t length, char *buff){
/***********  RS485 DE ve RE pinlerini gönderme için kuruyoruz. **********/

    GPIO_SetBits(GPIOB,GPIO_Pin_2); 
    GPIO_ResetBits(GPIOC,GPIO_Pin_15);
/****************************************************************
               send_ok=0;
tx_len = length; // göndereceğimiz bayt sayısı.
                                       // eğer string gönderiyor olsaydık, bu sayıya ihtiyacımız olmayacaktı.
tx_ptr = buff;  // kullanacağımız dizinin adresini pointerimize atıyoruz.
USART_ITConfig(Usart,USART_IT_TXE,ENABLE); // TX tamponu (DR) boşaldığında kesme üret.
}

void USART3_IRQHandler(void)
{
if(USART_GetITStatus(USART3,USART_IT_TXE)){
if(--tx_len == 0){ // Dizimizdeki tüm elemanlar bitti mi?
USART_ITConfig(USART3,USART_IT_TXE,DISABLE); // artık TXE bayrağı işimize yaramaz.
USART_ITConfig(USART3,USART_IT_TC,ENABLE); // Gönderme tamamlandığında kesme üret.
}
USART_SendData(USART3,(uint16_t) *tx_ptr);
tx_ptr++;
USART_ClearITPendingBit(USART3,USART_IT_TXE);

}

if(USART_GetITStatus(USART3,USART_IT_TC)){ // Son bit de yerine ulaştı mı?
    GPIO_ResetBits(GPIOB,GPIO_Pin_2);  // RS485 alma moduna ayarlıyoruz.
    GPIO_SetBits(GPIOC,GPIO_Pin_15);
USART_ITConfig(USART3,USART_IT_TC,DISABLE); // Verimizin tümü gitti Artık TC bayrağına da ihtiyaç yok.
USART_ClearITPendingBit(USART3,USART_IT_TC);
                             send_ok=1;
}

}



Verimizi gönderebiliriz.


int main(void)
{
init_rcc();
init_gpio();
init_usart(USART3,9600,USART_WordLength_8b,USART_Parity_No,USART_StopBits_1);

               Usart_SendBuf(USART3,27,"Merhaba dunyali biz dostuz\r\n");
               while(!send_ok);
               Usart_SendBuf(USART3,sprintf(txt_buf,"Text gonderiyoruz\r\n"),txt_buf);
               while(!send_ok);
               Usart_SendBuf(USART3,sprintf(txt_buf,"Veri : %u\r\n", 256),txt_buf);
               while(!send_ok);
               Usart_SendBuf(USART3,sizeof(bin_buf),bin_buf);
               while(!send_ok);
               Usart_SendBuf(USART3,20,bin_buf);
         

while (1){}
}




ekleme:
Bu şekilde veri gönderirken işlemci zamanından çok çalmayız. Ama bir şeye çok dikkat etmek gerekir. Usart_SendBuf(..) fonksiyonu ayarları yapıp kesmeyi kurar ve hemen işini bitirir. Bundan sonrasını kesme hizmet rutini halleder.
Bir dizinin gönderilmesi tamamen bitmeden ikinciye başlamamak gerekir.  Eğer iki tane USART_SendBuf(...) fonksiyonu, işlemin bitmesi beklenmeden peş peşe çağırılırsa  tampon boşalmadan, tampona yeni veri gelir. Bu da işlerin karışması demektir.

Daha önce bu işi kesme kullanmadan yapanların çok dikkatli olması gerekir. Kesme kullanmadığınız durumda, send_buf(...) gibi bir fonksiyonu çağırdığınızda, verilerin tümünün göderilmesi bitmeden çağırıldığı noktaya geri dönmeyeceği için, peş peşe yapabiliyorlardı. Bu şekilde bir kullanımda  sadece  farklı tamponlar ve farklı usart kanalları sözkonusu ise peş peşe kullanılabilir.

Göndermenin tamamen bitip bitmediini anlamak için send_ok bayrağına bakmak gerek.