From eaa5d0c28b0b9545a1c9cef7906b41b705bf4307 Mon Sep 17 00:00:00 2001 From: zhangyisong Date: Thu, 9 Jul 2026 11:06:11 +0800 Subject: [PATCH] Add NTC sample configuration and PID temperature control - Add CONFIG_SAMPLE_NTC to CMakeLists.txt and Kconfig - Implement PI temperature controller with PWM output in heating.hpp - Convert NtcGroup from template to non-template class - Add LED helper class with flash support - Implement COM protocol handlers for device ID and running state - Add SPI, sensor, and PWM configuration to prj.conf - Remove indicator overlay and update main.cpp with temperature monitoring - Add test script for serial protocol communication --- CMakeLists.txt | 4 + Kconfig | 2 + boards/dr2501a_g070rb.overlay | 18 -- include/com.hpp | 45 +++-- include/heating.hpp | 145 ++++++++++++++++- include/led.hpp | 29 ++++ include/ntc.hpp | 34 ++-- prj.conf | 11 ++ scripts/test.py | 299 ++++++++++++++++++++++++++++++++++ src/main.cpp | 59 ++++++- src/sample_ntc.cpp | 16 +- testcase.yaml | 3 + zbuild.py | 14 +- 13 files changed, 631 insertions(+), 48 deletions(-) create mode 100644 include/led.hpp create mode 100644 scripts/test.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 27b7228..616f81b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,10 @@ target_include_directories(app PRIVATE include) if(CONFIG_SAMPLE_HELLOWORLD) message("CONFIG_SAMPLE_HELLOWORLD is enabled") target_sources(app PRIVATE src/sample_hello_world.cpp) +elseif(CONFIG_SAMPLE_NTC) + message("CONFIG_SAMPLE_NTC is enabled") + target_sources(app PRIVATE src/sample_ntc.cpp) else() + message("build main app") target_sources(app PRIVATE src/main.cpp) endif() diff --git a/Kconfig b/Kconfig index 592fff9..721f3ef 100644 --- a/Kconfig +++ b/Kconfig @@ -4,3 +4,5 @@ endmenu config SAMPLE_HELLOWORLD bool "Hello World sample" +config SAMPLE_NTC + bool "NTC sample" diff --git a/boards/dr2501a_g070rb.overlay b/boards/dr2501a_g070rb.overlay index 4739cfd..e69de29 100644 --- a/boards/dr2501a_g070rb.overlay +++ b/boards/dr2501a_g070rb.overlay @@ -1,18 +0,0 @@ - &indicator { - led-strip = <&led_strip>; - compatible = "led-strip-indicator"; - en-gpios = <&gpiob 13 GPIO_ACTIVE_HIGH>; - standby { - rgb = <0 0 255>; // Blue - }; - running { - rgb = <0 255 0>; // Green - }; - pause { - rgb = <254 254 0>; - // interval-ms = <500>; - }; - error { - rgb = <255 0 0>; //Red - }; - }; \ No newline at end of file diff --git a/include/com.hpp b/include/com.hpp index 99581f1..b222a66 100644 --- a/include/com.hpp +++ b/include/com.hpp @@ -1,34 +1,59 @@ #ifndef __THER_COM_HPP__ #define __THER_COM_HPP__ +#include "led.hpp" #include #include +#include +#include #include #include namespace ther { -template class Com { +class Com { public: - enum Addr : uint8_t { TEMP = 0, GET_ID, RUNNING_STATE }; - using CbTableValueType = - std::pair; static auto Init() -> zpp::error { - s_proto->SetRxCallbackTable({GET_ID, Cb::GetId}, - {RUNNING_STATE, Cb::RunningState}); + s_proto->SetRxCallbackTable(kRxCallbackTable); return zpp::ok(); } + static auto Send(sensor_value val) -> void { + const uint8_t temp_data[] = {(uint8_t)val.val1, (uint8_t)val.val2}; + s_proto->Send(TEMP, temp_data); + } private: + enum HostStatus : uint8_t { STANDBY, RUNNING, PAUSE, ERROR }; + enum Addr : uint8_t { TEMP = 0, GET_ID, RUNNING_STATE }; + using CbTableValueType = + std::pair; struct Cb { static auto GetId(uart_com::DataType data) -> void { - // TODO + printk("handle get id"); + uint8_t buff[20]; + auto size = hwinfo_get_device_id(buff, sizeof(buff)); + s_proto->Send(GET_ID, uart_com::DataType(buff, size)); } static auto RunningState(uart_com::DataType data) -> void { - // TODO + if (data.size() != 1) { + return; + } + if (data[0] == RUNNING) { + InfLed::On(); + } else { + InfLed::Off(); + } + s_led_strip_indicator->Status(data[0]); } }; - constexpr static uart_com::SimpleProtocal *s_proto = - (uart_com::SimpleProtocal *)(kDev); + constexpr static std::pair + kRxCallbackTable[] = {{GET_ID, Cb::GetId}, + {RUNNING_STATE, Cb::RunningState}}; + inline static auto s_led_strip_indicator = + ZPP_DRV_GET_P(ledstrip::Indicator, DT_NODELABEL(indicator)); + inline static auto s_proto = + (uart_com::SimpleProtocal *)(DEVICE_DT_GET(DT_NODELABEL(pm_protocal))); + using InfLed = ther::Led; }; } // namespace ther diff --git a/include/heating.hpp b/include/heating.hpp index eafd951..98c123a 100644 --- a/include/heating.hpp +++ b/include/heating.hpp @@ -1,11 +1,154 @@ #ifndef __THER_HEATING_HPP__ #define __THER_HEATING_HPP__ +#include #include +#include +#include +#include +#include namespace ther { -template class HeatingPad { + +class HeatingPad { public: + /// Start PID temperature control toward the given target. + static auto Start(sensor_value target_temp) -> void { + s_target = sensor_value_to_double(&target_temp); + s_integral = 0.0; + s_last_error = 0.0; + s_last_time = k_uptime_get(); + + printk("[heat] start target=%.1f°C\n", s_target); + + // Check PWM device + if (!device_is_ready(s_pwm_spec.dev)) { + printk("[heat] ERR: PWM device not ready\n"); + return; + } + printk("[heat] PWM dev=%s ch=%d period=%u ns\n", s_pwm_spec.dev->name, + s_pwm_spec.channel, s_pwm_spec.period); + + // Check sensor + if (!device_is_ready(s_temp_sensor)) { + printk("[heat] ERR: temp sensor not ready\n"); + return; + } + printk("[heat] temp sensor OK\n"); + + // Test: set 50% duty immediately to verify PWM works + const uint32_t pulse_50 = s_pwm_spec.period / 2; + int ret = pwm_set_pulse_dt(&s_pwm_spec, pulse_50); + printk("[heat] test 50%% duty pulse=%u ret=%d\n", pulse_50, ret); + + k_timer_init(&s_timer, Tick, nullptr); + k_timer_start(&s_timer, K_NO_WAIT, K_MSEC(kUpdatePeriodMs)); + s_active = true; + } + static auto Start() { + + const uint32_t pulse_50 = s_pwm_spec.period / 2; + int ret = pwm_set_pulse_dt(&s_pwm_spec, pulse_50); + return ret; + } + /// Stop PID control and turn off heating. + static auto Stop() -> void { + printk("[heat] stop\n"); + s_active = false; + k_timer_stop(&s_timer); + pwm_set_pulse_dt(&s_pwm_spec, 0); + } + + /// Change the target temperature while keeping PID running. + static auto SetTarget(sensor_value target_temp) -> void { + s_target = sensor_value_to_double(&target_temp); + printk("[heat] new target=%.1f°C\n", s_target); + } + +private: + static constexpr uint32_t kPeriodNs = PWM_KHZ(5); + static constexpr uint32_t kUpdatePeriodMs = 100; + static constexpr double kOutMin = 0.0; + static constexpr double kOutMax = 1.0; + + // ── PI gains (tune these) ───────────────────────── + static constexpr double kKp = 2.0; // proportional + static constexpr double kKi = 0.02; // integral + static constexpr double kKd = 0.0; // derivative (not needed for heating-only) + + inline static bool s_active{false}; + inline static double s_target{0.0}; + inline static double s_integral{0.0}; + inline static double s_last_error{0.0}; + inline static int64_t s_last_time{0}; + + inline static pwm_dt_spec s_pwm_spec = { + .dev = DEVICE_DT_GET(DT_NODELABEL(pwm1)), + .channel = 1, + .period = PWM_KHZ(5), + .flags = PWM_POLARITY_NORMAL, + }; + + inline static const device *s_temp_sensor = + DEVICE_DT_GET(DT_NODELABEL(heating_pad_ntc)); + + inline static k_timer s_timer; + + /// Called periodically by the timer. + static auto Tick(k_timer * /*timer*/) -> void { + double current_temp; + if (ReadTemperature(current_temp) != 0) { + printk("[heat] ERR: read temp failed\n"); + return; + } + + printk("[heat] tick: cur=%.1f°C target=%.1f°C\n", current_temp, s_target); + + // PI computation + const int64_t now = k_uptime_get(); + const double dt = static_cast(now - s_last_time) / 1000.0; + s_last_time = now; + + const double error = s_target - current_temp; + + // Proportional + const double p = kKp * error; + + // Integral with anti-windup + s_integral += kKi * error * dt; + s_integral = std::clamp(s_integral, kOutMin, kOutMax); + + s_last_error = error; + + // Compute output, clamp to [0, 1] + double output = p + s_integral; + output = std::clamp(output, kOutMin, kOutMax); + + // Apply PWM duty cycle + const uint32_t pulse = static_cast(output * kPeriodNs); + printk("[heat] PID out=%.2f%% pulse=%u / %u\n", output * 100.0, pulse, + s_pwm_spec.period); + pwm_set_pulse_dt(&s_pwm_spec, pulse); + } + + /// Read temperature from the NTC sensor into `out_temp` (in °C). + static auto ReadTemperature(double &out_temp) -> int { + if (0 != sensor_sample_fetch(s_temp_sensor)) { + printk("[heat] ERR: sensor fetch failed\n"); + return -EIO; + } + + sensor_value val{}; + if (0 != + sensor_channel_get(s_temp_sensor, SENSOR_CHAN_AMBIENT_TEMP, &val)) { + printk("[heat] ERR: channel_get failed\n"); + return -EIO; + } + + out_temp = sensor_value_to_double(&val); + return 0; + } }; } // namespace ther + #endif diff --git a/include/led.hpp b/include/led.hpp new file mode 100644 index 0000000..4735706 --- /dev/null +++ b/include/led.hpp @@ -0,0 +1,29 @@ +#ifndef __THER_LED_HPP__ +#define __THER_LED_HPP__ +#include +#include +namespace ther { + +template class Led { +public: + static auto On() { led_on_dt(&kSpec); } + static auto Off() { led_off_dt(&kSpec); } + template + static auto Flash(std::chrono::duration period) { + s_work.submit(period); + } + +private: + inline static zpp::periodic_work<> s_work{[]() { + static bool is_on = false; + if (is_on) { + Off(); + } else { + On(); + } + is_on = !is_on; + }}; +}; + +} // namespace ther +#endif diff --git a/include/ntc.hpp b/include/ntc.hpp index 0fbcaeb..c337534 100644 --- a/include/ntc.hpp +++ b/include/ntc.hpp @@ -1,43 +1,55 @@ #ifndef THER_NTC_HPP #define THER_NTC_HPP +#include #include #include #include -#include namespace ther { -template class Ntc { +class NtcGroup { public: /// Read all NTC sensors, return the maximum temperature value. /// Returns error if every sensor read fails. - static auto GetSensorValue() - -> zpp::result> { + static auto GetSensorValue() -> zpp::result { bool found = false; - zpp::value max_val; + sensor_value max_val{}; for (const auto *dev : s_devices) { - if (0 != sensor_sample_fetch(dev)) + if (0 != sensor_sample_fetch(dev)) { continue; + } - zpp::value val; - if (0 != sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &val)) + sensor_value val{}; + if (0 != sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &val)) { continue; + } - if (!found || val.to_double() > max_val.to_double()) { + if (!found || val.val1 > max_val.val1 || + (val.val1 == max_val.val1 && val.val2 > max_val.val2)) { max_val = val; found = true; } } - if (found) + if (found) { return max_val; + } return zpp::error_code::k_io; } private: - static constexpr device *s_devices[] = {kNtcDev...}; + inline static const device *s_devices[] = { + DEVICE_DT_GET(DT_NODELABEL(pole_ntc1)), + DEVICE_DT_GET(DT_NODELABEL(pole_ntc2)), + DEVICE_DT_GET(DT_NODELABEL(pole_ntc3)), + DEVICE_DT_GET(DT_NODELABEL(pole_ntc4)), + DEVICE_DT_GET(DT_NODELABEL(pole_ntc5)), + DEVICE_DT_GET(DT_NODELABEL(pole_ntc6)), + DEVICE_DT_GET(DT_NODELABEL(pole_ntc7)), + DEVICE_DT_GET(DT_NODELABEL(pole_ntc8)), + }; }; } // namespace ther diff --git a/prj.conf b/prj.conf index 06118a8..a7da3d6 100644 --- a/prj.conf +++ b/prj.conf @@ -9,5 +9,16 @@ CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_HWINFO=y CONFIG_ADC=y +CONFIG_SPI=y +CONFIG_SPI_STM32=y +CONFIG_SENSOR=y +CONFIG_ADC_MCP320X_ACQUISITION_THREAD_STACK_SIZE=2048 CONFIG_REBOOT=y CONFIG_WATCHDOG=y +CONFIG_PWM=y +# # Debug logging +# CONFIG_LOG=y +# CONFIG_LOG_MODE_IMMEDIATE=y +# CONFIG_ADC_LOG_LEVEL_DBG=y +# CONFIG_SENSOR_LOG_LEVEL_DBG=y +# CONFIG_SPI_LOG_LEVEL_DBG=y diff --git a/scripts/test.py b/scripts/test.py new file mode 100644 index 0000000..9ca9bad --- /dev/null +++ b/scripts/test.py @@ -0,0 +1,299 @@ +#!/usr/bin/env python3 +""" +Test script for app_photomagnetic communication protocol. + +Frame format (SimpleProtocal, per DTS config): + H0(0x7E) H1(0xE7) CMD(1B) LEN(1B) DATA[0..N] CRC_LO CRC_HI + CRC = CRC-16 Modbus over CMD+LEN+DATA, LSB-MSB order + +Usage: + python test.py /dev/ttyUSB0 --get-id (request device ID) + python test.py /dev/ttyUSB0 --state 1 (set running state) + python test.py /dev/ttyUSB0 --monitor (listen for temp data) + python test.py /dev/ttyUSB0 --baud 115200 --loop (poll ID in a loop) +""" + +import argparse +import struct +import sys +import time + +try: + import serial +except ImportError: + print("Please install pyserial: pip install pyserial", file=sys.stderr) + sys.exit(1) + +# ── Protocol constants ──────────────────────────────────────── +HEADER = b"\x7e\xe7" # 2-byte header (DTS: header = <0x7EE7>) +HEADER_SIZE = len(HEADER) + +CMD_TEMP = 0x00 +CMD_GET_ID = 0x01 +CMD_RUNNING_STATE = 0x02 + +STATE_NAMES = { + 0x00: "Standby", + 0x01: "Running", + 0x02: "Pause", + 0x03: "Fault", + 0x04: "Upgrade", + 0x05: "EStop", + 0x06: "Exception", +} + + +# ── CRC-16 Modbus ───────────────────────────────────────────── +def crc16_modbus(data: bytes) -> int: + """CRC-16 Modbus (polynomial 0xA001, init 0xFFFF).""" + crc = 0xFFFF + for byte in data: + crc ^= byte + for _ in range(8): + if crc & 1: + crc = (crc >> 1) ^ 0xA001 + else: + crc >>= 1 + return crc & 0xFFFF + + +# ── Frame helpers ───────────────────────────────────────────── +def build_frame(cmd: int, payload: bytes = b"") -> bytes: + """Build a SimpleProtocal frame with LSB-MSB CRC. + Frame: H0 H1 CMD LEN DATA[0..N] CRC_LO CRC_HI""" + body = bytes([cmd, len(payload)]) + payload + crc = crc16_modbus(body) + crc_lo = crc & 0xFF + crc_hi = (crc >> 8) & 0xFF + return HEADER + body + bytes([crc_lo, crc_hi]) + + +def parse_frame(frame: bytes) -> tuple[int, bytes] | None: + """Parse a SimpleProtocal frame with 2-byte header + LSB-MSB CRC. + Returns (cmd, data) or None on error.""" + min_len = HEADER_SIZE + 2 + 2 # HDR(2) + CMD(1) + LEN(1) + CRC(2) = 6 + if len(frame) < min_len: + return None + if frame[:HEADER_SIZE] != HEADER: + return None + + cmd = frame[HEADER_SIZE] + length = frame[HEADER_SIZE + 1] + expected = HEADER_SIZE + 2 + length + 2 # HDR + CMD+LEN + DATA + CRC + if len(frame) != expected: + return None + + data = frame[HEADER_SIZE + 2 : HEADER_SIZE + 2 + length] + + # CRC covers CMD + LEN + DATA (LSB-MSB order on wire) + crc_body = frame[HEADER_SIZE : HEADER_SIZE + 2 + length] + actual_crc = crc16_modbus(crc_body) + crc_lo = frame[HEADER_SIZE + 2 + length] + crc_hi = frame[HEADER_SIZE + 2 + length + 1] + wire_crc = (crc_hi << 8) | crc_lo + + if actual_crc != wire_crc: + return None + + return cmd, data + + +# ── Serial reader ───────────────────────────────────────────── +class ProtocolReader: + """State-machine frame reader for 2-byte header protocol.""" + + def __init__(self, ser: serial.Serial): + self._ser = ser + self._buf = bytearray() + + def read_frame(self, timeout: float = 1.0) -> bytes | None: + """Read one complete frame. Returns raw frame bytes or None on timeout.""" + min_frame = HEADER_SIZE + 2 + 2 # HDR + CMD + LEN + CRC = 6 + + deadline = time.time() + timeout + while time.time() < deadline: + # Look for header + if len(self._buf) >= 2: + idx = self._buf.find(HEADER) + if idx > 0: + del self._buf[:idx] # discard bytes before header + elif idx < 0 and len(self._buf) > 0: + self._buf.clear() + + # Have a header → try to parse frame length + if len(self._buf) >= HEADER_SIZE: + if self._buf[:HEADER_SIZE] == HEADER: + if len(self._buf) >= min_frame: + length = self._buf[HEADER_SIZE + 1] + total = HEADER_SIZE + 2 + length + 2 + if len(self._buf) >= total: + frame = bytes(self._buf[:total]) + del self._buf[:total] + return frame + else: + # Wrong header, discard first byte and retry + del self._buf[0] + continue + + # Read more bytes + try: + chunk = self._ser.read(self._ser.in_waiting or 1) + if chunk: + self._buf.extend(chunk) + except (serial.SerialTimeoutException, serial.SerialException): + pass + + return None + + +# ── Command handlers ────────────────────────────────────────── +def cmd_get_id(ser: serial.Serial, timeout: float = 2.0): + """Send GET_ID request and print response.""" + print("→ Sending GET_ID...") + ser.write(build_frame(CMD_GET_ID)) + + reader = ProtocolReader(ser) + frame = reader.read_frame(timeout) + if frame is None: + print("✗ No response (timeout)") + return + + result = parse_frame(frame) + if result is None: + print(f"✗ Invalid frame: {frame.hex()}") + return + + cmd, data = result + if cmd != CMD_GET_ID: + print(f"✗ Unexpected cmd=0x{cmd:02X} (expected 0x{CMD_GET_ID:02X})") + return + + print(f"✓ Device ID ({len(data)} bytes): {data.hex(' ')}") + + +def cmd_set_state(ser: serial.Serial, state: int): + """Send RUNNING_STATE command.""" + name = STATE_NAMES.get(state, "Unknown") + print(f"→ Setting state: {name} (0x{state:02X})") + ser.write(build_frame(CMD_RUNNING_STATE, bytes([state]))) + print(f"✓ Sent") + + +def cmd_monitor(ser: serial.Serial): + """Listen for incoming frames (TEMP data) and print them.""" + print("Monitoring... (Ctrl+C to stop)") + reader = ProtocolReader(ser) + try: + while True: + frame = reader.read_frame(timeout=0.5) + if frame is None: + continue + + result = parse_frame(frame) + if result is None: + print(f"⚠ Bad frame: {frame.hex()}") + continue + + cmd, data = result + if cmd == CMD_TEMP: + if len(data) >= 2: + # sensor_value: val1 (int8), val2 (int8) + t = data[0] + frac = data[1] / 100.0 + temp = t + frac + print(f"🌡 Temp: {temp:.2f} °C (raw: {data.hex(' ')})") + else: + print(f"⚠ TEMP with invalid data: {data.hex()}") + elif cmd == CMD_GET_ID: + print(f"🆔 Device ID response: {data.hex(' ')}") + elif cmd == CMD_RUNNING_STATE: + print(f"🔁 Running state echo: 0x{data.hex()}") + else: + print(f"📦 Unknown cmd=0x{cmd:02X} data={data.hex()}") + except KeyboardInterrupt: + print("\nDone.") + + +def cmd_loop(ser: serial.Serial, count: int = 0): + """Send GET_ID in a loop.""" + i = 0 + print(f"Looping GET_ID... (Ctrl+C to stop)") + try: + while count == 0 or i < count: + cmd_get_id(ser, timeout=1.0) + i += 1 + time.sleep(0.5) + except KeyboardInterrupt: + print("\nDone.") + + +# ── Main ────────────────────────────────────────────────────── +def main(): + parser = argparse.ArgumentParser( + description="Test script for app_photomagnetic communication protocol" + ) + parser.add_argument("port", help="Serial port (e.g. /dev/ttyUSB0, COM3)") + parser.add_argument( + "--baud", type=int, default=115200, help="Baud rate (default: 115200)" + ) + parser.add_argument( + "--timeout", type=float, default=0.1, help="Serial timeout in seconds" + ) + parser.add_argument( + "--get-id", action="store_true", help="Send GET_ID and print response" + ) + parser.add_argument( + "--state", + type=lambda x: int(x, 0), + metavar="N", + help="Set running state (0=Standby, 1=Running, 2=Pause, ...)", + ) + parser.add_argument( + "--monitor", action="store_true", help="Listen for incoming frames" + ) + parser.add_argument( + "--loop", + type=int, + nargs="?", + const=0, + metavar="N", + help="Send GET_ID in a loop (N times, or infinite if omitted)", + ) + parser.add_argument( + "--raw", + type=lambda x: bytes.fromhex(x), + metavar="HEX", + help="Send raw payload (CMD DATA..., CRC auto-appended)", + ) + parser.add_argument("--debug", action="store_true", help="Show raw frame hex") + + args = parser.parse_args() + + ser = serial.Serial(args.port, args.baud, timeout=args.timeout) + print(f"Connected to {args.port} @ {args.baud} baud") + + try: + if args.get_id: + cmd_get_id(ser) + elif args.state is not None: + cmd_set_state(ser, args.state) + elif args.monitor: + cmd_monitor(ser) + elif args.loop is not None: + cmd_loop(ser, args.loop) + elif args.raw is not None: + payload = args.raw + cmd, data = payload[0], payload[1:] if len(payload) > 1 else b"" + print(f"→ Sending CMD=0x{cmd:02X} data={data.hex()}") + frame = build_frame(cmd, data) + print(f" Frame: {frame.hex()}") + ser.write(frame) + print(f"✓ Sent") + else: + print("No action. Use --get-id, --state N, --monitor, --loop, or --raw") + finally: + ser.close() + + +if __name__ == "__main__": + main() diff --git a/src/main.cpp b/src/main.cpp index d1607a4..07dfe35 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,16 +1,63 @@ #include #include #include +#include #include +#include #include -namespace {} // namespace +namespace { +using StatusLed = ther::Led; +bool is_infrared_ready = false; +auto infrared = DEVICE_DT_GET(DT_NODELABEL(godtek)); +auto GetmaxTemp() -> std::optional { + if (is_infrared_ready) { + sensor_value max_val{}; + bool found = false; -auto main(void) -> int { - auto wdt = app::WatchDogConfig{}; + // NTC 8 路最大值 + auto ntc_result = ther::NtcGroup::GetSensorValue(); + if (ntc_result) { + max_val = *ntc_result; + found = true; + } - while (1) { - - wdt.Feed(); + // 红外数据就绪时参与比较 + sensor_value ir_val{}; + if (0 == sensor_sample_fetch_chan(infrared, SENSOR_CHAN_AMBIENT_TEMP)) { + if (0 == + sensor_channel_get(infrared, SENSOR_CHAN_AMBIENT_TEMP, &ir_val)) { + if (!found || ir_val.val1 > max_val.val1 || + (ir_val.val1 == max_val.val1 && ir_val.val2 > max_val.val2)) { + max_val = ir_val; + } + is_infrared_ready = false; + } + } + return max_val; } + return std::nullopt; +} +} // namespace +auto main(void) -> int { + using namespace std::chrono_literals; + printk("app on board %s", CONFIG_BOARD); + // auto wdt = app::WatchDogConfig{}; + StatusLed::Flash(1s); + ther::Com::Init(); + // ther::HeatingPad::Start(); + ther::HeatingPad::Start(sensor_value{42, 0}); + sensor_trigger tri{.type = SENSOR_TRIG_DATA_READY, + .chan = SENSOR_CHAN_AMBIENT_TEMP}; + sensor_trigger_set(infrared, &tri, + [](const device *dev, const sensor_trigger *trig) { + is_infrared_ready = true; + }); + while (1) { + if (auto v = GetmaxTemp(); v.has_value()) { + ther::Com::Send(v.value()); + } + } + // wdt.Feed(); + k_sleep(K_MSEC(20)); return 0; } diff --git a/src/sample_ntc.cpp b/src/sample_ntc.cpp index 4267154..c64fd05 100644 --- a/src/sample_ntc.cpp +++ b/src/sample_ntc.cpp @@ -1,4 +1,18 @@ #include +#include #include -auto main() -> int { return 0; } +#define NTC_INS(node) DEVICE_DT_GET(node), + +const device *ntc = DEVICE_DT_GET(DT_NODELABEL(pole_ntc1)); +auto main() -> int { + sensor_value v; + while (1) { + if (0 == sensor_sample_fetch(ntc)) { + sensor_channel_get(ntc, SENSOR_CHAN_AMBIENT_TEMP, &v); + printk("temp: %d.%d\n", v.val1, v.val2); + } + k_sleep(K_SECONDS(1)); + } + return 0; +} diff --git a/testcase.yaml b/testcase.yaml index e8aad92..aaf757f 100644 --- a/testcase.yaml +++ b/testcase.yaml @@ -6,3 +6,6 @@ tests: sample.helloworld: extra_configs: - CONFIG_SAMPLE_HELLOWORLD=y + sample.ntc: + extra_configs: + - CONFIG_SAMPLE_NTC=y diff --git a/zbuild.py b/zbuild.py index c96b28c..90cbb53 100755 --- a/zbuild.py +++ b/zbuild.py @@ -6,16 +6,18 @@ Usage: python zbuild.py dr2501a_g070rb python zbuild.py dr2501a_g070rb -p auto python zbuild.py -p always (auto-detect board from west.yml) + python zbuild.py dr2501a_g070rb -T sample.helloworld Options: board Board name (optional, auto-detected from west.yml if omitted) -p PURSE Pristine option: auto / always / never -t TARGET CMake target to run after build, e.g. test / flash + -T TEST Test name/sample identifier (e.g. sample.helloworld) Steps: 1. west topdir → find Zephyr workspace root 2. west config --local manifest.file /west.yml - 3. west build [-p PURSE] -b BOARD [-t TARGET] + 3. west build [-p PURSE] -b BOARD [-t TARGET] [-T TEST] """ import argparse @@ -78,6 +80,13 @@ def main(): default=None, help="CMake target to run after build (e.g. test, flash)", ) + parser.add_argument( + "-T", + "--test", + default=None, + dest="test_id", + help="Test name / sample identifier (e.g. sample.helloworld)", + ) parser.add_argument( "--update", action="store_true", @@ -117,6 +126,7 @@ def main(): print(f" board : {args.board}") print(f" pristine : {args.pristine or '(none)'}") print(f" target : {args.target or '(none)'}") + print(f" test : {args.test_id or '(none)'}") print(f" update : {'yes' if args.update else 'no'}") # ── 1. Find workspace root ─────────────────────────────── @@ -146,6 +156,8 @@ def main(): build_cmd += ["-b", args.board] if args.target: build_cmd += ["-t", args.target] + if args.test_id: + build_cmd += ["-T", args.test_id] build_cmd += [script_dir] print(f" $ {' '.join(build_cmd)}")