1#include "linux_gpio.hpp"
15#if !defined(XR_LINUX_GPIO_DISABLE_V2) && defined(GPIO_V2_LINE_FLAG_INPUT) && \
16 defined(GPIO_V2_GET_LINEINFO_IOCTL) && defined(GPIO_V2_GET_LINE_IOCTL) && \
17 defined(GPIO_V2_LINE_SET_CONFIG_IOCTL) && defined(GPIO_V2_LINE_GET_VALUES_IOCTL) && \
18 defined(GPIO_V2_LINE_SET_VALUES_IOCTL) && defined(GPIO_V2_LINE_EVENT_RISING_EDGE) && \
19 defined(GPIO_V2_LINE_EVENT_FALLING_EDGE)
20#define XR_LINUX_GPIO_HAS_V2 1
22#define XR_LINUX_GPIO_HAS_V2 0
25#if defined(GPIOHANDLE_REQUEST_BIAS_DISABLE) && \
26 defined(GPIOHANDLE_REQUEST_BIAS_PULL_UP) && \
27 defined(GPIOHANDLE_REQUEST_BIAS_PULL_DOWN)
28#define XR_LINUX_GPIO_V1_HAS_BIAS_FLAGS 1
30#define XR_LINUX_GPIO_V1_HAS_BIAS_FLAGS 0
33#if defined(GPIOHANDLE_SET_CONFIG_IOCTL)
34#define XR_LINUX_GPIO_V1_HAS_SET_CONFIG 1
36#define XR_LINUX_GPIO_V1_HAS_SET_CONFIG 0
41constexpr const char* LINUX_GPIO_CONSUMER =
"LinuxGPIO";
44 bool interrupt_enabled)
46 if (!interrupt_enabled &&
58void CopyConsumer(
char (&dst)[N],
const char* src)
60 std::strncpy(dst, src, N - 1U);
64#if XR_LINUX_GPIO_HAS_V2
72 flags |= GPIO_V2_LINE_FLAG_INPUT;
75 flags |= GPIO_V2_LINE_FLAG_OUTPUT;
78 flags |= GPIO_V2_LINE_FLAG_OUTPUT | GPIO_V2_LINE_FLAG_OPEN_DRAIN;
81 flags |= GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_EDGE_RISING;
84 flags |= GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_EDGE_FALLING;
87 flags |= GPIO_V2_LINE_FLAG_INPUT | GPIO_V2_LINE_FLAG_EDGE_RISING |
88 GPIO_V2_LINE_FLAG_EDGE_FALLING;
95 flags |= GPIO_V2_LINE_FLAG_BIAS_DISABLED;
98 flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_UP;
101 flags |= GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN;
119 flags |= GPIOHANDLE_REQUEST_INPUT;
122 flags |= GPIOHANDLE_REQUEST_OUTPUT;
125 flags |= GPIOHANDLE_REQUEST_OUTPUT | GPIOHANDLE_REQUEST_OPEN_DRAIN;
132#if XR_LINUX_GPIO_V1_HAS_BIAS_FLAGS
133 flags |= GPIOHANDLE_REQUEST_BIAS_DISABLE;
137#if XR_LINUX_GPIO_V1_HAS_BIAS_FLAGS
138 flags |= GPIOHANDLE_REQUEST_BIAS_PULL_UP;
142#if XR_LINUX_GPIO_V1_HAS_BIAS_FLAGS
143 flags |= GPIOHANDLE_REQUEST_BIAS_PULL_DOWN;
156 return GPIOEVENT_REQUEST_RISING_EDGE;
158 return GPIOEVENT_REQUEST_FALLING_EDGE;
160 return GPIOEVENT_REQUEST_BOTH_EDGES;
166int SetNonBlocking(
int fd)
168 const int flags = fcntl(fd, F_GETFL, 0);
174 return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
177void DrainInterruptWakeFd(
int fd)
179 std::array<uint8_t, 32> buffer = {};
182 const ssize_t bytes = read(fd, buffer.data(), buffer.size());
188 if ((bytes < 0) && ((errno == EAGAIN) || (errno == EWOULDBLOCK) || (errno == EINTR)))
197enum class PollReadableResult : int8_t
205PollReadableResult PollReadable(
int fd,
int wake_fd,
int timeout_ms)
207 std::array<pollfd, 2> poll_fds = {};
211 poll_fds[nfds].fd = fd;
212 poll_fds[nfds].events = POLLIN;
218 poll_fds[nfds].fd = wake_fd;
219 poll_fds[nfds].events = POLLIN;
225 const int ret = poll(poll_fds.data(), nfds, timeout_ms);
226 if ((ret < 0) && (errno == EINTR))
232 return PollReadableResult::ERROR;
237 return PollReadableResult::TIMEOUT;
240 if ((wake_fd >= 0) && ((poll_fds[1].revents & POLLIN) != 0))
242 DrainInterruptWakeFd(wake_fd);
243 return PollReadableResult::WAKE;
246 if ((poll_fds[0].revents & (POLLNVAL | POLLERR | POLLHUP)) != 0)
249 return PollReadableResult::ERROR;
252 if ((poll_fds[0].revents & POLLIN) != 0)
254 return PollReadableResult::DATA;
258 return PollReadableResult::ERROR;
262bool IsKnownGPIOEvent(uint32_t event_id)
264 if (event_id == GPIOEVENT_EVENT_RISING_EDGE)
269 if (event_id == GPIOEVENT_EVENT_FALLING_EDGE)
274#if XR_LINUX_GPIO_HAS_V2
275 if (event_id == GPIO_V2_LINE_EVENT_RISING_EDGE)
280 if (event_id == GPIO_V2_LINE_EVENT_FALLING_EDGE)
286 XR_LOG_WARN(
"Unknown GPIO event id: %u", event_id);
295LinuxGPIO::LinuxGPIO(
const std::string& chip_path,
unsigned int line_offset)
296 : chip_path_(chip_path), line_offset_(line_offset)
298 UNUSED(InitInterruptWakePipe());
302LinuxGPIO::~LinuxGPIO()
304 interrupt_thread_exit_.store(
true);
305 request_kind_.store(RequestKind::NONE);
306 interrupt_enabled_.store(
false);
307 NotifyInterruptThread();
308 WaitForInterruptThreadStopped();
311 CloseInterruptWakePipe();
314bool LinuxGPIO::Read()
316 if (EnsureConfigured() != ErrorCode::OK)
321 const int request_fd = request_fd_.load();
322 if (abi_version_.load() == AbiVersion::V2)
324#if XR_LINUX_GPIO_HAS_V2
325 gpio_v2_line_values values = {};
327 if (ioctl(request_fd, GPIO_V2_LINE_GET_VALUES_IOCTL, &values) < 0)
329 XR_LOG_ERROR(
"Failed to read GPIO value: %s", std::strerror(errno));
332 return (values.bits & 1ULL) != 0U;
338 gpiohandle_data values = {};
339 if (ioctl(request_fd, GPIOHANDLE_GET_LINE_VALUES_IOCTL, &values) < 0)
341 XR_LOG_ERROR(
"Failed to read GPIO value: %s", std::strerror(errno));
345 return values.values[0] != 0U;
348void LinuxGPIO::Write(
bool value)
350 if (EnsureConfigured() != ErrorCode::OK)
355 const int request_fd = request_fd_.load();
356 if (abi_version_.load() == AbiVersion::V2)
358#if XR_LINUX_GPIO_HAS_V2
359 gpio_v2_line_values values = {};
361 values.bits = value ? 1ULL : 0ULL;
362 if (ioctl(request_fd, GPIO_V2_LINE_SET_VALUES_IOCTL, &values) < 0)
364 XR_LOG_WARN(
"Failed to write GPIO value: %s", std::strerror(errno));
372 gpiohandle_data values = {};
373 values.values[0] = value ? 1U : 0U;
374 if (ioctl(request_fd, GPIOHANDLE_SET_LINE_VALUES_IOCTL, &values) < 0)
376 XR_LOG_WARN(
"Failed to write GPIO value: %s", std::strerror(errno));
382 if (EnsureConfigured() != ErrorCode::OK)
384 return ErrorCode::STATE_ERR;
387 if (!IsInterruptDirection(current_config_.direction))
389 return ErrorCode::ARG_ERR;
392 if (interrupt_enabled_.load())
394 return ErrorCode::OK;
398 if (NeedsRequestReopen(current_config_))
400 ec = ReopenRequest(current_config_);
402 else if (abi_version_.load() == AbiVersion::V2)
404 ec = ReconfigureRequestV2(current_config_);
408 ec = ReconfigureRequestV1(current_config_);
410 if (ec != ErrorCode::OK)
416 interrupt_enabled_.store(
true);
417 StartInterruptThread();
418 return ErrorCode::OK;
423 if (EnsureConfigured() != ErrorCode::OK)
425 return ErrorCode::STATE_ERR;
428 if (!IsInterruptDirection(current_config_.direction))
430 return ErrorCode::ARG_ERR;
433 if (!interrupt_enabled_.load())
435 return ErrorCode::OK;
438 const Configuration inactive_config = ResolveRequestConfig(current_config_,
false);
440 if (NeedsRequestReopen(inactive_config))
442 ec = ReopenRequest(inactive_config);
444 else if (abi_version_.load() == AbiVersion::V2)
446 ec = ReconfigureRequestV2(inactive_config);
450 ec = ReconfigureRequestV1(inactive_config);
452 if (ec != ErrorCode::OK)
458 interrupt_enabled_.store(
false);
459 return ErrorCode::OK;
465 if (chip_ready != ErrorCode::OK)
470 const bool keep_interrupt_enabled =
471 interrupt_enabled_.load() && IsInterruptDirection(config.
direction);
473 ResolveRequestConfig(config, keep_interrupt_enabled);
476 if (request_fd_.load() < 0)
478 ec = ReopenRequest(request_config);
480 else if (NeedsRequestReopen(request_config))
482 ec = ReopenRequest(request_config);
484 else if (abi_version_.load() == AbiVersion::V2)
486 ec = ReconfigureRequestV2(request_config);
490 ec = ReconfigureRequestV1(request_config);
493 if (ec != ErrorCode::OK)
498 current_config_ = config;
500 interrupt_enabled_.store(keep_interrupt_enabled);
501 if (keep_interrupt_enabled)
503 StartInterruptThread();
505 return ErrorCode::OK;
508void LinuxGPIO::StartInterruptThread()
510 if (interrupt_thread_started_.exchange(
true))
515 interrupt_thread_.Create<LinuxGPIO*>(
516 this, [](LinuxGPIO* self) { self->InterruptLoop(); },
"irq_gpio",
517 INTERRUPT_THREAD_STACK_SIZE, Thread::Priority::MEDIUM);
520void LinuxGPIO::InterruptLoop()
522 while (!interrupt_thread_exit_.load())
524 if (!interrupt_enabled_.load())
530 if (request_kind_.load() != RequestKind::EVENT)
536 const int fd = request_fd_.load();
543 size_t event_count = 0;
544 interrupt_poll_active_.store(
true);
545 const ErrorCode pump = PumpEventQueue(fd, abi_version_.load(), event_count, 100);
546 interrupt_poll_active_.store(
false);
547 if (pump == ErrorCode::OK)
549 for (
size_t i = 0; i < event_count; ++i)
551 callback_.Run(
false);
556 if (pump != ErrorCode::EMPTY)
562 interrupt_poll_active_.store(
false);
563 interrupt_thread_started_.store(
false);
570 return ErrorCode::OK;
573 chip_fd_ = open(chip_path_.c_str(), O_RDONLY | O_CLOEXEC);
576 XR_LOG_ERROR(
"Failed to open GPIO chip: %s (%s)", chip_path_.c_str(),
577 std::strerror(errno));
578 return ErrorCode::INIT_ERR;
581 return DetectAbiVersion();
584void LinuxGPIO::CloseChip()
591 abi_version_.store(AbiVersion::UNKNOWN);
594void LinuxGPIO::CloseRequest()
596 request_kind_.store(RequestKind::NONE);
597 interrupt_enabled_.store(
false);
598 NotifyInterruptThread();
599 WaitForInterruptLoopIdle();
601 const int request_fd = request_fd_.exchange(-1);
607 request_kind_.store(RequestKind::NONE);
608 interrupt_enabled_.store(
false);
612ErrorCode LinuxGPIO::InitInterruptWakePipe()
614 if (interrupt_wake_pipe_[0] >= 0)
616 return ErrorCode::OK;
619 if (pipe(interrupt_wake_pipe_) < 0)
621 XR_LOG_ERROR(
"Failed to create GPIO interrupt wake pipe: %s", std::strerror(errno));
622 return ErrorCode::INIT_ERR;
625 if ((SetNonBlocking(interrupt_wake_pipe_[0]) < 0) ||
626 (SetNonBlocking(interrupt_wake_pipe_[1]) < 0))
628 XR_LOG_ERROR(
"Failed to configure GPIO interrupt wake pipe: %s",
629 std::strerror(errno));
630 CloseInterruptWakePipe();
631 return ErrorCode::INIT_ERR;
634 return ErrorCode::OK;
637void LinuxGPIO::CloseInterruptWakePipe()
639 for (
int& fd : interrupt_wake_pipe_)
649void LinuxGPIO::NotifyInterruptThread()
651 if (interrupt_wake_pipe_[1] < 0)
656 const uint8_t signal = 1U;
659 const ssize_t ret = write(interrupt_wake_pipe_[1], &signal,
sizeof(signal));
665 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
667 DrainInterruptWakeFd(interrupt_wake_pipe_[0]);
680void LinuxGPIO::WaitForInterruptLoopIdle()
682 if (!interrupt_thread_started_.load())
687 for (uint32_t i = 0; i < 200; ++i)
689 if (!interrupt_poll_active_.load())
697 XR_LOG_WARN(
"Timed out waiting for GPIO interrupt loop to go idle");
700void LinuxGPIO::WaitForInterruptThreadStopped()
702 if (!interrupt_thread_started_.load())
707 for (uint32_t i = 0; i < 500; ++i)
709 if (!interrupt_thread_started_.load())
714 NotifyInterruptThread();
718 XR_LOG_WARN(
"Timed out waiting for GPIO interrupt thread to stop");
723 if (abi_version_.load() != AbiVersion::UNKNOWN)
725 return ErrorCode::OK;
728 gpiochip_info chip_info = {};
729 if (ioctl(chip_fd_, GPIO_GET_CHIPINFO_IOCTL, &chip_info) < 0)
731 XR_LOG_ERROR(
"Failed to read GPIO chip info: %s", std::strerror(errno));
732 return ErrorCode::FAILED;
735 if (chip_info.lines == 0U)
737 XR_LOG_ERROR(
"GPIO chip exposes zero lines");
738 return ErrorCode::FAILED;
741#if XR_LINUX_GPIO_HAS_V2
742 gpio_v2_line_info info = {};
743 info.offset = (line_offset_ < chip_info.lines) ? line_offset_ : 0U;
744 if (ioctl(chip_fd_, GPIO_V2_GET_LINEINFO_IOCTL, &info) == 0)
746 abi_version_.store(AbiVersion::V2);
747 return ErrorCode::OK;
750 if ((errno == ENOTTY) || (errno == EINVAL)
752 || (errno == EOPNOTSUPP)
756 abi_version_.store(AbiVersion::V1);
757 return ErrorCode::OK;
760 XR_LOG_ERROR(
"Failed to probe GPIO chardev ABI: %s", std::strerror(errno));
761 return ErrorCode::FAILED;
763 abi_version_.store(AbiVersion::V1);
764 return ErrorCode::OK;
768ErrorCode LinuxGPIO::ReopenRequest(Configuration config)
772 if (abi_version_.load() == AbiVersion::V2)
774 return OpenRequestV2(config);
777 return OpenRequestV1(config);
780ErrorCode LinuxGPIO::OpenRequestV2(Configuration config)
782#if XR_LINUX_GPIO_HAS_V2
783 gpio_v2_line_request request = {};
784 request.offsets[0] = line_offset_;
785 request.num_lines = 1U;
786 request.event_buffer_size =
787 IsInterruptDirection(config.direction) ? EVENT_BUFFER_CAPACITY : 0U;
788 CopyConsumer(request.consumer, LINUX_GPIO_CONSUMER);
789 request.config.flags = BuildLineFlagsV2(config);
791 if (ioctl(chip_fd_, GPIO_V2_GET_LINE_IOCTL, &request) < 0)
793 XR_LOG_ERROR(
"Failed to request GPIO line (v2): %s", std::strerror(errno));
794 return ErrorCode::FAILED;
797 request_fd_.store(request.fd);
798 request_kind_.store(IsInterruptDirection(config.direction) ? RequestKind::EVENT
799 : RequestKind::HANDLE);
800 if (SetNonBlocking(request.fd) < 0)
802 XR_LOG_ERROR(
"Failed to enable non-blocking GPIO request fd: %s",
803 std::strerror(errno));
805 return ErrorCode::FAILED;
808 return ErrorCode::OK;
811 return ErrorCode::NOT_SUPPORT;
815ErrorCode LinuxGPIO::ReconfigureRequestV2(Configuration config)
817#if XR_LINUX_GPIO_HAS_V2
818 const bool was_interrupt_request =
819 (request_kind_.load() == RequestKind::EVENT) && interrupt_enabled_.load();
820 if (was_interrupt_request)
822 request_kind_.store(RequestKind::NONE);
823 interrupt_enabled_.store(
false);
824 NotifyInterruptThread();
825 WaitForInterruptLoopIdle();
828 gpio_v2_line_config line_config = {};
829 line_config.flags = BuildLineFlagsV2(config);
831 if (ioctl(request_fd_.load(), GPIO_V2_LINE_SET_CONFIG_IOCTL, &line_config) < 0)
833 XR_LOG_ERROR(
"Failed to reconfigure GPIO line (v2): %s", std::strerror(errno));
834 return ErrorCode::FAILED;
837 request_kind_.store(IsInterruptDirection(config.direction) ? RequestKind::EVENT
838 : RequestKind::HANDLE);
839 if (was_interrupt_request && IsInterruptDirection(config.direction))
841 interrupt_enabled_.store(
true);
843 return ErrorCode::OK;
846 return ErrorCode::NOT_SUPPORT;
850ErrorCode LinuxGPIO::OpenRequestV1(Configuration config)
852 if (IsInterruptDirection(config.direction))
854 gpioevent_request request = {};
855 request.lineoffset = line_offset_;
856 request.handleflags = BuildHandleFlagsV1(config);
857 request.eventflags = BuildEventFlagsV1(config.direction);
858 CopyConsumer(request.consumer_label, LINUX_GPIO_CONSUMER);
860 if (ioctl(chip_fd_, GPIO_GET_LINEEVENT_IOCTL, &request) < 0)
862 XR_LOG_ERROR(
"Failed to request GPIO event line (v1): %s", std::strerror(errno));
863 return ErrorCode::FAILED;
866 request_fd_.store(request.fd);
867 request_kind_.store(RequestKind::EVENT);
871 gpiohandle_request request = {};
872 request.lineoffsets[0] = line_offset_;
874 request.flags = BuildHandleFlagsV1(config);
875 CopyConsumer(request.consumer_label, LINUX_GPIO_CONSUMER);
877 if (ioctl(chip_fd_, GPIO_GET_LINEHANDLE_IOCTL, &request) < 0)
879 XR_LOG_ERROR(
"Failed to request GPIO handle line (v1): %s", std::strerror(errno));
880 return ErrorCode::FAILED;
883 request_fd_.store(request.fd);
884 request_kind_.store(RequestKind::HANDLE);
887 if (SetNonBlocking(request_fd_.load()) < 0)
889 XR_LOG_ERROR(
"Failed to enable non-blocking GPIO request fd: %s",
890 std::strerror(errno));
892 return ErrorCode::FAILED;
895 return ErrorCode::OK;
898ErrorCode LinuxGPIO::ReconfigureRequestV1(Configuration config)
900 if (request_kind_.load() != RequestKind::HANDLE)
902 return ReopenRequest(config);
905#if XR_LINUX_GPIO_V1_HAS_SET_CONFIG
906 gpiohandle_config handle_config = {};
907 handle_config.flags = BuildHandleFlagsV1(config);
908 if (ioctl(request_fd_.load(), GPIOHANDLE_SET_CONFIG_IOCTL, &handle_config) < 0)
910 XR_LOG_ERROR(
"Failed to reconfigure GPIO line (v1): %s", std::strerror(errno));
911 return ErrorCode::FAILED;
914 return ErrorCode::OK;
916 return ReopenRequest(config);
920ErrorCode LinuxGPIO::PumpEventQueue(
int fd, AbiVersion abi_version,
size_t& event_count,
921 int timeout_ms)
const
925 return ErrorCode::STATE_ERR;
928 const PollReadableResult ready = PollReadable(fd, interrupt_wake_pipe_[0], timeout_ms);
929 if (ready == PollReadableResult::ERROR)
933 return ErrorCode::EMPTY;
935 XR_LOG_ERROR(
"Failed to poll GPIO fd: %s", std::strerror(errno));
936 return ErrorCode::FAILED;
939 if ((ready == PollReadableResult::TIMEOUT) || (ready == PollReadableResult::WAKE))
941 return ErrorCode::EMPTY;
944 if (abi_version == AbiVersion::V2)
946 return ReadEventsV2(fd, event_count);
949 return ReadEventsV1(fd, event_count);
952ErrorCode LinuxGPIO::ReadEventsV2(
int fd,
size_t& event_count)
const
954#if XR_LINUX_GPIO_HAS_V2
955 bool received =
false;
958 std::array<gpio_v2_line_event, EVENT_BUFFER_CAPACITY> events = {};
959 const ssize_t bytes = read(fd, events.data(),
sizeof(events));
962 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
968 return received ? ErrorCode::OK : ErrorCode::EMPTY;
971 XR_LOG_ERROR(
"Failed to read GPIO v2 events: %s", std::strerror(errno));
972 return ErrorCode::FAILED;
980 if ((bytes %
static_cast<ssize_t
>(
sizeof(gpio_v2_line_event))) != 0)
982 XR_LOG_ERROR(
"Corrupted GPIO v2 event payload length");
983 return ErrorCode::FAILED;
986 const size_t count =
static_cast<size_t>(bytes /
sizeof(gpio_v2_line_event));
987 for (
size_t i = 0; i < count; ++i)
989 if (!IsKnownGPIOEvent(events[i].
id))
999 return received ? ErrorCode::OK : ErrorCode::EMPTY;
1003 return ErrorCode::NOT_SUPPORT;
1007ErrorCode LinuxGPIO::ReadEventsV1(
int fd,
size_t& event_count)
const
1009 bool received =
false;
1012 gpioevent_data
event = {};
1013 const ssize_t bytes = read(fd, &event,
sizeof(event));
1016 if ((errno == EAGAIN) || (errno == EWOULDBLOCK))
1022 return received ? ErrorCode::OK : ErrorCode::EMPTY;
1025 XR_LOG_ERROR(
"Failed to read GPIO v1 events: %s", std::strerror(errno));
1026 return ErrorCode::FAILED;
1034 if (bytes !=
static_cast<ssize_t
>(
sizeof(event)))
1036 XR_LOG_ERROR(
"Corrupted GPIO v1 event payload length");
1037 return ErrorCode::FAILED;
1040 if (!IsKnownGPIOEvent(event.id))
1049 return received ? ErrorCode::OK : ErrorCode::EMPTY;
1052ErrorCode LinuxGPIO::EnsureConfigured()
const
1054 if (!has_config_ || (request_fd_.load() < 0))
1056 XR_LOG_ERROR(
"GPIO is not configured");
1057 return ErrorCode::STATE_ERR;
1060 return ErrorCode::OK;
1062bool LinuxGPIO::IsInterruptDirection(Direction direction)
1064 return direction == Direction::RISING_INTERRUPT ||
1065 direction == Direction::FALL_INTERRUPT ||
1066 direction == Direction::FALL_RISING_INTERRUPT;
1069bool LinuxGPIO::NeedsRequestReopen(Configuration config)
const
1071 if (request_fd_.load() < 0)
1076 const bool old_interrupt =
1077 interrupt_enabled_.load() && IsInterruptDirection(current_config_.direction);
1078 const bool new_interrupt = IsInterruptDirection(config.direction);
1080 if (abi_version_.load() == AbiVersion::V2)
1082 return old_interrupt != new_interrupt;
1095 return request_kind_.load() != RequestKind::HANDLE;
Direction
定义 GPIO 引脚的方向类型 / Defines the direction types for GPIO pins.
@ 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.
@ NONE
无上拉或下拉 / No pull-up or pull-down.
@ DOWN
下拉模式 / Pull-down mode.
LibXR 命名空间 / LibXR namespace.
ErrorCode
定义错误码枚举 / Defines an enumeration for error codes
存储 GPIO 配置参数的结构体 / Structure storing GPIO configuration parameters.
Pull pull
GPIO 上拉/下拉配置 / GPIO pull-up/pull-down configuration.
Direction direction
GPIO 引脚方向 / GPIO pin direction.