earlephilhower / arduino-pico

Raspberry Pi Pico Arduino core, for all RP2040 and RP2350 boards
GNU Lesser General Public License v2.1
1.98k stars 412 forks source link

Atomic read/write for both processors to GPIO #1137

Closed Gavin-Perry closed 1 year ago

Gavin-Perry commented 1 year ago

Earle, you have done an amazing job. I so much appreciate being able to stick with Arduino IDE even when using SDK routines.

My program was working great (driving a BLDC motor) until I duplicated the code to run a second motor on the second processor. From the RP2040 Datasheet I read: Each peripheral register block is allocated 4kB of address space, with registers accessed using one of 4 methods, selected by address decode. • Addr + 0x0000 : normal read write access • Addr + 0x1000 : atomic XOR on write • Addr + 0x2000 : atomic bitmask set on write • Addr + 0x3000 : atomic bitmask clear on write

I've been using simple Arduino routines digitalRead() and digitalWrite() if (digitalRead(BURSTPB1_PIN)==LOW) { // button pressed delay(20); // Plenty of debounce time for old buttons while(digitalRead(BURSTPB1_PIN)==LOW); // Wait for button release to do it // DO a step motion ... Here is the actual step: rather crudely implemented but it worked (with just one motor) byte Pos2Coils[6] = {5,4,6,2,3,1}; // Array of bits for motor phases to do one rotation ..... newPos2 = Pos2Coils[mod(Position2,6)]; // next position (0-5) if (bitRead(newPos2,0)) digitalWrite(CM2A,HIGH); else digitalWrite(CM2A,LOW); if (bitRead(newPos2,1)) digitalWrite(CM2B,HIGH); else digitalWrite(CM2B,LOW); if (bitRead(newPos2,2)) digitalWrite(CM2C,HIGH); else digitalWrite(CM2C,LOW); I should probably gather all three bits and then put them out together but not knowing how (except in assembler) I punted.

The problem is Motor 1 stops when motor 2 runs in certain conditions. I'd use bitWrite() but it wants: x: the numeric variable to which to write. n: which bit of the number to write, starting at 0 for the least-significant (rightmost) bit. b: the value to write to the bit (0 or 1). No clue what to put for a GPIO pin. Pins may or may not even be in the same byte since I just assigned them sequentially for easy PCB layout. I'd rather not have to respin the PCB to line up the bits.

Is there a way to do the atomic XOR write in Arduino? (SDK call?) How do I know if (or avoid) the 2 processors in contention?
There are delayMicroseconds() after every step, but that's not doing it. DoStep(CHAN); // Move the selected motor one phase step delayMicroseconds(1000000/speed);

Thanks for your help (or anyone else that has done this)

Gavin

Gavin-Perry commented 1 year ago

I'm reading Datasheet 2.3.1.2. GPIO Control but would like help to be sure I get this right.

Thanks again

earlephilhower commented 1 year ago

There might be a slightly faster way depending on the number of write ports on the GPIO block, but you'd need to ping the RPI Pico forums to get the HW designers' answer, but why not just use a mutex to protect GPIO accesses?

Define a shared global GPIO mutex:

auto_init_mutex(gpioMutex);

Then acquire the mutex before doing any atomic changes necessary:

    mutex_enter_blocking(&gpioMutex);
    <do all the GPIO changes on this core, including XOR, etc.>
    mutex_exit(@gpioMutex);

Both cores' access to the GPIOs should use that same mutex. It will guarantee only one core at a time can modify GPIO state.

earlephilhower commented 1 year ago

I'd also probably disable interrupts after taking the mutex (and before touching GPIOs), and re-enable them before exiting the mutex.

I don't know BLDC motor control, but if it is driven by a Gray code then only 1 bit will be XOR'd between steps so you could just write to that XOR register inside the mutex protection and avoid any glitching)

Bodmer commented 1 year ago

The data sheet provides links to the relevant SDK code so this is where the XOR write (aka toggle) function can be found, viz: https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/hardware_gpio/include/hardware/gpio.h#L710-L717

However, the GPIO operations are already atomic and made so at hardware level in the SIO block, so I suspect the problem is elsewhere in the code. Look at the duplicated code you mentioned and look for any shared (global) variables that the two processors are sharing, this is likely where the problem is. Post the code if you cannot spot the problem.