STC8G1K08A demo mínimo de UART hello world em 349 bytes

Veja também:

Em nosso post anterior STC8G1K08A demo mínimo de UART hello world mostramos como usar a FwLib_STC8 para escrever uma aplicação UART hello world simples para o STC8G1K08A com pouco mais de 2.2 KB de tamanho.

Embora esse exemplo seja muito simples, ele sofre de um grande problema relacionado ao conjunto de recursos do SDCC: No momento, o SDCC não suporta a remoção de funções não utilizadas da saída do linker. Isso significa que se você usar qualquer função da biblioteca UART, acaba linkando a biblioteca inteira, o que adiciona muita sobrecarga de tamanho de código.

Para corrigir isso, podemos inlinar e otimizar um pouco o código.

main.c
#include "fw_uart.h"

/* Mantenha o baud rate em um lugar só para que a fórmula de reload do Timer1 permaneça óbvia. */
#define UART1_BAUD_RATE 115200UL

/* Reproduza o cálculo de SYSCLK da biblioteca sem chamar nenhum código de runtime. */
#define UART1_SYSCLK_HZ (__CONF_FOSC / ((__CONF_CLKDIV == 0) ? 1UL : (unsigned long)__CONF_CLKDIV))

/* O modo 1 da UART1 usa o overflow do Timer1 dividido por quatro como clock de baud rate. */
#define UART1_TIMER1_RELOAD (65536UL - (UART1_SYSCLK_HZ / (4UL * UART1_BAUD_RATE)))

/* Mantenha a mensagem inteira em uma área de memória para que a biblioteca possa transmiti-la. */
static unsigned char uart_message[] = "Hello, world from UART1!\r\n";

static void UART1_SendString(unsigned char *str)
{
    while (*str)
    {
        SBUF = *str++;
        TI = 0;
        while (!TI)
        {
        }
    }
}

#define TICKS_MS (__CONF_FOSC / ((__CONF_CLKDIV == 0) ? 1UL : (unsigned long)__CONF_CLKDIV) / 9000UL)

static void delay_ms(unsigned int ms)
{
    unsigned int i;

    do
    {
        i = TICKS_MS;
        while (--i)
        {
        }
    } while (--ms);
}

