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

共1条 1/1 1 跳转至

【Zephyr|MCXA366】SLCD显示板载P3T1755温度计

高工
2026-09-05 12:46:01     打赏

【前言】

在前面我的一篇文章介绍了驱动开发板上的SLCD:【Zephyr|MCXA366】OD-6010SLCD驱动-电子产品世界论坛

这一篇,我将分享如何驱动开发板上板的P3T1755,并在SLCD上面显示温度值。

【实现步骤】

板载 NXP P3T1755 数字温度传感器挂在 I3C 总线上(地址 0x48,别名 ambient-temp0)。Zephyr 自带驱动 drivers/sensor/nxp/p3t1755/,通过 DTS 自动链入;只需要在 prj.conf 打开 I3C 子系统和传感器 API:

CONFIG_I3C=y
CONFIG_I3C_MCUX=y
CONFIG_SENSOR=y

temperature_task.c 起一个独立 Zephyr 线程(优先级 7),线程体里 k_msleep(1000) 循环。线程睡眠期间没有其他就绪任务,调度器进 idle 线程 → Cortex-M33 走 WFI → 直到 1 Hz 系统 tick 把 CPU 唤醒。main() 只调一次 temperature_task_init() 然后返回:

/*
 * Copyright (c) 2026
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Implementation of the periodic ambient-temperature task.
 *
 * Runs a dedicated Zephyr thread that sleeps SAMPLE_PERIOD_MS between
 * samples.  The thread is alive for the lifetime of the application
 * but spends ~99.9 % of its time inside k_msleep() — when the
 * scheduler has no other runnable work to do, Zephyr's idle thread
 * issues WFI on Cortex-M, so the CPU clocks gate between ticks.
 * The SLCD peripheral keeps refreshing the LCD during those gaps
 * because slcd_init() set kSLCD_EnabledInWaitStop.
 *
 * We use a thread (priority 7) instead of a k_timer fired into the
 * system workqueue because the I3C controller driver on this board
 * does not behave the same way when invoked from workqueue context:
 * the bus transaction times out (k_sem_take inside the MCUX SDK
 * helper returns -ETIMEDOUT).  Running at normal thread priority in
 * a real thread restores the behaviour seen when the loop lived
 * directly in main().
 */

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

#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zephyr/sys/printk.h>

#include "slcd_od6010.h"
#include "temperature_task.h"

#define SAMPLE_PERIOD_MS  1000U
#define TASK_PRIORITY     7U
#define TASK_STACK_SIZE   1024U

static const struct device *s_sensor;

/* Render a temperature value (in milli-°C) onto the 6-digit glass.
 *
 * Layout (left to right):  pos0 pos1 pos2 . pos3
 *                           sign  integer digits  decimal point  tenths
 *
 *   -55.0 .. -10.0  →  "-XX.X"  (pos 0 = dash)
 *    -9.9 ..   -0.1  →  " -X.X"  (pos 0 = blank, pos 1 = dash)
 *     0.0 ..   9.9   →  "   X.X" (pos 0..1 = blank)
 *    10.0 ..  99.9   →  "  XX.X" (pos 0 = blank)
 *   100.0 .. 999.0   →  " XXX.X" (pos 0..2 = three integer digits)
 *
 * Decimal point is the lower dot DP5 between positions 2 and 3.
 */
