Simple GPIO Example - quandry

cancel
Showing results for 
Search instead for 
Did you mean: 

Simple GPIO Example - quandry

58,464 Views
Contributor IV

I am trying to build a simple GPIO Toggle Example on the IMX6UL UltraLite Board.

I have had no success yet.

I have created a simple HelloWorld project.

But, I can not expand it to include GPIO Access.

I can Not figure out how to include files such as gpio.h

But all of these fail: <gpio.h>  <Linux/gpio.h>  "Linux/gpio.h"  etc..

#include <stdio.h>

#include <gpio.h>

typedef unsigned long  U32;

typedef unsigned short U16;

typedef unsigned char  U8;

#define MX6SL_PAD_EPDC_SDCLK__GPIO1_IO23 0x110 0x400 0x000 0x5 0x0

// IOMUXC_SW_MUX_CTL_PAD_GPIO_1_23 = 20E_0000h base + 0x010

#define  GPIO_1_23_MUX_CTL       ( 0x2000000 + 0x0110 )

// IOMUXC_SW_PAD_CTL_PAD_GPIO_1_23 = 20E_0000h base + 0x000

#define  GPIO_1_23_PAD_CTL       ( 0x2000000 + 0x0400 )

// IOMUXC_SW_PAD_INPUT_GPIO_1_23   = 20E_0000h base + 0x0000

#define  GPIO_1_23_INPUT         ( 0x2000000 + 0x0000 )

// IOMUXC_SW_MUX_DATA_GPIO_1_23    = 0x05

#define  GPIO_1_23_MUX_DATA        ( 0x05 )

// IOMUXC_SW_CTL_DATA_GPIO_1_23    = 1 << 0x00

#define  GPIO_1_23_CTL_DATA     ( 0x01 << 0x00 )

#define MX6SL_PAD_EPDC_SDLE__GPIO1_IO24  0x114 0x404 0x000 0x5 0x0

#define MX6SL_PAD_EPDC_SDOE__GPIO1_IO25  0x118 0x408 0x000 0x5 0x0

#define MX6SL_PAD_EPDC_SDSHR__GPIO1_IO26 0x11c 0x40c 0x000 0x5 0x0

 

int _t_;

#define  msleep(n)  for(_t_=1; _t_<10000; _t_++){_t_=_t_;}

#define  true  1

//#define IMX_GPIO_NR(port, index) ((((port)-1)*32)+((index)&31))

#define  IMX_GPIO_NR(b,p)  ( ((b - 1) * 32) + p )

int main(int argc, char **argv)

{

    printf("Hello World!\n");

    gpio_request(IMX_GPIO_NR(3, 11), "external_gpio_0");

    gpio_export(IMX_GPIO_NR(3, 11), true);

    gpio_request(IMX_GPIO_NR(3, 27), "external_gpio_1");

    gpio_export(IMX_GPIO_NR(3, 27), true);

 gpio_direction_output( IMX_GPIO_NR(1,0) ,   1 );

    msleep(300);

    gpio_direction_output( IMX_GPIO_NR(2,18) ,  1 );

    msleep(300);

    gpio_direction_output( IMX_GPIO_NR(2,19) ,  1 );

    msleep(300);

    gpio_direction_output( IMX_GPIO_NR(1,20) ,  0 );

    gpio_direction_output( UART2_TX_DATA ,      0 );

    gpio_direction_output( IMX_GPIO_NR(1,21) ,  0 );

    gpio_direction_output( UART2_RX_DATA ,      0 );

    gpio_set_value(IMX_GPIO_NR(1, 0)  , 1);

    gpio_set_value(IMX_GPIO_NR(2, 18) , 1);

    gpio_set_value(IMX_GPIO_NR(2, 19) , 1);

    gpio_direction_output( IMX_GPIO_NR(1,23) ,  1 );

    gpio_set_value(IMX_GPIO_NR(1, 23)  , 1);

    msleep(1000);

    gpio_set_value(IMX_GPIO_NR(1, 23)  , 0);

    msleep(1000);

    gpio_set_value(IMX_GPIO_NR(1, 23)  , 1);

    msleep(1000);

    gpio_export(IMX_GPIO_NR(1, 23), true);

    gpio_direction_output( IMX_GPIO_NR(1,23) ,  1 );

    gpio_set_value(IMX_GPIO_NR(1, 23)  , 1);

    return 0;

}