static void UART1_Init(void)
{
    /*
     * Abra o banco SFR estendido para podermos escrever CLKDIV antes de iniciar a UART1.
     * B7 = 1: habilita o acesso à região SFR estendida.
     * B6..B0 = 0: deixa os demais controles de troca de banco e roteamento de periféricos inalterados.
     */
    P_SW2 = 0x80;

    /* Aplique o divisor de clock configurado com uma escrita direta no registrador inteiro. */
    CLKDIV = __CONF_CLKDIV;

    /*
     * Retorne ao banco SFR normal agora que o divisor de clock está programado.
     * B7 = 0: desabilita o acesso SFR estendido novamente.
     * B6..B0 = 0: mantém os controles restantes do P_SW2 inativos neste demo mínimo.
     */
    P_SW2 = 0x00;

    /*
     * Selecione a banda do oscilador RC interno de alta velocidade para a qual os valores de trim foram calibrados.
     * 0x03 mantém apenas IRCBAND[1:0], que são os bits de seleção de banda.
     * Quaisquer bits superiores no valor de configuração são descartados para tocarmos apenas o campo de banda documentado.
     */
    IRCBAND = (__CONF_IRCBAND & 0x03);

    /* Carregue o byte de trim de tensão correspondente para aquela banda do RC. */
    VRTRIM = __CONF_VRTRIM;

    /* Carregue o byte de trim fino do RC de alta velocidade. */
    IRTRIM = __CONF_IRTRIM;

    /*
     * Carregue o campo de trim do RC de baixa velocidade mesmo este demo rodando a partir do clock interno rápido.
     * 0x03 mantém apenas LIRTRIM[1:0], que são os bits válidos de trim de baixa velocidade.
     * Quaisquer bits superiores são forçados em nível baixo para não escrevermos valores indefinidos naquele registrador.
     */
    LIRTRIM = (__CONF_LIRTRIM & 0x03);

    /*
     * Coloque a UART1 no modo assíncrono padrão de 8 bits e permita que o receptor funcione.
     * 0x50 = 0101 0000b.
     * SM0 = 0, SM1 = 1: seleciona o modo 1 da UART, a UART usual de 8 bits com baud rate variável.
     * SM2 = 0: desabilita a filtragem de endereço multiprocessador, de modo que cada byte recebido é aceito.
     * REN = 1: habilita o receptor da UART.
     * TB8 = 0, RB8 = 0: o estado do nono bit de transmissão/recepção começa zerado porque o modo 1 não o utiliza.
     * TI = 0, RI = 0: limpa ambos os flags de status de transmissão e recepção antes do primeiro caractere.
     */
    SCON = 0x50;

    /*
     * Configure o Timer1 como o gerador de baud rate a partir do qual a UART1 contará.
     * 0x40 = 0100 0000b.
     * B6 = 1: roda o Timer1 no modo 1T para que ele incremente a cada clock do sistema em vez de a cada 12 clocks.
     * B0 = 0: mantém a fonte de baud da UART1 no Timer1 em vez de trocá-la para o Timer2.
     * B5..B1 = 0: deixa os recursos não relacionados do AUXR desabilitados neste demo.
     */
    AUXR = 0x40;

    /*
     * Coloque o Timer1 no modo de reload esperado pelo caminho de código UART da FwLib.
     * 0x00 = 0000 0000b.
     * GATE1 = 0: o Timer1 inicia e para apenas a partir de TR1, não a partir do pino INT1 externo.
     * C/T1 = 0: o Timer1 conta ticks de clock, não eventos externos.
     * M1_1 = 0, M0_1 = 0: seleciona o modo do Timer1 que esta biblioteca STC8 usa para a geração de baud da UART.
     * A metade Timer0 do TMOD também é limpa porque este demo não usa o Timer0.
     */
    TMOD = 0x00;

    /* Carregue o byte alto do valor de reload do Timer1 em tempo de compilação. */
    TH1 = (unsigned char)(UART1_TIMER1_RELOAD >> 8);

    /* Carregue o byte baixo para que o primeiro overflow já use o baud rate correto. */
    TL1 = (unsigned char)(UART1_TIMER1_RELOAD & 0xFF);

    /* Inicie o Timer1 para que a UART1 possa derivar seu timing de bit. */
    TR1 = 1;
}

void main(void)
{
    /* Uma variável de loop unsigned inicializada em zero fornece um delay compacto de faixa completa. */
    unsigned int settle = 0;

    UART1_Init();
    
    /* Entregue todo o buffer de mensagem terminado em NUL ao helper local de transmissão de string. */
    while (1)
    {
        UART1_SendString(uart_message);
        delay_ms(1000);
    }
}

Como gravar

Veja nosso post Como gravar o STC8G1K08A usando stcgal

Como verificar a saída serial

Verifique a saída usando

monitor.sh
picocom -b 115200 /dev/ttyUSB0

Uso de memória

memory_usage.txt
===== Memory usage summary for uart_minimal_size_demo =====
Internal RAM layout:
      0 1 2 3 4 5 6 7 8 9 A B C D E F
0x00:|0|0|0|0|0|0|0|0|S|S|S|S|S|S|S|S|
0x10:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x20:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x30:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x40:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x50:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x60:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x70:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x80:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0x90:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xa0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xb0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xc0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xd0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xe0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0xf0:|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|S|
0-3:Reg Banks, T:Bit regs, a-z:Data, B:Bits, Q:Overlay, I:iData, S:Stack, A:Absolute

Stack starts at: 0x08 (sp set to 0x07) with 248 bytes available.
No spare internal RAM space left.

Other memory:
   Name             Start    End      Size     Max     
   ---------------- -------- -------- -------- --------
   PAGED EXT. RAM                         0      256   
   EXTERNAL RAM     0x0001   0x0020      32     1024   
   ROM/EPROM/FLASH  0x0000   0x015c     349     8192   
==========================================================

com SDCC 4.2.0 #13081 - 349 bytes de flash é uma grande melhoria em relação aos 2.2 KB do exemplo anterior.

Note que você pode optar individualmente por apenas copiar funções da FwLib para o seu projeto (os termos liberais da licença Apache da FwLib_STC8 permitem isso, mas pode ser necessário manter a declaração de copyright), ou “inliná-las” todas juntas para comprimir o código ainda mais. Em nosso código acima, escolhemos fazer uma mistura de ambos.


Check out similar posts by category: STC8 Embedded C/C++