Skip to content

Commit 62beb54

Browse files
committed
alif/machine_adc: Add basic ADC support.
ADC12 information has been added to pin struct. Signed-off-by: Damien George <damien@micropython.org>
1 parent 31d18c5 commit 62beb54

6 files changed

Lines changed: 263 additions & 1 deletion

File tree

ports/alif/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ ALIF_SRC_C += $(addprefix $(ALIF_DFP_REL_TOP)/,\
176176
Device/common/source/system_M55.c \
177177
Device/common/source/system_utils.c \
178178
Device/core/$(MCU_CORE)/source/startup_$(MCU_CORE).c \
179+
drivers/source/adc.c \
179180
drivers/source/mhu_driver.c \
180181
drivers/source/mhu_receiver.c \
181182
drivers/source/mhu_sender.c \

ports/alif/irq.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
#define IRQ_PRI_MHU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 0, 0)
4444
#define IRQ_PRI_QUIET_TIMING NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0)
4545
#define IRQ_PRI_UART_REPL NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 1, 0)
46+
#define IRQ_PRI_ADC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 3, 0)
4647
#define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 5, 0)
4748
#define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0)
4849

ports/alif/machine_adc.c

Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2024 OpenMV LLC.
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
// This file is never compiled standalone, it's included directly from
28+
// extmod/machine_adc.c via MICROPY_PY_MACHINE_ADC_INCLUDEFILE.
29+
30+
#include "py/mphal.h"
31+
32+
#include "analog_config.h"
33+
#include "adc.h"
34+
#include "sys_ctrl_adc.h"
35+
36+
#define ADC_CHANNEL_TEMP_SENSOR (6)
37+
#define ADC_CHANNEL_INT_VREF (7)
38+
39+
static const uint8_t adc_instance_table[4] = {
40+
ADC_INSTANCE_ADC12_0,
41+
ADC_INSTANCE_ADC12_1,
42+
ADC_INSTANCE_ADC12_2,
43+
ADC_INSTANCE_ADC24_0,
44+
};
45+
46+
static ADC_Type *const adc_regs_table[4] = {
47+
(ADC_Type *)ADC120_BASE,
48+
(ADC_Type *)ADC121_BASE,
49+
(ADC_Type *)ADC122_BASE,
50+
(ADC_Type *)ADC24_BASE,
51+
};
52+
53+
static bool adc_inited[4];
54+
static conv_info_t adc_conv_info_table[4];
55+
56+
static void adc_nvic_config(unsigned int irq) {
57+
NVIC_ClearPendingIRQ(irq);
58+
NVIC_SetPriority(irq, IRQ_PRI_ADC);
59+
NVIC_EnableIRQ(irq);
60+
}
61+
62+
static void adc_init(uint8_t adc_periph) {
63+
if (adc_inited[adc_periph]) {
64+
return;
65+
}
66+
67+
ADC_Type *regs = adc_regs_table[adc_periph];
68+
uint8_t adc_instance = adc_instance_table[adc_periph];
69+
conv_info_t *conv_info = &adc_conv_info_table[adc_periph];
70+
71+
adc_set_clk_control(adc_instance, true);
72+
enable_cmp_periph_clk();
73+
analog_config_vbat_reg2();
74+
analog_config_cmp_reg2();
75+
adc_set_differential_ctrl(adc_instance, false);
76+
adc_set_comparator_ctrl(adc_instance, true, 2);
77+
78+
if (adc_instance == ADC_INSTANCE_ADC24_0) {
79+
enable_adc24();
80+
set_adc24_output_rate(0);
81+
set_adc24_bias(0);
82+
} else {
83+
adc_set_sample_width(regs, 32);
84+
}
85+
86+
adc_set_clk_div(regs, 4);
87+
adc_set_avg_sample(regs, 32);
88+
adc_set_n_shift_bit(regs, 1, 1);
89+
adc_set_single_ch_scan_mode(regs, conv_info);
90+
adc_unmask_interrupt(regs);
91+
92+
adc_nvic_config(ADC120_DONE0_IRQ_IRQn);
93+
adc_nvic_config(ADC120_DONE1_IRQ_IRQn);
94+
adc_nvic_config(ADC121_DONE0_IRQ_IRQn);
95+
adc_nvic_config(ADC121_DONE1_IRQ_IRQn);
96+
adc_nvic_config(ADC122_DONE0_IRQ_IRQn);
97+
adc_nvic_config(ADC122_DONE1_IRQ_IRQn);
98+
}
99+
100+
static uint16_t adc_config_and_read_u16(unsigned int adc_periph, uint32_t channel) {
101+
ADC_Type *adc_regs = adc_regs_table[adc_periph];
102+
conv_info_t *conv_info = &adc_conv_info_table[adc_periph];
103+
104+
conv_info->status = ADC_CONV_STAT_NONE;
105+
conv_info->mode = ADC_CONV_MODE_SINGLE_SHOT;
106+
107+
// Select channel and start conversion.
108+
adc_init_channel_select(adc_regs, channel);
109+
adc_set_single_ch_scan_mode(adc_regs, conv_info);
110+
adc_enable_single_shot_conv(adc_regs);
111+
112+
// Wait for conversion to complete.
113+
while (!(conv_info->status & ADC_CONV_STAT_COMPLETE)) {
114+
__WFE();
115+
}
116+
117+
return conv_info->sampled_value;
118+
}
119+
120+
static void adc_done0_irq_handler_helper(unsigned int adc_periph) {
121+
adc_done0_irq_handler(adc_regs_table[adc_periph], &adc_conv_info_table[adc_periph]);
122+
}
123+
124+
static void adc_done1_irq_handler_helper(unsigned int adc_periph) {
125+
adc_done1_irq_handler(adc_regs_table[adc_periph], &adc_conv_info_table[adc_periph]);
126+
}
127+
128+
void ADC120_DONE0_IRQHandler(void) {
129+
adc_done0_irq_handler_helper(0);
130+
__SEV();
131+
}
132+
133+
void ADC120_DONE1_IRQHandler(void) {
134+
adc_done1_irq_handler_helper(0);
135+
__SEV();
136+
}
137+
138+
void ADC121_DONE0_IRQHandler(void) {
139+
adc_done0_irq_handler_helper(1);
140+
__SEV();
141+
}
142+
143+
void ADC121_DONE1_IRQHandler(void) {
144+
adc_done1_irq_handler_helper(1);
145+
__SEV();
146+
}
147+
148+
void ADC122_DONE0_IRQHandler(void) {
149+
adc_done0_irq_handler_helper(2);
150+
__SEV();
151+
}
152+
153+
void ADC122_DONE1_IRQHandler(void) {
154+
adc_done1_irq_handler_helper(2);
155+
__SEV();
156+
}
157+
158+
/******************************************************************************/
159+
// MicroPython bindings for machine.ADC
160+
161+
#define MICROPY_PY_MACHINE_ADC_CLASS_CONSTANTS \
162+
{ MP_ROM_QSTR(MP_QSTR_CORE_TEMP), MP_ROM_INT(ADC_CHANNEL_TEMP_SENSOR) }, \
163+
{ MP_ROM_QSTR(MP_QSTR_CORE_VREF), MP_ROM_INT(ADC_CHANNEL_INT_VREF) }, \
164+
165+
typedef struct _machine_adc_obj_t {
166+
mp_obj_base_t base;
167+
uint8_t adc_periph;
168+
uint8_t adc_channel;
169+
} machine_adc_obj_t;
170+
171+
static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) {
172+
machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in);
173+
mp_printf(print, "<ADC12%u channel=%u>", self->adc_periph, self->adc_channel);
174+
}
175+
176+
// ADC(id)
177+
static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
178+
// Check number of arguments
179+
mp_arg_check_num(n_args, n_kw, 1, 1, false);
180+
181+
mp_obj_t source = all_args[0];
182+
183+
uint8_t adc_periph;
184+
uint8_t adc_channel;
185+
const machine_pin_obj_t *pin = NULL;
186+
187+
if (mp_obj_is_int(source)) {
188+
// Get and validate channel number.
189+
adc_channel = mp_obj_get_int(source);
190+
if (adc_channel < ADC_CHANNEL_TEMP_SENSOR) {
191+
adc_periph = 0;
192+
} else if (adc_channel == ADC_CHANNEL_TEMP_SENSOR || adc_channel == ADC_CHANNEL_INT_VREF) {
193+
adc_periph = 2;
194+
} else {
195+
mp_raise_ValueError(MP_ERROR_TEXT("invalid channel"));
196+
}
197+
} else {
198+
// Get GPIO and check it has ADC capabilities.
199+
pin = mp_hal_get_pin_obj(source);
200+
if (pin->adc12_periph <= 2 && pin->adc12_channel <= 5) {
201+
// Select the ADC12 peripheral and channel.
202+
adc_periph = pin->adc12_periph;
203+
adc_channel = pin->adc12_channel;
204+
// Configure the pad for analog input.
205+
pinconf_set(pin->port, pin->pin, PINMUX_ALTERNATE_FUNCTION_7, PADCTRL_READ_ENABLE);
206+
} else {
207+
mp_raise_ValueError(MP_ERROR_TEXT("Pin doesn't have ADC capabilities"));
208+
}
209+
}
210+
211+
// Initialise the ADC peripheral.
212+
adc_init(adc_periph);
213+
214+
// Create ADC object.
215+
machine_adc_obj_t *o = mp_obj_malloc(machine_adc_obj_t, &machine_adc_type);
216+
o->adc_periph = adc_periph;
217+
o->adc_channel = adc_channel;
218+
219+
return MP_OBJ_FROM_PTR(o);
220+
}
221+
222+
// read_u16()
223+
static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) {
224+
return adc_config_and_read_u16(self->adc_periph, self->adc_channel);
225+
}

