Skip to content

Commit

Permalink
Add ADC code to re-route P0 channel to Vdd.
Browse files Browse the repository at this point in the history
  • Loading branch information
microbit-carlos committed Apr 26, 2024
1 parent ef3c190 commit c42d121
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 1 deletion.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
A C++ example project showing how to measure the BBC micro:bit V2 battery
voltage.

All the ADC channels are currently used for the Analogue pins and the on-board
microphone, so for this project we hijack the ADC channel used for P0 and
re-route it to the internal Vdd (voltage from the microcontroller power input).

## Building the project

### Dependencies
Expand Down
77 changes: 76 additions & 1 deletion source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,85 @@

MicroBit uBit;


// The ADC channel to be used for the battery voltage measurement
static int BATTERY_ADC_CHANNEL = -1;

/**
* Measure the VDD voltage using the ADC channel for pin P0.
*
* @return int The VDD voltage in millivolts.
*/
static int get_vdd_millivolts() {
if (BATTERY_ADC_CHANNEL == -1) {
// Not initilised
return 0;
}
// Second arg ensures channel is activated before we do an analog read,
// so that it configures CH[n].CONFIG before we apply our changes
(void)uBit.adc.getChannel(uBit.io.P0, true);

// Configure CH[n].CONFIG with 0.6V internal reference and 1/6 gain
// so max Vin detection is 0-3.6V
NRF_SAADC->CH[BATTERY_ADC_CHANNEL].CONFIG =
// Positive channel resistor control set to bypass resistor ladder
(SAADC_CH_CONFIG_RESP_Bypass << SAADC_CH_CONFIG_RESP_Pos) |
// Negative channel resistor control set to bypass resistor ladder
(SAADC_CH_CONFIG_RESN_Bypass << SAADC_CH_CONFIG_RESN_Pos) |
// Gain control set to 1/6
(SAADC_CH_CONFIG_GAIN_Gain1_6 << SAADC_CH_CONFIG_GAIN_Pos) |
// Reference control set to internal 0.6V reference
(SAADC_CH_CONFIG_REFSEL_Internal << SAADC_CH_CONFIG_REFSEL_Pos) |
// Acquisition time set to 3us
(SAADC_CH_CONFIG_TACQ_3us << SAADC_CH_CONFIG_TACQ_Pos) |
// Differential mode set to single ended
(SAADC_CH_CONFIG_MODE_SE << SAADC_CH_CONFIG_MODE_Pos) |
// Burst mode set to disabled
(SAADC_CH_CONFIG_BURST_Disabled << SAADC_CH_CONFIG_BURST_Pos);

// And set the positive input to VDD
NRF_SAADC->CH[BATTERY_ADC_CHANNEL].PSELP = SAADC_CH_PSELP_PSELP_VDD << SAADC_CH_PSELP_PSELP_Pos;

int vin_millivolts = (1000 * 0.6 * 6 * uBit.io.P0.getAnalogValue()) / 1024;

return vin_millivolts;
}

/**
* Initialize the ADC to be able to measure Vdd voltage.
*
* To do this it initialises and finds the ADC channel for pin P0,
* which is hijacked by get_vdd_millivolts() to measure Vdd instead.
*/
static void vdd_adc_init() {
// Ensure CODAL has configured the P0 ADC channel
(void)uBit.io.P0.getAnalogValue();
(void)uBit.adc.getChannel(uBit.io.P0, true);
(void)uBit.io.P0.getAnalogValue();

// Now look in the MCU ADC channels for a channel configured for P0 (P0.2 AIN0)
for (size_t i = 0; i < NRF52_ADC_CHANNELS; i++) {
if (NRF_SAADC->CH[i].PSELP == (SAADC_CH_PSELP_PSELP_AnalogInput0 << SAADC_CH_PSELP_PSELP_Pos)) {
BATTERY_ADC_CHANNEL = i;
break;
}
}

// Do one measurement and throw away the result, as the first is always a bit off
(void)get_vdd_millivolts();
}

int main() {
uBit.init();

vdd_adc_init();

while (true) {
uBit.display.scroll("Hello world!");
ManagedString battery_mv = ManagedString(get_vdd_millivolts()) + " mV";

uBit.serial.send(battery_mv + "\r\n");
uBit.display.scroll(battery_mv);

uBit.sleep(1000);
}
}

0 comments on commit c42d121

Please sign in to comment.