USMI

Объявление

12/06/2025 (Administration) - Spamming for the purpose of boosting messages will be punished with a ban. Спам с целью накрутки сообщений будет караться баном.

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » USMI » MCU, SoC, CPU Микроконтроллеры » Подключение разной периферии к JL SoC. SPI/I2C/I2S/PWM/UART/GPIO...


Подключение разной периферии к JL SoC. SPI/I2C/I2S/PWM/UART/GPIO...

Сообщений 241 страница 253 из 253

241

Вот драйвер ST7789 (только для разрешения 240x240, без CS, без выделения памяти в куче, без использования внешних библиотек дисплея, без разделения времени рендеринга.
Без выбора микросхемы через GPIO; панель считается постоянно выбранной). .h, вы можете поместить его в apps/include/cpu/ui/lcd. Не забудьте добавить его в sdk/project.
/*

* Рабочий объект ST7789, поставляемый в SDK, использует SPI1B:

* PC4 = SCK, PC5 = MOSI, PC3 = D/C, PB5 = RESET.
*/
https://drive.google.com/file/d/1XPJqIb … sp=sharing

Цвет текста зеленый, вы можете изменить его, отредактировав директиву #define ST7789_BACKGROUND_COLOR в файле st7789_backend.h (например, #ifndef ST7789_BACKGROUND_COLOR
#define ST7789_BACKGROUND_COLOR 0x0000u
#endif делает цвет зеленым, 0xFFFFu — белым, если вы хотите монохромный вариант).

Я ещё работаю над этим, но это должно работать прямо из коробки.

Отредактировано barbaror4 (2026-08-06 16:05:40)

Подпись автора

Türkiye

242

https://upforme.ru/uploads/001b/ca/8a/339/t850631.jpg
https://upforme.ru/uploads/001b/ca/8a/339/t554296.jpg

Подпись автора

Türkiye

243

Отличная работа, хорошо хоть кто то работает с 692x...

Подпись автора

USMicro® 2026©

244

BIOS написал(а):

Отличная работа, хорошо хоть кто то работает с 692x...

Да )) Я не покину эту платформу, пока мой чип не выйдет из строя из-за того, что я постоянно что-то делаю. ))

Подпись автора

Türkiye

245

Что ж, я решил попробовать интегрировать ST7920, экран, известный своими глюками. Посмотрим, какие странности у меня получатся...

Подпись автора

Türkiye

246

https://upforme.ru/uploads/001b/ca/8a/339/t101129.jpg
Я думаю, я проверил все имеющиеся у меня ЖК-экраны.   :D

Подпись автора

Türkiye

247

Подключено через i2c с микросхемой контроллером?

Подпись автора

USMicro® 2026©

248

BIOS написал(а):

Подключено через i2c с микросхемой контроллером?

Нет, я интегрировал этот странный протокол, похожий на SPI с тремя проводами, хотя он невероятно медленный, но, похоже, работает. Я всё ещё экспериментирую, но в основном сосредоточусь на этом.

Подпись автора

Türkiye

249

Вот драйвер для ST7920, он работает, но наблюдаются очень незначительные, но заметные подтормаживания звука. Мне не удалось решить эту проблему. Есть какие-нибудь рекомендации?

Спасибо

st7920_backend.c

/******************************************************************************
* File        : st7920_backend.c
* Target      : JieLi AC6925A / AC692x SDK V2.6.3
* Display     : Sitronix ST7920, 128x64, native 3-wire serial interface
*
* Protocol note:
*   ST7920 serial framing packs each logical command/data byte into three
*   byte-aligned groups:
*     byte0 = 1 1 1 1 1  RW  RS  0      (sync + control)
*     byte1 = D7 D6 D5 D4 0 0 0 0       (high nibble)
*     byte2 = D3 D2 D1 D0 0 0 0 0       (low nibble)
*   which is exactly three standard 8-bit SPI transfers, MSB first. This
*   lets a normal hardware SPI peripheral drive the panel instead of
*   bit-banging SCLK/SID by hand, the way lcd_disp.c bit-bangs I2C.
*
* GDRAM addressing note:
*   128x64 graphic mode on ST7920 is NOT a flat linear GDRAM. Vertical
*   address only spans 0..31; the bottom half of the screen (rows 32..63)
*   is reached by adding 8 to the horizontal WORD index instead. That is:
*
*     vertical   = row & 0x1F
*     horizontal = word_index + ((row >= 32) ? 8 : 0)
*
*   where word_index is 0..7 and each word covers 16 pixel columns.  The
*   two halves require separate Y/X address pairs; GDRAM horizontal auto
*   increment wraps from 0x0F to 0x00 and does not move to the other half.
******************************************************************************/

#include "st7920_backend.h"

#include "common/common.h"
#include "hw_cpu.h"
#include "cpu/irq_api.h"
#include "ui/ui_api.h"
#include "uicon/ui_con.h"
#include "uicon/menu.h"
#include "uicon_api.h"
#include "font_api.h"