static void display_temperature(int64_t milli_c)
{
    uint32_t abs_value;
    uint32_t integer_part;
    uint32_t tenths;
    bool negative;

    if (milli_c < 0) {
        negative = true;
        /* Handle INT64_MIN: clamp to the supported sensor range. */
        abs_value = (uint32_t)(-(milli_c + 1)) + 1U;
    } else {
        negative = false;
        abs_value = (uint32_t)milli_c;
    }

    /* P3T1755 reports up to ±125 °C; clamp anything larger. */
    if (abs_value > 999000U) {
        abs_value = 999000U;
    }

    integer_part = abs_value / 1000U;
    tenths      = (abs_value / 100U) % 10U;

    slcd_clear();

    if (negative) {
        if (integer_part >= 10U) {
            /* "-XX.X" — use all 6 positions. */
            slcd_show_number(0, SLCD_GLYPH_DASH);
            slcd_show_number(1, (integer_part / 10U) % 10U);
            slcd_show_number(2, integer_part % 10U);
            slcd_show_number(3, tenths);
            slcd_set_icon(SLCD_ICON_DP5, true);
        } else {
            /* " -X.X" — leading blank then dash on pos 1. */
            slcd_show_number(1, SLCD_GLYPH_DASH);
            slcd_show_number(2, integer_part % 10U);
            slcd_show_number(3, tenths);
            slcd_set_icon(SLCD_ICON_DP5, true);
        }
    } else {
        if (integer_part >= 100U) {
            slcd_show_number(0, (integer_part / 100U) % 10U);
            slcd_show_number(1, (integer_part / 10U) % 10U);
            slcd_show_number(2, integer_part % 10U);
            slcd_show_number(3, tenths);
            slcd_set_icon(SLCD_ICON_DP5, true);
        } else if (integer_part >= 10U) {
            /* "  XX.X" */
            slcd_show_number(1, (integer_part / 10U) % 10U);
            slcd_show_number(2, integer_part % 10U);
            slcd_show_number(3, tenths);
            slcd_set_icon(SLCD_ICON_DP5, true);
        } else {
            /* "   X.X" */
            slcd_show_number(2, integer_part % 10U);
            slcd_show_number(3, tenths);
            slcd_set_icon(SLCD_ICON_DP5, true);
        }
    }
}

/* One iteration: read the sensor and push the value to the LCD. */
static void sample_once(void)
{
    struct sensor_value temp;
    int rc;
    int64_t milli;

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

    rc = sensor_channel_get(s_sensor, SENSOR_CHAN_AMBIENT_TEMP, &temp);
    if (rc != 0) {
        printk("sensor_channel_get: %d\n", rc);
        return;
    }

    milli = sensor_value_to_milli(&temp);

    display_temperature(milli);

    /* Mirror the value to the console for log capture. */
    int64_t whole = milli / 1000;
    int64_t frac  = (milli < 0 ? -milli : milli) % 1000 / 100;
    char sign = (milli < 0) ? '-' : ' ';

    printk("temp: %c%lld.%lld C\n", sign, whole, frac);
}

/* Dedicated thread.  Runs at user-level priority 7 so it has the same
 * scheduling context as the original main() loop.  Sleeps SAMPLE_PERIOD_MS
 * between samples; the kernel idle thread takes over and issues WFI
 * during each sleep window.
 */
static void temperature_thread_fn(void *p1, void *p2, void *p3)
{
    ARG_UNUSED(p1);
    ARG_UNUSED(p2);
    ARG_UNUSED(p3);

    while (1) {
        sample_once();
        k_msleep(SAMPLE_PERIOD_MS);
    }
}

K_THREAD_STACK_DEFINE(s_temperature_stack, TASK_STACK_SIZE);
static struct k_thread s_temperature_thread;

int temperature_task_init(void)
{
    int rc;

    s_sensor = DEVICE_DT_GET(DT_ALIAS(ambient_temp0));
    if (!device_is_ready(s_sensor)) {
        printk("P3T1755 @ ambient-temp0 not ready\n");
        return -ENODEV;
    }

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

    /* Sign-on: show "23.5" for 2 s so the user can confirm the
     * decimal point and segment wiring before the first real
     * reading lands.  DP5 is the lower dot between pos 2 and 3.
     */
    slcd_show_string("  235");
    slcd_set_icon(SLCD_ICON_DP5, true);
    k_msleep(2000);
    slcd_clear();

    /* Start the sampler thread.  It loops forever; main() can
     * return immediately and the kernel will context-switch
     * between the sampler and the idle thread (which drops into
     * WFI while the sampler is sleeping).
     */
    k_thread_create(&s_temperature_thread, s_temperature_stack,
            K_THREAD_STACK_SIZEOF(s_temperature_stack),
            temperature_thread_fn, NULL, NULL, NULL,
            TASK_PRIORITY, 0, K_NO_WAIT);
    k_thread_name_set(&s_temperature_thread, "temperature");

    return 0;
}

实现效果:

a302ba7b2294081e30e24bf159756807.jpg可见我们实现已有驱动的P3T1755还是挺方便的。




关键词: Zephyr     MCXA366     OD-6010     SL    

共1条 1/1 1 跳转至

回复

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