Consecutive processing of output_pins in macro (SET_PIN) on rpi pico

I’m currently wondering if the output_pins in an macro are / should be processed in a consecutive manner.

I got following constellation here:

RPI Pico (MCU) connected to an RPI3 which runs MainsailOS.

What me got to the problem:
I tried to run a HD44780 Display connected at the RPI Pico, but that has not worked properly and now I’m trying to initialize the Display manually by settings output_pins.

I’m sure that the display is connected correctly, because I programmed the pi pico in micropython and display worked here flawlessly.

MCU und Output PIN Configuration:

[mcu rp_pico]
serial: /dev/serial/by-id/usb-Klipper_rp2040_E6611C08CB077322-if00
baud: 250000
restart_method: rpi_usb

[output_pin pico_led]
pin: rp_pico:gpio25

[output_pin lcd_rs]
pin: rp_pico:gpio0

[output_pin lcd_e]
pin: rp_pico:gpio1
value: 1

[output_pin lcd_d4]
pin: rp_pico:gpio5

[output_pin lcd_d5]
pin: rp_pico:gpio4

[output_pin lcd_d6]
pin: rp_pico:gpio3

[output_pin lcd_d7]
pin: rp_pico:gpio2

Now to my actual problem:

When I run the lcd_test macro

[gcode_macro all_data_low]
gcode:
  SET_PIN PIN=lcd_d7 VALUE=0
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d5 VALUE=0
  SET_PIN PIN=lcd_d4 VALUE=0

[gcode_macro pulse]
gcode:
  SET_PIN PIN=lcd_e VALUE=0
  SET_PIN PIN=lcd_e VALUE=1

[gcode_macro lcd_init]
gcode:
  SET_PIN PIN=lcd_d4 VALUE=1
  SET_PIN PIN=lcd_d5 VALUE=1
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d7 VALUE=0
  pulse

[gcode_macro lcd_test]
gcode:

  #Default States for GPIOs
  SET_PIN PIN=lcd_rs VALUE=0
  SET_PIN PIN=lcd_e VALUE=1
  all_data_low
  
  #LCD Initializatation (https://web.alfredstate.edu/faculty/weimandn/lcd/lcd_initialization/lcd_initialization_index.html - "4-Bit Interface, Initialization by Instruction")
  lcd_init
  lcd_init
  lcd_init

I can see the outputs behaving correctly with my logic analyzer.

(Picture must be unfortunately external because i’m not allowed to upload more then one picture in my post.)

RS=lcd_rs
EN=lcd_en
Bus:3 = lcd_d4
Bus:2 = lcd_d5
Bus:1 = lcd_d6
Bus:2 = lcd_d7

But when I just make small changes to the macro, it seams that the output_pins would no be handled consecutively.

When I run the extended version of lcd_test macro

[gcode_macro all_data_low]
gcode:
  SET_PIN PIN=lcd_d7 VALUE=0
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d5 VALUE=0
  SET_PIN PIN=lcd_d4 VALUE=0

[gcode_macro pulse]
gcode:
  SET_PIN PIN=lcd_e VALUE=0
  SET_PIN PIN=lcd_e VALUE=1

[gcode_macro lcd_init]
gcode:
  SET_PIN PIN=lcd_d4 VALUE=1
  SET_PIN PIN=lcd_d5 VALUE=1
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d7 VALUE=0
  pulse

[gcode_macro lcd_test]
gcode:

  #Default States for GPIOs
  SET_PIN PIN=lcd_rs VALUE=0
  SET_PIN PIN=lcd_e VALUE=1
  all_data_low
  
  #LCD Initializatation (https://web.alfredstate.edu/faculty/weimandn/lcd/lcd_initialization/lcd_initialization_index.html - "4-Bit Interface, Initialization by Instruction")
  lcd_init
  lcd_init
  lcd_init

  #Initial Function Set
  all_data_low
  SET_PIN PIN=lcd_d5 VALUE=1
  pulse

The my logic anlyzer shows the following output:

RS=lcd_rs
EN=lcd_en
Bus:3 = lcd_d4
Bus:2 = lcd_d5
Bus:1 = lcd_d6
Bus:2 = lcd_d7

From macro defintion I would have expected that the Bus:2 Signal should have been put zero much later.

Maybe I also get the wrong idea of how do this. I’m pretty new to the klipper world.

Can somebody please explain:

  • Is this behavior correct ?
  • Are the output_pins processed in a consecutive way ?

Kind regards,
Sebastian

Macro evaluation in Klipper is kind of black magic. Ref to:
https://www.klipper3d.org/Command_Templates.html#the-printer-variable

And

Thanks for the reply.

So according to this information:
When I try to achieve a sequence of multiple output pins to be written in an consecutive manner, i need to do a SET_PIN for all outputs, also if I just want to change a single output_pin?

So, my Idea from the post before isn’t working also.

I got the same outputs on the logic analyzer if I run this macro. (See first post)

