Skip to content

Measure voltage supplied to chip #2213

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Oct 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion lib/tinyusb
Submodule tinyusb updated 0 files
98 changes: 68 additions & 30 deletions ports/atmel-samd/common-hal/microcontroller/Processor.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@
#define INT1V_DIVIDER_1000 1000.0
#define ADC_12BIT_FULL_SCALE_VALUE_FLOAT 4095.0

// channel argument (ignored in calls below)
#define IGNORED_CHANNEL 0

// Decimal to fraction conversion. (adapted from ASF sample).
STATIC float convert_dec_to_frac(uint8_t val) {
Expand Down Expand Up @@ -196,14 +198,12 @@ float common_hal_mcu_processor_get_temperature(void) {

adc_sync_set_resolution(&adc, ADC_CTRLB_RESSEL_12BIT_Val);
adc_sync_set_reference(&adc, ADC_REFCTRL_REFSEL_INT1V_Val);
// Channel passed in adc_sync_enable_channel is actually ignored (!).
adc_sync_enable_channel(&adc, ADC_INPUTCTRL_MUXPOS_TEMP_Val);
// Channel arg is ignored.
adc_sync_enable_channel(&adc, IGNORED_CHANNEL);
adc_sync_set_inputs(&adc,
ADC_INPUTCTRL_MUXPOS_TEMP_Val, // pos_input
ADC_INPUTCTRL_MUXNEG_GND_Val, // neg_input
ADC_INPUTCTRL_MUXPOS_TEMP_Val); // channel channel (this arg is ignored (!))

adc_sync_set_resolution(&adc, ADC_CTRLB_RESSEL_12BIT_Val);
IGNORED_CHANNEL); // channel (ignored)

hri_adc_write_CTRLB_PRESCALER_bf(adc.device.hw, ADC_CTRLB_PRESCALER_DIV32_Val);
hri_adc_write_SAMPCTRL_SAMPLEN_bf(adc.device.hw, ADC_TEMP_SAMPLE_LENGTH);
Expand All @@ -222,62 +222,100 @@ float common_hal_mcu_processor_get_temperature(void) {
// like voltage reference / ADC channel change"
// Empirical observation shows the first reading is quite different than subsequent ones.

// The channel listed in adc_sync_read_channel is actually ignored(!).
// Must be set as above with adc_sync_set_inputs.
adc_sync_read_channel(&adc, ADC_INPUTCTRL_MUXPOS_TEMP_Val, ((uint8_t*) &value), 2);
adc_sync_read_channel(&adc, ADC_INPUTCTRL_MUXPOS_TEMP_Val, ((uint8_t*) &value), 2);
// Channel arg is ignored.
adc_sync_read_channel(&adc, IGNORED_CHANNEL, ((uint8_t*) &value), 2);
adc_sync_read_channel(&adc, IGNORED_CHANNEL, ((uint8_t*) &value), 2);

adc_sync_deinit(&adc);
return calculate_temperature(value);
#endif // SAMD21

#ifdef SAMD51
adc_sync_set_resolution(&adc, ADC_CTRLB_RESSEL_12BIT_Val);
// Reference voltage choice is a guess. It's not specified in the datasheet that I can see.
// Using INTVCC0 as the reference voltage.
// INTVCC1 seems to read a little high.
// INTREF doesn't work: ADC hangs BUSY.
// INTREF doesn't work: ADC hangs BUSY. It's supposed to work, but does not.
// The SAME54 example from Atmel START implicitly uses INTREF.
adc_sync_set_reference(&adc, ADC_REFCTRL_REFSEL_INTVCC0_Val);

// If ONDEMAND=1, we don't need to use the VREF.TSSEL bit to choose PTAT and CTAT.
hri_supc_set_VREF_ONDEMAND_bit(SUPC);
// Enable temperature sensor.
hri_supc_set_VREF_TSEN_bit(SUPC);
hri_supc_set_VREF_VREFOE_bit(SUPC);

// Channel passed in adc_sync_enable_channel is actually ignored (!).
adc_sync_enable_channel(&adc, ADC_INPUTCTRL_MUXPOS_PTAT_Val);
// Channel arg is ignored.
adc_sync_enable_channel(&adc, IGNORED_CHANNEL);
adc_sync_set_inputs(&adc,
ADC_INPUTCTRL_MUXPOS_PTAT_Val, // pos_input
ADC_INPUTCTRL_MUXNEG_GND_Val, // neg_input
ADC_INPUTCTRL_MUXPOS_PTAT_Val); // channel (this arg is ignored (!))
IGNORED_CHANNEL); // channel (ignored)

// Read both temperature sensors.
volatile uint16_t ptat;
volatile uint16_t ctat;

// The channel listed in adc_sync_read_channel is actually ignored(!).
// Must be set as above with adc_sync_set_inputs.
// Read twice for stability (necessary?)
adc_sync_read_channel(&adc, ADC_INPUTCTRL_MUXPOS_PTAT_Val, ((uint8_t*) &ptat), 2);
adc_sync_read_channel(&adc, ADC_INPUTCTRL_MUXPOS_PTAT_Val, ((uint8_t*) &ptat), 2);
// Read twice for stability (necessary?).
adc_sync_read_channel(&adc, IGNORED_CHANNEL, ((uint8_t*) &ptat), 2);
adc_sync_read_channel(&adc, IGNORED_CHANNEL, ((uint8_t*) &ptat), 2);

adc_sync_set_inputs(&adc,
ADC_INPUTCTRL_MUXPOS_CTAT_Val, // pos_input
ADC_INPUTCTRL_MUXNEG_GND_Val, // neg_input
ADC_INPUTCTRL_MUXPOS_CTAT_Val); // channel (this arg is ignored (!))

// Channel passed in adc_sync_enable_channel is actually ignored (!).
adc_sync_enable_channel(&adc, ADC_INPUTCTRL_MUXPOS_CTAT_Val);
// The channel listed in adc_sync_read_channel is actually ignored(!).
// Must be set as above with adc_sync_set_inputs.
// Read twice for stability (necessary?)
adc_sync_read_channel(&adc, ADC_INPUTCTRL_MUXPOS_CTAT_Val, ((uint8_t*) &ctat), 2);
adc_sync_read_channel(&adc, ADC_INPUTCTRL_MUXPOS_CTAT_Val, ((uint8_t*) &ctat), 2);
hri_supc_set_VREF_ONDEMAND_bit(SUPC);
IGNORED_CHANNEL); // channel (ignored)

