kpri() - saját printf()

2014-09-06

 

A C nyelvnek nem része a formázott kiírásra szolgáló printf() függvény, de mégis a kezdetektől szorosan hozzátartozik. Egy PC-s Borland C-ben ha az ember használatba veszi a beépített printf()-t, nem rázza meg a becsatolódó néhány kbyte növekmény, ellentétben egy uC környezettel. Szóval régen vágytam egy saját printf() fgv.-re, amit saját kézben tarhatok. Ami kicsi, de mégis megkönnyíti a kiírást akár soros portra, akár LCD-re, vagy majd amire kell.

Itt látható egy részlet a képernyőmről, bal oldalon a program kód, jobb oldalon a terminál képernyője a program kiírásaival. Így elég jól látható, hogy milyen kiírásokat tudunk megvalósítani. Nem lett megvalósítva a lebegőpontos, és a hosszú egészek kiíratása, valamint a string-ek jobb oldalának vágása. Viszont az eredeti printf()-ben nem lehet egy betüből egy sorozatot kiíratni.

A printf()-nek az az érdekessége, hogy előre nem meghatározott számú argumentumot kap. Ezért kellett belinkelni az stdarg.h-t, amiben található a va_start - va_arg - va_end makró, ami az argumentumok beszedegetésére szolgál. Hazudnék, ha azt állítanám, pontsan megértettem minden részletében, de így kell használni.

Jelenleg LCD-re és soros portra kell ezt-azt kiírnom, aztán még ki tudja mire? Vagy mindegyikre ír az ember egy printf()-t, vagy egy általános megoldást keres. Egyik lehetőség, hogy a printf()-nek átadunk egy mutatót a betü kiíró fgv.-re. Ennek az az előnye, hogy nem kell nagy buffer, mert betünként kipötyögjük a szövegünket. Amennyire utána olvastam, meg nézegettem az elérhető mintákat, ilyenkor a betü kiíró fgv.-t a printf()-ben kell deklarálni. Ilyet sem csináltam C-ben eddig. Igaz nem nagy munka, de ha másik kiíró fgv.-t is akarok használni, akkor hozzá kell nyúlni a printf() kódjához. Azután van ez a megoldás, hogy az ember leteszi egy bufferbe a kiírandókat, és átad egy mutatót a kiíró fgv.-nek, ami azután s megfelelő eszközre kipötyögi a szöveget.

Nem mostanában kezdett a probléma foglakoztatni, de eddig nem tudtam összeszedni magam, hogy nekifussak és megküzdjek vele. És igazam is volt. Elolvastam mindenféle elérhető anyagot. Három nap alatt kétszer újraírtam az alábbi kódot. Azután végre megpróbáltam megetetni a fordítóval. Kaptam úgy 20-30 hibajezést és mindenféle problémázást az eltérő mutatótípusok miatt... Egy éjszaka alatt sikerült a mutatókat rendbe szednem. Következő éjszaka eltelt az egyébb hibajelzések kiköszöbölésével. Csak, csak, csak... a program nem akart egy betüt sem kiírni :(. Alig kellet hozzá még másfél éjszaka, mire a main()-ben lévő teszt kiírások azt produkálták amit szerettem volna. Szóval ez nem egy három éjszakás kaland volt :).

A formátum string a PSTR makróval a program memórban lett elhelyezve, mert különben a RAM-ba tenné. Egyrészt a RAM-ot is fogyasztaná, és RAM-ba az induláskor nyilván a programmemóriából töltené be...

Próbáltam valami makró megoldást keresni, hogy ne kelljen ezt a hosszú kiírást használni, de egyenlőre nem sikerült.

A kpri() a két kiegészítő fgv.-vel hozzávetőleg 1kbyte-tal növeli meg a programot.

kpri_u2s() fgv.-vel nem csak 2-10-16-os számrendszerekbe lehet átváltani. Lehetne még optimalizálni, mert egy számjegy előállítása során egy osztás és egy maradékos osztás kerül elvégzésre, ami időigényes a processor-nak.

