BlinkenBone - "/dev/blinkenbus": accessing low level I/O data
The Linux file interface for BlinkenBoards: /dev/blinkenbus
BlinkenBus address space.
Over BlinkenBus up to 32 BlinkenBoards can be connected, each occupying an address space of 16 bytes. In the board address space, 11 output registers and 5 input registers are located on overlapping addresses, byte address 15 gives access to the read/write board control register (see "BlinkenBus specification" attachement on the hardware page). The absolute 9-bit address of a certain register chip is given by the formula:
<absolute addr> := 16 * <board_address> + <register_address_on_board>
Over these registers you can read and write raw digital I/O data from the connected blinkenlight panel.
Accessing the BlinkenBus address space as a file
User applications can access I/O registers on the BlinkenBus very easy over a file interface.
The 512 register addresses are arranged as a file of 512 bytes. Reading certain positions in the file accesses the input registers, writing sets the output registers. This file is a simple character device file node, named "/dev/blinkenbus".
The translation between BlinkenBus signal patterns and the device file is done by a Linux kernel mode driver. On access to "bytes in the file" he creates the BlinkenBus signals patterns by toggeling with GPIO (general purpose IO) pins of the ARM cpu.
To get the signals on input register 4 on a board with address switched to hex 0xb (decimal 11), global address 11*16+4 =>180 must be read. This is done by opening "/dev/blinkenbus", moving the read pointer to byte 180, and reading it.
In a C program you would use these Linux system calls:
fbb = open("/dev/blinkenbus", O_RDWR | O_SYNC) ;
lseek(fbb, 180, SEEK_SET) ;
read(fbb, &your_byte_buffer, 1) ;
You can even do this from the shell prompt with the user command "dd", which is used for block read/write on files:
$ dd if=/dev/blinkenbus bs=1 seek=180 count=1 of=your_file
The byte value of register 4 on board 11 is written to the file named "your_file". Don't let the output go to terminal screen ... or prepare for some strange characters appearing.
A Linux system calls like "read()" is much slower than an access cycle to the BlinkenBus in the driver.
Tests indicate that BeagleBone can perform about 10.000 system calls per second. Even unoptimized driver code generate 250.000 BlinkenBus access cycles per second. So it's good practice to bundle read or write accesses to BlinkenBus registers into few block read/write transactions whenever possible.
Protection against hardware changes
There is one big benefits to have "/dev/blinkenbus" as interface: The underlying hardware (BlinkenBus, BlinkenBoards) can be modified or replaced by something totally different without impact to the overall system.
For example, there may be needs to change BlinkenBus from parallel to SPI or I2C or CAN bus. Or the BeagleBone itself is replaced by some other embedded Linux hardware with an incompatible GPIO system.
In any case, as long as the driver for the new hardware implements "/dev/blinkenbus" as defined, no application needs to be changed.
Who else needs this?
Well, accessing device data over pseudo files is the BIG standard under Linux: "Everything is a file"
You can open the file and access BlinkenBoard I/O from any programming environment, including
- the hot web app builder Cloud9 for node.js and bonescript
- script languages like Python
- classic C/C++
And you are not limited to Blinkenlight applications.
If you need massive digital I/O for the BeagleBone, think about using BlinkenBoards!