ports/alif/mcu/make-pins.py

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,51 @@
1010
NUM_PORTS = 16
1111
NUM_PINS_PER_PORT = 8
1212

13+
# Maps pad name to (adc12_periph, adc12_channel).
14+
ADC12_ANA_MAP = {
15+
"P0_0": (0, 0),
16+
"P0_1": (0, 1),
17+
"P0_2": (0, 2),
18+
"P0_3": (0, 3),
19+
"P0_4": (0, 4),
20+
"P0_5": (0, 5),
21+
"P0_6": (1, 0),
22+
"P0_7": (1, 1),
23+
"P1_0": (1, 2),
24+
"P1_1": (1, 3),
25+
"P1_2": (1, 4),
26+
"P1_3": (1, 5),
27+
"P1_4": (2, 0),
28+
"P1_5": (2, 1),
29+
"P1_6": (2, 2),
30+
"P1_7": (2, 3),
31+
"P2_0": (2, 4),
32+
"P2_1": (2, 5),
33+
}
34+
1335

1436
class AlifPin(boardgen.Pin):
1537
# Emit the struct which contains the pin instance.
1638
def definition(self):
1739
port, pin = self.name()[1:].split("_")
40+
adc12_periph, adc12_channel = ADC12_ANA_MAP.get(self.name(), (3, 7))
1841
base = "LPGPIO_BASE" if port == "15" else "GPIO{}_BASE".format(port)
1942
return (
2043
"{{ "
2144
".base = {{ .type = &machine_pin_type }}, "
2245
".gpio = (GPIO_Type *){base}, "
2346
".port = PORT_{port}, "
2447
".pin = PIN_{pin}, "
48+
".adc12_periph = {adc12_periph}, "
49+
".adc12_channel = {adc12_channel}, "
2550
".name = MP_QSTR_P{port}_{pin} "
26-
"}}".format(port=port, pin=pin, base=base)
51+
"}}".format(
52+
port=port,
53+
pin=pin,
54+
base=base,
55+
adc12_periph=adc12_periph,
56+
adc12_channel=adc12_channel,
57+
)
2758
)
2859

2960
# Alif cpu names must be "Pn_m".

ports/alif/mpconfigport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@
101101
#define MICROPY_PY_MACHINE_BOOTLOADER (1)
102102
#define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1)
103103
#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1)
104+
#define MICROPY_PY_MACHINE_ADC (1)
105+
#define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/alif/machine_adc.c"
104106
#define MICROPY_PY_MACHINE_DHT_READINTO (1)
105107
#define MICROPY_PY_MACHINE_PULSE (1)
106108
#define MICROPY_PY_MACHINE_SOFTI2C (1)

ports/alif/mphalport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ typedef struct _machine_pin_obj_t {
8585
GPIO_Type *gpio;
8686
uint8_t port;
8787
uint8_t pin;
88+
uint8_t adc12_periph : 2;
89+
uint8_t adc12_channel : 3;
8890
qstr name;
8991
} machine_pin_obj_t;
9092

0 commit comments

Comments
 (0)