/*******************************************************************************
*   Author       -  Kiraly Tibor
*                   http://www.tkiraaly.hu
*   Date         -  2014.09.06.
*   Chip         -  Atmel ATmega8
*   Compiler     -  avr-gcc (WinAVR)
*   
********************************************************************************
*   PonyProg Configuration and Security Bits (bepipalva):
*
*   CKSEL3, CKSEL2, CKSEL1, CKSEL0
*   P       P       -       -       0011  Calibrated Internal RC Oscillator 4MHz
*
*   Calibrated Internal RC Oscillator
*   SUT1  SUT0
*   -     P         Slowly rising power
*   
********************************************************************************
*
*    Soros port:
*     1 VCC
*     2 X
*     3 RX             - AVR RXD  - 2
*     4 TX             - ACR TXD  - 3
*     5 GND
*     
*    Programozo fej:
*     1 RST            - AVR NRES - 1
*     2 X
*     3 SCK            - AVR SCK  - 19
*     4 MISO           - AVR MISO - 18
*     5 MOSI           - AVR MOSI - 17
*     6 GND
*
*    Egyeb:
*     LED              - AVR PB0 - 14
*
*******************************************************************************/

#define F_CPU                _4MHZ




#include "tkiraaly_atmega8.h"
#include <avr/pgmspace.h>
#include <stdio.h>
#include <stdarg.h>



char * kpri( const char *, ...);                 // sajat printf
char * kpri_u2s( U16, U16, U8, U8);              // szam atalakitasa string-re
char * kpri_pad( char *, char *, U8, U8, U8);    // string levagasa, feltoltese, athelyezese
void usartra( char *);                           // string kiiras USART-ra
void usart_putc( char);                          // egy betu kiirasa USART-ra




int main()
{
   USART_SET_9600_8N1;
   U16 n= 381;
   int m= -3578;
   char s[]= "abcd";
   usartra( kpri( PSTR( "Kpri>\r")));
   usartra( kpri( PSTR( "A - %c\r"), '*'));
   usartra( kpri( PSTR( "B - %5c\r"), '*'));
   usartra( kpri( PSTR( "C - %s\r"), s));
   usartra( kpri( PSTR( "D - %2s\r"), s));
   usartra( kpri( PSTR( "E - %8s\r"), s));
   usartra( kpri( PSTR( "F - %08s\r"), s));
   usartra( kpri( PSTR( "G - %-08s\r"), s));
   usartra( kpri( PSTR( "H - %x\r"), n));
   usartra( kpri( PSTR( "I - 0x%04x\r"), n));
   usartra( kpri( PSTR( "J - %d\r"), n));
   usartra( kpri( PSTR( "K - %5.2d%%\r"), n));
   usartra( kpri( PSTR( "L - %5.2dmV\r"), m));
   usartra( kpri( PSTR( "M - %b\r"), n));
   return 0;
}




void usartra( char *s)                           // string kiiras USART-ra
{
   while( *s) usart_putc( *s++);
}




void usart_putc( char c)                         // egy betu kiirasa USART-ra
{
    while( BTC( UCSRA, UDRE));                   // 1, ha kesz az adat fogadasara
    UDR= c;
}




