1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2017-09-18     Haley        the first version
9  */
10 
11 #include <rtthread.h>
12 #include <rtdevice.h>
13 #include "am_mcu_apollo.h"
14 
15 #ifdef RT_USING_ADC
16 
17 /* messagequeue define */
18 struct rt_messagequeue adcbat_mq;
19 
20 #define BATTERY_GPIO            35                        /* Battery */
21 #define BATTERY_ADC_PIN         AM_HAL_PIN_35_ADCSE7
22 #define BATTERY_ADC_CHANNEL     AM_HAL_ADC_SLOT_CHSEL_SE7 /* BATTERY ADC采集通道 */
23 #define BATTERY_ADC_CHANNELNUM  7                         /* BATTERY ADC采集通道号 */
24 
25 #define ADC_CTIMER_NUM          3                         /* ADC使用定时器 */
26 #define ADC_CTIMER_COUNT        (2048/512 - 1)
27 
28 #define ADC_CHANNEL_NUM         1                         /* ADC采集通道个数 */
29 #define ADC_SAMPLE_NUM          8                         /* ADC采样个数 */
30 
31 rt_uint8_t bat_adc_cnt = 0;
32 static rt_uint8_t am_adcbat_buffer_pool[256];
33 static rt_int16_t am_adcbat_buffertemp[32];
34 
am_adc_data_get(rt_uint8_t channel,rt_int16_t * buff,rt_uint16_t size)35 rt_uint8_t am_adc_data_get(rt_uint8_t channel, rt_int16_t *buff, rt_uint16_t size)
36 {
37     rt_uint8_t adc_bufftemp[32];
38 
39     if (channel == BATTERY_ADC_CHANNELNUM)
40     {
41         /* wait adc message forever */
42         rt_mq_recv(&adcbat_mq, adc_bufftemp, 32, RT_WAITING_FOREVER);
43     }
44 
45     /* copy the data */
46     rt_memcpy(buff, adc_bufftemp, size*sizeof(rt_int16_t));
47 
48     return 0;
49 }
50 
am_adc_start(rt_uint8_t channel)51 void am_adc_start(rt_uint8_t channel)
52 {
53     /* messagequeue init */
54     rt_mq_init(&adcbat_mq, "mq_adcbat",
55             &am_adcbat_buffer_pool[0],
56             32 - sizeof(void*),
57             sizeof(am_adcbat_buffer_pool),
58             RT_IPC_FLAG_FIFO);
59 
60     /* Start the ctimer */
61     am_hal_ctimer_start(ADC_CTIMER_NUM, AM_HAL_CTIMER_TIMERA);
62 
63     /* Trigger the ADC once */
64     am_hal_adc_trigger();
65 }
66 
am_adc_stop(rt_uint8_t channel)67 void am_adc_stop(rt_uint8_t channel)
68 {
69     /* Stop the ctimer */
70     am_hal_ctimer_stop(ADC_CTIMER_NUM, AM_HAL_CTIMER_TIMERA);
71 
72     /* messagequeue delete */
73     rt_mq_delete(&adceeg_mq);
74 
75     /* messagequeue delete */
76     rt_mq_delete(&adcbat_mq);
77 }
78 
79 /**
80  * @brief Interrupt handler for the ADC
81  *
82  * This function is Interrupt handler for the ADC
83  *
84  * @return None.
85  */
am_adc_isr(void)86 void am_adc_isr(void)
87 {
88     uint32_t ui32Status, ui32FifoData;
89 
90     /* enter interrupt */
91     rt_interrupt_enter();
92 
93     /* Read the interrupt status */
94     ui32Status = am_hal_adc_int_status_get(true);
95 
96     /* Clear the ADC interrupt */
97     am_hal_adc_int_clear(ui32Status);
98 
99     /* If we got a FIFO 75% full (which should be our only ADC interrupt), go ahead and read the data */
100     if (ui32Status & AM_HAL_ADC_INT_FIFOOVR1)
101     {
102         do
103         {
104             /* Read the value from the FIFO into the circular buffer */
105             ui32FifoData = am_hal_adc_fifo_pop();
106 
107             if (AM_HAL_ADC_FIFO_SLOT(ui32FifoData) == BATTERY_ADC_CHANNELNUM)
108             {
109                 am_adcbat_buffertemp[bat_adc_cnt++] = AM_HAL_ADC_FIFO_SAMPLE(ui32FifoData);
110             }
111 
112             if ((bat_adc_cnt > ADC_SAMPLE_NUM + 2 - 1))
113             {
114                 bat_adc_cnt = 0;
115 
116                 /* send the message */
117                 rt_mq_send(&adcbat_mq, am_adcbat_buffertemp, ADC_SAMPLE_NUM*sizeof(rt_int16_t));
118             }
119         } while (AM_HAL_ADC_FIFO_COUNT(ui32FifoData) > 0);
120     }
121 
122     /* leave interrupt */
123     rt_interrupt_leave();
124 }
125 
timerA3_for_adc_init(void)126 static void timerA3_for_adc_init(void)
127 {
128     /* Start a timer to trigger the ADC periodically (1 second) */
129     am_hal_ctimer_config_single(ADC_CTIMER_NUM, AM_HAL_CTIMER_TIMERA,
130                                    AM_HAL_CTIMER_XT_2_048KHZ |
131                                    AM_HAL_CTIMER_FN_REPEAT |
132                                    AM_HAL_CTIMER_INT_ENABLE |
133                                    AM_HAL_CTIMER_PIN_ENABLE);
134 
135     am_hal_ctimer_int_enable(AM_HAL_CTIMER_INT_TIMERA3);
136 
137     /* Set 512 sample rate */
138     am_hal_ctimer_period_set(ADC_CTIMER_NUM, AM_HAL_CTIMER_TIMERA, ADC_CTIMER_COUNT, 1);
139 
140     /* Enable the timer A3 to trigger the ADC directly */
141     am_hal_ctimer_adc_trigger_enable();
142 
143     /* Start the timer */
144     //am_hal_ctimer_start(ADC_CTIMER_NUM, AM_HAL_CTIMER_TIMERA);
145 }
146 
147 /**
148  * @brief Initialize the ADC
149  *
150  * This function initialize the ADC
151  *
152  * @return None.
153  */
rt_hw_adc_init(void)154 int rt_hw_adc_init(void)
155 {
156     am_hal_adc_config_t sADCConfig;
157 
158     /* timer for adc init*/
159     timerA3_for_adc_init();
160 
161     /* Set a pin to act as our ADC input */
162     am_hal_gpio_pin_config(BATTERY_GPIO, BATTERY_ADC_PIN);
163 
164     /* Enable interrupts */
165     am_hal_interrupt_enable(AM_HAL_INTERRUPT_ADC);
166 
167     /* Enable the ADC power domain */
168     am_hal_pwrctrl_periph_enable(AM_HAL_PWRCTRL_ADC);
169 
170     /* Set up the ADC configuration parameters. These settings are reasonable
171        for accurate measurements at a low sample rate */
172     sADCConfig.ui32Clock = AM_HAL_ADC_CLOCK_HFRC;
173     sADCConfig.ui32TriggerConfig = AM_HAL_ADC_TRIGGER_SOFT;
174     sADCConfig.ui32Reference = AM_HAL_ADC_REF_INT_2P0;
175     sADCConfig.ui32ClockMode = AM_HAL_ADC_CK_LOW_POWER;
176     sADCConfig.ui32PowerMode = AM_HAL_ADC_LPMODE_0;
177     sADCConfig.ui32Repeat = AM_HAL_ADC_REPEAT;
178     am_hal_adc_config(&sADCConfig);
179 
180     /* For this example, the samples will be coming in slowly. This means we
181        can afford to wake up for every conversion */
182     am_hal_adc_int_enable(AM_HAL_ADC_INT_FIFOOVR1);
183 
184     /* Set up an ADC slot */
185     am_hal_adc_slot_config(BATTERY_ADC_CHANNELNUM, AM_HAL_ADC_SLOT_AVG_1 |
186                               AM_HAL_ADC_SLOT_14BIT |
187                               BATTERY_ADC_CHANNEL |
188                               AM_HAL_ADC_SLOT_ENABLE);
189 
190     /* Enable the ADC */
191     am_hal_adc_enable();
192 
193     /* Trigger the ADC once */
194     //am_hal_adc_trigger();
195 
196     //rt_kprintf("adc_init!\n");
197 
198     return 0;
199 }
200 #ifdef RT_USING_COMPONENTS_INIT
201 INIT_BOARD_EXPORT(rt_hw_adc_init);
202 #endif
203 
204 #endif
205 /*@}*/
206