I have also tried direct register access, but just got segment faults:

#include <stdio.h>

int main(int argc, char **argv)

{

    printf("Hello World!\n");

 

//printf("\nset MUX\n");

//      *((long*)0x0209C000??) = (5);  // -set MUX

printf("\nset DIR\n");

      *((long*)0x0209C004) = (1 << 23);  // -set GPIO_1_23.Dir = 1

printf("\nset DATA 23\n");

      *((long*)0x0209C000) = (1 << 23);  // -set GPIO_1_23.Dat = 1

 

    return 0;

}

Also this segmentation fault :

#include <stdio.h>

#define AIPS1_ARB_BASE_ADDR     0x02000000

#define AIPS1_ARB_END_ADDR      0x020FFFFF

#define AIPS2_ARB_BASE_ADDR     0x02100000

#define AIPS2_ARB_END_ADDR      0x021FFFFF

#define AIPS_TZ1_BASE_ADDR AIPS1_ARB_BASE_ADDR

#define AIPS_TZ2_BASE_ADDR AIPS2_ARB_BASE_ADDR

#define AIPS1_ON_BASE_ADDR (AIPS_TZ1_BASE_ADDR+0x7C000)

#define AIPS1_OFF_BASE_ADDR (AIPS_TZ1_BASE_ADDR+0x80000)

#define GPIO1_BASE_ADDR (AIPS1_OFF_BASE_ADDR+0x1C000)

#define IOMUXC_BASE_ADDR (AIPS1_OFF_BASE_ADDR+0x60000)

#define CCM_BASE_ADDR (AIPS1_OFF_BASE_ADDR+0x44000)

#define reg_32_CSU_SA (*(volatile U32*)(0x021C0218))

#define NSA_CP15 1

 

#define R32   (volatile unsigned long *)

#define R16   (volatile unsigned short *)

#define R8    (volatile unsigned char *)

typedef unsigned long  U32;

typedef unsigned short U16;

typedef unsigned char  U8;

 

int _t_;

#define  msleep(n)  for(_t_=1; _t_<10000; _t_++){_t_=_t_;}

#define  true  1

 

int main(int argc, char **argv)

{

    printf("Hello World!\n");

      //Write to DIR register [DIR]

      *R32(GPIO1_BASE_ADDR+4) = 0x00000004;  // 1 : GPIO 1_2  - output

      *R32 (GPIO1_BASE_ADDR) = 0x00000004;  // 1 --> GPIO 1_2

      *R32 ( GPIO1_BASE_ADDR) = 0  ;  // 0 --> GPIO 1_2

    return 0;

}

//

I also tried adding these lines below in the  conf/bblayers.conf  file, but still with No Results :

FILESPATH_append = "~/yocto_3.14.38-6UL/build/tmp/work/cortexa7hf-vfp-neon-poky-linux-gnueabi/linux-libc-headers/3.14-r0/linux-3.14/include/linux:"
FILESPATH_append = "~/yocto_3.14.38-6UL/build/tmp/work/cortexa7hf-vfp-neon-poky-linux-gnueabi/linux-libc-headers/3.14-r0/linux-3.14/include:"

FILESEXTRAPATHS_prepend := "${THISDIR}/files:"
FILESEXTRAPATHS_append = "~/yocto_3.14.38-6UL/build/tmp/work/cortexa7hf-vfp-neon-poky-linux-gnueabi/linux-libc-headers/3.14-r0/linux-3.14/include/linux:"
FILESEXTRAPATHS_append = "~/yocto_3.14.38-6UL/build/tmp/work/cortexa7hf-vfp-neon-poky-linux-gnueabi/linux-libc-headers/3.14-r0/linux-3.14/include:"

SRC_URI += "~/yocto_3.14.38-6UL/build/tmp/work/cortexa7hf-vfp-neon-poky-linux-gnueabi/linux-libc-headers/3.14-r0/linux-3.14/include/linux/gpio.h"