[gcode_macro lcd_test2]
gcode:
  SET_PIN PIN=lcd_rs VALUE=0
  SET_PIN PIN=lcd_e VALUE=1
  SET_PIN PIN=lcd_d7 VALUE=0
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d5 VALUE=0
  SET_PIN PIN=lcd_d4 VALUE=0

  # Stage 1 INIT
  SET_PIN PIN=lcd_rs VALUE=0
  SET_PIN PIN=lcd_e VALUE=1
  SET_PIN PIN=lcd_d7 VALUE=0
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d5 VALUE=1
  SET_PIN PIN=lcd_d4 VALUE=1

  SET_PIN PIN=lcd_rs VALUE=0
  SET_PIN PIN=lcd_e VALUE=0
  SET_PIN PIN=lcd_d7 VALUE=0
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d5 VALUE=1
  SET_PIN PIN=lcd_d4 VALUE=1

  SET_PIN PIN=lcd_rs VALUE=0
  SET_PIN PIN=lcd_e VALUE=1
  SET_PIN PIN=lcd_d7 VALUE=0
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d5 VALUE=1
  SET_PIN PIN=lcd_d4 VALUE=1

  #stage 2 init
  SET_PIN PIN=lcd_rs VALUE=0 
  SET_PIN PIN=lcd_e VALUE=0
  SET_PIN PIN=lcd_d7 VALUE=0
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d5 VALUE=1
  SET_PIN PIN=lcd_d4 VALUE=1

  SET_PIN PIN=lcd_rs VALUE=0
  SET_PIN PIN=lcd_e VALUE=1
  SET_PIN PIN=lcd_d7 VALUE=0
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d5 VALUE=1
  SET_PIN PIN=lcd_d4 VALUE=1

  #stage 3 init
  SET_PIN PIN=lcd_rs VALUE=0
  SET_PIN PIN=lcd_e VALUE=0
  SET_PIN PIN=lcd_d7 VALUE=0
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d5 VALUE=1
  SET_PIN PIN=lcd_d4 VALUE=1

  SET_PIN PIN=lcd_rs VALUE=0
  SET_PIN PIN=lcd_e VALUE=1
  SET_PIN PIN=lcd_d7 VALUE=0
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d5 VALUE=1
  SET_PIN PIN=lcd_d4 VALUE=1

  #Initial Function Set
  SET_PIN PIN=lcd_rs VALUE=0
  SET_PIN PIN=lcd_e VALUE=1
  SET_PIN PIN=lcd_d7 VALUE=0
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d5 VALUE=1
  SET_PIN PIN=lcd_d4 VALUE=0

  SET_PIN PIN=lcd_rs VALUE=0
  SET_PIN PIN=lcd_e VALUE=0
  SET_PIN PIN=lcd_d7 VALUE=0
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d5 VALUE=1
  SET_PIN PIN=lcd_d4 VALUE=0

  SET_PIN PIN=lcd_rs VALUE=0
  SET_PIN PIN=lcd_e VALUE=1
  SET_PIN PIN=lcd_d7 VALUE=0
  SET_PIN PIN=lcd_d6 VALUE=0
  SET_PIN PIN=lcd_d5 VALUE=1
  SET_PIN PIN=lcd_d4 VALUE=0

Can this somehow be done, what i’m trying to achieve?

I’m by no means a Klipper macro expert, so beware my advice:
You could try to do something like:

[gcode_macro MAIN]
MY_SETUP_MACRO
MY_STAGE1_MACRO
MY_STAGE2_MACRO

or if this does not help, then following chain:

[gcode_macro MAIN]
MY_SETUP_MACRO

[gcode_macro MY_SETUP_MACRO]
...
...
MY_STAGE1_MACRO

[gcode_macro MY_STAGE1_MACRO]
...
...
MY_STAGE2_MACRO

This should ensure that the MY_* macros are executed consecutively when calling MAIN.

Thanks, but both of your suggestions weren’t working for what I’m trying to achieve.

Neither the main calls child macros, nor the macro chain way.

The error for me is still the same, the output BUS:3 is set to zero before it is called by the macro to do so.

I drawed a red line to indicate, what I would expect the signals to be. And what is programmed in the macro.

But at arround -250ms the BUS:3 pins get set to zero, but from the way the SET_PIN commands were defined in the macro, it should stay at high, and go to low “much” later. Like indicated with the red line.

It seams to me that macro is parsed before, and because there is no change between on the output_pin, the command setting it zero is executed earlier then defined in the macro sequence. :face_with_monocle:

I gave up using this approach. I found a way to get my HD44780 clone display working.

Thanks @Sineos for your tips.

1 Like

FYI, the set_output_pin updates are all ordered by time. However, your macros above would all be scheduled to be run at the same time, and nothing guarantees ordering between them. (That is, we only strive for set_output_pin to be ordered relative to other events that alter timing - not relative to other set_output_pins which do not nominally consume time.)

To answer the higher level question though (as you likely found) macros are not intended to drive display devices. This is something one would typically implement a klipper host python modules to achieve.

-Kevin

@koconnor

Thank you for your explanation.

At that point I just had no further idea how to continue on my display problem. And my idea was to do the initialization by toggling GPIOs and see where the difference are on my logic analyzer.

But I managed to get it working. It has just needed a few tweaks in the delays on the utilization of the parallel display interface.

See here: