PX4 / PX4-Autopilot

PX4 Autopilot Software
https://px4.io
BSD 3-Clause "New" or "Revised" License
8.41k stars 13.47k forks source link

DMA issue on fmuv6x with neopixel driver #20693

Closed sebglatz closed 1 year ago

sebglatz commented 1 year ago

Describe the bug

Running the neopixel driver on a holybro pixhawk 6x does not produce the expected signal. I use the dma-version of this driver: srgbled_dma.cpp and the latest release (v1.13.2).

To Reproduce

Added to fmu-v6x/src/board_config.h:

#  define BOARD_HAS_N_S_RGB_LED          10
#  define S_RGB_LED_DMA                  DMAMAP_TIM5_CH4 /* Note, this is DMAMUX1 and DMA-Mux-Index 101 */
#  define S_RGB_LED_TIMER                5   /* timer 5   */
#  define S_RGB_LED_CHANNEL              4   /* channel 4  */
// #  define S_RGB_LED_CHANNELN           1   /* channel 4N */
#  define S_RGB_LED_TIM_GPIO             GPIO_TIM5_CH4OUT_2 /* PI0 aka FMU_CH1*/

Build and flash, then in nsh: neopixel start

Expected behavior

I would expect to see a pwm-like signal on pin FMU_CH1 of two different duty-cycles. Total bit time is 1200 ns, 0b1(600ns|600ns) 0b0(300ns|900ns).

Log Files and Screenshots

The signal on FMU_CH1 is either constantly zero, or if I enable DMA-debug-prints and disable DMA on the serial console (because it would freeze otherwise) I get a weird non-const signal with some pulses that have nothing to do with the expected behaviour.

log: https://logs.px4.io/plot_app?log=b063b920-a603-4820-9d73-71acd0f30f78

Drone (please complete the following information):

holybro pixhawk 6x on baseboard mini, powered with USBC.

Additional context

With print statements in stm32h7/stm32_dma.c (for debugging), the serial console freezes. Configuring the serial console without DMA would solve the freezing issue.

sebglatz commented 1 year ago

@davids5 do you have any hints on how to debug this?

FrankvVeelen commented 1 year ago

Did you get any further with this? running into similar issues

dagar commented 1 year ago

Did you get any further with this? running into similar issues

@FrankvVeelen What hardware do you have exactly and what are you seeing? Specific lights, flight controller, how it's connected, etc.

FrankvVeelen commented 1 year ago

I have a PX4 FMU v6x without IO controller. The neopixels are connected to PWM_FMU7 and PWM_FMU8 via a level translator (from 3.3V to 5V) What i have done is, starting with a "clean" release 1.13.2 repository EDIT: (commit 2295ee91649f30f6135139ff38be5289ec2d5246):

if defined(USE_S_RGB_LED_DMA)

define S_RGB_LED_DMA DMAMUX1_TIM12_CH1

define S_RGB_LED_TIMER 12 / timer 12 /

define S_RGB_LED_CHANNEL 1 / channel 1 /

// # define S_RGB_LED_CHANNELN 1 / channel 2N /

define S_RGB_LED_TIM_GPIO GPIO_TIM12_CH1OUT_1 // GPIO_TIM12_CH2OUT_2

endif

- Add the following to default.px4board:

neopixel stuff:

CONFIG_BOARD_COMPILE_DEFINITIONS="-DUSE_S_RGB_LED_DMA" CONFIG_DRIVERS_LIGHTS_NEOPIXEL=y

This then doesn't compile because of a change in ```stm32_dmasetup```, so i rewrote 

// stm32_dmasetup(dma_handle, // _TIM_REG(STM32_GTIM_DMAR_OFFSET), // (uint32_t) bits, // arraySize(bits), // SLED_DMA_SCR);

into:

struct stm32_dma_config_s config; config.paddr = _TIM_REG(STM32_GTIM_DMAR_OFFSET); config.maddr = (uint32_t) bits; config.ndata = arraySize(bits); config.cfg1 = SLED_DMA_SCR; config.cfg2 = 0; stm32_dmasetup(dma_handle, &config);


Which then didn't compile because of ```DMAMUX1_TIM12_CH1``` didn't exist.
I have very little knowledge of STM32s and DMA, but I figured I'd try changing an unused DMA map to be ```DMAMUX1_TIM12_CH1```. I then got it to compile, but it throws hardfaults everytime I do ```neopixel start``` in the mavlink console.
davids5 commented 1 year ago

@FrankvVeelen Did you validate that the dma_handle from stm32_dmachannel(S_RGB_LED_DMA); is not null?

davids5 commented 1 year ago

Where did DMAMUX1_TIM12_CH1 come from? I do not see a DMA source from TIM12 in arch/arm/src/stm32h7/hardware/stm32h7x3xx_dmamux.h

Also is It needs to be a in a DMAMAP_DMA12_XXXX like entry to provide a DMA{1|2} and a mux

FrankvVeelen commented 1 year ago

Hey @davids5 , thanks for taking a look at the problem. DMAMUX1_TIM12_CH1 was my shot in the dark at trying to get it to work, since DMA TIM12 does not seem to exist. Thanks for the pointer to DMAMAP_DMA12_XXXX!

Here is where I'm at now: I have for now changed the part in my board_config.h to:

#  define S_RGB_LED_DMA                  DMAMAP_DMA12_TIM1CH2_1
#  define S_RGB_LED_TIMER                1   /* timer 12    */
#  define S_RGB_LED_CHANNEL              2   /* channel 1  */
// #  define S_RGB_LED_CHANNELN             1   /* channel 2N */
#  define S_RGB_LED_TIM_GPIO             GPIO_TIM1_CH2OUT_2 // GPIO_TIM12_CH2OUT_2

In my understanding this should result in a LED control on PWM_FMU3. The code now compiles, and the FCU doesn't crash upon neopixel start. However the LEDs are also not driven. I have verified that the dma_handle is not null. At the moment I do not have access to a scope, but i will at a later moment. Is there any other way to verify that "something" is actually happening?

Another question I have: Since I have already produced a few PCBs for the flight controllers, I'm kind of stuck with using FMU7 & FMU8 for two different sets of LEDs. Both of those use TIM12. Is it not possible to use DMA with TIM12? Would it be possible with some changes in software, or is there a hardware limitation in the stm?

davids5 commented 1 year ago

Hey @davids5 , thanks for taking a look at the problem. DMAMUX1_TIM12_CH1 was my shot in the dark at trying to get it to work, since DMA TIM12 does not seem to exist. Thanks for the pointer to DMAMAP_DMA12_XXXX!

Here is where I'm at now: I have for now changed the part in my board_config.h to:

#  define S_RGB_LED_DMA                DMAMAP_DMA12_TIM1CH2_1
#  define S_RGB_LED_TIMER                1   /* timer 12    */
#  define S_RGB_LED_CHANNEL              2   /* channel 1  */
// #  define S_RGB_LED_CHANNELN             1   /* channel 2N */
#  define S_RGB_LED_TIM_GPIO             GPIO_TIM1_CH2OUT_2 // GPIO_TIM12_CH2OUT_2

In my understanding this should result in a LED control on PWM_FMU3. The code now compiles, and the FCU doesn't crash upon neopixel start. However the LEDs are also not driven. I have verified that the dma_handle is not null. At the moment I do not have access to a scope, but i will at a later moment. Is there any other way to verify that "something" is actually happening?

There are only 8 channels per DMA see for the utilization for the DMA . Yow may need trade off one of the DMA2 selections Maybe the debug console

Another question I have: Since I have already produced a few PCBs for the flight controllers, I'm kind of stuck with using FMU7 & FMU8 for two different sets of LEDs. Both of those use TIM12. Is it not possible to use DMA with TIM12? Would it be possible with some changes in software, or is there a hardware limitation in the stm?

Refer to DMAMUX1, DMA1 and DMA2 connections in the STM32H7 reference manual. I think only tim12_trgo is supported. IIRC the code needs to do synchronous update on the DMA request from the compare request and that is not wired to the DMA mux.

I am not sure what your application is, to advise you on how to solve the issues.

FrankvVeelen commented 1 year ago

Hey there again, I have got it "working" with PWM_FMU5. The leds turn on and respond to changes. The problem I'm still having is that the reset part of the protocol does not seem to work. I have got access to my oscilloscope today, and i can see all bits being transmitted as they should be, but the reset voltage seems to be randomly high or low (the time in between bits being sent).

Looking at the code I dont see any obvious way in which this bit would be set either, do you have an idea how it should be set? If needed I can provide some oscilloscope shots.

davids5 commented 1 year ago

@FrankvVeelen It has been a while since worked on this. I though it is was the lack of a transition that constituted a reset. Do you have the datasheet?

FrankvVeelen commented 1 year ago

I understand @davids5 . Thanks for your time providing this support. These are the "official" Adafruit ones. The datasheet specifies a "low voltage time" of >50us. The specific ones i use are a little bit different WS2812B-V5 and specify a "low voltage time" of >280us. All different versions I looked at do specify a low voltage time. It could of-course be the case that some modules do allow a no-transition time, but it's not what the spec says.

Maybe we could add driving the voltage low on the dma_callback, or somehow make sure that the last bit drives the output low? If you have any advice on what would be the best way to drive the output low in between packets, I can test it on my setup. I will also test with some official adafruit neopixels to see if they behave any different.

davids5 commented 1 year ago

Ok it is bad memory on my part....So it needs to be low. The toggle happens on compare. So 2 things might be happening:

1 )There is is comment: * [hi][lo]:{8 * 3 * 8} [ffff] Last DMA will set the output low Note line 407 memset(bits, 0, sizeof(bits)); and you try to set it to memset(bits, 0xff, sizeof(bits)); 2) The DMA completion is happening before the timer finishes and disabled the timer. The solution could be adding 1 more 16 bit word to bits and DMA all of bits but populate only the date in uint32_t i = 0, leds = 0; i < arraySize(bits) - 2

FrankvVeelen commented 1 year ago

Hey @davids5, that works to pull the line low in between transfers. I have found one more issue now though, which is that the data seems to be buffered somewhere and the timing seems to be off.

When i use led_control off in the mavlink console, the very first bit is updated to low, but the other bits aren't. If i repeat led_control off it turns all bits off. If i then do led_control on it only turns on the 1st bit. If i repeat led_control on it turns all bits on.

Furthermore, there seem to be 9 bits for the first LED, instead of 8. It seems that this is a timing issue, but playing with the size of bits doesn't seem to work.

Any idea what this could be?

davids5 commented 1 year ago

Could it be Line 387. Preloading the first bit? , try writing it to a 1 (SLED_rCCR = 1);

To see what the code is doing you can use the PROBE() macros. see board.h but avoid the FMU channel you are using! Adding PROBE(1,0) before the stm32_dmastart and PROBE(1,1); in the dma_callback will help to see the timing relative to the bit stream.

FrankvVeelen commented 1 year ago

Hello again @davids5 :). Setting SLED_rCCR = 0xFFFF; has fixed the 9th bit problem. Another problem I had was "random" glitches in the output, which I fixed by not initializing the channel I'm using as PWM output channel.

The output looks great now, and the LEDs are breathing happily :). I think I am now at the final issue: when adding neopixel start to either rc.board_defaults or rc.sensors the driver makes the first led green and then seems to crash. The driver reports to be controlling the leds when calling neopixel status, but no output is send to the gpio. The driver also doesn't respond well to neopixel stop, resulting in a timeout. If I then restart the driver with neopixel start the FCU crashes and restarts, once again turning the first LED green.

Maybe the driver is started before some of its' resources become available? If so could we delay starting the driver? I don't need the driver during startup.

davids5 commented 1 year ago

@FrankvVeelen, it could be an issues with the pwm output driver. To test it try adding a return 0; to the main. extern "C" __EXPORT int pwm_out_main(int argc, char *argv[]); If not, then it is time to debug the hardfault.

davids5 commented 1 year ago

@FrankvVeelen Also what are all the DMA assignments?

farhangnaderi commented 2 months ago

Hey there again, I have got it "working" with PWM_FMU5. The leds turn on and respond to changes. The problem I'm still having is that the reset part of the protocol does not seem to work. I have got access to my oscilloscope today, and i can see all bits being transmitted as they should be, but the reset voltage seems to be randomly high or low (the time in between bits being sent).

Looking at the code I dont see any obvious way in which this bit would be set either, do you have an idea how it should be set? If needed I can provide some oscilloscope shots.

Hey @FrankvVeelen How did you choose your GPIOs? I am trying to do this but no luck so far! Would be great if you could give a hint. @davids5 Is there anyway I can find GPIO Map for 6x board? Like how I can find the way GPIO_TIM1_CH2OUT_2 is defined?

omapex commented 1 day ago

I have followed the above steps and the code did compile and start the neopixel driver. The driver reports to be controlling the leds when calling neopixel status, but no output is send to the gpio.

GPIO_TIM1_CH2OUT_2 corresponds to which AUX pin on cube carrier board? Where can you see the labels for each AUX_X pin? How do I send the led_control commands to neopixel?

@FrankvVeelen, can you summarize all the steps and final changes you made to successfully get neopixel driver to perform led_control? @davids5 or anyone with solutions for this?

DronecodeBot commented 9 hours ago

This issue has been mentioned on Discussion Forum for PX4, Pixhawk, QGroundControl, MAVSDK, MAVLink. There might be relevant details there:

https://discuss.px4.io/t/neopixel-led-driver-control/42096/1