Skip to content
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

cpu/sam0_common: adc: add support for differential mode #18146

Merged
merged 8 commits into from
Sep 27, 2022

Conversation

benpicco
Copy link
Contributor

@benpicco benpicco commented May 30, 2022

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 benpicco requested review from dylad, biboc and keestux as code owners May 30, 2022 17:04
@github-actions github-actions bot added Area: cpu Area: CPU/MCU ports Platform: ARM Platform: This PR/issue effects ARM-based platforms labels May 30, 2022
@benpicco benpicco requested a review from maribu May 30, 2022 17:11
@benpicco benpicco added the CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR label May 30, 2022
benpicco added 7 commits May 30, 2022 20:41
 - store result in int16_t to ensure proper sign extension
 - double differential result to account for bit lost for sign
rg define\ PIN_.*_AIN | grep ADC | cut -d' ' -f2 | sort | uniq | sed -E "s/PIN_(P[A-F])([0-9][0-9])B_ADC0_AIN([0-9]*)/\3 GPIO_PIN(\1, \2),/" | sort -n | grep GPIO | cut -d ' ' -f2- | sed -E "s/0([0-9])/\1/"
rg define\ PIN_.*_AIN | grep ADC | cut -d' ' -f2 | sort | uniq | sed -E "s/PIN_(P[A-F])([0-9][0-9])B_ADC1_AIN([0-9]*)/\3 GPIO_PIN(\1, \2),/" | sort -n | grep GPIO | cut -d ' ' -f2- | sed -E "s/0([0-9])/\1/"
@maribu
Copy link
Member

maribu commented Aug 25, 2022

Maybe I should push a bit more for ADC NG?

@benpicco benpicco requested a review from MrKevinWeiss August 30, 2022 16:48
Copy link
Contributor

@MrKevinWeiss MrKevinWeiss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to also add some table of defines so we don't lose the coupling of the labels on the boards to the pins.

Since we know what that is based on the CPU stuff it would just be some alias:

#define ADC_INPUTCTRL_MUXPOS_PA2 ADC_INPUTCTRL_MUXPOS_PIN8
#define ADC_INPUTCTRL_MUXPOS_PA3 ADC_INPUTCTRL_MUXPOS_PIN8
/* ... */
#define ADC_INPUTCTRL_MUXNEG_PA2 ADC_INPUTCTRL_MUXNEG_PIN8

... Maybe an oversimplification as you probably need some extra info for both ADCs.

@MrKevinWeiss
Copy link
Contributor

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

@benpicco
Copy link
Contributor Author

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.

Copy link
Contributor

@MrKevinWeiss MrKevinWeiss left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, I tested (though only with grounds and 3.3) before the aliases were added. I trust the aliasing was done correctly so you have my ACK good sir.

@benpicco
Copy link
Contributor Author

Thank you!

@benpicco benpicco merged commit baf1687 into RIOT-OS:master Sep 27, 2022
@benpicco benpicco deleted the cpu/sam0_common/adc-diffmode branch September 27, 2022 15:34
@maribu maribu added this to the Release 2022.10 milestone Oct 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: cpu Area: CPU/MCU ports CI: ready for build If set, CI server will compile all applications for all available boards for the labeled PR Platform: ARM Platform: This PR/issue effects ARM-based platforms
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants