Embedded Systems Lab 2. LED Driver Module Due dates

Due dates
Embedded Systems
Lab 2. LED Driver Module
Demonstration: 4 PM - 6 PM, Thu. Oct 16, 2014. Lab., Room 2353, N5 building.
Report:
6 PM, Mon., Oct 27, 2014. RTCL, room 1230, E3-2 building.
1. Purpose
Understand how to drive the GPIO LEDs in BeagleBone with a suitable module driver.
Target board: BeagleBone (containing 4 GPIO LEDs) with Linux.
Host computer: PC with Linux, cross compiler, NFS, and minicom.
This experiment is for developing a device driver using the module concept, and to run on the module on
the BeagleBone.
2. Problem Statement
Problem 2 (LED Display)
Write a module program for LED display output on the embedded board. Also write an application program
named “metronome2” in the embedded board to output beats of the metronome visually using LED lamps
with fixed tempo of 120 and fixed time-signature of 6/8.
LED Output
Beats (LED lamps on) with respect to time
Period
Given by Tempo input 120.
Number of LED lamps
4. Controlled by Time signature input.
(3 strong, 2 medium, 1 weak)
Duty
50 %
Number of LED lamp output
Time signature 6/8:
3 1 1 2 1 1 ; 3 1 1 2 1
LED display pattern (‘O’
LED 4: X X X X
LED 3: O X X X
LED 2: O X X X
LED 1: O X O X
means on, ‘.’
X X X X X
X X X X X
X X O X X
O X O X O
means off):
X X X X
X X X O
X X X O
X O X O
X
X
X
X
X
X
X
O
X
X
X
X
X
X
X
O
X
X
X
X
X
X
O
O
X
X
X
X
X
X
X
O
X
X
X
X
X
X
X
O
1 ;…
X
X
X
X
…
…
…
…
Note. The various user-configurable tempo and beat will be implemented in the future.
3. Design
A. Hardware connection
User I/O  PC  Serial/USB connection  Embedded board  GPIO  LEDs
(keyboard and display)
1
Fig. 2.1 Block Diagram for Lab 2
B. Software Connection
You require three programs:
1) Minicom on the PC: The same as used in Lab 1.
2) Metronome2 on Embedded Board: Application program.
3) LED driver module on the Embedded Board: Module program.
Metronome2 on embedded board should call a module program on embedded board in order to access
LEDs.
Minicom on the PC can be used for displaying messages from your application program.
C. LED hardware circuit in BeagleBone
[Refer Beaglebone System Reference Manual A5. p. 44]
User LEDs
Four user LEDS are provided via GPIO pins on the processor. Figure 19 below shows the LED circuitry.
2
Fig 2.2. User LEDS in BeagleBone
Table 5. User LED Control
LED
GPIO
User 0
GPIO1_21
User 1
GPIO1_22
User 2
GPIO1_23
User 3
GPIO1_24
D. GPIO (General Purpose I/O)
Internal block diagram of AM3359 processor in BeangeBone is shown in Fig. 2.3. You can see “GPIO”
under Parallel input/output blocks.
3
Fig. 2.3 Internal block diagram of AM3359 Processor.
[Refer http://www.ti.com/product/am3359]
Purpose of GPIO peripheral in AM335x processor
The general-purpose interface combines four general-purpose input/output (GPIO) modules. Each GPIO
module provides 32 dedicated general-purpose pins with input and output capabilities; thus, the
general-purpose interface supports up to 128 (4 × 32) pins. These pins can be configured for the following
applications:
 Data input (capture)/output (drive)
 Keyboard interface with a debounce cell
 Interrupt generation in active mode upon the detection of external events. Detected events are
processed by two parallel independent interrupt-generation submodules to support biprocessor
operations.
GPIO Features
Each GPIO module is made up of 32 identical channels. Each channel can be configured to be used in the
following applications:
 Data input/output
 Keyboard interface with a de-bouncing cell
 Synchronous interrupt generation (in active mode) upon the detection of external events (signal
transition(s) and/or signal level(s))
 Wake-up request generation (in Idle mode) upon the detection of signal transition(s)
Global features of the GPIO interface are:
4


Synchronous interrupt requests from each channel are processed by two identical interrupt
generation sub-modules to be used independently by the ARM Subsystem
Wake-up requests from input channels are merged together to issue one wake-up signal to the
system
Integration
The device instantiates four GPIO_V2 modules. Each GPIO module provides the support for 32 dedicated
pins with input and output configuration capabilities. Input signals can be used to generate interruptions and
wake-up signal. Two Interrupt lines are available for bi-processor operation. Pins can be dedicated to be used
as a keyboard controller.
With four GPIO modules, the device allows for a maximum of 128 GPIO pins. (The exact number available
varies as a function of the device configuration and pin muxing.) GPIO0 is in the Wakeup domain and may be
used to wakeup the device via external sources. GPIO[1:3] are located in the peripheral domain.
5
General-Purpose Interface Basic Programming Model
Set and Clear Instructions
The GPIO module implements the set-and-clear protocol register update for the data output and interrupt
enable registers. This protocol is an alternative to the atomic test and set operations and consists of writing
operations at dedicated addresses (one address for setting bit[s] and one address for clearing bit[s]). The
data to write is 1 at bit position(s) to clear (or to set) and 0 at unaffected bit(s).
Registers can be accessed in two ways:
 Standard: Full register read and write operations at the primary register address
 Set and clear (recommended): Separate addresses are provided to set (and clear) bits in registers.
Writing 1 at these addresses sets (or clears) the corresponding bit into the equivalent register;
writing a 0 has no effect.
Therefore, for these registers, three addresses are defined for one unique physical register. Reading these
addresses has the same effect and returns the register value.
Refer Technical Reference Manual, pp. 4513 – 4515 for further details.
E. Device Driver
Device Driver Concepts [4]
One of the fundamental purposes of a device driver is to isolate the user's programs from
ready access to critical kernel data structures and hardware devices. Furthermore, a wellwritten device driver hides the complexity and variability of the hardware device from the user.
For example, a program that wants to write data to the hard disk need not care if the disk drive
uses 512-byte or 1024-byte sectors. The user simply opens a file and issues a write command.
The device driver handles the details and isolates the user from the complexities and perils of
hardware device programming. The device driver provides a consistent user interface to a large
variety of hardware devices. It provides the basis for the familiar UNIX/Linux convention that
everything must be represented as a file.
Loadable Modules
Unlike some other operating systems, Linux has the capability to add and remove kernel components at
runtime. Linux is structured as a monolithic kernel with a well-defined interface for adding and removing
device driver modules dynamically after boot time. This feature not only adds flexibility to the user, but it has
proven invaluable to the device driver development effort. Assuming that your device driver is reasonably well
behaved, you can insert and remove the device driver from a running kernel at will during the development
cycle instead of rebooting the kernel every time a change occurs.
Loadable modules have particular importance to embedded systems. Loadable modules enhance field
upgrade capabilities; the module itself can be updated in a live system without the need for a reboot. Modules
can be stored on media other than the root (boot) device, which can be space constrained.
Driver File System Operations [4]
Recall that module_init() is used for module initialization (with ‘insmod’ command), and module_exit() is
used for module exit (with ‘rmmod’ command). Now we need some methods to interface with our device
driver from our application program.
After the device driver is loaded into a live kernel, the first action we must take is to prepare the driver for
subsequent operations. The open() method is used for this purpose. After the driver has been opened, we
6
need routines for reading and writing to the driver. A release() routine is provided to clean up after operations
when complete (basically, a close call). Finally, a special system call ‘ioctl()’ is provided for nonstandard
communication to the driver.
Listing 2.1 hellodev1.c
/*
*
*/
File hellodev1.c
A simple character device driver with file system operations
#include <linux/module.h> /* Needed by all modules */
#include <linux/fs.h>
# define HELLO_MAJOR
234
static int debug_enable = 0;
module_param(debug_enable, int, 0);
MODULE_PARM_DESC(debug_enable, "Enable module debug mode.");
struct file_operations hellodev1_fops;
static int hellodev1_open(struct inode *inode, struct file *file)
{
printk("hellodev1_open: successful\n");
return 0;
}
static int hellodev1_release(struct inode *inode, struct file *file)
{
printk("hellodev1_release: successful\n");
return 0;
}
static ssize_t hellodev1_read(struct file *file, char *buf, size_t count,
loff_t *ptr)
{
printk("hellodev1_read: returning zero bytes\n");
return 0;
}
static ssize_t hellodev1_write(struct file *file, const char *buf, size_t
count,
loff_t *ppos)
{
printk("hellodev1_write: accepting zero bytes\n");
return 0;
}
static long hellodev1_ioctl(struct file *file, unsigned int cmd, unsigned long
arg)
{
7
printk("hellodev1_ioctl: cmd=%d, arg=%ld\n", cmd, arg);
return 0;
}
static int __init hellodev1_init(void)
{
int ret;
printk("Hellodev1 Init - debug mode is %s\n",
debug_enable ? "enabled" : "disabled" );
ret = register_chrdev(HELLO_MAJOR, "hellodev", &hellodev1_fops);
if (ret < 0) {
printk("Error registering hellodev1 fops\n");
return ret;
}
printk("Hellodev1: registered successfully!\n");
/* Init processing here ...
*/
return 0;
}
static void __exit hellodev1_exit(void)
{
unregister_chrdev(HELLO_MAJOR, "hellodev");
printk("Goodbye, hellodev1\n");
}
struct file_operations hellodev1_fops = {
owner:
THIS_MODULE,
read:
hellodev1_read,
write:
hellodev1_write,
compat_ioctl:
hellodev1_ioctl,
Changed 2.6.36
open:
hellodev1_open,
release:
hellodev1_release,
};
// ioctl --> compat_ioctl
module_init(hellodev1_init);
module_exit(hellodev1_exit);
MODULE_AUTHOR("Christoper Hallinan");
MODULE_DESCRIPTION("Hello Dev 1 Example");
MODULE_LICENSE("GPL");
This expanded device driver example includes many new lines. From the top, we've had to add a new
kernel header file to get the definitions for the file system operations. We've also defined a major number for
our device driver. Next we see definitions for four new functions, our open, close, read, and write methods.
In keeping with good coding practices, we've adopted a consistent naming scheme that will not collide with
any other subsystems in the kernel. Our new methods are called hellodev1_open(), hellodev1_release(),
hellodev1_read(), and hellodev1_write(), respectively. For purposes of this simple exercise, they are do
nothing functions that simply print a message to the kernel log subsystem.
8
Notice that we've also added a new function call ‘register_chrdev()’ to our hellodev1_init() routine. This line
registers our device driver with the kernel. With that registration call, we pass a structure containing pointers
to the required methods. The kernel uses this structure, of type struct file_operations, to bind our specific
device functions with the appropriate requests from the file system. When an application opens a device
represented by our device driver and requests a read() operation, the file system associates that generic
read() request with our module's hellodev1_read() function.
Device Nodes and mknod
A device node is a special file type in Linux that represents a device. Virtually all Linux distributions keep
device nodes in a common location (specified by the Filesystem Hierarchy Standard [6]), in a directory called
/dev. A dedicated utility is used to create a device node on a file system. This utility is called mknod. An
example of node creation is the best way to illustrate its functionality and the information it conveys. In
keeping with our simple device driver example, let's create the proper device node to exercise it:
$ mknod /dev/hello1 c 234 0
Application program
Now that we have a skeletal device driver, we can load it and exercise it. Listing 2.2 is a
simple user space application that exercises our device driver.
Listing 2.2 Application program use_hellodev1.c
/*
File use_hellodev1.c
#include
#include
#include
#include
#include
#include
*/
<stdio.h>
<stdlib.h>
<sys/types.h>
<sys/stat.h>
<fcntl.h>
<unistd.h>
int main(int argc, char **argv)
{
/* Our file descriptor
int fd;
int rc = 0;
char *rd_buf[16];
*/
printf("%s: entered\n", argv[0]);
// Open the device
fd = open("/dev/hellodev1", O_RDWR);
if (fd == -1) {
perror("open failed");
rc = fd;
exit(-1);
}
printf("%s: open successful\n", argv[0]);
// Issue a read
rc = read(fd, rd_buf, 0);
9
if (rc == -1) {
perror("read failed");
close(fd);
exit(-1);
}
printf("%s: read: returning %d bytes!\n", argv[0], rc);
// Close device
close(fd);
return 0;
}
F. GPIO LED Module
[Hardware Interfacing on the BeagleBone, http://www.nathandumont.com/node/250]
Pin mux
The BeagleBone has far more peripherals than pins, despite being a BGA package. To get around this it
assigns different functionality to each pin based on software selections like most other modern
microcontrollers. The BeagleBone actually has a more flexible arrangement than previous BeagleBoard
variants, allowing run-time customisation of most of the I/O pins from a set of files under the /sys/ folder. If
you take a look at the BeagleBone System Reference Manual starting on page 48 is a description of the I/O
pins. There are up to 66 3.3V GPIO pins available with several I²C, SPI and UART interfaces as well as timers
and PWM etc available as options instead of GPIO on certain pins. There are a group of ADC pins as well but
be careful the ADCs are only 1.8V tolerant so watch how many volts you put across them! The initial
connector pinout mentions the most likely pin mode for each pin, but on the following pages each pin has
details of the up to 8 functions available on each pin. The pin name as far as the kernel is concerned is always
the MODE0 function, which, frustratingly, isn't always the "Signal Name" given in the connector pinout.
GPIO Pins
There are a total of 66 GPIO pins available on the BeagleBone making it a very capable controller for a lot of
things. Using them without writing a Kernel module relies on toggling individual pins one at a time via control
files though, so don't plan on driving big wide parallel busses or anything without significant effort!
GPIO Hardware Overview
Refer AM335X Technical Reference Manual (in pdf form), Ch 25. General-Purpose Input/Output (p. 4505)
Purpose of the Peripheral
The general-purpose interface combines four general-purpose input/output (GPIO) modules. Each GPIO
module provides 32 dedicated general-purpose pins with input and output capabilities; thus, the generalpurpose interface supports up to 128 (4 × 32) pins. These pins can be configured for the following
applications:
• Data input (capture)/output (drive)
• Keyboard interface with a debounce cell
• Interrupt generation in active mode upon the detection of external events. Detected events are
processed by two parallel independent interrupt-generation submodules to support biprocessor
operations.
• Wake-up request generation (in Idle mode) upon the detection of signal transition(s)
GPIO Features
Shared registers can be accessed through “Set & Clear” protocol. The wake-up feature of the GPIO modules
is only supported on GPIO0.
Integration
10
See Figs in p. 4507, TRM
Clocks
GPIO module runs using two clocks:
• The debouncing clock is used for the debouncing sub-module logic (without the corresponding configuration
registers). This module can sample the input line and filters the input level using a programmed delay.
• The interface clock provided by the peripheral bus (OCP compatible system interface). It is used through the
entire GPIO module (except within the debouncing sub-module logic). It clocks the OCP interface and the
internal logic. Clock gating features allow adapting the module power consumption to the activity.
Set and Clear Instructions
The GPIO module implements the set-and-clear protocol register update for the data output and interrupt
enable registers. This protocol is an alternative to the atomic test and set operations and consists of writing
operations at dedicated addresses (one address for setting bit[s] and one address for clearing bit[s]). The
data to write is 1 at bit position(s) to clear (or to set) and 0 at unaffected bit(s).
Registers can be accessed in two ways:
• Standard: Full register read and write operations at the primary register address
• Set and clear (recommended): Separate addresses are provided to set (and clear) bits in registers.
Writing 1 at these addresses sets (or clears) the corresponding bit into the equivalent register; writing a 0
has no effect.
Therefore, for these registers, three addresses are defined for one unique physical register. Reading these
addresses has the same effect and returns the register value.
Clear Data Output Register (GPIO_CLEARDATAOUT):
• A write operation in the clear data output register clears the corresponding bit in the data output
register when the written bit is 1; a written bit at 0 has no effect.
• A read of the clear data output register returns the value of the data output register.
Set Data Output Register (GPIO_SETDATAOUT):
• A write operation in the set data output register sets the corresponding bit in the data output register
when the written bit is 1; a written bit at 0 has no effect.
• A read of the set data output register returns the value of the data output register.
Data Input (Capture)/Output (Drive)
The output enable register (GPIO_OE) controls the output/input capability for each pin. At reset, all the
GPIO-related pins are configured as input and output capabilities are disabled. This register is not used within
the module; its only function is to carry the pads configuration.
When configured as an output (the desired bit reset in GPIO_OE), the value of the corresponding bit in the
GPIO_DATAOUT register is driven on the corresponding GPIO pin. Data is written to the data output register
synchronously with the interface clock. This register can be accessed with read/write operations or by using
the alternate set and clear protocol register update feature. This feature lets you set or clear specific bits of
this register with a single write access to the set data output register (GPIO_SETDATAOUT) or to the clear
data output register (GPIO_CLEARDATAOUT) address. If the application uses a pin as an output and does
not want interrupt generation from this pin, the application must properly configure the interrupt enable
registers.
When configured as an input (the desired bit set to 1 in GPIO_OE), the state of the input can be read from
the corresponding bit in the GPIO_DATAIN register. The input data is sampled synchronously with the
interface clock and then captured in the data input register synchronously with the interface clock. When the
GPIO pin levels change, they are captured into this register after two interface clock cycles (the required
cycles to synchronize and to write data). If the application uses a pin as an input, the application must
properly configure the interrupt enable registers to the interrupt as needed.
11
Device Memory Read/Write
http://linuxgazette.net/issue83/thangaraju.html
A device driver is an entry point to access a device. Developing a device driver is not as simple a task as
writing application programs. Since any dynamically-loaded driver module is attached to the existing kernel,
any error in the driver will crash the entire system. Resource allocation for a device is one of the main
concerns for device driver developers. The device resources are I/O memory, IRQs and ports. This article
presents a risk-free way of allocating resource for an I/O memory mapped device for a dynamically loaded
Linux device driver, and is written so that less experienced Linux users can follow along.
To register the given I/O memory regions, the macro is
void request_mem_region (unsigned long start, unsigned long length, char *device_name);
The string argument char *device_name is the name of device, which will own the I/O memory regions
from start address to length size.
Before the device is unregistered, the allocated I/O memory regions should be released for other devices.
void release_mem_region (unsigned long start, unsigned long length);
The above function will de-allocate the I/O memory regions.
void* ioremap ( unsigned long phys_addr, unsigned long size )
Remap I/O memory into kernel address space. Here no real mapping is done. Only the virtual address is
returned.
To read from I/O memory, use:
unsigned int ioread32(void *addr);
Here, addr should be an address obtained from ioremap (perhaps with an integer offset); the return value is
what was read from the given I/O memory.
There is a similar set of functions for writing to I/O memory:
void iowrite32(u32 value, void *addr);
If you must read or write a series of values to a given I/O memory address, you can use the repeating
versions of the functions:
void ioread32_rep(void *addr, void *buf, unsigned long count);
void iowrite32_rep(void *addr, const void *buf, unsigned long count);
G. Plan for Files
Obviously, to run an application with GPIO LED, you need two programs: Application program and LED
device driver module program. It is very difficult if two programs – application and module – have bugs, since
bugs interact together to generate very strange behavior.
For this purpose, we need two step approaches:
 Debug module M with a simple application A1.
 Debug main application A2 with debugged module M.
Hence we plan three files M, A1, and A2.
12
 Module gpio_led_module1.c M
This module should be usable for both A1 and A2. Hence M should include
gpio_write(fp, data, size_of_1)
The ‘data’ is encoded with binary number (ranging 0 to 15), instead of four integer array.
In ‘data’, each bit n represents on/off of each LED n.
We can write four user LEDs at one time!
Include suitable header files from the kernel source.

A simple application program A1 named gpio_led_app.c
Application program to write to gpio_led_module1.ko.
Iteration of get user input (using scanf with ‘%d’) and write() (actually gpio_write()).
Negative user input terminates the program.

Main application program A2 named metronome2.c
Repeat displaying a measure 16 times. (Actually, infinite times, but we need to stop!)
One measure contains time-signature of 6/8.
H. Algorithm
A typical algorithm can be constructed as follows.
Module program M for LED display (gpio_led_module1.c)
Open_GPIO_LED:
Increase module use count.
Write_GPIO_LED(data):
Display the lower 4 bits of data using four LEDs.
Close GPIO_LED:
Decrease module use count.
Init_module()
Save the current GPIO configuration.
Register the device using register_chrdrv()
// Define file operations of open, write, and close.
Initialize GPIO_IO_init()
// Define usage of GPIO pins.
Cleanup_module()
Unregister the device using unregister_chrdrv().
Restore the GPIO configuration.
A simple application program A1 for write to LEDs (gpio_led_app.c)
Loop
Get user input of an integer (using scanf with “%d” format)
If the user input is negative, exit
Otherwise, bound the user input to 15 max (4-bit binary)
Call write() (actually to gpio_write()) to write to four LEDs at the same time.
A main application program A2 for Metronome time-signature (metronome2.c)
Set P = [ 7, 1, 1, 3, 1, 1];
Open_GPIO_LED
// LED bit pattern to display 3, 2, or 1 lamps
13
Let Tempo = 120.
Let Time_signature = 6/8.
// You may use two variables: numerator and denominator
Repeat the following 16 times (i.e., 16 measures)
Repeat the following 6 times (i=0, … , 5)
Write_GPIO_LED(P(i))
// Turn LED with pattern P(i)
Sleep n ms
// Depending on Tempo
Write_GPIO_LED(0)
// Turn all LEDs off
Sleep n ms
// Depending on Tempo
End repeat i
End repeat
Close GPIO_LED
Note that most of functions (LED pattern generation and timing, etc.) are performed in the application
program A2. The timing and LED pattern can be implemented in the module program: This is better approach,
and will be implemented in the succeeding labs.
4. Preparation
A. Design the algorithm (flowchart) for M, A1, and A2 of the problem 2. Improve if required.
B. Program the module program M - gpio_led_moduel1.c. Modification of the template GPIO driver in
the Appendix is quite a good way.
C. Program a simple application A1 - gpio_led_app.c.
D. Program the main application program A2 – metronome2.c.
5. Lab Procedures
Contents
Step 1. Setup cross development environment for module
Step 2. Test LEDs with commands
Step 3. Compile simple driver module program
Step 4. Run gpio_led_app app with GPIO LED driver module program
Step 5. Run metronome2 app with GPIO LED driver module program.
14
Step 1. Setup cross development environment for module
Note. Step 1 is already done in Lab 1, but should be repeated also after each power on.
1.1 Connection
PC --- Ethernet cable --- Router – Ethernet cable --- Beaglebone
PC ------------------------ USB cable --------------------- Beaglebone
1.2 Set the cross-compile environment for modules on PC
$ cd ~/Embedded/sh
$ source bu-cce-3813-bone63
Setting cross-development environment for BeagleBone Ubuntu 14.04...
Done.
1.3 Start PC NFS server
To start the NFS server, you can run the following command at a terminal prompt:
$ sudo /etc/init.d/nfs-kernel-server start
Check if nfs is started.
$ ps -aux | grep nfs
…...
root
3005 0.0 0.0
0
0?
S
17:51
0:00 [nfsd]
Or simply
$ ~/Embedded/sh/start_nfs_server.sh
1.4 Start nfs client on Beaglebone
Log in to Beaglebone with id ‘ubuntu’ and passwd ‘temppwd’.
Start nfs-client
# su
# ~/start_nfs_client.sh
Step 2. Test LEDs using sysfs and commands
2.1 Select LEDs for test
Note that four LEDs on BeagleBone are connected to GPIOs as follows:
Table 5. User LED Control [from Beaglebone System Reference Manual]
LED
GPIO
User 0
GPIO1_21
User 1
GPIO1_22
User 2
GPIO1_23
User 3
GPIO1_24
Selection: Test User 0 LED.
15
Since LED0 is used as heart beat signal for Ubuntu, we are going to stop the heartbeat, and perform our
experiment.
2.2 Check sysfs file
The control files are all contained in Beaglebone file system:
# ls -F /sys/class/gpio
export gpiochip0@ gpiochip32@ gpiochip64@ gpiochip96@ unexport
Browse the contents of gpiochip32 (containing GPIO1_0 to 31)
# cd /sys/class/gpio/gpiochip32
# ls -F
base label ngpio power/ subsystem@ uevent
Note
# ls -F /sys/class/gpio/gpiochip32
/sys/class/gpio/gpiochip32@
# ls -la /sys/class/gpio/gpiochip32
lrwxrwxrwx 1 root root 0 Jan 1 2000 /sys/class/gpio/gpiochip32 ->
../../devices/virtual/gpio/gpiochip32
# ls -F /sys/devices/virtual/gpio/gpiochip32
base label ngpio power/ subsystem@ uevent
Hence the actual location of gpiochip32 is /sys/devices/virtual/gpio/gpiochip32 .
Getting access to a GPIO (GPIO1_21: User LED0)
Request GPIO1_21 (GPIO53 for User LED 0) from the Kernel.
# echo 53 > /sys/class/gpio/export
-bash: echo: write error: Device or resource busy
Oops! We have to find another way.
2.3 Find LED sysfs
Search /sys/class:
# ls -F /sys/class
backlight/ hwmon/
mmc_host/
scsi_disk/
uio/
bdi/
i2c-adapter/ mtd/
scsi_host/
usbmon/
block/
i2c-dev/
net/
sound/
vc/
bsg/
input/
power_supply/ spidev/
video4linux/
dma/
lcd/
pps/
spi_master/
virtio-ports/
drm/
leds/
pwm/
thermal/
vtconsole/
dvb/
mbox/
rc/
timed_output/ watchdog/
firmware/ mdio_bus/
regulator/
tty/
gpio/
mem/
rtc/
ubi/
graphics/ misc/
scsi_device/ udc/
Found “leds”!
Search /sys/class/leds:
# ls -F /sys/class/leds
beaglebone:green:usr0@ beaglebone:green:usr2@
16
beaglebone:green:usr1@ beaglebone:green:usr3@
Here you see the directories for controlling each of the user LEDs. By default, usr0 flashes a heartbeat pattern
and usr1 flashes when the micro SD card is accessed. Let's control usr0.
2.4 Get access right to usr0 LED
Go to the directory /sys/class/leds
# cd /sys/class/leds
# cd beaglebone₩:green₩:usr0
Note that '₩' should be included before each ':'.
# ls -F
brightness device@ max_brightness power/ subsystem@ trigger uevent
See what's in trigger
# cat trigger
none nand-disk mmc0 timer oneshot [heartbeat] backlight gpio cpu0 default-on tra nsient
This shows trigger can have many values. The present value is heartbeat (enclosed with '[]'). Check the LED,
is it beating?
You can stop the heartbeat via:
# echo none > trigger
Heartbeat is stopped! Check:
# cat trigger
[none] nand-disk mmc0 timer oneshot heartbeat backlight gpio cpu0 default-on transient
2.5 Control on/off of usr0 LED
Turn on/off usr0 LED
# echo 1 > brightness
# echo 0 > brightness
Usr0 LED is turned on and off!
2.6 Control periodic on/off of usr0 LED
LED trigger with timer and 10% duty:
# echo timer > trigger
# echo 100 > delay_on
# echo 900 > delay_off
Observe period and duty of User LED0.
Step 3. Compile simple driver module program
Note. In this Step, we test a simple device driver hellodev1.ko.
3.1 Make a working directory
17
$ mkdir –p ~/Embedded/lab2/a_hellodev
$ cd ~/Embedded/lab2/a_hellodev
3.2 Edit hellodev1.c in Listing 2.1.
3.3 Edit Makefile and make
Edit Makefile
$ gedit Makefile
Makefile contents:
# Embedded Bone-Ubuntu cross-compile module makefile
ifneq ($(KERNELRELEASE),)
obj-m := hellodev1.o
else
SUBDIRS := $(shell pwd)
default:
ifeq ($(strip $(KERNELDIR)),)
$(error "KERNELDIR is undefined!")
else
$(MAKE) -C $(KERNELDIR) M=$(SUBDIRS) modules
endif
app:
arm-linux-gnueabihf-gcc -o use_hellodev1 use_hellodev1.c
clean:
rm -rf *~ *.ko *.o *.mod.c modules.order Module.symvers .pwm* .tmp_versions
rm use_hellodev1
endif
Make
$ make
Check that hellodev1.ko module is generated.
3.4 Edit application program
Edit app use_hellodev1.c in Listing 2.2.
$ gedit use_hellodev1.c
3.5 Make application
$ make app
Check that use_hellodev1 program is generated.
3.6 Test on Beaglebone using nfs.
Insert module hellodev1.ko
# insmod hellodev1.ko
18
# dmesg | tail
…...
[ 5105.054193] Hellodev1 Init - debug mode is disabled
[ 5105.054253] Hellodev1: registered successfully!
# lsmod
Hellodev1.ko ……
Run app.
# ./use_hellodev1
./use_hellodev1: entered
open failed: No such file or directory
Oops! Make node.
# mknod /dev/hellodev1 c 234 0
Run app again
# ./use_hellodev1
./use_hellodev1: entered
./use_hellodev1: open successful
./use_hellodev1: read: returning 0 bytes!
# dmesg | tail
…...
[ 5357.101638] hellodev1_open: successful
[ 5357.101772] hellodev1_read: returning zero bytes
[ 5357.101903] hellodev1_release: successful
Remove module
# rmmod hellodev1
Step 4. Run gpio_led_app app with GPIO_LED driver module
program
4.1 Make a working directory
Make a suitable working directory, such as lab2/e_gpio_led_module1
$ mkdir ~/Embedded/lab2/e_gpio_led_module1
$ cd ~/Embedded/lab2/e_gpio_led_module1
4.2 Edit prepared gpio_led_module1.c
$ gedit gpio_led_module1.c
Make sure that you set correct header files: am33xx.h and gpio.h in the directory ~/Embedded/Include.
4.3. Make
19
Edit prepared Makefile
$ gedit Makefile
Make
$ Make
4.4 Edit prepared app – gpio_led_app.c
$ gedit gpio_led_app.c
4.5 Make app.
$ make app
Check that gpio_led_app program is generated.
4.6 Test on Beaglebone.
Insert module
# insmod gpio_led_module1.ko
Note the Major number reported: N.
# dmesg | tail
[ 1054.200144]
A. Init gpio_led: The major number is 240
In this case, N equals 240.
Make node
# mknod /dev/gpio_led C 240 0
Run app.
# ./gpio_led_app
GPIO_LED device open success
Enter data for LED: 15
Write GPIO-LED with data f
Enter data for LED: -9
GPIO_LED closed.
// All four LEDs on!
Step 5. Run metronome2 app with GPIO_LED driver module program.
5.1 Use the same directory as in Step 4.
5.2 Edit prepared app – metronome2.c
$ gedit metronome2.c
5.3 Make app.
Edit the Makefile suitably.
Make
$ make app
Check that metronome2 program is generated.
20
5.4 Test on Beaglebone.
# insmod gpio_led_module1.ko
# dmesg | tail
# mknod /dev/gpio_led C 240 0
# ./metronome2
6. Demonstration
Demonstrate the result of Step 5.4 to TA.
7. Report
Each student should prepare his own report containing:
Purpose
Experiment sequence
Experimental results
Discussion: should be different even for each member of the same team.
References
8. References
[1] BeagleBone A5 System Reference manual.
[2] AM335x ARM Cortex-A8 Microprocessors (MPUs) Technical Reference Manual (Rev. F), Texas Instruments,
http://www.ti.com/lit/ug/spruh73f/spruh73f.pdf
[3] AM335x ARM Cortex-A8 Microprocessors (MPUs) (Rev. D) , Texas Instruments,
http://www.ti.com/lit/ds/symlink/am3359.pdf
[4] Christopher Hallinan, “Embedded Linux Primer”, Prentice Hall, 2007.
Appendix
Template program gpio_led_module1.c
//
File template_gpio_led_module1.c
//
// A simple character driver with four user LEDs in Beaglebone Ubuntu
//
//
Programmed by Byung Kook Kim, Sep. 26, 2014.
#include
#include
#include
#include
<linux/module.h>
<linux/fs.h>
<linux/ioport.h>
<asm/io.h>
#include
#include
#include
#include
<linux/init.h>
<linux/device.h>
<linux/semaphore.h>
<linux/cdev.h>
21
#include
#include
#include
#include
#include
#include
#include
<linux/fs.h>
<linux/errno.h>
<asm/uaccess.h>
<linux/moduleparam.h>
<linux/string.h>
<linux/ioctl.h>
<linux/slab.h>
// AM33xx specific register definitions
//- #include <plat/am33xx.h>
//-#include <plat/gpio.h>
#include "../../Include/am33xx.h"
#include "../../Include/gpio.h"
///#define GPIO_LED_MAJOR 234
#define GPIO_LED_MAJOR
0
// Should match to mknod
// Auto select major number
// GPIO_LED File operations table
struct file_operations gpio_led_fops;
// GPIO1 usage flag
static int gpio1_usage
= 0;
int major;
// Device major number in init/cleamup_module
// Global data
// GPIO1 physical/virtual address
unsigned long gpio1_start = AM33XX_GPIO1_BASE; // GPIO1 Start address
unsigned long gpio1_len = 0x1000;
// Length of GPIO1 addresses
void __iomem * gpio1_vbase = NULL;
// GPIO1 base virtual address
int gpio1_chk;
// You need to insert your codes into this function.
static ssize_t gpio_write (struct file *filp, const char *wbuf, size_t wcount,
loff_t *f_pos)
{
unsigned int mdata;
unsigned int oe1, oe1new;
unsigned int dout1, dout1new;
// Get data from app program
get_user(mdata, wbuf);
// (To var, Src addr)
// First set GPIO1 21 - 25 direction to output ('0')
// Add oyur code: Read GPIO1 Direction register to oe1
// Add your code: Set new value of GPIO1 Direction register as oe1new.
// Add your code: Write GPIO1 Direction register
printk("GPIO1_OE: %08x to %08x\n", oe1, oe1new);
//
//
//
//
Then set
Add oyur
Add oyur
Add oyur
gpio data out
code: Set GPIO1 Clear Register
code: Set GPIO1 Set Register
code: printk some message
return (0);
}
22
// GPIO open function for open() call
static int gpio_open(struct inode *inode, struct file *filp)
{
int result;
// 0. Check & set GPIO1 usage flag
if( gpio1_usage != 0 ) return -EBUSY;
gpio1_usage = 1;
// B. memory mapping for GPIO control
// B1. Check memory region and then request - GPIO1
gpio1_chk = check_mem_region(gpio1_start, gpio1_len);
if (gpio1_chk < 0) {
printk(" B1. Warning: GPIO1 check_mem_region failed...\n");
//- return(result);
} else {
request_mem_region (gpio1_start, gpio1_len, "gpio1");
// printk(" B1. GPIO1 request_mem_region.\n");
}
// B2. Physical to virtual memory mapping - GPIO1
gpio1_vbase = ioremap(gpio1_start, gpio1_len);
if (!gpio1_vbase) {
printk(" B2. GPIO1 ioremap failed.\n");
// -B1. Release memory region
if (gpio1_chk >= 0)
release_mem_region (gpio1_start, gpio1_len);
return(-2);
}
// printk(" B2. gpio_led open: ioremap gpio1_vbase= %08x\n", (unsigned
int)gpio1_vbase);
// C. Set MUXes
// Assumed to be done by Kernel correctly.
printk(" D. gpio_led opened.\n");
return 0;
}
// GPIO release function for release() call
static int gpio_release(struct inode *inode, struct file *filp)
{
// -B. REverse handling GPIO1 memory map
// -B2. Unmap ioaddress
iounmap(gpio1_vbase);
// -B1. Release memory region
if (gpio1_chk >= 0)
release_mem_region (gpio1_start, gpio1_len);
// -0. Clear GPIO1 usage flag
gpio1_usage = 0;
printk(" -D. gpio_led released.\n");
23
return 0;
}
// Define file operations table for gpio_led driver
struct file_operations gpio_led_fops =
{
owner:
THIS_MODULE,
open:
gpio_open,
write:
gpio_write,
release:
gpio_release,
};
// GPIO_LED init_module
static int __init gpio_led_init_module (void)
{
// A. Register gpio_led as a character device
major = register_chrdev( GPIO_LED_MAJOR, "gpio_led", &gpio_led_fops );
if (major < 0) {
printk(KERN_WARNING "Error registering gpio_led fops %d\n", major);
return major;
}
printk("
A. Init gpio_led: The major number is %d\n", major);
// Return success
return 0;
}
// GPIO_LED cleanup_module
static void __exit gpio_led_cleanup_module (void)
{
// -A. Unregister gpio_led device
unregister_chrdev( GPIO_LED_MAJOR, "gpio_led" );
printk(" -A. Cleanup gpio_led: Unregistered.\n");
}
// Define module init and cleanup functions
module_init(gpio_led_init_module);
module_exit(gpio_led_cleanup_module);
MODULE_AUTHOR("Byung Kook Kim");
MODULE_DESCRIPTION("GPIO_LED Driver");
MODULE_LICENSE("GPL");
FIN
24