adc_sync_read_channel(&adc, IGNORED_CHANNEL, ((uint8_t*) &ctat), 2);
adc_sync_read_channel(&adc, IGNORED_CHANNEL, ((uint8_t*) &ctat), 2);

// Turn off temp sensor.
hri_supc_clear_VREF_TSEN_bit(SUPC);

adc_sync_deinit(&adc);
return calculate_temperature(ptat, ctat);
#endif // SAMD51
}

float common_hal_mcu_processor_get_voltage(void) {
struct adc_sync_descriptor adc;

static Adc* adc_insts[] = ADC_INSTS;
samd_peripherals_adc_setup(&adc, adc_insts[0]);

#ifdef SAMD21
adc_sync_set_reference(&adc, ADC_REFCTRL_REFSEL_INT1V_Val);
#endif

#ifdef SAMD51
hri_supc_set_VREF_SEL_bf(SUPC, SUPC_VREF_SEL_1V0_Val);
// ONDEMAND must be clear, and VREFOE must be set, or else the ADC conversion will not complete.
// See https://community.atmel.com/forum/samd51-using-intref-adc-voltage-reference
hri_supc_clear_VREF_ONDEMAND_bit(SUPC);
hri_supc_set_VREF_VREFOE_bit(SUPC);
adc_sync_set_reference(&adc, ADC_REFCTRL_REFSEL_INTREF_Val);
#endif

adc_sync_set_resolution(&adc, ADC_CTRLB_RESSEL_12BIT_Val);
// Channel arg is ignored.
adc_sync_set_inputs(&adc,
ADC_INPUTCTRL_MUXPOS_SCALEDIOVCC_Val, // IOVCC/4 (nominal 3.3V/4)
ADC_INPUTCTRL_MUXNEG_GND_Val, // neg_input
IGNORED_CHANNEL); // channel (ignored).
adc_sync_enable_channel(&adc, IGNORED_CHANNEL);

volatile uint16_t reading;

// Channel arg is ignored.
// Read twice and discard first result, as recommended in section 14 of
// http://www.atmel.com/images/Atmel-42645-ADC-Configurations-with-Examples_ApplicationNote_AT11481.pdf
// "Discard the first conversion result whenever there is a change in ADC configuration
// like voltage reference / ADC channel change"
// Empirical observation shows the first reading is quite different than subsequent ones.
adc_sync_read_channel(&adc, IGNORED_CHANNEL, ((uint8_t*) &reading), 2);
adc_sync_read_channel(&adc, IGNORED_CHANNEL, ((uint8_t*) &reading), 2);

adc_sync_deinit(&adc);
// Multiply by 4 to compensate for SCALEDIOVCC division by 4.
return (reading / 4095.0f) * 4.0f;
}

uint32_t common_hal_mcu_processor_get_frequency(void) {
// TODO(tannewt): Determine this dynamically.
Expand Down
8 changes: 7 additions & 1 deletion ports/cxd56/common-hal/microcontroller/Processor.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,20 @@

#include <sys/boardctl.h>

// For NAN: remove when not needed.
#include <math.h>
#include "py/mphal.h"

uint32_t common_hal_mcu_processor_get_frequency(void) {
return mp_hal_ticks_cpu();
}

float common_hal_mcu_processor_get_temperature(void) {
return 0;
return NAN;
}

float common_hal_mcu_processor_get_voltage(void) {
return NAN;
}

void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) {
Expand Down
12 changes: 11 additions & 1 deletion ports/nrf/common-hal/analogio/AnalogIn.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,21 @@
#include "py/runtime.h"
#include "supervisor/shared/translate.h"

#include "nrfx_saadc.h"
#include "nrf_saadc.h"
#include "nrf_gpio.h"

#define CHANNEL_NO 0

void analogin_init(void) {
// Calibrate the ADC once, on startup.
nrf_saadc_enable();
nrf_saadc_event_clear(NRF_SAADC_EVENT_CALIBRATEDONE);
nrf_saadc_task_trigger(NRF_SAADC_TASK_CALIBRATEOFFSET);
while (nrf_saadc_event_check(NRF_SAADC_EVENT_CALIBRATEDONE) == 0) { }
nrf_saadc_event_clear(NRF_SAADC_EVENT_CALIBRATEDONE);
nrf_saadc_disable();
}

void common_hal_analogio_analogin_construct(analogio_analogin_obj_t *self, const mcu_pin_obj_t *pin) {
if (pin->adc_channel == 0)
mp_raise_ValueError(translate("Pin does not have ADC capabilities"));
Expand Down
2 changes: 2 additions & 0 deletions ports/nrf/common-hal/analogio/AnalogIn.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,4 +36,6 @@ typedef struct {
const mcu_pin_obj_t * pin;
} analogio_analogin_obj_t;

void analogin_init(void);

#endif // MICROPY_INCLUDED_NRF_COMMON_HAL_ANALOGIO_ANALOGIN_H
65 changes: 56 additions & 9 deletions ports/nrf/common-hal/microcontroller/Processor.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
#include "py/runtime.h"
#include "supervisor/shared/translate.h"

#include "nrfx_saadc.h"
#ifdef BLUETOOTH_SD
#include "nrf_sdm.h"
#endif
Expand All @@ -47,27 +48,73 @@ float common_hal_mcu_processor_get_temperature(void) {
if (err_code != NRF_SUCCESS) {
mp_raise_OSError_msg(translate("Cannot get temperature"));
}
}
return temp / 4.0f;
} // Fall through if SD not enabled.
#endif

NRF_TEMP->TASKS_START = 1;

while (NRF_TEMP->EVENTS_DATARDY == 0)
;

while (NRF_TEMP->EVENTS_DATARDY == 0) { }
NRF_TEMP->EVENTS_DATARDY = 0;

temp = NRF_TEMP->TEMP;

NRF_TEMP->TASKS_STOP = 1;

return temp / 4.0f;
}



uint32_t common_hal_mcu_processor_get_frequency(void) {
return 64000000ul;
}

float common_hal_mcu_processor_get_voltage(void) {
nrf_saadc_value_t value;

const nrf_saadc_channel_config_t config = {
.resistor_p = NRF_SAADC_RESISTOR_DISABLED,
.resistor_n = NRF_SAADC_RESISTOR_DISABLED,
.gain = NRF_SAADC_GAIN1_6,
.reference = NRF_SAADC_REFERENCE_INTERNAL,
.acq_time = NRF_SAADC_ACQTIME_10US,
.mode = NRF_SAADC_MODE_SINGLE_ENDED,
.burst = NRF_SAADC_BURST_DISABLED,
.pin_p = NRF_SAADC_INPUT_VDD,
.pin_n = NRF_SAADC_INPUT_VDD,
};

nrf_saadc_resolution_set(NRF_SAADC_RESOLUTION_14BIT);
nrf_saadc_oversample_set(NRF_SAADC_OVERSAMPLE_DISABLED);
nrf_saadc_enable();

for (uint32_t i = 0; i < NRF_SAADC_CHANNEL_COUNT; i++) {
nrf_saadc_channel_input_set(i, NRF_SAADC_INPUT_DISABLED, NRF_SAADC_INPUT_DISABLED);
}

nrf_saadc_channel_init(0, &config);
nrf_saadc_buffer_init(&value, 1);

nrf_saadc_task_trigger(NRF_SAADC_TASK_START);
while (nrf_saadc_event_check(NRF_SAADC_EVENT_STARTED) == 0) { }
nrf_saadc_event_clear(NRF_SAADC_EVENT_STARTED);

nrf_saadc_task_trigger(NRF_SAADC_TASK_SAMPLE);
while (nrf_saadc_event_check(NRF_SAADC_EVENT_END) == 0) { }
nrf_saadc_event_clear(NRF_SAADC_EVENT_END);

nrf_saadc_task_trigger(NRF_SAADC_TASK_STOP);
while (nrf_saadc_event_check(NRF_SAADC_EVENT_STOPPED) == 0) { }
nrf_saadc_event_clear(NRF_SAADC_EVENT_STOPPED);

nrf_saadc_disable();

if (value < 0) {
value = 0;
}

// The ADC reading we expect if VDD is 3.3V.
#define NOMINAL_VALUE_3_3 (((3.3f/6)/0.6f)*16383)
return (value/NOMINAL_VALUE_3_3) * 3.3f;
}


