Problem with parallel GPIO pins and NeoPixels

michaelu
Posts: 8
Joined: Sun Jan 28, 2018 5:29 pm
Location: München (Munich, Germany)

Problem with parallel GPIO pins and NeoPixels

Postby michaelu » Sun Jan 28, 2018 6:11 pm

Hi, I cut a 5m 300 LED strip into 10 strips of 30 LEDs each, and attached each strip to one of the GPIO pins 5,16,17,18,19,23,25,26,2,4.
Then I added a setpin(pin) function to the NeoPixel library, and programmed the strips in an outer loop, setting the pin, and an inner loop, for the 30 LED. This works nicely. Then I got the idea to bitbang all 10 pins in parallel. The (somewhat stripped down) code for this follows:

Code: Select all

void ICACHE_RAM_ATTR bitbang_send_pixels_800(uint8_t* pixels, uint16_t rowLen, uint16_t numPins, uint8_t* pins)
{
    uint32_t pinRegisters[numPins];
    uint8_t mask;
    uint8_t subpix;
    uint32_t cyclesStart;
    uint8_t *endp = pixels + rowLen; // end of first row
    uint32_t bv;
    uint32_t pinRegHigh = 0;

    for (uint16_t px = 0; px < numPins; px++) {
    	bv = _BV(pins[px]);
    	pinRegisters[px] = bv;
    	pinRegHigh |= bv;
    }

    // trigger immediately
    cyclesStart = _getCycleCount() - CYCLES_800;
    do
    {
    	for (mask = 0x80; mask != 0; mask >>= 1) {
        	uint8_t *pixp = pixels;

		uint32_t pinRegLow = 0;
    		for (uint16_t px = 0; px < numPins; px++) {
    			subpix = *pixp;
    			pixp += rowLen;
			if ((subpix & mask) == 0)
				pinRegLow |= pinRegisters[px];
    		}
		uint32_t cyclesNext = cyclesStart;
		do {
			cyclesStart = _getCycleCount();
		} while ((cyclesStart - cyclesNext) < CYCLES_800); // 300
		GPIO.out_w1ts = pinRegHigh;
    		do {
    			cyclesNext = _getCycleCount();
    		} while ((cyclesNext - cyclesStart) < CYCLES_800_T0H); // 96
		GPIO.out_w1tc = pinRegLow;
    		do {
    			cyclesNext = _getCycleCount()
    		} while ((cyclesNext - cyclesStart) < CYCLES_800_T1H); // 192
		GPIO.out_w1tc = pinRegHigh;
    	}
    	pixels++;
    } while (pixels < endp);
}
where rowLen is 30*4 for 30 Leds, 4 bytes/pixel for RGBW.
The idea is to set all pins high first, then clear all pins where the corresponding pixel bit is 0 after T0
This code shows very strange behaviour. If I set all 300 pixels to the same color, it works ok.
If I just set one line, e.g. to pure red, with values from 1 to 255, it shows the line, but in all possible
colours, depending on the value, e.g. blue with some white, and also neighboring lines have sometimes some Leds
set as well, often around column 21.
I tried a lot with the numbers for the CYCLES*-variables, or setting PULLUP or PULLDOWN together with OUTPUT,
but no effect. I notice some effect when I configure less than 10 lines. To me it looks a bit like crosstalk between the lines.
The power supply is 5v/20A, the ESP powered by USB, and ESP ground connected to power ground at a middle line,
All VCC and ground pins of the strips are connected together on the input side of the strips, the pins on the other side are
open.
I checked the code over and over again, dumping all the values, inspecting the pinRegLow values, for many hours,
and slowly reach the conclusion that it must be an electrical or timing problem.
I have not seen a GPIO initialzation taken place, but in the NeoEspBitBangMethodBase constructor pinMode(pin, OUTPUT) is called for all pins. Please remember, that everything works fine, if I set all pixels to the same value, i.e. pinRegLow is 0 or pinRegHigh.
Suggestions are very welcome!
Thank you, Michael
Last edited by michaelu on Mon Feb 05, 2018 2:23 pm, edited 1 time in total.

User avatar
Vader_Mester
Posts: 300
Joined: Tue Dec 05, 2017 8:28 pm
Location: Hungary
Contact:

Re: Problem with parallel GPIO pins

