RIOT icon indicating copy to clipboard operation
RIOT copied to clipboard

cpu/sam0_common: adc: add support for differential mode

Open benpicco opened this issue 3 years ago • 0 comments

Contribution description

In differential mode the ADC measures the difference between two input voltages by setting the muxneg bits to the ADC pin number instead of GND. This voltage differential can also be negative, which results in us reading a singed 16 bit value from the ADC result register.

The muxneg component is written together with the muxpos, the .muxpos field of the ADC config already gets written straight to the INPUTCTRL register, so we can just add muxneg there as well.

To avoid having to introduce a second gpio pin member to adc_conf_chan_t (and keeping that in sync with the .muxpos member, I instead added an array of all ADC Pins to the sam0 CPUs.

Those are fixed and have a 1:1 mapping to GPIOs. This allows us to just set .muxpos and derive the GPIO pin from that. The .pin member is now unused and will be removed by a follow-up (API breaking) PR. Then we can also rename the .muxpos member to the more fitting .inputctrl.

Testing procedure

To test differential mode I used a 3.3V CAN transceiver to create a differential voltage. In dominant mode we should be able to measure the voltage differential between CANH and CANL. In recessive mode the voltage differential should be 0V. A second ADC line was configured to also measure the voltage differential against GND.

  • ADC reference is set to 3.3 V Vcc
  • ADC line 0 is differential input: voltage between CANH and CANL
  • ADC line 1 is normal input: voltage between CANH or CANL and GND
same54-xpro

ADC config

#define ADC_REF_DEFAULT                     ADC_REFCTRL_REFSEL_INTVCC1

static const adc_conf_chan_t adc_channels[] = {
    /* muxpos, dev */
    { .muxpos = ADC_INPUTCTRL_MUXPOS_AIN4 | ADC_INPUTCTRL_MUXNEG_AIN5 | ADC_INPUTCTRL_DIFFMODE, .dev = ADC0},
    { .muxpos = ADC_INPUTCTRL_MUXPOS_AIN4 | ADC_INPUTCTRL_MUXNEG_GND, .dev = ADC0},
};
2022-05-30 18:14:20,801 - INFO # ADC_LINE(0): 724 (2333 mV)
2022-05-30 18:14:20,803 - INFO # ADC_LINE(1): 962 (3100 mV)
2022-05-30 18:14:20,906 - INFO # ADC_LINE(0): 724 (2333 mV)
2022-05-30 18:14:20,908 - INFO # ADC_LINE(1): 962 (3100 mV)
2022-05-30 18:14:21,011 - INFO # ADC_LINE(0): 724 (2333 mV)
2022-05-30 18:14:21,013 - INFO # ADC_LINE(1): 962 (3100 mV)
2022-05-30 18:14:21,117 - INFO # ADC_LINE(0): 724 (2333 mV)
2022-05-30 18:14:21,119 - INFO # ADC_LINE(1): 962 (3100 mV)
2022-05-30 18:14:21,221 - INFO # ADC_LINE(0): 724 (2333 mV)

# swap wires around

2022-05-30 18:14:23,847 - INFO # ADC_LINE(0): -726 (-2339 mV)
2022-05-30 18:14:23,849 - INFO # ADC_LINE(1): 236 (760 mV)
2022-05-30 18:14:23,952 - INFO # ADC_LINE(0): -726 (-2339 mV)
2022-05-30 18:14:23,954 - INFO # ADC_LINE(1): 236 (760 mV)
2022-05-30 18:14:24,056 - INFO # ADC_LINE(0): -726 (-2339 mV)
2022-05-30 18:14:24,059 - INFO # ADC_LINE(1): 236 (760 mV)
2022-05-30 18:14:24,161 - INFO # ADC_LINE(0): -726 (-2339 mV)
2022-05-30 18:14:24,164 - INFO # ADC_LINE(1): 236 (760 mV)
2022-05-30 18:14:24,266 - INFO # ADC_LINE(0): -726 (-2339 mV)
2022-05-30 18:14:24,269 - INFO # ADC_LINE(1): 237 (763 mV)

# recessive mode

2022-05-30 19:02:53,789 - INFO # ADC_LINE(0): -2 (-6 mV)
2022-05-30 19:02:53,791 - INFO # ADC_LINE(1): 699 (2252 mV)
2022-05-30 19:02:53,893 - INFO # ADC_LINE(0): -2 (-6 mV)
2022-05-30 19:02:53,896 - INFO # ADC_LINE(1): 699 (2252 mV)
2022-05-30 19:02:53,998 - INFO # ADC_LINE(0): -2 (-6 mV)
2022-05-30 19:02:54,000 - INFO # ADC_LINE(1): 699 (2252 mV)
2022-05-30 19:02:54,102 - INFO # ADC_LINE(0): -2 (-6 mV)
2022-05-30 19:02:54,105 - INFO # ADC_LINE(1): 700 (2255 mV)
2022-05-30 19:02:54,207 - INFO # ADC_LINE(0): -2 (-6 mV)
2022-05-30 19:02:54,209 - INFO # ADC_LINE(1): 699 (2252 mV)
2022-05-30 19:02:54,312 - INFO # ADC_LINE(0): -2 (-6 mV)
2022-05-30 19:02:54,314 - INFO # ADC_LINE(1): 699 (2252 mV)
samd20-xpro

ADC config

#define ADC_REF_DEFAULT                     ADC_REFCTRL_REFSEL_AREFB /* PA04 connected to Vcc */

static const adc_conf_chan_t adc_channels[] = {
    /* muxpos, dev */
    { .muxpos = ADC_INPUTCTRL_MUXPOS_PIN0 | ADC_INPUTCTRL_MUXNEG_PIN1 | ADC_INPUTCTRL_DIFFMODE },
    { .muxpos = ADC_INPUTCTRL_MUXPOS_PIN0 | ADC_INPUTCTRL_MUXNEG_GND },
};
2022-05-30 18:58:23,286 - INFO # ADC_LINE(0): 726 (2339 mV)
2022-05-30 18:58:23,287 - INFO # ADC_LINE(1): 962 (3100 mV)
2022-05-30 18:58:23,288 - INFO # ADC_LINE(0): 726 (2339 mV)
2022-05-30 18:58:23,290 - INFO # ADC_LINE(1): 962 (3100 mV)
2022-05-30 18:58:23,291 - INFO # ADC_LINE(0): 726 (2339 mV)
2022-05-30 18:58:23,292 - INFO # ADC_LINE(1): 963 (3103 mV)
2022-05-30 18:58:23,294 - INFO # ADC_LINE(0): 726 (2339 mV)
2022-05-30 18:58:23,294 - INFO # ADC_LINE(1): 962 (3100 mV)
2022-05-30 18:58:23,295 - INFO # ADC_LINE(0): 726 (2339 mV)
2022-05-30 18:58:23,296 - INFO # ADC_LINE(1): 962 (3100 mV)
2022-05-30 18:58:23,296 - INFO # ADC_LINE(0): 726 (2339 mV)
2022-05-30 18:58:23,296 - INFO # ADC_LINE(1): 962 (3100 mV)

# swap lines around

2022-05-30 18:58:34,147 - INFO # ADC_LINE(0): -728 (-2346 mV)
2022-05-30 18:58:34,149 - INFO # ADC_LINE(1): 237 (763 mV)
2022-05-30 18:58:34,253 - INFO # ADC_LINE(0): -728 (-2346 mV)
2022-05-30 18:58:34,255 - INFO # ADC_LINE(1): 237 (763 mV)
2022-05-30 18:58:34,359 - INFO # ADC_LINE(0): -728 (-2346 mV)
2022-05-30 18:58:34,361 - INFO # ADC_LINE(1): 237 (763 mV)
2022-05-30 18:58:34,465 - INFO # ADC_LINE(0): -728 (-2346 mV)
2022-05-30 18:58:34,467 - INFO # ADC_LINE(1): 237 (763 mV)
2022-05-30 18:58:34,571 - INFO # ADC_LINE(0): -728 (-2346 mV)
2022-05-30 18:58:34,573 - INFO # ADC_LINE(1): 238 (766 mV)
2022-05-30 18:58:34,677 - INFO # ADC_LINE(0): -728 (-2346 mV)
2022-05-30 18:58:34,679 - INFO # ADC_LINE(1): 237 (763 mV)

# recessive mode

2022-05-30 19:01:13,792 - INFO # ADC_LINE(0): 0 (0 mV)
2022-05-30 19:01:13,794 - INFO # ADC_LINE(1): 704 (2268 mV)
2022-05-30 19:01:13,897 - INFO # ADC_LINE(0): -2 (-6 mV)
2022-05-30 19:01:13,900 - INFO # ADC_LINE(1): 704 (2268 mV)
2022-05-30 19:01:14,003 - INFO # ADC_LINE(0): 0 (0 mV)
2022-05-30 19:01:14,005 - INFO # ADC_LINE(1): 703 (2265 mV)
2022-05-30 19:01:14,108 - INFO # ADC_LINE(0): 0 (0 mV)
2022-05-30 19:01:14,111 - INFO # ADC_LINE(1): 705 (2271 mV)

Issues/PRs references

benpicco avatar May 30 '22 17:05 benpicco

Maybe I should push a bit more for ADC NG?

maribu avatar Aug 25 '22 09:08 maribu

Something so I can look at the periph_conf and know how to connect the board.

MrKevinWeiss avatar Aug 30 '22 17:08 MrKevinWeiss

I wrote

This script
#include <stdbool.h>
#include <stdio.h>

#define GPIO_PIN(a, b) { #a, b }
#define ARRAY_SIZE(a) (sizeof((a)) / sizeof((a)[0]))

typedef struct {
	const char *port;
	unsigned pin;
} gpio_t;

#define MUXNEG_MAX 7
#define IS_SAMD2X 0

/**
 * @brief   Pins that can be used for ADC input
 */
static const gpio_t sam0_adc_pins[2][16] = {
    {   /* ADC0 pins */
        GPIO_PIN(PA, 2), GPIO_PIN(PA, 3), GPIO_PIN(PB, 8),  GPIO_PIN(PB, 9),
        GPIO_PIN(PA, 4), GPIO_PIN(PA, 5), GPIO_PIN(PA, 6),  GPIO_PIN(PA, 7),
        GPIO_PIN(PA, 8), GPIO_PIN(PA, 9), GPIO_PIN(PA, 10), GPIO_PIN(PA, 11),
        GPIO_PIN(PB, 0), GPIO_PIN(PB, 1), GPIO_PIN(PB, 2),  GPIO_PIN(PB, 3)
    },
    {   /* ADC1 pins */
        GPIO_PIN(PB, 8),  GPIO_PIN(PB, 9),  GPIO_PIN(PA, 8), GPIO_PIN(PA, 9),
        GPIO_PIN(PC, 2),  GPIO_PIN(PC, 3),  GPIO_PIN(PB, 4), GPIO_PIN(PB, 5),
        GPIO_PIN(PB, 6),  GPIO_PIN(PB, 7),  GPIO_PIN(PC, 0), GPIO_PIN(PC, 1),
        GPIO_PIN(PC, 30), GPIO_PIN(PC, 31), GPIO_PIN(PD, 0), GPIO_PIN(PD, 1)
    }
};

static const char* _num(unsigned i)
{
	static char buf[4];
	snprintf(buf, sizeof(buf), "%u", i);
	return buf;
}

static void print_alias(bool neg)
{
	for (unsigned j = 0; j < ARRAY_SIZE(sam0_adc_pins); ++j) {
		if (j) {
			puts("");
		}
		for (unsigned i = 0; i < ARRAY_SIZE(sam0_adc_pins[j]); ++i) {
			if (neg && i > MUXNEG_MAX) {
				break;
			}
			printf("#define ADC%s_INPUTCTRL_MUX%s_%s%02u ADC_INPUTCTRL_MUXPOS_%cIN%u /**< Alias for %cIN%u */\n",
				ARRAY_SIZE(sam0_adc_pins) > 1 ? _num(j) : "",
				neg ? "NEG" : "POS",
				sam0_adc_pins[j][i].port, sam0_adc_pins[j][i].pin,
				IS_SAMD2X ? 'P' : 'A', i,
				IS_SAMD2X ? 'P' : 'A', i
				);
		}
	}
}

int main(void)
{
	printf("/**\n * @brief ADC pin aliases\n * @{\n */\n");
	print_alias(false);
	puts("");
	print_alias(true);
	printf("/** @} */\n");
	return 0;
}

to generate the alias defines.

benpicco avatar Aug 31 '22 16:08 benpicco