void common_hal_mcu_processor_get_uid(uint8_t raw_id[]) {
for (int i=0; i<2; i++) {
((uint32_t*) raw_id)[i] = NRF_FICR->DEVICEID[i];
Expand Down
9 changes: 7 additions & 2 deletions ports/nrf/supervisor/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "shared-module/gamepad/__init__.h"
#include "common-hal/microcontroller/Pin.h"
#include "common-hal/_bleio/__init__.h"
#include "common-hal/analogio/AnalogIn.h"
#include "common-hal/busio/I2C.h"
#include "common-hal/busio/SPI.h"
#include "common-hal/busio/UART.h"
Expand Down Expand Up @@ -83,6 +84,10 @@ safe_mode_t port_init(void) {
// Configure millisecond timer initialization.
tick_init();

#if CIRCUITPY_ANALOGIO
analogin_init();
#endif

#if CIRCUITPY_RTC
rtc_init();
#endif
Expand All @@ -102,11 +107,11 @@ void reset_port(void) {
spi_reset();
uart_reset();

#ifdef CIRCUITPY_AUDIOBUSIO
#if CIRCUITPY_AUDIOBUSIO
i2s_reset();
#endif

#ifdef CIRCUITPY_AUDIOPWMIO
#if CIRCUITPY_AUDIOPWMIO
audiopwmout_reset();
#endif

Expand Down
7 changes: 6 additions & 1 deletion ports/stm32f4/common-hal/microcontroller/Processor.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
* THE SOFTWARE.
*/

#include <math.h>
#include "common-hal/microcontroller/Processor.h"
#include "py/runtime.h"
#include "supervisor/shared/translate.h"
Expand All @@ -34,7 +35,11 @@
#define STM32_UUID ((uint32_t *)0x1FFF7A10)

float common_hal_mcu_processor_get_temperature(void) {
return 0;
return NAN;
}

float common_hal_mcu_processor_get_voltage(void) {
return NAN;
}

uint32_t common_hal_mcu_processor_get_frequency(void) {
Expand Down
22 changes: 22 additions & 0 deletions shared-bindings/microcontroller/Processor.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,32 @@ const mp_obj_property_t mcu_processor_uid_obj = {
},
};

//| .. attribute:: voltage
//|
//| The input voltage to the microcontroller, as a float. (read-only)
//|
//| Is `None` if the voltage is not available.
//|
STATIC mp_obj_t mcu_processor_get_voltage(mp_obj_t self) {
float voltage = common_hal_mcu_processor_get_voltage();
return isnan(voltage) ? mp_const_none : mp_obj_new_float(voltage);
}

MP_DEFINE_CONST_FUN_OBJ_1(mcu_processor_get_voltage_obj, mcu_processor_get_voltage);

const mp_obj_property_t mcu_processor_voltage_obj = {
.base.type = &mp_type_property,
.proxy = {(mp_obj_t)&mcu_processor_get_voltage_obj, // getter
(mp_obj_t)&mp_const_none_obj, // no setter
(mp_obj_t)&mp_const_none_obj, // no deleter
},
};

STATIC const mp_rom_map_elem_t mcu_processor_locals_dict_table[] = {
{ MP_ROM_QSTR(MP_QSTR_frequency), MP_ROM_PTR(&mcu_processor_frequency_obj) },
{ MP_ROM_QSTR(MP_QSTR_temperature), MP_ROM_PTR(&mcu_processor_temperature_obj) },
{ MP_ROM_QSTR(MP_QSTR_uid), MP_ROM_PTR(&mcu_processor_uid_obj) },
{ MP_ROM_QSTR(MP_QSTR_voltage), MP_ROM_PTR(&mcu_processor_voltage_obj) },
};

STATIC MP_DEFINE_CONST_DICT(mcu_processor_locals_dict, mcu_processor_locals_dict_table);
Expand Down
1 change: 1 addition & 0 deletions shared-bindings/microcontroller/Processor.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ const mp_obj_type_t mcu_processor_type;
uint32_t common_hal_mcu_processor_get_frequency(void);
float common_hal_mcu_processor_get_temperature(void);
void common_hal_mcu_processor_get_uid(uint8_t raw_id[]);
float common_hal_mcu_processor_get_voltage(void);

#endif // MICROPY_INCLUDED_SHARED_BINDINGS_MICROCONTROLLER_PROCESSOR_H