这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » DIY与开源设计 » 电子DIY » 【Zephyr|MCXA366】OD-6010SLCD驱动

共1条 1/1 1 跳转至

【Zephyr|MCXA366】OD-6010SLCD驱动

高工
2026-09-05 09:16:16     打赏
FRDM-MCXA366 OD-6010 SLCD 驱动


5f0eef518a52a8cce2718917ced7fe59.jpg

在FRDM_MCXA366上有 OD-6010 段式 LCD 。显示屏为 OD-6010 6 位 7 段 + 6 个圆点 LCD 玻璃面板。

驱动在 Zephyr 4.4.0 上运行。

驱动直接调用 NXP MCUX SDK 的 fsl_slcd.h HAL( src/slcd_od6010.c),不修改 Zephyr 内核或设备树。

CMake 负责把 HAL 源文件 fsl_slcd.c 编入 Zephyr 镜像(见 CMakeLists.txt)。


1. 硬件概览

项目说明
SoCMCXA366(Cortex-M33)
LCD 控制器SLCD0(基地址 0x400C2000,48 个波形字节 WF8B[0..47]
玻璃OD-6010,6 位 7 段,TN 正显,1/4 duty、1/3 bias
段数6 数字 × 7 段 + 6 个圆点(DP1..DP6)
显示区尺寸27.60 × 11.60 mm;数字高度 8 mm
背板COM0..COM3 共 4 路

引脚全部走 PORT0,mux 设置为 ALT9(LCD 功能),无需外接电阻或电荷泵器件,SLCD 内部 VLL3 供电。


2. SLCD 引脚映射

SLCD 外设一共 48 个引脚(P0..P47),本项目只用到 16..31,全部经过 PORT0 的 12..27 号 PAD。

 SLCD pin   角色             GPIO pad
--------   ---------------  --------
  16..27   12 路段信号       P0_12..P0_23
  28..31   4 路背板 (COM0-3) P0_24..P0_27

k_pin_map[]slcd_od6010.c:111-130)把这 16 个 SLCD 引脚逐个路由到 PORT0:

static const slcd_pin_map_t k_pin_map[] = {
   /* 12 segment pins */
   {16U, 0U, 12U},   {17U, 0U, 13U},
   {18U, 0U, 14U},   {19U, 0U, 15U},
   {20U, 0U, 16U},   {21U, 0U, 17U},
   {22U, 0U, 18U},   {23U, 0U, 19U},
   {24U, 0U, 20U},   {25U, 0U, 21U},
   {26U, 0U, 22U},   {27U, 0U, 23U},
   /* 4 backplane pins */
   {28U, 0U, 24U},   {29U, 0U, 25U},
   {30U, 0U, 26U},   {31U, 0U, 27U},
};

slcd_init() 在配置 SLCD 之前先遍历这张表,调用 PORT_SetPinMux(PORT0, pad, kPORT_MuxAlt9) 把每个 PAD 切到 LCD 功能(slcd_od6010.c:333-341)。


3. 玻璃面板(OD-6010)的物理布局

3.1 数字顺序

OD-6010 把数字从右到左编号,物理上从左到右是:

dig6  dig5  dig4  dig3  dig2  dig1
 ↑     ↑     ↑     ↑     ↑     ↑
左                          右

所以 NXP 的 NUM_POS1(逻辑 pin 1 = SLCD pin 16)在玻璃上对应最右的那一位。

驱动对外约定的 pos 索引却是左到右slcd_show_string("123456") 让 "1" 显示在最左)。为消除歧义,PIN_ABCN/PIN_EFG 宏把逻辑引脚反向映射:

/* PIN_ABCN(x) / PIN_EFG(x) — 1-indexed digit → SLCD pin index.
* 因为玻璃从右到左编号 1..6,要把"我的位置 0 = 最左"
* 对齐到物理最左的 dig6 (SLCD pin 26/27),所以取反方向。 */
#define PIN_ABCN(x)  (uint32_t)(28U - 2U * (x))   /* SLCD pin 26,24,22,20,18,16 */
#define PIN_EFG(x)   (uint32_t)(29U - 2U * (x))   /* SLCD pin 27,25,23,21,19,17 */
我的位置 pos1-indexed xABCN pinEFG pin物理位置
0(左)12627dig6
122425dig5
232223dig4
342021dig3
451819dig2
5(右)61617dig1

3.2 1/4 duty 与背板相位

1/4 duty 表示在任意时刻 4 个 COM 里只有 1 个激活。SLCD 控制器用 4 个相位 A/B/C/D 循环激活 4 路背板:

static const slcd_phase_type_t k_digit_phase[4] = {
   PHASE_A, PHASE_B, PHASE_C, PHASE_D,
};

static const uint32_t k_com_pin[4] = { 28U, 29U, 30U, 31U };

static void program_backplanes(void)
{
   for (uint32_t d = 0; d < 4U; d++) {
       SLCD_SetBackPlanePhase(LCD0, k_com_pin[d], k_digit_phase[d]);
   }
}

SLCD_SetBackPlanePhase(LCD0, 28, kSLCD_PhaseAActivate) 的意思是:SLCD 引脚 28(COM0)在相位 A 时为有效电平。具体哪一段在哪个相位亮,由 WF8B[] 字节里设置的位决定。

3.3 段信号与相位的对应

OD-6010 的段信号在 SLCD 控制器侧的相位编码(参考 NXP slcd/OD-6010.c):

ABCN 引脚相位EFG 引脚相位
0A_COMaP_COMDP(圆点)
1B_COMbF_COMf
2C_COMcG_COMg
3D_COMdE_COMe

EFG 引脚的相位集合与 ABCN 不对称:bit 0 是 DP(不是 f),所以 SEGF_EFG 必须定义为 (1U << 1) 而非常见的 (1U << 0)

#define SEGF_EFG   (1U << 1)   /* upper-left  vertical */
#define SEGG_EFG   (1U << 2)   /* middle      horizontal */
#define SEGE_EFG   (1U << 3)   /* lower-left  vertical */

4. 字模表

每个数字用两字节描述:一个写到 PIN_ABCN,一个写到 PIN_EFG

