forked from EmbeddedTeam/app_photomagnetic
Refactor app to use static classes and event-driven callbacks
Restructure Led as a template on the DT spec, add a generic signal/slot mechanism for host status, and cache Infrared sensor samples via trigger callbacks. Add new init modules for LED, temp, and watchdog, and build them into the main app.
This commit is contained in:
parent
a9e8885ec8
commit
0e5d15c814
@ -15,5 +15,11 @@ elseif(CONFIG_SAMPLE_NTC)
|
|||||||
target_sources(app PRIVATE src/sample_ntc.cpp)
|
target_sources(app PRIVATE src/sample_ntc.cpp)
|
||||||
else()
|
else()
|
||||||
message("build main app")
|
message("build main app")
|
||||||
target_sources(app PRIVATE src/main.cpp)
|
|
||||||
|
target_sources(app PRIVATE
|
||||||
|
src/main.cpp
|
||||||
|
src/led.cpp
|
||||||
|
src/temp.cpp
|
||||||
|
src/watdog.cpp
|
||||||
|
)
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
106
include/com.hpp
106
include/com.hpp
@ -2,6 +2,8 @@
|
|||||||
#define __THER_COM_HPP__
|
#define __THER_COM_HPP__
|
||||||
|
|
||||||
#include "led.hpp"
|
#include "led.hpp"
|
||||||
|
#include <etl/delegate.h>
|
||||||
|
#include <etl/signal.h>
|
||||||
#include <led_strip_indicator/led_strip_indicator.hpp>
|
#include <led_strip_indicator/led_strip_indicator.hpp>
|
||||||
#include <uart_com/simple_protocal.hpp>
|
#include <uart_com/simple_protocal.hpp>
|
||||||
#include <zephyr/drivers/hwinfo.h>
|
#include <zephyr/drivers/hwinfo.h>
|
||||||
@ -12,96 +14,52 @@ namespace ther {
|
|||||||
|
|
||||||
class Com {
|
class Com {
|
||||||
public:
|
public:
|
||||||
|
enum HostStatus : uint8_t { STANDBY, RUNNING, PAUSE, ERROR };
|
||||||
|
using RunningStateSignal = etl::signal<void(HostStatus), 10>;
|
||||||
static auto Init() -> zpp::error {
|
static auto Init() -> zpp::error {
|
||||||
s_proto->SetRxCallbackTable(kRxCallbackTable);
|
s_proto->SetRxCallbackTable(kRxCallbackTable);
|
||||||
|
auto size = hwinfo_get_device_id(buff, sizeof(buff));
|
||||||
|
s_id_buff = etl::span<uint8_t>(buff, size);
|
||||||
return zpp::ok();
|
return zpp::ok();
|
||||||
}
|
}
|
||||||
static auto Send(sensor_value val) -> void {
|
static auto SendTemp(sensor_value val) -> void {
|
||||||
const uint8_t temp_data[] = {(uint8_t)val.val1, (uint8_t)val.val2};
|
const uint8_t temp_data[] = {(uint8_t)val.val1, (uint8_t)val.val2};
|
||||||
s_proto->Send(TEMP, temp_data);
|
s_proto->Send(R_TEMP, temp_data);
|
||||||
|
}
|
||||||
|
static auto AddRunningState(RunningStateSignal::slot_type slot) -> bool {
|
||||||
|
return s_running_state_sig.connect(slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum HostStatus : uint8_t { STANDBY, RUNNING, PAUSE, ERROR };
|
|
||||||
enum Addr : uint8_t {
|
enum Addr : uint8_t {
|
||||||
TEMP = 0,
|
R_TEMP = 0,
|
||||||
GET_ID,
|
R_GET_ID,
|
||||||
RUNNING_STATE,
|
W_RUNNING_STATE,
|
||||||
W_HEATING_STATE,
|
|
||||||
R_HEATING_STATE
|
|
||||||
};
|
};
|
||||||
enum DevAddr : uint8_t {
|
inline static RunningStateSignal s_running_state_sig{};
|
||||||
R_TEMP_POLE_NTC = 100,
|
static auto RunningState(uart_com::DataType data) -> void {
|
||||||
R_TEMP_HETING_PAD_NTC = 101,
|
if (data.size() != 1) {
|
||||||
};
|
return;
|
||||||
using CbTableValueType =
|
}
|
||||||
std::pair<const uint8_t, void (*)(uart_com::DataType)>;
|
s_running_state_sig(HostStatus(data[0]));
|
||||||
struct Cb {
|
}
|
||||||
static auto GetId(uart_com::DataType data) -> void {
|
static auto GetId(uart_com::DataType data) -> void {
|
||||||
printk("handle get id");
|
printk("handle get id");
|
||||||
uint8_t buff[20];
|
s_proto->Send(R_GET_ID, uart_com::DataType(s_id_buff));
|
||||||
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 {
|
|
||||||
if (data.size() != 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (data[0] == RUNNING) {
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
printk("set ledsrtip to %d", data[0]);
|
|
||||||
const uint8_t id = data[0];
|
|
||||||
s_led_strip_indicator->Status(id).on_error([](zpp::error_code err) {
|
|
||||||
printk("err code: %s", zpp::error_str(err));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
static auto WHeatingState(uart_com::DataType data) -> void {
|
|
||||||
// if (data.size() != 1) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// const bool state = (data[0] > 0);
|
|
||||||
// if (state) {
|
|
||||||
// HeatingPad::Start(sensor_value{data[0], 0});
|
|
||||||
// } else {
|
|
||||||
// HeatingPad::Stop();
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
static auto RHeatingState(uart_com::DataType data) -> void {
|
|
||||||
// if (data.size() != 0) {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// const sensor_value temp = HeatingPad::CurrentTemp();
|
|
||||||
// const uint8_t state[] = {s_heating_state, (uint8_t)temp.val1,
|
|
||||||
// (uint8_t)temp.val2};
|
|
||||||
// s_proto->Send(R_HEATING_STATE, state);
|
|
||||||
}
|
|
||||||
/// Read one pole NTC by index (0~7).
|
|
||||||
static auto ReadPoleNtcTemperature(uart_com::DataType data) -> void {
|
|
||||||
if (data.size() != 1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Read heating pad NTC (onboard ADC).
|
|
||||||
static auto ReadHeatingPadNtcTemperature(uart_com::DataType data) -> void {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr static std::pair<const uint8_t,
|
constexpr static std::pair<const uint8_t,
|
||||||
uart_com::SimpleProtocal::CallbackType>
|
uart_com::SimpleProtocal::CallbackType>
|
||||||
kRxCallbackTable[] = {
|
kRxCallbackTable[] = {
|
||||||
{GET_ID, Cb::GetId},
|
{R_GET_ID, GetId},
|
||||||
{RUNNING_STATE, Cb::RunningState},
|
{W_RUNNING_STATE, RunningState},
|
||||||
{W_HEATING_STATE, Cb::WHeatingState},
|
|
||||||
{R_HEATING_STATE, Cb::RHeatingState},
|
|
||||||
// Pole NTC (MCP3208)
|
|
||||||
{R_TEMP_POLE_NTC, Cb::ReadPoleNtcTemperature},
|
|
||||||
// Heating pad NTC (ADC1)
|
|
||||||
{R_TEMP_HETING_PAD_NTC, Cb::ReadHeatingPadNtcTemperature},
|
|
||||||
};
|
};
|
||||||
inline static auto s_led_strip_indicator =
|
inline static auto s_proto = (uart_com::SimpleProtocal *)DEVICE_DT_GET(
|
||||||
ZPP_DRV_GET_P(ledstrip::Indicator, DT_NODELABEL(indicator));
|
DT_COMPAT_GET_ANY_STATUS_OKAY(uart_com_simple_protocal));
|
||||||
inline static auto s_proto =
|
|
||||||
(uart_com::SimpleProtocal *)(DEVICE_DT_GET(DT_NODELABEL(pm_protocal)));
|
inline static uint8_t buff[20];
|
||||||
inline static auto s_heating_state = false;
|
inline static auto s_heating_state = false;
|
||||||
|
inline static etl::span<uint8_t> s_id_buff;
|
||||||
};
|
};
|
||||||
} // namespace ther
|
} // namespace ther
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
#ifndef __THER_INFRARED_HPP__
|
#ifndef __THER_INFRARED_HPP__
|
||||||
#define __THER_INFRARED_HPP__
|
#define __THER_INFRARED_HPP__
|
||||||
#include <etl/algorithm.h>
|
#include <etl/algorithm.h>
|
||||||
#include <etl/bitset.h>
|
#include <etl/array.h>
|
||||||
|
#include <etl/delegate.h>
|
||||||
#include <etl/tuple.h>
|
#include <etl/tuple.h>
|
||||||
|
#include <utility>
|
||||||
#include <zephyr/device.h>
|
#include <zephyr/device.h>
|
||||||
#include <zephyr/drivers/sensor.h>
|
#include <zephyr/drivers/sensor.h>
|
||||||
#include <zpp/result.hpp>
|
#include <zpp/result.hpp>
|
||||||
@ -13,42 +15,34 @@ namespace ther {
|
|||||||
class Infrared {
|
class Infrared {
|
||||||
public:
|
public:
|
||||||
static auto Init() -> zpp::error {
|
static auto Init() -> zpp::error {
|
||||||
static sensor_trigger tri{.type = SENSOR_TRIG_DATA_READY,
|
return InitImpl(std::make_index_sequence<s_dev.size()>{});
|
||||||
.chan = SENSOR_CHAN_AMBIENT_TEMP};
|
}
|
||||||
for (auto dev : s_dev) {
|
|
||||||
if (auto r = sensor_trigger_set(
|
/* Register a callback invoked every scan period (20 ms) with the
|
||||||
dev, &tri,
|
* hottest sensor value. Runs in the system workqueue thread context -
|
||||||
[](const device *dev, const sensor_trigger *trig) {
|
* keep the callback lightweight (no blocking, no SPI). */
|
||||||
const size_t id = etl::distance(
|
static auto
|
||||||
s_dev.begin(), etl::find(s_dev.begin(), s_dev.end(), dev));
|
AddCallbackWhenSensorValueReady(etl::delegate<void(sensor_value)> cb)
|
||||||
s_dev_ready[id] = 1;
|
-> zpp::error {
|
||||||
});
|
if (s_cb.is_valid()) {
|
||||||
r != 0) {
|
return zpp::error_code::k_busy;
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
s_cb = cb;
|
||||||
return zpp::ok();
|
return zpp::ok();
|
||||||
}
|
}
|
||||||
/* One-shot fetch of ALL sensors, return the hottest one. */
|
|
||||||
|
/* Hottest sensor among the latest cached samples (no polling: the
|
||||||
|
* trigger callback keeps the cache fresh). */
|
||||||
static auto GetMaxSensorValue() -> zpp::result<std::pair<int, sensor_value>> {
|
static auto GetMaxSensorValue() -> zpp::result<std::pair<int, sensor_value>> {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
int max_id = 0;
|
int max_id = 0;
|
||||||
sensor_value max_val{};
|
sensor_value max_val{};
|
||||||
|
|
||||||
for (size_t id = 0; id < s_dev.size(); ++id) {
|
for (size_t id = 0; id < s_dev.size(); ++id) {
|
||||||
/* -ENODATA just means no new sample for this channel; keep going. */
|
if (!s_latest_valid[id]) {
|
||||||
if (0 != sensor_sample_fetch(s_dev[id])) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const sensor_value val{s_latest_val1[id], s_latest_val2[id]};
|
||||||
sensor_value val;
|
|
||||||
if (0 != sensor_channel_get(s_dev[id], SENSOR_CHAN_AMBIENT_TEMP, &val)) {
|
|
||||||
s_dev_ready[id] = 0; /* sample consumed, drop the ready flag */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
s_dev_ready[id] = 0;
|
|
||||||
|
|
||||||
if (!found || val.val1 > max_val.val1 ||
|
if (!found || val.val1 > max_val.val1 ||
|
||||||
(val.val1 == max_val.val1 && val.val2 > max_val.val2)) {
|
(val.val1 == max_val.val1 && val.val2 > max_val.val2)) {
|
||||||
found = true;
|
found = true;
|
||||||
@ -63,11 +57,81 @@ public:
|
|||||||
return std::make_pair(max_id, max_val);
|
return std::make_pair(max_id, max_val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Snapshot of all cached sensor values (pure memory reads, safe in
|
||||||
|
* trigger context). Channels without a sample yet read as 0.
|
||||||
|
* auto return: the body is parsed in complete-class context where the
|
||||||
|
* trailing members (s_dev, caches) are visible. */
|
||||||
|
static auto GetAllSensorValues() {
|
||||||
|
etl::array<sensor_value, s_dev.size()> out{};
|
||||||
|
for (size_t id = 0; id < s_dev.size(); ++id) {
|
||||||
|
if (s_latest_valid[id]) {
|
||||||
|
out[id] = sensor_value{s_latest_val1[id], s_latest_val2[id]};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
/* Per-sensor trigger handler; the channel index is a compile-time
|
||||||
|
* constant. Runs in the UART trigger context (workqueue thread for
|
||||||
|
* CH9438 ports, ISR context for on-chip UARTs) - keep it lock-free. */
|
||||||
|
template <size_t I>
|
||||||
|
static void OnSensorDataReady(const struct device *dev,
|
||||||
|
const struct sensor_trigger *trig) {
|
||||||
|
sensor_value val;
|
||||||
|
if (0 != sensor_channel_get(dev, SENSOR_CHAN_AMBIENT_TEMP, &val)) {
|
||||||
|
printk("[infra] ch%u channel_get failed\n", (unsigned)I);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
s_latest_val1[I] = val.val1;
|
||||||
|
s_latest_val2[I] = val.val2;
|
||||||
|
s_latest_valid[I] = true;
|
||||||
|
if (s_cb.is_valid()) {
|
||||||
|
s_cb(MaxCachedValue()); /* notify with the hottest sensor value */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Latest cached max value; pure memory reads, safe in trigger context. */
|
||||||
|
static auto MaxCachedValue() -> sensor_value {
|
||||||
|
bool found = false;
|
||||||
|
sensor_value max_val{};
|
||||||
|
for (size_t id = 0; id < s_dev.size(); ++id) {
|
||||||
|
if (!s_latest_valid[id]) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const sensor_value val{s_latest_val1[id], s_latest_val2[id]};
|
||||||
|
if (!found || val.val1 > max_val.val1 ||
|
||||||
|
(val.val1 == max_val.val1 && val.val2 > max_val.val2)) {
|
||||||
|
found = true;
|
||||||
|
max_val = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <size_t... Is>
|
||||||
|
static auto InitImpl(std::index_sequence<Is...>) -> zpp::error {
|
||||||
|
static sensor_trigger tri{.type = SENSOR_TRIG_DATA_READY,
|
||||||
|
.chan = SENSOR_CHAN_AMBIENT_TEMP};
|
||||||
|
int ret = 0;
|
||||||
|
((printk("[infra] ch%u ready=%d\n", (unsigned)Is,
|
||||||
|
device_is_ready(s_dev[Is])),
|
||||||
|
ret |= sensor_trigger_set(s_dev[Is], &tri, &OnSensorDataReady<Is>)),
|
||||||
|
...);
|
||||||
|
return ret != 0 ? zpp::error{-ENODEV} : zpp::ok();
|
||||||
|
}
|
||||||
|
|
||||||
#define INFRARED_DEV(node) DEVICE_DT_GET(node),
|
#define INFRARED_DEV(node) DEVICE_DT_GET(node),
|
||||||
inline static etl::array s_dev{
|
inline static etl::array s_dev{
|
||||||
DT_FOREACH_STATUS_OKAY(godtek_temp_uart, INFRARED_DEV)};
|
DT_FOREACH_STATUS_OKAY(godtek_temp_uart, INFRARED_DEV)};
|
||||||
inline static etl::bitset<s_dev.size()> s_dev_ready;
|
/* Field-level volatile caches: written by the trigger handlers, read by
|
||||||
|
* GetMaxSensorValue. sensor_value itself cannot be volatile (no volatile
|
||||||
|
* copy/assign), so the two 32-bit fields are cached separately. A torn
|
||||||
|
* cross-field read is possible but negligible for slowly changing temps. */
|
||||||
|
inline static volatile int32_t s_latest_val1[s_dev.size()]{};
|
||||||
|
inline static volatile int32_t s_latest_val2[s_dev.size()]{};
|
||||||
|
inline static volatile bool s_latest_valid[s_dev.size()]{};
|
||||||
|
inline static etl::delegate<void(sensor_value)> s_cb;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ther
|
} // namespace ther
|
||||||
|
|||||||
@ -7,89 +7,26 @@
|
|||||||
#include <zpp/work_queue.hpp>
|
#include <zpp/work_queue.hpp>
|
||||||
namespace ther {
|
namespace ther {
|
||||||
|
|
||||||
/* Compile-time check: every LED name must be distinct so that
|
template <led_dt_spec kSpec> class Led {
|
||||||
* Led::On/Off/Flash<kName> name lookup stays unambiguous. */
|
|
||||||
template <typename T, std::size_t N>
|
|
||||||
consteval auto AreUniqueNames(const etl::array<T, N> &names) -> bool {
|
|
||||||
for (std::size_t i = 0; i < N; ++i) {
|
|
||||||
for (std::size_t j = i + 1; j < N; ++j) {
|
|
||||||
if (names[i] == names[j]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LED_SPEC(node) led_dt_spec LED_DT_SPEC_GET(node)
|
|
||||||
#define LED_DEV_CHILD_SPEC(node) DT_FOREACH_CHILD_SEP(node, LED_SPEC, (, )),
|
|
||||||
#define LED_NODE_NAME(node) DT_FOREACH_CHILD_SEP(node, DEVICE_DT_NAME, (, ))
|
|
||||||
class Led {
|
|
||||||
|
|
||||||
private:
|
|
||||||
template <led_dt_spec> struct Periodic {
|
|
||||||
static auto Flash(led_dt_spec *spec) -> void {
|
|
||||||
bool flag = false;
|
|
||||||
if (flag) {
|
|
||||||
led_on_dt(spec);
|
|
||||||
} else {
|
|
||||||
led_off_dt(spec);
|
|
||||||
}
|
|
||||||
flag = !flag;
|
|
||||||
}
|
|
||||||
static inline zpp::periodic_work<led_dt_spec *> work{Flash};
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <zpp::ct_string kName> static auto On() -> zpp::error {
|
static auto On() -> zpp::error { return led_on_dt(&kSpec); }
|
||||||
static_assert(IsDeviceName<kName>());
|
static auto Off() -> zpp::error { return led_off_dt(&kSpec); }
|
||||||
const auto id = etl::find(s_led_node_name.begin(), s_led_node_name.end(),
|
template <typename TRep, typename TPeriod>
|
||||||
std::string_view(kName)) -
|
|
||||||
s_led_node_name.begin();
|
|
||||||
return led_on_dt(&s_led_dev[id]);
|
|
||||||
}
|
|
||||||
template <zpp::ct_string kName> static auto Off() -> zpp::error {
|
|
||||||
static_assert(IsDeviceName<kName>());
|
|
||||||
const auto id = etl::find(s_led_node_name.begin(), s_led_node_name.end(),
|
|
||||||
std::string_view(kName)) -
|
|
||||||
s_led_node_name.begin();
|
|
||||||
return led_off_dt(&s_led_dev[id]);
|
|
||||||
}
|
|
||||||
template <zpp::ct_string kName, typename TRep, typename TPeriod>
|
|
||||||
static auto Flash(std::chrono::duration<TRep, TPeriod> period) {
|
static auto Flash(std::chrono::duration<TRep, TPeriod> period) {
|
||||||
static_assert(IsDeviceName<kName>());
|
s_work.submit(period);
|
||||||
const auto id = etl::find(s_led_node_name.begin(), s_led_node_name.end(),
|
|
||||||
std::string_view(kName)) -
|
|
||||||
s_led_node_name.begin();
|
|
||||||
Periodic<s_led_dev[id]>::work.submit(period);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
template <zpp::ct_string kName> static consteval auto IsDeviceName() {
|
static auto Flash() -> void {
|
||||||
for (size_t i = 0; i < s_led_node_name.size(); ++i) {
|
|
||||||
if (s_led_node_name[i] == std::string_view(kName)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static auto Flash(led_dt_spec *spec) -> void {
|
|
||||||
static bool flag = false;
|
static bool flag = false;
|
||||||
if (flag) {
|
if (flag) {
|
||||||
led_off_dt(spec);
|
led_off_dt(&kSpec);
|
||||||
} else {
|
} else {
|
||||||
led_on_dt(spec);
|
led_on_dt(&kSpec);
|
||||||
}
|
}
|
||||||
flag = !flag;
|
flag = !flag;
|
||||||
}
|
}
|
||||||
inline static constexpr etl::array s_led_dev{
|
static inline zpp::periodic_work<> s_work{Flash};
|
||||||
DT_FOREACH_STATUS_OKAY(gpio_leds, LED_DEV_CHILD_SPEC)};
|
|
||||||
inline static constexpr auto s_led_node_name =
|
|
||||||
etl::make_array<std::string_view>(
|
|
||||||
DT_FOREACH_STATUS_OKAY(gpio_leds, LED_NODE_NAME));
|
|
||||||
static_assert(AreUniqueNames(s_led_node_name),
|
|
||||||
"LED node names must be unique");
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ther
|
} // namespace ther
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
#include <zephyr/drivers/watchdog.h>
|
#include <zephyr/drivers/watchdog.h>
|
||||||
#include <zpp/assert.hpp>
|
#include <zpp/assert.hpp>
|
||||||
#include <zpp/timer.hpp>
|
#include <zpp/timer.hpp>
|
||||||
namespace app {
|
namespace ther {
|
||||||
class WatchDogConfig {
|
class WatchDogConfig {
|
||||||
public:
|
public:
|
||||||
WatchDogConfig() {
|
WatchDogConfig() {
|
||||||
@ -28,6 +28,6 @@ private:
|
|||||||
int chan_id;
|
int chan_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace app
|
} // namespace ther
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
6
prj.conf
6
prj.conf
@ -8,9 +8,15 @@ CONFIG_SERIAL=y
|
|||||||
CONFIG_UART_INTERRUPT_DRIVEN=y
|
CONFIG_UART_INTERRUPT_DRIVEN=y
|
||||||
CONFIG_HWINFO=y
|
CONFIG_HWINFO=y
|
||||||
|
|
||||||
|
|
||||||
|
CONFIG_LED=y
|
||||||
|
|
||||||
|
# CONFIG_PRINTK=n
|
||||||
CONFIG_ADC=y
|
CONFIG_ADC=y
|
||||||
CONFIG_SPI=y
|
CONFIG_SPI=y
|
||||||
CONFIG_SPI_STM32=y
|
CONFIG_SPI_STM32=y
|
||||||
|
# SPI 中断模式:传输带 1s 超时,多路高频 SPI 下不会永久挂死(与 ch9438 测试对齐)
|
||||||
|
CONFIG_SPI_STM32_INTERRUPT=y
|
||||||
CONFIG_SENSOR=y
|
CONFIG_SENSOR=y
|
||||||
CONFIG_ADC_MCP320X_ACQUISITION_THREAD_STACK_SIZE=2048
|
CONFIG_ADC_MCP320X_ACQUISITION_THREAD_STACK_SIZE=2048
|
||||||
CONFIG_REBOOT=y
|
CONFIG_REBOOT=y
|
||||||
|
|||||||
29
src/led.cpp
Normal file
29
src/led.cpp
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#include <com.hpp>
|
||||||
|
#include <etl/utility.h>
|
||||||
|
#include <led.hpp>
|
||||||
|
#include <led_strip_indicator/led_strip_indicator.hpp>
|
||||||
|
#include <zephyr/init.h>
|
||||||
|
using namespace ther;
|
||||||
|
namespace {
|
||||||
|
using McuState = Led<LED_DT_SPEC_GET(DT_NODELABEL(led_mcu_state))>;
|
||||||
|
using Inf = Led<LED_DT_SPEC_GET(DT_NODELABEL(led_inf))>;
|
||||||
|
auto indicator = ZPP_DRV_GET_P(ledstrip::Indicator, DT_NODELABEL(indicator));
|
||||||
|
constexpr const char *MODULE = "app_led";
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
static auto Init() -> int {
|
||||||
|
Com::AddRunningState([](Com::HostStatus state) {
|
||||||
|
indicator->Status(static_cast<uint8_t>(state));
|
||||||
|
if (state == Com::HostStatus::RUNNING) {
|
||||||
|
Inf::On();
|
||||||
|
} else {
|
||||||
|
Inf::Off();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
McuState::Flash(1s);
|
||||||
|
printk("[%s] Init: MCU state LED flashing at 1s interval\n", MODULE);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYS_INIT(Init, APPLICATION, 50);
|
||||||
@ -1,10 +1,11 @@
|
|||||||
|
|
||||||
|
#include <com.hpp>
|
||||||
#include <zephyr/kernel.h>
|
#include <zephyr/kernel.h>
|
||||||
|
|
||||||
auto main(void) -> int {
|
auto main(void) -> int {
|
||||||
|
ther::Com::Init();
|
||||||
|
printk("[main] init on %s\n", CONFIG_BOARD);
|
||||||
while (1) {
|
while (1) {
|
||||||
|
k_sleep(K_SECONDS(1));
|
||||||
k_sleep(K_MSEC(20));
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
69
src/temp.cpp
Normal file
69
src/temp.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
#include <com.hpp>
|
||||||
|
#include <etl/utility.h>
|
||||||
|
#include <infrared.hpp>
|
||||||
|
#include <zephyr/init.h>
|
||||||
|
#include <zephyr/kernel.h>
|
||||||
|
|
||||||
|
using namespace ther;
|
||||||
|
|
||||||
|
/* 调试打印开关:
|
||||||
|
* 1 = 每次触发打印全部 9 路 + 最大值
|
||||||
|
* 0 / 注释 = 只打印最大值(生产模式)
|
||||||
|
*/
|
||||||
|
#ifndef APP_TEMP_PRINT_ALL_CHANNELS
|
||||||
|
#define APP_TEMP_PRINT_ALL_CHANNELS 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
using namespace std::string_literals;
|
||||||
|
constexpr const char *MODULE = "app_temp";
|
||||||
|
constexpr auto kPeriodSend = std::chrono::milliseconds(20);
|
||||||
|
|
||||||
|
/* 触发回调:只做调试打印(采集/缓存由 Infrared 内部完成),
|
||||||
|
* 发送由 20ms 周期的 OnSendTick 负责。 */
|
||||||
|
constexpr auto OnSensorValueReady =
|
||||||
|
etl::delegate<void(sensor_value)>::create(+[](sensor_value val) {
|
||||||
|
#if APP_TEMP_PRINT_ALL_CHANNELS
|
||||||
|
const auto all = Infrared::GetAllSensorValues();
|
||||||
|
for (size_t i = 0; i < all.size(); ++i) {
|
||||||
|
printk("[%s]ch%u: %d.%d\n", MODULE, (unsigned)i, all[i].val1,
|
||||||
|
all[i].val2);
|
||||||
|
}
|
||||||
|
printk("[%s]max: %d.%d (%lld ms)\n", MODULE, val.val1, val.val2,
|
||||||
|
(long long)k_uptime_get());
|
||||||
|
#endif
|
||||||
|
});
|
||||||
|
|
||||||
|
static void OnSendTick(struct k_work *work);
|
||||||
|
/* 每 kPeriodSend(20ms) 发送一次最新缓存的最大值。 */
|
||||||
|
static K_WORK_DELAYABLE_DEFINE(s_send_work, OnSendTick);
|
||||||
|
|
||||||
|
static void OnSendTick(struct k_work *work) {
|
||||||
|
if (auto r = Infrared::GetMaxSensorValue(); r.ok()) {
|
||||||
|
Com::SendTemp(r.value().second);
|
||||||
|
}
|
||||||
|
k_work_schedule(&s_send_work, K_MSEC(kPeriodSend.count()));
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
static auto Init() -> int {
|
||||||
|
printk("[%s]temp init\n", MODULE);
|
||||||
|
if (auto r = Infrared::Init(); r.code_id() != zpp::error_code::k_ok) {
|
||||||
|
printk("[%s] Infrared::Init failed: %d\n", MODULE,
|
||||||
|
static_cast<int>(r.code_id()));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (auto r = Infrared::AddCallbackWhenSensorValueReady(OnSensorValueReady);
|
||||||
|
r.code_id() != zpp::error_code::k_ok) {
|
||||||
|
printk("[%s] AddCallback failed: %d\n", MODULE,
|
||||||
|
static_cast<int>(r.code_id()));
|
||||||
|
}
|
||||||
|
/* 启动 20ms 周期发送 */
|
||||||
|
k_work_schedule(&s_send_work, K_MSEC(kPeriodSend.count()));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Register AFTER the godtek/ch9438 drivers (POST_KERNEL): registering the
|
||||||
|
* sensor trigger earlier gets wiped by the sensor driver's own Init,
|
||||||
|
* which resets data_ready_handler to nullptr. */
|
||||||
|
SYS_INIT(Init, APPLICATION, 50);
|
||||||
16
src/watdog.cpp
Normal file
16
src/watdog.cpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include <watchdog.hpp>
|
||||||
|
#include <zpp/work_queue.hpp>
|
||||||
|
using namespace ther;
|
||||||
|
namespace {
|
||||||
|
WatchDogConfig wdt;
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
static auto Init() -> int {
|
||||||
|
printk("[watchdog] init\n");
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
static zpp::periodic_work<> work{[]() { wdt.Feed(); }};
|
||||||
|
work.submit(50ms);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SYS_INIT(Init, POST_KERNEL, 50);
|
||||||
Loading…
x
Reference in New Issue
Block a user