Вайб-монтаж печатных плат и такой же код

от автора

Рубрика пятничное.

Когда нужно что-то навскидку отладить, требуется довольно много времени на поиск необходимых инструментов поддержки в прямом смысле. Будь то 3D принтер, или то что необходимо склеить. Не всегда получается совместить материалы, обеспечить отсутствие механических воздействий, последующую ремонтопригодность или доступ осциллографом, например, залив печатные платы строительной пеной. Различные комбинации скотча и синей изоленты также имеют место быть но не всегда обеспечивают приятный результат.

Предлагаемый метод кружка кройки и шитья лишён подобного рода недостатков и позволяет произвести быстрое прототипирование с последующей упаковкой в подарочную коробочку для транспортировки, поработать с VsCode + CortexM3 MD + JTAG + шаговый двигатель и дисплей в автобусе, поезде или самолёте.

Как то необходимо было закрепить печатную плату с контроллером Cortex M3 Medium Density и дисплеем lcd 12864, драйвер шагового двигателя tmc2209, энкодер mt6701, JTAG — эмулятор.

Инструмент для печатных плат, и, как оказывается, для их монтажа.

Почти что челнок для намотки колец

Почти что челнок для намотки колец

Исходные материалы: печатные платы и капроновая нитка для товарной упаковки, где-то 0.5 мм диаметр, толстый полиэтиленовый или полипропиленовый/полистироловый (?) белый поролон, иногда выглядит как многослойный толщиной 3-4 см.

Примерный вид плат для монтажа - они не содержат каких-либо крепёжных отверстий, такие часто бывают для пластиковых отгибаемых фиксаторов-защёлок

Примерный вид плат для монтажа — они не содержат каких-либо крепёжных отверстий, такие часто бывают для пластиковых отгибаемых фиксаторов-защёлок

Порядок монтажа. Нет ничего проще: продеваем нить через паз инструмента и насквозь протыкаем поролон.

Технологическая операция 1 - продеть нить через поролон

Технологическая операция 1 — продеть нить через поролон

Далее во время обратного хода инструмента можно держать образовавшуюся петлю или продеть через неё держатель, всё что попалось под руку, например, резистор.

Технологическая операция 2 - фиксация петли

Технологическая операция 2 — фиксация петли

Далее показаны пример для крепления драйвера шагового двигателя

Подготовка к монтажу

Подготовка к монтажу

В результате получаем следующую конфигурацию

Драйвер надёжно зафиксирован и механически демпфирован

Драйвер надёжно зафиксирован и механически демпфирован

Аналогично производим монтаж главной печатной платы, она не содержит каких-либо отверстий, поэтому её крепим как хомутом

Привязываем основную плату в четырёх точках

Привязываем основную плату в четырёх точках

Ну и как же без контроля пакетов по UART. Для этого используем классическую платку с микросхемой FTDI. Но нам необходима экономия места, поэтому, делаем 3D-конструкцию. Прячем тестер последовательного порта в карман, вырезанный канцелярским ножом.

3D-монтаж

3D-монтаж

Для фиксации сбоку прошиваем дополнительно нитью, включаем USB, проверяем.

Подсветка питания и RX-TX создают настоящий вайб сквозь монтажную панель

Подсветка питания и RX-TX создают настоящий вайб сквозь монтажную панель

Для монтажа шагового двигателя используем отверстия, чуть ближе расположенные чем его, это позволяет сделать монтаж с натяжением

Шаговый двигатель надёжно примотан к демпферу

Шаговый двигатель надёжно примотан к демпферу

Конечный результат

Узелки и петли можно связывать между собой для улучшения крепёжных свойств

Узелки и петли можно связывать между собой для улучшения крепёжных свойств

Далее переходим к тому, что AI практически полностью с 0-я написал вполне приличную Embedded-систему, как будто бы сам вручную на С или Ассемблере вымучивал каждый байт или такт.

Чтобы не загромождать, перейдём сразу к наборам вайбов в виде md-саммари и результатов под спойлером, которые могут быть использованы для кодогенерации. MD-саммари это фактически скилл позволяющий модели реализовать +- стабильный код без лишних действий с напильником.

Поиск красивых моноширинных шрифтов
# Саммари: скрипт генерации превью шрифтов## Краткое описаниеСкрипт автоматически находит в системе все моноширинные шрифты и создаёт единое изображение (PNG) со списком их имён и тестовым текстом.## Как работает скрипт1. **Поиск шрифтов**:   * Используется утилита `fc-list`.   * Фильтруются только моноширинные шрифты (`spacing=100`).2. **Очистка данных**:   * Пути к файлам сортируются.   * Удаляются дубликаты.   * С помощью `basename` отсекаются лишние директории — остаются только имена файлов.3. **Рендеринг**:   * Для каждого шрифта с помощью ImageMagick создаётся строка текста в формате `ИмяФайла: abcdefghij`.4. **Сборка итогового изображения**:   * Все сгенерированные строки объединяются по вертикали.   * Результат сохраняется в один PNG‑файл.## Параметры и зависимости### Зависимости* `fontconfig` (для работы `fc-list`).* `ImageMagick` (для рендеринга текста и сборки изображения).  * Скрипт адаптируется под разные версии ImageMagick: использует `magick` либо старую команду `convert`.### Конфигурационные переменные* `OUTPUT_FILE` — имя итогового изображения (по умолчанию: `monospace_filenames.png`).* `FONT_SIZE` — размер шрифта (по умолчанию: 32).* `PREVIEW_TEXT` — шаблон текста для проверки отображения (по умолчанию: `abcdefghij`).
#!/bin/bash# ConfigurationOUTPUT_FILE="monospace_filenames.png"FONT_SIZE=32PREVIEW_TEXT="abcdefghij"# Compatibility check for ImageMagickCOMMAND=$(command -v magick || command -v convert)if [ -z "$COMMAND" ]; then    echo "Error: ImageMagick is not installed."    exit 1fiecho "Generating preview using filenames..."# 1. Fetch file paths for monospace fonts# 2. Sort to remove duplicates# 3. Use basename to extract just the filename (e.g., 'UbuntuMono-R.ttf')fc-list :spacing=100 -f "%{file}\n" | sort -u | while read -r path; do    if [ -f "$path" ]; then        # Get just the filename from the full path        fname=$(basename "$path")        $COMMAND -background white \                 -fill black \                 -font "$path" \                 -pointsize "$FONT_SIZE" \                 label:"$fname: $PREVIEW_TEXT" miff:-    fidone | $COMMAND - -append "$OUTPUT_FILE"echo "Done! Preview saved to: $OUTPUT_FILE"

