Exercise Bike Upgrades: User Display

Continuing from the Exercise Bike Upgrades and Exercise Bike Upgrades: Getting The Data Into A Computer, we finally need to get a way to display the data to a user.

There’s a number of decisions that can be made at this point. The Beaglebone has HDMI out. There are all sorts of LCD screens made for the device – some of which have touchscreen… There’s also a webserver option, but I feel that’d force me down the road of web design or iOS app development.

I did want to stay in the Embedded Linux world though. Just plugging in an HDMI as the output seems like a waste. I also wanted to at least start with stuff I had lying around. Fortunately, I had one of these displays lying around.

A display similar to what I had lying around

Perfect. Hardware found. If I want to display RPMs, or power, or cycle through a few different things, this 32-character display should suffice. I wonder if there’s a Linux driver for a 16×2 LCD driver out there…

Well that first result looks nice. I should be able to start with this. I wonder what it was created for…

Readme snippet of the driver

Oh wow. I’m running the 3.8.13 kernel on the Beaglebone Black. So it seems like this driver is pretty much the perfect fit!

The same steps from the GPIO were necessary.

  1. Setting up a device tree overlay to configure the pin mux.
  2. Configuring any parts of the driver that might be necessary.
  3. Compiling and inserting the kernel module.
  4. Testing and implementation

Setting up the Device Tree Overlay

The driver has hard-coded pins compiled in.

There’s no feedback from the LCD, so it is probably safe to assume they’re all outputs from the processor. Plus if you look at the fact that each pin calls gpio_direction_output, you can put two and two together. Each of these six pins then need to be configured as outputs. But there’s a bit of info needed for the device tree to be configured.

This site contains the GPIO mapping to their relative offsets in the CPU’s GPIO module. Yes, the processor’s TRM also has this information, but I read those enough in non-leisure environments. I don’t care to poke around those in my free time if I can avoid it.

The first pin – the reset pin – is GPIO 67, and is labeled as P8_8. Basically this is pin 8 on the connector 8. I can verify with that table here that Pin 8 (the last row photographed here:

Pin 8, showing offset 0x894

… does in fact match GPIO 67

Pin 8 showing GPIO 67

So I decided to trust this info, and what I was specifically looking for is the offset of 0x894 I mentioned in the caption above.

Comparing that table against the device tree overlay from Derek Molloy’s blog, I figured that I needed to subtract 0x800 from that offset. I can’t say I’m positive as to why that’s the case, but it is. With all that in mind, and going over all the pins, I came to this overlay:

Alright. Hopefully this worked…

Configuring the driver

Well this is a short section. It turns out I didn’t really have to change anything. None of the pins used for this driver overlapped with ones I’m using. I could change the pin numbers to module parameters. Meh… no need at this point. On to the next part.

Compiling the driver

On to the next step. For GPIO tachometer driver I’d started with the source and a makefile that built on the target itself. So I’ll just copy this over to the target, run make, and end up with a .ko file.

KLCD module makefile

So another decision to make. Copy the file to the target and write a new makefile to build locally, or step up to the plate and finally set up a cross-compile environment. I decided to do the latter.

I had to figure out what version of the kernel I was running. A simple “uname -r” told me I was running 3.8.3-bone86. I need to pull down these sources locally, and they can be found at https://github.com/beagleboard/linux. A simple checkout of the 3.8.13-bone86 tag should be what I need.

Next, I need to configure the kernel in the same way that the Beagleboard was configured. This can be pulled from the Beaglebone itself, and can be found at /boot/config-3.8.13-bone86. I can copy this file to the .config and boom, the kernel is configured to compile for this device.

Throughout all this, I’d had to install cross compile tools. Specifically the arm-linux-gnueabi- toolchain. Then the single command “make -j6 ARCH=arm CROSS_COMPILE=arm-linux-gnueabi-” will spin up a number of threads to compile the whole kernel.

Lastly, to compile the kernel module I found myself needing to modify the path in the Makefile:

Modified makefile

With that Makefile, and the kernel checked out and compiled in the KDIR directory:

KDIR contents

I was able to simply run make in the kernel module directory to build the .ko from my main machine:

The klcd.ko build

Copy the .ko file over, and run an insmod and the KLCD device shows up in /dev.

Better yet, running echo “Colin Here” into the device actually works!

The command to write to the display / device driver
The device showing the message!

Tying it all together

So now there’s a driver for measuring the GPIO, and a driver to display the events. At this point the program is incredibly straightforward. Read the speed and display it.

Sticking with the cross-compile technique, I made a simple Makefile:

Simple cross-compile makefile

There’s a little bit to the program insofar as signal trapping, but the main loop only consists of the read and write:

The main loop

Ok, so it isn’t a “write” per se, rather an ioctl. I think a write / seek would work, but the reference app that came with the driver uses ioctl all over the place. I followed suit here.

The last steps can be found all over the internet, so I’m not going to detail them. Adding the overlays to be persistent, inserting the kernel modules at boot, and starting the monitoring app. That’s all that is needed for phase 1!