/*******************************************************************************
*   Author       -  Kiraly Tibor
*                   http://www.tkiraaly.hu
*   Date         -  2014.09.06.
*
*   Sajat kpri() - nagyjabol a printf-bol amire szuksegem volt. A kimenetet
*   string-be irja. A fuggveny nem nagyon ellenorzi az ertekeket, ezert mas
*   kombinaciok is beallithatok, de talan ezeknek van ertelme. Szinten nem
*   figyel a buffer tulcsordulasra. Hoszabb kiiras eseten noveljuk meg a
*   buffer-t. Nekem LCD-re csak 16 betu fer, USART-ra gondolva 40-re allitottam.
*   Rovidebb hossz beallitasa eseten a szamok elejet levagja!
*   Helytakarekossagbol a formatum string a program memoriaba (PSTR) kerul.  
*
*   %b    - binaris szam, fix 8 betu, balrol 0-val feltoltve, unsigned char
*
*   %c    - 1 betu kiirasa
*   %5c   - 5 azonos betu kiirasa
*
*   %d    - signed int decimalis kiirasa, amilyen hosszu, elojellel egyutt
*   %5d   - 5 betu hosszu, elojellel egyutt, balrol szokozzel feltoltve 
*   %05d  - 5 betu hosszu, elojellel egyutt, balrol 0-val feltoltve 
*   %-5d  - 5 betu hosszu, elojellel egyutt jobbrol szokozzel feltoltve 
*   %5.2d - 5 betu hosszu, tizedes ponttal és elojellel egyutt, jobb oldalt ket tizedes jegy
*    
*   %s    - string beszurasa
*   %5s   - string utolso 5 betujenek kiirasa, ha rovidebb, balrol szokozzel feltoltve
*   %-5s  - string utolso 5 betujenek kiirasa, ha rovidebb, jobbrol szokozzel feltoltve
*
*   %u    - mint %d, de unsigned int
*   
*   %x    - unsigned int hexadecimalis kiirasa
*   %2x   - hexa szam ket utolso betuje
*   
*******************************************************************************/
char * kpri( const char * frmt, ...)
{
   va_list ap;
   static char buffer[ 32];
   const char * f;                               // mutato formatum stringre
   char * t;                                     // mutato a bufferre
   int i;                                        // int szam
   U16 u;                                        // unsigned int szam
   char * s;                                     // mutato string-re
   char * n;                                     // mutato szam string-re
   U8 jelzo= 0;                                  // 0 - alapeset, egy beturol van szo 
                                                 // 1 - % utan
                                                 // 2 - . utan
   U8 c;                                         // egy betu tarolasara
   U8 balra= 0;                                  // hova legyen utkoztetve a string
   U8 hossz= 0;                                  // string hossza
   U8 elojel_betu= 0;                            // elojel
   U8 tizedesjegy= 0;                            // tizedes jegyek szama
   U8 feltolto_betu= ' ';                        // betu, string feltoltesehez
   va_start( ap, frmt);
   f= frmt;                                      // formatum string
   t= buffer;                                    // buffer eleje
   while( (c= pgm_read_byte( f++)))              // formatum string beolvasasa betunkent
   {
      if( ( jelzo == 0) && ( c != '%')) *t++= c; // kiirando betu
      else
      {
         switch( c)
         {
            case '%': 
               if( jelzo)                        // masodik %
               {
                  *t++= c;
                  jelzo= 0;
               }
               else                              // elso % eseten
               {
                  jelzo= 1;                      // jelzo beallitasa
                  balra= 0;                      // alap ertekek beallitasa
                  hossz= 0;
                  elojel_betu= 0;
                  tizedesjegy= 0;
                  feltolto_betu= ' ';
               }
               break;
            case '-': 
               if( jelzo == 1) balra= 1;         // balra kell utkoztetni
               break;
            case '.': 
               if( jelzo == 1) jelzo= 2;         // tizedespont utani ertek fog kovetkezni
               if( hossz) hossz++;               // tizedespont hosszaval valomegnoveles
               break;
            case '0': 
            case '1': 
            case '2': 
            case '3': 
            case '4': 
            case '5': 
            case '6': 
            case '7': 
            case '8': 
            case '9': 
               if( jelzo == 1)                   // ha % utan vagyunk
               {                                 // kezdo 0, nullaval valo feltoltest jelol
                  if( ( c == '0') && (hossz == 0)) feltolto_betu= '0';
                  else
                  {
                     if( hossz) hossz*= 10;      // tobb szamjegy eseten
                     hossz+= c- '0';             // hosszusag beallitasa
                  }
               } 
               if( jelzo == 2)                   // ha tizedespont utan vagyunk
               {  
                  if( tizedesjegy) tizedesjegy*= 10;
                  tizedesjegy+= c- '0';          // tizedesjegyek beallitasa
               }
               break; 
            case 'b':                            // binaris kiiras - 00000000..11111111
               u= 0x00FF & va_arg( ap, unsigned int);
               n= kpri_u2s( u, 2, 0, 0);
               t= kpri_pad( n, t, 8, 1, '0');
               jelzo= 0;                         // visszaalitas alapra
               break;
            case 'c':                            // 1..hossz char kiirasa
               c= (char)va_arg( ap, U16); 
               if( hossz == 0) hossz= 1;
               while( hossz--) *t++= c; 
               jelzo= 0;
               break;
            case 'd':                            // signed int decimalis kiirasa - +/- 0..32767
               i= va_arg( ap, int);
               if( i < 0)                        // negativ szam eseten
               {
                  i= -i;
                  elojel_betu= '-';
               }   
               n= kpri_u2s( (U16)i, 10, elojel_betu, tizedesjegy);
               t= kpri_pad( n, t, hossz, balra, feltolto_betu);
               jelzo= 0;
               break;
            case 'u':                            // unsigned int decimalis kiirasa - 0..65535
               u= va_arg( ap, U16);
               n= kpri_u2s( u, 10, 0, tizedesjegy);
               t= kpri_pad( n, t, hossz, balra, feltolto_betu);
               jelzo= 0;
               break;
            case 'x':                            // unsigned int hexadecimalis kiirasa - 0..FFFF
               u= va_arg( ap, U16);
               n= kpri_u2s( u, 16, 0, tizedesjegy);
               t= kpri_pad( n, t, hossz, balra, feltolto_betu);
               jelzo= 0;
               break;
            case 's':                            // string beszurasa
               s= va_arg( ap, char *);
               t= kpri_pad( s, t, hossz, balra, feltolto_betu);
               jelzo= 0;
               break;
         }
      }
   }
   *t= '\0';                                     // string vege
   va_end( ap);
   return buffer;  
}