/* k_digit_abcn: a/b/c/d 段 */
static const uint8_t k_digit_abcn[16] = {
   [0] = SEGA_ABCN | SEGB_ABCN | SEGC_ABCN | SEGD_ABCN,  /* 0 = ABCD  */
   [1] = SEGB_ABCN | SEGC_ABCN,                          /* 1 = BC    */
   [2] = SEGA_ABCN | SEGB_ABCN | SEGD_ABCN,              /* 2 = ABD   */
   [3] = SEGA_ABCN | SEGB_ABCN | SEGC_ABCN | SEGD_ABCN,  /* 3 = ABCD  */
   [4] = SEGB_ABCN | SEGC_ABCN,                          /* 4 = BC    */
   [5] = SEGA_ABCN | SEGC_ABCN | SEGD_ABCN,              /* 5 = ACD   */
   [6] = SEGA_ABCN | SEGC_ABCN | SEGD_ABCN,              /* 6 = ACD   */
   [7] = SEGA_ABCN | SEGB_ABCN | SEGC_ABCN,              /* 7 = ABC   */
   [8] = SEGA_ABCN | SEGB_ABCN | SEGC_ABCN | SEGD_ABCN,  /* 8 = ABCD  */
   [9] = SEGA_ABCN | SEGB_ABCN | SEGC_ABCN | SEGD_ABCN,  /* 9 = ABCD  */
};

/* k_digit_efg: e/f/g 段 */
static const uint8_t k_digit_efg[16] = {
   [0] = SEGE_EFG | SEGF_EFG,    /* 0 = EF  */
   [1] = 0U,                     /* 1 = -   */
   [2] = SEGE_EFG | SEGG_EFG,    /* 2 = EG  */
   [3] = SEGG_EFG,               /* 3 = G   */
   [4] = SEGF_EFG | SEGG_EFG,    /* 4 = FG  */
   [5] = SEGF_EFG | SEGG_EFG,    /* 5 = FG  */
   [6] = SEGE_EFG | SEGF_EFG | SEGG_EFG,  /* 6 = EFG */
   [7] = 0U,                     /* 7 = -   */
   [8] = SEGE_EFG | SEGF_EFG | SEGG_EFG,  /* 8 = EFG */
   [9] = SEGF_EFG | SEGG_EFG,    /* 9 = FG  */
};

数字 3/4/7/9 在 EFG 表里只用部分段;6/8 用全部三段,这与标准 7 段字模完全一致。

刷新一个位置的实现:

static void refresh_digit(uint32_t pos)
{
   uint8_t glyph = s_digit_glyph[pos];
   if (glyph > 9U) {
       glyph = 0U;       /* 超范围当作空白 */
       s_digit_glyph[pos] = 0U;
   }

   SLCD_SetFrontPlaneSegments(LCD0, PIN_ABCN(pos + 1U), k_digit_abcn[glyph]);
   SLCD_SetFrontPlaneSegments(LCD0, PIN_EFG(pos + 1U),  k_digit_efg[glyph]);
}

SLCD_SetFrontPlaneSegments(base, pin, value)value 直接写到 WF8B[pin],一次写入 8 位,刚好对应一个 SLCD 引脚上的 8 个相位(A..H)。


5. 小数点(DP)

OD-6010 的 6 个圆点分布在 3 个空隙内,每个空隙一上一下:

位置   上方点   下方点   物理位置(在 dig5-dig4 间隙)
 ↑      ↓        ↓
dig6  dig5  ·P1·  dig4  ·P2·  dig3  ·P3·  dig2  dig1
            ·P4·        ·P5·        ·P6·

每个圆点在 SLCD 物理上借用某一位的 EFG 引脚:在 WF8B[pin] 字节里设置 bit 0(= P_COM 相位),玻璃就把那个交叉点的圆点点亮。圆点位置与 EFG 引脚的对应:

圆点SLCD 引脚物理位置
DP123上方,位置 1-2 之间
DP221上方,位置 2-3 之间
DP319上方,位置 3-4 之间
DP425下方,位置 1-2 之间
DP527下方,位置 2-3 之间
DP617下方,位置 3-4 之间

k_dp_pin[]slcd_od6010.c:238-245)就是这张表的程序化版本。

刷新逻辑——DP 和数字共享一个 SLCD 引脚,所以必须把 DP 的相位 A 位与当前字模合并后再写:

static void refresh_dp(uint32_t icon_id)
{
   uint32_t slcd_pin = k_dp_pin[icon_id];
   uint32_t efg_digit = (27U - slcd_pin) / 2U;        /* 反向 PIN_EFG */
   uint8_t  glyph     = s_digit_glyph[efg_digit];

   uint8_t efg_phase = k_digit_efg[glyph];            /* 段 f/g/e */
   if (s_dp_glyph[icon_id] != 0U) {
       efg_phase |= (uint8_t)PHASE_A;                 /* bit 0 = DP 相位 */
   }
   SLCD_SetFrontPlaneSegments(LCD0, slcd_pin, efg_phase);
}

每当 slcd_show_number() 改了一个数字,会调用 refresh_dp_for_digit(pos) 把跟该数字共享 EFG 引脚的所有 DP 重新合并一遍,避免覆盖掉已经点亮的圆点。


6. SLCD 控制器初始化

slcd_init() 流程如下(slcd_od6010.c:327-403):

  1. GPIO 路由:循环 k_pin_map[],把每个 PAD 切到 kPORT_MuxAlt9

  2. 解除外设复位RESET_ReleasePeripheralReset(kSLCD0_RST_SHIFT_RSTn)——上电后 SLCD 默认处于复位状态,必须先释放才能访问寄存器。

  3. 开时钟CLOCK_EnableClock(kCLOCK_GateSLCD0)。HAL 在 FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL=1 模式下不会自己开时钟,所以这里手动开。

  4. 填配置

    cfg.dutyCycle         = kSLCD_1Div4DutyCycle;
    cfg.slcdLowPinEnabled = 0xFFFF0000U;   /* 只使能 P16..P31 */
    cfg.backPlaneLowPin   = 0xF0000000U;   /* P28..P31 设为背板 */
    cfg.clkPrescaler      = kSLCD_ClkPrescaler04;  /* 32 kHz / 64 ≈ 64 Hz 帧率 */
    cfg.voltageTrimVLL1   = kSLCD_VolatgeTrimNo;   /* 不调对比度 */
    cfg.voltageTrimVLL2   = kSLCD_VolatgeTrimNo;
  5. SLCD_Init(LCD0, &cfg):把 16 个 WF[](32-bit 视图对应 WF8B[])清零、写入 GCR 控制位、配置背板引脚。

  6. program_backplanes():把 COM0..COM3 绑到相位 A/B/C/D。

  7. SLCD_StartDisplay(LCD0):使能 LCD 驱动开始扫描。

SLCD_Init 已经把所有 WF8B[] 清零,不需要再写一次循环清零。


7. 公共 API

