/**************************************************************************//** * @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved. * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes * 2020-12-12 Wayne First version * ******************************************************************************/ #include #if defined(BSP_USING_ADC) #include #include "NuMicro.h" #include "drv_sys.h" #include "nu_bitutil.h" #include "drv_adc.h" /* Private define ---------------------------------------------------------------*/ #define DEF_ADC_TOUCH_SMPL_TICK 40 /* Private Typedef --------------------------------------------------------------*/ struct nu_adc { struct rt_adc_device dev; char *name; uint32_t OpFreqKHz; IRQn_Type irqn; E_SYS_IPRST rstidx; E_SYS_IPCLK clkidx; int chn_num; uint32_t chn_mask; rt_sem_t m_psSem; #if defined(BSP_USING_ADC_TOUCH) rt_touch_t psRtTouch; rt_timer_t psRtTouchMenuTimer; rt_mq_t m_pmqTouchXYZ; #endif nu_adc_cb m_isr[eAdc_ISR_CNT]; nu_adc_cb m_wkisr[eAdc_WKISR_CNT]; }; typedef struct nu_adc *nu_adc_t; #if defined(BSP_USING_ADC_TOUCH) struct nu_adc_touch_data { uint32_t u32X; uint32_t u32Y; uint32_t u32Z0; uint32_t u32Z1; }; typedef struct nu_adc_touch_data *nu_adc_touch_data_t; #endif /* Private functions ------------------------------------------------------------*/ static rt_err_t nu_adc_enabled(struct rt_adc_device *device, rt_uint32_t channel, rt_bool_t enabled); static rt_err_t nu_adc_convert(struct rt_adc_device *device, rt_uint32_t channel, rt_uint32_t *value); static rt_err_t _nu_adc_control(rt_device_t dev, int cmd, void *args); /* Public functions ------------------------------------------------------------*/ int rt_hw_adc_init(void); /* Private variables ------------------------------------------------------------*/ static struct nu_adc g_sNuADC = { .name = "adc", .OpFreqKHz = 4000, /* 1000 <= OpFreqKHz <= 4000 */ .chn_num = 8, .irqn = IRQ_ADC, .rstidx = ADCRST, .clkidx = ADCCKEN, .chn_mask = 0 }; static void nu_adc_isr(int vector, void *param) { rt_int32_t isr, wkisr; nu_adc_t psNuAdc = (nu_adc_t)param; rt_int32_t irqidx; isr = inpw(REG_ADC_ISR); wkisr = inpw(REG_ADC_WKISR); while ((irqidx = nu_ctz(isr)) < eAdc_ISR_CNT) { uint32_t u32IsrBitMask = 1 << irqidx ; if (psNuAdc->m_isr[irqidx].cbfunc != RT_NULL) { //rt_kprintf("[%s] %d %x\n", __func__, irqidx, psNuAdc->m_isr[irqidx].cbfunc); psNuAdc->m_isr[irqidx].cbfunc(isr, psNuAdc->m_isr[irqidx].private_data); } /* Clear sent bit */ outpw(REG_ADC_ISR, u32IsrBitMask); isr &= ~(u32IsrBitMask); } //while while ((irqidx = nu_ctz(wkisr)) < eAdc_WKISR_CNT) { uint32_t u32IsrBitMask = 1 << irqidx ; if (psNuAdc->m_wkisr[irqidx].cbfunc != RT_NULL) { psNuAdc->m_wkisr[irqidx].cbfunc(wkisr, psNuAdc->m_wkisr[irqidx].private_data); } /* Clear sent bit */ outpw(REG_ADC_WKISR, u32IsrBitMask); wkisr &= ~(u32IsrBitMask); } //while } static rt_err_t _nu_adc_init(rt_device_t dev) { uint32_t div; nu_adc_t psNuAdc = (nu_adc_t)dev; /* ADC Engine Clock is set to freq Khz */ if (psNuAdc->OpFreqKHz > 4000) psNuAdc->OpFreqKHz = 4000; if (psNuAdc->OpFreqKHz < 1000) psNuAdc->OpFreqKHz = 1000; div = 12000 / psNuAdc->OpFreqKHz; outpw(REG_CLK_DIVCTL7, inpw(REG_CLK_DIVCTL7) & ~((0x3 << 19) | (0x7 << 16) | (0xFFul << 24))); outpw(REG_CLK_DIVCTL7, (0 << 19) | (0 << 16) | ((div - 1) << 24)); /* Install interrupt service routine */ rt_hw_interrupt_install(psNuAdc->irqn, nu_adc_isr, (void *)psNuAdc, psNuAdc->name); return RT_EOK; } static int32_t AdcMenuStartCallback(uint32_t status, uint32_t userData) { nu_adc_t psNuAdc = (nu_adc_t)userData; #if defined(BSP_USING_ADC_TOUCH) static struct nu_adc_touch_data point; static rt_bool_t bDrop = RT_FALSE; static uint32_t u32LastZ0 = 0xffffu; if (psNuAdc->psRtTouch != RT_NULL) { uint32_t value; value = inpw(REG_ADC_XYDATA); point.u32X = (value & 0x0ffful); point.u32Y = ((value >> 16) & 0x0ffful); value = inpw(REG_ADC_ZDATA); point.u32Z0 = (value & 0x0ffful); point.u32Z1 = ((value >> 16) & 0x0ffful); /* Trigger next or not. */ if (point.u32Z0 == 0) { /* Stop sampling procedure. */ rt_timer_stop(g_sNuADC.psRtTouchMenuTimer); /* Re-start pendown detection */ nu_adc_touch_detect(RT_TRUE); bDrop = RT_TRUE; } else { bDrop = RT_FALSE; } /* Notify upper layer. */ if ((!bDrop || (u32LastZ0 != 0)) && rt_mq_send(psNuAdc->m_pmqTouchXYZ, (const void *)&point, sizeof(struct nu_adc_touch_data)) == RT_EOK) { rt_hw_touch_isr(psNuAdc->psRtTouch); } u32LastZ0 = point.u32Z0; } else #endif { rt_err_t result = rt_sem_release(psNuAdc->m_psSem); RT_ASSERT(result == RT_EOK); } return 0; } #if defined(BSP_USING_ADC_TOUCH) void nu_adc_touch_detect(rt_bool_t bStartDetect) { nu_adc_t psNuAdc = (nu_adc_t)&g_sNuADC; if (bStartDetect) { /* Start detect PenDown */ _nu_adc_control((rt_device_t)psNuAdc, PEPOWER_ON, RT_NULL); } else { /* Stop detect PenDown */ _nu_adc_control((rt_device_t)psNuAdc, PEPOWER_OFF, RT_NULL); } } static int32_t PenDownCallback(uint32_t status, uint32_t userData) { nu_adc_touch_detect(RT_FALSE); rt_timer_start(g_sNuADC.psRtTouchMenuTimer); return 0; } int32_t nu_adc_touch_read_xyz(uint32_t *bufX, uint32_t *bufY, uint32_t *bufZ0, uint32_t *bufZ1, int32_t dataCnt) { int i; struct nu_adc_touch_data value; for (i = 0 ; i < dataCnt; i++) { if (rt_mq_recv(g_sNuADC.m_pmqTouchXYZ, (void *)&value, sizeof(struct nu_adc_touch_data), 0) == -RT_ETIMEOUT) break; bufX[i] = value.u32X; bufY[i] = value.u32Y; bufZ0[i] = value.u32Z0; bufZ1[i] = value.u32Z1; } return i; } void nu_adc_touch_start_conv(void) { nu_adc_t psNuAdc = (nu_adc_t)&g_sNuADC; _nu_adc_control((rt_device_t)psNuAdc, START_MST, RT_NULL); } rt_err_t nu_adc_touch_enable(rt_touch_t psRtTouch) { nu_adc_t psNuAdc = (nu_adc_t)&g_sNuADC; nu_adc_cb sNuAdcCb; rt_adc_enable((rt_adc_device_t)psNuAdc, 4); rt_adc_enable((rt_adc_device_t)psNuAdc, 5); rt_adc_enable((rt_adc_device_t)psNuAdc, 6); rt_adc_enable((rt_adc_device_t)psNuAdc, 7); outpw(REG_ADC_CONF, (inpw(REG_ADC_CONF) & ~(0xfful << 24)) | 0xfful << 24); /* Register touch device. */ psNuAdc->psRtTouch = psRtTouch; /* Enable TouchXY. */ _nu_adc_control((rt_device_t)psNuAdc, T_ON, RT_NULL); /* Enable TouchZZ. */ _nu_adc_control((rt_device_t)psNuAdc, Z_ON, RT_NULL); /* Register PenDown callback. */ sNuAdcCb.cbfunc = PenDownCallback; sNuAdcCb.private_data = (rt_uint32_t)psRtTouch; _nu_adc_control((rt_device_t)psNuAdc, PEDEF_ON, (void *)&sNuAdcCb); nu_adc_touch_detect(RT_TRUE); return RT_EOK; } rt_err_t nu_adc_touch_disable(void) { nu_adc_t psNuAdc = (nu_adc_t)&g_sNuADC; nu_adc_touch_detect(RT_FALSE); _nu_adc_control((rt_device_t)psNuAdc, T_OFF, RT_NULL); _nu_adc_control((rt_device_t)psNuAdc, Z_OFF, RT_NULL); _nu_adc_control((rt_device_t)psNuAdc, PEDEF_OFF, RT_NULL); rt_adc_disable((rt_adc_device_t)psNuAdc, 4); rt_adc_disable((rt_adc_device_t)psNuAdc, 5); rt_adc_disable((rt_adc_device_t)psNuAdc, 6); rt_adc_disable((rt_adc_device_t)psNuAdc, 7); return RT_EOK; } #endif static rt_err_t _nu_adc_control(rt_device_t dev, int cmd, void *args) { rt_err_t ret = RT_EINVAL ; nu_adc_t psNuAdc = (nu_adc_t)dev; nu_adc_cb_t psAdcCb = (nu_adc_cb_t)args; switch (cmd) { case START_MST: /* Menu Start Conversion */ { /* Enable interrupt */ outpw(REG_ADC_IER, inpw(REG_ADC_IER) | ADC_IER_MIEN); /* Start conversion */ outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) | ADC_CTL_MST); /* Wait it done */ ret = rt_sem_take(psNuAdc->m_psSem, RT_WAITING_FOREVER); RT_ASSERT(ret == RT_EOK); /* Get data: valid data is 12-bit */ if (args != RT_NULL) *((uint32_t *)args) = inpw(REG_ADC_DATA) & 0x00000FFF; } break; /* case START_MST_POLLING: Not supported. */ case VBPOWER_ON: /* Enable ADC Internal Bandgap Power */ { outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) | ADC_CTL_VBGEN); } break; case VBPOWER_OFF: /* Disable ADC Internal Bandgap Power */ { outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) & ~ADC_CTL_VBGEN); } break; case KPPOWER_ON: /* Enable ADC Keypad Power */ { outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) | ADC_CTL_PWKPEN); } break; case KPPOWER_OFF: /* Disable ADC Keypad Power */ { outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) & ~ADC_CTL_PWKPEN); } break; case PEPOWER_ON: /* Enable Pen Power */ { int retry = 100; uint32_t treg = inpw(REG_ADC_IER); outpw(REG_ADC_IER, treg & ~(ADC_IER_PEDEIEN | ADC_IER_PEUEIEN)); outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) | ADC_CTL_PEDEEN); do { outpw(REG_ADC_ISR, ADC_ISR_PEDEF | ADC_ISR_PEUEF); rt_thread_mdelay(1); if (retry-- == 0) break; } while (inpw(REG_ADC_ISR) & (ADC_ISR_PEDEF | ADC_ISR_PEUEF)); outpw(REG_ADC_IER, treg); } break; case PEPOWER_OFF: /* Disable Pen Power */ { outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) & ~ADC_CTL_PEDEEN); } break; case KPPRESS_ON: /* Enable Keypad press event */ { if (psAdcCb) { rt_memcpy(&psNuAdc->m_isr[eAdc_KPEF], psAdcCb, sizeof(nu_adc_cb)); } outpw(REG_ADC_IER, inpw(REG_ADC_IER) | ADC_IER_KPEIEN); } break; case KPPRESS_OFF: /* Disable Keypad press event */ { outpw(REG_ADC_IER, inpw(REG_ADC_IER & ~ADC_IER_KPEIEN)); } break; case KPUP_ON: /* Enable Keypad up event */ { if (psAdcCb) { rt_memcpy(&psNuAdc->m_isr[eAdc_KPUEF], psAdcCb, sizeof(nu_adc_cb)); } outpw(REG_ADC_IER, inpw(REG_ADC_IER) | ADC_IER_KPUEIEN); } break; case KPUP_OFF: /* Disable Keypad up event */ { outpw(REG_ADC_IER, inpw(REG_ADC_IER) & ~ADC_IER_KPUEIEN); } break; case PEDEF_ON: /* Enable Pen Down Event */ { if (psAdcCb) { rt_memcpy(&psNuAdc->m_isr[eAdc_PEDEF], psAdcCb, sizeof(nu_adc_cb)); } outpw(REG_ADC_IER, inpw(REG_ADC_IER) | ADC_IER_PEDEIEN); } break; case PEDEF_OFF: /* Disable Pen Down Event */ { outpw(REG_ADC_IER, inpw(REG_ADC_IER) & ~ADC_IER_PEDEIEN); } break; case WKP_ON: /* Enable Keypad Press Wake Up */ { if (psAdcCb) { rt_memcpy(&psNuAdc->m_wkisr[eAdc_WKPEF], psAdcCb, sizeof(nu_adc_cb)); } outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) | ADC_CTL_WKPEN); outpw(REG_ADC_IER, inpw(REG_ADC_IER) | ADC_IER_WKPIEN); //outpw(REG_SYS_WKUPSER, inpw(REG_SYS_WKUPSER) | (1 << 26)); } break; case WKP_OFF: /* Disable Keypad Press Wake Up */ { outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) & ~ADC_CTL_WKPEN); outpw(REG_ADC_IER, inpw(REG_ADC_IER) & ~ADC_IER_WKPIEN); //outpw(REG_SYS_WKUPSER, inpw(REG_SYS_WKUPSER) & ~(1 << 26)); } break; case WKT_ON: /* Enable Touch Wake Up */ { if (psAdcCb) { rt_memcpy(&psNuAdc->m_wkisr[eAdc_WPEDEF], psAdcCb, sizeof(nu_adc_cb)); } outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) | ADC_CTL_WKTEN); outpw(REG_ADC_IER, inpw(REG_ADC_IER) | ADC_IER_WKTIEN); //outpw(REG_SYS_WKUPSER, inpw(REG_SYS_WKUPSER) | (1 << 26)); } break; case WKT_OFF: /* Disable Touch Wake Up */ { outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) & ~ADC_CTL_WKTEN); outpw(REG_ADC_IER, inpw(REG_ADC_IER) & ~ADC_IER_WKTIEN); //outpw(REG_SYS_WKUPSER, inpw(REG_SYS_WKUPSER) & ~(1 << 26)); } break; case SWITCH_5WIRE_ON: /* Wire Mode Switch to 5-Wire */ { outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) | ADC_CTL_WMSWCH); } break; case SWITCH_5WIRE_OFF: /* Wire Mode Switch to 4-Wire */ { outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) & ~ADC_CTL_WMSWCH); } break; case T_ON: /* Enable Touch detection function */ { outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) | ADC_CONF_TEN); } break; case T_OFF: /* Disable Touch detection function */ { outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) & ~ADC_CONF_TEN); } break; case TAVG_ON: /* Enable Touch Mean average for X and Y function */ { outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) | ADC_CONF_DISTMAVEN); } break; case TAVG_OFF: /* Disable Touch Mean average for X and Y function */ { outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) & ~ADC_CONF_DISTMAVEN); } break; case Z_ON: /* Enable Press measure function */ { outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) | ADC_CONF_ZEN); } break; case Z_OFF: /* Disable Press measure function */ { outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) & ~ADC_CONF_ZEN); #if defined(BSP_USING_ADC_TOUCH) rt_mq_control(psNuAdc->m_pmqTouchXYZ, RT_IPC_CMD_RESET, RT_NULL); #endif } break; case TZAVG_ON: /* Enable Pressure Mean average for Z1 and Z2 function */ { outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) | ADC_CONF_DISZMAVEN); } break; case TZAVG_OFF: /* Disable Pressure Mean average for Z1 and Z2 function */ { outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) & ~ADC_CONF_DISZMAVEN); } break; case NAC_ON: /* Enable Normal AD Conversion */ { outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) | ADC_CONF_NACEN | ADC_CONF_REFSEL_AVDD33); } break; case NAC_OFF: /* Disable Normal AD Conversion */ { outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) & ~ADC_CONF_NACEN); } break; case VBAT_ON: /* Enable Voltage Battery Conversion */ { if (psAdcCb) { rt_memcpy(&psNuAdc->m_isr[eAdc_VBF], psAdcCb, sizeof(nu_adc_cb)); } outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) | ADC_CONF_VBATEN); } break; case VBAT_OFF: /* Disable Voltage Battery */ { outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) & ~ADC_CONF_VBATEN); } break; case KPCONV_ON: /* Enable Keypad conversion function */ { if (psAdcCb) { rt_memcpy(&psNuAdc->m_isr[eAdc_KPCF], psAdcCb, sizeof(nu_adc_cb)); } outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) | ADC_CONF_KPCEN); outpw(REG_ADC_IER, inpw(REG_ADC_IER) | ADC_IER_KPEIEN); } break; case KPCONV_OFF: /* Disable Keypad conversion function */ { outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) & ~ADC_CONF_KPCEN); } break; case SWITCH_CH: { int chn = (int)args; if (chn >= psNuAdc->chn_num) { return -ret; } outpw(REG_ADC_CONF, (inpw(REG_ADC_CONF) & ~ADC_CONF_CHSEL_Msk) | (chn << ADC_CONF_CHSEL_Pos)); } break; default: return -(ret); } return RT_EOK; } static rt_err_t _nu_adc_open(rt_device_t dev, rt_uint16_t oflag) { nu_adc_t psNuAdc = (nu_adc_t)dev; /* Enable ADC engine clock */ nu_sys_ipclk_enable(psNuAdc->clkidx); /* Reset the ADC IP */ nu_sys_ip_reset(psNuAdc->rstidx); /* Enable ADC Power */ outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) | ADC_CTL_ADEN); /* Enable ADC to high speed mode */ outpw(REG_ADC_CONF, inpw(REG_ADC_CONF) | ADC_CONF_HSPEED); /* Enable interrupt */ rt_hw_interrupt_umask(psNuAdc->irqn); /* Enable Normal AD Conversion */ _nu_adc_control(dev, NAC_ON, RT_NULL); return RT_EOK; } static rt_err_t _nu_adc_close(rt_device_t dev) { nu_adc_t psNuAdc = (nu_adc_t)dev; /* Disable Normal AD Conversion */ _nu_adc_control(dev, NAC_OFF, RT_NULL); /* Disable interrupt */ rt_hw_interrupt_mask(psNuAdc->irqn); /* Disable ADC Power */ outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) & ~ADC_CTL_ADEN); /* Disable ADC engine clock */ nu_sys_ipclk_disable(psNuAdc->clkidx); return RT_EOK; } static const struct rt_adc_ops nu_adc_ops = { nu_adc_enabled, nu_adc_convert, }; /* nu_adc_enabled - Enable ADC clock and wait for ready */ static rt_err_t nu_adc_enabled(struct rt_adc_device *device, rt_uint32_t channel, rt_bool_t enabled) { nu_adc_t psNuADC = (nu_adc_t)device; RT_ASSERT(device != RT_NULL); if (channel >= psNuADC->chn_num) return -(RT_EINVAL); if (enabled) { psNuADC->chn_mask |= (1 << channel); } else { psNuADC->chn_mask &= ~(1 << channel); } if (psNuADC->chn_mask > 0 && ((rt_device_t)device)->ref_count == 0) { _nu_adc_open((rt_device_t)device, 0); ((rt_device_t)device)->ref_count = 1; } else if ((psNuADC->chn_mask == 0) && ((rt_device_t)device)->ref_count == 1) { _nu_adc_close((rt_device_t)device); ((rt_device_t)device)->ref_count = 0; } return RT_EOK; } static rt_err_t nu_adc_convert(struct rt_adc_device *device, rt_uint32_t channel, rt_uint32_t *value) { rt_err_t ret = RT_EOK; nu_adc_t psNuAdc = (nu_adc_t)device; RT_ASSERT(device != RT_NULL); RT_ASSERT(value != RT_NULL); if (channel >= psNuAdc->chn_num) { ret = RT_EINVAL; goto exit_nu_adc_convert; } else if ((ret = _nu_adc_control((rt_device_t)device, SWITCH_CH, (void *)channel)) != RT_EOK) { goto exit_nu_adc_convert; } else if ((ret = _nu_adc_control((rt_device_t)device, START_MST, (void *)value)) != RT_EOK) { goto exit_nu_adc_convert; } exit_nu_adc_convert: return (-ret) ; } static void nu_adc_touch_smpl(void *p) { /* Enable interrupt */ outpw(REG_ADC_IER, inpw(REG_ADC_IER) | ADC_IER_MIEN); /* Start conversion */ outpw(REG_ADC_CTL, inpw(REG_ADC_CTL) | ADC_CTL_MST); } int rt_hw_adc_init(void) { rt_err_t result = RT_ERROR; rt_device_t psDev = &g_sNuADC.dev.parent; result = rt_hw_adc_register(&g_sNuADC.dev, g_sNuADC.name, &nu_adc_ops, &g_sNuADC); RT_ASSERT(result == RT_EOK); result = _nu_adc_init(psDev); RT_ASSERT(result == RT_EOK); g_sNuADC.m_psSem = rt_sem_create("adc_mst_sem", 0, RT_IPC_FLAG_FIFO); RT_ASSERT(g_sNuADC.m_psSem != RT_NULL); #if defined(BSP_USING_ADC_TOUCH) g_sNuADC.m_pmqTouchXYZ = rt_mq_create("ADC_TOUCH_XYZ", sizeof(struct nu_adc_touch_data), TOUCH_MQ_LENGTH, RT_IPC_FLAG_FIFO); RT_ASSERT(g_sNuADC.m_pmqTouchXYZ != RT_NULL); 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); RT_ASSERT(g_sNuADC.psRtTouchMenuTimer != RT_NULL); #endif rt_memset(&g_sNuADC.m_isr, 0, sizeof(g_sNuADC.m_isr)); rt_memset(&g_sNuADC.m_wkisr, 0, sizeof(g_sNuADC.m_wkisr)); g_sNuADC.m_isr[eAdc_MF].cbfunc = AdcMenuStartCallback; g_sNuADC.m_isr[eAdc_MF].private_data = (UINT32)&g_sNuADC; return (int)result; } INIT_BOARD_EXPORT(rt_hw_adc_init); #endif //#if defined(BSP_USING_ADC)