Результат

Выбираем что понравилось

Выбираем что понравилось

Пишем генератор шрифтов, который формирует C-файл с битмапом. Ремарка. Для генерации необходима была обратная связь от того что было на экране, так как рендер и генератор взаимно связаны. Поэтому, для дальнейших изысканий необходимо дополнить h-файл некоторыми константами которые используются в генераторе, так как присутствует паддинг для букв, например, g, у, ц итд, имеющие относительно нижней линии заданное смещение. Оно также задаётся.

Промпты для вайбкода делал на английском, так как на нём бо́льшая кодовая база и по ощущениям генерация идёт чуточку получше, поэтому код с англ. комментариями.

Генератор шрифта с формированием .c и .h

Генератор шрифтов:

# Генератор шрифтов для встраиваемых системСкрипт на Python генерирует оптимизированные шрифтовые ресурсы для встраиваемых дисплеев. Создаёт три файла:* `display_fonts.c` — данные шрифта в формате C;* `fontdef.h` — заголовочный файл с конфигурацией;* `font_preview.png` — PNG‑превью сетки символов.## Ключевые константы конфигурации### Базовые параметры шрифта и сетки* **`FONT_PATH`** — путь к файлу шрифта (по умолчанию `cour.ttf`).* **`FONT_SIZE`** = $12$ — размер шрифта в пунктах.* **`WIDTH`** = $6$, **`HEIGHT`** = $12$ — размеры ячейки для символа в пикселях (ширина × высота).* **`FIRST_CHAR`** = `ord(' ')` ($32$) — начальный ASCII‑код символа.* **`LAST_CHAR`** = `ord('}')` ($125$) — конечный ASCII‑код символа.* **`COLS`** = $16$ — количество столбцов в сетке превью PNG.### Параметры выравнивания и типографики* **`V_ALIGN`** = `"BOTTOM"` — стратегия вертикального выравнивания: `"TOP"`, `"CENTER"` или `"BOTTOM"`.* **`USE_DUAL_BBOX`** = `True` — флаг использования точных пиксельных масок (`True`) или метрик движка шрифта (`False`).* **`NON_DESCENDER_OFFSET`** = $-2$ — сдвиг обычных символов (положительное значение — вниз, отрицательное — вверх).* **`DESCENDER_RELATIVE_OFFSET`** = $-1$ — относительный сдвиг символов с нижними выносными элементами (например, «g», «p»).* **`DESCENDER_CHARS`** — множество символов с выносными элементами ниже базовой линии: `set("gjpqy,;Q")`.### Параметры инверсии (для коррекции ориентации дисплея)* **`FLIP_HORIZONTAL`** = `False` — зеркальное отображение по горизонтали (если дисплей инвертирован слева направо).* **`FLIP_VERTICAL`** = `False` — зеркальное отображение по вертикали (если дисплей инвертирован сверху вниз).### Настройки превью* **`APPLY_FLIPS_TO_PREVIEW`** = `False` — флаг применения инверсий к превью:    * `True` — превью повторяет зеркальность дисплея;    * `False` — обычное отображение текста.## Алгоритм работы скрипта1. **Загрузка шрифта.** Пытается загрузить шрифт из `FONT_PATH`. Если не удаётся — использует шрифт по умолчанию.2. **Генерация списка символов.** Формирует список символов в диапазоне от `FIRST_CHAR` до `LAST_CHAR`.3. **Создание атласа PNG.** Инициализирует монохромное изображение (`'1'` mode) с размерами, рассчитанными по `COLS`, `WIDTH` и `HEIGHT`.4. **Обработка каждого символа:**    * создаёт отдельное изображение символа;    * рассчитывает bounding box (с учётом `USE_DUAL_BBOX`);    * применяет вертикальное выравнивание (`V_ALIGN`) и индивидуальные сдвиги (`NON_DESCENDER_OFFSET`, `DESCENDER_RELATIVE_OFFSET`);    * рисует символ с точным позиционированием;    * при необходимости применяет инверсию (`FLIP_HORIZONTAL`, `FLIP_VERTICAL`) к данным и превью.5. **Упаковка пикселей.** Преобразует пиксели символа в последовательность байтов, упакованных вертикально (по столбцам).6. **Формирование C‑файла.** Записывает данные шрифта в `display_fonts.c` как массив `uint8_t`.7. **Генерация заголовочного файла.** Создаёт `fontdef.h` с макросами, описывающими конфигурацию (размеры, флаги инверсии и т. д.).8. **Сохранение превью.** Масштабирует атлас и сохраняет его как `font_preview.png` для визуального контроля.## Выходные файлы* **`display_fonts.c`**    * содержит массив `fontCRC` с упакованными данными символов;    * каждый символ представлен последовательностью байтов в формате `0xHH`.* **`fontdef.h`**    * заголовочный файл с макросами конфигурации;    * включает размеры (`CRCFONT_WIDTH`, `CRCFONT_HEIGHT`), коды символов (`CRCFONT_FIRSTCHAR`, `CRCFONT_LASTCHAR`), флаги выравнивания и инверсии (`CRCFONT_VALIGN_BOTTOM`, `CRCFONT_VFLIP`, `CRCFONT_HFLIP`).* **`font_preview.png`**    * визуальное превью сетки символов;    * масштабировано в $4$ раза для удобства просмотра;    * позволяет проверить корректность выравнивания, инверсии и общего вида шрифта.
Генератор python-вывод
import mathfrom PIL import Image, ImageFont, ImageDraw# ==================== CONFIGURATION ====================FONT_PATH = "cour.ttf"       # Ensure this file is in your script directoryFONT_SIZE = 12                # Font size to renderWIDTH, HEIGHT = 6, 12        # Works perfectly with odd, even, large, or small gridsFIRST_CHAR = ord(' ')        # ASCII range start (32)LAST_CHAR = ord('}')         # ASCII range end (125)COLS = 16                     # Number of columns in the PNG preview grid# --- Alignment & Typographical Tuning ---V_ALIGN = "BOTTOM"            # Base alignment strategy: "TOP", "CENTER", or "BOTTOM"USE_DUAL_BBOX = True          # True: Use pixel-perfect masks | False: Use font-engine metrics# --- Fine-Grained Fine Tuning Controls ---NON_DESCENDER_OFFSET = -2     # Shift normal chars (+ down, - up). e.g., -2 lifts them up from baseline.DESCENDER_RELATIVE_OFFSET = -1 # Relative shift for descenders (+ down, - up) to position tails perfectly.# Characters that naturally drop below the baseline horizonDESCENDER_CHARS = set("gjpqy,;Q") # --- Hardware Orientation Inversion Toggles ---FLIP_HORIZONTAL = False       # Mirror Left-to-Right (True if hardware is H-mirrored)FLIP_VERTICAL = False         # Mirror Top-to-Bottom (True if hardware is V-mirrored)# --- Preview Customization ---APPLY_FLIPS_TO_PREVIEW = False # True: preview mimics H/W mirror | False: normal text preview# =======================================================def generate_font_assets():    try:        font = ImageFont.truetype(FONT_PATH, FONT_SIZE)    except Exception:        print(f"Warning: Could not load '{FONT_PATH}'. Using default font.")        font = ImageFont.load_default()    chars_to_gen = [chr(code) for code in range(FIRST_CHAR, LAST_CHAR + 1)]    rows = math.ceil(len(chars_to_gen) / COLS)        # Initialize PNG Atlas (1-bit monochrome mode)    atlas = Image.new('1', (COLS * WIDTH, rows * HEIGHT), 0)    bytes_per_col = math.ceil(HEIGHT / 8)        c_lines = [        '#include "fontdef.h"',        '#include <stdint.h>',        '',        '/* ',        ' * Ultimate Optimized Font Data',        f' * Configuration - H-Flip: {FLIP_HORIZONTAL}, V-Flip: {FLIP_VERTICAL}, V-Align: {V_ALIGN}',        ' * Packed vertically column-by-column to match display page layout.',        f' * Each column takes {bytes_per_col} bytes based on an exact height of {HEIGHT}px.',        ' */',        'const uint8_t fontCRC [] = {'    ]    for i, char in enumerate(chars_to_gen):        char_img = Image.new('1', (WIDTH, HEIGHT), 0)        draw = ImageDraw.Draw(char_img)                # 1. Fetch layout metrics bounding box using Left-Top anchor format        m_left, m_top, m_right, m_bottom = draw.textbbox((0, 0), char, font=font, anchor="lt")                # 2. Fetch True Pixel Mask Rendering Bounding Box        mask_tuple = font.getmask(char).getbbox() if USE_DUAL_BBOX else None                if USE_DUAL_BBOX and mask_tuple:            b_left, b_top, b_right, b_bottom = mask_tuple        else:            b_left, b_top, b_right, b_bottom = m_left, m_top, m_right, m_bottom                tw = b_right - b_left        th = b_bottom - b_top                if tw <= 0 or th <= 0:            x_off, y_off = 0, 0        else:            # Horizontal centering calculation relative to absolute left point            x_off = math.floor((WIDTH - tw) / 2.0) - b_left                        # --- VERTICAL ALIGNMENT MATH ---            align = V_ALIGN.upper()            if align == "TOP":                base_y = 0 - b_top            elif align == "BOTTOM":                base_y = (HEIGHT - th) - b_top            else:  # CENTER                base_y = math.floor((HEIGHT - th) / 2.0) - b_top                            # --- SEPARATED OFFSET TRIMMING ENGINE ---            if char in DESCENDER_CHARS:                # Independent shift for descenders relative to the base calculation                y_off = base_y + DESCENDER_RELATIVE_OFFSET            else:                # Independent shift for flat baseline characters                y_off = base_y + NON_DESCENDER_OFFSET        # Draw character safely with explicit positioning anchors        draw.text((x_off, y_off), char, font=font, fill=1, anchor="lt")                # Create matching preview glyph        preview_img = char_img.copy()                # --- Apply Hardware Inversion Adjustments to Data Image ---        if FLIP_HORIZONTAL:            char_img = char_img.transpose(Image.FLIP_LEFT_RIGHT)        if FLIP_VERTICAL:            char_img = char_img.transpose(Image.FLIP_TOP_BOTTOM)                # --- Apply Inversion Options to Preview Image ---        if APPLY_FLIPS_TO_PREVIEW:            if FLIP_HORIZONTAL:                preview_img = preview_img.transpose(Image.FLIP_LEFT_RIGHT)            if FLIP_VERTICAL:                preview_img = preview_img.transpose(Image.FLIP_TOP_BOTTOM)                        # Paste the transformed glyph into the PNG atlas grid        atlas.paste(preview_img, ((i % COLS) * WIDTH, (i // COLS) * HEIGHT))                pixels = char_img.load()        hex_values = []                # Pack raw pixel bits vertically based on active canvas dimensions        for x in range(WIDTH):            for b_idx in range(bytes_per_col):                byte_val = 0                start_y = b_idx * 8                                for bit in range(8):                    target_y = start_y + bit                    if target_y < HEIGHT:                        if pixels[x, target_y]:                            byte_val |= (1 << bit)                                            hex_values.append(f"0x{byte_val:02X}")                comment_char = '\\\\' if char == '\\' else char        c_lines.append(f"    {', '.join(hex_values)}, /* \"{comment_char}\" */")    c_lines.append("};")        # Write optimized array source data    with open("display_fonts.c", "w", encoding="utf-8") as f:        f.write("\n".join(c_lines) + "\n")            # --- Map high-level configuration properties to hardware flags ---    valign_flag = 1 if V_ALIGN.upper() == "BOTTOM" else 0    vflip_flag = 1 if FLIP_VERTICAL else 0    hflip_flag = 1 if FLIP_HORIZONTAL else 0    # Write tracking architecture configuration header definitions    h_lines = [        "#ifndef FONTDEF_H",        "#define FONTDEF_H",        "",        "#include <stdint.h>",        "",        f"#define CRCFONT_WIDTH           {WIDTH}",        f"#define CRCFONT_HEIGHT          {HEIGHT}",        f"#define CRCFONT_BYTES_PER_COL   {bytes_per_col}",        f"#define CRCFONT_BYTES_PER_CHAR  {WIDTH * bytes_per_col}",        f"#define CRCFONT_FIRSTCHAR       ((uint8_t)'{chr(FIRST_CHAR)}')",        f"#define CRCFONT_LASTCHAR        ((uint8_t)'{chr(LAST_CHAR)}')",        "",        "// --- NEW CRITICAL GENERATOR FLAGS ---",        f"#define CRCFONT_VALIGN_BOTTOM     {valign_flag}  // 1 if aligned to container bottom, 0 if top",        f"#define CRCFONT_VFLIP             {vflip_flag}  // 1 if bits flow upside down, 0 if normal",        f"#define CRCFONT_HFLIP             {hflip_flag}  // 1 if columns flow right-to-left, 0 if normal",        "",        "extern const uint8_t fontCRC [];",        "",        "#endif /* FONTDEF_H */"    ]        with open("fontdef.h", "w", encoding="utf-8") as f:        f.write("\n".join(h_lines) + "\n")        # Save a scaled preview sheet to inspect orientation transformations visually    preview_scale = 4    atlas_rescaled = atlas.resize(        (atlas.width * preview_scale, atlas.height * preview_scale),         resample=Image.NEAREST    )    atlas_rescaled.save("font_preview.png")    print(f"Success! Generated stable assets with split character group offset fine-tuning.")if __name__ == "__main__":    generate_font_assets()

