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.