/* slcd_od6010.h */
int  slcd_init(void);
void slcd_clear(void);
void slcd_show_number(uint8_t pos, uint8_t num);
void slcd_show_string(const char *str);
void slcd_set_icon(uint8_t icon_id, bool on);
void slcd_show_decimal(int32_t value);
函数行为
slcd_init()一次性的初始化(详见 §6)。必须在所有其他 slcd_* 之前调用。
slcd_clear()清掉所有段和 DP,但控制器继续扫描。
slcd_show_number(pos, num)pos(0..5,左到右)显示 num(0..9),其他数字不变。
slcd_show_string(str)解析最多 6 字符,'0'..'9' 正常显示,其余字符当作空白。
slcd_set_icon(DPx, on)点亮/熄灭指定 DP。
slcd_show_decimal(value)value mod 10^6,以 6 位前面补零的形式显示。

驱动内部维护两份影子状态

static uint8_t s_digit_glyph[SLCD_DIGIT_COUNT];   /* 每位的当前数字 0..9 或 0=空 */
static uint8_t s_dp_glyph[SLCD_ICON_COUNT];       /* 每个 DP 的开关 */

每次修改一个字或一个 DP,只把受影响的那几路 WF8B[] 字节重写一次——这样改 slcd_show_number(2, 5) 不会让其他位的旧显示闪烁。


8. 演示程序:1Hz 计数器

src/main.c 在初始化后做两件事:

  1. 开机显示:写 123456,把 6 个 DP 全点亮 2 秒,验证每个位置和每个圆点的引脚映射都正确。然后清屏进入主循环。

  2. 1Hz 计数:每秒 counter++slcd_show_decimal(counter);同时让 DP4/DP5 以 2 Hz 闪烁,DP6 与 counter 的最低位联动(每秒切换一次)。

while (1) {
   uint64_t now = k_uptime_get();

   if (now - last_count >= COUNT_PERIOD_MS) {
       last_count = now;
       counter = (counter + 1U) % 1000000U;
       slcd_show_decimal((int32_t)counter);
   }

   if (now - last_blink >= BLINK_PERIOD_MS) {
       last_blink = now;
       blink_phase ^= 1U;
       slcd_set_icon(SLCD_ICON_DP4, blink_phase);
       slcd_set_icon(SLCD_ICON_DP5, blink_phase);
       slcd_set_icon(SLCD_ICON_DP6,
                     blink_phase && (counter & 0x1U));
   }

   k_msleep(10U);
}

9. 构建与烧录

export ZEPHYR_TOOLCHAIN_VARIANT=zephyr
export ZEPHYR_SDK_INSTALL_DIR=/d/luglZephyrproject/zephyr-sdk-1.0.1/zephyr-sdk-1.0.1

# 配置并构建(-p always 强制 cmake 重跑首次构建)
west build -b frdm_mcxa366 app/frdm_mcxa366_slcd -p always

# 增量构建
west build -b frdm_mcxa366 app/frdm_mcxa366_slcd

# 通过 J-Link 烧录
west flash -r jlink

构建产物在 app/frdm_mcxa366_slcd/build/zephyr/zephyr.elf,FLASH 占用约 32 KB / 1 MB,RAM 占用约 6 KB / 240 KB。


10. 文件清单

1、 main.c

/*
 * Copyright (c) 2026
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Demo for the OD-6010 segment-LCD driver on the FRDM-MCXA366 board.
 *
 * The main thread counts up from 0 once per second and shows the value
 * on the six-digit display.  Three of the battery icons are toggled to
 * blink at 1 Hz so the user can see the icon API in action.
 */

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

#include "slcd_od6010.h"

#define BLINK_PERIOD_MS   500U
#define COUNT_PERIOD_MS   1000U

int main(void)
{
    int rc;
    uint32_t counter = 0U;
    uint32_t blink_phase = 0U;
    uint64_t last_count = 0U;
    uint64_t last_blink = 0U;

    printk("FRDM-MCXA366 OD-6010 SLCD demo\n");
    printk("6-digit counter + icon blink at %u Hz\n",
           1000 / BLINK_PERIOD_MS);

    rc = slcd_init();
    if (rc != 0) {
        printk("slcd_init failed: %d\n", rc);
        return rc;
    }

    /* Sign-on: show "123456" with every DP lit so the user can
     * confirm the full dot layout on the OD-6010 glass.  The glass
     * has six dots in three gaps between the inner digits — upper
     * (DP1/2/3, between positions 1-2, 2-3, 3-4) and lower
     * (DP4/5/6, in the same three gaps).  No dots between the
     * outermost gaps (pos 0-1 or pos 4-5).
     */
    slcd_show_string("123456");
    slcd_set_icon(SLCD_ICON_DP1, true);
    slcd_set_icon(SLCD_ICON_DP2, true);
    slcd_set_icon(SLCD_ICON_DP3, true);
    slcd_set_icon(SLCD_ICON_DP4, true);
    slcd_set_icon(SLCD_ICON_DP5, true);
    slcd_set_icon(SLCD_ICON_DP6, true);
    k_msleep(2000);

    slcd_clear();

    while (1) {
        uint64_t now = k_uptime_get();

        /* ----- count once per second ----- */
        if (now - last_count >= COUNT_PERIOD_MS) {
            last_count = now;
            counter = (counter + 1U) % 1000000U;
            slcd_show_decimal((int32_t)counter);
        }

        /* ----- blink DP4..DP6 at 1 Hz ----- */
        if (now - last_blink >= BLINK_PERIOD_MS) {
            last_blink = now;
            blink_phase ^= 1U;
            slcd_set_icon(SLCD_ICON_DP4, blink_phase);
            slcd_set_icon(SLCD_ICON_DP5, blink_phase);
            slcd_set_icon(SLCD_ICON_DP6,
                      blink_phase && (counter & 0x1U));
        }

        k_msleep(10U);
    }

    return 0;
}

2、slcd_od6010.c

/*
 * Copyright (c) 2026
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * OD-6010 segment-LCD driver — implementation.
 *
 * See slcd_od6010.h for the public API and the glass-layout description.
 * The driver mixes the MCUX HAL (fsl_slcd.h) into the Zephyr build — Zephyr
 * does not ship an SLCD devicetree node for the MCXA366.  All pin
 * routing, duty-cycle, and backplane configuration comes from NXP's
 * official frdmmcxa366_slcd reference example
 * (board/app.h, board/pin_mux.c, slcd/OD-6010.c).
 */

#include "slcd_od6010.h"

#include <stddef.h>
#include <string.h>

#include <zephyr/kernel.h>
#include <zephyr/sys/printk.h>

