I have found this online about the beaglebone (not black). Any GPIO can be used as an interrupt and is limited to two interrupts per GPIO Bank for a maximum. A maximum of 66 GPIO pins are accessible from the expansion header. All of these pins are 3.3V and can be configured as inputs or outputs.Any GPIO can be used as an interrupt and is limited to two interrupts per GPIO Bank for a maximum of eight pins as interrupts.
30 Apr 2016GPL3
This will be a simple article about Linux Device Drivers, in fact about a char driver that controls the GPIO ( General Purpose Input-Output) port of the Beaglebone Black.
Introduction
This will be a simple article about Linux Device Drivers, in fact about a char driver that controls the GPIO ( General Purpose Input-Output) port of the Beaglebone Black.
Background
Some time ago I worked on a project on Beaglebone Black that had to control some IOs, UART ports, also had to take some decisions and communicate with a PC via sockets. The interesting part at that project was the time constraint, the software had to take a decision or wait for an input at exactly the specified time. For example, it had to change the state of an output port from 0 to 1 with a frequency of 1 KHz, it had to change the value each millisecond. I managed to fulfill the time constraint by using code optimizations techniques and by writing some device drivers.
Using the code
The driver use IOCTLs for performing basic settings of the port, such as exporting a pin, set the direction input or output, use capture events such rising edge, falling edge etc. In the next paragraphs I will present the source code of some functions and also an example of how to use this device driver.
Data Types and IOCTL Options
For transferring data between user space and kernel space and also for IOCTL I used a structure that contains two buffers for read and write operations, the number of the pin that is controlled and the interrupt number, if interrupts are used for reading data.
The settings that should be made before reading and writing data, driver's work mode etc. are implemented using IOCTLs. All the requests that are defined for this device driver are:
Writing Data
The easiest part to implement at this device driver was the function that set the output state of the port, the function should take a value from user space and write it in the port's register.
This function will try to lock a mutex, to prevent concurrent access to shared data and if it will fail, will return EBUSY. If the value returned by mutex_trylock was 0, SUCCESS, buffer's content will be copied from userspace and if the operation succeeds, the output value will be updated with the value from write_buffer.
Reading Data
A little more challenging part was to implement the read functionality because the device driver had to work in busy-wait mode or using interrupts.
Busy-wait Mode
In busy-wait, the driver will read the value of the port and it will send it to user space. This mode was designed to be used when data should be captured continuously, without using any triggers.
The read function is somehow similar with the write. Firstly, will try to lock a mutex, and if the operation succeeds, it will copy from userspace the buffer's value. This operation is needed because I wanted to know what pin I will read. Next, it will read the pin's value and after that will update the buffer and copy it back to userspace.
Interrupt Mode
Interrupt mode was designed because I wanted the driver to react on events and start to capture data only when a transition from HIGH to LOW or rising/falling edge is detected. Data is captured in the ISR ( interrupt service routine), is saved in a ring buffer and only when ioctl with IOCBBBGPIORD request is called, it will be transferred to userspace.
When the interrupt service routine is called, the mutex will be locked, data will be read from the pin and saved in the ring buffer bbb_data_buffer.
IOCBBBGPIORD ioctl request is handled in the function static long bbbgpio_ioctl(struct file *file, unsigned int ioctl_num ,unsigned long ioctl_param) where is checked if the ring buffer is not empty and, if is not, then data will be take from it and copied to user space.
Example Application
An example of how to use this device driver can be found HERE.
In the Makefile of the project I also added some steps for installing & uninstalling the driver.
![Interrupt Interrupt](http://derekmolloy.ie/wp-content/uploads/2013/05/BeagleboneBlackP8HeaderTable_mini.png)
Points of Interest
This project has bought me a lot of fun, one or two sleepless nights and a little bit of frustration because I didn't want to use linux/gpio.h but to write values directly to registers. I implemented the read & write by directly accessing registers but I was stucked at detecting the interrupt number, I couldn't probe and register the interrupt for edge detecting.
Full source code can be found HERE.
History
- Initial commit
- Working version
30 Apr 2016GPL3
This will be a simple article about Linux Device Drivers, in fact about a char driver that controls the GPIO ( General Purpose Input-Output) port of the Beaglebone Black.
Introduction
This will be a simple article about Linux Device Drivers, in fact about a char driver that controls the GPIO ( General Purpose Input-Output) port of the Beaglebone Black.
Background
Some time ago I worked on a project on Beaglebone Black that had to control some IOs, UART ports, also had to take some decisions and communicate with a PC via sockets. The interesting part at that project was the time constraint, the software had to take a decision or wait for an input at exactly the specified time. For example, it had to change the state of an output port from 0 to 1 with a frequency of 1 KHz, it had to change the value each millisecond. I managed to fulfill the time constraint by using code optimizations techniques and by writing some device drivers.
Using the code
The driver use IOCTLs for performing basic settings of the port, such as exporting a pin, set the direction input or output, use capture events such rising edge, falling edge etc. In the next paragraphs I will present the source code of some functions and also an example of how to use this device driver.
Data Types and IOCTL Options
For transferring data between user space and kernel space and also for IOCTL I used a structure that contains two buffers for read and write operations, the number of the pin that is controlled and the interrupt number, if interrupts are used for reading data.
The settings that should be made before reading and writing data, driver's work mode etc. are implemented using IOCTLs. All the requests that are defined for this device driver are:
Writing Data
The easiest part to implement at this device driver was the function that set the output state of the port, the function should take a value from user space and write it in the port's register.
This function will try to lock a mutex, to prevent concurrent access to shared data and if it will fail, will return EBUSY. If the value returned by mutex_trylock was 0, SUCCESS, buffer's content will be copied from userspace and if the operation succeeds, the output value will be updated with the value from write_buffer.
Reading Data
A little more challenging part was to implement the read functionality because the device driver had to work in busy-wait mode or using interrupts.
Busy-wait Mode
In busy-wait, the driver will read the value of the port and it will send it to user space. This mode was designed to be used when data should be captured continuously, without using any triggers.
The read function is somehow similar with the write. Firstly, will try to lock a mutex, and if the operation succeeds, it will copy from userspace the buffer's value. This operation is needed because I wanted to know what pin I will read. Next, it will read the pin's value and after that will update the buffer and copy it back to userspace.
![Beaglebone black io pins Beaglebone black io pins](http://derekmolloy.ie/wp-content/uploads/2015/04/Button-and-LED-large.png)
Interrupt Mode
Interrupt mode was designed because I wanted the driver to react on events and start to capture data only when a transition from HIGH to LOW or rising/falling edge is detected. Data is captured in the ISR ( interrupt service routine), is saved in a ring buffer and only when ioctl with IOCBBBGPIORD request is called, it will be transferred to userspace.
When the interrupt service routine is called, the mutex will be locked, data will be read from the pin and saved in the ring buffer bbb_data_buffer.
IOCBBBGPIORD ioctl request is handled in the function static long bbbgpio_ioctl(struct file *file, unsigned int ioctl_num ,unsigned long ioctl_param) where is checked if the ring buffer is not empty and, if is not, then data will be take from it and copied to user space.
Example Application
An example of how to use this device driver can be found HERE.
In the Makefile of the project I also added some steps for installing & uninstalling the driver.
Points of Interest
This project has bought me a lot of fun, one or two sleepless nights and a little bit of frustration because I didn't want to use linux/gpio.h but to write values directly to registers. I implemented the read & write by directly accessing registers but I was stucked at detecting the interrupt number, I couldn't probe and register the interrupt for edge detecting.
Full source code can be found HERE.
History
- Initial commit
- Working version