Labels (4)
Tags (2)
60 Replies

1,528 Views
Contributor I

I hit the same issue and got the solution. Not only the GPIO value need to be changed, but also the IOMUX register: the mux register and the control register.

I'm using the imx6q board and in arch/arm/boot/dts/imx6q-pinctrl.h, there's the pin table. I have to set the MX6QDL_PAD_EIM_DA0__GPIO3_IO00 as GPIO and output 1.

#define MX6QDL_PAD_EIM_DA0__GPIO3_IO00  0x114 0x428 0x000 0x5 0x0

The first value is the offset of mux register, the second is control register offset, the remain is: input register offset, mux value, input value.

Then what I did:

Get the exact mux register address. In the "i.MX 6Dual/6Quad Applications Processor Reference Manual", 36.4.65, it's the iomux mux register description.

Get the control register address. In 36.4.262, it's the control register description.

The last step which need the memtool:

root@imx6qsabresd:~# ./memtool -32 20E0114=0x5
Writing 32-bit value 0x5 to address 0x020E0114
root@imx6qsabresd:~# ./memtool -32 20E0428=0x1010b
Writing 32-bit value 0x1010B to address 0x020E0428
root@imx6qsabresd:~# cd /sys/class/gpio
root@imx6qsabresd:/sys/class/gpio# echo 64 > export
root@imx6qsabresd:/sys/class/gpio# echo out > gpio64/direction
root@imx6qsabresd:/sys/class/gpio# echo 1 > gpio64/value

It's done.

The gpio number is as upper: (block - 1) * 32 + offset. I'm using GPIO3 IO0, then the value is as (3-1) * 32 + 0 = 64.

0 Kudos

1,528 Views
Contributor I

Could someone please create a table, stating which /sys/class/gpio.... number corresponds to which of the "Arduino" compatible digital (and analog...) pins on the iMX6UL EVK dev board?

1,528 Views
Contributor III

Hi everybody,

I have a similar problem related with the topic talked it in this post. I am working with the imx6ul.

I want to change the value of the "MX6UL_PAD_CSI_DATA01__GPIO4_IO22". It means change it to low and high output value.