/* The MCUX SDK is built with CMSIS headers; the SLCD HAL and PORT mux
 * helpers come from the SDK module pulled in via the west.yml entry.
 * Zephyr already provides the CMSIS register maps and clock gates, so
 * we tell the HAL not to touch them on its own.  Guarded in case a
 * build-system setting already defined the macro.
 */
#ifndef FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL
#define FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL 1
#endif

#include <fsl_slcd.h>
/* The generic MCUX SDK PORT driver is header-only — provides
 * PORT_SetPinMux(), PORT_Type and the kPORT_MuxAltN enum.  The MCXA366
 * device header does not generate its own PERI_PORT.h, so this is the
 * canonical entry point.
 */
#include <fsl_port.h>

/* The MCXA366 SDK does not ship its own fsl_clock.h — it inherits the
 * one from the MCXA344/MCXA266 family, which already defines the gate
 * numbers we need.  Including it is sufficient to get kCLOCK_GateSLCD0
 * and CLOCK_EnableClock().  We still gate the SLCD peripheral ourselves
 * (above) so the function is a no-op here.
 *
 * Same story for fsl_reset.h: the SLCD reset shift constant
 * (kSLCD0_RST_SHIFT_RSTn) lives in the MCXA346/266 driver, not in the
 * (empty) MCXA366 driver set.  We need RESET_ReleasePeripheralReset()
 * to release the SLCD before the first register access — otherwise
 * the bus faults because the peripheral is still in reset.
 */
#include <fsl_clock.h>
#include <fsl_reset.h>

/* MCXA366 PORT base addresses — the on-tree SDK has no PERI_PORT.h for
 * the MCXA366, so we define the five PORT instances here from the
 * reference manual (same layout as MCXA266 / 344 / 346).
 */
#ifndef PORT0_BASE
#define PORT0_BASE  (0x400BC000u)
#define PORT1_BASE  (0x400BD000u)
#define PORT2_BASE  (0x400BE000u)
#define PORT3_BASE  (0x400BF000u)
#define PORT4_BASE  (0x400C0000u)
#define PORT0       ((PORT_Type *)PORT0_BASE)
#define PORT1       ((PORT_Type *)PORT1_BASE)
#define PORT2       ((PORT_Type *)PORT2_BASE)
#define PORT3       ((PORT_Type *)PORT3_BASE)
#define PORT4       ((PORT_Type *)PORT4_BASE)
#endif

/* --------------------------------------------------------------------------
 * SLCD <-> GPIO pin table for the FRDM-MCXA366 board (per NXP example)
 *
 * The MCXA366 SLCD peripheral exposes 48 pins (P0..P47).  On this board
 * the OD-6010 glass is wired to pins 16..31, all routed through PORT0
 * pads 12..27 in mux ALT9 (LCD function).  The table below lists every
 * pin we actually drive — the rest stay in their reset (GPIO) state.
 *
 *   slcd_pin  role        gpio pad     board signal
 *   --------  ---------   --------      ----
 *      16     SEG1a/b/c/d PORT0[12]    P0_12
 *      17     SEG1e/f/g   PORT0[13]    P0_13
 *      18     SEG2a/b/c/d PORT0[14]    P0_14
 *      19     SEG2e/f/g   PORT0[15]    P0_15
 *      20     SEG3a/b/c/d PORT0[16]    P0_16
 *      21     SEG3e/f/g   PORT0[17]    P0_17
 *      22     SEG4a/b/c/d PORT0[18]    P0_18
 *      23     SEG4e/f/g   PORT0[19]    P0_19
 *      24     SEG5a/b/c/d PORT0[20]    P0_20
 *      25     SEG5e/f/g   PORT0[21]    P0_21
 *      26     SEG6a/b/c/d PORT0[22]    P0_22
 *      27     SEG6e/f/g   PORT0[23]    P0_23
 *      28     COM0        PORT0[24]    P0_24
 *      29     COM1        PORT0[25]    P0_25
 *      30     COM2        PORT0[26]    P0_26
 *      31     COM3        PORT0[27]    P0_27
 *
 * If your board routes the glass differently, edit this table and
 * nothing else needs to change.
 * ------------------------------------------------------------------------ */

typedef struct {
    uint8_t  slcd_pin;     /* SLCD peripheral pin index (16..31 here)   */
    uint8_t  port;         /* GPIO port   (0 on MCXA366 for these pads) */
    uint8_t  gpio_pin;     /* GPIO pin    (12..27 here)                 */
} slcd_pin_map_t;

static const slcd_pin_map_t k_pin_map[] = {
    /* 12 segment pins */
    {16U, 0U, 12U},
    {17U, 0U, 13U},
    {18U, 0U, 14U},
    {19U, 0U, 15U},
    {20U, 0U, 16U},
    {21U, 0U, 17U},
    {22U, 0U, 18U},
    {23U, 0U, 19U},
    {24U, 0U, 20U},
    {25U, 0U, 21U},
    {26U, 0U, 22U},
    {27U, 0U, 23U},
    /* 4 backplane pins */
    {28U, 0U, 24U},
    {29U, 0U, 25U},
    {30U, 0U, 26U},
    {31U, 0U, 27U},
};

#define PIN_MAP_LEN  (sizeof(k_pin_map) / sizeof(k_pin_map[0]))

/* --------------------------------------------------------------------------
 * Glass geometry — derived from the OD-6010 panel definition.
 *
 * Each digit owns a pair of segment pins (logical "1..12" in NXP's
 * terminology).  Pin (2x-1) carries segments a, b, c, d on phases
 * A, B, C, D respectively.  Pin (2x) carries segments e, f, g but on
 * the ASYMMETRIC phase set {D, A, C} — that mapping is hard-wired in
 * the OD-6010 glass; we cannot change it.
 *
 * SLCD peripheral pin index = logical pin index + 15, so logical
 * pin (2x-1) = SLCD pin (15 + 2x), pin (2x) = SLCD pin (16 + 2x).
 * ------------------------------------------------------------------------ */

#define PHASE_A  kSLCD_PhaseAActivate   /* bit 0 */
#define PHASE_B  kSLCD_PhaseBActivate   /* bit 1 */
#define PHASE_C  kSLCD_PhaseCActivate   /* bit 2 */
#define PHASE_D  kSLCD_PhaseDActivate   /* bit 3 */

