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

Portenta H7: ADC Implementation #76

Open
mjs513 opened this issue Feb 21, 2025 · 3 comments
Open

Portenta H7: ADC Implementation #76

mjs513 opened this issue Feb 21, 2025 · 3 comments

Comments

@mjs513
Copy link

mjs513 commented Feb 21, 2025

In order to get ADC working on the H7 used the Giga Overlay as the model. The following changes were made to the arduino_portenta_h7_m7.overlay:


&adc1 {
	pinctrl-0 = <&adc1_inp12_pc2
    &adc1_inp13_pc3
    &adc1_inp18_pa4
    &adc1_inp3_pa6
		&adc1_inp0_pa0_c
		&adc1_inp1_pa1_c>;
	pinctrl-names = "default";
	st,adc-clock-source = <SYNC>;
	st,adc-prescaler = <4>;
	status = "okay";

	#address-cells = <1>;
	#size-cells = <0>;

	channel@c {
		reg = <12>;
		zephyr,gain = "ADC_GAIN_1";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_MAX>;
		zephyr,resolution = <16>;
	};
	channel@d {
		reg = <13>;
		zephyr,gain = "ADC_GAIN_1";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_MAX>;
		zephyr,resolution = <16>;
	};
	channel@12 {
		reg = <18>;
		zephyr,gain = "ADC_GAIN_1";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_MAX>;
		zephyr,resolution = <16>;
	};
	channel@3 {
		reg = <3>;
		zephyr,gain = "ADC_GAIN_1";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_MAX>;
		zephyr,resolution = <16>;
	};

	/* PA0_C and PA1_C */
	channel@0 {
		reg = <0>;
		zephyr,gain = "ADC_GAIN_1";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_MAX>;
		zephyr,resolution = <16>;
	};
	channel@1 {
		reg = <1>;
		zephyr,gain = "ADC_GAIN_1";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_MAX>;
		zephyr,resolution = <16>;
	};
};

&adc3 {
	pinctrl-0 = <&adc3_inp0_pc2_c
		&adc3_inp1_pc3_c>;
	pinctrl-names = "default";
	st,adc-clock-source = <SYNC>;
	st,adc-prescaler = <4>;
	status = "okay";

	#address-cells = <1>;
	#size-cells = <0>;

	channel@0 {
		reg = <0>;
		zephyr,gain = "ADC_GAIN_1";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_MAX>;
		zephyr,resolution = <16>;
	};
	channel@1 {
		reg = <1>;
		zephyr,gain = "ADC_GAIN_1";
		zephyr,reference = "ADC_REF_INTERNAL";
		zephyr,acquisition-time = <ADC_ACQ_TIME_MAX>;
		zephyr,resolution = <16>;
	};
};
.....
		adc-pin-gpios =	<&gpioz 0 0>,			/* analog only */
					<&gpioz 1 0>,			/* analog only */
					<&gpioz 2 0>,			/* analog only */
					<&gpioz 3 0>,			/* analog only */
          <&gpioc 2 0>,
          <&gpioc 3 0>,
          <&gpioa 4 0>,
          <&gpioa 6 0>;
...
  io-channels =	<&adc1 0>,
				<&adc1 1>,
        <&adc3 0>,
        <&adc3 1>,
        <&adc1 12>,
        <&adc1 13>,
				<&adc1 18>,
				<&adc1 3>;        
	};

In the conf file enabled CONFIG_ADC=y

Also added the pure_analog_pins,h and ,cpp files modified to use the H7 Analog Pins A0-A3.

Rung out the analog pins a0-a7 using a analog joystick from adafruit:

void setup() {
  // put your setup code here, to run once:
  pinMode(LED_BUILTIN, OUTPUT);
  //pinMode(10, OUTPUT);  // PC2
  Serial.begin(115200);
  while (!Serial && millis() < 4000) {}
}

void loop() {
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  //digitalWrite(10, !digitalRead(10));
  Serial.print(analogRead(A0)); Serial.print("\t");
  Serial.print(analogRead(A1)); Serial.print("\t");
  Serial.print(analogRead(A2)); Serial.print("\t");
  Serial.print(analogRead(A3)); Serial.print("\t");
  Serial.print(analogRead(A4)); Serial.print("\t");
  Serial.print(analogRead(A5)); Serial.print("\t"); 
  Serial.print(analogRead(A6)); Serial.print("\t");
  Serial.print(analogRead(A7)); Serial.println();
  delay(250);
  if (Serial.available() ) {
    while (Serial.read() != -1) {}
    //analogRead(A4);
  }

}

While A0-A3 and A6/A7 worked as expected found A4 and A5 were non operational. We tried a few things but no luck.

@KurtE instrumented a simple test sketch to print out the pin configs

void print_gpio_regs(const char *name, GPIO_TypeDef *port) {
  //printk("GPIO%s(%p) %08X %08X %08x\n", name, port, port->MODER, port->AFR[0], port->AFR[1]);
  Serial.print("GPIO");
  Serial.print(name);
  Serial.print(" ");
  Serial.print(port->MODER, HEX);
  Serial.print(" ");
  Serial.print(port->AFR[0], HEX);
  Serial.print(" ");
  Serial.println(port->AFR[1], HEX);
}

