Skip to content

Commit 7ba89f5

Browse files
committed
Add _bhb user module for Big Honking Button
BHB needs better accuracy from the ADC readings. To avoid changing the ADC configuration for all boards or adding complexity to AnalogIn, I implemented a custom user module to allow the BHB to talk to the ADC in the way that it needs to. I'm open to other approaches here, but this seemed like the least invasive and complex option.
1 parent 4d7b9cd commit 7ba89f5

File tree

3 files changed

+130
-0
lines changed

3 files changed

+130
-0
lines changed

ports/atmel-samd/boards/winterbloom_big_honking_button/mpconfigboard.mk

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,7 @@ CIRCUITPY_RGBMATRIX = 0
3232
CIRCUITPY_PS2IO = 0
3333
CIRCUITPY_USB_HID = 0
3434
CIRCUITPY_RTC = 0
35+
36+
# Enable board-specific modules
37+
USER_C_MODULES = boards/winterbloom_big_honking_button/usermods
38+
CFLAGS += -DMODULE_BHB_ENABLED=1
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#include "py/obj.h"
2+
#include "py/runtime.h"
3+
#include "shared-bindings/microcontroller/Pin.h"
4+
#include "samd/pins.h"
5+
#include "sam.h"
6+
7+
STATIC mp_obj_t _bhb_read_adc(void);
8+
9+
STATIC mp_obj_t _bhb_init_adc(void) {
10+
claim_pin(&pin_PB08);
11+
common_hal_never_reset_pin(&pin_PB08);
12+
13+
/* Enable the APB clock for the ADC. */
14+
PM->APBCMASK.reg |= PM_APBCMASK_ADC;
15+
16+
/* Enable GCLK0 for the ADC */
17+
GCLK->CLKCTRL.reg = GCLK_CLKCTRL_CLKEN |
18+
GCLK_CLKCTRL_GEN_GCLK0 |
19+
GCLK_CLKCTRL_ID_ADC;
20+
21+
/* Wait for bus synchronization. */
22+
while (GCLK->STATUS.bit.SYNCBUSY) {};
23+
24+
uint32_t bias = (*((uint32_t *) ADC_FUSES_BIASCAL_ADDR) & ADC_FUSES_BIASCAL_Msk) >> ADC_FUSES_BIASCAL_Pos;
25+
uint32_t linearity = (*((uint32_t *) ADC_FUSES_LINEARITY_0_ADDR) & ADC_FUSES_LINEARITY_0_Msk) >> ADC_FUSES_LINEARITY_0_Pos;
26+
linearity |= ((*((uint32_t *) ADC_FUSES_LINEARITY_1_ADDR) & ADC_FUSES_LINEARITY_1_Msk) >> ADC_FUSES_LINEARITY_1_Pos) << 5;
27+
28+
/* Wait for bus synchronization. */
29+
while (ADC->STATUS.bit.SYNCBUSY) {};
30+
31+
/* Write the calibration data. */
32+
ADC->CALIB.reg = ADC_CALIB_BIAS_CAL(bias) | ADC_CALIB_LINEARITY_CAL(linearity);
33+
34+
/* Use the internal VCC reference. This is 1/2 of what's on VCCA.
35+
since VCCA is 3.3v, this is 1.65v.
36+
*/
37+
ADC->REFCTRL.reg = ADC_REFCTRL_REFSEL_INTVCC1;
38+
39+
/* Capture 64 samples. */
40+
ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_64 | ADC_AVGCTRL_ADJRES(4);
41+
42+
/* Set the clock prescaler to 32, which is the same as the CircuitPython default.
43+
Set the resolution to 16 for averaging
44+
*/
45+
ADC->CTRLB.reg = ADC_CTRLB_PRESCALER_DIV32 |
46+
ADC_CTRLB_RESSEL_16BIT;
47+
48+
/* Configure the input parameters.
49+
50+
- GAIN_DIV2 means that the input voltage is halved. This is important
51+
because the voltage reference is 1/2 of VCCA. So if you want to
52+
measure 0-3.3v, you need to halve the input as well.
53+
54+
- MUXNEG_GND means that the ADC should compare the input value to GND.
55+
56+
- MUXPOS_PIN3 means that the ADC should read from AIN2, or PB08.
57+
*/
58+
ADC->INPUTCTRL.reg = ADC_INPUTCTRL_GAIN_DIV2 |
59+
ADC_INPUTCTRL_MUXNEG_GND |
60+
ADC_INPUTCTRL_MUXPOS_PIN2;
61+
62+
63+
/* Set PB08 as an input pin. */
64+
PORT->Group[1].DIRCLR.reg = PORT_PB08;
65+
66+
/* Enable the peripheral multiplexer for PB08. */
67+
PORT->Group[1].PINCFG[8].reg |= PORT_PINCFG_PMUXEN;
68+
69+
/* Set PB08 to function B which is analog input. */
70+
PORT->Group[1].PMUX[4].reg |= PORT_PMUX_PMUXE_B;
71+
72+
/* Wait for bus synchronization. */
73+
while (ADC->STATUS.bit.SYNCBUSY) {};
74+
75+
/* Enable the ADC. */
76+
ADC->CTRLA.bit.ENABLE = true;
77+
78+
/* Make one read and throw it away, as per the datasheet. */
79+
_bhb_read_adc();
80+
81+
return mp_const_none;
82+
}
83+
84+
STATIC mp_obj_t _bhb_read_adc(void) {
85+
/* Wait for bus synchronization. */
86+
while (ADC->STATUS.bit.SYNCBUSY) {};
87+
88+
/* Start the ADC using a software trigger. */
89+
ADC->SWTRIG.bit.START = true;
90+
91+
/* Wait for the result ready flag to be set. */
92+
while (ADC->INTFLAG.bit.RESRDY == 0);
93+
94+
/* Clear the flag. */
95+
ADC->INTFLAG.reg = ADC_INTFLAG_RESRDY;
96+
97+
/* Read the value. */
98+
uint32_t result = ADC->RESULT.reg;
99+
100+
return MP_OBJ_NEW_SMALL_INT(result);
101+
}
102+
103+
104+
STATIC MP_DEFINE_CONST_FUN_OBJ_0(_bhb_init_adc_obj, _bhb_init_adc);
105+
STATIC MP_DEFINE_CONST_FUN_OBJ_0(_bhb_read_adc_obj, _bhb_read_adc);
106+
107+
STATIC const mp_rom_map_elem_t _bhb_module_globals_table[] = {
108+
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__bhb) },
109+
{ MP_ROM_QSTR(MP_QSTR_init_adc), MP_ROM_PTR(&_bhb_init_adc_obj) },
110+
{ MP_ROM_QSTR(MP_QSTR_read_adc), MP_ROM_PTR(&_bhb_read_adc_obj) },
111+
};
112+
113+
STATIC MP_DEFINE_CONST_DICT(_bhb_module_globals, _bhb_module_globals_table);
114+
115+
const mp_obj_module_t _bhb_user_cmodule = {
116+
.base = { &mp_type_module },
117+
.globals = (mp_obj_dict_t*)&_bhb_module_globals,
118+
};
119+
120+
MP_REGISTER_MODULE(MP_QSTR__bhb, _bhb_user_cmodule, MODULE_BHB_ENABLED);
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
USERMODULES_DIR := $(USERMOD_DIR)
2+
3+
# Add all C files to SRC_USERMOD.
4+
SRC_USERMOD += $(USERMODULES_DIR)/bhb.c
5+
6+
CFLAGS_USERMOD += -I$(USERMODULES_DIR)

0 commit comments

Comments
 (0)