#include <string.h>

#if LCD_128X64_EN

/******************************************************************************
*                         COMMAND CONSTANTS
******************************************************************************/

/* Basic instruction set (RE=0) */
#define ST7920_CMD_DISPLAY_CLEAR            0x01u
#define ST7920_CMD_ENTRY_MODE               0x06u  /* I/D=1, S=0 */
#define ST7920_CMD_DISPLAY_ON               0x0Cu  /* D=1 C=0 B=0 */
#define ST7920_CMD_DISPLAY_OFF              0x08u
#define ST7920_CMD_FUNCTION_SET_BASIC       0x30u  /* DL=1 RE=0 */

/* Extended instruction set (RE=1) */
#define ST7920_CMD_FUNCTION_SET_EXTENDED    0x34u  /* DL=1 RE=1 G=0 */
#define ST7920_CMD_FUNCTION_SET_GRAPHIC_ON  0x36u  /* DL=1 RE=1 G=1 */
#define ST7920_CMD_SET_GDRAM_ADDR           0x80u  /* | vertical or horiz. */

#define ST7920_SERIAL_SYNC_WRITE            0xF8u  /* sync + RW=0 + RS=0 */
#define ST7920_RS_BIT                       BIT(1)

#define ST7920_WORDS_PER_ROW                8u     /* 128px / 16px-per-word */
#define ST7920_PAGE_BYTES                   ST7920_DISP_WIDTH
#define ST7920_ALL_PAGE_MASK                ((u8)((1u << ST7920_DISP_PAGES) - 1u))

/******************************************************************************
*                         GPIO PORT SELECTION
******************************************************************************/

#if ST7920_CS_PORT_SELECT == ST7920_GPIO_PORT_A
#define ST7920_CS_PORT                       JL_PORTA
#elif ST7920_CS_PORT_SELECT == ST7920_GPIO_PORT_B
#define ST7920_CS_PORT                       JL_PORTB
#elif ST7920_CS_PORT_SELECT == ST7920_GPIO_PORT_C
#define ST7920_CS_PORT                       JL_PORTC
#elif ST7920_CS_PORT_SELECT == ST7920_GPIO_PORT_D
#define ST7920_CS_PORT                       JL_PORTD
#else
#error "Invalid ST7920_CS_PORT_SELECT value."
#endif

#if ST7920_RESET_PORT_SELECT == ST7920_GPIO_PORT_A
#define ST7920_RESET_PORT                    JL_PORTA
#elif ST7920_RESET_PORT_SELECT == ST7920_GPIO_PORT_B
#define ST7920_RESET_PORT                    JL_PORTB
#elif ST7920_RESET_PORT_SELECT == ST7920_GPIO_PORT_C
#define ST7920_RESET_PORT                    JL_PORTC
#elif ST7920_RESET_PORT_SELECT == ST7920_GPIO_PORT_D
#define ST7920_RESET_PORT                    JL_PORTD
#else
#error "Invalid ST7920_RESET_PORT_SELECT value."
#endif

#define ST7920_CS_MASK                       BIT(ST7920_CS_PIN_INDEX)
#define ST7920_RESET_MASK                    BIT(ST7920_RESET_PIN_INDEX)

/* SPI1 register bits/values.  Although the ST7920 bit-bang example starts
* with SCLK low, this AC6925 SPI1B route has been electrically proven on the
* connected 5V module only with CKID set.  Keep that known-good clock phase
* for the first hardware test; GDRAM addressing is independent from it. */
#define ST7920_SPI_CON_TRANSFER_DONE         BIT(15)
#define ST7920_SPI_CON_CLEAR_DONE            BIT(14)
#define ST7920_SPI_CON_DIRECTION_READ        BIT(12)
#define ST7920_SPI_CON_THREE_WIRE            BIT(3)
#define ST7920_SPI_CON_BASE                  0x4021u
#define ST7920_SPI_CON_ENABLED               (ST7920_SPI_CON_BASE | BIT(6))

#define ST7920_IOMAP_SPI1_GROUP_SELECT       BIT(4)
#define ST7920_IOMAP_SPI1_EXTRA_MASK         (BIT(18) | BIT(19))

#define ST7920_SPI_BLOCKING_TIMEOUT_LOOPS    200000UL

/******************************************************************************
*                         STATIC STORAGE
******************************************************************************/

u8 disp_buf[ST7920_DISP_PAGES * ST7920_DISP_WIDTH];

static volatile u8 st7920_spi_initialized;
static volatile u8 st7920_driver_available;

/******************************************************************************
*                         JIELI UI DESCRIPTORS
******************************************************************************/

const UI_LCD_IO ui_lcd_io = {
    .lcd_clear_area_callback            = lcd_clear_area,
    .lcd_clear_area_with_draw_callback  = lcd_clear_area_with_draw,
    .lcd_clear_area_rect_callback       = lcd_clear_area_rect,
    .lcd_TurnPixelReverse_Page_callback = lcd_TurnPixelReverse_Page,
    .lcd_TurnPixelReverse_Rect_callback = lcd_TurnPixelReverse_Rect,
};