Функция для рендеринга шрифта на С

С-рендер (пример)
// External display variables referenced by your graphics engineextern uint8_t display_buffer[DISPLAY_WIDTH * DISPLAY_PAGES];extern uint8_t display_update_needed;uint8_t Display_DrawCharFont(uint8_t x, uint8_t y, char ch){    // 1. Instant out-of-bounds boundary validation    if (ch < CRCFONT_FIRSTCHAR || ch > CRCFONT_LASTCHAR || x >= DISPLAY_WIDTH || y >= DISPLAY_HEIGHT) {        return 0;    }    // 2. Pre-calculate flash memory character pointer offset dynamically    const uint32_t char_offset = (uint32_t)(ch - CRCFONT_FIRSTCHAR) * CRCFONT_BYTES_PER_CHAR;    const uint8_t *font_ptr = &fontCRC[char_offset];    // 3. Compute buffer page mapping offsets and safe drawing boundaries    uint8_t start_page = y >> 3;         // Fast division by 8    uint8_t bit_shift  = y & 0x07;       // Fast modulo 8 calculation    uint8_t max_cols   = (x + CRCFONT_WIDTH <= DISPLAY_WIDTH) ? CRCFONT_WIDTH : (DISPLAY_WIDTH - x);    // 4. Generate dynamic master mask based strictly on font height macro (12 bits)    uint32_t master_mask = (1UL << CRCFONT_HEIGHT) - 1UL;    // 5. Blit vertical pixel strips across screen columns    for (uint8_t col = 0; col < max_cols; col++) {                // --- FINAL LEFT-RIGHT MIRROR CORRECTION ---        // Restore standard left-to-right sequence to achieve final horizontal orientation        uint8_t target_col = col;                 // --- NATIVE LITTLE-ENDIAN WORD ASSEMBLY ---        uint32_t glyph_32 = 0;        for (uint8_t b = 0; b < CRCFONT_BYTES_PER_COL; b++) {            glyph_32 |= ((uint32_t)font_ptr[(target_col * CRCFONT_BYTES_PER_COL) + b]) << (b * 8);        }        // Clean out any remaining padding artifacts outside the active font height        glyph_32 &= master_mask;        // --- VERTICAL MIRROR PIPELINE ---        // Reverses the bits within the active 12-bit window to keep text right-side up        uint32_t flipped_glyph = 0;        for (uint8_t i = 0; i < CRCFONT_HEIGHT; i++) {            if (glyph_32 & (1UL << i)) {                flipped_glyph |= (1UL << (CRCFONT_HEIGHT - 1 - i));            }        }        glyph_32 = flipped_glyph;                // Dynamically align both font strip and mask to coordinate 'y' bit shift        uint32_t shifted_glyph = glyph_32 << bit_shift;        uint32_t shifted_mask  = master_mask << bit_shift;        uint16_t base_idx = x + col;        uint8_t current_page = start_page;        // Loop dynamically through display pages until shifted mask bits are exhausted        while (shifted_mask > 0) {            if (current_page < DISPLAY_PAGES) {                uint8_t p_mask = (uint8_t)(shifted_mask & 0xFFUL);                                if (p_mask) {                    uint8_t p_data = (uint8_t)(shifted_glyph & 0xFFUL);                    uint16_t page_idx = (uint16_t)current_page * DISPLAY_WIDTH + base_idx;                                        display_buffer[page_idx] &= ~p_mask;                    display_buffer[page_idx] |= p_data;                }            } else {                break;             }            shifted_glyph >>= 8;            shifted_mask  >>= 8;            current_page++;        }    }    display_update_needed = 1;    return (CRCFONT_WIDTH + 1); }