/******************************************************************************* 
*   Author       -  Kiraly Tibor
*                   http://www.tkiraaly.hu
*   Date         -  2014.09.06.
*
*   string levagasa, feltoltese bal/jobb oldalt es athelyezese
*
*   honnan - hol talalhato a string
*   hova - hova kell atmasolni a string-et
*   hossz - a string hossza
*   balra - balra vagy jobbra kell utkoztetni
*   feltolto_betu - ha rovidebb mint hossz, az ures betuk helyere irando betu 
*
*******************************************************************************/ 
char * kpri_pad(
   char * honnan,
   char * hova,
   U8 hossz,
   U8 balra,
   U8 feltolto_betu
)
{
   char * p;
   U8 l= 0;
   p= honnan;
   while( *p++) l++;                                  // hossz kiszamolasa - strlen()
   if( hossz == 0) hossz= l;                          // hossz == 0 eseten a teljes hossz 
   while( l > hossz )                                 // vagas, ha a string hoszabb mint a megadott hossz
   {
      l--;
      honnan++; 
   }
   l= hossz- l;                                       // hany feltolto betu kell
   if( !balra) while( l--) *hova++= feltolto_betu;    // feltoltes string-tol balra
   while( *honnan) *hova++= *honnan++;                // string masolasa 
   if( balra) while( l--) *hova++= feltolto_betu;     // feltoltes string-tol jobbra
   return hova;                                       // kovetkezo char helye
}




/******************************************************************************* 
*   Author       -  Kiraly Tibor
*                   http://www.tkiraaly.hu
*   Date         -  2014.09.06.
*
*   unsigned int szam konvertalasa a kivant alapu szamrendszerbeli string-re
*
*   elojel_betu - ha 0, nincs elojel
*   tizedesjegy - tizedespont utani szamjegyek szama, ha 0, nincs tizedespont
*
*******************************************************************************/ 
char * kpri_u2s(
   U16 szam,
   U16 alap,
   U8 elojel_betu,
   U8 tizedesjegy
)                                                // szam -> string
{
   static char buffer[10];
   U8 n= 0;                                      // betuk szama
   char * p; 
   p= &buffer[ sizeof( buffer)- 1];
   *p= '\0';                                     // string vege, hatulrol elore
   if( tizedesjegy) tizedesjegy++;
   do
   {
      n++;
      p--;          
      if( n == tizedesjegy) *p= '.';             // ha tizedespont
      else
      {
         *p= szam% alap;                         // maradek a szamjegy
         if( *p > 9) *p+= 55;                    // 9-nel nagyobb, akkor ABC..
         else *p+= '0';                          // 9-ig szamjegy
         szam/= alap;
      }
   } while( szam != 0);
   if( elojel_betu) *--p= elojel_betu;           // ha van elojel
   return p;                                     // mutato a stringre
}

Itt a vége, fussatok el vélem (én nem futok), legyetek az én vendégeim, innen letölthetitek a hozzávalókat összecsomagolva.