void show_all_gpio_regs() {
  print_gpio_regs("A", (GPIO_TypeDef *)GPIOA_BASE);
  print_gpio_regs("B", (GPIO_TypeDef *)GPIOB_BASE);
  print_gpio_regs("C", (GPIO_TypeDef *)GPIOC_BASE);
  print_gpio_regs("D", (GPIO_TypeDef *)GPIOD_BASE);
  print_gpio_regs("E", (GPIO_TypeDef *)GPIOE_BASE);
  print_gpio_regs("F", (GPIO_TypeDef *)GPIOF_BASE);
  print_gpio_regs("G", (GPIO_TypeDef *)GPIOG_BASE);
  print_gpio_regs("H", (GPIO_TypeDef *)GPIOH_BASE);
  print_gpio_regs("I", (GPIO_TypeDef *)GPIOI_BASE);
  print_gpio_regs("J", (GPIO_TypeDef *)GPIOJ_BASE);
  print_gpio_regs("K", (GPIO_TypeDef *)GPIOK_BASE);
}


void setup() {
  // put your setup code here, to run once:
  pinMode(LED_BUILTIN, OUTPUT);
  //pinMode(10, OUTPUT);  // PC2
  Serial.begin(115200);
  while (!Serial && millis() < 4000) {}
  show_all_gpio_regs();
}

void loop() {
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  //digitalWrite(10, !digitalRead(10));
  Serial.print(analogRead(A0));
  Serial.print("\t");
  Serial.print(analogRead(A1));
  Serial.print("\t");
  Serial.print(analogRead(A2));
  Serial.print("\t");
  Serial.print(analogRead(A3));
  Serial.print("\t");
  Serial.print(analogRead(A4));
  Serial.print("\t");
  Serial.print(analogRead(A5));
  Serial.print("\t");
  Serial.print(analogRead(A6));
  Serial.print("\t");
  Serial.print(analogRead(A7));
  Serial.println();
  delay(250);
  if (Serial.available()) {
    show_all_gpio_regs();
    while (Serial.read() != -1) {}
    Serial.println("Paused");
    while (Serial.read() == -1) {}
    while (Serial.read() != -1) {}
    //analogRead(A4);
  }
}

and found that pins A4 and A5 modes were set to AF5 which according to the spec

Image

identifed a4 and a5 as MISO and MOSI for SPI2.

@KurtE found there was an open issue [RFC] Add device init/de-init functionality in Zephyr related to this.

So besides some PWM pins being non-operational we now have some analog pins non-operational.

@KurtE
Copy link

KurtE commented Feb 21, 2025

As @mjs513 mentioned, the test sketch output: GPIOC FFFFAAAA 22BB55BA 0
The first HEX is the MODER For C2/C3 it shows both of them with mode 2 or AF and the AF's are 5

I was curious, so I hacked up a version like pinMode to try to set the pin to Analog.

 #include "zephyrInternal.h"
 static const struct gpio_dt_spec arduino_pins[] = {DT_FOREACH_PROP_ELEM_SEP(
   DT_PATH(zephyr_user), digital_pin_gpios, GPIO_DT_SPEC_GET_BY_IDX, (, ))};


  void pinModeAnalog(pin_size_t pinNumber) {
     gpio_pin_configure_dt(&arduino_pins[pinNumber],
                           GPIO_INPUT /*| GPIO_ACTIVE_HIGH */ | GPIO_MODE_ANALOG);
 }
 



void print_gpio_regs(const char *name, GPIO_TypeDef *port) {
  //printk("GPIO%s(%p) %08X %08X %08x\n", name, port, port->MODER, port->AFR[0], port->AFR[1]);
  Serial.print("GPIO");
  Serial.print(name);
  Serial.print(" ");
  Serial.print(port->MODER, HEX);
  Serial.print(" ");
  Serial.print(port->AFR[0], HEX);
  Serial.print(" ");
  Serial.println(port->AFR[1], HEX);
}

void show_all_gpio_regs() {
  print_gpio_regs("A", (GPIO_TypeDef *)GPIOA_BASE);
  print_gpio_regs("B", (GPIO_TypeDef *)GPIOB_BASE);
  print_gpio_regs("C", (GPIO_TypeDef *)GPIOC_BASE);
  print_gpio_regs("D", (GPIO_TypeDef *)GPIOD_BASE);
  print_gpio_regs("E", (GPIO_TypeDef *)GPIOE_BASE);
  print_gpio_regs("F", (GPIO_TypeDef *)GPIOF_BASE);
  print_gpio_regs("G", (GPIO_TypeDef *)GPIOG_BASE);
  print_gpio_regs("H", (GPIO_TypeDef *)GPIOH_BASE);
  print_gpio_regs("I", (GPIO_TypeDef *)GPIOI_BASE);
  print_gpio_regs("J", (GPIO_TypeDef *)GPIOJ_BASE);
  print_gpio_regs("K", (GPIO_TypeDef *)GPIOK_BASE);
}


