【前言】
在前面我的一篇文章介绍了驱动开发板上的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;
}实现效果:
可见我们实现已有驱动的P3T1755还是挺方便的。
我要赚赏金
