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"
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.
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?
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
>;
};
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:
Anyone know what could it happens??
Thanks,
Javier
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
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
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.
Thanks a lot, it is working!
The solution was:
Modifying the imx6ul-pinfunc.h and changing my used pin:
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
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)
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
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.
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.
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:
T
The file Dropbox - gpio_examples.tar.gz was deleted off your dropbox, do you still have it and can you link it again?
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?
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!
:smileyplus::smileyplus::smileyplus: christophertelemanngreat table with the overview of different, achievable, frequency!
Thanks Christopher,
Will list the results.
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);
}
}
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