feat: Add Kconfig and conditional build for hello world sample

This commit is contained in:
zhangyisong 2026-07-07 19:47:30 +08:00
parent 1f2a92ad04
commit e74fa5c804
13 changed files with 458 additions and 27 deletions

View File

@ -3,7 +3,13 @@ cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(app_photomagnetic) project(app_photomagnetic)
target_include_directories(app PRIVATE include) 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)
else()
target_sources(app PRIVATE src/main.cpp) target_sources(app PRIVATE src/main.cpp)
endif()

6
Kconfig Normal file
View File

@ -0,0 +1,6 @@
menu "app thermotherapy"
source "Kconfig.zephyr"
endmenu
config SAMPLE_HELLOWORLD
bool "Hello World sample"

35
include/com.hpp Normal file
View File

@ -0,0 +1,35 @@
#ifndef __THER_COM_HPP__
#define __THER_COM_HPP__
#include <led_strip_indicator/led_strip_indicator.hpp>
#include <uart_com/simple_protocal.hpp>
#include <zpp/driver.hpp>
#include <zpp/error.hpp>
namespace ther {
template <device *kDev> class Com {
public:
enum Addr : uint8_t { TEMP = 0, GET_ID, RUNNING_STATE };
using CbTableValueType =
std::pair<const uint8_t, void (*)(uart_com::DataType)>;
static auto Init() -> zpp::error {
s_proto->SetRxCallbackTable({GET_ID, Cb::GetId},
{RUNNING_STATE, Cb::RunningState});
return zpp::ok();
}
private:
struct Cb {
static auto GetId(uart_com::DataType data) -> void {
// TODO
}
static auto RunningState(uart_com::DataType data) -> void {
// TODO
}
};
constexpr static uart_com::SimpleProtocal *s_proto =
(uart_com::SimpleProtocal *)(kDev);
};
} // namespace ther
#endif

11
include/heating.hpp Normal file
View File

@ -0,0 +1,11 @@
#ifndef __THER_HEATING_HPP__
#define __THER_HEATING_HPP__
#include <zephyr/device.h>
namespace ther {
template <const device *kDev> class HeatingPad {
public:
};
} // namespace ther
#endif

49
include/infrared.hpp Normal file
View File

@ -0,0 +1,49 @@
#ifndef __THER_INFRARED_HPP__
#define __THER_INFRARED_HPP__
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zpp/result.hpp>
#include <zpp/value.hpp>
#include <zpp/work_queue.hpp>
namespace ther {
template <device *kDev> class Infrared {
public:
static auto Init() -> zpp::error {
static sensor_trigger tri{.type = SENSOR_TRIG_DATA_READY,
.chan = SENSOR_CHAN_AMBIENT_TEMP};
if (auto r = sensor_trigger_set(
kDev, &tri,
[](const device *dev, const sensor_trigger *trig) {
s_is_data_ready = true;
});
r != 0) {
return -ENODEV;
};
}
static auto GetSensorValue()
-> zpp::result<zpp::value<zpp::value_type::AMBIENT_TEMP>> {
if (!s_is_data_ready) {
return zpp::error_code::k_nodata;
}
if (0 != sensor_sample_fetch(kDev)) {
return zpp::error_code::k_io;
}
zpp::value<zpp::value_type::AMBIENT_TEMP> val;
if (0 != sensor_channel_get(kDev, SENSOR_CHAN_AMBIENT_TEMP, &val)) {
return zpp::error_code::k_io;
}
s_is_data_ready = false;
return val;
}
private:
inline static bool s_is_data_ready{false};
};
} // namespace ther
#endif

45
include/ntc.hpp Normal file
View File

