1 | // espy Copyright (c) 2023 Josh Stockin <josh@joshstock.in> |
2 | // [https://joshstock.in] [https://github.com/joshuas3] |
3 | // |
4 | // This software is licensed and distributed under the terms of the MIT License. |
5 | // See the MIT License in the LICENSE file of this project's root folder. |
6 | // |
7 | // This comment block and its contents, including this disclaimer, MUST be |
8 | // preserved in all copies or distributions of this software's source. |
9 |
|
10 |
|
11 | // Filename: input.c |
12 | // Description: Handlers for radio receiver, rotary encoder |
13 |
|
14 |
|
15 | #include "esp_attr.h" |
16 | #include "esp_err.h" |
17 | #include "esp_check.h" |
18 | #include "esp_log.h" |
19 | #include "freertos/FreeRTOS.h" |
20 | #include "freertos/queue.h" |
21 | #include "driver/gpio.h" |
22 | #include "driver/gptimer.h" |
23 |
|
24 | #include "input.h" |
25 | #include "pins.h" |
26 |
|
27 |
|
28 | static const char* TAG = "input.c"; |
29 |
|
30 | static uint32_t current_radio_channel = 0; |
31 | static uint32_t radio_channels[NUM_RADIO_CHANNELS] = {0}; |
32 | static QueueHandle_t xInputQueue; |
33 | gptimer_handle_t pulse_timer = NULL; |
34 |
|
35 | esp_err_t setup_radio_and_rotenc_input( void ) { |
36 |
|
37 | ESP_LOGI( TAG, "creating input queue" ); |
38 |
|
39 | xInputQueue = xQueueCreate( 10, sizeof( xInputEvent* ) ); // xInputEvent from input.h |
40 | ESP_RETURN_ON_FALSE( xInputQueue != 0, ESP_FAIL, TAG, "xQueueCreate failed" ); |
41 |
|
42 | ESP_LOGI( TAG, "configuring RADIO_PPM pin for input, pull_down_en" ); |
43 |
|
44 | gpio_config_t radio_pin_conf = { |
45 | .pin_bit_mask = (1ULL << RADIO_PPM), |
46 | .mode = GPIO_MODE_INPUT, |
47 | .pull_down_en = 1, |
48 | .pull_up_en = 0, |
49 | }; |
50 | ESP_RETURN_ON_ERROR( gpio_config( &radio_pin_conf ), TAG, "gpio_config failed" ); |
51 |
|
52 | ESP_LOGI( TAG, "creating positive edge RADIO_PPM interrupt for radio_pulse callback" ); |
53 |
|
54 | ESP_RETURN_ON_ERROR( gpio_set_intr_type( RADIO_PPM, GPIO_INTR_POSEDGE ), TAG, "gpio_set_intr_type failed" ); |
55 | ESP_RETURN_ON_ERROR( gpio_isr_handler_add( RADIO_PPM, radio_pulse, NULL ), TAG, "gpio_isr_handler_add failed" ); |
56 | ESP_RETURN_ON_ERROR( gpio_intr_enable( RADIO_PPM ), TAG, "gpio_intr_enable failed" ); |
57 |
|
58 | ESP_RETURN_ON_ERROR( gpio_wakeup_enable( RADIO_PPM, GPIO_INTR_POSEDGE ), TAG, "gpio_wakeup_enable failed" ); |
59 |
|
60 | gptimer_config_t timer_config = { |
61 | .clk_src = GPTIMER_CLK_SRC_DEFAULT, |
62 | .direction = GPTIMER_COUNT_UP, |
63 | .resolution_hz = 1000000, // 1MHz, microsecond precision |
64 | }; |
65 | ESP_RETURN_ON_ERROR( gptimer_new_timer( &timer_config, &pulse_timer ), TAG, "gpitimer_new_timer failed" ); |
66 | ESP_RETURN_ON_ERROR( gptimer_enable( pulse_timer ), TAG, "gptimer_enable failed" ); |
67 | ESP_RETURN_ON_ERROR( gptimer_start( pulse_timer ), TAG, "gptimer_enable failed" ); |
68 |
|
69 | TaskHandle_t input_task = NULL; |
70 | xTaskCreate( input_handler_task, "input_handler_task", 400, NULL, 2, &input_task ); // stack size 400 bytes, priority 2 |
71 |
|
72 | return ESP_OK; |
73 |
|
74 | } |
75 |
|
76 | void IRAM_ATTR radio_pulse( void* arg ) { |
77 |
|
78 | // get pulse timer value |
79 | uint64_t offset = 0; |
80 | gptimer_get_raw_count( pulse_timer, &offset ); |
81 |
|
82 | // insert timer value into queue |
83 | xInputEvent pulse_event = { |
84 | .type = INPUT_EVENT_RADIOPULSE, |
85 | .data = offset, |
86 | }; |
87 | xQueueSendFromISR( xInputQueue, &pulse_event, NULL ); |
88 |
|
89 | // reset timer |
90 | gptimer_set_raw_count( pulse_timer, 0 ); |
91 |
|
92 | // debug log event |
93 | ESP_DRAM_LOGD( TAG, "INTR: received radio pulse (offset %lu), queued", offset ); |
94 |
|
95 | } |
96 |
|
97 | void input_handler_task( void* arg ) { |
98 | // clears input event queue |
99 |
|
100 | xInputEvent event; |
101 | for ( ;; ) { |
102 | while ( xQueueReceive( xInputQueue, &event, 0 ) ) { |
103 |
|
104 | if ( event.type == INPUT_EVENT_RADIOPULSE ) { |
105 | // ppm radio receiver handling |
106 |
|
107 | if ( event.data >= RADIO_PULSE_STOPGAP_US ) { |
108 |
|
109 | // pulse gap is larger than designated stopgap time, |
110 | // indicates end of a stopgap (beginning of transmission); |
111 | // reset data |
112 | current_radio_channel = 0; |
113 | for ( int i = 0; i < NUM_RADIO_CHANNELS; i++ ) { |
114 | radio_channels[i] = 0; |
115 | } |
116 |
|
117 | } else { |
118 |
|
119 | if ( ++current_radio_channel >= NUM_RADIO_CHANNELS ) { |
120 | // surpassed number of expected pulses without a stopgap; |
121 | // something's wrong, continue until next stopgap |
122 | continue; |
123 | } |
124 |
|
125 | // valid data; since the time between the Nth pulse and the |
126 | // Nth-1 pulse indicates the value of channel N-1, set it |
127 | // accordingly |
128 | radio_channels[ current_radio_channel - 1 ] = event.data; |
129 |
|
130 | ESP_LOGD( TAG, "radio channel %lu: %lu", current_radio_channel, event.data ); |
131 | } |
132 |
|
133 | } else if ( event.type == INPUT_EVENT_ROTENCSW ) { |
134 |
|
135 | // TODO |
136 |
|
137 | } else if ( event.type == INPUT_EVENT_ROTENCTURN ) { |
138 |
|
139 | // TODO |
140 |
|
141 | } |
142 |
|
143 | } |
144 |
|
145 | taskYIELD(); // queue is empty, don't waste anymore processor time |
146 | } |
147 |
|
148 | } |
149 |
|