/* Per-digit SLCD pin indices (1-indexed logical → 0-indexed SLCD).
 *
 * Digit x (1..6) owns the logical pin pair (2x-1, 2x).  NXP's
 * slcd_lcd_gpio_seg_pin[] maps logical pin N to SLCD pin (N+15),
 * but the OD-6010 glass numbers its digits right-to-left: physical
 * layout is dig6 dig5 dig4 dig3 dig2 dig1 (left to right), so
 * logical pin 1 (SLCD pin 16) is the RIGHTMOST digit on the panel.
 *
 * To make our position 0 (the "leftmost" call site) line up with
 * the leftmost digit on the glass, we REVERSE the pin assignment:
 *
 *     PIN_ABCN(x) = SLCD pin 28 - 2x
 *     PIN_EFG(x)  = SLCD pin 29 - 2x
 *
 * For 0-based digit index d (0..5), with x = d + 1:
 *     PIN_ABCN(d) = 26 - 2d    (SLCD pins 26, 24, 22, 20, 18, 16)
 *     PIN_EFG(d)  = 27 - 2d    (SLCD pins 27, 25, 23, 21, 19, 17)
 */
#define PIN_ABCN(x)  (uint32_t)(28U - 2U * (x))   /* a, b, c, d pin */
#define PIN_EFG(x)   (uint32_t)(29U - 2U * (x))   /* e, f, g    pin */

/* 7-segment bit masks.  Bit ordering on each SLCD pin matches NXP's
 * OD-6010 reference (OD-6010.c: bit0 = phase A, bit3 = phase D, etc.).
 *
 *   On PIN_ABCN:   bit0=A_COM, bit1=B_COM, bit2=C_COM, bit3=D_COM.
 *                  Phase mapping: A=a, B=b, C=c, D=d.
 *
 *   On PIN_EFG:    bit0=P_COM, bit1=F_COM, bit2=G_COM, bit3=E_COM.
 *                  Phase mapping: P=DP (decimal point), F=f, G=g, E=e.
 *                  The DP sits on phase A (bit 0) of the EFG pin, so
 *                  enabling a DP ORs phase A into the same byte that
 *                  drives segment 'f'.  The OD-6010 glass wired by NXP
 *                  doesn't expose 'f' on the DP pin's electrode, so
 *                  the two don't collide visually.
 */
#define SEGA_ABCN  (1U << 0)
#define SEGB_ABCN  (1U << 1)
#define SEGC_ABCN  (1U << 2)
#define SEGD_ABCN  (1U << 3)

#define SEGF_EFG   (1U << 1)   /* upper-left  vertical */
#define SEGG_EFG   (1U << 2)   /* middle      horizontal */
#define SEGE_EFG   (1U << 3)   /* lower-left  vertical */

/* Font tables: each digit is described by two phase bytes — one for
 * the ABCD pin and one for the EFG pin.  Index 0..9 are digits, 10
 * is the blank glyph (all segments off).  The letter forms (A..F)
 * reuse the digit shape with the lowercase 'b'..'f' mapping where the
 * panel supports it; we keep this table simple (digits + blank) and
 * fall back to blank for any unrendered glyph.
 */
static const uint8_t k_digit_abcn[16] = {
    [0] = SEGA_ABCN | SEGB_ABCN | SEGC_ABCN | SEGD_ABCN,  /* 0 = ABCD */
    [1] = SEGB_ABCN | SEGC_ABCN,                          /* 1 = BC   */
    [2] = SEGA_ABCN | SEGB_ABCN | SEGD_ABCN,              /* 2 = ABD  */
    [3] = SEGA_ABCN | SEGB_ABCN | SEGC_ABCN | SEGD_ABCN,  /* 3 = ABCD */
    [4] = SEGB_ABCN | SEGC_ABCN,                          /* 4 = BC   */
    [5] = SEGA_ABCN | SEGC_ABCN | SEGD_ABCN,              /* 5 = ACD  */
    [6] = SEGA_ABCN | SEGC_ABCN | SEGD_ABCN,              /* 6 = ACD  */
    [7] = SEGA_ABCN | SEGB_ABCN | SEGC_ABCN,              /* 7 = ABC  */
    [8] = SEGA_ABCN | SEGB_ABCN | SEGC_ABCN | SEGD_ABCN,  /* 8 = ABCD */
    [9] = SEGA_ABCN | SEGB_ABCN | SEGC_ABCN | SEGD_ABCN,  /* 9 = ABCD */
};
static const uint8_t k_digit_efg[16] = {
    [0] = SEGE_EFG | SEGF_EFG,                            /* 0 = EF   */
    [1] = 0U,                                             /* 1 = -    */
    [2] = SEGE_EFG | SEGG_EFG,                            /* 2 = EG   */
    [3] = SEGG_EFG,                                       /* 3 = G    */
    [4] = SEGF_EFG | SEGG_EFG,                            /* 4 = FG   */
    [5] = SEGF_EFG | SEGG_EFG,                            /* 5 = FG   */
    [6] = SEGE_EFG | SEGF_EFG | SEGG_EFG,                 /* 6 = EFG  */
    [7] = 0U,                                             /* 7 = -    */
    [8] = SEGE_EFG | SEGF_EFG | SEGG_EFG,                 /* 8 = EFG  */
    [9] = SEGF_EFG | SEGG_EFG,                            /* 9 = FG   */
};

/* Backplane phase table — only the four phases used by 1/4 duty. */
static const slcd_phase_type_t k_digit_phase[SLCD_DIGIT_COUNT] = {
    PHASE_A, PHASE_B, PHASE_C, PHASE_D,
};

/* Back-plane pin index for each of the four COM lines. */
static const uint32_t k_com_pin[4] = {
    28U, 29U, 30U, 31U,
};

/* Decimal-point pin index (logical pin 1..12 = SLCD pin 16..27, DP is
 * lit on phase A only — see slcd_od6010.h for the mapping).
 *
 * OD-6010 has six dots in three gaps (paired upper/lower) between the
 * four inner digits (dig2..dig5 on the glass).  Per the NXP SLCD_Icon[]
 * array and the Orient Display OD-6010 datasheet:
 *   P1 (upper), P4 (lower) — between dig5 and dig4  → SLCD pin 23/25
 *   P2 (upper), P5 (lower) — between dig4 and dig3  → SLCD pin 21/27
 *   P3 (upper), P6 (lower) — between dig3 and dig2  → SLCD pin 19/17
 * After reversing the position mapping (see PIN_EFG above), the SLCD
 * pin formula is PIN_EFG(x) = 29 - 2x, so the digit whose EFG pin this
 * DP shares is:
 */