@ -0,0 +1,45 @@
#ifndef THER_NTC_HPP
#define THER_NTC_HPP
#include <zephyr/device.h>
#include <zephyr/drivers/sensor.h>
#include <zpp/result.hpp>
#include <zpp/value.hpp>
namespace ther {
template <device *...kNtcDev> class Ntc {
public:
/// Read all NTC sensors, return the maximum temperature value.
/// Returns error if every sensor read fails.
static auto GetSensorValue()
-> zpp::result<zpp::value<zpp::value_type::AMBIENT_TEMP>> {
bool found = false;
zpp::value<zpp::value_type::AMBIENT_TEMP> max_val;
for (const auto *dev : s_devices) {
if (0 != sensor_sample_fetch(dev))
continue;
zpp::value<zpp::value_type::AMBIENT_TEMP> val;
if (0 != sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &val))
continue;
if (!found || val.to_double() > max_val.to_double()) {
max_val = val;
found = true;
}
}
if (found)
return max_val;
return zpp::error_code::k_io;
}
private:
static constexpr device *s_devices[] = {kNtcDev...};
};
} // namespace ther
#endif // THER_NTC_HPP

View File

@ -1,14 +1,14 @@
CONFIG_STDOUT_CONSOLE=y CONFIG_STDOUT_CONSOLE=y
CONFIG_CBPRINTF_FP_SUPPORT=y CONFIG_CBPRINTF_FP_SUPPORT=y
CONFIG_STD_CPP20=y CONFIG_STD_CPP23=y
CONFIG_CPP=y CONFIG_CPP=y
CONFIG_REQUIRES_FULL_LIBCPP=y CONFIG_REQUIRES_FULL_LIBCPP=y
CONFIG_CONSOLE=y CONFIG_CONSOLE=y
CONFIG_SERIAL=y CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_PMC_COM=y
CONFIG_HWINFO=y CONFIG_HWINFO=y
CONFIG_ADC=y
CONFIG_REBOOT=y CONFIG_REBOOT=y
CONFIG_WATCHDOG=y CONFIG_WATCHDOG=y

View File

