|
| 1 | +/* |
| 2 | + * |
| 3 | + * Copyright (c) 2024 Project CHIP Authors |
| 4 | + * |
| 5 | + * Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | + * you may not use this file except in compliance with the License. |
| 7 | + * You may obtain a copy of the License at |
| 8 | + * |
| 9 | + * http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | + * |
| 11 | + * Unless required by applicable law or agreed to in writing, software |
| 12 | + * distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | + * See the License for the specific language governing permissions and |
| 15 | + * limitations under the License. |
| 16 | + */ |
| 17 | + |
| 18 | +#include "IcdUatButton.h" |
| 19 | + |
| 20 | +#include "driver/gpio.h" |
| 21 | +#include "esp_attr.h" |
| 22 | +#include "esp_log.h" |
| 23 | +#include "freertos/FreeRTOS.h" |
| 24 | +#include "freertos/queue.h" |
| 25 | +#include "freertos/task.h" |
| 26 | +#include "hal/gpio_types.h" |
| 27 | +#include <cstdint> |
| 28 | + |
| 29 | +#define ESP_INTR_FLAG_DEFAULT 0 |
| 30 | + |
| 31 | +static const char TAG[] = "Button"; |
| 32 | +QueueHandle_t UatButton::sEventQueue = nullptr; |
| 33 | +TaskHandle_t UatButton::sTaskHandle = nullptr; |
| 34 | + |
| 35 | +static void IRAM_ATTR gpio_isr_handler(void * arg) |
| 36 | +{ |
| 37 | + if (UatButton::sEventQueue) |
| 38 | + { |
| 39 | + UatButton * button = (UatButton *) arg; |
| 40 | + button->GpioIntrEnable(false); |
| 41 | + xQueueSendFromISR(UatButton::sEventQueue, &button, NULL); |
| 42 | + } |
| 43 | +} |
| 44 | + |
| 45 | +void UatButton::RunEventLoop(void * arg) |
| 46 | +{ |
| 47 | + bool eventDone = true; |
| 48 | + UatButton * button = nullptr; |
| 49 | + |
| 50 | + for (;;) |
| 51 | + { |
| 52 | + if (xQueueReceive(sEventQueue, &button, portMAX_DELAY) == pdTRUE && button) |
| 53 | + { |
| 54 | + button->GpioIntrEnable(false); |
| 55 | + eventDone = false; |
| 56 | + } |
| 57 | + while (!eventDone) |
| 58 | + { |
| 59 | + // GPIO Pull up is enabled so the button is pressed when this value is false. |
| 60 | + bool value = gpio_get_level(button->mGpioNum); |
| 61 | + switch (button->mState) |
| 62 | + { |
| 63 | + case ButtonState::kIdle: |
| 64 | + button->mState = value == false ? ButtonState::kPressed : ButtonState::kIdle; |
| 65 | + break; |
| 66 | + case ButtonState::kPressed: |
| 67 | + button->mState = value == false ? ButtonState::kPressed : ButtonState::kReleased; |
| 68 | + break; |
| 69 | + case ButtonState::kReleased: |
| 70 | + button->mState = ButtonState::kIdle; |
| 71 | + if (button->mUatButtonPressCallback) |
| 72 | + { |
| 73 | + button->mUatButtonPressCallback(button); |
| 74 | + } |
| 75 | + break; |
| 76 | + default: |
| 77 | + break; |
| 78 | + } |
| 79 | + if (button->mState == ButtonState::kIdle) |
| 80 | + { |
| 81 | + button->GpioIntrEnable(true); |
| 82 | + eventDone = true; |
| 83 | + break; |
| 84 | + } |
| 85 | + vTaskDelay(10 / portTICK_PERIOD_MS); |
| 86 | + } |
| 87 | + } |
| 88 | +} |
| 89 | + |
| 90 | +void UatButton::GpioIntrEnable(bool enable) |
| 91 | +{ |
| 92 | + if (enable) |
| 93 | + { |
| 94 | + gpio_intr_enable(mGpioNum); |
| 95 | + } |
| 96 | + else |
| 97 | + { |
| 98 | + gpio_intr_disable(mGpioNum); |
| 99 | + } |
| 100 | +} |
| 101 | + |
| 102 | +void UatButton::Init(gpio_num_t gpioNum, esp_sleep_ext1_wakeup_mode_t wakeupMode) |
| 103 | +{ |
| 104 | + mGpioNum = gpioNum; |
| 105 | + mState = ButtonState::kIdle; |
| 106 | + gpio_config_t io_conf = {}; |
| 107 | + io_conf.intr_type = GPIO_INTR_LOW_LEVEL; |
| 108 | + io_conf.pin_bit_mask = (1ULL << static_cast<uint8_t>(mGpioNum)); |
| 109 | + io_conf.mode = GPIO_MODE_INPUT; |
| 110 | + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; |
| 111 | + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; |
| 112 | + // configure GPIO with the given settings |
| 113 | + gpio_config(&io_conf); |
| 114 | + if (!sEventQueue) |
| 115 | + { |
| 116 | + // create a queue to handle gpio event from isr |
| 117 | + sEventQueue = xQueueCreate(10, sizeof(UatButton *)); |
| 118 | + if (!sEventQueue) |
| 119 | + { |
| 120 | + ESP_LOGE(TAG, "Failed to create GPIO EventQueue"); |
| 121 | + return; |
| 122 | + } |
| 123 | + } |
| 124 | + if (!sTaskHandle) |
| 125 | + { |
| 126 | + // start gpio task |
| 127 | + xTaskCreate(RunEventLoop, "UatButton", 4096, nullptr, 10, &sTaskHandle); |
| 128 | + if (!sTaskHandle) |
| 129 | + { |
| 130 | + ESP_LOGE(TAG, "Failed to create GPIO Task"); |
| 131 | + return; |
| 132 | + } |
| 133 | + } |
| 134 | + // install gpio isr service |
| 135 | + gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); |
| 136 | + // hook isr handler for specific gpio pin |
| 137 | + gpio_isr_handler_add(mGpioNum, gpio_isr_handler, (void *) this); |
| 138 | + ESP_LOGI(TAG, "UAT Button initialized.."); |
| 139 | + // Configure RTC IO wake up |
| 140 | + esp_sleep_enable_ext1_wakeup(1ULL << static_cast<uint8_t>(mGpioNum), wakeupMode); |
| 141 | +#if SOC_RTCIO_INPUT_OUTPUT_SUPPORTED |
| 142 | + rtc_gpio_pulldown_dis(mGpioNum); |
| 143 | + rtc_gpio_pullup_en(mGpioNum); |
| 144 | +#else |
| 145 | + gpio_pulldown_dis(mGpioNum); |
| 146 | + gpio_pullup_en(mGpioNum); |
| 147 | +#endif |
| 148 | +} |
0 commit comments