1 /*
2  * Copyright (c) 2015 - 2025, Nordic Semiconductor ASA
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice, this
11  *    list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its
18  *    contributors may be used to endorse or promote products derived from this
19  *    software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <nrfx.h>
35 
36 #if NRFX_CHECK(NRFX_QDEC_ENABLED)
37 
38 #include <nrfx_qdec.h>
39 
40 #if !NRFX_FEATURE_PRESENT(NRFX_QDEC, _ENABLED)
41 #error "No enabled QDEC instances. Check <nrfx_config.h>."
42 #endif
43 
44 #include <haly/nrfy_gpio.h>
45 
46 #define NRFX_LOG_MODULE QDEC
47 #include <nrfx_log.h>
48 
49 #define EVT_TO_STR(event)                                             \
50     (event == NRF_QDEC_EVENT_SAMPLERDY ? "NRF_QDEC_EVENT_SAMPLERDY" : \
51     (event == NRF_QDEC_EVENT_REPORTRDY ? "NRF_QDEC_EVENT_REPORTRDY" : \
52     (event == NRF_QDEC_EVENT_ACCOF     ? "NRF_QDEC_EVENT_ACCOF"     : \
53                                          "UNKNOWN EVENT")))
54 
55 // Control block - driver instance local data.
56 typedef struct
57 {
58     nrfx_drv_state_t          state;
59     bool                      skip_gpio_cfg;
60     nrfx_qdec_event_handler_t handler;
61     void *                    p_context;
62 } qdec_control_block_t;
63 
64 static qdec_control_block_t m_cb[NRFX_QDEC_ENABLED_COUNT];
65 
qdec_configure(nrfx_qdec_t const * p_instance,nrfx_qdec_config_t const * p_config)66 static void qdec_configure(nrfx_qdec_t const *        p_instance,
67                            nrfx_qdec_config_t const * p_config)
68 {
69     if (!p_config->skip_gpio_cfg)
70     {
71         nrfy_gpio_cfg_input(p_config->psela, NRF_GPIO_PIN_NOPULL);
72         nrfy_gpio_cfg_input(p_config->pselb, NRF_GPIO_PIN_NOPULL);
73         if (p_config->pselled != NRF_QDEC_PIN_NOT_CONNECTED)
74         {
75             nrfy_gpio_cfg_input(p_config->pselled, NRF_GPIO_PIN_NOPULL);
76         }
77     }
78 
79     nrfy_qdec_config_t nrfy_config =
80     {
81         .reportper = p_config->reportper,
82         .sampleper = p_config->sampleper,
83         .pins = {
84             .a_pin   = p_config->psela,
85             .b_pin   = p_config->pselb,
86             .led_pin = p_config->pselled
87         },
88         .ledpre    = p_config->ledpre,
89         .ledpol    = p_config->ledpol,
90         .dbfen     = p_config->dbfen,
91         .skip_psel_cfg = p_config->skip_psel_cfg
92     };
93 
94     nrfy_qdec_periph_configure(p_instance->p_reg, &nrfy_config);
95 
96     uint32_t int_mask = NRF_QDEC_INT_ACCOF_MASK;
97 
98     if (p_config->reportper_inten)
99     {
100         int_mask |= NRF_QDEC_INT_REPORTRDY_MASK;
101         nrfy_qdec_shorts_enable(p_instance->p_reg, NRF_QDEC_SHORT_REPORTRDY_READCLRACC_MASK);
102     }
103     if (p_config->sample_inten)
104     {
105         int_mask |= NRF_QDEC_INT_SAMPLERDY_MASK;
106     }
107     nrfy_qdec_int_init(p_instance->p_reg, int_mask, p_config->interrupt_priority, true);
108 }
109 
nrfx_qdec_init(nrfx_qdec_t const * p_instance,nrfx_qdec_config_t const * p_config,nrfx_qdec_event_handler_t handler,void * p_context)110 nrfx_err_t nrfx_qdec_init(nrfx_qdec_t const *        p_instance,
111                           nrfx_qdec_config_t const * p_config,
112                           nrfx_qdec_event_handler_t  handler,
113                           void *                     p_context)
114 {
115     NRFX_ASSERT(p_config);
116     NRFX_ASSERT(handler);
117 
118     qdec_control_block_t * const p_cb = &m_cb[p_instance->drv_inst_idx];
119 
120     nrfx_err_t err_code;
121 
122     if (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED)
123     {
124 #if NRFX_API_VER_AT_LEAST(3, 2, 0)
125         err_code = NRFX_ERROR_ALREADY;
126 #else
127         err_code = NRFX_ERROR_INVALID_STATE;
128 #endif
129         NRFX_LOG_WARNING("Function: %s, error code: %s.",
130                          __func__,
131                          NRFX_LOG_ERROR_STRING_GET(err_code));
132         return err_code;
133     }
134 
135     p_cb->handler = handler;
136     p_cb->p_context = p_context;
137 
138     if (p_config)
139     {
140         p_cb->skip_gpio_cfg = p_config->skip_gpio_cfg;
141         qdec_configure(p_instance, p_config);
142     }
143 
144     p_cb->state = NRFX_DRV_STATE_INITIALIZED;
145 
146     err_code = NRFX_SUCCESS;
147     NRFX_LOG_INFO("Function: %s, error code: %s.", __func__, NRFX_LOG_ERROR_STRING_GET(err_code));
148     return err_code;
149 }
150 
nrfx_qdec_reconfigure(nrfx_qdec_t const * p_instance,nrfx_qdec_config_t const * p_config)151 nrfx_err_t nrfx_qdec_reconfigure(nrfx_qdec_t const *        p_instance,
152                                  nrfx_qdec_config_t const * p_config)
153 {
154     NRFX_ASSERT(p_config);
155     qdec_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
156 
157     if (p_cb->state == NRFX_DRV_STATE_UNINITIALIZED)
158     {
159         return NRFX_ERROR_INVALID_STATE;
160     }
161     if (p_cb->state == NRFX_DRV_STATE_POWERED_ON)
162     {
163         return NRFX_ERROR_BUSY;
164     }
165     qdec_configure(p_instance, p_config);
166     nrfy_qdec_enable(p_instance->p_reg);
167     return NRFX_SUCCESS;
168 }
169 
nrfx_qdec_uninit(nrfx_qdec_t const * p_instance)170 void nrfx_qdec_uninit(nrfx_qdec_t const * p_instance)
171 {
172     qdec_control_block_t * const p_cb = &m_cb[p_instance->drv_inst_idx];
173     nrfy_qdec_pins_t pins;
174 
175     NRFX_ASSERT(p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
176 
177     nrfy_qdec_disable(p_instance->p_reg);
178     nrfy_qdec_int_disable(p_instance->p_reg, 0xFFFFFFFF);
179     nrfy_qdec_int_uninit(p_instance->p_reg);
180 
181     nrfy_qdec_shorts_disable(p_instance->p_reg, NRF_QDEC_SHORT_REPORTRDY_READCLRACC_MASK);
182     if (!p_cb->skip_gpio_cfg)
183     {
184         nrfy_qdec_pins_get(p_instance->p_reg, &pins);
185         nrfy_gpio_cfg_default(pins.a_pin);
186         nrfy_gpio_cfg_default(pins.b_pin);
187 
188         uint32_t led_pin = nrfy_qdec_led_pin_get(p_instance->p_reg);
189         if (led_pin != NRF_QDEC_PIN_NOT_CONNECTED)
190         {
191             nrfy_gpio_cfg_default(led_pin);
192         }
193     }
194 
195     p_cb->state = NRFX_DRV_STATE_UNINITIALIZED;
196     NRFX_LOG_INFO("Uninitialized.");
197 }
198 
nrfx_qdec_init_check(nrfx_qdec_t const * p_instance)199 bool nrfx_qdec_init_check(nrfx_qdec_t const * p_instance)
200 {
201     qdec_control_block_t * p_cb = &m_cb[p_instance->drv_inst_idx];
202 
203     return (p_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
204 }
205 
nrfx_qdec_enable(nrfx_qdec_t const * p_instance)206 void nrfx_qdec_enable(nrfx_qdec_t const * p_instance)
207 {
208     qdec_control_block_t * const p_cb = &m_cb[p_instance->drv_inst_idx];
209 
210     NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_INITIALIZED);
211 
212     nrfy_qdec_enable(p_instance->p_reg);
213     nrfy_qdec_task_trigger(p_instance->p_reg, NRF_QDEC_TASK_START);
214     p_cb->state = NRFX_DRV_STATE_POWERED_ON;
215     NRFX_LOG_INFO("Enabled.");
216 }
217 
nrfx_qdec_disable(nrfx_qdec_t const * p_instance)218 void nrfx_qdec_disable(nrfx_qdec_t const * p_instance)
219 {
220     qdec_control_block_t * const p_cb = &m_cb[p_instance->drv_inst_idx];
221 
222     NRFX_ASSERT(p_cb->state == NRFX_DRV_STATE_POWERED_ON);
223     nrfy_qdec_task_trigger(p_instance->p_reg, NRF_QDEC_TASK_STOP);
224     nrfy_qdec_disable(p_instance->p_reg);
225     p_cb->state = NRFX_DRV_STATE_INITIALIZED;
226     NRFX_LOG_INFO("Disabled.");
227 }
228 
nrfx_qdec_accumulators_read(nrfx_qdec_t const * p_instance,int32_t * p_acc,uint32_t * p_accdbl)229 void nrfx_qdec_accumulators_read(nrfx_qdec_t const * p_instance,
230                                  int32_t *           p_acc,
231                                  uint32_t *          p_accdbl)
232 {
233     NRFX_ASSERT(p_accdbl);
234     NRFX_ASSERT(p_acc);
235     NRFX_ASSERT(m_cb[p_instance->drv_inst_idx].state == NRFX_DRV_STATE_POWERED_ON);
236 
237     nrfy_qdec_task_trigger(p_instance->p_reg, NRF_QDEC_TASK_READCLRACC);
238     nrfy_qdec_accumulators_read(p_instance->p_reg, p_acc, p_accdbl);
239 
240     NRFX_LOG_DEBUG("Accumulators data, ACC register:");
241     NRFX_LOG_HEXDUMP_DEBUG((uint8_t *)p_acc, sizeof(p_acc[0]));
242     NRFX_LOG_DEBUG("Accumulators data, ACCDBL register:");
243     NRFX_LOG_HEXDUMP_DEBUG((uint8_t *)p_accdbl, sizeof(p_accdbl[0]));
244 }
245 
irq_handler(NRF_QDEC_Type * p_qdec,qdec_control_block_t * p_cb)246 static void irq_handler(NRF_QDEC_Type * p_qdec, qdec_control_block_t * p_cb)
247 {
248     uint32_t evt_to_process;
249     nrfx_qdec_event_t event;
250     uint32_t evt_mask;
251     uint32_t all_evt_mask;
252 
253     all_evt_mask = NRFY_EVENT_TO_INT_BITMASK(NRF_QDEC_EVENT_SAMPLERDY) |
254                    NRFY_EVENT_TO_INT_BITMASK(NRF_QDEC_EVENT_REPORTRDY) |
255                    NRFY_EVENT_TO_INT_BITMASK(NRF_QDEC_EVENT_ACCOF);
256 
257     evt_to_process = nrfy_qdec_int_enable_check(p_qdec, all_evt_mask);
258     evt_mask = nrfy_qdec_events_process(p_qdec, evt_to_process);
259 
260     if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_QDEC_EVENT_SAMPLERDY))
261     {
262         NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_QDEC_EVENT_SAMPLERDY));
263 
264         event.type = NRF_QDEC_EVENT_SAMPLERDY;
265         event.data.sample.value = (int8_t)nrfy_qdec_sample_get(p_qdec);
266         p_cb->handler(event, p_cb->p_context);
267     }
268 
269     if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_QDEC_EVENT_REPORTRDY))
270     {
271         NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_QDEC_EVENT_REPORTRDY));
272 
273         event.type = NRF_QDEC_EVENT_REPORTRDY;
274         nrfy_qdec_accumulators_read(p_qdec, &event.data.report.acc, &event.data.report.accdbl);
275         p_cb->handler(event, p_cb->p_context);
276     }
277 
278     if (evt_mask & NRFY_EVENT_TO_INT_BITMASK(NRF_QDEC_EVENT_ACCOF))
279     {
280         NRFX_LOG_DEBUG("Event: %s.", EVT_TO_STR(NRF_QDEC_EVENT_ACCOF));
281 
282         event.type = NRF_QDEC_EVENT_ACCOF;
283         p_cb->handler(event, p_cb->p_context);
284     }
285 }
286 
287 NRFX_INSTANCE_IRQ_HANDLERS(QDEC, qdec)
288 
289 #endif // NRFX_CHECK(NRFX_QDEC_ENABLED)
290