niedziela, 23 sierpnia 2015

printf - przekierowanie w LPCXpresso

Idealnym rozwiązaniem komunikacji mikrokontrolera ze światem zewnętrznym jest funkcja printf i jej podobne. Wystarczy odpowiednio przekierować wyście / wejście na odpowiednie urządzenie i korzystać z jej dobrodziejstw. To ona będzie się "martwić" za odpowiednie wyświetlenie tekstu, liczb całkowitych jak również zmiennoprzecinkowych.
W środowisku programistycznym LPCXpresso istnej możliwość utworzenia projektu z Semihosting, wtedy funkcja printf automatycznie będzie przekazywała rezultat swojego działania do LPCXpresso i jej wynik będzie widoczny podczas debuggowania w IDE. Jednak ta możliwość nie jest tematem postu, więcej nt. można przeczytać na stronie www.lpcware.com/content/faq/lpcxpresso/semihosting

Pierwszym krokiem jaki należy wykonać jest przełączenie projektu żeby korzystał z biblioteki Redlib (nohost), wg. kroków pokazanych na rysunku poniżej


Po przełączeniu biblioteki kod podany poniżej bez problemu powinien się skompilować

#include "LPC11xx.h"

int main(void) {

    printf( "Hello World\r\n" );

    while(1)  {}

    return 0 ;
}


Drugim krokiem jest napisanie własnych funkcji __sys_write, która odpowiada za wysyłanie danych do urządzenia oraz __sys_readc odpowiedzialnej za pobieranie danych z urządzenia. Ponieważ printf zamierzamy przekierować do UART'u należy napisać także funkcje incjalizującą UART.
Ponieważ kod źródłowy inicjalizujący UART zbędnie zajmowałaby zawartość postu przykładowy projekt wraz z funkcjami znajduje się tutaj.

Oto przykładowe ciało funkcji __sys_write
int _sys_write(int iFileHandle, char *pcBuffer, int iLength)
{
    unsigned int i;

    for (i = 0; i < iLength; i++)
    {
        UART_Sendchar(pcBuffer[i]); // wysyla znak na UART
    }
    return iLength;
}

 

Oto przykładowe ciało funkcji __sys_readc
int __sys_readc(void)
{
    char c = UART_Getchar();
    return (int)c;

 

Przykład wykorzystania przekierowania:
#include "LPC11xx.h"
#include <stdio.h>
#include "uart.h"

int main(void) {
    volatile static int i = 0 ;

    UART_Init( 115200 );    // ustawia UART na predkosc 115200

    printf( "Hello World\r\n" );

    while(1) {
        printf( "I=%d\r\n", i++ );
    }
    return 0 ;
}


Jak widać w kodzie powyżej programista musi pamiętać aby  użyć funkcji Uart_Init przed korzystaniem z printf. Elastyczność funkcji printf wiąże się z rozmiarem kodu generowanego przez kompilator, dla kodu przedstawionego powyżej generuje 12336 bajtów, aby zmniejszyć  rozmiar kodu wynikowego można użyć symbolu CR_INTEGER_PRINTF przekazanego do kompilacji który spowoduje, że funkcja printf nie będzie potrafiła wyświetlić liczb zmiennoprzecinkowych (float / double). Dodając do kompilacji symbol CR_INTEGER_PRINTF kod wynikowy zmniejszył się do 7416 bajtów. Jak powiadają prawdziwi programiści embedded "float nie jest potrzebny do życia" :) zawsze można napisać program w ten sposób by uniknąć stosowania liczb zmiennoprzecinkowych.