Postby Vader_Mester » Mon Jan 29, 2018 3:09 pm

First of all..., it depends which board you use (I guess Devboard with WROOM32).
You should be using level converter to conver the 3,3V IO of the ESP32, to 5V IO the neopixel needs. I guess, you have already done that. If not then.... :shock:

If it's a neopixel, and you use bitbang, it is always a timing issue.
:D
As I remember, it is very very veeeeery hard to do bitbang with Neopixels, because you need to keep timing of them exactly right.
The issue might be that ESP32 doesn't just execute code, rather gazillion of stuff is running in the background... at least writing in C language.

I think you better off making a function to produce the waveform with proper timing on the GPIO pins, runing under an IF function, to check if the bit you want to send is either a 0 or a 1 (so the function knows what to do).
Then you call this from some function that processes your RGBW code for this function to send out the GPIO pin.

You should watch this video to get the concept.

If you wanna do it right, buy a USB scope or a cheaper logic analyzer, to check after the timing.

Vader[BEN]

Code: Select all

task_t coffeeTask()
{
	while(atWork){
		if(!xStreamBufferIsEmpty(mug)){
			coffeeDrink(mug);
		} else {
			xTaskCreate(sBrew, "brew", 9000, &mug, 1, NULL);
			xSemaphoreTake(sCoffeeRdy, portMAX_DELAY);
		}
	}
	vTaskDelete(NULL);
}

michaelu
Posts: 8
Joined: Sun Jan 28, 2018 5:29 pm
Location: München (Munich, Germany)

Re: Problem with parallel GPIO pins

Postby michaelu » Mon Feb 05, 2018 2:21 pm

Preview: Re: Problem with parallel GPIO pins
This is now fixed, luckily. First I did what Vader told me to do: add level converters from 3.3 to 5 volt to each of the 10 GPIO pins.
This improved things a lot, still I saw Leds shining in unexpected places, obviously a timing problem. After a while I came up with the
following solution. Basically, in the bitbang method, right before I set all pins to 1, I compute the cycle count when the low bits
are cleared (T0), when the high bits are cleared (T1), and when the cycle repeats (T3). I am not sure why the original
NeoPixel code does not work for me. I added counters to the 3 loops, and found that once in a while the loops were executed differently. Anyway, I like my code better:

Code: Select all

void ICACHE_RAM_ATTR bitbang_send_pixels_800(uint8_t* pixels, uint16_t rowLen,
		uint16_t numPins, uint8_t* pins) {
	uint32_t pinRegisters[numPins];
	uint8_t mask;
	uint32_t cyclesStart, cyclesT0, cyclesT1, cyclesT2;
	uint8_t *endp = pixels + rowLen; // end of first row
	uint32_t bv;
	uint32_t pinRegHigh = 0;

	for (uint16_t px = 0; px < numPins; px++) {
		bv = _BV(pins[px]);
		pinRegisters[px] = bv;
		pinRegHigh |= bv;
	}

	cyclesT2 = _getCycleCount();
	do {
		for (mask = 0x80; mask != 0; mask >>= 1) {
			register uint8_t *pixp = pixels;
			register int lc = 0;
			register uint32_t pinRegLow = 0;
			register uint32_t cyclesNext;
			for (register uint16_t px = 0; px < numPins; px++) {
				// do the checks here while we are waiting on time to pass
				if ((*pixp & mask) == 0)
					pinRegLow |= pinRegisters[px];
				pixp += rowLen;
			}

			do {
				cyclesNext = _getCycleCount();
			} while (cyclesNext < cyclesT2);
			cyclesStart = _getCycleCount();
			cyclesT0 = cyclesStart + CYCLES_800_T0H;
			cyclesT1 = cyclesT0 + CYCLES_800_T1H;
			cyclesT2 = cyclesStart + CYCLES_800;
			// set high
#if defined(ARDUINO_ARCH_ESP32)
			GPIO.out_w1ts = pinRegHigh;
#else
			GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegHigh);
#endif
			do {
				cyclesNext = _getCycleCount();
			} while (cyclesNext < cyclesT0);

			// set low the pins that transmit a 0
#if defined(ARDUINO_ARCH_ESP32)
			GPIO.out_w1tc = pinRegLow;
#else
			GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegLow);
#endif
			// wait for the LOW
			do {
				cyclesNext = _getCycleCount();
				lc++;
			} while (cyclesNext < cyclesT1);
			// set low the pins that transmit a 1
#if defined(ARDUINO_ARCH_ESP32)
			GPIO.out_w1tc = pinRegHigh;
#else
			GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegHigh);
