Adding Human Input Devices
From Ubicom Developer
Contents |
Introduction
The purpose of this page is to describe the process of adding a Human Input Device (HID) to the Ubicom platform. This application note will walk through the process of adding a rotary shaft grey encoder (aka jog dial) to the Ubicom Internet Radio Reference design.
Board Requirements
The two primary input mechanisms for the jog dial into the Ubicom processor is through an interrupt pin and/or GPIO. The benefit of using an interrupt pin is that the processor does not have to spend time checking the jog dial when there is no activity from the device. In the case of GPIO, the processor is required to poll for events from the device or risk missing user input.
The Ubicom IP7K has three interrupt pins (PA4,PA5,PA6) whereas most of the pins on the IP7K can be repurposed for GPIO so the designer must weigh the benefit of using an interrupt pin versus polling overhead.
Linux Requirements
Once the jog dial is available to the Ubicom IP7K, then the appropriate Linux level must be made in order to pass the input events up to user space.
Enable Driver
The first step is adding the jog dial kernel driver as part of the kernel build. This is with the assumption that your distribution has already been configured. If this is not the case, please read this first.
/scratch2/jbass/ubicom-distro> make linux_menuconfig Device Drivers ---> Input device support ---> [*] Miscellaneous devices ---> <*> Ubicom32 Jog Dial
Initialize Driver
Edit the Linux board file to include the jog dial driver in the platform device list and configure it for the board characteristics. In this case, PA5 is being used as an interrupt line (A xor B).
/scratch2/jbass/ubicom-distro/linux-2.6.x/arch/ubicom32/mach-ip7k> emacs board-ip7500iap.c
/*
* arch/ubicom32/mach-ip7k/board-ip7500iap.c
* Support for IP7500 Internet Audio Player
...
#include <asm/ubicom32jog.h>
static struct ubicom32jog_platform_data ip7500iap_jog_platform_data = {
.button_active_low = 0,
.has_button = 1,
.button_gpio = GPIO_RB_0,
.jog_gpio_a = GPIO_RA_5,
.jog_gpio_b = GPIO_RB_1,
};
static struct platform_device ip7500iap_jog_device = {
.name = "ubicom32jog",
.dev = {
.platform_data = &ip7500iap_jog_platform_data,
},
};
/******************************************************************************
* Devices on this board
*/
static struct platform_device *ip7500iap_devices[] __initdata = {
&ip7500iap_i2c_device,
&ip7500iap_i2c_2_device,
&ip7500iap_jog_device, //Add jog to the platform device list
};
...
Build and Load
Save the board file, rebuild the kernel, and burn the board. Additional instructions can be found here.
/scratch2/jbass/ubicom-distro> make linux /scratch2/jbass/ubicom-distro> make install_distro
Catching User Space Events
At this point, Linux should initialize the jog dial driver and show the initialization debug in the boot messages.
BusyBox v1.14.1 (2010-04-08 14:35:15 PDT) hush - the humble shell Enter 'help' for a list of built-in commands. / # [ 0.000000] Linux version 2.6.28.10 (jbass@thor) (gcc version 4.4.1 20091103 (stable) (GCC) ) #15 Thu Apr 8 17:02:19 PDT 2010 [ 0.000000] processor dram 40100000-48000000, expecting 40100000-48000000 [ 0.000000] processor dram 40100000-48000000, expecting 40100000-48000000 [ 0.000000] processor ocm 3ffc2100-3ffe1200, expecting 3ffc2100-3ffe1200 [ 0.000000] IP7K Processor, Ubicom, Inc. <www.ubicom.com> [ 0.000000] Device Tree: [ 0.000000] 3fffbb40: sendirq=014, recvirq=013, name=audio [ 0.000000] 3fffbfd0: sendirq=255, recvirq=255, name=board [ 0.000000] 3fffbd8c: sendirq=255, recvirq=255, name=bootargs ... [ 1.940000] input: Ubicom32 Jog as /class/input/input1 [ 1.970000] ubicom32jog: button: 36 irq: 13
Now events can be seen in user space. Pushbtnd is a test application I modified for catching linux input events. The effect of seeing events can be achieved by using cat.
\> pushbtnd /dev/input/event1 &
Input driver version is 1.0.0
Input device ID: bus 0x19 vendor 0x0 product 0x0 version 0x0 Input
device name: "Ubicom32 Jog"
Supported events:
Event type 0 (Reset)
Event code 0 (Reset)
Event code 1 (Key)
Event code 2 (Relative)
Event code 3 (Absolute)
Event type 1 (Key)
Event code 113 (Mute)
Event type 2 (Relative)
Event code 7 (Dial)
Event type 3 (Absolute)
Event code 32 (Volume)
Value 0
Min -2147483648
Max 2147483647
Testing ... (interrupt to exit
Debugging
If the Linux driver is not forwarding events to user space, the first thing to check is if the jog dial has been connected to the IP7K properly. In this application note, the jog dial has the following pinout:
1. Push button (active high) connected to PB4
2. Rotary pins A xor B connected to PA5 (needed for interrupting on A or B)
3. Rotary pin B connected to PB9
This debugging step is to verify that the processor is able to receive the jog dial signals. Some information regarding the IP7K:
1. 0x200N000 where N is the port number (A = 0, B = 1 ... I = 8)
2. 0x200N050 is the GPIO mux control register to enable or disable GPIO on a pin. The corresponding bit must be set to 1.
3. 0x200N004 is the output enable register. To set a pin to input, the corresponding bit must be set to 0.
4. 0x200N00C is the GPIO data input register.
In the snippet below, I am using gdb to modify and print the control and input registers on the IP7K for the pushbutton pin (PB4).
/scratch2/jbass/ubicom-distro> ubicom32-elf-gdb bin/upgrade.elf
GNU gdb 6.8
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "--host=i686-pc-linux-gnu --target=ubicom32-elf"...
(gdb) target ubicom32
__might_sleep (file=<value optimized out>, line=<value optimized out>)
at /scratch2/jbass/ubicom-distro/linux-2.6.x/arch/ubicom32/include/asm/thread.h:75
75 asm volatile (
[New Thread 0]
[New Thread 1]
[New Thread 2]
[New Thread 3]
[New Thread 4]
[New Thread 5]
[New Thread 6]
[New Thread 7]
[New Thread 8]
[New Thread 9]
[New Thread 10]
[New Thread 11]
(gdb) set radix 16
Input and output radices now set to decimal 16, hex 10, octal 20.
(gdb) print *(u32_t *)0x2001050
$3 = 0x0
(gdb) print *(u32_t *)0x2001050|=(1 << 4)
$4 = 0x10
(gdb) print *(u32_t *)0x2001004&=~(1 << 4)
$6 = 0xfffef
(gdb) print *(u32_t *)0x200100C **note: not pressing push button
$7 = 0x0
(gdb) print *(u32_t *)0x200100C **note: holding push button down
$8 = 0x10
A similar approach can be taken to verify the grey encoded output from the rotary dial. Most incremental rotary encoders use a gray code state machine to switch on left and right turns. If the encoder is a mechanical incremental encoder, then likely the inputs change in between the mechanical notches or stops when turning the dial. If the dial is turned between the settled notched state, then you can see the inputs change using a multimeter or gdb as seen above.


