libxr  1.0
Want to be the best embedded framework
Loading...
Searching...
No Matches
linux_gpio.hpp
1#pragma once
2
3#include <gpiod.h>
4
5#include <cerrno>
6#include <cstddef>
7#include <cstring>
8#include <limits>
9#include <string>
10
11#include "gpio.hpp"
12#include "logger.hpp"
13
14namespace LibXR
15{
16
21enum class GPIOEventType : uint8_t
22{
25};
26
32{
33 int64_t timestamp;
35};
36
42class LinuxGPIO : public GPIO
43{
44 public:
45 static constexpr size_t EVENT_BUFFER_CAPACITY = 64;
46
53 LinuxGPIO(const std::string& chip_path, unsigned int line_offset)
54 : chip_path_(chip_path),
55 line_offset_(line_offset),
56 chip_(gpiod_chip_open(chip_path.c_str()))
57 {
58 if (!chip_)
59 {
60 XR_LOG_ERROR("Failed to open GPIO chip: %s", chip_path.c_str());
61 ASSERT(false);
62 return;
63 }
64
65 settings_ = gpiod_line_settings_new();
66 if (!settings_)
67 {
68 XR_LOG_ERROR("Failed to create GPIO line settings");
69 ASSERT(false);
70 return;
71 }
72
73 line_cfg_ = gpiod_line_config_new();
74 if (!line_cfg_)
75 {
76 XR_LOG_ERROR("Failed to create GPIO line config");
77 ASSERT(false);
78 return;
79 }
80
81 req_cfg_ = gpiod_request_config_new();
82 if (!req_cfg_)
83 {
84 XR_LOG_ERROR("Failed to create GPIO request config");
85 ASSERT(false);
86 return;
87 }
88
89 gpiod_request_config_set_consumer(req_cfg_, "LinuxGPIO");
90 gpiod_request_config_set_event_buffer_size(req_cfg_, EVENT_BUFFER_CAPACITY);
91
92 event_buffer_ = gpiod_edge_event_buffer_new(EVENT_BUFFER_CAPACITY);
93 if (!event_buffer_)
94 {
95 XR_LOG_ERROR("Failed to allocate GPIO edge event buffer");
96 ASSERT(false);
97 return;
98 }
99 }
100
101 LinuxGPIO(const LinuxGPIO&) = delete;
102 LinuxGPIO& operator=(const LinuxGPIO&) = delete;
103
108 bool Read() override
109 {
110 if (!EnsureConfigured())
111 {
112 return false;
113 }
114
115 enum gpiod_line_value value = gpiod_line_request_get_value(request_, line_offset_);
116 if (value == GPIOD_LINE_VALUE_ERROR)
117 {
118 XR_LOG_ERROR("Failed to read GPIO value: %s", std::strerror(errno));
119 return false;
120 }
121
122 return value == GPIOD_LINE_VALUE_ACTIVE;
123 }
124
129 void Write(bool value) override
130 {
131 if (!EnsureConfigured())
132 {
133 return;
134 }
135
136 enum gpiod_line_value line_value =
137 value ? GPIOD_LINE_VALUE_ACTIVE : GPIOD_LINE_VALUE_INACTIVE;
138
139 if (gpiod_line_request_set_value(request_, line_offset_, line_value) < 0)
140 {
141 XR_LOG_WARN("Failed to write GPIO value: %s", std::strerror(errno));
142 }
143 }
144
149 ErrorCode EnableInterrupt() override
150 {
151 if (!has_config_ || !request_)
152 {
153 return ErrorCode::STATE_ERR;
154 }
155
156 if (!IsInterruptDirection(current_config_.direction))
157 {
158 return ErrorCode::ARG_ERR;
159 }
160
161 interrupt_enabled_ = true;
162 return ErrorCode::OK;
163 }
164
169 ErrorCode DisableInterrupt() override
170 {
171 interrupt_enabled_ = false;
172 return ErrorCode::OK;
173 }
174
180 int GetFd() const
181 {
182 if (EnsureInterruptReady() != ErrorCode::OK)
183 {
184 return -1;
185 }
186
187 return gpiod_line_request_get_fd(request_);
188 }
189
198 ErrorCode HandleInterrupt()
199 {
200 const ErrorCode READY = EnsureInterruptReady();
201 if (READY != ErrorCode::OK)
202 {
203 return READY;
204 }
205
206 bool handled = false;
207 while (true)
208 {
209 int ready = gpiod_line_request_wait_edge_events(request_, 0);
210 if (ready < 0)
211 {
212 XR_LOG_ERROR("Failed to poll GPIO edge events: %s", std::strerror(errno));
213 return ErrorCode::FAILED;
214 }
215
216 if (ready == 0)
217 {
218 break;
219 }
220
221 int read = gpiod_line_request_read_edge_events(request_, event_buffer_,
222 EVENT_BUFFER_CAPACITY);
223 if (read < 0)
224 {
225 XR_LOG_ERROR("Failed to read GPIO edge events: %s", std::strerror(errno));
226 return ErrorCode::FAILED;
227 }
228
229 if (read == 0)
230 {
231 break;
232 }
233
234 handled = true;
235 if (!callback_.Empty())
236 {
237 for (int i = 0; i < read; ++i)
238 {
239 callback_.Run(false);
240 }
241 }
242 }
243
244 return handled ? ErrorCode::OK : ErrorCode::EMPTY;
245 }
246
252 ErrorCode ReadEvent(GPIOEvent& event)
253 {
254 const ErrorCode READY_STATUS = EnsureInterruptReady();
255 if (READY_STATUS != ErrorCode::OK)
256 {
257 return READY_STATUS;
258 }
259
260 int ready = gpiod_line_request_wait_edge_events(request_, 0);
261 if (ready < 0)
262 {
263 XR_LOG_ERROR("Failed to poll GPIO edge events: %s", std::strerror(errno));
264 return ErrorCode::FAILED;
265 }
266
267 if (ready == 0)
268 {
269 return ErrorCode::EMPTY;
270 }
271
272 int ret = gpiod_line_request_read_edge_events(request_, event_buffer_, 1);
273
274 if (ret < 0)
275 {
276 XR_LOG_ERROR("Failed to read GPIO edge event: %s", std::strerror(errno));
277 return ErrorCode::FAILED;
278 }
279
280 if (ret == 0)
281 {
282 return ErrorCode::EMPTY;
283 }
284
285 struct gpiod_edge_event* edge_event =
286 gpiod_edge_event_buffer_get_event(event_buffer_, ret - 1);
287 if (!edge_event)
288 {
289 XR_LOG_ERROR("Failed to access GPIO edge event from buffer");
290 return ErrorCode::FAILED;
291 }
292
293 const uint64_t TIMESTAMP_NS = gpiod_edge_event_get_timestamp_ns(edge_event);
294 if (TIMESTAMP_NS > static_cast<uint64_t>(std::numeric_limits<int64_t>::max()))
295 {
296 XR_LOG_ERROR("GPIO edge event timestamp out of int64 range");
297 return ErrorCode::OUT_OF_RANGE;
298 }
299
300 event.timestamp = static_cast<int64_t>(TIMESTAMP_NS);
301 event.type =
302 (gpiod_edge_event_get_event_type(edge_event) == GPIOD_EDGE_EVENT_RISING_EDGE)
305
306 return ErrorCode::OK;
307 }
308
314 ErrorCode SetConfig(Configuration config) override
315 {
316 if (!settings_ || !line_cfg_ || !req_cfg_ || !chip_)
317 {
318 return ErrorCode::INIT_ERR;
319 }
320
321 interrupt_enabled_ = false;
322
323 gpiod_line_settings_reset(settings_);
324 gpiod_line_config_reset(line_cfg_);
325
326 if (ApplyDirection(config.direction) != ErrorCode::OK)
327 {
328 return ErrorCode::ARG_ERR;
329 }
330
331 if (ApplyPull(config.pull) != ErrorCode::OK)
332 {
333 return ErrorCode::ARG_ERR;
334 }
335
336 if (gpiod_line_config_add_line_settings(line_cfg_, &line_offset_, 1, settings_) < 0)
337 {
338 return ErrorCode::FAILED;
339 }
340
341 if (!request_)
342 {
343 request_ = gpiod_chip_request_lines(chip_, req_cfg_, line_cfg_);
344 if (!request_)
345 {
346 return ErrorCode::FAILED;
347 }
348 }
349 else
350 {
351 if (gpiod_line_request_reconfigure_lines(request_, line_cfg_) < 0)
352 {
353 return ErrorCode::FAILED;
354 }
355 }
356
357 current_config_ = config;
358 has_config_ = true;
359
360 return ErrorCode::OK;
361 }
362
363 private:
364 std::string chip_path_;
365 unsigned int line_offset_;
366 gpiod_chip* chip_ = nullptr;
367 gpiod_edge_event_buffer* event_buffer_ =
368 nullptr;
369 gpiod_line_settings* settings_ = nullptr;
370 gpiod_request_config* req_cfg_ = nullptr;
371 gpiod_line_config* line_cfg_ = nullptr;
372 gpiod_line_request* request_ = nullptr;
373 Configuration current_config_ = {Direction::INPUT, Pull::NONE};
374 bool has_config_ = false;
375 bool interrupt_enabled_ = false;
376
382 ErrorCode ApplyDirection(Direction direction)
383 {
384 if (!settings_)
385 {
386 return ErrorCode::FAILED;
387 }
388
389 switch (direction)
390 {
391 case Direction::INPUT:
392 if (gpiod_line_settings_set_direction(settings_, GPIOD_LINE_DIRECTION_INPUT) < 0)
393 {
394 return ErrorCode::FAILED;
395 }
396 if (gpiod_line_settings_set_edge_detection(settings_, GPIOD_LINE_EDGE_NONE) < 0)
397 {
398 return ErrorCode::FAILED;
399 }
400 break;
401
403 if (gpiod_line_settings_set_direction(settings_, GPIOD_LINE_DIRECTION_OUTPUT) < 0)
404 {
405 return ErrorCode::FAILED;
406 }
407 if (gpiod_line_settings_set_drive(settings_, GPIOD_LINE_DRIVE_PUSH_PULL) < 0)
408 {
409 return ErrorCode::FAILED;
410 }
411 if (gpiod_line_settings_set_edge_detection(settings_, GPIOD_LINE_EDGE_NONE) < 0)
412 {
413 return ErrorCode::FAILED;
414 }
415 break;
416
418 if (gpiod_line_settings_set_direction(settings_, GPIOD_LINE_DIRECTION_OUTPUT) < 0)
419 {
420 return ErrorCode::FAILED;
421 }
422 if (gpiod_line_settings_set_drive(settings_, GPIOD_LINE_DRIVE_OPEN_DRAIN) < 0)
423 {
424 return ErrorCode::FAILED;
425 }
426 if (gpiod_line_settings_set_edge_detection(settings_, GPIOD_LINE_EDGE_NONE) < 0)
427 {
428 return ErrorCode::FAILED;
429 }
430 break;
431
433 if (gpiod_line_settings_set_direction(settings_, GPIOD_LINE_DIRECTION_INPUT) < 0)
434 {
435 return ErrorCode::FAILED;
436 }
437 if (gpiod_line_settings_set_edge_detection(settings_, GPIOD_LINE_EDGE_RISING) < 0)
438 {
439 return ErrorCode::FAILED;
440 }
441 break;
442
444 if (gpiod_line_settings_set_direction(settings_, GPIOD_LINE_DIRECTION_INPUT) < 0)
445 {
446 return ErrorCode::FAILED;
447 }
448 if (gpiod_line_settings_set_edge_detection(settings_, GPIOD_LINE_EDGE_FALLING) <
449 0)
450 {
451 return ErrorCode::FAILED;
452 }
453 break;
454
456 if (gpiod_line_settings_set_direction(settings_, GPIOD_LINE_DIRECTION_INPUT) < 0)
457 {
458 return ErrorCode::FAILED;
459 }
460 if (gpiod_line_settings_set_edge_detection(settings_, GPIOD_LINE_EDGE_BOTH) < 0)
461 {
462 return ErrorCode::FAILED;
463 }
464 break;
465 }
466
467 return ErrorCode::OK;
468 }
469
475 ErrorCode ApplyPull(Pull pull)
476 {
477 if (!settings_)
478 {
479 return ErrorCode::FAILED;
480 }
481
482 int ret = 0;
483 switch (pull)
484 {
485 case Pull::NONE:
486 ret = gpiod_line_settings_set_bias(settings_, GPIOD_LINE_BIAS_DISABLED);
487 break;
488 case Pull::UP:
489 ret = gpiod_line_settings_set_bias(settings_, GPIOD_LINE_BIAS_PULL_UP);
490 break;
491 case Pull::DOWN:
492 ret = gpiod_line_settings_set_bias(settings_, GPIOD_LINE_BIAS_PULL_DOWN);
493 break;
494 }
495
496 return ret < 0 ? ErrorCode::FAILED : ErrorCode::OK;
497 }
498
503 static bool IsInterruptDirection(Direction direction)
504 {
505 return direction == Direction::RISING_INTERRUPT ||
506 direction == Direction::FALL_INTERRUPT ||
508 }
509
513 bool EnsureConfigured() const
514 {
515 if (!has_config_ || !request_)
516 {
517 XR_LOG_ERROR("GPIO is not configured");
518 ASSERT(false);
519 return false;
520 }
521
522 return true;
523 }
524
528 ErrorCode EnsureInterruptReady() const
529 {
530 if (!EnsureConfigured())
531 {
532 return ErrorCode::STATE_ERR;
533 }
534
535 if (!IsInterruptDirection(current_config_.direction))
536 {
537 XR_LOG_ERROR("GPIO is not configured for interrupt mode");
538 ASSERT(false);
539 return ErrorCode::ARG_ERR;
540 }
541
542 if (!interrupt_enabled_)
543 {
544 XR_LOG_ERROR("GPIO interrupt is not enabled");
545 return ErrorCode::STATE_ERR;
546 }
547
548 return ErrorCode::OK;
549 }
550};
551
552} // namespace LibXR
void Run(bool in_isr, PassArgs &&... args) const
执行回调函数并传递参数 / Execute the callback with arguments
Definition libxr_cb.hpp:225
bool Empty() const
检查回调是否为空 / Check whether the callback is empty
Definition libxr_cb.hpp:236
通用输入输出(GPIO)接口类。General Purpose Input/Output (GPIO) interface class.
Definition gpio.hpp:13
Callback callback_
GPIO 事件的回调函数。Callback function for GPIO events.
Definition gpio.hpp:56
Direction
定义 GPIO 引脚的方向类型。Defines the direction types for GPIO pins.
Definition gpio.hpp:20
@ OUTPUT_PUSH_PULL
推挽输出模式。Push-pull output mode.
@ RISING_INTERRUPT
上升沿中断模式。Rising edge interrupt mode.
@ FALL_RISING_INTERRUPT
双沿触发中断模式。Both edge interrupt mode.
@ OUTPUT_OPEN_DRAIN
开漏输出模式。Open-drain output mode.
@ INPUT
输入模式。Input mode.
@ FALL_INTERRUPT
下降沿中断模式。Falling edge interrupt mode.
Pull
定义 GPIO 引脚的上拉/下拉模式。Defines the pull-up/pull-down configurations for GPIO pins.
Definition gpio.hpp:35
@ NONE
无上拉或下拉。No pull-up or pull-down.
@ DOWN
下拉模式。Pull-down mode.
@ UP
上拉模式。Pull-up mode.
基于 libgpiod v2.x 的 Linux GPIO 实现 Linux GPIO implementation using libgpiod v2.x
ErrorCode EnableInterrupt() override
使能 GPIO 中断处理状态。Enables GPIO interrupt handling state.
static bool IsInterruptDirection(Direction direction)
判断 direction 是否为中断方向。Checks whether direction is interrupt mode.
ErrorCode ApplyDirection(Direction direction)
根据 GPIO 方向配置 line settings。 Applies line settings according to GPIO direction.
ErrorCode ApplyPull(Pull pull)
根据 GPIO 上拉/下拉配置 line settings。 Applies line settings according to GPIO pull mode.
gpiod_edge_event_buffer * event_buffer_
持久化事件缓冲区。Persistent edge event buffer.
LinuxGPIO(const std::string &chip_path, unsigned int line_offset)
构造 LinuxGPIO 对象。Constructs a LinuxGPIO instance.
void Write(bool value) override
写入 GPIO 引脚状态。Writes a level to the GPIO line.
ErrorCode ReadEvent(GPIOEvent &event)
读取单个中断事件。Reads a single interrupt edge event.
bool Read() override
读取 GPIO 引脚状态。Reads the GPIO line level.
ErrorCode SetConfig(Configuration config) override
配置 GPIO 引脚参数。Configures GPIO line settings.
ErrorCode EnsureInterruptReady() const
确保中断路径已启用。Ensures interrupt path is enabled and ready.
ErrorCode DisableInterrupt() override
禁用 GPIO 中断处理状态。Disables GPIO interrupt handling state.
int GetFd() const
获取 GPIO request 对应的文件描述符,用于 epoll/poll 注册。 Gets the request file descriptor for epoll/poll registrati...
ErrorCode HandleInterrupt()
非阻塞处理中断事件,排空队列并触发回调。 Handles GPIO interrupt events in non-blocking mode, drains queued events and dis...
bool EnsureConfigured() const
确保 GPIO 已配置。Ensures GPIO has been configured.
LibXR 命名空间
Definition ch32_can.hpp:14
GPIOEventType
GPIO 事件类型。GPIO event type.
@ FALLING_EDGE
下降沿事件。Falling edge event.
@ RISING_EDGE
上升沿事件。Rising edge event.
存储 GPIO 配置参数的结构体。Structure storing GPIO configuration parameters.
Definition gpio.hpp:46
Pull pull
GPIO 上拉/下拉配置。GPIO pull-up/pull-down configuration.
Definition gpio.hpp:48
Direction direction
GPIO 引脚方向。GPIO pin direction.
Definition gpio.hpp:47
GPIO 事件结构体。GPIO event structure.
int64_t timestamp
事件时间戳(纳秒)。Event timestamp in nanoseconds.
GPIOEventType type
事件类型。Event type.