Hello,
I'm working on imx6ul.
I'm trying to generate a very simple clock using gpio pin, I already configured gpios to work in userspace with "export" and I writed a simple program to put gpio HIGH and LOW as fast as possible.
But I'm facing issue during first 20ms, it seems gpio controller has warmup because the frequency generated during this first 20ms is superior than the rest of measure. (c.f. attachment.jpg)
The code is very simple and looks like that :
gpio_test.c
#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <time.h>
#include "imx6ul_zaack.h"
#define ESCAPE 27
#define KEY_UP 68
int imx6ul_init()
{
GPIOExport(PIN_DATA);
GPIOExport(PIN_CLK);
GPIODirection(PIN_DATA, OUT);
GPIODirection(PIN_CLK, OUT);
}
int main(int argc, char **argv)
{
// initialise μP
printf("Starting\n");
imx6ul_init();
printf("Initialization done\n");
int loop = 0;
int c;
struct timespec start;
start.tv_nsec = 10000L;
while(!loop){
for(c=0; c < 1000; c++)
{
gpio_set(PIN_DATA);
nanosleep(&start, NULL);
gpio_clr(PIN_DATA);
nanosleep(&start, NULL);
}
sleep(2);
}
return 0;
}
blink.h
/* blink.c
*
* Raspberry Pi GPIO example using sysfs interface.
* Guillermo A. Amaral B. <g@maral.me>
*
* This file blinks GPIO 4 (P1-07) while reading GPIO 24 (P1_18).
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define IN 0
#define OUT 1
#define LOW 0
#define HIGH 1
#define PIN_DATA 9
#define PIN_CLK 1
#define IMX_GPIO_NR(port, index) ((((port)-1)*32)+((index)&31))
static int
GPIOExport(int pin)
{
#define BUFFER_MAX 3
char buffer[BUFFER_MAX];
ssize_t bytes_written;
int fd;
fd = open("/sys/class/gpio/export", O_WRONLY);
if (-1 == fd) {
fprintf(stderr, "Failed to open export for writing!\n");
return(-1);
}
bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin);
write(fd, buffer, bytes_written);
close(fd);
return(0);
}
static int
GPIOUnexport(int pin)
{
char buffer[BUFFER_MAX];
ssize_t bytes_written;
int fd;
fd = open("/sys/class/gpio/unexport", O_WRONLY);
if (-1 == fd) {
fprintf(stderr, "Failed to open unexport for writing!\n");
return(-1);
}
bytes_written = snprintf(buffer, BUFFER_MAX, "%d", pin);
write(fd, buffer, bytes_written);
close(fd);
return(0);
}
static int
GPIODirection(int pin, int dir)
{
static const char s_directions_str[] = "in\0out";
#define DIRECTION_MAX 35
char path[DIRECTION_MAX];
int fd;
snprintf(path, DIRECTION_MAX, "/sys/class/gpio/gpio%d/direction", pin);
fd = open(path, O_WRONLY);
if (-1 == fd) {
fprintf(stderr, "Failed to open gpio direction for writing!\n");
return(-1);
}
if (-1 == write(fd, &s_directions_str[IN == dir ? 0 : 3], IN == dir ? 2 : 3)) {
fprintf(stderr, "Failed to set direction!\n");
return(-1);
}
close(fd);
return(0);
}
static int
GPIORead(int pin)
{
#define VALUE_MAX 30
char path[VALUE_MAX];
char value_str[3];
int fd;
snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", pin);
fd = open(path, O_RDONLY);
if (-1 == fd) {
fprintf(stderr, "Failed to open gpio value for reading!\n");
return(-1);
}
if (-1 == read(fd, value_str, 3)) {
fprintf(stderr, "Failed to read value!\n");
return(-1);
}
close(fd);
return(atoi(value_str));
}
static int
GPIOWrite(int pin, int value)
{
static const char s_values_str[] = "01";
char path[VALUE_MAX];
int fd;
snprintf(path, VALUE_MAX, "/sys/class/gpio/gpio%d/value", pin);
fd = open(path, O_WRONLY);
if (-1 == fd) {
fprintf(stderr, "Failed to open gpio value for writing!\n");
return(-1);
}
if (1 != write(fd, &s_values_str[LOW == value ? 0 : 1], 1)) {
fprintf(stderr, "Failed to write value!\n");
return(-1);
}
close(fd);
}
static int gpio_clr(int pin)
{
GPIOWrite(pin, LOW);
}
static int gpio_set(int pin)
{
GPIOWrite(pin, HIGH);
}
static int gpio_fsel(int pin, int direction)
{
GPIODirection(pin, direction);
}
static int gpio_lev(int pin)
{
return GPIORead(pin);
}
Any one know why the frequency is different ?
Thank you for your answer.
What you can try is to write to to the GPIO registers directly via memory map, instead of using the sysfs entries. This way you should have considerably less overhead and by that also be a little bit more deterministic.
However, this method is quite dirty and you should take care that nothing else (not even the kernel) is accessing the same register while you're messing around with it.
OpenOCD is doing it that way for example when bit-banging JTAG/SWD with GPIOs:
Public Git Hosting - openocd.git/blob - src/jtag/drivers/imx_gpio.c
For such protocols I would go with a Kernel driver, doing this from user space with GPIO bitbanging could be difficult.
Most bi-directional bus protocols contain some time constraints and timeouts which can be hard to fulfil from user space.
Maybe you find an already existing Kernel driver for something like that, which you can simply modify.
For one or the other "blinky" job it might be better to configure a PWM pin. It can be configured upfront and when it's needed it can be enabled. That's for sure more deterministic than a software controlled GPIO toggling.
Hi Luc
I am afraid there is no way to produce fast toggling
in linux, as operating system introduces unpredictable delays.
May be useful to check
https://community.nxp.com/message/920894?commentID=920894#comment-920894
Best regards
igor
-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------