Update remaining power after each sleeping period to something that
[lpc82x] / drivers / adc.c
1 /****************************************************************************
2  *  drivers/adc.c
3  *
4  * Copyright 2012 Nathael Pajani <nathael.pajani@ed3l.fr>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  *
19  *************************************************************************** */
23 /***************************************************************************** */
24 /*                Analog to Digital Converter (ADC)                            */
25 /***************************************************************************** */
27 /* ADC driver for the integrated ADC module of the LPC82x.
28  * Refer to LPC82x documentation (UM10800.pdf) for more information.
29  */
31 #include "core/system.h"
32 #include "drivers/adc.h"
34 /* ADC Clock should be as near to 30MHz as possible */
35 #define adc_clk_Val  (30 * 1000 * 1000)
39 /***************************************************************************** */
40 /* Generic ADC handler */
41 void (*adc_int_callback)(uint32_t) = NULL;
42 void ADC_Handler(void)
43 {
44         volatile struct lpc_adc* adc = LPC_ADC_REGS;
45         uint32_t flags = adc->flags;
47         if (adc_int_callback != NULL) {
48                 adc_int_callback(flags);
49         }
50 }
51 void ADC_SEQA_Handler(void) __attribute__ ((alias ("ADC_Handler")));
52 void ADC_SEQB_Handler(void) __attribute__ ((alias ("ADC_Handler")));
53 void ADC_THCMP_Handler(void) __attribute__ ((alias ("ADC_Handler")));
54 void ADC_OVR_Handler(void) __attribute__ ((alias ("ADC_Handler")));
56 /* Read the conversion from the given channel
57  * This function reads the conversion value directly in the data register and
58  * always returns a value.
59  * Return 0 if the value is a new one and no overrun occured.
60  * Return -EINVAL if channel does not exist
61  * Retuen 1 if the value is an old one
62  * Return 2 if an overrun occured
63  */
64 int adc_get_value(uint16_t * val, uint8_t channel)
65 {
66         struct lpc_adc* adc = LPC_ADC_REGS;
67         uint32_t save_reg = 0;
69         if (channel >= NB_ADC_CHANNELS)
70                 return -1;
72         /* Save the whole register as some bits are cleared when register is read */
73         save_reg = adc->data[channel];
74         *val = ((save_reg >> LPC_ADC_RESULT_SHIFT) & LPC_ADC_RESULT_MASK);
75         /* Has this conversion value been already read ? */
76         if (! (save_reg & LPC_ADC_CONV_DONE)) {
77                 return 1;
78         }
79         if (save_reg & LPC_ADC_OVERRUN) {
80                 return 2;
81         }
82         return 0;
83 }
85 /* Start a conversion on the given channel (0 to 7) */
86 void adc_start_convertion_once(uint8_t channel, uint8_t seq_num, uint8_t use_int)
87 {
88         struct lpc_adc* adc = LPC_ADC_REGS;
89         uint32_t reg_val = 0;
91         if (channel >= NB_ADC_CHANNELS) {
92                 return;
93         }
94         if (seq_num >= NB_ADC_SEQUENCES) {
95                 return;
96         }
97         
98         /* Set conversion channel bit */
99         reg_val = ADC_MCH(channel);
101         /*  Use of interrupts for the specified channel ? */
102         if (use_int) {
103                 /* Set interrupt Bit */
104                 adc->int_en = ADC_MCH(channel);
105         } else {
106                 adc->int_en = 0;
107         }
109         /* Start conversion */
110         reg_val |= (LPC_ADC_START_CONV_NOW | LPC_ADC_SEQ_EN);
111         adc->seqctrl[seq_num] = reg_val;
115 /* Start burst conversions.
116  * channels is a bit mask of requested channels.
117  * Use ADC_MCH(x) (x = 0 .. 7) for channels selection.
118  */
119 void adc_start_burst_conversion(uint16_t channels, uint8_t seq_num)
121         struct lpc_adc* adc = LPC_ADC_REGS;
122         uint32_t reg_val = 0;
124         if (seq_num >= NB_ADC_SEQUENCES) {
125                 return;
126         }
127         
128         /* Set conversion channel bits and burst mode */
129         reg_val = (channels & ADC_MCH_MASK);
130         reg_val |= LPC_ADC_BURST;
132         /*  Do not use of interrupts in burst mode. */
133         /* If you need to, then you should also use DMA for data transfer, and make sure your code is
134          * able to handle the load */
135         if (seq_num == LPC_ADC_SEQA) {
136                 adc->int_en &= ~(LPC_ADC_SEQA_INTEN);
137         } else {
138                 adc->int_en &= ~(LPC_ADC_SEQB_INTEN);
139         }
141         /* Start conversion */
142         adc->seqctrl[seq_num] = (reg_val | LPC_ADC_SEQ_EN);
144 void adc_stop_burst_conversion(uint8_t seq_num)
146         struct lpc_adc* adc = LPC_ADC_REGS;
147         if (seq_num >= NB_ADC_SEQUENCES) {
148                 return;
149         }
150         adc->seqctrl[seq_num] &= ~LPC_ADC_BURST;
154 /* This should be used to configure conversion start on falling or rising edges of
155  * some signals, or on timer for burst conversions.
156  */
157 void adc_prepare_conversion_on_event(uint16_t channels, uint8_t event, uint8_t seq_num,
158                                                                                 uint8_t use_int, uint32_t mode)
160         struct lpc_adc* adc = LPC_ADC_REGS;
161         uint32_t reg_val = 0;
163         if (seq_num >= NB_ADC_SEQUENCES) {
164                 return;
165         }
167         /* Set conversion channel bits and burst mode */
168         reg_val |= (channels & ADC_MCH_MASK);
169         /* Set conversion condition bits */
170         switch (event) {
171                 case LPC_ADC_START_CONV_ADC_PINTRIG_0:
172                 case LPC_ADC_START_CONV_ADC_PINTRIG_1:
173                 case LPC_ADC_START_CONV_SCT0_OUT3:
174                 case LPC_ADC_START_CONV_ACMP_OUT:
175                 case LPC_ADC_START_CONV_ARM_TXEV:
176                         reg_val |= LPC_ADC_START_CONV_EVENT(event);
177                         break;
178                 default:
179                 case LPC_ADC_START_CONV_SOFT:
180                         /* Trigger = 0 : already OK */
181                         break;
182         }
183         if (mode != 0) {
184                  reg_val |= (mode & LPC_ADC_ADDITIONAL_MODE_MASK);
185         }
187         /*  Use of interrupts for the specified channel ? */
188         if (use_int) {
189                 if (seq_num == LPC_ADC_SEQA) {
190                         adc->int_en = LPC_ADC_SEQA_INTEN;
191                 } else {
192                         adc->int_en = LPC_ADC_SEQB_INTEN;
193                 }
194         } else {
195                 adc->int_en = 0;
196         }
198         /* Enable conversion on selected event */
199         adc->seqctrl[seq_num] = (reg_val | LPC_ADC_SEQ_EN);
202 /* Software trigger of the given configured sequence */
203 void adc_trigger_sequence_conversion(uint8_t seq_num)
205         struct lpc_adc* adc = LPC_ADC_REGS;
207         if (seq_num >= NB_ADC_SEQUENCES) {
208                 return;
209         }
211         adc->seqctrl[seq_num] |= (LPC_ADC_START_CONV_NOW);
215 /***************************************************************************** */
216 /*   ADC Setup : private part : Clocks, Power and Mode   */
218 /* When lowpower_en is not 0 the ADC low power mode is selected, which adds a 15ADC clock delay
219  * before each set of consecutive conversions (but not between consecutive conversions)
220  */
221 void adc_set_low_power(int lowpower_en)
223         struct lpc_adc* adc = LPC_ADC_REGS;
224         if (lowpower_en) {
225                 adc->ctrl |= LPC_ADC_LOW_POWER_EN;
226         } else {
227                 adc->ctrl &= ~(LPC_ADC_LOW_POWER_EN);
228         }
231 void adc_clk_update(void)
233         struct lpc_adc* adc = LPC_ADC_REGS;
234         uint32_t main_clock = get_main_clock();
235         uint32_t clkdiv = 0;
237         /* Configure ADC clock to get the 9MHz sample clock */
238         clkdiv = (main_clock / adc_clk_Val);
239         adc->ctrl &= ~LPC_ADC_CLK_MASK;
240         adc->ctrl |= LPC_ADC_CLK_DIV(clkdiv);
244 void adc_on(void (*adc_callback)(uint32_t))
246         struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
247         struct lpc_adc* adc = LPC_ADC_REGS;
249         /* Disable ADC Interrupts */
250         NVIC_DisableIRQ(ADC_SEQA_IRQ);
251         NVIC_DisableIRQ(ADC_SEQB_IRQ);
252         NVIC_DisableIRQ(ADC_THCMP_IRQ);
253         NVIC_DisableIRQ(ADC_OVR_IRQ);
255         /* Power-up ADC */
256         sys_config->powerdown_run_cfg &= ~LPC_POWER_DOWN_ADC;
257         /* Provide clock to ADC */
258         subsystem_power(LPC_SYS_AHB_CLK_CTRL_ADC, 1);
259         adc->ctrl = 0;
260         adc_clk_update();
262         /* Prevent unconfigured conversion start */
263         adc->seqctrl[0] = 0;
264         adc->seqctrl[1] = 0;
266         /* Remove the default global interrupt enabled setting */
267         adc->int_en = 0;
268         /* Register a possible calback */
269         adc_int_callback = adc_callback;
271         /* Enable ADC Interrupts */
272         NVIC_EnableIRQ(ADC_SEQA_IRQ);
273         NVIC_EnableIRQ(ADC_SEQB_IRQ);
274         NVIC_EnableIRQ(ADC_THCMP_IRQ);
275         NVIC_EnableIRQ(ADC_OVR_IRQ);
278 void adc_off(void)
280         struct lpc_sys_config* sys_config = LPC_SYS_CONFIG;
282         /* Disable ADC Interrupts */
283         NVIC_DisableIRQ(ADC_SEQA_IRQ);
284         NVIC_DisableIRQ(ADC_SEQB_IRQ);
285         NVIC_DisableIRQ(ADC_THCMP_IRQ);
286         NVIC_DisableIRQ(ADC_OVR_IRQ);
287         /* Remove callback */
288         adc_int_callback = NULL;
289         /* Power Down ADC */
290         sys_config->powerdown_run_cfg |= LPC_POWER_DOWN_ADC;
291         /* Remove clock from ADC block */
292         subsystem_power(LPC_SYS_AHB_CLK_CTRL_ADC, 0);