const LCD_INFO lcd_info = {
    .ui_lcd_callback = (UI_LCD_IO *)&ui_lcd_io,
    .lcd_width       = ST7920_DISP_WIDTH,
    .lcd_height      = ST7920_DISP_HEIGHT,
    .lcd_buff        = disp_buf,
};

/******************************************************************************
*                         GPIO / DELAY HELPERS
******************************************************************************/

#define ST7920_CONFIGURE_OUTPUT(port, mask)  \
    do {                                    \
        (port)->DIR &= ~(mask);              \
        (port)->DIE &= ~(mask);              \
        (port)->PU  &= ~(mask);              \
        (port)->PD  &= ~(mask);              \
    } while (0)

static void st7920_gpio_cs(u8 enabled)
{
    /* Serial-mode RS pin doubles as CS: 1 = enabled, 0 = disabled/reset. */
    if (enabled) {
        ST7920_CS_PORT->OUT |= ST7920_CS_MASK;
    } else {
        ST7920_CS_PORT->OUT &= ~ST7920_CS_MASK;
    }
}

static void st7920_gpio_reset(u8 high)
{
    if (high) {
        ST7920_RESET_PORT->OUT |= ST7920_RESET_MASK;
    } else {
        ST7920_RESET_PORT->OUT &= ~ST7920_RESET_MASK;
    }
}

static void st7920_gpio_initialize(void)
{
    ST7920_CONFIGURE_OUTPUT(ST7920_CS_PORT, ST7920_CS_MASK);
    ST7920_CONFIGURE_OUTPUT(ST7920_RESET_PORT, ST7920_RESET_MASK);

#if ST7920_USE_SPI1B
    ST7920_CONFIGURE_OUTPUT(JL_PORTC, BIT(4));
    ST7920_CONFIGURE_OUTPUT(JL_PORTC, BIT(5));
    /* CKID mode: clock idles high. SID remains fixed while CS is low. */
    JL_PORTC->OUT |= BIT(4);
    JL_PORTC->OUT &= ~BIT(5);
#else
    ST7920_CONFIGURE_OUTPUT(JL_PORTB, BIT(4));
    ST7920_CONFIGURE_OUTPUT(JL_PORTB, BIT(5));
    JL_PORTB->OUT |= BIT(4);
    JL_PORTB->OUT &= ~BIT(5);
#endif

    st7920_gpio_cs(0u);
    st7920_gpio_reset(1u);
}

extern void clear_wdt(void);

/* Microsecond-granularity busy-wait, calibrated the same way as the
* millisecond delay in st7789_backend.c. Re-check against actual CPU
* clock configuration before trusting the timing at high SPI baud rates. */
static void st7920_delay_us(u16 microseconds)
{
    volatile u32 loops;

    loops = (u32)microseconds * ST7920_DELAY_LOOPS_PER_US;
    while (loops--) {
        /* busy wait */
    }
}

static void st7920_delay_ms(u16 milliseconds)
{
    while (milliseconds--) {
        st7920_delay_us(1000u);
        clear_wdt();
    }
}

/******************************************************************************
*                         SPI1 TRANSPORT
******************************************************************************/

static void st7920_spi_initialize_once(void)
{
    if (st7920_spi_initialized) {
        return;
    }

    JL_SPI1->CON = 0;

#if ST7920_USE_SPI1B
#if ST7920_DISABLE_IIC_FOR_SPI1B
    JL_IIC->CON0 &= ~BIT(0);
#endif
    JL_IOMAP->CON1 |= ST7920_IOMAP_SPI1_GROUP_SELECT;
    JL_IOMAP->CON1 &= ~ST7920_IOMAP_SPI1_EXTRA_MASK;
#else
    JL_IOMAP->CON1 &= ~ST7920_IOMAP_SPI1_GROUP_SELECT;
#endif

    st7920_gpio_initialize();

    JL_SPI1->BAUD = ST7920_SPI_BAUD_DIV;
    JL_SPI1->CON = ST7920_SPI_CON_ENABLED;
    JL_SPI1->CON &= ~ST7920_SPI_CON_DIRECTION_READ;
    JL_SPI1->CON &= ~ST7920_SPI_CON_THREE_WIRE;
    JL_SPI1->CON |= ST7920_SPI_CON_CLEAR_DONE;

    st7920_spi_initialized = 1u;
}

static u8 st7920_spi_transfer_done(void)
{
    return (JL_SPI1->CON & ST7920_SPI_CON_TRANSFER_DONE) ? 1u : 0u;
}

static void st7920_spi_acknowledge_done(void)
{
    JL_SPI1->CON |= ST7920_SPI_CON_CLEAR_DONE;
}

