uarttan veri göndermek için printf kullanılanımı bu işin mantığı nedir?

Başlatan armsistem, 06 Şubat 2013, 00:00:29

armsistem

Arkadaşlar merhaba ; uarttan veri göndermek için printf kullanılan örnekler var , bu işin mantığı nedir.

Ben
uart_send(*p){.......}
şeklinde uğraşıyorum ve netteki bütün örneklerde bu şekilde neden printf kullanılmıyor.

bocek

"Nasıl oluyor da daha önceden tanımlı bir fonksiyonu (fputc) biz tekrar tanımlayınca sorun olmuyor?" diye merak edip araştırırken şuraya rastladım:
http://microchip.wikidot.com/xc32:how-to-redirect-stdout-for-use-with-printf

en altta diyor ki:
Alıntı Yap_mon_putc() is defined in the PIC32 libraries with the weak attribute tag. This means that if another function with the same name is found in a project without the weak attribute, that one will take precedence and the weak one will be ignored.

yani :
Alıntı Yap_mon_putc() fonksiyonu PIC32 kütüphanesinde tanımlanırken "weak" "attribute" ile tanımlanmıştır. Bu da şu demektir ki eğer projede aynı isimde ve "weak" olmayan bir fonksiyon varsa bunu boşver onu kullan.

Demek ki keilde de fputc fonksiyonu weak olarak tanımlanmış ve biz tekrar fputc diye tanımlayınca sorun olmuyor.
(Aynı işlem avr kütüphanesinde FDEV_SETUP_STREAM makrosu ile yapılıyor o da ayrı bir konu).

Şimdi; C++'taki "function overriding" in C'deki hali gibi birşey mi oluyor bu?
1 ya da 0. işte 'bit'ün mesele..

armsistem

Yanıtlar için teşekkür ederim.

Erol YILMAZ

Konu ile alakalı olduğu için buraya yazıyorum.

Şimdi:

#define  TYPE_INPUT   1
#define  TYPE_CALC    1

#define DEBUG(x, ...) if ((x)) { fprintf(stderr, __VA_ARGS__); }


şeklinde bir "Variadic Macro" tanımlamış olalım...

Programın bazı noktalarında,

DEBUG("bidibidibudubudu");

şeklinde kullanmak istiyorum...
Aslında açıkça anlaşılabileceği gibi Debug için kullanacağım printf çıktılarını DebugPort'a  yonlendirmek istiyorum.

Bu amaçla güncellediğim;
#include <stdio.h>
#include <rt_misc.h>
#include "Serial.h"

//#pragma import(__use_no_semihosting_swi)

struct __FILE { int handle; /* Add whatever you need here */ };

#define	STDIN_HANDLE 		0
#define	STDOUT_HANDLE 	1
#define	DEBUG_HANDLE 		2

FILE __stdin 	= {STDIN_HANDLE};
FILE __stdout = {STDOUT_HANDLE};
FILE __stderr	= {DEBUG_HANDLE};

int fputc(int c, FILE *f) {
	
	switch(f->handle){
		
		case	STDOUT_HANDLE:
			SER_PutChar(c);
		break;
		
		case	DEBUG_HANDLE:
			DEBUG_PutChar(c);
		break;
		
	}

return(0);
}


Ne kadar doğrudur ?

AsHeS

Alıntı yapılan: gerbay - 06 Şubat 2013, 10:25:31
printf i uart a göndermenin mantığı şu şekilde;

printf kendi içerisinde karakter i print etmek için "fputc" metoduna çağrı yapar..

fputc nin tanımı şu şekilde;   int fputc(int c, FILE *f)

siz kendi proje dosyalarınızda bu metodu tanımladığınızda keil kendi kütüphanesindeki ile değil de sizin yazdığınız ile linkler ve printf metodu kendi içinde sizin yazdığınız "fputc" metodunu çağırmaya başlar.. siz de o metodda artık ne yaparsanız size kalmış..

normalde printf metodu ön tanımlı "stdout" a print eder.. fprintf ile siz de istediğiniz file stream i verebilirsiniz. hatta

FILE *uart1;
FILE *uart2;
FILE *uart3;

şeklinde tanım yapıp;   

fprintf(uart1,"deneme\r\n")
fprintf(uart2,"başka deneme\r\n");

şeklinde farklı uartlara da göndertebilirsiniz.. basit olarak printf ile stdout a göndermek için yapmanız gerekenler;
Yukarıda ki gibi fprintf() için tanımlanan fputc() fonksiyonunu içini nasıl doldurmamız gerekir ?
Gelen dosya isminin yapısını mı kontrol etmek lazım printf() için putc() fonksiyonun içini sadece 1 UART kanalı için kullanabiliyorum.
fprintf() hadisesini nasıl gerçekleyebiliriz ?