void setup() {
  // put your setup code here, to run once:
  pinMode(LED_BUILTIN, OUTPUT);
  //pinMode(10, OUTPUT);  // PC2
  Serial.begin(115200);
  while (!Serial && millis() < 4000) {}
  pinMode(19, INPUT);
  pinMode(20, INPUT);
  pinModeAnalog(19);
  pinModeAnalog(20);
  show_all_gpio_regs();
}

void loop() {
  digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
  //digitalWrite(10, !digitalRead(10));
  Serial.print(analogRead(A0));
  Serial.print("\t");
  Serial.print(analogRead(A1));
  Serial.print("\t");
  Serial.print(analogRead(A2));
  Serial.print("\t");
  Serial.print(analogRead(A3));
  Serial.print("\t");
  Serial.print(analogRead(A4));
  Serial.print("\t");
  Serial.print(analogRead(A5));
  Serial.print("\t");
  Serial.print(analogRead(A6));
  Serial.print("\t");
  Serial.print(analogRead(A7));
  Serial.println();
  delay(250);
  if (Serial.available()) {
    show_all_gpio_regs();
    while (Serial.read() != -1) {}
    Serial.println("Paused");
    while (Serial.read() == -1) {}
    while (Serial.read() != -1) {}
    //analogRead(A4);
  }
}

I found it could change the MODER register to 0's which is Analog, but still not getting valid analog from those
pins. But currently have my setup with those two pins using ADC2 which I believe that MBED version was using (ALT0).

I noticed it left the AF field in the other register as 5, but not sure that matters. May try to use the RAW version instead.

@KurtE
Copy link

KurtE commented Mar 11, 2025

@mjs513 @facchinm @pillo79

The Analog issues on the Portenta H7, actually two main issues.

First issue: Duplicate pins:
The first is A4 and A5 don't work. This is because their GPIO pins PC_2 and PC_3 are duplicated physically on these boards
A4(19) is PC_2 is also D10 - So the enumeration code that reads in the tables from the device tree sums the indexes so
it downed up as 29, A5(20) is also D8(8) so it showed up in the Analog table as looking for pin 28.

I have sort of fix for this, in that in the pin Table for 19,20, I changed them to Z4 and Z5, but I did not change that in the Analog to GPIO table, so A4=10 and A5=8, and those work.

		adc-pin-gpios =	<&gpioz 0 0>,			/* analog only */
				<&gpioz 1 0>,			/* analog only */
				<&gpioz 2 0>,			/* analog only */
				<&gpioz 3 0>,			/* analog only */
				<&gpioc 2 0>,
				<&gpioc 3 0>,
				<&gpioa 4 0>,
				<&gpioa 6 0>,
				<&gpioz 4 0>,			/* Hack for D19  */
				<&gpioz 5 0>;			/* Hack for D20 */
                    
...       
  io-channels =	<&adc1 0>,
				<&adc1 1>,
				<&adc3 0>,
				<&adc3 1>,
				<&adc1 12>,
				<&adc1 13>,
				<&adc1 18>,
				<&adc1 3>,        
				<&adc1 12>, /* Hack for D19 */
				<&adc1 13>; /* Hack for D20 */
	};

These are up in my Fork/Branch: https://github.com/KurtE/ArduinoCore-zephyr/tree/Portenta_h7_spi_uart_pin_names
Which also has the other current stuff. More in the pr:
#82

The digital code for D19/D20 will fail, as there is no glue. However, if you hook up anything to this pin D20 and do any
digital calls on D8, they will act on the D20 pin.

The confusion on some of these pins is not new, there was a thread about these pins almost 3 years ago;
https://forum.arduino.cc/t/portenta-h7-attention-not-all-pins-are-usable/1007703?u=kurte
 
Second issue: The Analog code does nothing to switch the IO pin into Analog mode. It only works on those pins
which have nothing else defined on them as the MODER register defaults to analog (3). That will also break if your sketch
uses the pin in a mixed way:
like:

pinMode(21, INPUT);
if (digitalRead(21)) {
   delay(5); // give time for other side to switch
  return analogRead(21);
}

There is nothing to switch the two bits in the appropriate MODER register (back) to 0x3

There may be a hack that can work for STM32 boards, in maybe calling gpio_pin_configure_dt or gpio_pin_configure
with the port/pin with special bit that says ANALOG. I believe I saw it in the STM32 gpio driver code, where you can
do something like:

 gpio_pin_configure_dt(&arduino_pins[pinNumber],
                           GPIO_INPUT | GPIO_ACTIVE_HIGH | GPIO_MODE_ANALOG);

However these flags may be specific for STM32... boards, maybe not supported by Portenta C33, or UNO R4 or ...
Wonder if this part needs to be addressed more at the zephyr level?

@KurtE
Copy link

KurtE commented Mar 13, 2025

Taking a look at the C33 stuff, I don't see any logical GPIO settings of Analog for the Renesas processors, so I don't think the approach of trying some gpio_pin_configure will work in this case.

Wondering with the init/deinit PR that was pulled in (post new release). What is the granularity of it?
For example I assume you can say to init SPI1 or SPI2, but what about Analog or PWM, can you tell it to init/deinit as the pin level?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants