Örnek: STM32Fxx USART TX interrupt + RS485

Başlatan Klein, 17 Kasım 2012, 10:30:15

Klein

17 Kasım 2012, 10:30:15 Son düzenlenme: 17 Kasım 2012, 11:22:44 Klein
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 *Usartuint32_t BaudRateuint16_t DataBitsuint16_t Parityuint16_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 lengthchar *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. 

Yasal Uyarı: Picproje.org sitemizde 5651 sayılı kanunun 8. maddesine ve T.C.Knın 125. maddesine göre tüm üyelerimiz yaptıkları paylaşımlardan kendileri sorumludur. Picproje.org hakkında yapılacak tüm hukuksal şikayetleri İletişim sayfamızdan bize bildirdikten en geç 3 (üç) iş günü içerisinde ilgili kanunlar ve yönetmelikler çerçevesinde tarafımızca incelenerek gereken işlemler yapılacak ve site yöneticilerimiz tarafından bilgi verilecektir.