AsHeS

Alıntı yapılan: gerbay - 10 Temmuz 2013, 21:56:09
Mesela fputc ye parametre olarak gelen FILE in handle degerini kontrol edebilirsiniz. Yukarda Allegro hocamin verdigi ornek çok aciklayici aslinda
struct __FILE { int handle; /* Add whatever you need here */ };
Bu şekilde bir yazımla FILE yapısı içerisine ekleme yapılabiliyor mu ? örnek yardımıyla işi ezbere kotarırım ama mantığını anlamadım açıkçası.

Burak B

@gerbay süpersin. Basitçe şöyle arkadaşlar. İlk mesajlarda @gerbay' ın yine yazdığı gibi aslında olay basit "printf" gibi stdio.h fonksiyonları içerden putc ve getc fonksiyonlarına bağımlı. Siz putc fonksiyonunu kendiniz yazıp derleyiciye bunu kullan diyorsunuz.

Burada FILE __stdin, __stdout, __stderr; v.s. aslında şu demek oluyor. Ben bu karakteri;

UART
LCD
SPI
I2C
CAN

v.s. ye yönlendiriyorum. Bunu da basitçe tüm bu IO aygıtlarına birer numara vererek yapıyorsunuz. Sonra putc içerisinde basit bir switch bloğu ile FILEHANDLE yani IO aygıtı ne ise ona bu gelen karakteri yazıyorsunuz.

@Allegro üstadımın verdiği örnek gayet açıklayıcı zaten. Olayın özündeki önemli nokta şu stdio putc ve getc fonksiyonlarını kullanır. "stdio.h" içerisinde kullanışlı (ve bir o kadar yer kaplayan  :D) bir sürü fonksiyon var. En önemlisi de printf. Bu fonksiyonu;

fprintf(LCDHANDLE,"%3.3f%cC",sicaklik,223);
fprintf(UARTHANDLE,"%3.3f%cC",sicaklik,223);
fprintf(SPIHANDLE,"%3.3f%cC",sicaklik,223);


seçeneklerinden istediğiniz biriyle kullanabilirsiniz. Aynı şekilde bu yazılımın aşağıdaki yazılımdan bir farkı da yoktur;

char buf[64]
...
sprintf(buf,"%3.3f%cC\0",sicaklik,223);
for (i=0;(i<sizeof(buf)) || (buf[i]!=0); i++)
   SendSPIByte(buf[i]); // UARTSend(buf[i]); veya LCDWrite(buf[i]); de olabilir.


"... a healthy dose of paranoia leads to better systems." Jack Ganssle

Kazım

Arkadaşlar gets () , fprint () vb. fonksiyonları kullanmanın bir avantajı varmı ben gerekli kodalrı hep kendin yazıyorum..Birkaç defa editledim kusura bakmayın ..