Обратите внимание — можно оптимизировать битовые операции под конкретный шрифт и его свойства, например, кратное 8, 4, 2 итд

Пример битмапа:

//заголовочник#ifndef FONTDEF_H#define FONTDEF_H#include <stdint.h>#define CRCFONT_WIDTH           6#define CRCFONT_HEIGHT          12#define CRCFONT_BYTES_PER_COL   2#define CRCFONT_BYTES_PER_CHAR  12#define CRCFONT_FIRSTCHAR       ((uint8_t)' ')#define CRCFONT_LASTCHAR        ((uint8_t)'}')// --- NEW CRITICAL GENERATOR FLAGS ---#define CRCFONT_VALIGN_BOTTOM     1  // 1 if aligned to container bottom, 0 if top#define CRCFONT_VFLIP             0  // 1 if bits flow upside down, 0 if normal#define CRCFONT_HFLIP             0  // 1 if columns flow right-to-left, 0 if normalextern const uint8_t fontCRC [];#endif /* FONTDEF_H *///данные#include "fontdef.h"#include <stdint.h>/*  * Ultimate Optimized Font Data * Configuration - H-Flip: False, V-Flip: False, V-Align: BOTTOM * Packed vertically column-by-column to match display page layout. * Each column takes 2 bytes based on an exact height of 12px. */const uint8_t fontCRC [] = {    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* " " */    0x00, 0x00, 0x00, 0x00, 0x7C, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* "!" */    0x80, 0x03, 0x80, 0x00, 0x00, 0x00, 0x80, 0x03, 0x80, 0x00, 0x00, 0x00, /* """ */