@ -3,47 +3,85 @@
#include <zephyr/drivers/hwinfo.h> #include <zephyr/drivers/hwinfo.h>
#include <zephyr/drivers/sensor.h> #include <zephyr/drivers/sensor.h>
#include <led_strip_indicator/led_strip_indicator.hpp>
#include <uart_com/protocal.hpp>
#include <zpp/device.hpp>
#include "watchdog.hpp" #include "watchdog.hpp"
#include "zephyr/drivers/gpio.h" #include "zephyr/drivers/gpio.h"
#include "zephyr/kernel.h" #include "zephyr/kernel.h"
#include "zpp/fmt.hpp" #include "zpp/fmt.hpp"
#include "zpp/timer.hpp" #include "zpp/timer.hpp"
#include <bitset>
#include <led_strip_indicator/led_strip_indicator.hpp>
#include <uart_com/simple_protocal.hpp>
#include <zpp/device.hpp>
#include <zpp/value.hpp>
namespace { namespace {
constexpr uart_com::SimpleProtocal::CallbackType kCbTable[] = {};
auto sensor = DEVICE_DT_GET(DT_NODELABEL(godtek)); auto sensor = DEVICE_DT_GET(DT_NODELABEL(godtek));
auto &pmc = ZPP_DRV_GET(uart_com::Protocal, DT_NODELABEL(pm_protocal)); auto &pmc =
*(uart_com::SimpleProtocal *)(DEVICE_DT_GET(DT_NODELABEL(pm_protocal)));
auto &led_strip = ZPP_DRV_GET(ledstrip::Indicator, DT_NODELABEL(indicator)); auto &led_strip = ZPP_DRV_GET(ledstrip::Indicator, DT_NODELABEL(indicator));
enum Command { kTemp = 0x00, kGetId, kRunningState }; enum Command { kTemp = 0x00, kGetId, kRunningState };
enum RunningState {}; enum RunningState {};
constexpr auto kDeviceIdSize = 20; constexpr auto kDeviceIdSize = 20;
enum StatusId : uint8_t { kStandby, kRunning, kPause, kError }; enum StatusId : uint8_t { kStandby, kRunning, kPause, kError };
volatile bool flag{false};
gpio_dt_spec led_g = GPIO_DT_SPEC_GET(DT_NODELABEL(led_g), gpios); gpio_dt_spec led_g = GPIO_DT_SPEC_GET(DT_NODELABEL(led_g), gpios);
k_timer led_timer; k_timer led_timer;
} // namespace
auto main(void) -> int { #define GET_NTC_DRV(node) DEVICE_DT_GET(node),
gpio_pin_configure_dt(&led_g, GPIO_OUTPUT_ACTIVE); const device *ntc_sensor[] = {
k_timer_init(&led_timer, [](k_timer* tim) { DT_FOREACH_CHILD(DT_NODELABEL(mcp3208), GET_NTC_DRV)};
gpio_pin_toggle_dt(&led_g);
}, [](k_timer* tim) {}); struct Cb {
k_timer_start(&led_timer, K_NO_WAIT, K_SECONDS(1)); static auto GetId(uart_com::DataType data) -> void {
pmc.SetRxCallback(kGetId, [](uart_com::DataType data) -> void {
uint8_t buffer[kDeviceIdSize]; uint8_t buffer[kDeviceIdSize];
auto size = hwinfo_get_device_id(buffer, sizeof(buffer)); auto size = hwinfo_get_device_id(buffer, sizeof(buffer));
pmc.Send(kGetId, uart_com::DataType(buffer, size)); pmc.Send(kGetId, uart_com::DataType(buffer, size));
}); }
static auto RunningState(uart_com::DataType data) -> void {
pmc.SetRxCallback(kRunningState, [](uart_com::DataType data) -> void {
auto state = static_cast<StatusId>(data[0]); auto state = static_cast<StatusId>(data[0]);
pmc.Send( pmc.Send(
kRunningState, kRunningState,
uart_com::DataType(reinterpret_cast<uint8_t *>(&state), sizeof(state))); uart_com::DataType(reinterpret_cast<uint8_t *>(&state), sizeof(state)));
led_strip.Status(state).on_error([](zpp::error_code code) {}); led_strip.Status(state).on_error([](zpp::error_code code) {});
}); }
static constexpr std::pair<const uint8_t,
uart_com::SimpleProtocal::CallbackType>
kCbTable[] = {
{kGetId, GetId},
{kRunningState, RunningState},
};
};
enum SensorType {
NTC0 = 0,
NTC1,
NTC2,
NTC3,
INFRARED,
};
std::bitset<5> bs;
auto SensorTriggerCallback(const device *dev, const sensor_trigger *trig)
-> void {
if (dev == sensor) {
bs.set(NTC0);
}
}
auto InitSensors() {
static const sensor_trigger tri{.chan = SENSOR_CHAN_AMBIENT_TEMP,
.type = SENSOR_TRIG_DATA_READY};
sensor_trigger_set(sensor, &tri, SensorTriggerCallback);
}
} // namespace
auto main(void) -> int {
gpio_pin_configure_dt(&led_g, GPIO_OUTPUT_ACTIVE);
k_timer_init(
&led_timer, [](k_timer *tim) { gpio_pin_toggle_dt(&led_g); },
[](k_timer *tim) {});
k_timer_start(&led_timer, K_NO_WAIT, K_SECONDS(1));
pmc.SetRxCallbackTable(Cb::kCbTable);
auto wdt = app::WatchDogConfig{}; auto wdt = app::WatchDogConfig{};
sensor_trigger tri{.type = SENSOR_TRIG_DATA_READY, sensor_trigger tri{.type = SENSOR_TRIG_DATA_READY,
.chan = SENSOR_CHAN_AMBIENT_TEMP}; .chan = SENSOR_CHAN_AMBIENT_TEMP};
@ -61,9 +99,16 @@ auto main(void) -> int {
sensor_channel_get(sensor, SENSOR_CHAN_AMBIENT_TEMP, &val); sensor_channel_get(sensor, SENSOR_CHAN_AMBIENT_TEMP, &val);
const uint8_t data[2] = {static_cast<uint8_t>(val.val1), const uint8_t data[2] = {static_cast<uint8_t>(val.val1),
static_cast<uint8_t>(val.val2)}; static_cast<uint8_t>(val.val2)};
pmc.Send("temp", data); pmc.Send(kTemp, data);
}
for (auto ntc: ntc_sensor) {
if (sensor_sample_fetch_chan(ntc, SENSOR_CHAN_AMBIENT_TEMP) == 0) {
sensor_channel_get(ntc, SENSOR_CHAN_AMBIENT_TEMP, &val);
const uint8_t data[2] = {static_cast<uint8_t>(val.val1),
static_cast<uint8_t>(val.val2)};
pmc.Send(kTemp, data);
}
} }
flag = false; flag = false;
} }
wdt.Feed(); wdt.Feed();

