how to detect a pin change of a GPIO on Linux board - c

I am using 3.12 kernel on an ARM based linux board (imx233 CPU). My purpose is to detect pin change of a GPIO (1 to 0).
I can read the pin value constantly calling the below function (in a while(1) loop)
int GPIO_read_value(int pin){
int gpio_value = 0;
char path[35] = {'\0'};
FILE *fp;
sprintf(path, "/sys/class/gpio/gpio%d/value", pin);
if ((fp = fopen(path,"rb+")) == NULL){ //echo in > direction
//error
}
fscanf(fp, "%d", &gpio_value);
fclose(fp);
return gpio_value;
}
But it causes too much load to the CPU. I don't use usleep or nanosleep, because the pin change happens for a very short of a time that would cause me to miss the event.
As far as I find out, it is not possible to use poll(). Is there any poll() like function that I can use to detect a pin change of a GPIO?
EDIT: Just in case, if I am doing something wrong, here is my poll() usage that does not detect the pin change
struct pollfd pollfds;
int fd;
int nread, result;
pollfds.fd = open("/sys/class/gpio/gpio51/value", O_RDWR);
int timeout = 20000; /* Timeout in msec. */
char buffer[128];
if( pollfds.fd < 0 ){
printf(" failed to open gpio \n");
exit (1);
}
pollfds.events = POLLIN;
printf("fd opens..\n");
while (1)
{
result = poll (&pollfds, 0, timeout);
switch (result)
{
case 0:
printf ("timeout\n");
break;
case -1:
printf ("poll error \n");
exit (1);
default:
printf("something is happening..\n");
if (pollfds.revents & POLLIN)
{
nread = read (pollfds.fd, buffer, 8);
if (nread == 0) {
printf ("result:%d\n", nread);
exit (0);
} else {
buffer[nread] = 0;
printf ("read %d from gpio: %s", nread, buffer);
}
}
}
}
close(fd);
EDIT2: the code on https://developer.ridgerun.com/wiki/index.php/Gpio-int-test.c works fine with poll() I needed to define the rising/falling edge for the interrupt and a little bit fix on the definition. It solves my problem, however, it might be good for me and some other people to hear/know the alternative methods.