static u8 st7920_spi_write_byte(u8 value)
{
    u32 timeout;

    JL_SPI1->CON &= ~ST7920_SPI_CON_DIRECTION_READ;
    JL_SPI1->BUF = value;

    timeout = ST7920_SPI_BLOCKING_TIMEOUT_LOOPS;
    while (!st7920_spi_transfer_done()) {
        if (--timeout == 0u) {
            return 0u;
        }
    }

    st7920_spi_acknowledge_done();
    return 1u;
}

/******************************************************************************
*                         ST7920 PROTOCOL LAYER
******************************************************************************/

/*
* Send one logical byte (command or data) as the three-byte serial frame.
* Brackets the transaction with CS the same way the datasheet's own 8051
* bit-bang example does: assert, clock out the frame, deassert.
*/
static u8 st7920_send(u8 rs, u8 value)
{
    u8 sync_byte;
    u8 ok;

    sync_byte = ST7920_SERIAL_SYNC_WRITE | (rs ? ST7920_RS_BIT : 0u);

    st7920_spi_initialize_once();
    st7920_gpio_cs(1u);

    ok = st7920_spi_write_byte(sync_byte);
    ok = ok && st7920_spi_write_byte((u8)(value & 0xF0u));
    ok = ok && st7920_spi_write_byte((u8)(value << 4));

    st7920_gpio_cs(0u);
    return ok;
}

static u8 st7920_write_cmd(u8 cmd)
{
    u8 ok = st7920_send(0u, cmd);
    st7920_delay_us(ST7920_INSTR_DELAY_US);
    return ok;
}

/*
* GDRAM data writes are the vast majority of transactions during a redraw
* (32 per vertical address vs. 2 address-set commands), so they're the
* highest-leverage place to shrink delay. ST7920_INSTR_DELAY_US (72us) is
* the datasheet's WORST-CASE figure for all instructions including RAM
* writes, chosen conservatively because serial mode can't poll the busy
* flag. ST7920_DATA_DELAY_US is a separate, shorter knob you can dial down
* experimentally -- start around 5-10us and back off (increase) only if
* you see corrupted/shifted pixels, which indicates the controller hadn't
* finished the previous write yet.
*/
static u8 st7920_write_data(u8 data)
{
    u8 ok = st7920_send(1u, data);
    st7920_delay_us(ST7920_DATA_DELAY_US);
    return ok;
}

static u8 st7920_set_gdram_address(u8 row, u8 word_index)
{
    u8 vertical;
    u8 horizontal;

    vertical   = row & 0x1Fu;
    horizontal = word_index + ((row >= 32u) ? 8u : 0u);

    if (!st7920_write_cmd((u8)(ST7920_CMD_SET_GDRAM_ADDR | vertical))) {
        return 0u;
    }
    return st7920_write_cmd((u8)(ST7920_CMD_SET_GDRAM_ADDR | horizontal));
}

static void st7920_hardware_reset(void)
{
    st7920_gpio_reset(1u);
    st7920_delay_ms(20u);
    st7920_gpio_reset(0u);
    st7920_delay_ms(20u);
    st7920_gpio_reset(1u);
    st7920_delay_ms(50u);   /* datasheet: wait >40ms after power/reset */
}

static u8 st7920_controller_initialize(void)
{
    st7920_spi_initialize_once();
    st7920_hardware_reset();

    if (!st7920_write_cmd(ST7920_CMD_FUNCTION_SET_BASIC)) {
        return 0u;
    }
    st7920_delay_us(100u);
    if (!st7920_write_cmd(ST7920_CMD_FUNCTION_SET_BASIC)) {
        return 0u;
    }

    if (!st7920_write_cmd(ST7920_CMD_DISPLAY_ON)) {
        return 0u;
    }

    if (!st7920_write_cmd(ST7920_CMD_DISPLAY_CLEAR)) {
        return 0u;
    }
    st7920_delay_ms(ST7920_CLEAR_DELAY_MS);

    if (!st7920_write_cmd(ST7920_CMD_ENTRY_MODE)) {
        return 0u;
    }

    /* Switch to extended instruction set, then enable graphic mode in a
     * second instruction -- DL/RE/G cannot all change in one transaction. */
    if (!st7920_write_cmd(ST7920_CMD_FUNCTION_SET_EXTENDED)) {
        return 0u;
    }
    if (!st7920_write_cmd(ST7920_CMD_FUNCTION_SET_GRAPHIC_ON)) {
        return 0u;
    }

    return 1u;
}

/******************************************************************************
*                         FRAMEBUFFER -> GDRAM CONVERSION
******************************************************************************/

/*
* Push one 16-pixel-wide GDRAM word built from the current disp_buf page
* contents for physical row `row` and word index `word_index` (columns
* word_index*16 .. word_index*16+15).
*
* disp_buf is page/column/bit oriented exactly like lcd_disp.c's
* disp_buff[page][col]: bit b of disp_buf[page][col] is pixel
* row = page*8+b, column = col.
*/
static u16 st7920_build_word(u8 row, u8 word_index)
{
    u8 page;
    u8 bit;
    u16 word;
    u8 col;
    u8 i;

    page = row >> 3;
    bit  = (u8)BIT(row & 0x07u);
    word = 0u;

    for (i = 0; i < 16u; i++) {
        col = (u8)(word_index * 16u + i);
        word <<= 1;
        if (disp_buf[(u16)page * ST7920_PAGE_BYTES + col] & bit) {
            word |= 1u;
        }
    }

    return word;
}