static const uint32_t k_dp_pin[SLCD_ICON_COUNT] = {
    /* DP1 */ 23U,   /* SLCD P8  — upper, between pos 1–2 (dig5–dig4) */
    /* DP2 */ 21U,   /* SLCD P6  — upper, between pos 2–3 (dig4–dig3) */
    /* DP3 */ 19U,   /* SLCD P4  — upper, between pos 3–4 (dig3–dig2) */
    /* DP4 */ 25U,   /* SLCD P10 — lower, between pos 1–2 (dig5–dig4) */
    /* DP5 */ 27U,   /* SLCD P12 — lower, between pos 2–3 (dig4–dig3) */
    /* DP6 */ 17U,   /* SLCD P2  — lower, between pos 3–4 (dig3–dig2) */
};

/* Per-digit shadow of the current digit glyph (0..9, others = blank). */
static uint8_t s_digit_glyph[SLCD_DIGIT_COUNT];

/* Per-DP shadow.  DP pins share their phase-A bit with the 'f'
 * segment on the same EFG pin, so we OR the DP bit into whatever the
 * digit-rendering code wrote.  Phase A on PIN_EFG = SEGF_EFG = bit 0.
 */
static uint8_t s_dp_glyph[SLCD_ICON_COUNT];

/* --------------------------------------------------------------------------
 * Waveform helpers
 * ------------------------------------------------------------------------ */

static void program_backplanes(void)
{
    for (uint32_t d = 0; d < 4U; d++) {
        SLCD_SetBackPlanePhase(LCD0, k_com_pin[d], k_digit_phase[d]);
    }
}

/* Write the per-digit ABCD + EFG phase bytes from the shadow state. */
static void refresh_digit(uint32_t pos)
{
    uint8_t glyph = s_digit_glyph[pos];

    if (glyph > 9U) {
        glyph = 0U;       /* treat as blank */
        s_digit_glyph[pos] = 0U;
    }

    SLCD_SetFrontPlaneSegments(LCD0, PIN_ABCN(pos + 1U),
                   k_digit_abcn[glyph]);
    SLCD_SetFrontPlaneSegments(LCD0, PIN_EFG(pos + 1U),
                   k_digit_efg[glyph]);
}

/* Re-write every digit after the shadow changed. */
static void refresh_all(void)
{
    for (uint32_t d = 0; d < SLCD_DIGIT_COUNT; d++) {
        refresh_digit(d);
    }
}

/* Update a single DP pin by OR-ing the DP phase-A bit into whatever
 * the EFG pin currently shows.  Called from slcd_set_icon().
 */
static void refresh_dp(uint32_t icon_id);

/* Helper: re-merge DPs that share a digit's EFG pin.  Called from
 * slcd_show_number() so changing a digit doesn't drop a lit DP.
 */
static void refresh_dp_for_digit(uint32_t pos);

static void refresh_dp(uint32_t icon_id)
{
    uint32_t slcd_pin = k_dp_pin[icon_id];
    uint8_t  efg_phase;

    /* The DP shares its SLCD pin with the EFG pin of the digit it
     * sits next to (DP1 → PIN_EFG(3) = SLCD pin 23, etc.).  We OR
     * bit 0 (phase A) onto the existing EFG phase byte if this DP
     * is on; otherwise we re-write the pin with just the digit's
     * EFG byte (which never sets phase A for digits 0..9, so the
     * DP bit really turns off).
     */
    uint32_t efg_digit = (27U - slcd_pin) / 2U;   /* PIN_EFG(x) = 28-2x */
    uint8_t  glyph     = s_digit_glyph[efg_digit];

    if (glyph > 9U) {
        glyph = 0U;
    }
    efg_phase = k_digit_efg[glyph];
    if (s_dp_glyph[icon_id] != 0U) {
        efg_phase |= (uint8_t)PHASE_A;
    }
    SLCD_SetFrontPlaneSegments(LCD0, slcd_pin, efg_phase);
}

/* --------------------------------------------------------------------------
 * Public API
 * ------------------------------------------------------------------------ */

int slcd_init(void)
{
    slcd_config_t cfg;

    /* Route every SLCD pin we use to its GPIO pad.  ALT9 selects the
     * LCD function on PORT0_12..PORT0_27 (verified in the MCXA366
     * reference manual and the NXP pin_mux.c).
     */
    static PORT_Type *const k_ports[5] = {
        PORT0, PORT1, PORT2, PORT3, PORT4,
    };
    for (size_t i = 0; i < PIN_MAP_LEN; i++) {
        uint8_t port = k_pin_map[i].port;

        if (port >= ARRAY_SIZE(k_ports)) {
            continue;
        }
        PORT_SetPinMux(k_ports[port], k_pin_map[i].gpio_pin,
                   kPORT_MuxAlt9);
    }

    /* Ungate the SLCD peripheral clock.  Zephyr leaves the gate
     * alone (we set FSL_SDK_DISABLE_DRIVER_CLOCK_CONTROL), so we
     * have to enable it ourselves.
     *
     * The SLCD starts in reset out of POR — touching any LCD0
     * register while the reset is asserted returns a bus fault.
     * Release the reset first, then ungate the clock.
     */
    RESET_ReleasePeripheralReset(kSLCD0_RST_SHIFT_RSTn);
    CLOCK_EnableClock(kCLOCK_GateSLCD0);

    /* Default configuration: 1/4 duty, charge pump off, internal VLL3,
     * no fault detection.  The MCXA366 has FSL_FEATURE_SLCD_LP_CONTROL
     * so the default fills in the LP fields for us; we only override
     * the duty cycle and clock prescaler.
     */
    SLCD_GetDefaultConfig(&cfg);

    cfg.displayMode        = kSLCD_NormalMode;
    cfg.dutyCycle          = kSLCD_1Div4DutyCycle;
    cfg.lowPowerBehavior   = kSLCD_EnabledInWaitStop;
    cfg.lowPowerWaveform   = true;
    cfg.voltageTrimVLL1    = kSLCD_VolatgeTrimNo;
    cfg.voltageTrimVLL2    = kSLCD_VolatgeTrimNo;
    cfg.sampleHold         = kSLCD_SampleHoldNone;
    cfg.clkPrescaler       = kSLCD_ClkPrescaler04;   /* ~64 Hz @ 32 kHz */
#if FSL_FEATURE_SLCD_HAS_FRAME_FREQUENCY_INTERRUPT
    cfg.frameFreqIntEnable = false;
#endif
    cfg.faultConfig        = NULL;

    /* From NXP board/app.h: only pins 16..31 are enabled, and
     * pins 28..31 are backplanes.  High-half register is unused.
     */
    cfg.slcdLowPinEnabled  = 0xFFFF0000U;
    cfg.slcdHighPinEnabled = 0U;
    cfg.backPlaneLowPin    = 0xF0000000U;
    cfg.backPlaneHighPin   = 0U;

    SLCD_Init(LCD0, &cfg);

    /* SLCD_Init() already clears every WF8B[0..47] byte via the
     * WF[] 32-bit access path (it iterates 12 entries covering all
     * 48 bytes).  Do NOT add a second clear loop here — writes
     * past offset 0x4F fault (BFAR Address: 0x400c2054, i.e.
     * WF8B[52]).
     */
    program_backplanes();

    memset(s_digit_glyph, 0, sizeof(s_digit_glyph));
    memset(s_dp_glyph,    0, sizeof(s_dp_glyph));
    refresh_all();

    SLCD_StartDisplay(LCD0);
    return 0;
}

