tin  1.5.9
io.h
Go to the documentation of this file.
1 // Copyright (c) 2026 Tinverse LLC. All rights reserved.
2 // SPDX-License-Identifier: LicenseRef-Tinverse-Commercial
3 
11 
12 #pragma once
13 
14 #include <concepts>
15 #include <cstddef>
16 #include <cstdint>
17 #include <span>
18 #include <type_traits>
19 #include <utility>
20 
21 #include "tsm/ticks.h"
22 
23 namespace tsm::io {
24 
25 enum class level : unsigned char
26 {
27  low,
28  high
29 };
30 
31 enum class bus_status : unsigned char
32 {
33  ok,
34  busy,
35  error,
36  timeout
37 };
38 
40 {
42 };
43 
44 struct adc_sample
45 {
46  std::uint16_t millivolts{};
47 };
48 
49 struct pwm_duty
50 {
51  std::uint16_t permille{};
52 };
53 
54 struct can_id
55 {
56  std::uint32_t value{};
57  bool extended{};
58 };
59 
60 struct can_frame
61 {
62  can_id id{};
63  std::span<std::byte const> payload{};
64 };
65 
66 template<typename Awaitable>
67 concept awaitable = requires(Awaitable awaitable)
68 {
69  {
70  awaitable.await_ready()
71  } -> std::convertible_to<bool>;
72  awaitable.await_resume();
73 };
74 
75 template<typename Pin>
76 concept gpio_input = requires(Pin pin)
77 {
78  {
79  pin.read()
80  } -> std::same_as<level>;
81 };
82 
83 template<typename Pin>
84 concept gpio_output = requires(Pin pin, level value)
85 {
86  {
87  pin.write(value)
88  } -> std::convertible_to<bus_status>;
89 };
90 
91 template<typename Pin>
92 concept input_pin = gpio_input<Pin>;
93 
94 template<typename Pin>
95 concept output_pin = gpio_output<Pin>;
96 
97 template<typename Pin>
98 concept async_gpio_input = gpio_input<Pin> && requires(Pin pin, level value)
99 {
100  {
101  pin.wait_for(value)
102  } -> awaitable;
103 };
104 
105 template<typename Converter>
106 concept adc = requires(Converter adc)
107 {
108  {
109  adc.read()
110  } -> std::same_as<adc_sample>;
111 };
112 
113 template<typename Converter>
114 concept async_adc = adc<Converter> && requires(Converter adc)
115 {
116  {
117  adc.read_async()
118  } -> awaitable;
119 };
120 
121 template<typename Bus>
122 concept uart = requires(Bus bus,
123  std::span<std::byte> rx,
124  std::span<std::byte const> tx)
125 {
126  {
127  bus.read(rx)
128  } -> std::convertible_to<bus_status>;
129  {
130  bus.write(tx)
131  } -> std::convertible_to<bus_status>;
132 };
133 
134 template<typename Bus>
135 concept async_uart = uart<Bus> &&
136  requires(Bus bus, std::span<std::byte> rx, std::span<std::byte const> tx)
137 {
138  {
139  bus.read_async(rx)
140  } -> awaitable;
141  {
142  bus.write_async(tx)
143  } -> awaitable;
144 };
145 
146 template<typename Bus>
147 concept spi = requires(Bus bus,
148  std::span<std::byte const> tx,
149  std::span<std::byte> rx)
150 {
151  {
152  bus.transfer(tx, rx)
153  } -> std::convertible_to<bus_status>;
154 };
155 
156 template<typename Bus>
157 concept async_spi = spi<Bus> &&
158  requires(Bus bus, std::span<std::byte const> tx, std::span<std::byte> rx)
159 {
160  {
161  bus.transfer_async(tx, rx)
162  } -> awaitable;
163 };
164 
165 template<typename Bus>
166 concept i2c = requires(Bus bus,
167  std::uint16_t address,
168  std::span<std::byte const> tx,
169  std::span<std::byte> rx)
170 {
171  {
172  bus.write(address, tx)
173  } -> std::convertible_to<bus_status>;
174  {
175  bus.read(address, rx)
176  } -> std::convertible_to<bus_status>;
177 };
178 
179 template<typename Bus>
180 concept async_i2c = i2c<Bus> && requires(Bus bus,
181  std::uint16_t address,
182  std::span<std::byte const> tx,
183  std::span<std::byte> rx)
184 {
185  {
186  bus.write_async(address, tx)
187  } -> awaitable;
188  {
189  bus.read_async(address, rx)
190  } -> awaitable;
191 };
192 
193 template<typename Output>
194 concept pwm = requires(Output output, pwm_duty duty)
195 {
196  {
197  output.set_duty(duty)
198  } -> std::convertible_to<bus_status>;
199  {
200  output.enable()
201  } -> std::convertible_to<bus_status>;
202  {
203  output.disable()
204  } -> std::convertible_to<bus_status>;
205 };
206 
207 template<typename Bus>
208 concept can = requires(Bus bus, can_frame frame, std::span<std::byte> rx)
209 {
210  {
211  bus.send(frame)
212  } -> std::convertible_to<bus_status>;
213  {
214  bus.receive(rx)
215  } -> std::convertible_to<bus_status>;
216 };
217 
218 template<typename Bus>
219 concept async_can = can<Bus> &&
220  requires(Bus bus, can_frame frame, std::span<std::byte> rx)
221 {
222  {
223  bus.send_async(frame)
224  } -> awaitable;
225  {
226  bus.receive_async(rx)
227  } -> awaitable;
228 };
229 
230 // clang-format off
231 template<typename Timer>
232 concept tick_source_rep = requires(Timer timer) {
233  { timer.ticks() } -> std::convertible_to<tsm::tick_rep>;
234 };
235 
236 template<typename Timer>
237 concept tick_source_duration = requires(Timer timer) {
238  { timer.ticks() } -> std::convertible_to<tsm::tick_duration>;
239 };
240 // clang-format on
241 
242 template<typename Timer>
243 concept tick_source = tick_source_rep<Timer> || tick_source_duration<Timer>;
244 
245 template<typename Sink, typename Payload>
246 concept isr_payload_sink = requires(Sink sink, Payload payload)
247 {
248  {
249  sink.try_send_from_isr(std::forward<Payload>(payload))
250  } -> std::convertible_to<bool>;
251 };
252 
253 template<typename Source>
254 concept isr_notifier = requires(Source source)
255 {
256  source.notify_from_isr();
257 };
258 
259 template<typename Sink, typename Payload>
260 requires isr_payload_sink<Sink&, Payload>
261 [[nodiscard]] bool
262 push_from_isr(Sink& sink, Payload&& payload)
263 {
264  return sink.try_send_from_isr(std::forward<Payload>(payload));
265 }
266 
267 template<adc Converter, typename Sink>
268 requires isr_payload_sink<Sink&, adc_sample>
269 [[nodiscard]] bool
270 sample_adc_from_isr(Converter& converter, Sink& sink)
271 {
272  return push_from_isr(sink, converter.read());
273 }
274 
275 template<gpio_input Pin, typename Sink>
276 requires isr_payload_sink<Sink&, digital_input_state>
277 [[nodiscard]] bool
278 sample_gpio_from_isr(Pin& pin, Sink& sink)
279 {
280  return push_from_isr(sink, digital_input_state{ pin.read() });
281 }
282 
283 template<isr_notifier Source>
284 void
285 notify_from_isr(Source& source)
286 {
287  source.notify_from_isr();
288 }
289 
290 } // namespace tsm::io
requires(!has_transition_type_c< T > &&has_transition_member_c< T >) struct transitions_of< T >
Definition: transition.h:479
Definition: io.h:23
concept async_spi
Definition: io.h:157
requires isr_payload_sink< Sink &, digital_input_state > bool sample_gpio_from_isr(Pin &pin, Sink &sink)
Definition: io.h:278
concept output_pin
Definition: io.h:95
concept tick_source_rep
Definition: io.h:232
concept isr_payload_sink
Definition: io.h:246
concept pwm
Definition: io.h:194
concept async_i2c
Definition: io.h:180
concept gpio_input
Definition: io.h:76
concept tick_source_duration
Definition: io.h:237
concept gpio_output
Definition: io.h:84
concept adc
Definition: io.h:106
concept tick_source
Definition: io.h:243
concept i2c
Definition: io.h:166
concept uart
Definition: io.h:122
requires isr_payload_sink< Sink &, Payload > bool push_from_isr(Sink &sink, Payload &&payload)
Definition: io.h:262
concept spi
Definition: io.h:147
requires isr_payload_sink< Sink &, adc_sample > bool sample_adc_from_isr(Converter &converter, Sink &sink)
Definition: io.h:270
concept async_adc
Definition: io.h:114
level
Definition: io.h:26
concept async_uart
Definition: io.h:135
concept isr_notifier
Definition: io.h:254
bus_status
Definition: io.h:32
concept input_pin
Definition: io.h:92
void notify_from_isr(Source &source)
Definition: io.h:285
concept awaitable
Definition: io.h:67
concept can
Definition: io.h:208
concept async_can
Definition: io.h:219
concept async_gpio_input
Definition: io.h:98
Definition: io.h:45
std::uint16_t millivolts
Definition: io.h:46
Definition: io.h:61
std::span< std::byte const > payload
Definition: io.h:63
Definition: io.h:55
bool extended
Definition: io.h:57
std::uint32_t value
Definition: io.h:56
Definition: io.h:40
level value
Definition: io.h:41
Definition: io.h:50
std::uint16_t permille
Definition: io.h:51
Target-neutral tick value type.