/******************************************************************************
*                         ASYNC STREAM SENDER (TIMER3 UI-DRIVEN)
*
* The old code blocked synchronously for the whole frame: build one logical
* byte, blast it over SPI, busy-wait ~72us for the controller to digest it,
* repeat ~1000 times in an unbroken run. That's a single multi-millisecond
* stretch of the CPU doing nothing useful, which is exactly what starves a
* timing-sensitive audio ISR (Timer0) of prompt service.
*
* The fix mirrors i2c_oled_hw.c's approach: do all the CPU-only work (the
* page-format -> GDRAM-word bit transpose) once, up front, into a flat
* buffer of already-framed SPI bytes. The existing Timer3 UI loop drains
* bounded groups from that buffer, leaving the audio/BT tasks serviceable.
******************************************************************************/

/* Per Y: 2 address commands + 16 top-half data bytes + 2 address commands
* + 16 bottom-half data bytes.  The old formula omitted the lower half and
* overflowed st7920_stream while preparing a full frame. */
#define ST7920_STREAM_LOGICAL_BYTES  (32u * (4u + 4u * ST7920_WORDS_PER_ROW))
#define ST7920_STREAM_RAW_BYTES      (ST7920_STREAM_LOGICAL_BYTES * 3u)

static u8 st7920_stream[ST7920_STREAM_RAW_BYTES];
static volatile u16 st7920_stream_len;
static volatile u8 st7920_stream_active;
static volatile u8 st7920_redraw_pending;

static u16 st7920_stream_put(u16 pos, u8 rs, u8 value)
{
    st7920_stream[pos++] = ST7920_SERIAL_SYNC_WRITE | (rs ? ST7920_RS_BIT : 0u);
    st7920_stream[pos++] = (u8)(value & 0xF0u);
    st7920_stream[pos++] = (u8)(value << 4);
    return pos;
}

/*
* Build the entire frame's SPI byte stream from the current disp_buf
* contents. Pure CPU math, no I/O and no waiting -- safe to run in normal
* (non-ISR) context from draw_lcd_buf(). Uses the same top/bottom-half
* pairing as before: one vertical address's 16-word auto-increment span
* covers a top-half row followed immediately by its bottom-half
* counterpart, so only 2 address commands are needed per 32 data words.
*/
static void st7920_prepare_stream(void)
{
    u16 pos;
    u8 vertical;
    u8 word_index;
    u16 word;

    pos = 0u;

    for (vertical = 0; vertical < 32u; vertical++) {
        /* Top physical half: Y=vertical, X=0..7. */
        pos = st7920_stream_put(pos, 0u, (u8)(ST7920_CMD_SET_GDRAM_ADDR | vertical));
        pos = st7920_stream_put(pos, 0u, (u8)(ST7920_CMD_SET_GDRAM_ADDR | 0u));

        for (word_index = 0; word_index < ST7920_WORDS_PER_ROW; word_index++) {
            word = st7920_build_word(vertical, word_index);
            pos = st7920_stream_put(pos, 1u, (u8)(word >> 8));
            pos = st7920_stream_put(pos, 1u, (u8)word);
        }

        /* Bottom physical half: Y is the same but X begins at 8.  It must
         * be explicitly addressed; automatic increment only wraps X. */
        pos = st7920_stream_put(pos, 0u, (u8)(ST7920_CMD_SET_GDRAM_ADDR | vertical));
        pos = st7920_stream_put(pos, 0u, (u8)(ST7920_CMD_SET_GDRAM_ADDR | 8u));
        for (word_index = 0; word_index < ST7920_WORDS_PER_ROW; word_index++) {
            word = st7920_build_word((u8)(vertical + 32u), word_index);
            pos = st7920_stream_put(pos, 1u, (u8)(word >> 8));
            pos = st7920_stream_put(pos, 1u, (u8)word);
        }
    }

    st7920_stream_len = pos;
}

static void st7920_start_stream_send(void)
{
    st7920_prepare_stream();
    st7920_redraw_pending = 0u;

    /* CS may remain high for consecutive serial packets.  With PSB low this
     * is the datasheet's recommended minimal-system arrangement and lets one
     * non-blocking SPI DMA operation carry the whole prepared frame. */
    JL_SPI1->CON &= ~ST7920_SPI_CON_DIRECTION_READ;
    JL_SPI1->CON |= ST7920_SPI_CON_CLEAR_DONE;
    st7920_gpio_cs(1u);
    JL_SPI1->ADR = (u32)st7920_stream;
    JL_SPI1->CNT = st7920_stream_len;
    st7920_stream_active = 1u;
}

