1 /*
2  * Copyright (c) 2006-2024, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2022-05-16     shelton      first version
9  */
10 
11 #include "drv_adc.h"
12 #include "fsl_lpadc.h"
13 #include "fsl_spc.h"
14 #include "fsl_common.h"
15 
16 #if defined(BSP_USING_ADC0) || defined(BSP_USING_ADC1)
17 
18 #define DEFAULT_HW_AVG          (kLPADC_HardwareAverageCount4)
19 #define DEFAULT_SAMPLE_TIME     (kLPADC_SampleTimeADCK7)
20 
21 
22 /* by default: cmd = chl+1 */
23 static uint8_t adc_chl2cmd[] =  {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
24 static uint8_t adc_cmd2trig[] = {0, 1, 2 ,3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3};
25 
26 struct mcx_adc
27 {
28     struct rt_adc_device        mcx_adc_device;
29     ADC_Type                   *adc_base;
30     clock_attach_id_t           clock_attach_id;
31     clock_div_name_t            clock_div_name;
32     uint8_t                     clock_div;
33     uint8_t                     referenceVoltageSource; /* 00, VREFH reference pin, 01, ANA_7(VREFI/VREFO) pin, 10, VDDA supply pin */
34     char *name;
35 };
36 
37 static struct mcx_adc mcx_adc_obj[] =
38 {
39 #ifdef BSP_USING_ADC0
40     {
41         .adc_base = ADC0,
42         .clock_attach_id = kCLK_IN_to_ADC0,
43         .clock_div_name = kCLOCK_DivAdc0Clk,
44         .clock_div = 2,
45         .referenceVoltageSource = 0,
46         .name = "adc0",
47     },
48 #endif
49 
50 };
51 
at32_adc_enabled(struct rt_adc_device * device,rt_int8_t channel,rt_bool_t enabled)52 static rt_err_t at32_adc_enabled(struct rt_adc_device *device, rt_int8_t channel, rt_bool_t enabled)
53 {
54     struct mcx_adc *adc = (struct mcx_adc *)device->parent.user_data;
55 
56     lpadc_conv_command_config_t cmd_cfg;
57     LPADC_GetDefaultConvCommandConfig(&cmd_cfg);
58 
59     cmd_cfg.channelNumber = channel;
60     cmd_cfg.conversionResolutionMode = kLPADC_ConversionResolutionHigh;
61     cmd_cfg.hardwareAverageMode = DEFAULT_HW_AVG;
62     cmd_cfg.loopCount = 0;
63     cmd_cfg.sampleTimeMode = DEFAULT_SAMPLE_TIME;
64 /*
65     kLPADC_SampleChannelSingleEndSideA = 0U,
66     kLPADC_SampleChannelSingleEndSideB = 1U,
67     kLPADC_SampleChannelDiffBothSide = 2U,
68     kLPADC_SampleChannelDualSingleEndBothSide =
69 */
70     cmd_cfg.sampleChannelMode = kLPADC_SampleChannelSingleEndSideA;
71     LPADC_SetConvCommandConfig(adc->adc_base, adc_chl2cmd[channel], &cmd_cfg);
72 
73     lpadc_conv_trigger_config_t trig_config;
74     LPADC_GetDefaultConvTriggerConfig(&trig_config);
75     trig_config.targetCommandId       = adc_chl2cmd[channel];
76     trig_config.enableHardwareTrigger = false;
77     LPADC_SetConvTriggerConfig(adc->adc_base, adc_cmd2trig[trig_config.targetCommandId], &trig_config); /* Configurate the trigger0. */
78 
79     return RT_EOK;
80 }
81 
at32_get_adc_value(struct rt_adc_device * device,rt_int8_t channel,rt_uint32_t * value)82 static rt_err_t at32_get_adc_value(struct rt_adc_device *device, rt_int8_t channel, rt_uint32_t *value)
83 {
84     struct mcx_adc *adc = (struct mcx_adc *)device->parent.user_data;
85 
86     lpadc_conv_result_t mLpadcResultConfigStruct;
87 
88     LPADC_DoSoftwareTrigger(adc->adc_base, 1<<(adc_cmd2trig[adc_chl2cmd[channel]])); /* 1U is trigger0 mask. */
89     while (!LPADC_GetConvResult(adc->adc_base, &mLpadcResultConfigStruct, 0))
90     {
91     }
92 
93 
94     *value = mLpadcResultConfigStruct.convValue;
95     return RT_EOK;
96 }
97 
98 static const struct rt_adc_ops mcx_adc_ops =
99 {
100     .enabled = at32_adc_enabled,
101     .convert = at32_get_adc_value,
102 };
103 
rt_hw_adc_init(void)104 static int rt_hw_adc_init(void)
105 {
106     int result = RT_EOK;
107     int i = 0;
108 
109     /* Enable VREF */
110     SPC0->ACTIVE_CFG1 |= 0xFFFFFFFF;
111     SPC_SetActiveModeBandgapModeConfig(SPC0, kSPC_BandgapEnabledBufferEnabled);
112 
113     for (i = 0; i < sizeof(mcx_adc_obj) / sizeof(mcx_adc_obj[0]); i++)
114     {
115         CLOCK_SetClkDiv(mcx_adc_obj[i].clock_div_name, mcx_adc_obj[i].clock_div);
116         CLOCK_AttachClk(mcx_adc_obj[i].clock_attach_id);
117 
118         lpadc_config_t adc_config;
119         LPADC_GetDefaultConfig(&adc_config);
120         adc_config.enableAnalogPreliminary = true;
121         adc_config.referenceVoltageSource = mcx_adc_obj[i].referenceVoltageSource;
122         adc_config.conversionAverageMode = kLPADC_ConversionAverage128; /* this is for calibartion avg mode */
123         adc_config.powerLevelMode = kLPADC_PowerLevelAlt4;
124         adc_config.enableConvPause       = false;
125         adc_config.convPauseDelay        = 0;
126 
127         LPADC_Init(mcx_adc_obj[i].adc_base, &adc_config);
128         LPADC_DoOffsetCalibration(mcx_adc_obj[i].adc_base);
129         LPADC_DoAutoCalibration(mcx_adc_obj[i].adc_base);
130 
131         rt_hw_adc_register(&mcx_adc_obj[i].mcx_adc_device, mcx_adc_obj[i].name, &mcx_adc_ops, &mcx_adc_obj[i]);
132     }
133 
134     return result;
135 }
136 INIT_BOARD_EXPORT(rt_hw_adc_init);
137 
138 #endif /* BSP_USING_ADC */
139