void slcd_clear(void)
{
    memset(s_digit_glyph, 0, sizeof(s_digit_glyph));
    memset(s_dp_glyph,    0, sizeof(s_dp_glyph));

    /* Zero every front-plane byte we touch — pins 16..31 (12 SEG +
     * 4 COM).  Skip the backplane phase bytes we just set.
     */
    for (uint32_t i = 16U; i < 28U; i++) {
        LCD0->WF8B[i] = 0U;
    }
}

void slcd_show_number(uint8_t pos, uint8_t num)
{
    if (pos >= SLCD_DIGIT_COUNT) {
        return;
    }
    if (num <= 9U) {
        s_digit_glyph[pos] = num;
    } else {
        s_digit_glyph[pos] = 0U;       /* render as blank */
    }
    refresh_digit(pos);
    refresh_dp_for_digit(pos);
}

void slcd_show_string(const char *str)
{
    if (str == NULL) {
        slcd_clear();
        return;
    }
    for (uint32_t d = 0; d < SLCD_DIGIT_COUNT; d++) {
        char c = str[d];

        if (c >= '0' && c <= '9') {
            s_digit_glyph[d] = (uint8_t)(c - '0');
        } else if (c == '-' || c == '_' || c == ' ') {
            s_digit_glyph[d] = 0U;    /* blank / dash */
        } else {
            s_digit_glyph[d] = 0U;    /* unsupported glyph */
        }
    }
    refresh_all();
}

void slcd_set_icon(uint8_t icon_id, bool on)
{
    if (icon_id >= SLCD_ICON_COUNT) {
        return;
    }
    /* The DP pin shares the EFG pin of the digit it sits next to.
     * We keep the DP state in s_dp_glyph[] and re-merge it with the
     * digit's EFG phase byte whenever either changes.  Phase A is
     * bit 0 — and the 'f' segment is also bit 0 on the EFG pin, so
     * turning a DP on actually lights BOTH 'f' and the DP.  The
     * OD-6010 glass wired by NXP doesn't expose that 'f' segment as
     * visible (it falls outside the 7-seg area), so this overlap
     * does not show up on the panel.
     */
    s_dp_glyph[icon_id] = on ? 1U : 0U;
    refresh_dp(icon_id);
}

/* Helper: re-merge DPs that share a digit's EFG pin.  Called from
 * slcd_show_number() so changing a digit doesn't drop a lit DP.
 */
static void refresh_dp_for_digit(uint32_t pos)
{
    for (uint32_t i = 0; i < SLCD_ICON_COUNT; i++) {
        uint32_t slcd_pin = k_dp_pin[i];
        uint32_t efg_digit = (27U - slcd_pin) / 2U;

        if (efg_digit == pos) {
            refresh_dp(i);
        }
    }
}

void slcd_show_decimal(int32_t value)
{
    char buf[SLCD_DIGIT_COUNT + 1];
    uint32_t uval;

    if (value < 0) {
        uval = (uint32_t)(-value);
    } else {
        uval = (uint32_t)value;
    }

    /* Take value modulo 10^6 — anything larger doesn't fit. */
    uval %= 1000000U;

    buf[SLCD_DIGIT_COUNT] = '\0';
    for (uint32_t i = SLCD_DIGIT_COUNT; i > 0U; i--) {
        buf[i - 1U] = (char)('0' + (uval % 10U));
        uval /= 10U;
    }
    slcd_show_string(buf);
}

3、slcd_6010.h

/*
 * Copyright (c) 2026
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Driver for the OD-6010 6-digit 7-segment + decimal-point LCD driven by
 * the on-chip SLCD controller of the NXP MCXA366 (used on the FRDM-
 * MCXA366 board). Zephyr has no upstream SLCD devicetree node, so this
 * driver mixes the MCUX HAL (fsl_slcd.h) into the Zephyr build without
 * modifying the kernel.
 *
 * Glass layout (verified against NXP's official frdmmcxa366_slcd example):
 *
 *   - 6 numeric digits (DIG1..DIG6, left to right)
 *   - 1/4 duty cycle — 4 backplanes (COM0..COM3)
 *   - Each digit owns 2 SLCD segment pins:
 *         pin (2x-1) drives segments a, b, c, d on phases A, B, C, D
 *         pin (2x)   drives segments e, f, g      on phases E, F, G
 *         (the EFG pin also carries the DP on phase A, sharing bit 0)
 *   - 6 decimal points (DP1..DP6), one per digit, on phase A
 *   - 12 segment pins total → SLCD pins 16..27 → PORT0 pads 12..23
 *   -  4 backplane pins      → SLCD pins 28..31 → PORT0 pads 24..27
 *
 * Waveform summary (1/4 duty, phases A..D for COM0..COM3):
 *   - During phase A: COM0 high, COM1..COM3 low → only segments wired
 *     to "phase A on their pin" are visible (those would be the 'a'
 *     segments of every digit and 'f' segments of every digit, plus
 *     DP1..DP6).
 *   - A SEG pin lights any segment whose phase bit is set in its
 *     WF8B[] byte — only segments whose COM is also high become visible.
 *   - Phases E..H are unused by this glass; their bits always read 0.
 */

#ifndef SLCD_OD6010_H_
#define SLCD_OD6010_H_

#include <stdbool.h>
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

/* Numeric digit positions, left to right. */
#define SLCD_DIGIT_COUNT  6U
#define SLCD_DIGIT_LEFT   0U
#define SLCD_DIGIT_RIGHT  5U

/* Decimal-point identifiers (passed to slcd_set_icon()).
 *
 * The OD-6010 glass wired up by the FRDM-MCXA366 has six DPs, one per
 * digit.  No battery / signal-bar icons are exposed on this board
 * variant — see the NXP frdmmcxa366_slcd example for the canonical
 * reference.
 */