#endif
		}
		pixels++;
	} while (pixels < endp);
}


User avatar
Vader_Mester
Posts: 300
Joined: Tue Dec 05, 2017 8:28 pm
Location: Hungary
Contact:

Re: Problem with parallel GPIO pins

Postby Vader_Mester » Tue Feb 06, 2018 7:46 am

michaelu wrote:Preview: Re: Problem with parallel GPIO pins
This is now fixed, luckily. First I did what Vader told me to do: add level converters from 3.3 to 5 volt to each of the 10 GPIO pins.
This improved things a lot, still I saw Leds shining in unexpected places, obviously a timing problem. After a while I came up with the
following solution. Basically, in the bitbang method, right before I set all pins to 1, I compute the cycle count when the low bits
are cleared (T0), when the high bits are cleared (T1), and when the cycle repeats (T3). I am not sure why the original
NeoPixel code does not work for me. I added counters to the 3 loops, and found that once in a while the loops were executed differently. Anyway, I like my code better:

Code: Select all

void ICACHE_RAM_ATTR bitbang_send_pixels_800(uint8_t* pixels, uint16_t rowLen,
		uint16_t numPins, uint8_t* pins) {
	uint32_t pinRegisters[numPins];
	uint8_t mask;
	uint32_t cyclesStart, cyclesT0, cyclesT1, cyclesT2;
	uint8_t *endp = pixels + rowLen; // end of first row
	uint32_t bv;
	uint32_t pinRegHigh = 0;

	for (uint16_t px = 0; px < numPins; px++) {
		bv = _BV(pins[px]);
		pinRegisters[px] = bv;
		pinRegHigh |= bv;
	}

	cyclesT2 = _getCycleCount();
	do {
		for (mask = 0x80; mask != 0; mask >>= 1) {
			register uint8_t *pixp = pixels;
			register int lc = 0;
			register uint32_t pinRegLow = 0;
			register uint32_t cyclesNext;
			for (register uint16_t px = 0; px < numPins; px++) {
				// do the checks here while we are waiting on time to pass
				if ((*pixp & mask) == 0)
					pinRegLow |= pinRegisters[px];
				pixp += rowLen;
			}

			do {
				cyclesNext = _getCycleCount();
			} while (cyclesNext < cyclesT2);
			cyclesStart = _getCycleCount();
			cyclesT0 = cyclesStart + CYCLES_800_T0H;
			cyclesT1 = cyclesT0 + CYCLES_800_T1H;
			cyclesT2 = cyclesStart + CYCLES_800;
			// set high
#if defined(ARDUINO_ARCH_ESP32)
			GPIO.out_w1ts = pinRegHigh;
#else
			GPIO_REG_WRITE(GPIO_OUT_W1TS_ADDRESS, pinRegHigh);
#endif
			do {
				cyclesNext = _getCycleCount();
			} while (cyclesNext < cyclesT0);

			// set low the pins that transmit a 0
#if defined(ARDUINO_ARCH_ESP32)
			GPIO.out_w1tc = pinRegLow;
#else
			GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegLow);
#endif
			// wait for the LOW
			do {
				cyclesNext = _getCycleCount();
				lc++;
			} while (cyclesNext < cyclesT1);
			// set low the pins that transmit a 1
#if defined(ARDUINO_ARCH_ESP32)
			GPIO.out_w1tc = pinRegHigh;
#else
			GPIO_REG_WRITE(GPIO_OUT_W1TC_ADDRESS, pinRegHigh);
#endif
		}
		pixels++;
	} while (pixels < endp);
}

I'm happy you found a solution. Also don't forget to add termination resistors to the Neopixels.
Read this :)

Vader [BEN]

Code: Select all

task_t coffeeTask()
{
	while(atWork){
		if(!xStreamBufferIsEmpty(mug)){
			coffeeDrink(mug);
		} else {
			xTaskCreate(sBrew, "brew", 9000, &mug, 1, NULL);
			xSemaphoreTake(sCoffeeRdy, portMAX_DELAY);
		}
	}
	vTaskDelete(NULL);
}