while( 1 ) {
      // Get the string, wait until the new line (i.e. gets ())
      gets( rxBuffer );
      //
      // parse the buffer and build the display lines
      //
      result = strtok( rxBuffer, delims );         // get message ID
      
      if ( !strcmp (result, "$GPGGA") ) {        // Parse, if NEMA ID is position sentence
         if( rxBuffer[7] != ',' ) {             // If no time, old fix
            result = strtok( NULL, delims );   // store the time
            
              }

Kazım

Alıntı yapılan: gerbay - 11 Temmuz 2013, 11:44:07
kullandığınız "gets" ve "strtok" zaten.. ben sizin yerinizde olsam her zaman mümkün olduğunca "runtime library" de gelen fonksiyonları kullanmaya çalışırım. bu kodlar test edilmiş onaylanmış, hatasız olduğu kabul edilen kodlar.. boş yere tırmalamaya gerek yok.

ha diyelimki RTOS kullanıyorsunuz ve birden fazla taskınız (threadiniz) var, bunların en az ikisinden "strtok" metodu çağırılıyor, bu durumda "strtok" kullanırsanız bug lı bir kodunuz olur. strtok metodu kendi içinde statik değişken kullanıldığından "reentrant" (thread-safe) değildir.  kullandığınız kütüphanede varsa thread-safe varyasyonu olan "strtok_r" metodunu kullanırsınız ya da oturup yeniden yazarsınız.

Evet anlıyorum . Özelliklede hatasız kod durumunu. Teşekkür ederim. Ayrıca " strtok_r " içinde ayrıca teşekkür ederim.

kantirici

@gerbay hocam açıklamalar için teşekkürler. Son olarak anladığım kadarıyla rotos kullanmayacaksak veri göndermek için putch fonk. kendimiz yazıp içinde de basit swicth mantığı ile buraya gelen veriyi istedigimiz yere yönlendirmek yeterli olur değil mi?

AsHeS

Alıntı yapılan: gerbay - 11 Temmuz 2013, 09:56:52
keil ın header dosyalarını incelerseniz FILE yapısı için şöyle bir tanım var;

typedef struct __FILE FILE;


yani FILE, __FILE tipinde tanımlı, siz "__FILE" tanımını yaptığınızda internal olarak kullandığı FILE tipi de sizin tanımladığınız tipte oluyor.

standart IO için ön tanımlı FILE lar ise;

extern FILE __stdin, __stdout, __stderr;


şeklinde extern olarak tanımlı..

sizin FILE ları birbirinden ayırt edebilmeniz için sadece "handle" alanı yeterli aslında, tamponlama yapacaksanız vs, ona göre bu yapıyı değiştirip ilgili fonksiyonlar ile de tampon yönetimi yapabilirsiniz.

// FILE struct
struct __FILE 
{
  int handle;
};


şeklinde bir tanımlama yaptığınızda "FILE" yapısı da aynen bu yapıda oluyor ve parametre olarak geçilen yerlerde de bu yapı bekleniyor.

standart handle lar için FILE stream ler;

FILE __stdin 	= { 0 };
FILE __stdout   = { 1 };
FILE __stderr	= { 2 };


şeklinde tanımlandığında sadece handle değerleri atanmış FILE struct tipinde örnekler oluşturuluyor. Benzer şekilde siz de ilave yapabilirsiniz;

FILE uart0file  = { 3 };
FILE uart1file  = { 4 };
FILE uart2file  = { 5 };
FILE uart3file  = { 6 };

FILE i2c0file   = { 7 };
FILE i2c1file   = { 8 };

FILE spi0file   = { 9  };
FILE spi1file   = { 10 };
FILE lcdFile    = { 11 };


gibi.. dikkat etmeniz gereken kısım handle değerleri için kullandığınız değerleri tek bir kez kullanmanız, handle değerleri "unique" olmalı..

standart FILE tanımları üzerinden gidecek olursak; örneğin "standart output" a yazmak için "stdout" a yazmak gerekiyor;

fprintf(stdout, ...);


şeklinde, yukardaki tanım ise "__stdout" şeklindeydi.. aslında "stdout"  tanımı "__stdout" şeklinde yukarda örneği olan FILE yapısına işaret eden bir pointer tanımından başka birşey değil.. şu şekilde tanımlı aslında;

#define stdin  (&__stdin)
#define stdout (&__stdout)
#define stderr (&__stderr)


FILE ile işlem yapan fonksiyonlar genellikle "FILE*" şeklinde pointer olarak parametre alır. orada kullanmak için gerekli pointer tanımları da bu şekilde yapılmış durumda.. kendi tanımlamalarınız için de aynı şeyi yapabilirsiniz;

#define uart0 (&uart0file)
#define uart1 (&uart1file)
#define uart2 (&uart2file)
...

gibi...

bu tanımlamaları da yaptıktan sonra "FILE*" parametre alarak en son okuma/yazma vs. yapan fonksiyonları yeniden yazmalısınız. eğer sadece fprintf tarzında fonksiyonlar içinden kullanacaksanız  fgetc, fputc, _ttywrch, ferror gibi fonksiyonları yeniden yazmanız yeterli..

örneğin "fputc" metodunu ele alalım;

int fputc(int ch, FILE *f);


şeklinde tanımlı.   siz yukardaki gibi tanımlamaları yapıp  uart0 a fprintf ile birşey göndereceğiniz zaman, örneğin;
fprintf(uart0, "deneme %d\r\n", 1);

şeklinde bir yazım yaptığınızda fprintf metodu basmak istediği her karakter için karakteri ve ilk parametre olarak verdiğiniz uart0 parametresiniz fputc metoduna geçiriyor. diyelimki ilk karakter olan 'd' karakterini basmak istiyor, o durumda;
fputc('d', &uart0file);

şeklinde çağrı yapıyor..

siz fputc ye geçirilen FILE pointerının handle değerini kontrol ederek nereye basılmak istediğini kontrol edip oraya yönlendireceksiniz karakteri..

int fputc(int ch, FILE *f) 
{
     if (3 == f->handle) uart0send(ch); else
     if (4 == f->handle) uart1send(ch); else
     ....
     if (9 == f->handle) spi0send(ch); ...
     ...
     else return -1;

     return ch;    
}

gibi...

Allegro hocamın verdiği örneği dikkatli incelemenizde fayda var.


__FILE yapısını istediğiniz gibi genişleterek mesela uart parametrelerini, spi ya da i2c için kullanacaksanız onların parametrelerini vs de o yapıya koyup sizin metodunuza geçirilen FILE pointer ı üzerinden alabilirsiniz..
Çok teşekkür ederim gerbay hocam benim için karanlık olan tüm noktaları aydınlattınız .