Несмотря на фрагмент — данный С-скетч вполне осознаваем ИИ, имеет небольшое количество токенов и достаточен для генерации и коррекции чего бы то ни было.

Паддинг можно настраивать как угодно. Пример вывода файла со сгенерированным шрифтом

Варианты шрифтов

Артефакты: телефон фотографирует изменения значений, на заднем плане - прямоугольник (отрисовывание производится прямым наложением, без маски)

Артефакты: телефон фотографирует изменения значений, на заднем плане — прямоугольник (отрисовывание производится прямым наложением, без маски)

Генерация изображений. Используется преобразование png->С битмап с применением постеризации для ч/б изображения. Вначале RGB->B/W затем всё что больше 127 — белое, меньше — чёрное. Пиксели сохраняются

Генератор изображений
# Саммари кода: генератор bitmap‑изображений для встраиваемых систем## НазначениеСкрипт на Python преобразует изображение в монохромный bitmap и генерирует C‑файлы (`image.h` и `image.c`) для использования в встраиваемых системах (например, на микроконтроллерах).## Импорты- `os` — проверка существования файла изображения;- `math` — не используется в коде (возможно, планировался к использованию);- `PIL.Image` — работа с изображениями (открытие, масштабирование, конвертация).## Функция `generate_bitmap`### Параметры- `image_path` — путь к исходному изображению;- `output_name` (по умолчанию `"my_generated_image"`) — имя выходного объекта в C‑коде;- `max_w` (по умолчанию 128) — максимальная ширина изображения;- `max_h` (по умолчанию 64) — максимальная высота изображения.### Этапы обработки изображения1. **Открытие изображения**     Изображение открывается через `Image.open()`, получаются его исходные размеры.2. **Масштабирование**     - Рассчитываются коэффициенты масштабирования по ширине (`scale_w`) и высоте (`scale_h`).   - Выбирается минимальный коэффициент (`scale_factor`), чтобы сохранить пропорции.   - Если `scale_factor < 1.0`, изображение уменьшается с помощью `resize()` (метод Bicubic) до размеров, соответствующих ограничениям.3. **Конвертация в градации серого**     Изображение переводится в режим `'L'` (grayscale).4. **Бинаризация (чёрно‑белое изображение)**     Применяется порог `127`: пиксели ярче — белые (`1`), темнее — чёрные (`0`). Результат — изображение в режиме `'1'`.5. **Упаковка пикселей в байты**     - Пиксели (1 бит каждый) упаковываются в байты (8 бит), начиная со старшего бита (MSB first).   - Если общее число пикселей не кратно 8, оставшиеся биты дополняются нулями.6. **Генерация `image.h`**     Создаётся заголовочный файл с:   - защитой от множественного включения (`#ifndef IMAGE_H_`);   - подключением `<stdint.h>`;   - определением структуры `DisplayImage_t` (указатель на данные, ширина, высота);   - объявлением внешнего объекта с именем `output_name`.7. **Генерация `image.c`**     Создаётся файл реализации с:   - массивом `output_name_data` типа `const uint8_t`, содержащим упакованные данные изображения (в шестнадцатеричном формате, `0xXX`);   - инициализацией структуры `DisplayImage_t` с указанием указателя на данные, ширины и высоты.## Основной блок (`if __name__ == "__main__":`)- Проверяет существование файла `logo.png`.- Если файл найден, вызывает `generate_bitmap()` с ограничениями `max_w=128` и `max_h=64`.- Если файла нет, выводит инструкцию по размещению изображения.## Результаты выполненияПосле успешного запуска:- выводятся сообщения о масштабировании (если применимо) и успехе;- создаются файлы `image.h` и `image.c`, готовые к компиляции в проекте для микроконтроллера.
Питон-генератор С-битмапа
import osimport mathfrom PIL import Imagedef generate_bitmap(image_path, output_name="my_generated_image", max_w=128, max_h=64):    # 1. Open the image    img = Image.open(image_path)    orig_w, orig_h = img.size        # 2. Calculate proportional scale down factors    scale_w = max_w / orig_w    scale_h = max_h / orig_h    scale_factor = min(scale_w, scale_h)        # Only downscale if the image exceeds display limits    if scale_factor < 1.0:        new_w = int(orig_w * scale_factor)        new_h = int(orig_h * scale_factor)        # Apply high-quality Bicubic scaling        img = img.resize((new_w, new_h), Image.Resampling.BICUBIC)        print(f"Rescaled image from {orig_w}x{orig_h} down to {new_w}x{new_h}")        # 3. Convert to grayscale ('L' mode)    img_gray = img.convert('L')        # 4. Posterize by 255/2 threshold (127) to convert to sharp black and white    threshold = 127    img_bw = img_gray.point(lambda p: 1 if p > threshold else 0, mode='1')        width, height = img_bw.size    pixels = list(img_bw.getdata())        # 5. Pack 1-bit pixels into array of bytes (MSB first)    byte_array = []    current_byte = 0    bit_count = 0        for pixel in pixels:        current_byte = (current_byte << 1) | pixel        bit_count += 1                if bit_count == 8:            byte_array.append(current_byte)            current_byte = 0            bit_count = 0                # Pad trailing bits if total pixel count is not a multiple of 8    if bit_count > 0:        current_byte = current_byte << (8 - bit_count)        byte_array.append(current_byte)    # 6. Generate image.h    with open("image.h", "w") as h_file:        h_file.write("#ifndef IMAGE_H_\n#define IMAGE_H_\n\n")        h_file.write("#include <stdint.h>\n\n")        h_file.write("typedef struct {\n    const uint8_t *data;\n    uint8_t width;\n    uint8_t height;\n} DisplayImage_t;\n\n")        h_file.write(f"extern const DisplayImage_t {output_name};\n\n")        h_file.write("#endif // IMAGE_H_\n")    # 7. Generate image.c    with open("image.c", "w") as c_file:        c_file.write('#include "image.h"\n\n')        c_file.write(f"static const uint8_t {output_name}_data[{len(byte_array)}] = {{\n    ")                for i, byte in enumerate(byte_array):            c_file.write(f"0x{byte:02X}, ")            if (i + 1) % 12 == 0:                c_file.write("\n    ")                        c_file.write("\n};\n\n")        c_file.write(f"const DisplayImage_t {output_name} = {{\n")        c_file.write(f"    .data = {output_name}_data,\n")        c_file.write(f"    .width = {width},\n")        c_file.write(f"    .height = {height}\n")        c_file.write("};\n")    print(f"Success! Generated image.h and image.c for '{image_path}' ({width}x{height})")if __name__ == "__main__":    target_image = "logo.png"         if os.path.exists(target_image):        # Passes target layout max dimensions (128 width, 64 height)        generate_bitmap(target_image, max_w=128, max_h=64)    else:        print(f"Please place your source image as '{target_image}' or edit the script path variable.")

Готовая пиктограммка

Пример С-вывода

//заголовочник#ifndef IMAGE_H_#define IMAGE_H_#include <stdint.h>typedef struct {    const uint8_t *data;    uint8_t width;    uint8_t height;} DisplayImage_t;extern const DisplayImage_t my_generated_image;#endif // IMAGE_H_// реализация#include "image.h"static const uint8_t my_generated_image_data[190] = {    0xEF, 0xF8, 0x29, 0xFF, 0xFF, 0x1C, 0x00, 0x7F, 0xFE, 0xEC, 0x01, 0x67,    0xFF, 0xFE, 0x80, 0x09, 0xFF, 0xBF, 0xD0, 0x12, 0x8E, 0x87, 0xE0, 0x06,    0x96, 0x66, 0x3E, 0x1C, 0x3F, 0xD1, 0x0C, 0x04, 0x15, 0x83, 0x18, 0x04,....  const DisplayImage_t my_generated_image = {    .data = my_generated_image_data,    .width = 37,    .height = 41};...  /// функция для рендера пикторграммки    

Результат — на первом фото

Далее приведён саммари для драйвера дисплея (полный) который может быть использован для генерации необходимых функций и их согласования с Python-генератором шрифтов.

Саммари по функциям рисования для дисплея
# Саммари по файлу `display_draw.c`Данный файл содержит функции для рисования графических примитивов и вывода текста на монохромный дисплей с разрешением 128x64, управляемый контроллером ST7565R (или совместимым). Функции работают с буфером отображения, который затем отправляется на дисплей через соответствующие функции из `display.c`.## Общие сведения- **Цель файла**: Реализация функций отрисовки пикселей, линий, прямоугольников, текста и изображений.- **Размеры дисплея**: 128 пикселей в ширину, 64 пикселя в высоту. Данные в памяти организованы по страницам (8 страниц по 8 пикселей в высоту).- **Буферы**: Файл использует внешние буферы `display_buffer` и `last_display_buffer`, определенные в `display.c`. Переменная `display_update_needed` сигнализирует о необходимости обновления дисплея.## Ключевые функции и алгоритмы### 1. `Display_SetPixel(x, y, color)`Устанавливает или сбрасывает отдельный пиксель в буфере отображения.- **Алгоритм**:   - Проверяет, находится ли координата внутри границ дисплея.  - Вычисляет, к какой странице (`page = y / 8`) и какому байту в строке принадлежит пиксель.  - Модифицирует соответствующий бит в байте буфера с помощью битовых операций (`|=` для установки, `&=~` для сброса).  - Устанавливает флаг `display_update_needed`.### 2. `Display_DrawString(x, y, str)`Выводит строку символов на дисплей.- **Алгоритм**:  - Проверяет корректность входных данных.  - Циклически проходит по каждому символу строки.  - Для каждого символа вызывает `Display_DrawCharFont`, чтобы нарисовать его и получить его ширину.  - Увеличивает координату `x` на ширину символа для вывода следующего символа справа от предыдущего.  - Останавливается, если строка выходит за правый край экрана.### 3. `Display_DrawCharFont(x, y, ch)`Рисует один символ из встроенного шрифта (fontCRC) на экране. Это наиболее сложная функция, реализующая побитовую отрисовку шрифта.- **Алгоритм**:  - **Проверка границ**: Проверяет, что символ в пределах допустимого диапазона и что начальная координата `x` в пределах экрана.  - **Доступ к данным шрифта**: Рассчитывает смещение в массиве `fontCRC` на основе кода символа.  - **Работа со страницами**: Символ может занимать несколько страниц (вертикально). Функция определяет начальную страницу на экране, куда нужно рисовать (`start_page`).  - **Вертикальное отражение (Mirroring)**: Данные шрифта в `fontCRC` хранятся в инвертированном по вертикали виде. Перед отрисовкой функция "переворачивает" биты символа, чтобы он отображался правильно.  - **Поблочная отрисовка**: Символ рисуется по столбцам. Для каждого столбца из данных шрифта формируется 32-битное слово.  - **Сдвиг и наложение**: Слово со сдвинуто на `bit_shift` битов (смещение внутри страницы) и побитово накладывается на соответствующие байты в буфере отображения, используя маску, чтобы не затронуть соседние пиксели.  - **Межстрочное расстояние**: Функция всегда возвращает ширину символа плюс один пиксель (пробел), обеспечивая автоматическое расстояние между символами.### 4. `Display_DrawLine`, `Display_DrawRectangle`, `Display_FillRectangle`Функции для отрисовки линий, контуров и закрашенных прямоугольников.- **Алгоритм**:  - `DrawHLine` и `DrawVLine`: Используют цикл и `Display_SetPixel` для установки пикселей вдоль линии.  - `DrawRectangle`: Рисует четыре линии (две горизонтальные, две вертикальные).  - `FillRectangle`: Закрашивает прямоугольную область, устанавливая каждый пиксель в цикле.### 5. `Display_DrawImage(img, configuration)`Рисует монохромное изображение с возможностью масштабирования и отражения.- **Алгоритм**:  - Рассчитывает начальные координаты `start_x` и `start_y` на основе флагов выравнивания (центр, право, низ и т.д.).  - Циклически проходит по каждому пикселю исходного изображения.  - Рассчитывает его цвет по битовой маске из массива `img->data`.  - Если включены флаги `FLIP_LR` или `FLIP_TB`, координаты пикселя отражаются.  - Вызывает `Display_SetPixel` для вывода пикселя в итоговую позицию на экране с учетом `start_x` и `start_y`. ## Особенности реализации- **Оптимизация**: Функции содержат прямые проверки границ для немедленного выхода при выходе за пределы экрана, что улучшает производительность.- **Безопасность**: Используются беззнаковые типы данных и проверки, чтобы избежать ошибок переполнения.- **Зависимости**: Файл зависит от `fontdef.h` (описание шрифта), `display.h` (декларации буферов и функций) и `image.h` (структура изображения).
Саммари для драйвера дисплея и модуля SPI с использованием DMA
# Саммари: display.cДанный файл содержит реализацию драйвера для графического дисплея LCD12864/ER3805/ST7565R на микроконтроллере STM32. Основная особенность реализации — использование технологии DMA для передачи данных дисплею, что позволяет освободить ядро микроконтроллера и повысить общую производительность системы.## Общие сведенияДрайвер управляет дисплеем через интерфейс SPI. Для обеспечения гибкости, код использует двойную буферизацию (`display_buffer` и `last_display_buffer`):*   **`display_buffer`**: Текущий буфер кадра, в который рисуются все элементы (линии, строк, изображения).*   **`last_display_buffer`**: Буфер предыдущего кадра. Сравнивается с текущим, чтобы определить, какие именно страницы (строки) дисплея изменились. Это позволяет обновлять только измененные области (`differential update`), что ускоряет отрисов��у.Ключевым элементом является функция `Display_Update()`, которая выполняет фазированную передачу данных дисплею.## Подробное описание работы с DMADMA (Direct Memory Access) — это механизм, позволяющий периферийным устройствам (в данном случае, SPI) напрямую обращаться к памяти микроконтроллера, минуя CPU. Это критически важно для дисплеев, так как передача всего экрана (1024 байта) по SPI в режиме опроса (polling) занимает слишком много времени и блокирует ядро.### 1. Инициализация DMA (в `main.c`)Инициализация DMA выполняется в функции `SPI1_DMA_Init()`, которая вызывается из `main()` *до* инициализации дисплея (`Display_Init()`).*   **Каналы DMA**: Используются два канала DMA1:    *   `DMA1_Channel2` — для приема данных от SPI (`SPI1_RX`).    *   `DMA1_Channel3` — для передачи данных по SPI (`SPI1_TX`).*   **Буферы**: Для каждого канала выделены буферы в памяти:    *   `SPI1_TxBuffer[1024]` — буфер передачи.    *   `SPI1_RxBuffer[1024]` — буфер приема (в данном случае используется для приема данных от дисплея, если это нужно, или как "dummy" буфер).*   **Конфигурация канала передачи (DMA1_Channel3)**:    *   `DMA_PeripheralBaseAddr`: Указывается адрес регистра данных SPI (`&(SPI1->DR)`).    *   `DMA_MemoryBaseAddr`: Указывается адрес начала буфера передачи.    *   `DMA_DIR`: Направление `DMA_DIR_PeripheralDST` (данные из памяти в периферию).    *   `DMA_BufferSize`: Размер буфера (1024 байта).    *   `DMA_MemoryInc`: Увеличение адреса памяти включено (`DMA_MemoryInc_Enable`), чтобы последовательно читать весь буфер.    *   `DMA_PeripheralInc`: Увеличение адреса периферии выключено (`DMA_PeripheralInc_Disable`), так как данные всегда пишутся в один и тот же регистр SPI.    *   `DMA_Mode`: Установлен в `DMA_Mode_Normal`, что означает передачу одного блока данных.    *   `DMA_Priority`: Приоритет `High`.*   **Включение DMA для SPI**: После конфигурации каналов, включаются запросы DMA для SPI:    *   `SPI_I2S_DMACmd(SPI1, SPI_I2S_DMAReq_Tx, ENABLE)` — разрешение DMA для передачи.*   **Включение прерываний**: Включаются прерывания от DMA по завершению передачи (`DMA_IT_TC`), чтобы CPU мог узнать, когда передача закончена.### 2. Процесс передачи данных с использованием DMAПроцесс обновления дисплея (вызывается из `Display_Update()`) состоит из нескольких этапов:1.  **Настройка позиции (Polling)**: Сначала на дисплее устанавливается позиция (страница и столбец) с помощью команд. Этот этап выполняется в режиме опроса (без DMA), чтобы гарантировать точное позиционирование.2.  **Инициализация DMA-пересылки (DMA)**: Вызывается функция `SPI1_DMA_Transfer()`. Она:    *   Отключает каналы DMA.    *   Устанавливает адрес источника данных в памяти (`txData`) как указатель на начало буфера текущей страницы.    *   Устанавливает размер передачи (128 байт — ширина одной страницы дисплея).    *   Очищает флаги завершения передачи.    *   Включает каналы DMA (сначала `DMA1_Channel2`, затем `DMA1_Channel3`).        С этого момента передача данных происходит автоматически: DMA-контроллер считывает байт из `display_buffer` и помещает его в регистр `SPI1->DR`. SPI автоматически начинает передачу по шине. Этот процесс выполняется независимо от CPU.3.  **Ожидание завершения (Блокировка)**: Вызывается `SPI1_DMA_WaitForTransfer()`. Эта функция входит в цикл ожидания флага `SPI1_TransferComplete`, который устанавливается в обработчике прерывания `DMA1_Channel3_IRQHandler()` при завершении передачи одного блока данных. После этого, проверяется флаг `SPI_I2S_FLAG_BSY` в регистре статуса SPI, чтобы убедиться, что последний бит физически покинул вывод микроконтроллера.### 3. ПрерыванияПрерывания играют ключевую роль в асинхронной работе DMA и SPI:*   **`DMA1_Channel3_IRQHandler()`**: Это прерывание срабатывает, когда DMA завершил передачу всего блока данных, указанного в `CNDTR`. В обработчике устанавливается флаг `SPI1_TransferComplete = 1`, который `SPI1_DMA_WaitForTransfer()` использует для выхода из цикла ожидания.*   **`DMA1_Channel2_IRQHandler()`**: Обрабатывает прерывание по завершению приема данных (если данные с дисплея читаются через DMA). В данном случае, он просто очищает флаг.*   **Конфигурация в `SPI1_DMA_Init()`**: Прерывания для каналов DMA1 Channel2 и Channel3 явно включаются через `NVIC_EnableIRQ(DMA1_Channel2_IRQn)` и `NVIC_EnableIRQ(DMA1_Channel3_IRQn)`.Таким образом, комбинация DMA и прерываний позволяет эффективно управлять высокоскоростным SPI-интерфейсом, освобождая CPU для выполнения других задач, таких как обработка данных с энкодера, мотора или формирование нового кадра для отображения.

В итоге:

Таким образом, ИИ можно смело использовать для разработки кода для микроконтроллеров, при этом, обязательно использовать тесты, включая для целевой платформы, например QEMU, а также ввод-вывод для отладочной информации, включая assertion, контроль регистров, стека, памяти, секций размещения, включая настройки DMA, периферии и др.

Язык Markdown, по всей видимости, станет первой строчкой в рейтингах популярности уже в ближайшем будущем. Также, важны необходимые MCP, промпт-инъекции, методики тестирования, вспомогательные скиллы, текстовые эмбеддеры для кодовой базы и поиска в ней а также нативные средства компилятора для рефакторинга и управления кодом.

ссылка на оригинал статьи https://habr.com/ru/articles/1037042/