michaelu
Posts: 8
Joined: Sun Jan 28, 2018 5:29 pm
Location: München (Munich, Germany)

Re: Problem with parallel GPIO pins and NeoPixels

Postby michaelu » Sat Feb 10, 2018 7:45 pm

Shortly after I wrote the last post I realized it was wrong in several respects.
First, under some circumstances the leds did still flicker. Second, there was
a programming bugs:
Instead of comparing the current cycle count against a number:

Code: Select all

		cyclesT0 = _getCycleCount() + CYCLES_T0;
		do {
			cyclesNext = _getCycleCount();
		} while (cyclesNext < cyclesT0);
do this:

Code: Select all

		cyclesStart = _getCycleCount();
		while ((_getCycleCount() - cyclesStart) < CYCLES_T0)
			;
because the first statement does not work if the cyclecount is about to wrap from
MAX_UNSIGNED to 0. Consider cyclesNext = 0xfffffff0 and cyclesTo = 0xA. Then the while
expression is false immediately.

Then I realzed that the above code could be simplified.
And lastly I experimented with the values for CYCLES_800_T0H. After experimenting
so much with the NeoPixels I think that only the "short" pulse time T0 is important.
It must not be too short, and of course, if it is too long, then the LED sees a 1
instead of a 0. The "long" pulse time T1 is not so sensitive. It is enough if it is not
so long that it is interpreted as reset. After several tryouts 40 gave the best results
on the ESP32. Both smaller and larger numbers showed more flicker.

I recorded the cycle count differences for the loops (C), and counted the number of times
the loops were executed (E), and found sometimes differences. With 30 Leds in a row and
4 bytes pixel size (RGBW) the 3 loops are iterated 960 times, and C and E were constant
maybe 930 times, and different 30 times. This happened of course with disabled interrupts,
so I have no real explanation for these variations. My guess is, that WLAN and, more
generally, FreeRTOS activities on core 0 sometimes slow down core 1, perhaps because
buses or memory are shared resources, and if core 0 is busy, core 1 runs slower.
(setup and loop of the sketch run on core 1, as a call
Serial.printf("running on core %d\n", xPortGetCoreID());
shows).
With CYCLES_800_T0H = 40 the "short" pulse time is apparently still short enough, even
if the loop takes a bit longer.

All my attempts to write a short test program that displays the same cycle variations failed.

And lastly, instead of disabling interrupts with portDIS/ENABLE_INTERRUPTS I now use
portENTER/EXIT_CRITICAL(&spinlock). The latter seems to have a "stronger" effect...

This is the current code, note the changed CYCLES*-Values:

Code: Select all


#define CYCLES_800_T0H  40 
#define CYCLES_800_T1H  288
#define CYCLES_800      300

void ICACHE_RAM_ATTR bitbang_send_pixels_800(uint8_t* pixels, uint16_t rowLen, uint16_t numPins, uint8_t* pins)
{
	uint32_t pinRegisters[numPins];
	uint8_t mask;
	register uint32_t cyclesStart;
	uint8_t *endp = pixels + rowLen; // end of first row
	uint32_t bv;
	uint32_t pinRegHigh = 0;

	for (uint16_t px = 0; px < numPins; px++) {
		bv = _BV(pins[px]);
		pinRegisters[px] = bv;
		pinRegHigh |= bv;
	}

	cyclesStart = _getCycleCount() - CYCLES_800; // start immediately
	do
	{
		for (mask = 0x80; mask != 0; mask >>= 1)
		{
			uint8_t *pixp = pixels;
			uint32_t pinRegLow = 0;


			for (uint16_t px = 0; px < numPins; px++) {
				// do the checks here while we are waiting on time to pass
				if ((*pixp & mask) == 0)
					pinRegLow |= pinRegisters[px];
				pixp += rowLen;
			}

			while ((_getCycleCount() - cyclesStart) < CYCLES_800)
				;
			cyclesStart = _getCycleCount();
			// set high
			GPIO.out_w1ts = pinRegHigh;
			while ((_getCycleCount() - cyclesStart) < CYCLES_800_T0H)
				;
			// set low the pins that transmit a 0
			GPIO.out_w1tc = pinRegLow;
			// wait for the LOW
			while ((_getCycleCount() - cyclesStart) < CYCLES_800_T1H)
				;
			// set low the pins that transmit a 1
			GPIO.out_w1tc = pinRegHigh;
		}
		pixels++;
	} while (pixels < endp);
}
Last edited by michaelu on Sun Feb 11, 2018 7:25 pm, edited 1 time in total.