/*
* Runs from the SDK's existing 2ms Timer3 UI loop.  It never writes SPI data
* and never waits: SPI1 DMA owns the physical transfer.  Timer1 stays free
* for key_drv_ir.c and audio is no longer held behind per-packet delays.
*/
static void st7920_stream_pump(void)
{
    if (!st7920_stream_active) {
        return;
    }

    if (!(JL_SPI1->CON & ST7920_SPI_CON_TRANSFER_DONE)) {
        return;
    }
    JL_SPI1->CON |= ST7920_SPI_CON_CLEAR_DONE;
    st7920_gpio_cs(0u);
    st7920_stream_active = 0u;

    if (st7920_redraw_pending) {
        st7920_start_stream_send();
    }
}

LOOP_UI_REGISTER(st7920_stream_pump_loop) = {
    .time = 1,
    .fun  = st7920_stream_pump,
};

/******************************************************************************
*                         PUBLIC HARDWARE API
******************************************************************************/

void lcd_hardware_init(void)
{
    st7920_spi_initialize_once();

    if (!st7920_controller_initialize()) {
        st7920_driver_available = 0u;
        return;
    }

    st7920_driver_available = 1u;
}

void lcd_reset(void)
{
    st7920_driver_available = 0u;
    lcd_hardware_init();
    if (st7920_driver_available) {
        draw_lcd_buf();
    }
}

void lcd_display_on(void)
{
    if (!st7920_driver_available) {
        return;
    }
    /* Display ON/OFF is a basic-set instruction; drop to RE=0, issue it,
     * then return to the extended/graphic-on state used for GDRAM access. */
    st7920_write_cmd(ST7920_CMD_FUNCTION_SET_BASIC);
    st7920_write_cmd(ST7920_CMD_DISPLAY_ON);
    st7920_write_cmd(ST7920_CMD_FUNCTION_SET_EXTENDED);
    st7920_write_cmd(ST7920_CMD_FUNCTION_SET_GRAPHIC_ON);
}

void lcd_display_off(void)
{
    if (!st7920_driver_available) {
        return;
    }
    st7920_write_cmd(ST7920_CMD_FUNCTION_SET_BASIC);
    st7920_write_cmd(ST7920_CMD_DISPLAY_OFF);
    st7920_write_cmd(ST7920_CMD_FUNCTION_SET_EXTENDED);
    st7920_write_cmd(ST7920_CMD_FUNCTION_SET_GRAPHIC_ON);
}

/******************************************************************************
*                         PUBLIC FRAMEBUFFER API
******************************************************************************/

void lcd_clear(void)
{
    memset(disp_buf, 0, sizeof(disp_buf));
}

void draw_lcd_buf(void)
{
    if (!st7920_driver_available) {
        return;
    }

    if (st7920_stream_active) {
        /* A send is already in flight -- don't rebuild the stream out
         * from under the ISR mid-transfer. Flag it so the ISR restarts
         * a fresh send (picking up whatever disp_buf looks like then)
         * as soon as the current one finishes. */
        st7920_redraw_pending = 1u;
        return;
    }

    st7920_start_stream_send();
}

void lcd_clear_area(u8 start_page, u8 end_page)
{
    u8 page;

    if (start_page >= ST7920_DISP_PAGES) {
        return;
    }
    if (end_page > ST7920_DISP_PAGES) {
        end_page = ST7920_DISP_PAGES;
    }
    if (start_page >= end_page) {
        return;
    }

    for (page = start_page; page < end_page; page++) {
        memset(&disp_buf[(u16)page * ST7920_PAGE_BYTES], 0, ST7920_PAGE_BYTES);
    }
}

void lcd_clear_area_with_draw(u8 start_page, u8 end_page)
{
    lcd_clear_area(start_page, end_page);
    draw_lcd_buf();
}

void lcd_clear_area_rect(u8 start_page, u8 end_page, u8 x0, u8 x1)
{
    u8 page;
    u16 length;

    if (start_page >= ST7920_DISP_PAGES || x0 >= ST7920_DISP_WIDTH) {
        return;
    }
    if (end_page > ST7920_DISP_PAGES) {
        end_page = ST7920_DISP_PAGES;
    }
    if (x1 > ST7920_DISP_WIDTH) {
        x1 = ST7920_DISP_WIDTH;
    }
    if (start_page >= end_page || x0 >= x1) {
        return;
    }

    length = (u16)x1 - x0;
    for (page = start_page; page < end_page; page++) {
        memset(&disp_buf[(u16)page * ST7920_PAGE_BYTES + x0], 0, length);
    }
}

u32 lcd_TurnPixelReverse_Page(u8 startpage, u8 pagelen)
{
    u8 page;
    u16 column;
    u8 end_page;

    if (startpage >= ST7920_DISP_PAGES || pagelen == 0u) {
        return FALSE;
    }

    if ((u16)startpage + pagelen > ST7920_DISP_PAGES) {
        end_page = ST7920_DISP_PAGES;
    } else {
        end_page = startpage + pagelen;
    }

    for (page = startpage; page < end_page; page++) {
        for (column = 0; column < ST7920_PAGE_BYTES; column++) {
            disp_buf[(u16)page * ST7920_PAGE_BYTES + column] ^= 0xFFu;
        }
    }

    return TRUE;
}

u32 lcd_TurnPixelReverse_Rect(u8 left, u8 top, u8 right, u8 bottom)
{
    u8 page;
    u16 column;

    if (left >= ST7920_DISP_WIDTH || top >= ST7920_DISP_PAGES) {
        return FALSE;
    }
    if (right > ST7920_DISP_WIDTH) {
        right = ST7920_DISP_WIDTH;
    }
    if (bottom >= ST7920_DISP_PAGES) {
        bottom = ST7920_DISP_PAGES - 1u;
    }
    if (left >= right || top > bottom) {
        return FALSE;
    }

    for (page = top; page <= bottom; page++) {
        for (column = left; column < right; column++) {
            disp_buf[(u16)page * ST7920_PAGE_BYTES + column] ^= 0xFFu;
        }
    }

    return TRUE;
}

/******************************************************************************
*                         INIT ENTRY POINT
******************************************************************************/

void lcd_init(void)
{
    memset(disp_buf, 0, sizeof(disp_buf));

    if (!uicon_init_api()) {
        return;
    }

    if (!font_init_api(Chinese_Simplified)) {
        return;
    }

    lcd_hardware_init();
    lcd_clear();
    draw_lcd_buf();
}

#endif /* LCD_128X64_EN */

st7920_backend.h

#ifndef ST7920_BACKEND_H
#define ST7920_BACKEND_H

/******************************************************************************
* File        : st7920_backend.h
* Target      : JieLi AC6925A / AC692x SDK V2.6.3
* Display     : Sitronix ST7920, 128x64, native serial (3-wire) interface
*
* This is a drop-in replacement for lcd_disp.c (128x64 SSD1306-style I2C
* OLED backend). It keeps the same page-oriented disp_buff[8][128] layout
* and the same JieLi-facing function names, so uicon/menu/font code above
* this layer needs no changes.
*
* Wiring reuses the same four GPIO signals already routed for the ST7789
* SPI backend in this project, repurposed as follows:
*
*   ST7789 role      -> ST7920 role
*   ------------------------------------
*   SPI1 SCK  (PC4)  -> SCLK (E)
*   SPI1 MOSI (PC5)  -> SID  (RW)
*   D/C       (PC3)  -> CS   (RS)      (ST7920 serial CS is active-HIGH)
*   RESET     (PB5)  -> XRESET         (active-LOW)
*
* ST7920 serial mode has no MISO/read path, so busy-flag polling is not
* available; the driver instead waits the datasheet's worst-case
* instruction execution time after each transaction.
******************************************************************************/

#include "sdk_cfg.h"
#include "typedef.h"
#include "uicon/dc_showres.h"