I included in my Device Tree the following lines:

        pinctrl-0 = <&pinctrl_hog_1>;
        imx6ul-ddr3-u15 {
                pinctrl_hog_1: hoggrp-1 {
                        fsl,pins = < 
                                MX6UL_PAD_CSI_DATA01__GPIO4_IO22                0x4001b8b1
                        >;
                };

Selection_010.png

As you can see, I can add the gpio2 in the filesystem. However, as you can see in the next picture, it continues at low level:

Selection_011.png

Anyone know what could it happens??

Thanks,

Javier

0 Kudos

1,528 Views
Contributor III

Hi again,

I saw one mistake, i did not use this formula to map the gpio port:

linux gpio number = (gpio_bank - 1) * 32 + gpio_bit (reference)
It means the gpio port will be 118 = (4-1)*+22

However, it continues failing.

Any idea? thanks

Javier

0 Kudos

1,528 Views
Contributor III

Hi,

I found the "mistake". It was working all the time. However, the "value" never changes. But, if I check the output with a voltmeter, it is working. Therefore, the problem is with the returned value.

To sum up, when the direction is "out" the software "value" is always 0. But, if i check the hardware pin, it changes.

Thanks,

Javier

0 Kudos

1,528 Views
NXP Employee
NXP Employee

Please search the SION and  IOMUXC.SW_MUX_CTL_PAD_SD1_DATA1.SION=1 in the entire page to see if you can find the answer.

We have already given  a clear answer for output and explain what is SION in the previous answer.

Please double check if you have set the SION bit.

Please search the SION and  IOMUXC.SW_MUX_CTL_PAD_SD1_DATA1.SION=1 in the entire page to see if you can find the answer.

We have already given  a clear answer for output and explain what is SION.

Please double check if you have set the SION bit for the MX6UL_PAD_CSI_DATA01__GPIO4_IO22.

1,528 Views
Contributor III

Thanks a lot, it is working!

The solution was:

Modifying the imx6ul-pinfunc.h and changing my used pin:

  • #define MX6UL_PAD_CSI_DATA01__GPIO4_IO22 0x01E8 0x0474 0x0000 0x15 0x0

This means SION bit = 1, and the 5 = ALT5

The changes must be done in imx6ul-14x14-evk.dts file:

BEFORE: MX6UL_PAD_CSI_DATA01__GPIO4_IO22                0x17059 (BIT SION NOT ACTIVATED)

AFTER:    MX6UL_PAD_CSI_DATA01__GPIO4_IO22                0x40017059 (BIT SION ACTIVATED)

Kind regards,

Javier

1,528 Views
NXP Employee
NXP Employee

Javier,

One more explaination.

If you want to add the SION, the original dts like this:

MX6UL_PAD_CSI_DATA01__GPIO4_IO22         0x17059

you need to modify and become

MX6UL_PAD_CSI_DATA01__GPIO4_IO22         0x40017059(0x40000000 |0x17059)

1,528 Views
Contributor III

Hi,

Thanks for the solution. It is working too. And it is better to change the devicetree and not the pinfunc definition.

I deleted the post in order to not confuse with the answer.

Regards,

Javier

0 Kudos

1,528 Views
NXP Employee
NXP Employee

Javier,

Your  code is not good enough, every time please don't modify  the headers.


In device tree for pinctl,  0x80000000 means   no pin config need
And 0x40000000 means  set SION .
So the correct change should be as following in imx6ul-14x14-evk.dts:

MX6UL_PAD_CSI_DATA01__GPIO4_IO22        0x40000000

Here is the test result:

Before set SION:

root@imx6ulevk:~# /unit_tests/memtool IOMUXC.SW_MUX_CTL_PAD_CSI_DATA01
SOC: i.MX6UL
IOMUXC   Addr:0x20e0000
  IOMUXC.SW_MUX_CTL_PAD_CSI_DATA01 Addr:0x020E01E8 Value:0x00000005 - SW_MUX_CTL Register
     IOMUXC.SW_MUX_CTL_PAD_CSI_DATA01.MUX_MODE(0..3)    :0x5
             Select 1 of 9 iomux modes to be used for pad: CSI_DATA01.
     IOMUXC.SW_MUX_CTL_PAD_CSI_DATA01.SION(4..4)        :0x0
             Force the selected mux mode Input path no matter of MUX_MODE functionality.

Set SION in dts:

imx6ul-14x14-evk.dts
67 &iomuxc {
268         pinctrl-names = "default";
269         pinctrl-0 = <&pinctrl_hog_1>;
270         imx6ul-evk {
271                 pinctrl_hog_1: hoggrp-1 {
272                         fsl,pins = <
273                                 MX6UL_PAD_UART1_RTS_B__GPIO1_IO19       0x17059 /* SD1 CD */
274                                 MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT    0x17059 /* SD1 VSELECT */
275                                 MX6UL_PAD_GPIO1_IO09__GPIO1_IO09        0x17059 /* SD1 RESET */
276                                 MX6UL_PAD_SNVS_TAMPER0__GPIO5_IO00      0x80000000
277                                 MX6UL_PAD_CSI_DATA01__GPIO4_IO22        0x40000000
278                         >;
279                 };


root@imx6ulevk:~# /unit_tests/memtool IOMUXC.SW_MUX_CTL_PAD_CSI_DATA01
SOC: i.MX6UL
IOMUXC   Addr:0x20e0000
  IOMUXC.SW_MUX_CTL_PAD_CSI_DATA01 Addr:0x020E01E8 Value:0x00000015 - SW_MUX_CTL Register
     IOMUXC.SW_MUX_CTL_PAD_CSI_DATA01.MUX_MODE(0..3)    :0x5
             Select 1 of 9 iomux modes to be used for pad: CSI_DATA01.
     IOMUXC.SW_MUX_CTL_PAD_CSI_DATA01.SION(4..4)        :0x1
             Force the selected mux mode Input path no matter of MUX_MODE functionality.

1,528 Views
Contributor III

I've tried to get a GPIO pin toggling (from kernel space) as well some time ago

and I find it insane how many hoops I have to jump through just to set a pin

high and low.

They really need to simplify this process.

0 Kudos

1,586 Views
Contributor III

there are different ways to access the gpios. you can access them from kernel- and userspace. in kernel space you have to use simple kernel modules. in user space you can toggle in bash using sysfs or in c using mmap and so on. here are some toggle speed results i get

Using sysfs (bash/c) [userspace]: ~3500Hz

calling /dev/mem using mmap [userspace]: ~800kHz

using gpio.h in a simple kernel module [kernelspace]: ~280kHz

ioremap() with writel [kernelspace] : ~1.6MHz

ioremap() with pointers [kernelspace]: ~5.5MHz

check out my code:

Dropbox - gpio_examples.tar.gz

42 Views
Contributor II

T

0 Kudos

50 Views
Contributor II

The file Dropbox - gpio_examples.tar.gz was deleted off your dropbox, do you still have it and can you link it again?

0 Kudos

1,528 Views
Contributor I

Hi Christopher Telemann:

In imx8mq platform,i not found GPIO1_DR,GPIO1_GDIR,GPIO1_PSR reg,i only fonud IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO15 and IOMUXC_SW_PAD_CTL_PAD_GPIO1_IO15 reg , Unable to configure such as dir reg ,please help!

what should i do?

0 Kudos

1,528 Views
Contributor I

Dear @Christopher Telemann, Could you please share the code Dropbox - gpio_examples.tar.gz code again.The link is no more valid

Thank a lot for your help in advance!

0 Kudos

1,528 Views
Contributor III

:smileyplus::smileyplus::smileyplus: christophertelemanngreat table with the overview of different, achievable, frequency!

1,586 Views
Contributor IV

Thanks Christopher,

Will list the results.

0 Kudos

1,586 Views
Contributor IV

Christopher - Thanks for the code !

I compiled your code and expanded it to about 8 more pins.

But, I still do not see any changes on the Port Pins.

- Maybe, there is something that I need to Enable the GPIO handler ?

(GPIO already seems enabled in MenuConfig)

-

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>

//// MUX reg:
#define IOMUXC                           0x020E0000
//// MUX PAD reg:
#define IOMUXC_SW_MUX_CTL_PAD_SD4_DATA0  0x020E031C

//// DATA reg:
#define GPIO1_DR                         0x0209C000
//// DIR reg:
#define GPIO1_GDIR                       0x0209C004

#define GPIO1_PSR                        0x0209C008

#define GPIO2_DR                         0x020A0000
//// DIR reg:
#define GPIO2_GDIR                       0x020A0004
#define GPIO2_PSR                        0x020A0008

static volatile uint32_t *gpio;

int main(int argc, char **argv)
{
    printf("\n*** Test GPIOs :\n");

    int fd ;

    printf("- Open /dev/mem ..\n");

    //Obtain handle to physical memory
    if ((fd = open ("/dev/mem", O_RDWR | O_SYNC) ) < 0) {
        printf("Unable to open /dev/mem: %s\n", strerror(errno));
        return -1;
    }

    printf("- Map MUX Pins ..\n");

    /**
      * Map some of the pin mux registers within the range of the
      * GPIO40 GPIO40 pin.
    **/
    gpio = (uint32_t *)mmap(0, (getpagesize() * 250), PROT_READ|PROT_WRITE, MAP_SHARED, fd, IOMUXC);

    // The first 3 bits are the only thing that matters here according to the
    // datasheet.
    *(gpio + ((IOMUXC_SW_MUX_CTL_PAD_SD4_DATA0 - IOMUXC) / 4)) = 0x5;
//    *(gpio + ((0x020E031C                      - IOMUXC) / 4)) = 0x5;    // UART1_RTS
    *(gpio + ((0x020E04B8                      - IOMUXC) / 4)) = 0x5;    // USB_OTG1_ID
    *(gpio + ((0x020E0660                      - IOMUXC) / 4)) = 0x5;    // USB_OTG2_OC
    *(gpio + ((0x020E01BC                      - IOMUXC) / 4)) = 0x5;    // SD1_CMD
    *(gpio + ((0x020E01C0                      - IOMUXC) / 4)) = 0x5;    // SD1_CLK
    *(gpio + ((0x020E01C4                      - IOMUXC) / 4)) = 0x5;    // SD1_DATA0
    *(gpio + ((0x020E01C8                      - IOMUXC) / 4)) = 0x5;    // SD1_DATA1
    *(gpio + ((0x020E01CC                      - IOMUXC) / 4)) = 0x5;    // SD1_DATA2
    *(gpio + ((0x020E01D0                      - IOMUXC) / 4)) = 0x5;    // SD1_DATA3

    printf("- MunMap ..\n");

    //Be a good neighbor
    if((munmap((void *)gpio, (getpagesize() * 250))) < 0){
        printf("munmap failed: %s\n", strerror(errno));
        exit(1);
    }

    printf("- MMap => gpio addr ..\n");

    /** The i.MX 6 Dual and Quad Processor have 7 GPIO banks
      * Each bank amounts to 1mb of memory which is perfectly
      * aligned for a memory map. Mmap will fail if the offset
      * is not a multiple of 1mb. For this example I just want
      * the GPIO2 bank which starts at 0x020A0000. I want the
      * bank (1mb or 250 pages of memory). I could get away with
      * a single page, but I am greedy :-P.
    **/
    gpio = (uint32_t *)mmap(0, (getpagesize() * 250), PROT_READ|PROT_WRITE, MAP_SHARED, fd, GPIO2_DR);

    if ((int32_t)gpio < 0){
        printf("mmap failed: %s\n", strerror(errno));
        exit(1);
    }

    printf("- Setup GPIOs DIR ..\n\n");

    /**
      * Setup GPIO40 or GPIO2_IO08 as an output.
      * gpio[1] is the GPIO direction register (GPIO2_GDIR).
      * We want to only want to set bit 8 of this register
      * to a binary 1 while leaving the rest intact.
    **/
    *(gpio + ((GPIO2_GDIR - GPIO2_DR) / 4)) = *(gpio + ((GPIO2_GDIR - GPIO2_DR) / 4)) | 1<<8;
    *(gpio + ((GPIO2_GDIR - GPIO2_DR) / 4)) = *(gpio + ((GPIO2_GDIR - GPIO2_DR) / 4)) | (0xFFFFFFFF);    // Gpio2.00-32
   
    printf("- Looping ..\n");

    /**
      * Blink GPIO40 every 1000 millisec=1sec //500 milliseconds.
      * gpio[0] is the GPIO data register (GPIO2_DR).
      * We only want to toggle bit 8 of this register
      * while leaving the rest alone.
    **/
    while(1)
    {
        printf(".\n");

        *(gpio + ((GPIO2_DR - GPIO2_DR) / 4)) = *(gpio + ((GPIO2_DR - GPIO2_DR) / 4)) ^ 1<<8;   
        *(gpio + ((GPIO2_DR - GPIO2_DR) / 4)) = *(gpio + ((GPIO2_DR - GPIO2_DR) / 4)) | (0xFFFFFFFF);    // Gpio2.00-32
        usleep(500000);  //  usleep(250000);

        *(gpio + ((GPIO2_DR - GPIO2_DR) / 4)) = *(gpio + ((GPIO2_DR - GPIO2_DR) / 4)) ^ 1<<8;   
        *(gpio + ((GPIO2_DR - GPIO2_DR) / 4)) = *(gpio + ((GPIO2_DR - GPIO2_DR) / 4)) & (0x00000000);    // Gpio2.00-32
        usleep(500000);  //  usleep(250000);
    }
}

1,586 Views
Contributor III

have you tried the other examples?check out the example "gpiotoggle.sh". just use the pin you want to toggle. that should work or you will get an error code during exporting the gpio.

what about gpioMod.c ?

this exmaple makes use of gpio.h. usage is very simple and the function gpio_request() should work or will give back an error.

this is how you calculate the GPIO Numbers:

linux gpio number = (<X> - 1) * 32 + <Y>

GPIO<X>_IO<Y> (example: GPIO3_IO23, X=3, Y=23)

cheers

christopher