I have never seen this board before, however I guess PIC is fully implemented for this board (usually is like that) but you have to configure interrupt additionally in GPIO controller (usually is like that).
Some part should be done as a kernel module, then you have to pass information about interrupt to you application.
Example way to do this is to implement following thing as a kernel module:
setup GPIO controller to enable interrupt on particular port and level
(how to do this you can find here: http://cache.freescale.com/files/dsp/doc/ref_manual/IMX23RM.pdf 37.2.3.3 Input Interrupt Operation)
enable GPIO interrupt in PIC (how to do this: http://lwn.net/images/pdf/LDD3/ch10.pdf Chapter10)
implement interrupt handling routine (I will describe a little bit below)
implement ioctl interfaces for your module.
and a rest in your application:
a function that can coosomeoneperate with interrupt.
Simplest way of passing information about interrupt from kernel to app is by semaphore on kernel side.
in module you can implement an ioctl that will sleep until interrupt happen.
So application will call this ioctl and its thread will be blocked until interrupt happen.
Inside module, interrupt routine should check if application thread is now blocked, and if so up() semaphore.
EDIT*****
This CPU has SSP that has working mode for SPI. Why dont use it ??

Related

STM32 HAL: How to read I2C memory in truly non-blocking mode?

Here's what I have: PCA9555 chip that has inputs, if a signal state on the input changes, the interrupt signal is sent. Then I can read the chip via I2C to check the inputs.
What I need - when a pin changes state, I need to read the chip, check which pin changed state and notify my app about it.
So I have an interrupt and the interrupt handler MUST NOT block the MCU.
My obvious choice is using HAL_I2C_Mem_Read_IT(), right?
I made the whole code, tested it. It seemed like it worked... For a while.
Until I added reading the chip like every 100ms.
The code still works, but I see the blinking things stutter, stop blinking for more than a second or even 2. So - it became obvious that HAL_I2C_Mem_Read_IT() BLOCKS my interrupt that causes the MCU to freeze.
I checked the HAL sources and found this:
static HAL_StatusTypeDef I2C_RequestMemoryRead(I2C_HandleTypeDef *hi2c, uint16_t DevAddress,
uint16_t MemAddress, uint16_t MemAddSize, uint32_t Timeout,
uint32_t Tickstart)
{
I2C_TransferConfig(hi2c, DevAddress, (uint8_t)MemAddSize, I2C_SOFTEND_MODE, I2C_GENERATE_START_WRITE);
/* Wait until TXIS flag is set */
if (I2C_WaitOnTXISFlagUntilTimeout(hi2c, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* If Memory address size is 8Bit */
if (MemAddSize == I2C_MEMADD_SIZE_8BIT)
{
/* Send Memory Address */
hi2c->Instance->TXDR = I2C_MEM_ADD_LSB(MemAddress);
}
/* If Memory address size is 16Bit */
else
{
/* Send MSB of Memory Address */
hi2c->Instance->TXDR = I2C_MEM_ADD_MSB(MemAddress);
/* Wait until TXIS flag is set */
if (I2C_WaitOnTXISFlagUntilTimeout(hi2c, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
/* Send LSB of Memory Address */
hi2c->Instance->TXDR = I2C_MEM_ADD_LSB(MemAddress);
}
/* Wait until TC flag is set */
if (I2C_WaitOnFlagUntilTimeout(hi2c, I2C_FLAG_TC, RESET, Timeout, Tickstart) != HAL_OK)
{
return HAL_ERROR;
}
return HAL_OK;
}
As the name I2C_WaitOnTXISFlagUntilTimeout() suggests - it WAITS. Yes, it's a while loop that blocks the executing thread until a flag is set:
static HAL_StatusTypeDef I2C_WaitOnFlagUntilTimeout(I2C_HandleTypeDef *hi2c, uint32_t Flag, FlagStatus Status,
uint32_t Timeout, uint32_t Tickstart)
{
while (__HAL_I2C_GET_FLAG(hi2c, Flag) == Status)
{
/* Check for the Timeout */
if (Timeout != HAL_MAX_DELAY)
{
if (((HAL_GetTick() - Tickstart) > Timeout) || (Timeout == 0U))
{
hi2c->ErrorCode |= HAL_I2C_ERROR_TIMEOUT;
hi2c->State = HAL_I2C_STATE_READY;
hi2c->Mode = HAL_I2C_MODE_NONE;
/* Process Unlocked */
__HAL_UNLOCK(hi2c);
return HAL_ERROR;
}
}
}
return HAL_OK;
}
There are 3 of those lagging functions.
For my application this is a show stopper. It just doesn't work, since it depends on handling the events in real time. Also it has a GUI that freezes when the interrupt handler blocks.
Is there a quick workaround for this? Is it a bug in HAL driver?
Do I have to implement my own non-blocking function? It seems like many, many hours of coding, since the function is non trivial and tightly coupled with the rest of the module.
My idea is to rewrite it and replace while loops with my non-blocking delay function that uses a timer interrupt to continue work after some time passes. To make it more non-trivial, each callback would have to receive the necessary state data to continue. Then the state machine to figure out where we are with my I2C_RequestMemoryRead_ process. At the end I just call the registered callback and done. It should work truly non-blocking...
But I have deadlines. Can it be done faster? How is it even possible the HAL "_IT" function BLOCKS the thread with some while loops? It's just wrong! It defeats the entire purpose of an "interrupt mode function". If it blocks, there already IS a blocking version that is simpler.
I solved the problem with hacking the original HAL driver.
https://gist.github.com/HTD/e36fb68488742f27a737a5d096170623
After adding the files to the project the original HAL driver needs to be modified as described in the stm32h7xx_hal_i2c_nb.h comment.
For use with STM32CubeIDE it's best to move the modified driver file to a different location to prevent the IDE from overwriting it.
I left other *IT functions unchanged, but it's very easy to add similar modifications to all remaining functions.
The hacked driver was tested in real world application on my STM32H747I-DISCO board with PCA9555 16-bit I/O expander.
For the test I spammed the input with random noise signals that just crashed the original driver. Here it works, doesn't block other threads, the GUI works with full speed.

How to change the baudrate of a serial port at runtime using C?

I have written some C code for aarch64-based SoC (Rockchip RK3399) with Debian 9 LXDE, to receive data from a GPS module. The GPS module is connected to "ttyS4" port in my SoC. I have created a pthread to receive data from the GPS module. I'm using the termios library. So my flow goes as follows:
Initialize the UART port (baud rate, parity, stop bits etc.).
Create a thread to receive the data from the module.
Now I need to change the baud rate of the UART upon receiving the baud rate from a external source. I am able to receive the new baud rate which I need to set it to the port.
How do I set the new baud rate to the port? Should I pthread_exit() the receiving thread, initialize the UART port and then start the thread again?
Or should I just close the fd and initialize the UART port with the new baud rate without exiting from the thread?
Or is there any other simple way or function to set the UART to the port ?
My Initialization code:
int Gpsfd;
struct termios Gps_termios, Gps_old;
void GpsPortInit(void)
{
char path[12] = "/dev/ttyS4";
//open GSM_termios for tx/rx
Gpsfd = open(path, O_RDWR | O_NOCTTY);
if (Gpsfd < 0)
printf("port failed to open\n");
//save current attributes
tcgetattr(Gpsfd, &Gps_old);
bzero(&Gps_termios, sizeof(Gps_termios));
Gps_termios.c_cflag = CLOCAL | CREAD | CS8;
if (!strcmp(g_sParameters.RS232BaudRate, "9600"))
{
Gps_termios.c_cflag |= B9600;
}
else if (!strcmp(g_sParameters.RS232BaudRate, "19200"))
{
Gps_termios.c_cflag |= B19200;
}
else if (!strcmp(g_sParameters.RS232BaudRate, "57600"))
{
Gps_termios.c_cflag |= B57600;
}
else if (!strcmp(g_sParameters.RS232BaudRate, "115200"))
{
Gps_termios.c_cflag |= B115200;
}
Gps_termios.c_iflag = IGNPAR;
Gps_termios.c_oflag = 0;
Gps_termios.c_lflag = 0;
Gps_termios.c_cc[VTIME] = 0;
Gps_termios.c_cc[VMIN] = 1;
//clean the line and set the attributes
tcflush(Gpsfd, TCIFLUSH);
tcsetattr(Gpsfd, TCSANOW, &Gps_termios);
}
This is the baudrate change function suggested below:
int set_baudrate(speed_t speed)
{
//struct termios tty;
if (tcgetattr(Gpsfd, &Gps_termios) < 0) {
printf("Error from tcgetattr1: %s\n", strerror(errno));
return -1;
}
cfsetospeed(&Gps_termios, speed);
cfsetispeed(&Gps_termios, speed);
if (tcsetattr(Gpsfd, TCSANOW, &Gps_termios) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
tcflush(Gpsfd, TCIOFLUSH); /* discard buffers */
return 0;
}
I am Initializing the UART once with GpsPortInit and after that if I get any request to change the baudrate I change it with set_baudrate.
How do I set the new baud rate to the port?
In Linux, userspace does not have direct access to hardware such as a UART, so your program is constrained to use the serial terminal and the termios API.
That is confirmed by your use of /dev/ttyS4 (rather than a device node named /dev/uart4).
Should I pthread_exit() the receiving thread, initialize the UART port and then start the thread again?
That should not matter.
The pthread is merely "reading" from the termios buffer, rather than directly accessing any hardware.
However your program needs to be robust and able to cope with possible mangled messages.
Or should I just close the fd and initialize the UART port with the new baud rate without exiting from the thread?
Closing the file descriptor would deny your program further access to the serial terminal, so that does not make sense.
Userspace does not have direct access to hardware such as a UART, so your program is constrained to use the termios API.
Or is there any other simple way or function to set the UART to the port ?
Use the termios API to change the baudrate of the serial terminal, e.g.:
int set_baudrate(int fd, speed_t speed)
{
struct termios tty;
int rc1, rc2;
if (tcgetattr(fd, &tty) < 0) {
printf("Error from tcgetattr: %s\n", strerror(errno));
return -1;
}
rc1 = cfsetospeed(&tty, speed);
rc2 = cfsetispeed(&tty, speed);
if ((rc1 | rc2) != 0 ) {
printf("Error from cfsetxspeed: %s\n", strerror(errno));
return -1;
}
if (tcsetattr(fd, TCSANOW, &tty) != 0) {
printf("Error from tcsetattr: %s\n", strerror(errno));
return -1;
}
tcflush(fd, TCIOFLUSH); /* discard buffers */
return 0;
}
The above should be similar to the initialization that you used to "initialize the UART port (baud rate, parity, stop bits etc.) ... using the termios library".
That is, the code above adheres to Setting Terminal Modes Properly
.
Note that the tcsetattr() call could use the argument TCSAFLUSH instead of TCSANOW. That would "change attributes when output has drained; also flush pending input" according to the termios.h man page.
However waiting for the output to drain seems unnecessary, as changing the baudrate implies the current baudrate is incompatible with the other end of the serial link. IOW garbage is presumably being transmitted and garbage is presumably being received.
So immediately change the baudrate (i.e. use TCSANOW), and then discard the contents of the receive buffer.
Addendum: review of your initialization code
Your initialization code is low quality, is not portable, and may not reliably setup the serial terminal.
Starting with a zeroed-out termios structure (instead of the existing values) is contrary to recommended practice as described in Setting Terminal Modes Properly
and Serial Programming Guide for POSIX Operating Systems.
See this answer for concise code for proper serial terminal initialization to blocking non-canonical mode.
This is the baudrate change function suggested below:
Note that the routine has been updated in my answer to enhance error reporting.
You neglect to show exactly how you use/call this routine.
I am Initializing the UART once with GpsPortInit and after that if I get any request to change the baudrate I change it with set_baudrate.
How do you coordinate any baudrate changes with the other end of the serial link?
You also neglect to describe your test procedures, what baudrates you are using, how the other end is setup.
Any of these omissions could reveal the reason(s) why this set_baudrate() routine "is not working" for you.

Waiting for serial transmission to complete in Win32

I seem to be having a bit of trouble in waiting for the completion of serial data transmissions.
My interpretation of the relevant MSDN article is the EV_TXEMPTY event is the correct signal and which indicates that:
EV_TXEMPTY - The last character in the output buffer was sent.
However in my tests the event always fires immediately as soon as the data has been submitted to the buffer and long before the final has actually reached the wire. See the repro code below where the period is always zero.
Have I made an error in the implementation, am I misunderstanding the purpose of the flag, or is this feature simply not supported by modern drivers? In the latter case is there a viable workaround, say some form of synchronous line state request?
For the record the tests were conducted with FTDI USB-RS485 and TTL-232R devices in a Windows 10 system, a USB-SERIAL CH340 interface on a Windows 7 system, as well as the on-board serial interface of a 2005-vintage Windows XP machine. In the FTDI case sniffing the USB bus reveals only bulk out transactions and no obvious interrupt notification of the completion.
#include <stdio.h>
#include <windows.h>
static int fatal(void) {
fprintf(stderr, "Error: I/O error\n");
return 1;
}
int main(int argc, const char *argv[]) {
static const char payload[] = "Hello, World!";
// Use a suitably low bitrate to maximize the delay
enum { BAUDRATE = 300 };
// Ask for the port name on the command line
if(argc != 2) {
fprintf(stderr, "Syntax: %s {COMx}\n", argv[0]);
return 1;
}
char path[MAX_PATH];
snprintf(path, sizeof path, "\\\\.\\%s", argv[1]);
// Open and configure the serial device
HANDLE handle = CreateFileA(path, GENERIC_WRITE, 0, NULL,
OPEN_EXISTING, 0, NULL);
if(handle == INVALID_HANDLE_VALUE)
return fatal();
DCB dcb = {
.DCBlength = sizeof dcb,
.BaudRate = BAUDRATE,
.fBinary = TRUE,
.ByteSize = DATABITS_8,
.Parity = NOPARITY,
.StopBits = ONESTOPBIT
};
if(!SetCommState(handle, &dcb))
return fatal();
if(!SetCommMask(handle, EV_TXEMPTY))
return fatal();
// Fire off a write request
DWORD written;
unsigned int elapsed = GetTickCount();
if(!WriteFile(handle, payload, sizeof payload, &written, NULL) ||
written != sizeof payload)
return fatal();
// Wait for transmit completion and measure time elapsed
DWORD event;
if(!WaitCommEvent(handle, &event, NULL))
return fatal();
if(!(event & EV_TXEMPTY))
return fatal();
elapsed = GetTickCount() - elapsed;
// Display the final result
const unsigned int expected_time =
(sizeof payload * 1000 /* ms */ * 10 /* bits/char */) / BAUDRATE;
printf("Completed in %ums, expected %ums\n", elapsed, expected_time);
return 0;
}
The background is that this is part of a Modbus RTU protocol test suite where I am attempting to inject >3.5 character idle delays between characters on the wire to validate device response.
Admittedly, an embedded realtime system would have been more far suitable for the task but for various reasons I would prefer to stick to a Windows environment while controlling the timing as best as possible.
According to the comments by #Hans Passant and #RbMm the output buffer being referred in the EV_TXEMPTY documentation is an intermediate buffer and the event indicates that data has been forwarded to the driver. No equivalent notification event is defined which encompasses the full chain down to the final device buffers.
No general workaround is presently clear to me short of a manual delay based upon the bitrate and adding a significant worst-case margin for any remaining buffer layers to be traversed, inter-character gaps, clock skew, etc.
I would therefore very much appreciate answers with better alternate solutions.
Nevertheless, for my specific application I have implemented a viable workaround.
The target hardware is a half-duplex bus with a FTDI RS485 interface. This particular device offers an optional local-echo mode in which data actively transmitted onto the bus is not actively filtered from the reception.
After each transmission I am therefore able to wait for the expected echo to appear as a round-trip confirmation. In addition, this serves to detect certain faults such as a short-circuited bus.

poll() returns both, POLLPRI & POLLERR

I started to get in C programming with Linux and embedded systems (router hardware and openwrt). I have got interupts on GPIOs enabled, using poll works ... nearly.
I can use poll() and if i press the button to trigger the interrupt, poll() returns with a value > 0. So far so good. Now i try to use poll() on several GPIOs simultaniosly and therefor want to analyze the revents of every potential interrupt source. Allthough the interrupt seems to work, i get POLLPRI & POLLERR back and i do not understand why. Reducing the pollfd structure to 1 entry does not change anything.
char value;
int fd_btn1 = open("/sys/class/gpio/gpio14/value", O_RDONLY);
int fd_left = open("/sys/class/gpio/gpio12/value", O_RDONLY);
int fd_right = open("/sys/class/gpio/gpio13/value", O_RDONLY);
struct pollfd fds[3];
fds[0].fd = fd_btn1;
fds[1].fd = fd_left;
fds[2].fd = fd_right;
fds[0].events = POLLPRI;
fds[1].events = POLLPRI;
fds[2].events = POLLPRI;
read(fd_btn1, &value, 1);
read(fd_left, &value, 1);
read(fd_right, &value, 1);
ret = poll(fds, 1, 10000);
//debugging purpose
printf("ret: %i - revents[0]: %i", ret, fds[0].revents);
In case button was pressed (interrupt triggered):
ret=1d, revents=10d
In case nothing was pressed, both is 0d
I've found some answer alluding to your question at: http://e2e.ti.com/support/dsp/davinci_digital_media_processors/f/716/t/182883
I just ran into the POLLERR thing you're seeing too. Turns out this is
how all sysfs files work, and you're using gpio through the sysfs
interface.
From the sysfs GPIO kernel document: If the pin can be configured as
interrupt-generating interrupt and if it has been configured to
generate interrupts (see the description of "edge"), you can poll(2)
on that file and poll(2) will return whenever the interrupt was
triggered. If you use poll(2), set the events POLLPRI and POLLERR.
Also, if you look take a look at the kernel code in fs/sysfs/file.c,
you'll see that sysfs_poll returns DEFAULT_POLLMASK | POLLERR |
POLLPRI.
All that said, it does seem strange that sysfs files return
POLLERR, I'm not sure why they chose to do that.

Cannot Wake up Atmel ATSHA204 Using I2C

I've recently been given the task of writing drivers for some of the I2C devices in our product. I was a complete beginner at this, but I've managed to use a mixture of i2cset and i2cget along with smbus to control some LEDs.
My latest task is to read a 7 byte serial number from the EEPROM of an Atmel ATSHA204 chip. My first job is to wake up the chip. The data sheet says this is done as follows
The Wake condition requires either that the system processor manually drive
the SDA pin low for tWLO, or that a data byte of 0x00 is transmitted at a
clock rate sufficiently slow that SDA is low for a minimum period of tWLO.
When the device is awake, the normal processor I2C hardware and/or software
can be used for device communications up to and including the I/O sequence
required to put the device back into low-power (sleep) mode.
So it seems I have to manually set one of the I2C pins low for a time of tWLO which is apparently at least 60 microseconds before I can use conventional I2C calls. I'm really not sure how this is done, Ideally I'd do this in C, so would some variation of the following work?
int file;
file = open("/dev/i2c-0", O_RDWR);
if (file < 0)
{
exit(1);
}
int addr = 0x64; // Address of LED driver
if (ioctl(file, I2C_SLAVE, addr) < 0)
{
return 1;
}
write(file, 0x00, 1); // write a 0x00 to the chip to wake it up
I guess I'm not sure about the last bit, how do I keep writing until the device has woken up? I'd appreciate any help with this, as low-level programming such as this is new to me.
You don't want to pass 0x00 to the write function. That param isn't a value, it's a pointer to a buffer containing the data. You need to know what clock speed your I2C is running. That will determine how many bytes of 0x00 need to be written satisfy the required duration for wakeup.
Below could help you to awake the chip
bool wakeup (int fd)
{
unsigned char buf[4] = {0};
bool awake = false;
if (fcntl(fd, F_GETFD) < 0)
perror ("Invalid FD.\n");
while (!awake){
if (write(fd, buf, sizeof(buf)) > 1){
printf ("Device is awake!\n");
}
if (read (fd, buf, sizeof(buf)) == 4){
printf ("Awake Done!\n");
awake = true;
}
else {
printf ("Failed to awake the device!");
break;
}
}
return awake;
}

Resources