michaelu
Posts: 8
Joined: Sun Jan 28, 2018 5:29 pm
Location: München (Munich, Germany)

Re: Problem with parallel GPIO pins and NeoPixels

Postby michaelu » Sun Feb 11, 2018 7:20 pm


User avatar
Vader_Mester
Posts: 300
Joined: Tue Dec 05, 2017 8:28 pm
Location: Hungary
Contact:

Re: Problem with parallel GPIO pins and NeoPixels

Postby Vader_Mester » Mon Feb 12, 2018 8:56 pm

Veeeeery nice!

If you want more consistency, you can use the ULP co-processor in the ESP32.

I know it sound crazy enough, but it can be very good, to keep the waveforms and cycle counts consistent.
Since ULP is more of a Real-Time controller than the ESP cores (because FreeRTOS is running), this would give you a much better result.

Also it is such a fun to program assembly isn't it :lol:

Code: Select all

task_t coffeeTask()
{
	while(atWork){
		if(!xStreamBufferIsEmpty(mug)){
			coffeeDrink(mug);
		} else {
			xTaskCreate(sBrew, "brew", 9000, &mug, 1, NULL);
			xSemaphoreTake(sCoffeeRdy, portMAX_DELAY);
		}
	}
	vTaskDelete(NULL);
}

yvesbazin
Posts: 2
Joined: Tue Feb 13, 2018 12:57 pm

Re: Problem with parallel GPIO pins and NeoPixels

Postby yvesbazin » Tue Feb 13, 2018 1:36 pm

Hello
Could you please an example on how you use portENTER/EXIT_CRITICAL(&spinlock)
I have some issues with the first led of each strip when the wifi is on.
thank you

some example of what I do
https://www.youtube.com/watch?v=4JQ5rrqL-L8
https://www.youtube.com/watch?v=PaJcA815olE

michaelu
Posts: 8
Joined: Sun Jan 28, 2018 5:29 pm
Location: München (Munich, Germany)

Re: Problem with parallel GPIO pins and NeoPixels

Postby michaelu » Sat Feb 02, 2019 7:12 am

Recently I returned to this, and found a better way to do it:
https://spin.atomicobject.com/2018/11/0 ... els-esp32/
With bitbanging in bitbang_send_pixels I never got completely rid of flickering. But the approach with rmt channels works nicely.

The new code is now:

Code: Select all

#include <Arduino.h>
#include "driver/rmt.h"

#define NUM_ROWS 10
#define NUM_COLS 30
#define NUM_CHANS 5 	/* we could use 8+2 instead of 5+5 */
#define BYTES_PER_LED 4 /* RGBW, 4 colors a 8 bit */
#define BITS_PER_LED 32 /* RGBW, 4 colors a 8 bit */
#define LED_BUFFER_ITEMS ((NUM_COLS * BITS_PER_LED)) // bits per row = 30*4*8=960

// These values are determined by measuring pulse timing with logic analyzer and adjusting to match datasheet.
#define T0H 14  // 0 bit high time
#define T1H 52  // 1 bit high time
#define TL  52  // low time for either bit
rmt_item32_t bitOn = {{{T1H, 1, TL, 0}}};
rmt_item32_t bitOff = {{{T0H, 1, TL, 0}}};

// This is the buffer which the hw peripheral will access while pulsing the output pin
rmt_item32_t led_data_buffer[NUM_ROWS][LED_BUFFER_ITEMS];
rmt_config_t config[NUM_CHANS];

void ws2812_control_init(uint8_t *pins)
{
	for (int chan = 0; chan < NUM_CHANS; chan++) {
		config[chan].rmt_mode = RMT_MODE_TX;
		config[chan].channel = RMT_CHANNEL_0 + chan;
		config[chan].gpio_num = pins[chan];
		config[chan].mem_block_num = 1;
		config[chan].tx_config.loop_en = false;
		config[chan].tx_config.carrier_en = false;
		config[chan].tx_config.idle_output_en = true;
		config[chan].tx_config.idle_level = 0;
		config[chan].clk_div = 2;

		ESP_ERROR_CHECK(rmt_config(&config[chan]));
		ESP_ERROR_CHECK(rmt_driver_install(config[chan].channel, 0, 0));
	}
}

