1 /**************************************************************************//**
2 * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Change Logs:
7 * Date Author Notes
8 * 2020-12-12 Wayne First version
9 *
10 ******************************************************************************/
11
12 #include <rtconfig.h>
13
14 #if defined(BSP_USING_ADC)
15
16 #include <rtdevice.h>
17 #include "NuMicro.h"
18 #include "drv_sys.h"
19 #include "nu_bitutil.h"
20 #include "drv_adc.h"
21
22 /* Private define --------------------------------------------------------------- */
23 #define DEF_ADC_TOUCH_SMPL_TICK 40
24 #define TOUCH_MQ_LENGTH 64
25
26 /* Private Typedef -------------------------------------------------------------- */
27 struct nu_adc
28 {
29 struct rt_adc_device dev;
30 char *name;
31 ADC_T *base;
32 uint32_t bReset;
33 IRQn_Type irqn;
34 E_SYS_IPRST rstidx;
35 E_SYS_IPCLK clkidx;
36 uint32_t chn_mask;
37 rt_sem_t m_psSem;
38
39 #if defined(BSP_USING_ADC_TOUCH)
40 rt_touch_t psRtTouch;
41 rt_timer_t psRtTouchMenuTimer;
42 rt_mq_t m_pmqTouchXYZ;
43 #endif
44
45 nu_adc_cb m_isr[eAdc_ISR_CNT];
46 nu_adc_cb m_wkisr[eAdc_WKISR_CNT];
47 };
48 typedef struct nu_adc *nu_adc_t;
49
50 #if defined(BSP_USING_ADC_TOUCH)
51 struct nu_adc_touch_data
52 {
53 uint32_t u32X;
54 uint32_t u32Y;
55 uint32_t u32Z0;
56 uint32_t u32Z1;
57 };
58 typedef struct nu_adc_touch_data *nu_adc_touch_data_t;
59 #endif
60
61 /* Private functions ------------------------------------------------------------ */
62 static rt_err_t nu_adc_enabled(struct rt_adc_device *device, rt_int8_t channel, rt_bool_t enabled);
63 static rt_err_t nu_adc_convert(struct rt_adc_device *device, rt_int8_t channel, rt_uint32_t *value);
64 static rt_err_t _nu_adc_control(rt_device_t dev, int cmd, void *args);
65
66 /* Public functions ------------------------------------------------------------ */
67 int rt_hw_adc_init(void);
68
69 /* Private variables ------------------------------------------------------------ */
70
71 static struct nu_adc g_sNuADC =
72 {
73 .name = "adc",
74 .base = (ADC_T *)ADC_BA,
75 .bReset = 1,
76 .irqn = IRQ_ADC,
77 .rstidx = ADCRST,
78 .clkidx = ADCCKEN,
79 .chn_mask = 0
80 };
81
nu_adc_isr(int vector,void * param)82 static void nu_adc_isr(int vector, void *param)
83 {
84 volatile rt_int32_t isr, wkisr;
85 nu_adc_t psNuAdc = (nu_adc_t)param;
86 rt_int32_t irqidx;
87 ADC_T *adc = psNuAdc->base;
88
89 /* rt_kprintf("[%s %d] CTL: %08x CONF:%08x IER:%08x ISR:%08x\n", __func__, __LINE__, adc->CTL, adc->CONF, adc->IER, adc->ISR); */
90
91 isr = adc->ISR;
92 wkisr = adc->WKISR;
93
94 adc->ISR = isr;
95
96 while ((irqidx = nu_ctz(isr)) < eAdc_ISR_CNT)
97 {
98 uint32_t u32IsrBitMask = 1 << irqidx ;
99
100 if (psNuAdc->m_isr[irqidx].cbfunc != RT_NULL)
101 {
102 /* rt_kprintf("[%s] %d %x\n", __func__, irqidx, psNuAdc->m_isr[irqidx].cbfunc); */
103 psNuAdc->m_isr[irqidx].cbfunc(isr, psNuAdc->m_isr[irqidx].private_data);
104 }
105
106 /* Clear sent bit */
107 isr &= ~(u32IsrBitMask);
108 } /* while */
109
110 while ((irqidx = nu_ctz(wkisr)) < eAdc_WKISR_CNT)
111 {
112 uint32_t u32IsrBitMask = 1 << irqidx ;
113
114 if (psNuAdc->m_wkisr[irqidx].cbfunc != RT_NULL)
115 {
116 psNuAdc->m_wkisr[irqidx].cbfunc(wkisr, psNuAdc->m_wkisr[irqidx].private_data);
117 }
118
119 wkisr &= ~(u32IsrBitMask);
120 } /* while */
121 }
122
123
124 #define DEF_ADC_SRC_CLOCK_DIV (12000 / 1000)
_nu_adc_init(rt_device_t dev)125 static rt_err_t _nu_adc_init(rt_device_t dev)
126 {
127 nu_adc_t psNuAdc = (nu_adc_t)dev;
128
129 /* Set ADC Engine Clock */
130 outpw(REG_CLK_DIVCTL7, inpw(REG_CLK_DIVCTL7) & ~((0x3 << 19) | (0x7 << 16) | (0xFFul << 24)));
131 outpw(REG_CLK_DIVCTL7, (0 << 19) | (0 << 16) | ((DEF_ADC_SRC_CLOCK_DIV - 1) << 24));
132
133 /* Install interrupt service routine */
134 rt_hw_interrupt_install(psNuAdc->irqn, nu_adc_isr, (void *)psNuAdc, psNuAdc->name);
135
136 return RT_EOK;
137 }
138
139 #define ADC_TOUCH_Z0_ACTIVE 20
AdcMenuStartCallback(uint32_t status,uint32_t userData)140 static int32_t AdcMenuStartCallback(uint32_t status, uint32_t userData)
141 {
142 nu_adc_t psNuAdc = (nu_adc_t)userData;
143
144 #if defined(BSP_USING_ADC_TOUCH)
145 ADC_T *adc = psNuAdc->base;
146 static struct nu_adc_touch_data point;
147 static rt_bool_t bDrop = RT_FALSE;
148 static uint32_t u32LastZ0 = 0xffffu;
149
150 if (psNuAdc->psRtTouch != RT_NULL)
151 {
152 point.u32X = ADC_GET_CONVERSION_XDATA(adc);
153 point.u32Y = ADC_GET_CONVERSION_YDATA(adc);
154
155 point.u32Z0 = ADC_GET_CONVERSION_Z1DATA(adc);
156 point.u32Z1 = ADC_GET_CONVERSION_Z2DATA(adc);
157
158 /* rt_kprintf("x=%d y=%d z0=%d z1=%d\n", point.u32X, point.u32Y, point.u32Z0, point.u32Z1); */
159 /* Trigger next or not. */
160 if (point.u32Z0 < ADC_TOUCH_Z0_ACTIVE)
161 {
162 /* Stop sampling procedure. */
163 rt_timer_stop(g_sNuADC.psRtTouchMenuTimer);
164
165 /* Re-start pendown detection */
166 nu_adc_touch_detect(RT_TRUE);
167
168 psNuAdc->bReset = 1;
169
170 bDrop = RT_TRUE;
171 }
172 else
173 {
174 bDrop = RT_FALSE;
175 }
176
177 /* Notify upper layer. */
178 if ((!bDrop || (u32LastZ0 > ADC_TOUCH_Z0_ACTIVE)) && rt_mq_send(psNuAdc->m_pmqTouchXYZ, (const void *)&point, sizeof(struct nu_adc_touch_data)) == RT_EOK)
179 {
180 rt_hw_touch_isr(psNuAdc->psRtTouch);
181 }
182
183 u32LastZ0 = point.u32Z0;
184 }
185 else
186 #endif
187 {
188 rt_err_t result = rt_sem_release(psNuAdc->m_psSem);
189 RT_ASSERT(result == RT_EOK);
190 }
191
192 return 0;
193 }
194
195 #if defined(BSP_USING_ADC_TOUCH)
196
nu_adc_touch_antiglitch(ADC_T * adc)197 static void nu_adc_touch_antiglitch(ADC_T *adc)
198 {
199 int count = 10;
200 do
201 {
202 rt_hw_us_delay(1000); /* 1ms */
203 ADC_CLR_INT_FLAG(adc, adc->ISR);
204 if (adc->ISR == 0)
205 break;
206 }
207 while (count-- > 0);
208 }
209
nu_adc_touch_detect(rt_bool_t bStartDetect)210 void nu_adc_touch_detect(rt_bool_t bStartDetect)
211 {
212 nu_adc_t psNuAdc = (nu_adc_t)&g_sNuADC;
213 ADC_T *adc = psNuAdc->base;
214
215 /* Disable interrupt */
216 rt_hw_interrupt_mask(psNuAdc->irqn);
217
218 ADC_POWER_DOWN(adc);
219
220 /* Disable interrupt */
221 ADC_DISABLE_INT(adc, ADC_IER_PEDEIEN_Msk | ADC_IER_MIEN_Msk);
222 nu_adc_touch_antiglitch(adc);
223
224 if (bStartDetect == RT_TRUE)
225 {
226 /* Switch to PenDown detection mode */
227 ADC_DETECT_PD_MODE(adc);
228 nu_adc_touch_antiglitch(adc);
229
230 /* Enable interrupt */
231 ADC_ENABLE_INT(adc, ADC_IER_PEDEIEN_Msk);
232 }
233 else
234 {
235 /* Switch to XY coordination converting mode */
236 ADC_CONVERT_XY_MODE(adc);
237 nu_adc_touch_antiglitch(adc);
238
239 /* Enable interrupt */
240 ADC_ENABLE_INT(adc, ADC_IER_MIEN_Msk);
241 }
242
243 ADC_POWER_ON(adc);
244
245 /* Enable interrupt */
246 rt_hw_interrupt_umask(psNuAdc->irqn);
247 }
248
PenDownCallback(uint32_t status,uint32_t userData)249 static int32_t PenDownCallback(uint32_t status, uint32_t userData)
250 {
251 nu_adc_t psNuAdc = (nu_adc_t)userData;
252 return rt_timer_start(psNuAdc->psRtTouchMenuTimer);
253 }
254
nu_adc_touch_read_xyz(uint32_t * bufX,uint32_t * bufY,uint32_t * bufZ0,uint32_t * bufZ1,int32_t dataCnt)255 int32_t nu_adc_touch_read_xyz(uint32_t *bufX, uint32_t *bufY, uint32_t *bufZ0, uint32_t *bufZ1, int32_t dataCnt)
256 {
257 int i;
258 struct nu_adc_touch_data value;
259
260 for (i = 0 ; i < dataCnt; i++)
261 {
262 if (rt_mq_recv(g_sNuADC.m_pmqTouchXYZ, (void *)&value, sizeof(struct nu_adc_touch_data), 0) == -RT_ETIMEOUT)
263 break;
264
265 bufX[i] = value.u32X;
266 bufY[i] = value.u32Y;
267 bufZ0[i] = value.u32Z0;
268 bufZ1[i] = value.u32Z1;
269 }
270 return i;
271 }
272
nu_adc_touch_enable(rt_touch_t psRtTouch)273 rt_err_t nu_adc_touch_enable(rt_touch_t psRtTouch)
274 {
275 nu_adc_t psNuAdc = (nu_adc_t)&g_sNuADC;
276 nu_adc_cb sNuAdcCb;
277 ADC_T *adc = psNuAdc->base;
278
279 adc->CONF = 0x0;
280
281 rt_adc_enable((rt_adc_device_t)psNuAdc, 4); /* Channel number 4 */
282 rt_adc_enable((rt_adc_device_t)psNuAdc, 5); /* Channel number 5 */
283 rt_adc_enable((rt_adc_device_t)psNuAdc, 6); /* Channel number 6 */
284 rt_adc_enable((rt_adc_device_t)psNuAdc, 7); /* Channel number 7 */
285
286 /* Register touch device. */
287 psNuAdc->psRtTouch = psRtTouch;
288
289 /* Register PenDown callback. */
290 sNuAdcCb.cbfunc = PenDownCallback;
291 sNuAdcCb.private_data = (rt_uint32_t)psNuAdc;
292 rt_memcpy(&psNuAdc->m_isr[eAdc_PEDEF], &sNuAdcCb, sizeof(nu_adc_cb));
293
294 nu_adc_touch_detect(RT_TRUE);
295
296 return RT_EOK;
297 }
298
nu_adc_touch_disable(void)299 rt_err_t nu_adc_touch_disable(void)
300 {
301 nu_adc_t psNuAdc = (nu_adc_t)&g_sNuADC;
302
303 nu_adc_touch_detect(RT_FALSE);
304
305 _nu_adc_control((rt_device_t)psNuAdc, T_OFF, RT_NULL);
306 _nu_adc_control((rt_device_t)psNuAdc, Z_OFF, RT_NULL);
307 _nu_adc_control((rt_device_t)psNuAdc, PEDEF_OFF, RT_NULL);
308
309 rt_adc_disable((rt_adc_device_t)psNuAdc, 4); /* Channel number 4 */
310 rt_adc_disable((rt_adc_device_t)psNuAdc, 5); /* Channel number 5 */
311 rt_adc_disable((rt_adc_device_t)psNuAdc, 6); /* Channel number 6 */
312 rt_adc_disable((rt_adc_device_t)psNuAdc, 7); /* Channel number 7 */
313
314 return RT_EOK;
315 }
316
nu_adc_touch_smpl(void * p)317 static void nu_adc_touch_smpl(void *p)
318 {
319 nu_adc_t psNuAdc = (nu_adc_t)p;
320 if (psNuAdc->bReset)
321 {
322 psNuAdc->bReset = 0;
323 nu_adc_touch_detect(RT_FALSE);
324 }
325
326 /* Start conversion */
327 ADC_START_CONV(psNuAdc->base);
328 }
329 #endif
330
_nu_adc_control(rt_device_t dev,int cmd,void * args)331 static rt_err_t _nu_adc_control(rt_device_t dev, int cmd, void *args)
332 {
333 rt_err_t ret = -RT_EINVAL ;
334 nu_adc_t psNuAdc = (nu_adc_t)dev;
335 ADC_T *adc = psNuAdc->base;
336
337 nu_adc_cb_t psAdcCb = (nu_adc_cb_t)args;
338
339 switch (cmd)
340 {
341 case START_MST: /* Menu Start Conversion */
342 {
343 /* Enable interrupt */
344 ADC_ENABLE_INT(adc, ADC_IER_MIEN_Msk);
345
346 /* Start conversion */
347 ADC_START_CONV(adc);
348
349 /* Wait it done */
350 ret = rt_sem_take(psNuAdc->m_psSem, RT_WAITING_FOREVER);
351 RT_ASSERT(ret == RT_EOK);
352
353 /* Get data: valid data is 12-bit */
354 if (args != RT_NULL)
355 *((uint32_t *)args) = ADC_GET_CONVERSION_DATA(adc, 0);
356 }
357 break;
358
359 case WKT_ON: /* Enable Touch Wake Up */
360 {
361 if (psAdcCb)
362 {
363 rt_memcpy(&psNuAdc->m_wkisr[eAdc_WPEDEF], psAdcCb, sizeof(nu_adc_cb));
364 }
365 adc->CTL |= ADC_CTL_WKTEN_Msk;
366 adc->IER |= ADC_IER_WKTIEN_Msk;
367
368 /* TODO outpw(REG_SYS_WKUPSER, inpw(REG_SYS_WKUPSER) | (1 << 26)); */
369 }
370 break;
371
372 case WKT_OFF: /* Disable Touch Wake Up */
373 {
374 adc->CTL &= ~ADC_CTL_WKTEN_Msk;
375 adc->IER &= ~ADC_IER_WKTIEN_Msk;
376
377 /* TODO outpw(REG_SYS_WKUPSER, inpw(REG_SYS_WKUPSER) & ~(1 << 26)); */
378 }
379 break;
380
381 case SWITCH_5WIRE_ON: /* Wire Mode Switch to 5-Wire */
382 {
383 adc->CTL |= ADC_CTL_WMSWCH_Msk;
384 }
385 break;
386
387 case SWITCH_5WIRE_OFF: /* Wire Mode Switch to 4-Wire */
388 {
389 adc->CTL &= ~ADC_CTL_WMSWCH_Msk;
390 }
391 break;
392
393 case T_ON: /* Enable Touch detection function */
394 {
395 adc->CONF |= ADC_CONF_TEN_Msk;
396 }
397 break;
398
399 case T_OFF: /* Disable Touch detection function */
400 {
401 adc->CONF &= ~ADC_CONF_TEN_Msk;
402 }
403 break;
404
405 case TAVG_ON: /* Enable Touch Mean average for X and Y function */
406 {
407 adc->CONF |= ADC_CONF_TMAVDIS_Msk;
408 }
409 break;
410
411 case TAVG_OFF: /* Disable Touch Mean average for X and Y function */
412 {
413 adc->CONF &= ~ADC_CONF_TMAVDIS_Msk;
414 }
415 break;
416
417 case Z_ON: /* Enable Press measure function */
418 {
419 adc->CONF |= ADC_CONF_ZEN_Msk;
420 }
421 break;
422
423 case Z_OFF: /* Disable Press measure function */
424 {
425 adc->CONF &= ~ADC_CONF_ZEN_Msk;
426 #if defined(BSP_USING_ADC_TOUCH)
427 rt_mq_control(psNuAdc->m_pmqTouchXYZ, RT_IPC_CMD_RESET, RT_NULL);
428 #endif
429 }
430 break;
431
432 case TZAVG_ON: /* Enable Pressure Mean average for Z1 and Z2 function */
433 {
434 adc->CONF |= ADC_CONF_ZMAVDIS_Msk;
435 }
436 break;
437
438 case TZAVG_OFF: /* Disable Pressure Mean average for Z1 and Z2 function */
439 {
440 adc->CONF &= ~ADC_CONF_ZMAVDIS_Msk;
441 }
442 break;
443
444 case NAC_ON: /* Enable Normal AD Conversion */
445 {
446 adc->CONF |= (ADC_CONF_NACEN_Msk | ADC_CONF_REFSEL_AVDD33);
447 }
448 break;
449
450 case NAC_OFF: /* Disable Normal AD Conversion */
451 {
452 adc->CONF &= ~ADC_CONF_NACEN_Msk;
453 }
454 break;
455
456 case SWITCH_CH:
457 {
458 int chn = (int)args;
459 if (chn >= ADC_CH_NUM)
460 {
461 return -ret;
462 }
463 adc->CONF &= ~ADC_CONF_CHSEL_Msk;
464 adc->CONF |= (chn << ADC_CONF_CHSEL_Pos);
465 }
466 break;
467
468 default:
469 return -(ret);
470 }
471
472 return RT_EOK;
473 }
474
_nu_adc_open(rt_device_t dev,rt_uint16_t oflag)475 static rt_err_t _nu_adc_open(rt_device_t dev, rt_uint16_t oflag)
476 {
477 nu_adc_t psNuAdc = (nu_adc_t)dev;
478 ADC_T *adc = psNuAdc->base;
479
480 /* Enable ADC engine clock */
481 nu_sys_ipclk_enable(psNuAdc->clkidx);
482
483 /* Reset the ADC IP */
484 nu_sys_ip_reset(psNuAdc->rstidx);
485
486 /* Enable ADC Power */
487 ADC_POWER_ON(adc);
488
489 /* Enable ADC to high speed mode */
490 adc->CONF |= ADC_CONF_SPEED_Msk;
491
492 /* Enable interrupt */
493 rt_hw_interrupt_umask(psNuAdc->irqn);
494
495 /* Enable Normal AD Conversion */
496 _nu_adc_control(dev, NAC_ON, RT_NULL);
497
498 return RT_EOK;
499 }
500
_nu_adc_close(rt_device_t dev)501 static rt_err_t _nu_adc_close(rt_device_t dev)
502 {
503 nu_adc_t psNuAdc = (nu_adc_t)dev;
504 ADC_T *adc = psNuAdc->base;
505
506 /* Disable Normal AD Conversion */
507 _nu_adc_control(dev, NAC_OFF, RT_NULL);
508
509 /* Disable interrupt */
510 rt_hw_interrupt_mask(psNuAdc->irqn);
511
512 /* Disable ADC Power */
513 ADC_POWER_DOWN(adc);
514
515 /* Disable ADC engine clock */
516 nu_sys_ipclk_disable(psNuAdc->clkidx);
517
518 return RT_EOK;
519 }
520
521 static const struct rt_adc_ops nu_adc_ops =
522 {
523 nu_adc_enabled,
524 nu_adc_convert,
525 };
526
527 /* nu_adc_enabled - Enable ADC clock and wait for ready */
nu_adc_enabled(struct rt_adc_device * device,rt_int8_t channel,rt_bool_t enabled)528 static rt_err_t nu_adc_enabled(struct rt_adc_device *device, rt_int8_t channel, rt_bool_t enabled)
529 {
530 nu_adc_t psNuADC = (nu_adc_t)device;
531 RT_ASSERT(device);
532
533 if (channel >= ADC_CH_NUM)
534 return -(RT_EINVAL);
535
536 if (enabled)
537 {
538 psNuADC->chn_mask |= (1 << channel);
539 }
540 else
541 {
542 psNuADC->chn_mask &= ~(1 << channel);
543 }
544
545 if (psNuADC->chn_mask > 0 && ((rt_device_t)device)->ref_count == 0)
546 {
547 _nu_adc_open((rt_device_t)device, 0);
548 ((rt_device_t)device)->ref_count = 1;
549 }
550 else if ((psNuADC->chn_mask == 0) && ((rt_device_t)device)->ref_count == 1)
551 {
552 _nu_adc_close((rt_device_t)device);
553 ((rt_device_t)device)->ref_count = 0;
554 }
555 return RT_EOK;
556 }
557
nu_adc_convert(struct rt_adc_device * device,rt_int8_t channel,rt_uint32_t * value)558 static rt_err_t nu_adc_convert(struct rt_adc_device *device, rt_int8_t channel, rt_uint32_t *value)
559 {
560 rt_err_t ret = RT_EOK;
561
562 RT_ASSERT(device);
563 RT_ASSERT(value);
564
565 if (channel >= ADC_CH_NUM)
566 {
567 ret = -RT_EINVAL;
568 goto exit_nu_adc_convert;
569 }
570 else if ((ret = _nu_adc_control((rt_device_t)device, SWITCH_CH, (void *)(intptr_t)channel)) != RT_EOK)
571 {
572 goto exit_nu_adc_convert;
573 }
574 else if ((ret = _nu_adc_control((rt_device_t)device, START_MST, (void *)value)) != RT_EOK)
575 {
576 goto exit_nu_adc_convert;
577 }
578
579 exit_nu_adc_convert:
580
581 return (-ret) ;
582 }
583
rt_hw_adc_init(void)584 int rt_hw_adc_init(void)
585 {
586 rt_err_t result = -RT_ERROR;
587 rt_device_t psDev = &g_sNuADC.dev.parent;
588
589 result = rt_hw_adc_register(&g_sNuADC.dev, g_sNuADC.name, &nu_adc_ops, &g_sNuADC);
590 RT_ASSERT(result == RT_EOK);
591
592 result = _nu_adc_init(psDev);
593 RT_ASSERT(result == RT_EOK);
594
595 g_sNuADC.m_psSem = rt_sem_create("adc_mst_sem", 0, RT_IPC_FLAG_FIFO);
596 RT_ASSERT(g_sNuADC.m_psSem);
597
598 #if defined(BSP_USING_ADC_TOUCH)
599 g_sNuADC.m_pmqTouchXYZ = rt_mq_create("ADC_TOUCH_XYZ", sizeof(struct nu_adc_touch_data), TOUCH_MQ_LENGTH, RT_IPC_FLAG_FIFO);
600 RT_ASSERT(g_sNuADC.m_pmqTouchXYZ);
601
602 g_sNuADC.psRtTouchMenuTimer = rt_timer_create("TOUCH_SMPL_TIMER", nu_adc_touch_smpl, (void *)&g_sNuADC, DEF_ADC_TOUCH_SMPL_TICK, RT_TIMER_FLAG_PERIODIC);
603 RT_ASSERT(g_sNuADC.psRtTouchMenuTimer);
604 #endif
605
606 rt_memset(&g_sNuADC.m_isr, 0, sizeof(g_sNuADC.m_isr));
607 rt_memset(&g_sNuADC.m_wkisr, 0, sizeof(g_sNuADC.m_wkisr));
608
609 g_sNuADC.m_isr[eAdc_MF].cbfunc = AdcMenuStartCallback;
610 g_sNuADC.m_isr[eAdc_MF].private_data = (uint32_t)&g_sNuADC;
611
612 return (int)result;
613 }
614 INIT_BOARD_EXPORT(rt_hw_adc_init);
615
616 #endif /* #if defined(BSP_USING_ADC) */
617