Крошечный 349-байтовый UART hello world демо для STC8G1K08A

Смотрите также:

В нашем предыдущем посте STC8G1K08A минимальный UART hello world демо мы показали, как использовать FwLib_STC8 для написания простого UART hello world приложения для STC8G1K08A, размер которого чуть превышает 2,2 КБ.

Хотя этот пример очень прост, он страдает от большой проблемы, связанной с набором функций SDCC: На данный момент SDCC не поддерживает удаление неиспользуемых функций из результата компоновки. Это означает, что если вы используете любую функцию из библиотеки UART, вы в итоге компонуете всю библиотеку целиком, что добавляет много избыточного кода.

Чтобы исправить это, мы можем встроить (inline) и несколько оптимизировать код.

main.c
#include "fw_uart.h"

/* Держим скорость передачи в одном месте, чтобы формула перезагрузки Timer1 оставалась очевидной. */
#define UART1_BAUD_RATE 115200UL

/* Воспроизводим вычисление SYSCLK библиотеки без вызова какого-либо кода во время выполнения. */
#define UART1_SYSCLK_HZ (__CONF_FOSC / ((__CONF_CLKDIV == 0) ? 1UL : (unsigned long)__CONF_CLKDIV))

/* UART1 режим 1 использует переполнение Timer1, делённое на четыре, в качестве источника тактов для скорости передачи. */
#define UART1_TIMER1_RELOAD (65536UL - (UART1_SYSCLK_HZ / (4UL * UART1_BAUD_RATE)))

/* Держим всё сообщение в одной области памяти, чтобы библиотека могла его передать. */
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)
{
    /*
     * Открываем расширенный банк SFR, чтобы можно было записать CLKDIV перед запуском UART1.
     * B7 = 1: разрешить доступ к расширенной области SFR.
     * B6..B0 = 0: оставить остальные управляющие биты переключения банков и маршрутизации периферии без изменений.
     */
    P_SW2 = 0x80;

    /* Применяем настроенный делитель частоты прямой записью всего регистра. */
    CLKDIV = __CONF_CLKDIV;

    /*
     * Возвращаемся к обычному банку SFR, теперь, когда делитель частоты запрограммирован.
     * B7 = 0: снова отключить доступ к расширенному SFR.
     * B6..B0 = 0: держим остальные управляющие биты P_SW2 неактивными в этом минимальном демо.
     */
    P_SW2 = 0x00;

    /*
     * Выбираем диапазон высокоскоростного внутреннего RC-генератора, под который были настроены значения триммирования.
     * 0x03 оставляет только IRCBAND[1:0], которые являются битами выбора диапазона.
     * Любые старшие биты в конфигурационном значении отбрасываются, чтобы мы затрагивали только задокументированное поле диапазона.
     */
    IRCBAND = (__CONF_IRCBAND & 0x03);

    /* Загружаем соответствующий байт триммирования напряжения для этого RC-диапазона. */
    VRTRIM = __CONF_VRTRIM;

    /* Загружаем байт точной настройки высокоскоростного RC. */
    IRTRIM = __CONF_IRTRIM;

    /*
     * Загружаем поле триммирования низкоскоростного RC, хотя это демо работает от быстрого внутреннего генератора.
     * 0x03 оставляет только LIRTRIM[1:0], которые являются допустимыми битами триммирования низкой скорости.
     * Любые старшие биты принудительно устанавливаются в низкий уровень, чтобы не записывать неопределённые значения в этот регистр.
     */
    LIRTRIM = (__CONF_LIRTRIM & 0x03);

    /*
     * Переводим UART1 в стандартный 8-битный асинхронный режим и разрешаем работу приёмника.
     * 0x50 = 0101 0000b.
     * SM0 = 0, SM1 = 1: выбираем режим UART 1, обычный 8-битный UART с переменной скоростью передачи.
     * SM2 = 0: отключаем фильтрацию адресов мультипроцессорного режима, поэтому каждый принятый байт принимается.
     * REN = 1: разрешаем приёмник UART.
     * TB8 = 0, RB8 = 0: состояние девятого бита передачи/приёма изначально сброшено, так как режим 1 его не использует.
     * TI = 0, RI = 0: очищаем оба флага статуса передачи и приёма перед первым символом.
     */
    SCON = 0x50;

    /*
     * Настраиваем Timer1 как генератор скорости передачи, от которого UART1 будет вести отсчёт.
     * 0x40 = 0100 0000b.
     * B6 = 1: запускаем Timer1 в режиме 1T, чтобы он инкрементировался каждый системный такт, а не каждые 12 тактов.
     * B0 = 0: оставляем источник скорости UART1 на Timer1, не переключая его на Timer2.
     * B5..B1 = 0: оставляем несвязанные функции AUXR отключёнными в этом демо.
     */
    AUXR = 0x40;

    /*
     * Переводим Timer1 в режим перезагрузки, ожидаемый кодом UART из FwLib.
     * 0x00 = 0000 0000b.
     * GATE1 = 0: Timer1 запускается и останавливается только от TR1, а не от внешнего вывода INT1.
     * C/T1 = 0: Timer1 считает такты частоты, а не внешние события.
     * M1_1 = 0, M0_1 = 0: выбираем режим Timer1, который эта библиотека STC8 использует для генерации скорости UART.
     * Половина Timer0 в TMOD также очищена, потому что это демо не использует Timer0.
     */
    TMOD = 0x00;

    /* Загружаем старший байт значения перезагрузки Timer1, вычисленного на этапе компиляции. */
    TH1 = (unsigned char)(UART1_TIMER1_RELOAD >> 8);

    /* Загружаем младший байт, чтобы самое первое переполнение уже использовало правильную скорость передачи. */
    TL1 = (unsigned char)(UART1_TIMER1_RELOAD & 0xFF);

    /* Запускаем Timer1, чтобы UART1 мог получать своё битовое время. */
    TR1 = 1;
}

void main(void)
{
    /* Инициализированная нулём переменная цикла даёт компактную задержку во всём диапазоне. */
    unsigned int settle = 0;

    UART1_Init();
    
    /* Передаём весь буфер сообщения, завершённый NUL, локальной вспомогательной функции передачи строки. */
    while (1)
    {
        UART1_SendString(uart_message);
        delay_ms(1000);
    }
}

Как прошить

Смотрите наш пост Как прошить STC8G1K08A с помощью stcgal

Как проверить последовательный вывод

Проверьте вывод с помощью

monitor.sh
picocom -b 115200 /dev/ttyUSB0

Использование памяти

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   
==========================================================

с SDCC 4.2.0 #13081 — 349 байт flash-памяти — это огромное улучшение по сравнению с 2,2 КБ предыдущего примера.

Обратите внимание, что вы можете по отдельности просто скопировать функции FwLib в свой проект (либеральные условия лицензии Apache FwLib_STC8 это позволяют, но вам может потребоваться сохранить уведомление об авторских правах) или «встроить» их все вместе, чтобы сжать код ещё больше. В нашем коде выше мы выбрали смешанный подход.


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