void setup_rmt_data_buffer(uint8_t *pixels)
{
	for (int row = 0; row < NUM_ROWS; row++) {
		int bit = 0;
		for (int col = 0; col < NUM_COLS; col++) {
			for (int b = 0; b < BYTES_PER_LED; b++) {
				uint8_t bits_to_send = *pixels++;
				uint8_t mask = 0x80;
				for (uint8_t i = 0; i < 8; i++) {
					led_data_buffer[row][bit++] = (bits_to_send & mask) ? bitOn : bitOff;
					mask >>= 1;
				}
			}
		}
	}
}

void ws2812_write_leds(uint8_t *pixels, uint16_t rowLen, uint8_t *pins) {
	int rem = NUM_ROWS;
	int off = 0;
	int cnt = NUM_ROWS < NUM_CHANS ? NUM_ROWS : NUM_CHANS;

	setup_rmt_data_buffer(pixels);

	while (rem > 0) {
		for (int chan = 0; chan < cnt; chan++) {
			int pin = pins[off + chan];
			// is there an rmt_clear_pin?
			//ESP_ERROR_CHECK(rmt_set_pin(RMT_CHANNEL_0 + chan, RMT_MODE_TX, pin));

			pinMode(pin, OUTPUT);
		    pinMatrixOutAttach(pin, RMT_SIG_OUT0_IDX + chan, 0, 0);
		}
		for (int chan = 0; chan < cnt; chan++) {
			ESP_ERROR_CHECK(rmt_write_items(RMT_CHANNEL_0 + chan, led_data_buffer[off + chan], LED_BUFFER_ITEMS, false));
			rem--;
		}
		for (int chan = 0; chan < cnt; chan++) {
			ESP_ERROR_CHECK(rmt_wait_tx_done(RMT_CHANNEL_0 + chan, portMAX_DELAY));
		}
		for (int chan = 0; chan < cnt; chan++) {
			int pin = pins[off + chan];
		    pinMatrixOutDetach(pin, 0, 0);
		}
		off += cnt;
		cnt = rem < NUM_CHANS ? rem : NUM_CHANS;
	}
}

void bitbang_send_pixels_800(uint8_t* pixels, uint16_t rowLen, uint16_t numPins, uint8_t* pins)
{
	ws2812_write_leds(pixels, rowLen, pins);
}

void bitbang_initialize(uint8_t *pins) {
	ws2812_control_init(pins);
}

I have 10 rows, but there are "only" 8 RMT channels. I use 5 of them to display first rows 0..4, then 5..9.
So I assign the first 5 GPIO pins to channels 0..4, transmit the rows, then unassign the pins, and assign the second 5 GPIO pins. If you do not unassign, then the RMT channel drives 2 GPIO pins, which leads to strange looking results.
Bitbang_initialize() is called from NeoEspBitBangMethod.Initialize().
No flickering any more!

The drawback is, that you use lots of memory for the led_data_buffer, in my case 10 rows * 30 columns * 4 bytes per led(RGBW) * 8 bits per byte * 4 bytes per bit = 38400 bytes.

ESP_Sprite
Posts: 9757
Joined: Thu Nov 26, 2015 4:08 am

Re: Problem with parallel GPIO pins and NeoPixels

Postby ESP_Sprite » Tue Mar 28, 2023 12:46 am

JohnyWalter32 wrote:
Mon Mar 27, 2023 12:44 pm
There can be issues with using parallel GPIO pins and NeoPixels due to the amount of current required to drive the NeoPixels. Each NeoPixel can draw up to 60mA of current at full brightness, and when multiple NeoPixels are connected in parallel, the total current draw can quickly exceed the capabilities of the GPIO pins.
That makes no sense at all. Neopixels indeed take up to 60mA, but they do that from the power supply lines. The GPIO is connected to the data lines, which pull almost no current.

Who is online

Users browsing this forum: No registered users and 193 guests