This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

What is the purpose of defining a port when working with GPIO? And how do you properly do it.

Hello!

I have been working through examples lately trying to get the GPIO and PWM to work. But one issue I keep coming up to is how to define pins as input or output, and how to use GPIO pins in general. I see in all the examples they use nodes and define ports and stuff, but honestly I don't understand what this is and why you do it.

Suppose I just want a button on a breadboard as input, then make a led light on the same breadboard light up when it's pressed. Do I have to make two devices and use that as parameter when defining the gpio_pin_configure? And what would the flag be?

Also, what is the flag parameter.

In all older tutorials and examples I notice this wasn't a thing, and it makes it really difficult to follow and learn.

Thanks in advance.

Parents
  • and define ports and stuff

    I'll refer to Wikipedia for an explanation of a GPIO port:

    "A GPIO port is a group of GPIO pins (usually 8 GPIO pins) arranged in a group and controlled as a group" (link)

    I can see that it may not make sense to you because the nRF52832 only has one port:

    Unlike the nRF52840 for example, which has two ports:

    Since the Zephyr/NCS samples are written in a generic manner and should work for different boards/architectures, you need to specify the exact port.

    But one issue I keep coming up to is how to define pins as input or output, and how to use GPIO pins in general.

    gpio_input_no_dts.zip

    Above I have attached a simplifed sample, that demonstrates how to configure P0.13 as input an generate an interrupt on a pin change. If you press Button1 on the nRF52 DK and open a terminal you should get an output like: "Button1 pressed at 66542". The sample above does not use any information from the Device Tree (.overlay, .dts files) when configuring the GPIO.

    Let me go through the code. 

    gpio_dev = device_get_binding("GPIO_0");
    if (gpio_dev == NULL) {
    	printk("Could not get device GPIO_0\n");
    	return;
    }

    First you need to get a reference (device struct) to the Port you're going to use. Since the nRF52832 only has one port, you need to use Port 0. In order to successfully get a device by label, it needs to be defined in the Device Tree. You can see that gpio0 is defined in zephyr/boards/arm/nrf52dk_nrf52832/nrf52dk_nrf52832.dts and enabled in zephyr/boards/arm/nrf52dk_nrf52832/nrf52dk_nrf52832.dts (You can confirm this by searching for "GPIO_0" in the file <samples>/build/zephyr/zephyr.dts).

    err = gpio_pin_configure(gpio_dev, GPIO_PIN, GPIO_INPUT | GPIO_PULL_UP | GPIO_ACTIVE_LOW); 
    if (err != 0) {
    	printk("Failed to configure pin %d\n", GPIO_PIN);
    	return;
    }

    Next you need to configure the pin you want to use. The function gpio_pin_configure() has three parameters, the gpio device (which we got through device_get_binding()), the pin number to use and what flags to use.

    In the provided sample, I set GPIO_PIN to 13, which is connected to button1 on the nrf52dk_nrf52832. For the flag input parameter, you configure the pin to be input/output, pullup/pulldown or if the pin is in active low or active high state. Use the flags defined in zephyr\include\drivers\gpio.h and/or zephyr/include/dt-bindings/gpio/gpio.h

    	err = gpio_pin_interrupt_configure(gpio_dev, GPIO_PIN, GPIO_INT_EDGE_TO_ACTIVE);
    	if (err != 0) {
    		printk("failed to configure pin %d as interrupt\n", GPIO_PIN);
    		return;
    	}
    
    	gpio_init_callback(&button_cb_data, button_pressed, BIT(GPIO_PIN));
    	gpio_add_callback(gpio_dev, &button_cb_data);

    Eventually I enabled an interrupt for the pin and set the interrupt callback to run. You can see that the third parameter of gpio_pin_interrupt_configure() is also flags, and for that you can use the flags defined in zephyr/include/drivers/gpio.h (starts with GPIO_INT*).

    I see in all the examples they use nodes and

    As you can see in the sample I provided, you can configure the GPIO pin without using the Device tree (except for gpio0, which is enabled by default in the soc/board dts files). However, it is a nice practice to use the Device Tree to configure the hardware. In this way you can easily switch between the nRF52832 DK to an nRF52840 DK or to an nRF52832 custom board, you use the same main.c and create several overlay files (nrf52840dk_nrf52840.overlay and nrf52dk_nrf52832.overlay for example) where you specify the GPIO pins/flags according to the specific board.

    Down below is a modified sample of the gpio sample I uploaded above. I have modified it to get the pin number and the flags from the device tree instead of setting it explicitly in main.c. Check it out here:

    gpio_input_using_dts.zip

    I created an overlay file nrf52dk_nrf52832.overlay with the following information:

    / {
        gpiocustom {
            compatible = "gpio-keys";
            gpiocus0: gpiocus_0 {
                gpios = <&gpio0 13 (GPIO_PULL_UP | GPIO_ACTIVE_LOW)>;
                label = "Custom gpio 13";
            };
        };
        aliases {
            gpiocus0 = &gpiocus0;
        };
    };

    Then in main.c I read out the pin number and flags using:

    #define GPIO_CUS_NODE DT_ALIAS(gpiocus0)
    #if DT_NODE_HAS_STATUS(GPIO_CUS_NODE, okay)
    #define GPIO_CUS DT_GPIO_LABEL(GPIO_CUS_NODE, gpios)
    #define GPIO_CUS_PIN DT_GPIO_PIN(GPIO_CUS_NODE, gpios)
    #define GPIO_CUS_FLAGS DT_GPIO_FLAGS(GPIO_CUS_NODE, gpios)
    #else
    #error "Unsupported board: gpiocus0 devicetree alias is not defined"
    #endif

    Which I then fed into the gpio functions

    Suppose I just want a button on a breadboard as input, then make a led light on the same breadboard light up when it's pressed. Do I have to make two devices and use that as parameter when defining the gpio_pin_configure? And what would the flag be?

    Take a look at the Zephyr button sample, which actually does this: https://github.com/nrfconnect/sdk-zephyr/tree/199d339e98f91cd44f038bf4eb81e41e1d5ccac5/samples/basic/button 

    Best regards,

    Simon

  • Thank you for this answer! It was very detailed and enlightening.

    Suppose I would like to send a PWM signal through a GPIO pin. How would I go about defining a port, pin, values and flags in this case?

    I assume I would have to use the PWM library, but after trying to experiment making a servo move I have had no luck. It seems firstly that the pwm is given in microseconds, so I have to convert everything to milliseconds. That's ok, but also when I set the pulse and period such that they always are on I get that the voltage across is 0. And when I set it to be 0 it shows 5 volts. So it seems it's inverted. If I change the flag to 1, which is supposed to change the polarity it simply doesn't work.

    I tried using the servo motor example from zepyr. But I could not get that to work. Even after trying to get the overlay thing to work. I add the alias pwm-servo for the sw_pwm. But it triggers the error for not getting the okay status back. 

    I am working with the common MG90S servo with period 20ms, and pulse width from 1ms to 2ms. After setting the pwm signal to that I just get no response. However, if I attach a stepper motor I clearly see that there is pulses coming from the pin.

    Again, thank you for the great answer and examples!

  • Is there a reason you want to use the GPIO to generate a PWM signal instead of just using the PWM peripheral itself?

    Test the following sample (taken from 3 Create a light intensity controller)

     5531.pwm_simple.zip

    You can see that I have set the pin to 18 in the file nrf52dk_nrf52832.overlay. This is connected to LED2 on the nRF52 DK, so after building and programming this sample, you should see that LED2 blinks.

    You can discard my previous answer, since that is about configuring the GPIO peripheral, not the PWM peripheral.

    Best regards,

    Simon

  • Okay! That works.

    I may not have been clear enough. I would want to use the PWM library :) Just couldn't get it to work.

    However, in this example the led on the board, led 2, increases in intensity. But if I hook up a led to pin 18, it decreases in intensity. They seem inverted. Why is this the case? I also notice that in the dts file it says that it is ch0-inverted.

    If I would want to do this to any of the pins, say pin 3, how would I define this in the overlay device tree?

    Thanks again!

    Filip.