View File

@ -0,0 +1,18 @@
#include <zephyr/drivers/led.h>
#include <zephyr/kernel.h>
led_dt_spec status = LED_DT_SPEC_GET(DT_NODELABEL(status_led));
auto main() -> int {
printk("hello world %s\n", CONFIG_BOARD);
printk("hello world %s\n", CONFIG_BOARD);
printk("hello world %s\n", CONFIG_BOARD);
printk("hello world %s\n", CONFIG_BOARD);
while (true) {
printk("hello world\n");
led_on_dt(&status);
k_sleep(K_MSEC(1000));
led_off_dt(&status);
k_sleep(K_MSEC(1000));
}
return 0;
}

1
src/sample_ntc.cpp Normal file
View File

@ -0,0 +1 @@
auto main() -> int {}

View File

@ -0,0 +1,8 @@
common:
platform:
- native_sim/native/64
- nucleo_f429zi
tests:
sample.helloworld:
extra_configs:
- CONFIG_SAMPLE_HELLOWORLD=y

47
west.yml Normal file
View File

@ -0,0 +1,47 @@
manifest:
projects:
- import:
name-allowlist:
- cmsis_6
- hal_stm32
- hal_ti
path-prefix: extern
name: zephyr
path: zephyr
remote: zephyr
revision: v4.4.0
- name: etl
path: modules/etl
remote: robotstorm
revision: master
- name: uart_com
path: modules/uart_com
remote: robotstorm
revision: main
- name: zpp
path: modules/zpp
remote: robotstorm
revision: dev
- name: dr2501a_g070rb
path: boards/dr2501a_g070rb
remote: robotstorm
revision: main
- name: led_strip_indicator
path: modules/led_strip_indicator
remote: robotstorm
revision: main
- name: godtek_temp
path: modules/godtek_temp
remote: robotstorm
revision: main
- name: heading_pad
path: modules/heading_pad
revision: main
url: undefined
remotes:
- name: zephyr
url-base: https://gitcode.com/gh_mirrors/ze
- name: robotstorm
url-base: https://git.robotstorm.tech/EmbeddedTeam
self:
west-commands: scripts/west-commands.yml

160
zbuild.py Executable file
View File

@ -0,0 +1,160 @@
#!/usr/bin/env python3
"""
Zephyr build helper for app_i_core_link.
Usage:
python zbuild.py dr2501a_g070rb
python zbuild.py dr2501a_g070rb -p auto
python zbuild.py -p always (auto-detect board from west.yml)
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
Steps:
1. west topdir find Zephyr workspace root
2. west config --local manifest.file <this_script_dir>/west.yml
3. west build [-p PURSE] -b BOARD [-t TARGET] <this_script_dir>
"""
import argparse
import os
import re
import subprocess
import sys
def get_script_dir() -> str:
"""Absolute path to the directory containing this script."""
return os.path.dirname(os.path.abspath(__file__))
def run(cmd: list[str], cwd: str | None = None) -> str:
"""Run a command and return stdout. Exit on failure."""
proc = subprocess.run(cmd, capture_output=True, text=True, cwd=cwd)
if proc.returncode != 0:
print(f"{' '.join(cmd)}", file=sys.stderr)
print(f" {proc.stderr.strip()}", file=sys.stderr)
sys.exit(proc.returncode)
return proc.stdout.strip()
def discover_boards(manifest_path: str) -> list[str]:
"""Parse west.yml and return board names whose path starts with 'boards/'."""
if not os.path.exists(manifest_path):
return []
with open(manifest_path) as f:
content = f.read()
# Match entries like:
# - name: <board_name>
# path: boards/<something>
boards = re.findall(r"name:\s*(\S+)\s*\n\s+path:\s*boards/\S+", content)
return boards
def main():
parser = argparse.ArgumentParser(
description="Zephyr build helper for app_i_core_link"
)
parser.add_argument(
"board",
nargs="?",
default=None,
help="Board name (optional, auto-detected from west.yml if omitted)",
)
parser.add_argument(
"-p",
"--pristine",
default=None,
choices=["auto", "always", "never"],
help="Pristine build option",
)
parser.add_argument(
"-t",
"--target",
default=None,
help="CMake target to run after build (e.g. test, flash)",
)
parser.add_argument(
"--update",
action="store_true",
help="Run west update after setting manifest",
)
args = parser.parse_args()
# ── 0. Auto-detect board from west.yml when not specified ───
if args.board is None:
script_dir = get_script_dir()
manifest = os.path.join(script_dir, "west.yml")
boards = discover_boards(manifest)
if len(boards) == 0:
print(
" ✗ No board specified and no board found in west.yml "
"(no entry with path: boards/...)",
file=sys.stderr,
)
sys.exit(1)
elif len(boards) == 1:
args.board = boards[0]
print(f" → Auto-detected board: {args.board}")
else:
print(
" ✗ No board specified. Multiple boards found in west.yml:",
", ".join(boards),
"\n Please specify one explicitly.",
file=sys.stderr,
)
sys.exit(1)
script_dir = get_script_dir()
manifest = os.path.join(script_dir, "west.yml")
print(f" script dir : {script_dir}")
print(f" board : {args.board}")
print(f" pristine : {args.pristine or '(none)'}")
print(f" target : {args.target or '(none)'}")
print(f" update : {'yes' if args.update else 'no'}")
# ── 1. Find workspace root ───────────────────────────────
print("\n [1/3] Finding west workspace root...")
topdir = run(["west", "topdir"])
print(f"{topdir}")
# ── 2. Set local manifest ────────────────────────────────
print("\n [2/3] Setting local manifest...")
if not os.path.exists(manifest):
print(f" ✗ manifest not found: {manifest}", file=sys.stderr)
sys.exit(1)
run(["west", "config", "--local", "manifest.file", manifest], cwd=topdir)
print(f" → manifest.file = {manifest}")
# ── 2.5. Run west update (optional) ───────────────────────
if args.update:
print("\n [2.5/3] Running west update...")
run(["west", "update", "--fetch", "smart"], cwd=topdir)
print(" → update done")
# ── 3. Run west build ─────────────────────────────────────
print("\n [3/3] Running west build...")
build_cmd = ["west", "build"]
if args.pristine:
build_cmd += ["-p", args.pristine]
build_cmd += ["-b", args.board]
if args.target:
build_cmd += ["-t", args.target]
build_cmd += [script_dir]
print(f" $ {' '.join(build_cmd)}")
proc = subprocess.run(build_cmd, cwd=topdir)
if proc.returncode != 0:
print(f"\n ✗ Build failed (exit code {proc.returncode})")
sys.exit(proc.returncode)
print("\n ✓ Build succeeded")
if __name__ == "__main__":
main()