#ifdef __cplusplus
extern "C" {
#endif

/******************************************************************************
*                         BUILD-TIME CONFIGURATION
******************************************************************************/

#ifndef ST7920_DISP_WIDTH
#define ST7920_DISP_WIDTH                    128u
#endif

#ifndef ST7920_DISP_HEIGHT
#define ST7920_DISP_HEIGHT                   64u
#endif

#ifndef ST7920_DISP_PAGES
#define ST7920_DISP_PAGES                    8u    /* DISP_HEIGHT / 8 */
#endif

#if (ST7920_DISP_WIDTH != 128u) || (ST7920_DISP_HEIGHT != 64u)
#error "This backend assumes the standard 128x64 ST7920 GDRAM addressing scheme."
#endif

/*
* Reuse the same SPI1 route already validated for the ST7789 backend.
* Set to 0 to select the SDK's SPI1A mapping (PB4/PB5) instead.
*/
#ifndef ST7920_USE_SPI1B
#define ST7920_USE_SPI1B                     1
#endif

#define ST7920_GPIO_PORT_A                    0u
#define ST7920_GPIO_PORT_B                    1u
#define ST7920_GPIO_PORT_C                    2u
#define ST7920_GPIO_PORT_D                    3u

/* CS takes the physical pin previously used as D/C for the ST7789 wiring. */
#ifndef ST7920_CS_PORT_SELECT
#define ST7920_CS_PORT_SELECT                ST7920_GPIO_PORT_C
#endif

#ifndef ST7920_CS_PIN_INDEX
#define ST7920_CS_PIN_INDEX                  3u
#endif

#ifndef ST7920_RESET_PORT_SELECT
#define ST7920_RESET_PORT_SELECT             ST7920_GPIO_PORT_B
#endif

#ifndef ST7920_RESET_PIN_INDEX
#define ST7920_RESET_PIN_INDEX               5u
#endif

#ifndef ST7920_DISABLE_IIC_FOR_SPI1B
#define ST7920_DISABLE_IIC_FOR_SPI1B         1
#endif

/*
* The complete ST7920 stream is transferred by SPI DMA.  Keep SPI slow
* enough that the 24 clocks of one serial packet also provide the controller
* time to complete the preceding instruction.  This is intentionally much
* slower than the ST7789 setting and removes the former 72us busy-waits from
* the high-priority UI timer.
*/
#ifndef ST7920_SPI_BAUD_DIV
#define ST7920_SPI_BAUD_DIV                  0x60u
#endif

/* Worst-case per-instruction execution time per datasheet (fOSC=540kHz),
* used instead of busy-flag polling since serial mode cannot read BF. */
#ifndef ST7920_INSTR_DELAY_US
#define ST7920_INSTR_DELAY_US                72u
#endif

/* Delay used only for the handful of blocking init-time commands (basic
* <-> extended instruction-set switches, display on/off, clear). These
* run rarely (init / power state changes), so blocking here is harmless. */
#ifndef ST7920_DATA_DELAY_US
#define ST7920_DATA_DELAY_US                 72u
#endif

#ifndef ST7920_CLEAR_DELAY_MS
#define ST7920_CLEAR_DELAY_MS                2u   /* >1.6ms spec, rounded up */
#endif

#ifndef ST7920_DELAY_LOOPS_PER_US
#define ST7920_DELAY_LOOPS_PER_US            48u  /* derived from the
                                                      known-good 48000/ms
                                                      figure used elsewhere
                                                      in this SDK; re-check
                                                      against actual CPU clk */
#endif

/******************************************************************************
*                         JIELI COMPATIBILITY API
******************************************************************************/

extern u8 disp_buf[ST7920_DISP_PAGES * ST7920_DISP_WIDTH];
extern const UI_LCD_IO ui_lcd_io;
extern const LCD_INFO lcd_info;

void lcd_init(void);
void lcd_clear(void);
void draw_lcd_buf(void);

void lcd_clear_area(u8 start_page, u8 end_page);
void lcd_clear_area_with_draw(u8 start_page, u8 end_page);
void lcd_clear_area_rect(u8 start_page, u8 end_page, u8 x0, u8 x1);
u32 lcd_TurnPixelReverse_Page(u8 startpage, u8 pagelen);
u32 lcd_TurnPixelReverse_Rect(u8 left, u8 top, u8 right, u8 bottom);

/* Extra hardware-level helpers, not required by uicon but useful for
* bring-up/debug and power management. */
void lcd_hardware_init(void);
void lcd_display_on(void);
void lcd_display_off(void);
void lcd_reset(void);

#ifdef __cplusplus
}
#endif

#endif /* ST7920_BACKEND_H */

Подпись автора

Türkiye

250

barbaror4 написал(а):

st7920_delay_us(1000u);

Вот явная проблема, это блокирующая задержка.
От такого в коде стоить отказаться вообще, или задержка должна быть такой мальенькой, что бы другие функции не замечали её.
Но от такого подхода следует отказаться.
Лучше использовать не блокирующий - NOP цикл , но он привязан к частоте ядра.
Или самый правильный вариант - использовать свободный hardware таймер, он не привязан ни к чему, тогда дравйвер будет максимально надежным и стабильным. Но и не блокирующим NOP циклом можно обойтись.
Ага. вижу что писал какой то AI. Но без разницы, я тоже так делаю.  :D

Подпись автора

USMicro® 2026©

251

BIOS написал(а):

Вот явная проблема, это блокирующая задержка.
От такого в коде стоить отказаться вообще, или задержка должна быть такой мальенькой, что бы другие функции не замечали её.
Но от такого подхода следует отказаться.
Лучше использовать не блокирующий - NOP цикл , но он привязан к частоте ядра.
Или самый правильный вариант - использовать свободный hardware таймер, он не привязан ни к чему, тогда дравйвер будет максимально надежным и стабильным. Но и не блокирующим NOP циклом можно обойтись.
Ага. вижу что писал какой то AI. Но без разницы, я тоже так делаю.

Да, вы правы, мне немного помог AI :) (ST7920 такой упрямый, не считывает флаг занятости по SPI). Спасибо.вает флаг занятости по SPI). Спасибо.

Подпись автора

Türkiye

252

Мне надоел ac692x... может быть, мне стоит попробовать прикрепить новый экран, потому что почему бы и нет?

Отредактировано barbaror4 (2026-08-22 19:00:59)

Подпись автора

Türkiye

253

Ну, новая жертва, JieLi Ac6965e https://upforme.ru/uploads/001b/ca/8a/339/t734467.jpg
https://upforme.ru/uploads/001b/ca/8a/339/t713502.jpg

Подпись автора

Türkiye


Вы здесь » USMI » MCU, SoC, CPU Микроконтроллеры » Подключение разной периферии к JL SoC. SPI/I2C/I2S/PWM/UART/GPIO...