Reply
  • Okay! That works.

    I may not have been clear enough. I would want to use the PWM library :) Just couldn't get it to work.

    However, in this example the led on the board, led 2, increases in intensity. But if I hook up a led to pin 18, it decreases in intensity. They seem inverted. Why is this the case? I also notice that in the dts file it says that it is ch0-inverted.

    If I would want to do this to any of the pins, say pin 3, how would I define this in the overlay device tree?

    Thanks again!

    Filip.

Children
  • Faws said:
    However, in this example the led on the board, led 2, increases in intensity. But if I hook up a led to pin 18, it decreases in intensity. They seem inverted. Why is this the case?

    Take a look at this statement in the infocenter-->nRF52 DK-->Buttons and LEDs, which may clear up your confusion:

    "The LEDs are active low, meaning that writing a logical zero ('0') to the output pin will illuminate the LED"

    Faws said:
    I also notice that in the dts file it says that it is ch0-inverted.

    It seems like this property simply inverts the output, If I ran the program with the following main() (ch0-inverted is present under the pwm0 node in build/zephyr/zephyr.dts)

    void main(void)
    {
    	printk("PWM Application has started!\r\n");
    	uint8_t pulse = 0;
    	const struct device *pwm_dev = device_get_binding(PWM_DEVICE_NAME);
    	if (!pwm_dev) {
    		printk("Cannot find %s!\n", PWM_DEVICE_NAME);
    		return;
    	}
        if (pwm_pin_set_usec(pwm_dev, PWM_CH0_PIN, PWM_PERIOD, PWM_PERIOD-10, 0)) {
    		printk(".");
        }
    
    }

    I got the following output:

    If I then deleted the ch0-inverted property:

    //nrf52dk_nrf52832.overlay
    
    &pwm0 {
    	ch0-pin = < 18 >;
    	/delete-property/ ch0-inverted;
    };

    The ouput is now inverted:

    Faws said:
    If I would want to do this to any of the pins, say pin 3, how would I define this in the overlay device tree?

    You simply set ch0-pin to pin 3, like this:

    &pwm0 {
    	ch0-pin = < 3 >;
    };

    Just as an additional note, since you were confused about ports earlier. If you're using e.g. an nRF52840 and would like to use Port 1 (instead of Port 0) and pin3 you would need to set ch0-pin to 32+3=35 

    Best regards,

    Simon

  • Thank you for the great replies!

    This makes so much more sense now.

    Have a good weekend!

Related