enum slcd_icon {
    SLCD_ICON_DP1 = 0,   /* DP for digit 1 (leftmost)  */
    SLCD_ICON_DP2 = 1,
    SLCD_ICON_DP3 = 2,
    SLCD_ICON_DP4 = 3,
    SLCD_ICON_DP5 = 4,
    SLCD_ICON_DP6 = 5,   /* DP for digit 6 (rightmost) */
    SLCD_ICON_COUNT = 6,
};

/* Initialize the SLCD controller, route the COM/SEG pins to the LCD
 * peripheral, and turn on the display.  Must be called once at startup
 * before any other slcd_*() call.
 *
 * Returns 0 on success, negative errno on failure.
 */
int slcd_init(void);

/* Turn off every segment and decimal point (display goes blank but the
 * controller keeps running).
 */
void slcd_clear(void);

/* Show one decimal digit (0..9) at position pos (0..5, left to right).
 * Out-of-range digits are rendered as blanks.
 */
void slcd_show_number(uint8_t pos, uint8_t num);

/* Show a NUL-terminated string of up to 6 characters.  Only the ASCII
 * subset '0'..'9', 'A'..'F', '-', ' ' is rendered — every other
 * character becomes a blank.  Trailing characters past the rightmost
 * digit are ignored.
 */
void slcd_show_string(const char *str);

/* Turn a decimal point on or off.  icon_id is one of enum slcd_icon
 * (SLCD_ICON_DP1..SLCD_ICON_DP6).  Out-of-range IDs are silently
 * ignored.
 */
void slcd_set_icon(uint8_t icon_id, bool on);

/* Convenience: clear and write the decimal representation of `value`.
 * `value` is rendered modulo 10^6 (so 1234567 displays as "234567").
 */
void slcd_show_decimal(int32_t value);

#ifdef __cplusplus
}
#endif

#endif /* SLCD_OD6010_H_ */

4、CMakeLists.txt

# SPDX-License-Identifier: Apache-2.0
#
# Build the OD-6010 SLCD demo for the FRDM-MCXA366 board.
#
# This sample mixes the MCUX HAL (fsl_slcd.h, fsl_port.h, fsl_clock.h) into
# the Zephyr application.  Zephyr's own pinctrl/GPIO drivers stay out of
# the SLCD pins — we route them through the MCUX PORT mux helper inside
# slcd_od6010.c.
#
# Required pieces:
#   * The NXP HAL west module must be present (modules/hal/nxp/) so that
#     `fsl_slcd.c` is available.  Adding mcux-sdk-ng as a Zephyr module
#     via `west.yml` provides it.
#   * The MCXA366 device headers must be on the include path so that
#     `MCXA366.h` (and through it `fsl_device_registers.h`) can be found.
#     We add the relevant paths below.

cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(frdm_mcxa366_slcd)

# --------------------------------------------------------------------------
# MCUX HAL include paths
#
# The NXP HAL west module ships device headers under:
#   modules/hal/nxp/mcux/mcux-sdk-ng/devices/MCX/MCXA/MCXA366/
# and shared utilities / drivers under:
#   modules/hal/nxp/mcux/mcux-sdk-ng/
#   modules/hal/nxp/mcux/mcux-sdk-ng/utilities/
#   modules/hal/nxp/mcux/mcux-sdk-ng/drivers/slcd/
#   modules/hal/nxp/mcux/mcux-sdk-ng/drivers/port/
#   modules/hal/nxp/mcux/mcux-sdk-ng/components/...
# We add them to the app's include path so `#include <fsl_slcd.h>` and
# friends resolve.  Zephyr itself does not include any of these.
# --------------------------------------------------------------------------
set(MCUX_SDK_NG_ROOT  "${ZEPHYR_BASE}/../modules/hal/nxp/mcux/mcux-sdk-ng")

zephyr_include_directories(
    # Device header for MCXA366 (provides MCXA366.h, MCXA366_COMMON.h,
    # PERI_LCD.h, and the LCD0_Type typedef).
    ${MCUX_SDK_NG_ROOT}/devices/MCX/MCXA/MCXA366
    # Generic MCUX SDK headers (fsl_common.h, fsl_port.h, etc.).
    # fsl_clock.h and the clock-IP gate enum (kCLOCK_GateSLCD0 et al.)
    # are pulled in automatically by Zephyr's hal_nxp module for the
    # MCXA366, so we do not need to add MCXA266/drivers here.
    ${MCUX_SDK_NG_ROOT}
    ${MCUX_SDK_NG_ROOT}/drivers/slcd
    ${MCUX_SDK_NG_ROOT}/drivers/port
    ${MCUX_SDK_NG_ROOT}/utilities
    ${MCUX_SDK_NG_ROOT}/components/clock
)

# Compile the SLCD driver from the MCUX SDK — Zephyr does not build any
# SLCD driver for the MCXA366 itself.
zephyr_library_sources(${MCUX_SDK_NG_ROOT}/drivers/slcd/fsl_slcd.c)

# fsl_clock.h and fsl_port.h are header-only — `CLOCK_EnableClock`,
# `PORT_SetPinMux`, and `kCLOCK_GateSLCD0` are already provided by
# Zephyr's hal_nxp module (which pulls the MCXA346 SoC driver set).
# Do NOT compile MCXA266/devices/MCX/MCXA/MCXA266/drivers/fsl_clock.c
# here — it conflicts with the hal_nxp module's copy.

target_sources(app PRIVATE src/main.c src/slcd_od6010.c)

5、prj.conf

# OD-6010 SLCD demo on FRDM-MCXA366
#
# This config enables a minimal Zephyr setup that mixes the MCUX HAL
# (fsl_slcd.h, fsl_port.h, fsl_clock.h) into the build.  No SLCD Kconfig
# symbols exist in upstream Zephyr for the MCXA366, so nothing related to
# the driver itself needs to be toggled here.

CONFIG_GPIO=y
# Console + printk for the demo (debug UART on lpuart2 @115200).
CONFIG_SERIAL=y
CONFIG_CONSOLE=y
CONFIG_PRINTK=y
CONFIG_UART_CONSOLE=y
# main thread needs k_msleep()/k_uptime_get()
CONFIG_SYS_CLOCK_EXISTS=y

# Pull in the MCUX HAL device header from the NXP module.  Zephyr does
# not expose the MCXA366 device headers through Kconfig; we add the
# include path directly from CMakeLists.txt.  No Kconfig entry needed
# here, but it is useful to know that the device header ends up in
# modules/hal/nxp/mcux/mcux-sdk-ng/devices/MCX/MCXA/MCXA366/.

# Reduce firmware size: no logging / no shell / no extra init hooks.
CONFIG_LOG=y
# No Zephyr SLCD subsystem symbols required.





关键词: Zephyr     MCXA366     OD-6010     SL    

共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]