www.elektor-magazine.
com
magazine
Embedded Linux
Made Easy
Part 1: Kickoff
Part 2: Hardware
Part 3: Software Development
Part 4: A Look at the Kernel
Part 5: I/O, ADC, PWM, LAN & Web Server
Part 6: Networks and Servers
Part 7: I2C, Serial Ports and RS485
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
Embedded Linux
Made Easy(1)
Part 1: Kickoff
By Benedikt Sauter (Germany)[1]
Today Linux can be found running on all sorts of
devices, even coffee machines. Many electronics
enthusiasts will be keen to use Linux as the basis of a new
microcontroller project, but the apparent complexity of the
operating system and the high price of development boards has been
a hurdle. Here Elektor solves both these problems, with a beginners course
accompanied by a compact and inexpensive circuit board.
There are many introductory courses available for eight-bit microcontrollers, but comparatively little literature and comparatively
few websites address the needs of beginners in embedded Linux. Many descriptions assume too much prior knowledge
or descend too quickly into cryptic source
code, specialist topics or other unwanted
detail. However, Linux is at its heart just a
classical, well-structured and very modular
piece of firmware, and, despite its apparent
complexity, it is possible to understand it in
terms of ordinary microcontroller concepts.
What do we need to take our first steps in
this world? In the early days of computing
and microprocessors it was relatively easy
for an interested user to understand the
hardware, the operating system, the applications, drivers and all other parts of his
machine. The main reason for this is that
there was not the same enormous choice
of components as we enjoy today, and so
it was easier to focus ones efforts on the
components and tools that were available.
Normally people built and operated their
hardware in their own individual way, and
hence it was usually down to the individual
to fix his own bugs and faults. This in turn
demanded an in-depth knowledge of how
the system worked.
This is the approach we will take to understanding Linux in this series of articles. Our
hardware will be a compact board that
includes everything necessary for a modern
embedded project (Figure1): a USB interface, an SD card connection and various
other expansion options. It is also easy to
hook the board up to an Ethernet network,
as we shall see later in the series. The Elektor
Linux board is based on the Gnublin open
source project, which was developed at the
Augsburg University of Applied Sciences for
teaching purposes[2].
There are no specialist components on
the Linux board. The printed circuit board
has two layers and is available from Elektor
ready populated (see Figure2). The second
part of this series will look more closely at
the circuit diagram, but we also show it here
(Figure3) for the sake of completeness. The
hardware is available under the freedomdefined.org/OSHW licence, which means that
the CAD files are also available[3]. Needless to say, the software for this project is
also entirely open source, and is as always
available for download from the Elektor
website[3].
Step by step
Figure4 shows an outline of the roadmap
for this course. The first thing for Linux
beginners to understand is where the most
important applications and software components originate from. These components
form the basis of our Linux system, just as
they do of any PC-based Linux system. We
will also learn how the hardware is constructed and how it operates. And then we
will see how to install a suitable Linux development environment on a PC to compile
our own source code: when installing Linux
on a microcontroller it is much easier if the
host PC development environment is also
running Linux (for compatibility of directory path names if nothing else). Windows
users have the option of installing Linux in
a virtual machine.
By the end of the course we hope you will
have gained a better understanding of how
the Linux operating system works through
practical example applications. Our final
goal will be to construct a simple heating
controller with a graphical display and data
analysis via a browser.
The origins of GNU and Linux
It is important for anyone using Linux seriously to understand why and how the free
GNU/Linux implementation of Unix arose
and how it is organized. This allows better
understanding of where the boundary of
the operating system lies and, most importantly, to work out which piece of software
or hardware might be responsible for a
problem.
Significant development of Unix[4] began
in 1969 at Bell Laboratories in the USA. Ken
Thompson wrote the first version in assembler. To get a better idea of what interfaces
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
Features of the Elektor Linux board
Elektor Linux Board
For USB hardware
For load switching
As root console
7 - 12 V
DC
Power supply
1.2 V
USB interface
for
peripherals
Relays
Silabs CP2102
USB console
GPIO/AD/
I2C/SPI
IO, AD channels,
I2C and SPI
3.3 V
NXP
LPC3131
Processor
1.8 V
AMIC
DRAM
memory
IO
Wire terminals
Power supply
Pushbuttons
3.3 V output
CLK
Non-volatile
memory
Wire terminals
and drivers would be needed he wrote,
along with Dennis Ritchie, the game Space
Travel. From a microcontroller programmers point of view this approach is easy to
understand: when developing an embedded system it is important to plan from
the beginning how the software (including
hardware drivers and utility functions) will
be structured to maximize the reusability
of the source code. Ken and Dennis soon
worked out which components belonged
in the operating system and how the
whole thing should be organized. Between
1972 and 1974 they re-wrote the heart of
the operating system from scratch using
the C programming language, which had
also been developed at Bell Laboratories.
The operating system, including a C compiler, was distributed to universities free of
charge.
At the end of the 1970s AT&T, the carrier
behind Bell Laboratories, realized that there
was potential to market Unix commercially.
Until that time it was normal for software to
be shared and exchanged freely. Pirating
and other such illegal acts were unheard
of. Software was distributed with the goal of
improving it collaboratively. This underlying
attitude still lies at the heart of the free and
open-source software movement[5],[6].
Once AT&T had started to sell Unix it could
no longer be exchanged freely. Suddenly,
because of the high licence costs, it was
no longer feasible to use it in university
courses or for self-study. At this time more
and more companies started to licence their
own Unix variants: one example is Siemens
SINIX, which has its origins in the Xenix version of Unix from Microsoft.
Richard Stallman, at MIT in the USA[7],
was not happy that Unix was now in general only available to companies. It seemed
that the happy days when Unix was copied and shared between colleagues and
friends were over. There was only one solution: a complete new and free version of the
Unix system had to be developed from the
ground up.
And so GNU[8] (for GNUs not Unix) was
born in 1983. A huge amount of work lay
ahead of Richard Stallman: everything had
to be reimplemented in order to create a
100% free operating system. He would
need:
two-layer board using readily-available components
no special debugging or programming hardware required
fully bootable from an SD memory card
Linux pre-installed
180MHz, 8MB RAM (32MB optional), 64MB swap
integrated USB-to-RS-232 converter for console access
relay, external power supply, and pushbuttons for quick testing
four GPIO pins, 3 A/D channels and a PWM channel on-board
I2C and SPI buses accessible from Linux
USB interface for further expansion
I/O and A/D
channels
Bootloader
MicroSD card
up to 32 GB
120026 - 13
Figure1. The board makes a powerful basis for custom microcontroller projects.
A network interface is also available.
Figure2. The printed circuit board is available from Elektor ready-populated.
3
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
+1V2
C6
C4
+3V3
+3V3
L3
47n
R13
C13
3n3
4uH7
R6
AS1324
DNP
10u
VOUT/
VFB
SW
SPI_MOSI
B7
B8
M13
M12
M11
N14
F12
E14
G10
TP1 TP2
LPC_RXD
P12
LPC_TXD
N12
N13
TP5
P14
TP6
B9
PWM_DATA
USB_VDDA12-PLL
GPIO3
I2SRX_WS0
GPIO4
SPI_CS_OUTO
MGPIO6
SPI_SCK
MGPIO7
SPI_MISO
MGPIO8
SPI_CS_IN
MGPIO9
SPI_MOSI
MGPIO10
GPIO12
MI2STX_BCK0
GPIO13
MI2STX_WS0
GPIO14
MI2STX_CLK0
GPIO15
I2STX_DATA1
GPIO16
I2STX_BCK1
GPIO17
I2STX_WS1
GPIO18
B6
MUART_RTS_N
ADC10B-GPA1
PWM_DATA
A10
R26
Q2
C30
10k
LPC_MCI_CMD
A5
LPC_MCI_DAT0
B5
LPC_MCI_DAT1
C5
LPC_MCI_DAT2
A4
LPC_MCI_DAT3
J14
GPIO14
J13
GPIO15
GPIO2
GPIO18
H11
GPIO19
SV1
GPA0
A14
GPA1
C14
S3
C11
+3V3
10k
LPC_D10
A1
LPC_D11
C2
LPC_D12
G3
LPC_D13
D3
LPC_D14
E3
LPC_D15
F3
LPC_DQM0
H1
LPC_WE
J2
J1
J3
K1
K2
E6
E7
D4
EBI_D_6
MLCD_DB_6
IC6.B
EBI_D_7
MLCD_DB_7
EBI_D_8
MLCD_DB_8
EBI_D_9
MLCD_DB_9
EBI_D_10
MLCD_DB_10
EBI_D_11
MLCD_DB_11
EBI_D_12
MLCD_DB_12
EBI_D_13
MLCD_DB_13
EBI_D_14
MLCD_DB_14
EBI_D_15
MLCD_DB_15
LPC313XFET180
EBI_DQM_0_NOE
EBI_A_0_ALE
EBI_A_1_CLE
EBI_NWE
EBI_NCAS_BLOUT_0
NAND_NCS_0 EBI_NRAS_BLOUT_1
NAND_NCS_1
NAND_NCS_2
MLCD_RS
NAND_NCS_3
MLCD_RW_WR
MNAND_RYBN0
MLCD_E_RD
MNAND_RYBN1
MLCD_CSB
N6
LPC_A2
P6
LPC_A3
N7
LPC_A4
P7
LPC_A5
K6
LPC_A6
P5
LPC_A7
N5
LPC_A8
L5
LPC_A9
K7
LPC_A10
N4
LPC_A11
K5
LPC_A12
P4
LPC_A13
P3
LPC_A14
N3
LPC_A15
B3
LPC_A0
A2
LPC_A1
G1
LPC_CAS
H2
LPC_RAS
P8
L8
DAT3
CMD
V+
CLK
GND
DAT0
DAT1
SW_A
SW_B
SD-CardSocket
C18
C19
10u
100n
10n
4 GPIO15
10k 10k 10k 10k 10k 10k
L7
L12
C12
+1V2
C2
10u
301k
+1V8
C33
10u
100n
F5
10n
G5
H5
+1V8
10u
M7
C25
100n
M9
10n
+3V3
D7
LPC_CKE
K8
C22
10u
E8
C17
100n
G12
10n
MNAND_RYBN3
L10
VSSA12
LPC313XFET180
VDDE_IOA
VSSE_IOA
VDDE_IOA
VSSE_IOA
VDDE_IOA
VSSE_IOA
VDDE_IOA
VSSE_IOA
VDDE_IOA
VSSE_IOA
VDDE_IOB
VSSE_IOB
VDDE_IOB
VSSE_IOB
VDDE_IOB
VSSE_IOB
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VSSE_IOC
K11
+3V3
IC6.C
VDDE_IOC
D5
C23
VSSI
VSSE_IOB
C13
LPC_CS
VDDI
VDDE_IOB
M5
C26
VSSI
VSSE_IOA
L4
C27
VSSI
VDDI
VDDA12
E5
C31
VDDI
VDDA12
E10
B2
C32
VSSI
VPP_B
D11
100n
VSSI
VDDI
VPP_A
C9
C38
VDDI
VSSI
A9
R19
VDDE_ESD
A13
VSSE_IOC
ADC10B_GNDA
ADC10B_VDDA33
H7
LPC_A1
H8
LPC_A2
J8
LPC_A3
J7
LPC_A4
J3
LPC_A5
J2
LPC_A6
H3
LPC_A7
H2
LPC_A8
H1
LPC_A9
G3
LPC_A10
H9
LPC_A11
G2
LPC_A15
G1
LPC_A14
G7
LPC_A13
G8
A0
A1
DQ0
DQ1
A2
DQ2
A3
DQ3
IC5
A4
DQ4
A5
DQ5
A6
DQ6
DRAM
A7
A8
DQ7
DQ8
A43E26161
A9
DQ9
A10
DQ10
A11
DQ11
A12
DQ12
BA0
DQ13
BA1
DQ14
DQ15
LPC_DQM0 E8
LPC_DQM1 F1
LPC_CLK
F2
LPC_CKE
F3
LDQM
CAS
UDQM
RAS1
CLK
CKE
+5V
LPC_MCI_DAT2
LPC_MCI_DAT3
LPC_MCI_CMD
4
LPC_MCI_CLK
WE
CS
A8
LPC_D0
VCC
LPC_MCI_DAT0
LPC_MCI_DAT1
LPC_MCI_CD
D+
GND
USB
J3
R9
GPA0
GPA3
4 PWM_DATA
I2C_SCL
D12
G4
L6
L11
E9
C3
C4
E4
F4
H4
K3
M3
M4
M6
M8
B12
D6
D8
D9
G11
L9
L13
A12
LPC_A1
SPI_MOSI
SPI_MISO
B8
LPC_D2
SYSCLK_O 9
10
SPI_SCK
C9
LPC_D3
GPIO14
11
12
GPIO11
C8
LPC_D4
13
14
D9
LPC_D5
D8
LPC_D6
E9
LPC_D7
E1
LPC_D8
D2
LPC_D9
D1
LPC_D10
C2
LPC_D11
C1
LPC_D12
B2
LPC_D13
B1
LPC_D14
A2
LPC_D15
F7
LPC_CAS
F8
LPC_RAS
F9
LPC_WE
G9
LPC_CS
D
D+
GND
47u
4
5
12
11
MINI-USB
REGIN
VBUS
VDD
RI
RST
D+
LPC_VBUS
LED5 270R
13
RTS
SUSPEND
CTS
3
R10
R14
GPIO18
LPC_DP
USB_ID
2
C10
R18
JP1
1M
1M
100n
10k
2
1
28
27
26 LPC_RXD
25 LPC_TXD
24
23
TP7
GND
3
Relay
BSS123
+5V
GND
1
270R
G6D-1A-ASI5VDC
LPC_DM
TXD
SUSPEND
MC78M05ABDT
Q4
270R
X1
K4
BAT54
DSR
RXD
EXP
11
DTR
CD21021
GND
D4
DCD
IC7
D-
+5V
1k
R24
1u
C24
+5V
LED1
R27
C28
+5V
J4
X2
+5V
+5VEXT
+3V3
+3V3
LED2
I2C_SDA
B9
GPA1
R22
K1
D
A11
C7
+3V3
GPA1
C21
C6
N9 LPC_DQM1
U1
DM3D-SF
DAT2
MNAND_RYBN2
R29
1
R25
3 GPIO14
GND
R23
100n
2 GPIO11
3.3V
R30
X4
X7
R20
10k
GPA3
RESET
22p
A3
LPC_A0
R17
R4
J5
B14
+3V3
R16
GND
10R
K14
B13
ADC10B-GPA3
R21
GPIO0
R15
22p
301k
AS1324
H3
LPC_CLK
P9
K13
ADC10B-GPA2
+3V3
150k
K12
RESET
+3V3
VOUT/
VFB
SW
10u
J11
C29
22p
B1
LPC_D9
LPC_MC1_CD B4
12MHz
S1
C1
LPC_D8
GPIO11
J12
UART_TXD
ADC10B-GPA0
A6
H10
GPIO20
MUART_CTS_N
LPC_MCI_CLK
H13
GPIO19
UART_RXD
LED1
C11
GPIO11
MI2STX_DATA0
GPIO2
B11
MGPIO5
+3V3
GPIO15
L14
GPIO2
I2SRX_BCK0
GPIO0
J10
GPIO1
LPC313XFET180
I2SRX_DATA0
IC2
4uH7
C3
C7
VIN
LED1
C8
K10
GPIO0
I2SRX_BCK1
LPC_D7
MLCD_DB_5
N8
J9
SPI_MISO
TP4
I2SRX_DATA1
10k
SYSCLK_O
D2
EBI_D_5
E7
A8
G13
SYSCLK_O
I2SRX_WS1
R7
J1
A7
SPI_SCK
J4
LPC_D6
MLCD_DB_4
A1
TP3
H12
D1
EBI_D_4
D3
P11
CLOCK_OUT
R2
LPC_D5
MLCD_DB_3
VDDE-IOC
N10
10k
BUF_TMS
CLK_256FS_O
I2C_SCL1
D14
+3V3
E2
MLCD_DB_2
EBI_D_3
VSSE-IOC
R1
D13
LPC_D4
EBI_D_2
C7
M10
F11
BUF_TCK
E1
VDDE-IOC
F13
SCAN_TDO
I2C_SDA1
TDO
F10
BUF_TRST_N
I2C_SCL0
TCK
E11
F1
LPC_D3
VSSE-IOC
F14
+3V3
IC6.A
I2C_SDA0
M14
LPC_D2
MLCD_DB_1
E3
G14
ARM_TDO
USB_VSSA_TERM
TDI
TMS
MLCD_DB_0
B3
E13
EBI_D_1
VDDE-IOC
E12
I2C_SCL
TCK
USB_VSSA_REF
USB_GNDA
R3
EN
22p
301k
GND
EBI_D_0
A7
I2C_SDA
TMS
TRST_N
K9
P10
F2
VDDE-IOC
D10
TDI
G2
LPC_D1
VDDE-IOC
C10
P13
TRST_N
USB_RREF
LPC_D0
VSSE-IOC
N1
RSTIN_N
RESET
VSSE-IOC
L3
H14
10k
K4
12k
1%
VOUT/
VFB
AS1324
L2
+1V2
USB_ID
FFAST_OUT
J5
R11
USB_DP
SW
C9
N11
JTAGSEL
B10
M1
USB_ID
USB_DM
R8
+1V2
4u7
VIN
IC1
10u
66k5
L1
M2
P1
P2
USB_VBUS
USB_VDDA33-DRV
LPC_DP
USB_VDDA33
N2
FFAST_IN
L2
LPC_DM
4uH7
C1
GND
2
LPC_VBUS
22p
301k
VSSE-IOC
100n
C12
IC3
D7
10u
C15
L1
EN
C3
C16
4u7
C8
+5V
C5
+1V8
VIN
A9
C14
R5
EN
10k
VDDE-IOC
220p
VSSE-IOC
1n
R12
+3V3
B7
10u
4u7
VDDE-IOC
C36
VSSE-IOC
C35
A3
C37
+3V3
IC8
X6
1
2
DC 7 - 12V
120026 - 11
Figure3. The circuit diagram is surprisingly straightforward for such a powerful board.
4
Personal Download for Fernando Faria Corra | copyright Elektor
C20
47u
+5VEXT
MICROCONTROLLERS
a C compiler, linker and assembler (a
toolchain)
a text editor to write source code
an operating system kernel
various utility programs
a root file system for the operating
system
By 1990 all the important parts had been
assembled, with the exception of the operating system kernel. Richard Stallman knew
that it only made sense to start working on
the kernel when a stable text editor and
compiler were in place.
The beginnings of Linux
At around the same time a Finnish student
by the name of Linus Torvalds bought his
first x86 computer and wrote a simple terminal program as an exercise to understand
the computer better[9]. He installed Minix,
a paid-for Unix variant that had been developed by a professor from Amsterdam and
his team (and which is still in use today). As
he worked on his terminal program, Linus
Torvalds saw that it was becoming more
and more like an operating system in itself.
So as to allow compatibility with the widest possible range of existing software
it was clear that the system would have
to be POSIX compliant. POSIX defines a
standard for how a Unix operating system
should appear externally. Fortunately for us,
his local bookshop had the relevant POSIX
documentation: this was probably in the
form of a manual for one of the many other
Unix variants. The main thing was that Linus
Torvalds had the information about how the
system calls were named and with what
arguments they were used.
In 1992 the young developer made his
creation available on the Internet for free
download[10]. He needed a suitable
licence under which to release it, and it
so happened that he had recently heard
Richard Stallman speaking at his university. The GNU GPL (the open source licence
used by the GNU project) was ideal. And
then something happened which had not
been planned: the open source community
quickly realized that Linus Torvalds kernel
was the missing element in Richard Stallmans GNU project! It is worth noting that
Stallman had already started on a free GNU
kernel called Hurd, although this does
Start
History and
background of
GNU/Linux
Overview
of components
(SW and HW)
Bootloader creation /
kernel conversion
Hardware
description
Individual boot
image creation
Network access
USB peripherals
Hardware
commissioning
Development
platform setup
Linux kernel
(overview)
Application
development /
script languages
Demo project:
heating control
Extra options
for the
Elektor Linux Board
End
120026 - 12
Figure4. Road map of our multi-part introduction to embedded Linux.
Elektor Linux Board
PC
Text editor
Text editor
Compiler
Assembler
Linker
Bootloader
Kernel (as image)
Toolchain
RS232
serial console
Kernel (as source text)
File system (as source text)
USB
Shell (Console)
C library (libc)
User
application
File system (as image)
C library (libc)
120026 - 14
Figure5. For software development a PC (running Linux) is used
in conjunction with the target board.
5
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
The kernel
Figure6. Screenshot of the console in action.
nothing to detract from the importance of
Torvalds contribution.
Now, with the GNU project complemented
by the Linux kernel, a complete free and
open operating system was available for
the first time. Strictly speaking it is best to
refer to the operating system as GNU/Linux,
the Linux part referring to the kernel and
GNU to the rest of the operating system
supplied by the GNU project.
The big picture
After that brief historical digression it is
time to take a look at the overall picture of
what components make up our GNU/Linux
system (see Figure5). In essence little has
changed since the early days: the same
basic elements that were needed then are
needed for the Elektor Linux board today.
For this project we will use several of these
original Linux programs. Others we will
need to give a wider berth, and we will mention the reasons for this later on.
Text editor
Todays developers are accustomed to using
their own particular choice of text editor,
with syntax highlighting, code completion
and built-in API documentation. In order
to make small changes quickly to files on
the Elektor Linux board we need a text editor that can be used over the Linux console
(see below).
There are traditional editors such as vi
(or vim in its more user-friendly form)
and nano that fit the bill perfectly. Both
of these are present in the root file system
(see below) of the Elektor Linux board. Linux
developers often use the same text editor
on their desktop PC, so as to avoid confusion when switching between editors.
Another option is the widespread Emacs
editor, whose reputation some readers may
be familiar with. Emacs was developed by
Richard Stallman as part of the GNU project. It is popular with experienced developers because of the wide range of functions
it provides; however, beginners might be
better off with a more lightweight editor.
Compiler + linker + assembler =
tool chain
In order to run programs on a processor it is
necessary to convert it to the machine code
of the relevant target architecture. The GNU
tool chain includes all the software components needed to convert C into machine
code. It has been designed so that it is relatively straightforward to add a new instruction set, and so, for example, versions are
available for x86, AMD64, AVR, ARM, MIPS,
MSP430 and many other processors. The
Elektor Linux board uses an ARM-compatible microcontroller and so we use the corresponding ARM tool chain. More on this later,
when we come to install it.
The kernel lies at the heart of the operating system. It originates in the source code
written by Linus Torvalds, but since then
some ten thousand kernel developers have
worked on the code. However, Torvalds has
always had the final say on whether changes
and extensions are accepted into the kernel or rejected. Any developer not agreeing
with his decision is of course free to fork
his own version of the kernel, as the whole
thing is open source. To date, however,
there has been no significant forking of the
Linux kernel code. The development of the
software is organized using mailing lists,
and anyone is allowed to join these lists
and make suggestions. These suggestions
will be examined by others and discussed.
With the exception of just a few lines of
code, the kernel is written entirely inC, and
can be simply converted from C to machine
code using the GNU tool chain. We will
see how this is done at a later point in this
series.
File system
Under the Windows operating system it
is clear enough that a users files go in the
directory Documents and Settings, programs are installed in C:\Program Files,
and lower-level operating system files are
kept in the System32 directory under C:\
Windows. Like any other operating system,
Windows has its own structure for organizing its many program and data files. So
naturally we ask how things are arranged
in embedded GNU/Linux systems. Here
the origins do not lie with Linus Torvalds
or Richard Stallman; the common basis for
all Unix and Linux file systems was mostly
developed incrementally as part of POSIX
standardization. The so-called root file
system structure has been further developed by the well-known distributions such
as Debian, SUSE and the like. These distributions each offer the user a complete GNU/
Linux system with applications already
installed, a graphical user interface, and an
up-to-date kernel.
To use Linux on our board we will also need
to set up a root file system. For our situation a fully-featured desktop version of
Linux would be too unwieldy, and a cutdown version of the full system will usu-
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
Internet Links
[6] http://en.wikipedia.org/wiki/Open_source
[1] sauter@embedded-projects.net
[7] http://en.wikipedia.org/wiki/
Massachusetts_Institute_of_Technology
[2] www.gnublin.org (site in German only)
[8] www.gnu.org
[3] www.elektor.com/120026
[4] http://en.wikipedia.org/wiki/Unix
[5] http://en.wikipedia.org/wiki/Free_software
ally suffice, with a relatively small choice of
programs and libraries. There are speciallywritten programs that can be used to create
custom root file systems; alternatively, all
the mainstream distributions
offer ready-made versions for
ARM processors. More on this
topic later.
[9] 'Rebel Code: Linux and the Open Source Revolution', Glyn
Moody: ISBN 0738206709
[10] www.kernel.org
is loaded at run-time as required by application programs (it is dynamically linked).
This saves memory as a single copy of the
library can serve all running applications.
The standard C library
Applications provide the visible face of any computer or
similar product. The operating
system sits in the background,
driving the hardware, allocating memory, handling communications over the network
or other interfaces and much
else besides. Now, application
writers do not want to spend
their time forever rewriting
functions to read and write
files, manipulate strings and
so on. To the developers rescue comes the standard C
library, in its most popular
form known as libc. Slimmed-down versions of this library are available that are
suitable for embedded systems where computing power and storage are relatively limited compared to desktop PCs.
The standard C library provides the interface between the application and the
kernel. It also includes a number of commonly-wanted utility functions. The library
make a Linux system easier to operate. We
will look at the shell in greater depth later.
When Linux is booted on a desktop PC the
keyboard and screen provide the traditional
root console. (It is usually possible to switch to this console
from the graphical user interface
by pressing control-shift-F1.)
When administering machines
remotely it is common to use a
protocol such as SSH or, where
security is not a consideration,
TELNET, to access the root console over a network connection.
A third option is to access the
console over an RS-232 interface.
A PC with a serial port can be
used at the other end of this connection, running a terminal emulator program such as HyperTerminal or TeraTerm (under Windows) or picocom (under Linux).
What the future holds
The serial console and shell
The console, which can be compared with
the command prompt in Windows, can
be used for entering commands, triggering actions (possibly on a remote machine)
and displaying results. In this way it provides a user interface to the system. Usually
it is used in conjunction with a shell, which
provides many handy extra features that
In the next installment in this
series we will look at how the
hardware (Figure1) is arranged. We will
look closely at the power supply, the microcontroller, the SDRAM device and the various interfaces. On the software side, we will
examine the boot process: thanks to the
pre-installed demonstration software (Figure6) the board is ready for experimentation straight away.
(120026)
Elektor Products and Services
Elektor Linux board, ready built and tested: # 120026-91
All products and downloads are available via the article support
Free software download
page: www.elektor.com/120026
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
Embedded Linux
made Easy(2)
Hardware
Choosing a suitable microcontroller and
supporting ICs is an essential step at the
beginning of many a project. In this part of our
course we will examine the circuit diagram of
our board and look more closely at the most
important components. We will then boot it for
the first time and study what happens. We shall
see how easy Linux can be!
By Benedikt Sauter[1]
In the first part of this series we looked at
the history of the free GNU/Linux operating
system and its most important elements.
We now want to look at our hardware in
detail: the Elektor Linux board.
The main components
The circuit has deliberately been kept simple: we have included only the most important components. The idea is that any
reader should be able to understand the
system from end to end: from applying
power to launching complex applications.
Running the GNU/Linux operating system
requires the standard components of a
computer architecture: a processor, RAM
(working memory) and ROM (non-volatile
memory): see Figure1. How do these all
work together on our board? When power
is applied a mini-bootloader (roughly the
equivalent of the BIOS in a PC) is started,
which copies the kernel from ROM into
RAM. The kernel is then started: this in turn
also has access to the ROM, from which
it can copy any application into RAM as
needed. We will now look at these components in more detail.
The processor
We start with the processor. It is located
in the center of the board, in a 12mm-by12mm BGA package. The Linux kernel
was originally developed for the x86 architecture, which is rather a different beast
from that of a typical embedded processor. Because of the open way in which the
programmers work and the well-thoughtout structure of the software, however, it
is possible to port the kernel to a range of
different processors. Essential to porting
Linux to a new architecture is the availability of the GNU GCC toolchain[2], which
was briefly described in the first article in
this series. The processor must have a hardware timer and a 32-bit architecture. An
MMU (memory management unit) is not
absolutely necessary, but it does help the
operating system to allow different applications to run stably and independently of
one another. Each application is assigned
its own virtual memory space and does not
have access to the memory of other applications. The project uclinux.org[3] provided
patches (see the text box) to the kernel to
allow use with a processor lacking an MMU.
Recently, however, these patches have been
integrated into the mainline kernel. In any
case, we will take advantage of the MMU in
our LPC3131 processor and will therefore
not need to use these patches.
The LPC3131[4] processor by NXP uses an
ARM9 core (to be precise, an ARM926 running the ARMv5 instruction set). It runs
at 180MHz and has 192Kbytes of internal SRAM; an integrated DRAM controller
allows external memory to be attached. All
the usual microcontroller interfaces, such
as I2C, SPI and a UART, are included. There
are 21 GPIO pins, four analog inputs, an LCD
interface, an I2S audio interface and a highspeed USB (480Mbit/s) interface, which
open up a wide range of application possibilities from within the Linux operating
system. The Elektor Linux board is only double-sided, and so not all the signals can be
Elektor products and services
Elektor Linux board, ready built and tested: 120026-91
Free software download
All products and downloads are available via the web page for this
article: www.elektor.com/120146
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX
Power supply
brought out: however, all of the important
ones are available.
The processor we have chosen is ideal for
an introduction to the world of Linux as it
includes only the absolutely essential facilities and peripherals, falling decidedly in the
low cost and low power categories. Other
ARM9-based microcontrollers might offer a
more comprehensive range of features, but
tend to be correspondingly harder to use.
Nevertheless, once the basics are understood, it is relatively easy to progress to
more complex devices.
USB
Processor
LPC3131
Serial
console
RAM
Nonvolatile
memory
ROM
Figure1. The main components of the Elektor Linux board.
Working memory
The processor itself includes just 192Kbytes
of RAM, which we will need to expand with
external memory to allow us to run the kernel, the other operating system components and applications. The internal RAM is
used only to run the bootloader program.
Once the bootloader has configured the
external memory appropriately (which
includes setting up the timing parameters
on the integrated memory controller and
initializing the memory contents), the internal RAM is only used for speed reasons, for
example as a cache for the operating system or for applications. The external memory takes the form of SDRAM (synchronous
dynamic memory): these are very low-cost
devices offering large storage capacities. A
quick glance at the LPC3131 datasheet[4]
suffices to identify find a suitable IC. We
also discover that the device supports a
16-bit external data bus and a maximum
address range of 128Mbytes. A special feature is support for so-called low-power
SDRAM devices, which are optimized for
current consumption. On our board we use
an 8Mbyte device; a 32Mbyte device can
be substituted if desired. Since the world of
RAMs is standardized by JEDEC[5], a suitable device can be obtained from a wide
choice of manufacturers. In the circuit we
have shown the AMIC A43E26161[6]: this
is a low-power device that needs a 1.8V
supply.
The hard drive
The hard drive of our board is a commonor-garden microSD card (Figure2), as used
these days in practically every digital camera and many mobile phones. The card
essentially consists of a NAND flash memory and a simple controller. Unlike NOR flash
memory, NAND flash is always addressed
by block and sector. Our standard microSD
card has a capacity of 1Gbyte, although
larger cards can of course be used. As well
as being readily available, microSD cards
have the advantage that no special programming equipment is needed for the
board: all programs and data files can simply be copied to the card using an ordinary
card reader connected to a PC.
for the LPC3131, 1.8V for the SDRAM, and
1.2V for the ARM9 core in the processor.
When power is applied the enable signals
of the regulators (pin1 in each case) are
immediately taken high and they immediately start to produce their output voltages.
Some of the more sophisticated ARM-based
processors have rather onerous power
sequencing requirements.
The circuit
We now turn to the circuit diagram (Figure3), starting with the power supply. One
possibility is to power the board using a
USB cable at connector X2. The same connection can be used to talk directly to the
serial console of the Linux system over USB,
the CP2102 (IC7) functioning as a USB-toserial converter.
If the system is being used without a console (for example if the UART interface is
being used for a different purpose) external power can be applied at connector X6.
This allows higher voltages to be applied, as
an MC7805ABDT linear regulator appears
between this source and the regulators for
the processor proper. We suggest using a
DC supply of between 7V and 12V. Jumper
J4 is used to select whether external power
or USB power is used.
The internal 5V rail is taken directly to the
inputs of switching regulators IC1 to IC3.
These have a maximum permissible input
voltage of just 6V. Resistors R4, R6 and R7
determine the output voltages of these
regulators. The voltages we need are 3.3V
Figure2. A microSD card
and an SD card adaptor.
With the board now powered up we can
turn our attention to the LPC3131 in the
circuit diagram. It is device IC6, split up in
the diagram into three parts labeled IC6.A
to IC6.C: splitting the pins of the device into
logical groups like this makes the diagram
easier to understand. Block IC6.A covers all
the important interfaces and I/Os of the
processor; block IC6.B includes the data and
address buses; and block IC6.C contains the
power supply pins.
9
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX
+1V2
C6
C4
+3V3
+3V3
L3
10k
C14
C16
C15
C12
C13
10u
100n
47n
3n3
10u
R13
4uH7
R5
EN
R6
AS1324
M12
M11
N14
F12
E14
G10
TP1 TP2
LPC_RXD
P12
LPC_TXD
N12
N13
TP5
P14
TP6
B9
PWM_DATA
LPC313XFET180
J10
L14
GPIO2
I2SRX_BCK0
GPIO3
I2SRX_WS0
GPIO4
SPI_CS_OUTO
MGPIO6
SPI_SCK
MGPIO7
SPI_MISO
MGPIO8
SPI_CS_IN
MGPIO9
SPI_MOSI
MGPIO10
GPIO12
MI2STX_BCK0
GPIO13
MI2STX_WS0
GPIO14
MI2STX_CLK0
GPIO15
I2STX_DATA1
GPIO16
I2STX_BCK1
GPIO17
I2STX_WS1
GPIO18
B6
MUART_RTS_N
ADC10B-GPA1
PWM_DATA
A10
Q2
C30
10k
LPC_MCI_CMD
A5
LPC_MCI_DAT0
B5
LPC_MCI_DAT1
C5
LPC_MCI_DAT2
J14
GPIO14
J13
GPIO15
GPIO2
GPIO18
H11
GPIO19
SV1
GPA0
A14
GPA1
B13
C14
RESET
S3
C11
100n
10k
A1
LPC_D11
C2
LPC_D12
G3
LPC_D13
D3
LPC_D14
E3
LPC_D15
F3
LPC_DQM0
H1
LPC_WE
J2
J1
J3
K1
K2
E6
E7
D4
MLCD_DB_6
IC6.B
EBI_D_7
MLCD_DB_8
EBI_D_9
MLCD_DB_9
EBI_D_10
MLCD_DB_10
EBI_D_11
MLCD_DB_11
EBI_D_12
MLCD_DB_12
EBI_D_13
MLCD_DB_13
EBI_D_14
MLCD_DB_14
EBI_D_15
MLCD_DB_15
LPC313XFET180
EBI_DQM_0_NOE
EBI_A_0_ALE
EBI_A_1_CLE
EBI_NWE
EBI_NCAS_BLOUT_0
NAND_NCS_0 EBI_NRAS_BLOUT_1
NAND_NCS_1
NAND_NCS_2
MLCD_RS
NAND_NCS_3
MLCD_RW_WR
MNAND_RYBN0
MLCD_E_RD
MNAND_RYBN1
MLCD_CSB
N6
LPC_A2
P6
LPC_A3
N7
LPC_A4
P7
LPC_A5
K6
LPC_A6
P5
LPC_A7
N5
LPC_A8
L5
LPC_A9
K7
LPC_A10
N4
LPC_A11
K5
LPC_A12
P4
LPC_A13
P3
LPC_A14
N3
LPC_A15
B3
LPC_A0
A2
LPC_A1
G1
LPC_CAS
H2
LPC_RAS
P8
DAT3
CMD
V+
CLK
GND
DAT0
DAT1
SW_A
SW_B
SD-CardSocket
3 GPIO14
10k 10k 10k 10k 10k 10k
L8
C18
10u
L7
C19
100n
L12
10n
C12
C6
+1V2
10u
301k
C33
10u
100n
F5
10n
G5
H5
+1V8
10u
M7
C25
100n
M9
10n
+3V3
D7
C23
K8
C22
10u
E8
C17
100n
G12
10n
L10
MNAND_RYBN2
MNAND_RYBN3
VSSA12
LPC313XFET180
VDDE_IOA
VSSE_IOA
VDDE_IOA
VSSE_IOA
VDDE_IOA
VSSE_IOA
VDDE_IOA
VSSE_IOA
VDDE_IOA
VSSE_IOA
VDDE_IOB
VSSE_IOB
VDDE_IOB
VSSE_IOB
VDDE_IOB
VSSE_IOB
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VSSE_IOC
K11
+3V3
IC6.C
VDDE_IOC
D5
LPC_CKE
VSSI
VSSE_IOB
C13
LPC_CS
VDDI
VDDE_IOB
M5
C26
VSSI
VSSE_IOA
L4
C27
VSSI
VDDI
VDDA12
E5
C31
VDDI
VDDA12
E10
B2
C32
VSSI
VPP_B
D11
100n
VSSI
VDDI
VPP_A
C9
C38
+1V8
VDDI
VSSI
A9
R19
VDDE_ESD
A13
VSSE_IOC
ADC10B_GNDA
ADC10B_VDDA33
LPC_MCI_DAT3
LPC_MCI_CMD
4
LPC_MCI_CLK
LPC_A0
H7
LPC_A1
H8
LPC_A2
J8
LPC_A3
J7
LPC_A4
J3
LPC_A5
J2
LPC_A6
H3
LPC_A7
H2
LPC_A8
H1
LPC_A9
G3
LPC_A10
H9
LPC_A11
G2
LPC_A15
G1
LPC_A14
G7
LPC_A13
G8
A0
A1
A2
DQ0
DQ1
DQ2
A3
DQ3
IC5
A4
DQ4
A5
DQ5
A6
DQ6
DRAM
A7
A8
DQ7
DQ8
A43E26161
A9
DQ9
A10
DQ10
A11
DQ11
A12
DQ12
BA0
DQ13
BA1
DQ14
DQ15
LPC_DQM0 E8
LPC_DQM1 F1
LPC_CLK
F2
LPC_CKE
F3
LDQM
CAS
UDQM
RAS1
CLK
CKE
+5V
WE
CS
VCC
LPC_MCI_DAT0
LPC_MCI_DAT1
LPC_MCI_CD
D+
GND
USB
J3
R9
GPA0
GPA3
4 PWM_DATA
A8
LPC_D0
I2C_SCL
I2C_SDA
B9
LPC_A1
SPI_MOSI
SPI_MISO
B8
LPC_D2
SYSCLK_O 9
10
SPI_SCK
C9
LPC_D3
GPIO14
11
12
GPIO11
C8
LPC_D4
13
14
D9
LPC_D5
D8
LPC_D6
E9
LPC_D7
D12
G4
L6
L11
E9
C3
C4
E4
F4
H4
K3
M3
M4
M6
M8
B12
D6
D8
D9
G11
L9
L13
A12
E1
LPC_D8
D2
LPC_D9
D1
LPC_D10
C2
LPC_D11
C1
LPC_D12
B2
LPC_D13
B1
LPC_D14
A2
LPC_D15
F7
LPC_CAS
F8
LPC_RAS
F9
LPC_WE
G9
LPC_CS
+5V
D
D+
GND
47u
4
5
12
11
MINI-USB
REGIN
VBUS
VDD
RI
RST
D+
LPC_VBUS
LED5 270R
13
RTS
SUSPEND
CTS
GPIO18
LPC_DP
USB_ID
R10
R14
C10
R18
JP1
1M
1M
100n
2
1
28
27
26 LPC_RXD
25 LPC_TXD
24
23
TP7
GND
3
Relay
BSS123
MC78M05ABDT
IC8
X6
+5V
GND
10k
270R
Q4
LPC_DM
TXD
SUSPEND
G6D-1A-ASI5VDC
270R
X1
K4
BAT54
DSR
RXD
EXP
11
DTR
CD2102
GND
D4
DCD
IC7
D-
+5V
1k
R24
1u
+5V
C24
X2
+5V
LED1
R27
C28
J4
+3V3
LED2
+5VEXT
+3V3
GPA1
R22
K1
D
A11
C7
+3V3
LPC_MCI_DAT2
C21
N9 LPC_DQM1
U1
DM3D-SF
DAT2
C2
GND
H3
LPC_CLK
P9
MLCD_DB_7
EBI_D_8
R29
LPC_D10
GPA1
4 GPIO15
R25
2 GPIO11
+3V3
R23
X4
GND
R30
R20
10k
GPA3
10k
3.3V
R4
J5
B14
R17
R16
22p
301k
AS1324
10R
K14
+3V3
X7
R21
GPIO0
R15
VOUT/
VFB
SW
10u
150k
C7
VIN
IC2
4uH7
C3
K13
ADC10B-GPA3
+3V3
R7
K12
RESET
+3V3
301k
J11
ADC10B-GPA2
22p
A3
LPC_MC1_CD B4
J12
C29
22p
LPC_D9
GPIO11
12MHz
S1
B1
LPC_MCI_DAT3
H10
UART_TXD
ADC10B-GPA0
A6
A4
GPIO20
MUART_CTS_N
LPC_MCI_CLK
H13
GPIO19
UART_RXD
LED1
C11
GPIO11
MI2STX_DATA0
GPIO2
B11
MGPIO5
R26
GPIO15
L1
M2
GPIO1
+3V3
22p
R3
EN
LED1
M13
I2SRX_WS1
GPIO0
C1
LPC_D8
MLCD_DB_5
EBI_D_6
N8
J9
B7
GPIO0
K10
LPC_D7
EBI_D_5
E7
SPI_MOSI
I2SRX_BCK1
I2SRX_DATA0
J1
B8
I2SRX_DATA1
D2
D3
C8
SYSCLK_O
10k
SYSCLK_O
LPC_D6
MLCD_DB_4
VDDE-IOC
SPI_MISO
TP4
G13
D1
EBI_D_4
VSSE-IOC
A8
J4
LPC_D5
MLCD_DB_3
C7
A7
SPI_SCK
CLOCK_OUT
H12
E2
MLCD_DB_2
EBI_D_3
A1
TP3
I2C_SCL1
D14
R2
LPC_D4
EBI_D_2
VDDE-IOC
P11
CLK_256FS_O
+3V3
E1
VSSE-IOC
N10
10k
D13
F1
LPC_D3
MLCD_DB_1
E3
M10
R1
BUF_TMS
I2C_SDA1
TDO
F11
BUF_TCK
I2C_SCL0
E11
F10
SCAN_TDO
BUF_TRST_N
I2C_SDA0
TCK
LPC_D2
MLCD_DB_0
B3
F13
AS1324
2
EBI_D_1
VDDE-IOC
+3V3
IC6.A
USB_GNDA
VOUT/
VFB
L2
GND
EBI_D_0
VSSE-IOC
F14
ARM_TDO
USB_VSSA_TERM
M14
F2
D7
G14
TCK
USB_VSSA_REF
TDI
TMS
G2
LPC_D1
C3
E13
TMS
K9
P10
LPC_D0
A9
E12
I2C_SCL
TDI
TRST_N
A7
D10
I2C_SDA
TRST_N
USB_RREF
RESET
P13
VDDE-IOC
N1
C10
USB_ID
H14
VDDE-IOC
L3
RSTIN_N
VSSE-IOC
12k
1%
SW
10u
66k5
VSSE-IOC
K4
USB_DP
FFAST_OUT
J5
R11
USB_DM
C1
GND
IC1
C9
VIN
N11
JTAGSEL
B10
M1
4uH7
R8
+1V2
4u7
+1V2
USB_VDDA12-PLL
P1
P2
USB_ID
USB_VDDA33-DRV
N2
LPC_DP
USB_VDDA33
LPC_DM
USB_VBUS
FFAST_IN
L2
22p
301k
VOUT/
VFB
LPC_VBUS
L1
EN
IC3
SW
DNP
4u7
C8
+5V
C5
+1V8
VIN
VDDE-IOC
220p
VSSE-IOC
1n
R12
+3V3
B7
10u
4u7
VDDE-IOC
C36
VSSE-IOC
C35
A3
C37
+3V3
1
2
DC 7 - 12V
120146 - 13
Figure3. Circuit diagram of the Elektor Linux board.
10
Personal Download for Fernando Faria Corra | copyright Elektor
C20
47u
+5VEXT
EMBEDDED LINUX
For example, it is possible to boot from the
SD card or over the USB-to-serial bridge at
X2 mentioned above. A further possibility is
to boot over the second USB interface at X1
(see below), for which a DFU (device firmware update) programmer is needed.
LED1 lights when power is present on the
board; also, for simple experiments or as a
status indicator we have LED2. Button S1
can be used as an input device: its state can
be polled on GPIO15. We have also provided
a relay to control external equipment via X1.
We will look in more detail at this possibility
later in the series.
The overall current consumption of the
board is around 85mA to 100mA, which
corresponds to a power consumption of
around half a watt.
Interfaces
Things really start to get interesting when
we use Linux to access directly the various
microcontroller-style interfaces provided by
the processor, including digital inputs and
outputs, PWM, I2C and SPI. The relevant
pins are brought out to 14-way header J5
and connector X4.
GPIO
The 3.3V-compatible inputs and outputs
GPIO11, GPIO14 and GPIO15 are available on screw terminals at X4. GPIO14 and
GPIO11 are simultaneously available on the
14-way header J5.
A/D channels
Three of the four channels are made available for simple analog measurements. The
3.3V supply is used as a reference voltage.
GPA0, GPA1 and GPA3 are available on J5,
with GPA1 also being available on a screw
terminal.
SPI
SPI peripherals can be controlled in exactly
the same way as I2C devices. The MOSI,
MISO and SCK signals are available on J5,
while the chip select signal OUT0 and (in
the case where the LPC3131 is operating as
an SPI slave) the CS_IN signal are available
on test points TP3 and TP4.
PWM
A PWM output is ideal for driving a servo
or for generating an analog voltage. The
LPC3131 has a hardware PWM output, and
the corresponding pin is brought out to J5.
UART
The UART protocol is a particularly convenient way to implement simple communications, especially between two microcontrollers. Unfortunately the processor has
only one UART, which in the normal configuration is used for the root console. If we
subsequently add a network interface (see
below) then it is possible to run the root
console over this interface as well, freeing
up the UART for other applications. The RX
and TX signals are available at test points
TP1 and TP2.
USB
The USB interface at K1 opens up a wide
range of expansion possibilities for the
Elektor Linux board. Not only do there
already exist Linux drivers for a wide range
of USB devices (including audio and video
interfaces, 3G modems, wireless LAN, wired
LAN and so on), but also many simple 8-bit
microcontrollers can these days be controlled over USB. A semi-autonomous controller or coprocessor of this kind makes an
ideal extension to the Linux system.
Network
It is also possible to implement a network
connection, either to a wired LAN or a wireless LAN, using USB connector K1. This
UART
6
DFU
6
J2:Boot
SD/MMC
J2:Boot
I2C
The LPC3131 can act as an I2C bus master or
bus slave. In our case the most likely option
is master: this gives us an easy way to control external devices, such as a PCA9555
I/O expander). The SDA and SCL signals are
available on J5.
J2:Boot
In the bottom left-hand corner of the circuit
diagram is U1, the SD card socket. Normally
this will hold an SD card from which the
Linux system firmware will be loaded. The
LPC3131 is in fact capable of booting from
a range of storage devices and interfaces.
The options available on the Elektor Linux
board can be chosen from using header SV1
(see Figure4).
6
1
Figure4. Bootloader jumper.
interface can be operated in host mode
or in device mode (USB OTG, or on-thego). In host mode any USB stick LAN or
WLAN adaptor can simply be plugged into
the socket. In device mode the Linux board
plays the role of a USB device, for example
behaving as a virtual USB network interface.
We will of course describe later how this all
works.
Das Boot
Before we boot the board for the first time
let us take a quick look at what happens
during the boot process. In order to see
what is happening on the board, we need
to connect it to a computer over USB and
use a serial terminal program. Under Windows suitable options are Tera Term and
HyperTerminal; if a Linux PC is available,
you can use picocom, microcom or a similar program.
Using a standard Ubuntu system, at the
Linux PC console type
sudo apt-get install picocom
and then
picocom -b 115200 /dev/ttyUSB0
to connect to the Elektor Linux board.
Under Windows things are (of course) different. The VCP driver for the USB-to-serial
converter has to be installed manually[7].
Using Tera Term[8] or HyperTerminal it is
then possible to select the newly-created
COM port. The settings are shown in the
screenshot in Figure5.
Once the correct interface is selected the
output from the bootloader and from the
kernel should be visible in the terminal program on the PC.
11
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX
If the boot process is already complete, simply press the reset button (RST) to repeat
the process. Figure6 illustrates what happens during booting.
Figure5. Tera Term terminal program.
Boot loader
is copied from SD card
into SRAM
from LPC3131
Power
ON
KernelExtract
Figure7. The APEX bootloader.
Boot loader
initialises
SDRAM
Boot loader
copies kernel image
into SDRAM
RAM
Init
Bootloader
Optional
checksum
extraction
KernelBoot
Kernel
boots
Initialisation
of HW
Boot loader
invokes
kernel
Jump
Kernel
Kernel
ready
Mount
root file
system
Invoke
Kernel
ready
/sbin/init
Optional:
invoke
root console
A power-on reset interrupt is triggered.
The internal bootloader in the LPC3131
looks for the bootloader firmware and
copies it into internal RAM. Where the
bootloader looks depends on the setting
of the bootloader jumper: in our case we
arrange for it to look on the SD card.
The firmware that has been loaded into
the internal RAM is launched.
The firmware (the APEX bootloader)
initializes the external SDRAM and copies the kernel from the SD card into the
SDRAM (see Figure7).
The firmware calls the kernel.
The kernel unpacks itself and then automatically starts up (Figure8).
The kernel initializes the hardware
(including the UART for the root
console).
During the boot process the kernel
mounts the root file system (which is
stored on the SD card).
The kernel launches the first process, /
sbin/init.
The kernel starts the root console on the
UART interface.
Shell
Figure6. The boot process.
The system now waits at the login prompt
(Figure9) for input. You can log in as the
root user: simply type root and press the
Enter key.
You can now try some simple experiments
using the commands that we have listed in
Table1. For example, you can create a test
file and edit it. It is equally easy to create a
new directory. With a little practice using
the file system will become second nature.
As a very simple taste of what is possible,
we will show how to switch the red LED on
the board on and off. To do this we exploit
a fundamental principle of Unix operating
systems: everything is a file! Every device is
represented in the file system as a file, which
can be read from and written to. This even
applies to our LED!
First enter the following commands.
Figure8. Messages from the Linux kernel
during boot.
Figure9. The login prompt.
cd /sys/class/gpio
12
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX
Source code terminology
Patch
Mainline
A patch is a file that can be used to make
changes to original source code with the
help of a suitable utility program. If, for
example, you make an extension to a program, you can use a utility to determine the
differences between your new version and
the original and generate a patch file to represent them.
Members of a development team can also
use patches to help ensure that they are all
working with the same version of the source
code. Patch files form the backbone of open
source development, where all large-scale
projects are worked on simultaneously by
many developers.
The development of large-scale open source
projects is organized in various ways. In general there is a single maintainer who looks
after the source code, applies patches from
other developers, and regularly releases
new versions of the software to users. In the
case of the Linux kernel there is the so-called
mainline, which is maintained by Linus Torvalds and his team.
Since there is an enormous number of
patches being offered to extend the kernel it
often takes some time before a new feature
will appear in the mainline. Before this point
it is possible to obtain patch files directly
from their developers (for example via their
echo 3 > export
home pages) and apply the patches to the
kernel yourself. The aim, however, is to get
everything into the mainline.
Maintainer
The maintainer is usually a single person
(often the founder of the project). He or she
looks after the master version of the source
code, applies patches, and regularly releases
new versions of the source code. In the case
of Linux the various subsystems such as
network, drivers, file system and so on each
have their own maintainer, who is responsible for the source code and who helps ensure its stability and availability.
easy as we can for readers by providing a
ready-made image for download, which can
be used in a virtual machine.
cd gpio3
(120146)
echo out > direction
These configure the relevant port pin as an
output. Then enter
echo 1 > value
[3] www.uclinux.org
[4] http://ics.nxp.com/products/lpc3000/
datasheet/lpc3130.lpc3131.pdf
[5] http://www.jedec.org
[6] www.amictechnology.com/pdf/
A43E26161.pdf
Internet Links
[1] sauter@embedded-projects.net
[7] www.silabs.com/products/mcu/Pages/
USBtoUARTBridgeVCPDrivers.aspx
[2] http://gcc.gnu.org
[8] http://ttssh2.sourceforge.jp
to switch the LED on, and
Table1: Important Linux commands
echo 0 > value
to switch it off again. See how simple Linux
can be?
Before we finish, we should explain how to
power the board down safely (like a PC). As
Table1 shows, the relevant command is
called halt. As soon as the system replies
with the message System halted.
power can be removed.
What the future holds
In the next article in this series we will
download some source code for the first
time and write a small program. A somewhat longer part of the course will cover the
installation of a development environment.
Here too, however, we will make things as
Command
Description
ps ax
Display all processes
free
Show memory use
date
Show current time and date
touch test.xt
Create empty file called test.txt
rm test.txt
Delete file test.txt
nano test.txt
Open file test.txt in an editor (use control-O to write the
file, control-X to leave the editor)
df
Show partitions
mkdir test
Create a directory called test
cd test
Descend into the directory called test
cd ..
Move one level up in the directory structure
rmdir test
Delete the directory test
cat /proc/cpuinfo
Display the contents of the file /proc/cpuinfo
halt
Bring Linux system to an orderly halt
13
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
Embedded Linux
Made Easy(3)
Software development
By Benedikt Sauter[1]
It takes the right software to bring a
microcontroller to life. Beyond the usual
firmware, in an embedded GNU/Linux system
we have to deal with building the components of the
operating system. In this article we show how it all works,
and even write our first program inC!
It is easiest to develop for an embedded Linux system with the help
shown in Figure10.
of a conventional Linux system, normally running on a PC. We will
An alternative (and potentially more
base our experiments on version 12.04 of the Ubuntu[2] distribuconvenient) approach is to run the operattion. What do we need to install such a system? Not a lot: a little free
ing system in a virtual machine. The author has prepared an image
space on the hard disk and, ideally, a network connection.
of a Linux computer set up for development especially for Elektor
The first thing to do is download the image of the installation CD
readers, and it can be downloaded from the Elektor website[3]. The
from the internet[2]. We can use either the 32-bit or the 64-bit
virtualization program VirtualBox is needed to run the image: it
desktop variants: if in doubt, select the 32-bit variant.
can be downloaded free of charge at[4]. When VirtualBox has been
Once the CD image is downloaded it has to be burned onto a CD
installed, the image is loaded by simply selecting File> Import
using a suitable program. It is important to burn the file to the disk
Appliance from the main menu; it can then be run immediately.
as an image rather than copying it as a simple file.
Now insert your newly-burned CD into the PC and boot from it
Toolchain on CD
(which may require some adjustments to your BIOS settings).
With the new operating system running the next step is to install
Ubuntu starts up with the splash screen shown in Figure1. To
the toolchain. If you are using the VirtualBox image this has all
make the system more convenient to use will we install it to the
already been done for you, and you can skip the next two sections.
hard disk, by selecting the second menu option (see Figure2). You
will now be guided step-by-step through the installation process.
The quickest way to do things in Linux is usually to use the console.
First choose your language (Figure3). We do not need to install
Simply open up a new terminal window on the PC by pressing Conany third-party software (Figure4). The
next window, shown in Figure5, lets you
choose to let Linux occupy the whole hard
disk (which would be suitable for a machine
that does not already have another operating system installed); alternatively, you can
install Linux to a second hard disk, or partition the main hard disk into two areas, one
area retaining the already-installed operating system and the other dedicated to the
new installation. In Figure6 you select the
drive or partition that will be used, and then
proceed as shown in Figure7 and Figure8.
We will explain more about the password
Figure1. Use down-arrow to select the
Figure2. Select Install Ubuntu
that you are asked to set (Figure9) later.
second menu item.
and press Enter.
If all goes well, you should reach the point
14
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX MADE EASY
Figure3. Choosing your language.
Figure4. Preparing for installation.
Figure5. Choosing where the operating
system will be installed.
trol-Alt-T. In the terminal, switch to the directory /tmp
cd /tmp
and then download the ARM toolchain CD directly using the wget command thus:
wget ftp://ftp.denx.de/pub/eldk/5.0/iso/armv5te-qte-5.0.iso
This will download a CD image. The image can be opened directly using Linux: there is no
need actually to burn the file to a CD. However, the file does need to be mounted, which
makes it visible as a set of files to the operating system. First switch to the directory /
media:
Figure6. Selecting the hard disk.
cd /media
In theory you can mount the contents of the CD image wherever you like in the file system,
but there are certain conventions in the Linux world that make it easier for people to find
their way around a new system. We will look in more detail later at the standard arrangement of the file system.
We now want to create a new empty directory called eldk-iso, where we will subsequently
mount the CD image. Although this might seem odd to someone familiar with Windows,
we are not going to copy the files from the CD image to the new directory: instead, the new
directory just marks the place in the file system from which the contents of the CD are made
accessible. In Linux, everything is handled through files and directories.
Here is the command to create the new directory:
sudo mkdir eldk-iso
Figure7. Selecting the time zone.
The machine will prompt you for a password, as creating the directory requires you
Figure8. Selecting the keyboard layout.
Figure9. Entering a user name and
password.
Figure10. The installation process begins.
15
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
Permissions and privileges
A Linux system always has a user called
root. This user has the highest level of
privileges on the system: other users typically only have ordinary privilege levels.
Full access to system files, devices and so
on requires root privileges. One approach
is simply to execute all commands as the
user root but this is not advisable. One of
the reasons Linux is so secure is that the
restrictions on what ordinary users can do
prevent a lot of potential damage. Users are
normally only given permission to run the
programs they need, not to access system
files or other important information.
The Linux programmer can, however, grant
any application a higher level of privileges as
needed, and so there is no need to execute
all commands as the user root. We will
also create a new user account on our Linux
board for carrying out ordinary tasks and
running the programs we write.
If a user briefly requires root privileges,
for example to create a directory within a
system directory, then we can use the command sudo provided in most modern Linux
distributions (including the version of Ubun-
(briefly) to have root privileges: see the text box Permissions and
privileges. To make this happen, we have prefixed the normal command with sudo, which asks the user for the password that was
configured when the system was installed. If you are using the VirtualBox image, the password is elektor, entirely in lower-case.
tu we are using) as a prefix to the command
proper. This indicates that the command is
to be run as if by the root user.
You will be prompted for a password. If you
have installed the Linux system on the PC
yourself, you will have set up this password
as part of the installation process; if you are
using the VirtualBox image, the password is
elektor, entirely in lower-case.
This step is important, as the next thing we will do is unmount the
CD image. This will not work if you are currently in a directory within
the image: the operating system will refuse to execute the command and an error message will be printed.
sudo umount /media/eldk-iso
The CD image can now be mounted in the file system to allow us to
access its contents:
sudo mount -o loop /tmp/armv5te-qte-5.0.iso /media/
eldk-iso
From our current position in the file system we can change directory
into the CD image by typing:
cd eldk-iso
Installing the toolchain
In the directory you will find a small script that you can use to
install the toolchain. Again, you need root privileges to install new
programs:
The directory you created for the toolchain CD can now be deleted.
sudo rmdir eldk-iso/
When installation is complete the toolchain will be located in the
directory /opt/eldk-5.0/. So that we can access the programs in
the toolchain using the command line from whichever directory
you happen to be in, we have to add this directory to the PATH variable. This is a Linux environment variable which contains a list of
directories in which the system will automatically look for programs
to execute.
The best approach is to write a small script file (call it set.ch) which
you can run from the console before using the toolchain. Create a
new file using the editor:
gedit set.sh
sudo ./install.sh -s -i qte armv5te
and add the following lines to it:
The following message should appear:
#!/bin/bash
*** Installing ./targets/armv5te/eldk-eglibc-i686arm-toolchain-qte-5.0.tar.bz2
When installation is complete you can leave the CD directory:
P1=/opt/eldk-5.0/armv5te/sysroots/i686-oesdk-linux/
usr/bin/armv5te-linux-gnueabi/
P2=/opt/eldk-5.0/armv5te/sysroots/i686-oesdk-linux/
bin/armv5te-linux-gnueabi/
cd ..
16
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX MADE EASY
export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabiexport PATH=$P1:$P2:$PATH
Save it and leave the editor. Now we are back in the console. To
compile the program, type:
arm-linux-gnueabi-gcc -o hello hello.c
The last command here adds the path mentioned above to the PATH
variable.
Then write the new file to your start-up directory (called the home
directory). For the commands in the file to take effect, you have
to cause the shell (the Linux command line interpreter) to read
them in (or source them). One way to do this is with the following command:
. ./set.sh
Type this carefully: the line starts full stop, space, full stop!
If you would prefer not to have to type this command every time
you bring up a new console you can include it in the file .bashrc,
which is automatically executed whenever the shell starts up. The
file is located in your home directory, which you can switch to using
the command cd without any arguments. You can edit the file using
the command:
gedit .bashrc
To test whether the above process has been successful, we can copy
the file hello that the compiler has created to the Elektor Linux
boards SD card. Make sure the board is off and remove the card.
Insert it into the PCs card reader, and plug the reader into the PC.
Wait a few seconds for the machine to detect the card. Normally
Ubuntu will automatically pop up a window when this happens:
since we will be copying to the card using the console, we can close
this window.
Full-scale operating systems such as Ubuntu automatically mount
external storage devices when they are plugged in. We therefore
need to find where Ubuntu has decided to mount the SD card. It is
easiest to switch to the directory /media:
cd /media
and then type
ls
The compiler in action
The toolchain programs that we will use to build the Linux kernel and the bootloader all have names that begin with armv5te-.
For example, the GCC compiler is called armv5te-gcc. Typing the
command
armv5te-gcc --version
will give the version number of the compiler (note that there are
two dashes before version). If this command works, it means that
you have successfully set up the path to the toolchain programs.
To compile application programs for Linux we need to use the toolchain commands that start arm-linux-gnueabi- rather than armv5te-. So how do we compile a simple hello world program?
(for list) to display a list of files and directories within this directory.
If there is more than one subdirectory, you can type
cd directory-name
to switch to a given directory, look inside using ls, and then cd ..
to move back up one directory level into /media. The name of the
directory where the SD card is mounted will typically consist of a
long string of digits. The directory will contain the complete file
system of the Linux board, including files with names such as zImage and swapfile1.
If you get lost in the file system, you can always return to your home
directory using the command cd without any arguments. Another
handy command is pwd, which will tell you the path to your current directory.
First create a source file
Hello world!
gedit hello.c
To test the hello world program, switch to the directory where the
SD card is mounted and copy the file across:
with contents as follows:
#include <stdio.h>
cp ~/hello ./
int main(void)
{
printf(Hello world!\r\n);
return 0;
}
Then we have to unmount the directory manually so that the operating system is forced to finish writing all the data to the card.
cd
sudo umount /media/directory-name
17
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
First aid for a sick SD card
If a system that has been booted from an SD card is not powered
down properly, using the halt or poweroff commands, it is possible that the file system on the card will be corrupted. This typically
results in EXT2-fs errors:
Filesystem EXT2-fs (mmcblk0p1): error: ext2_lookup:
deleted inode referenced: 694962:
Fortunately we can usually rescue the file system using a Linux PC from the console.
First put the SD card into the card reader and use dmesg to determine what name it has been assigned. For example, if you see the following
[
[
[
[
[
1549.424156]
1549.425624]
1549.427527]
1549.427533]
1549.730223]
sd 7:0:0:2: [sdh] Assuming drive cache: write through
sdh: sdh1 sdh2
sd 7:0:0:2: [sdh] Assuming drive cache: write through
sd 7:0:0:2: [sdh] Attached SCSI removable disk
EXT2-fs (sdh1): warning: mounting unchecked fs, running e2fsck is recommended
it means that the first partition, which is the one we are interested in, has been given the name sdh1. This contains a file system in ext2 format, which it is possible to repair. We have to unmount the file system
umount /dev/sdh1
before we can use the tool e2fsck (or equivalently fsck.ext2) to attempt the repair:
sudo e2fsck /dev/sdh1
The result should be as shown in the screenshot. From time to time the program will ask if certain actions should be carried out: you should
normally answer y. The result should be an error-free SD card!
Here again directory-name should be replaced by the name of the
directory that the operating system chose when the SD card was
mounted.
We can now move the SD card back to the Linux board and start it
up. Connect to the board using a terminal emulator on the PC, as
described in the previous installment in this series[5].
The result should be that Hello World! appears in the terminal window (see Figure11).
If the system is booted from the SD card, it is important to shut the
system down in an orderly fashion when you have finished. For this
we need the command
halt
Now, on the board, we switch to the top-level directory in the file
system using the terminal emulator:
cd /
and run the program:
It is then necessary to wait until the message System halted
appears before it is safe to remove power: otherwise it is possible
that not all files will be updated properly on the SD card. This can in
turn result in EXT2-fs errors. It is normally possible to recover the
situation using a Linux PC: see the text box.
./hello
18
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX MADE EASY
Bootloader and kernel
We shall now look at how we can build the two main components
of the operating system, the bootloader and the kernel.
The source code we need (290MB for the current version) is available from the Elektor website[3]. The simplest approach is to download the files using a browser: on a Linux machine the files will normally end up in the Downloads directory.
Figure11. Hello world running on the board.
When the download is complete, switch to the directory Downloads and find the file called 120026-11.zip. Move the file into
your home directory using the command
mv ~/Downloads/120026-11.zip ~/
To unpack the file, switch back to your home directory using cd and
enter the command
unzip 120026-11.zip
mv apex-1.6.8 work_1.6.8
cd work_1.6.8
In the source code tree we need to apply a couple of patches that are
required to make the bootloader work with the Elektor Linux board.
patch -p1 < ../apex-1.6.8_lpc313x.patch
patch -p1 < ../gnublin-apex-1.6.8.patch
Figure12 shows what you should see on the console.
Building the bootloader
The bootloader is a program which is copied from the SD card to the
internal SRAM of the LPC3131 on system reset (assuming the jumpers are set correctly: see [5]). The following sequence of commands
shows how we can compile the bootloader for ourselves and copy it
to the SD card, to allow us to boot from the card. Before starting we
need to install a couple of packages on the Ubuntu system:
sudo apt-get install patch libncurses5-dev
The build process for the bootloader is controlled by what is called
a configuration file. We have to copy the master version of this file
into our directory, with the new name .config. Note that here the
full stop before the word config is very important: it marks the file
as hidden to the operating system.
cp ../gnublin-apex-1.6.8.config .config
At this point, if you installed the toolchain manually and did not
arrange for the environment variables to be set in your .bashrc file,
you will need to set them by running the script . ~/set.sh .
Now switch to the source code directory
The build process can now be initiated:
cd
ElektorLinuxBoardDownload_20120509
make apex.bin
unpack the tar file that contains the bootloader
tar xvzf bootloader.tar.gz
then switch to the new bootloader directory
cd bootloader
and unpack the source code proper:
tar xvzf apex-1.6.8.tar.gz
Now comes an important step if we want to be able to distribute any
changes we make to the bootloader. We create a so-called working copy of the source code and make changes only on that copy.
Later it will be easy to create and publish a patch that represents
the changes we have made.
Normally at this point we would need to copy the bootloader firmware into flash memory on the microcontroller using a suitable programmer. Here, however, we can copy the firmware to the SD card
using ordinary Linux commands.
With the SD card once again in the PCs card reader, run the
command
dmesg
to see the messages output by the kernel running on the PC. The
results obtained on the authors PC are shown in Figure13. As you
can see, the SD card has been recognized as /dev/sdh and contains
two partitions, called /dev/sdh1 and /dev/sdh2. For safety it is
best to unmount these partitions, as we will be copying the bootloader across by accessing the blocks on the SD card directly rather
than via a file system:
19
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
sudo umount /dev/sdh1
sudo umount /dev/sdh2
The following command (where the sdh2 will need to be modified
to reflect the results from the dmesg command above) will copy the
file apex.bin to the SD card in such a way that the LPC3131 can find
it when booting:
sudo dd if=src/arch-arm/rom/apex.bin of=/dev/sdh2
bs=512
The command copies the bootloader code to the beginning of the
second partition of /dev/sdh using a block size of 512bytes. The
Linux operating system does not guarantee exactly when the new
blocks of data are actually written to the NAND storage on the SD
card. The command
dure is much the same as for the bootloader above, with the appropriate changes. Dont forget to unmount the partition!
A look at the possibilities
Now we are in the happy position of being able to rebuild the bootloader and kernel at will, we can look at the possibilities for making
modifications to them, in particular to the kernel. Of course, we only
have space here to look at this in general terms. You can take a first
look at the Linux kernel configuration by typing
make menuconfig
from the directory linux-2.6.33-lpc3131x. The result is shown in
Figure14. If, for example, you want to use a particular USB device
with the Linux board, you have to enable to corresponding driver
here.
sync
is therefore needed to force the operating system to ensure that all
pending blocks are written out and that the new bootloader code
is safely stored on the SD card. We can now try to boot the Linux
board from the SD card.
Restoring the boot image
Building the kernel
Building the kernel is a similar process to building the bootloader.
First we switch to the home directory using the command cd, and
then into the source code directory.
cd
You can navigate around the blue window using the arrow keys.
The Enter key opens and closes the menu. With a bit of hunting
around you will be able to find drivers for various devices you recognize. In the next article in this series we will go into this subject
in more detail.
ElektorLinuxBoardDownload_20120509
We unpack the kernel source code
Since we are now beginning to get down to the nitty-gritty of how
the Elektor Linux board works, it is a good idea to make an exact
copy of the SD card for backup purposes.
Put the card in the PCs card reader and then check, using dmesg,
what device name the operating system has chosen for it.
Again, for safety, unmount the partitions:
umount /dev/sdletter1
umount /dev/sdletter2
tar xvzf linux-2.6.33-lpc313x-gnublin-032012.tar.gz
switch to the kernel source directory
(where you should insert the appropriate character for letter, for
example giving /dev/sdb1 and /dev/sdb2 or /dev/sdh1 and /
dev/sdh2).
cd linux-2.6.33-lpc3131x
You can now take an exact copy of the cards contents:
and start the build process that will result in a bootable kernel.
make zImage
We also need to compile the loadable kernel modules. The is simply
done using the command:
sudo dd if=/dev/sdletter of=Image_SD_card_backup_
copy.img
This will take some time. You should now find a file in your current
directory whose size is exactly equal to the capacity of the SD card:
make modules
ls -lh
With everything built we need to copy the kernel and the modules
to the SD card. In our case the kernel zImage happens to be there
already, but it is worth practising the process for replacing the kernel. We need to copy the file arch/arm/boot/zImage in the kernel
source tree directly to the first partition on the SD card: the proce-
You now need to put another SD card, exactly the same size as the
original, in the PCs card reader. Again, find out the device name
using dmesg. The following command will copy the card image you
have just created onto the new card:
20
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX MADE EASY
sudo dd if=Image_SD_card_backup_copy.img of=/dev/
sdletter
This will take even longer than reading the original card. Assuming
the command is successful you can now issue the sync command to
ensure all blocks are actually written out to the card, and then try
the new card in the Elektor Linux board. There is no need to unmount
the card as the file system on the card was never mounted in the
first place: we wrote blocks directly to the card.
The above process is fairly straightforward, but unfortunately it
does not work if the sizes of the two cards are not identical. Also,
we sometimes want to create a new card with different partitions
or with a different file system from those of the original card. These
cases are handled by a graphical installer, which we shall look at
briefly in the next article in this series.
Figure12. Messages that appear
when unpacking the software download.
Figure13. The messages from the kernel show
that the SD card has been assigned to /dev/sdh.
What the future holds
In this installment we have made good progress on the route to
understanding our embedded GNU/Linux system. We have a development environment in place, we can now build our own bootloader and kernel, and we have compiled and run a small program.
In the next article we will take a quick look at the structure of the
source code for Linux so that we can be in a position to write our
own driver for a particular piece of hardware. We will also see how
easy it is to write programs using scripting languages.
(120180)
Figure14. Configuring the Linux kernel.
Internet links
[1]sauter@embedded-projects.net
[4]http://www.virtualbox.org
[2]http://www.ubuntu.com
[5]http://www.elektor.com/120146
[3]http://www.elektor.com/120180
21
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
Embedded Linux
Made Easy(4)
A look at the kernel
By Benedikt Sauter[1]
GNU/Linux provides interfaces and applications
for devices that we are used to seeing on desktop
computers and servers. Much of this transfers to
the embedded domain, where Linux relieves the
programmer of a lot of work by handling networking,
USB, Bluetooth and more without the need to write
complex dedicated Cprograms. And as we shall see,
using a modern operating system brings many other
benefits too.
In the previous installments in the series we have looked at how the
tool chain, the kernel and bootloader, and a standard file system can
be got up and running. One aim of the series is to show developers
what is happening under the bonnet, for example in how devices
and processes are implemented. In this installment we will first look
at what advantages there are in using an operating system when
compared to driving hardware at the bare metal level.
What is the essence of an operating system? At its heart it consists
of a collection of all the software needed to run specially-written
applications in parallel with others. The distinction between normal firmware and operating-system-based firmware is, however,
somewhat blurred. It is common enough for normal firmware to
include a clean interface to the hardware layer to provide commonly-wanted functions, for example in memory management;
and often there will be a small scheduler to divide the processors
attention between different programs. Together these already form
the beginnings of what might be called an operating system.
A typical operating system
There are of course many books on the subject of operating systems[2]. Here we shall only take a broad overview of the main
components:
Device management
Process management
Memory management
File management
User and permissions management
In the embedded world device management is usually a particularly
important aspect. The operating system must present peripherals
and hardware to applications in such a way as to simplify their use
as far as possible. This is done using device drivers. The operating
system is also responsible for handling all the details behind allowing several programs to run simultaneously, all wanting access to
SD cards, network interfaces and so on.
In the simplest programming model, an application running under
an operating system is a single, independently executable process.
A process has its own virtual address space, ensuring that it cannot
disturb other processes. Processes are centrally managed via the
process table, and each has its own process control block[3] which
typically includes space for temporary storage of processor register
contents and other important variables:
Program counter
CPU registers
Stack pointer
Status of open files
Process state
Other status information
When we launch our hello world program the kernel loads the
application and creates a new process. A new process is always created on the basis of the current one, and inherits the user permissions associated with it. The command
ps -ejH
will display the current set of processes in the system in a tree structure. At the root of the tree is the first process launched by the ker-
22
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX MADE EASY
nel, invariably called /sbin/init. Starting this process is one of the
first things the kernel does when booted.
As developers we are particularly interested in the following features of an operating system:
The interface for device drivers
The file system for programs and data
Launching and executing programs
Shared use of libraries by different programs
Access to input and output ports
Transferring working memory to non-volatile memory
Memory management for applications and processes
Process management
Let us start with device management.
Configuring the kernel
Which interfaces and peripheral blocks of the embedded processor
we need will depend on the application. The Linux kernel is designed
in a modular fashion, which results in the developer having very
fine control over its settings and in particular over what drivers and
mechanisms will be included in the kernel. It is also subsequently
possible to load other components dynamically. The best way to
see how this is done is to look directly in the kernel source code. So,
switch to the kernel source code tree:
cd ElektorLinuxBoardDownload_20120509
cd linux-2.6.33-lpc3131x
and invoke the kernel configuration menu:
make menuconfig
The main menu (Figure1) should immediately appear. If it fails to
appear, one possible cause is that the libraries for displaying console-style menus are missing. They can be installed on our Ubuntu
system by typing:
sudo apt-get install libncurses5-dev
From the main kernel configuration menu it is possible to enable or
disable any of its features and device drivers at will. For the beginner
it will not always be immediately obvious where within the menu
structure a particular option might be hiding, but with a bit of practice it soon becomes familiar.
An alternative to using the menu is to go straight to the configuration file .config in the kernel source directory (linux-2.6.33lpc3131x) and edit it by hand. (The full stop at the start of .config
indicates that it is normally a hidden file.) However, it is usually
more convenient to use the interactive menu. Before changing the
configuration it is a good idea to make a backup copy of the current
configuration file.
Figure1. Main kernel configuration menu.
cp .config config_backup
Newcomers to Linux often ask where good documentation can be
found. The best documentation for the kernel is in the kernel source
itself. It is also possible to click on help at any time in the menu to
find information on a particular item, or you can browse through
the Documentation directory (at the top level of the kernel source
tree) to look for information on a particular topic.
As Figure1 shows, the menu items are sorted into groups. At the
top are the general kernel settings, and then follow settings for the
device drivers and file systems, and finally items relating to cryptography and security.
General setup
The menu item General setup covers the basic characteristics of
the kernel, including, for example, whether there is any swap space,
how memory is managed and how internal communications are carried out. There are also settings relating to the kernel image itself,
optimizations for small systems, statistics, tools and much more
besides. The more you find out about the Linux kernel, the more
handy features you discover: there are only a few people who can
lay claim to understanding it completely!
Enable loadable module support
Another good feature of Linux is that the kernel can be extended,
while it is running, by simply adding extra modules. The kernel
configuration menu lets you decide whether a particular feature
or driver should be permanently built into the kernel code or compiled as a module: anM before a menu item means that the feature
will be compiled as a module, whereas an asterisk before an item
means that it will be built in permanently. If you want to be able
23
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
New version of the Elektor Linux board
Demand for the Elektor Linux board has been truly overwhelming!
Because of the large number of orders we have received the next
production batch has had to be made rather sooner than expected.
Unfortunately the DRAM chip in the BGA package has a lead time
to support a wide range of different devices, building all the drivers into the kernel can make it unwieldy: it is better to compile the
drivers as modules and load them manually or let the kernel load
them automatically.
Enable the block layer
The kernel distinguishes between two types of device: character
devices and block devices. A character device sends and receives
messages and other data one character at a time; a block device
always does so in complete blocks. Typical character devices include
consoles and UART interfaces; block devices include hard disks and
memory cards such as MMC and SD cards. If no block devices are
needed (as is possible for some embedded systems) some memory
space can be saved by disabling the block layer.
of some 20weeks, and so we decided to change the layout to accept a TSOP54 device. The new memory has the same capacity as
the old one but requires a supply voltage of 3.3V instead of 1.8V.
The upper picture shows the original version of the board with the
BGA memory chip, while the version in the middle picture has the
TSOP54 device fitted. Since the beginning of July we have been delivering version 1.1blue of the board, in which the now redundant
1.8V voltage regulator circuitry is removed. You can easily identify
the most recent version of the Elektor Linux board by its blue colour.
The corresponding circuit diagram is shown here.
System type
Some kernel features depend on the type of processor being used.
For example, it may be desirable to enable or disable caches or
memory management units. This menu item lets you specify (as
far as is possible) the processor type: if your particular processor is
not listed, you will need to find a patch or suitable board support
package (or write it yourself).
Bus support
Here it is possible to enable support for typical PC-type buses. In our
case only the PCMCIA option is available.
Kernel features
This item is very kernel-specific: you can specify what binary interface convention is to be presented to programs and other miscellaneous features, such as displaying processor load via the blink frequency of an attached LED.
Boot options
How the kernel is started up depends in the case of the Elektor Linux
board chiefly on the bootloader. The main interface between the
bootloader and the kernel is the so-called kernel command line.
This command allows the bootloader to pass various parameters to
the kernel that can subsequently be examined by it or by other programs. The menu item allows you to specify that a given different
command line should be used instead. Alternatively, it is possible to
compile the kernel so that it can be run directly from flash storage.
CPU Power Management
These days processors offer an ever wider range of features to support efficient power management. The kernel has to be properly
configured to allow applications to take full advantage of these
features.
Floating point emulation
This kernel configuration menu option allows you to configure it so
that it takes responsibility for floating-point calculations or so that
hardware support is used. At least one of these alternatives must be
selected to allow programs to be run.
24
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX MADE EASY
+1V2
+3V3
C35
C36
10u
1n
220p
+3V3
4u7
R12
+3V3
+3V3
L3
10k
C14
C16
C15
C12
C13
10u
100n
47n
3n3
10u
R13
VOUT/
VFB
SW
GND
M11
N14
F12
E14
G10
TP1 TP2
LPC_RXD
P12
LPC_TXD
N12
N13
TP5
P14
TP6
PWM_DATA
B9
GPIO3
I2SRX_WS0
GPIO4
SPI_CS_OUTO
MGPIO6
SPI_SCK
MGPIO7
SPI_MISO
MGPIO8
SPI_CS_IN
MGPIO9
SPI_MOSI
MGPIO10
GPIO12
MI2STX_BCK0
GPIO13
MI2STX_WS0
GPIO14
MI2STX_CLK0
GPIO15
I2STX_DATA1
GPIO16
I2STX_BCK1
GPIO17
I2STX_WS1
GPIO18
MUART_RTS_N
ADC10B-GPA1
PWM_DATA
A10
Q2
C30
10k
LPC_MCI_CLK
A6
LPC_MCI_CMD
A5
LPC_MCI_DAT0
B5
LPC_MCI_DAT1
C5
LPC_MCI_DAT2
A4
LPC_MCI_DAT3
J14
GPIO14
J13
GPIO15
GPIO18
H11
GPIO19
GPA0
A14
GPA1
B13
C14
SV1
10k
RESET
S3
C11
E3
LPC_D15
F3
LPC_DQM0
H1
LPC_WE
J2
J1
J3
K1
K2
E6
E7
D4
MLCD_DB_11
EBI_D_12
MLCD_DB_12
EBI_D_13
MLCD_DB_13
EBI_D_14
MLCD_DB_14
EBI_D_15
MLCD_DB_15
LPC313XFET180
EBI_DQM_0_NOE
EBI_A_0_ALE
EBI_A_1_CLE
EBI_NWE
EBI_NCAS_BLOUT_0
NAND_NCS_0 EBI_NRAS_BLOUT_1
NAND_NCS_1
NAND_NCS_2
MLCD_RS
NAND_NCS_3
MLCD_RW_WR
MNAND_RYBN0
N6
LPC_A2
P6
LPC_A3
N7
LPC_A4
P7
LPC_A5
K6
LPC_A6
P5
LPC_A7
N5
LPC_A8
L5
LPC_A9
K7
LPC_A10
N4
LPC_A11
K5
LPC_A12
P4
LPC_A13
P3
LPC_A14
N3
LPC_A15
B3
LPC_A0
A2
LPC_A1
G1
LPC_CAS
H2
LPC_RAS
P8
L8
DAT3
CMD
V+
CLK
GND
DAT0
DAT1
SW_A
SW_B
SD-CardSocket
10k 10k 10k 10k 10k 10k
10u
100n
10n
L7
L12
C12
10u
301k
10u
100n
10n
E5
F5
G5
H5
+3V3
L4
C26
C25
10u
100n
10n
M9
D7
LPC_CKE
10u
E8
C17
100n
G12
10n
L10
MNAND_RYBN2
MNAND_RYBN3
VSSA12
LPC313XFET180
VDDE_IOA
VSSE_IOA
VDDE_IOA
VSSE_IOA
VDDE_IOA
VSSE_IOA
VDDE_IOA
VSSE_IOA
VDDE_IOA
VSSE_IOA
VDDE_IOB
VSSE_IOB
VDDE_IOB
VSSE_IOB
VDDE_IOB
VSSE_IOB
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VSSE_IOC
K11
+3V3
IC6.C
VDDE_IOC
D5
C22
VSSI
VSSE_IOB
C13
C23
VDDI
VDDE_IOB
M7
+3V3
VSSI
VSSE_IOA
M5
C27
VSSI
VDDI
VDDA12
B2
C31
VDDI
VDDA12
E10
C33
VSSI
VPP_B
D11
C32
VSSI
VDDI
VPP_A
C9
100n
+3V3
VDDI
VSSI
A9
C38
R19
VDDE_ESD
A13
VSSE_IOC
ADC10B_VDDA33
ADC10B_GNDA
LPC_MCI_DAT3
LPC_MCI_CMD
4
LPC_MCI_CLK
LPC_A0
23
LPC_A1
24
LPC_A2
25
LPC_A3
26
LPC_A4
29
LPC_A5
30
LPC_A6
31
LPC_A7
32
LPC_A8
33
LPC_A9
34
LPC_A10
22
LPC_A11
35
LPC_A12
36
LPC_A14
20
LPC_A13
21
A0
A1
DQ0
DQ1
A2
DQ2
A3
DQ3
IC5
A4
DQ4
A5
DQ5
A6
DQ6
DRAM
A7
A8
DQ7
DQ8
A43E26161
A9
DQ9
A10/AP
DQ10
A11
DQ11
NC
DQ12
BS0
DQ13
BS1
DQ14
DQ15
LPC_DQM0 15
LPC_DQM1 39
LPC_CLK
38
LPC_CKE
37
LDQM
CAS
UDQM
RAS1
CLK
WE
CKE
CS
+5V
VCC
LPC_MCI_DAT0
LPC_MCI_DAT1
LPC_MCI_CD
D+
GND
USB
J3
R9
GPA0
GPA3
4 PWM_DATA
LPC_D0
I2C_SCL
I2C_SDA
LPC_A1
SPI_MOSI
SPI_MISO
LPC_D2
SYSCLK_O 9
10
SPI_SCK
LPC_D3
GPIO14
11
12
GPIO11
LPC_D4
13
14
10
LPC_D5
11
LPC_D6
13
LPC_D7
42
LPC_D8
D12
G4
L6
L11
E9
C3
C4
E4
F4
H4
K3
M3
M4
M6
M8
B12
D6
D8
D9
G11
L9
L13
A12
44
LPC_D9
45
LPC_D10
47
LPC_D11
48
LPC_D12
50
LPC_D13
51
LPC_D14
53
LPC_D15
17
LPC_CAS
18
LPC_RAS
16
LPC_WE
19
LPC_CS
+5V
D
D+
GND
LPC_VBUS
47u
4
5
12
11
REGIN
VBUS
VDD
RI
RST
DD+
13
RTS
SUSPEND
CTS
GPIO18
LPC_DP
USB_ID
R10
R14
C10
R18
JP1
1M
1M
100n
10k
2
1
28
27
26 LPC_RXD
25 LPC_TXD
24
23
TP7
GND
3
Relay
BSS123
+5V
GND
1
270R
MC78M05ABDT
Q4
LPC_DM
TXD
SUSPEND
G6D-1A-ASI5VDC
270R
X1
K4
BAT54
DSR
RXD
EXP
11
DTR
CD2102
GND
D4
DCD
IC7
+5V
LED5 270R
R24
1u
MINI-USB
1k
LED1
R27
C28
C24
X2
+5V
+5V
J4
+3V3
LED2
+5VEXT
+3V3
GPA1
R22
K1
D
A11
C7
+3V3
LPC_MCI_DAT2
C19
LPC_CS
U1
DM3D-SF
DAT2
C18
+1V2
K8
MLCD_CSB
C21
C6
N9 LPC_DQM1
MLCD_E_RD
MNAND_RYBN1
R29
R25
1
R23
LPC_D14
GPA1
4 GPIO15
R30
3 GPIO14
+3V3
10k
100n
2 GPIO11
GND
R21
X4
3.3V
R20
10k
GPA3
R17
R16
D3
MLCD_DB_10
EBI_D_11
GPIO2
GPIO0
R15
C2
J5
B14
+3V3
X7
LPC_D13
EBI_D_10
H3
LPC_CLK
K13
ADC10B-GPA3
+3V3
AS1324
10R
K14
RESET
+3V3
R4
K12
ADC10B-GPA2
22p
G3
LPC_MC1_CD B4
C29
22p
LPC_D12
GPIO11
12MHz
S1
22p
301k
GND
J11
UART_TXD
ADC10B-GPA0
B6
J12
GPIO20
MUART_CTS_N
LED1
H10
GPIO19
UART_RXD
GPIO2
B11
H13
GPIO11
MI2STX_DATA0
L14
C11
MGPIO5
R26
GPIO15
L1
I2SRX_BCK0
+3V3
VOUT/
VFB
SW
10u
P9
49
M12
GPIO2
C2
MLCD_DB_9
43
M13
I2SRX_DATA0
J10
GPIO1
LPC313XFET180
A1
LPC_D11
EBI_D_9
VDDQ
B7
B8
GPIO0
LPC_D10
MLCD_DB_8
VDDQ
C8
SPI_MOSI
I2SRX_BCK1
GPIO0
K10
A3
N8
MLCD_DB_7
EBI_D_8
VSS
SPI_MISO
TP4
I2SRX_DATA1
LPC_D9
IC6.B
EBI_D_7
VSSQ
A8
G13
SYSCLK_O
I2SRX_WS1
C1
C7
VIN
IC2
4uH7
54
SPI_SCK
CLOCK_OUT
I2C_SCL1
10k
SYSCLK_O
B1
MLCD_DB_6
52
A7
J4
C1
LPC_D8
EBI_D_6
10k
TP3
H12
LPC_D7
MLCD_DB_5
27
P11
CLK_256FS_O
D2
MLCD_DB_4
EBI_D_5
14
N10
BUF_TMS
I2C_SDA1
R2
LPC_D6
EBI_D_4
VDD
M10
R1
I2C_SCL0
D14
+3V3
D1
VDD
F13
D13
LPC_D5
MLCD_DB_3
VSSQ
+3V3
F11
BUF_TCK
E2
MLCD_DB_2
EBI_D_3
VSS
F14
SCAN_TDO
BUF_TRST_N
I2C_SDA0
TDO
LPC_D4
EBI_D_2
41
G14
F10
E1
46
E13
E11
ARM_TDO
IC6.A
USB_GNDA
TCK
F1
LPC_D3
28
E12
I2C_SCL
USB_VSSA_TERM
M14
LPC_D2
MLCD_DB_1
I2C_SDA
TCK
USB_VSSA_REF
TDI
TMS
MLCD_DB_0
EBI_D_1
D10
TMS
TRST_N
K9
P10
EBI_D_0
VDD
C10
TDI
F2
VDDQ
N1
P13
TRST_N
USB_RREF
G2
LPC_D1
VDDQ
L3
USB_ID
LPC_D0
VSS
K4
12k
1%
RSTIN_N
RESET
VSSQ
J5
H14
VSSQ
M1
R11
USB_DP
22p
R3
EN
N11
JTAGSEL
12
USB_ID
USB_DM
66k5
L2
+1V2
USB_VDDA12-PLL
P2
FFAST_OUT
LPC_DP
USB_VBUS
B10
N2
USB_VDDA33-DRV
L2
FFAST_IN
LPC_VBUS
USB_VDDA33
P1
M2
LPC_DM
301k
R6
AS1324
DNP
C8
VIN
IC3
4uH7
R5
EN
+1V2
4u7
LED1
C37
+5V
C5
C4
IC8
X6
+5VEXT
1
2
DC 7 - 12V
C20
47u
120181 - 13
25
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
User space binary formats
Programs and applications are compiled using the tool chain (see
the previous installment in this series). Normally, for a Linux system, the tool chain will produce either a .elf file (executable and
linkable format, corresponding roughly to a .exe under Windows)
or an a.out file. The kernel reads these formats and launches the
compiled application appropriately.
A handy Linux command is file which lets you see what format a
given file has. Switch to the appropriate directory using cd and then
type file followed by the name of the file of interest. For example, in
the Linux directory there is a file called vmlinux. Typing
file vmlinux
produces the answer
vmlinux: ELF 32-bit LSB executable, ARM, version 1
(SYSV), statically linked, not stripped
The file is an executable file for ARM processors that is statically
linked.
Power management options
It is not just the processor that offers power management features:
many peripherals and other devices have the ability to go into a
sleep mode. The operating system can take a copy of important
data and send the peripheral to sleep. When the time comes to
wake the peripheral up again, the data are copied back from memory and its state is restored. The exact behavior of the kernel in
these situations can be configured here.
Networking support
Finally we come to one of the most interesting points for developers. Under networking support we can enable, disable and configure all the software stacks (protocol libraries) for the various communication interfaces, including Ethernet, TCP/IP, CAN, infrared,
amateur radio, Bluetooth and wireless.
Security options
Linux includes a range of different security features. The most familiar of these is the system of user and group permissions and the existence of a root user. Applications can be further protected using
techniques such as trusted platform modules and internal security
models.
Cryptographic API
This menu item covers cryptographic functions in the kernel.
Library routines
This menu item includes functions for calculating checksums alongside many other things.
If you want to save your new kernel configuration, use the last menu
item Save an alternate configuration file. After entering a name for
the new kernel configuration it will be stored locally in the kernel
source tree.
We described in the previous installment in this series[4] how to
go about compiling the kernel (make zImage) and copying it to an
SD card. If new modules are to be included in the kernel, they have
to be separately compiled and installed. Compile the modules by
typing:
make modules
and to copy them to the SD card, either give the path to the destination SD card directly in the following command, or install the
modules into /tmp and then copy the new directory that is created
there to the SD card, either as user root or using the sudo command. (It is not that root privileges are required for the copy operation itself, but rather that we must ensure that the files are copied
to the file system on the SD card in such a way that the kernel can
subsequently use them as the root user.)
make INSTALL_MOD_PATH=/tmp modules_install
Device Drivers
We are also especially interested in device drivers. This menu item
includes everything you might wish for, from the simplest I/O port
to the most up-to-the-minute high speed server bus. The kernel
source code (see download at[4]) includes drivers for a wide range
of USB, I2C and SPI devices which we now have at our disposal.
Now we switch the the /tmp directory and copy the directory lib
to the SD card:
File systems
File systems are important to us for two reasons. First, the operating system itself must reside in a file system (on the SD card), so
that the kernel can read the files it needs from it. And second, the
operating system has to use a file system to allow application files
and directories to be stored on a hard disk.
with the appropriate change to the long hex string.
Kernel hacking
This menu item is chiefly aimed at kernel developers.
cd /tmp
sudo cp -R lib /media/86b3be7-00f3-45e0-832elf48c2c3065e
Interfaces
Linux has its roots in the PC world. There we are faced with interfaces such as IDE, SATA, PCI, AGP and the like. In the embedded
world the focus is more on the traditional microcontroller interfaces
and, of course, USB.
To summarize, our board has the following interfaces:
26
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX MADE EASY
Table1. Device files typically found in the /dev directory
Device type
File in /dev
Character or block device
IDE hard disk
/dev/hda, /dev/hdb, ...
Block
SCSI hard disk
/dev/sda, /dev/sdb, ...
Block
RS-232 interface
/dev/ttyS0, /dev/ttyS1, ...
Character
Printer
/dev/lp0, /dev/lp1
Character
Camera
/dev/video0, /dev/video1
Block
Mouse
/dev/input/mice
Character
RAM
/dev/ram
Block
USB (as host and device)
UART (in its current form as the system console, but can also be
used as an interface to external devices)
I2C (as master)
SPI (as master)
Also, the processor has other hardware functions such as:
GPIO
A/D converter
PWM output
As we proceed we will look more closely at these interfaces.
Device files
In order to allow applications to talk to devices, we need an interface to the device driver. The way that drivers are accessed varies
greatly from one operating system to another. Linux is based on
a UNIX file system, and under UNIX things are particularly simple:
each device is treated as a file. The idea is that no special tools are
required to drive a device; instead, we can just use small, simple
programs (from the console), operating on devices exactly as if they
were files. We showed an example of how this works in the case
of GPIO pins in the second installment of this series[5], where we
demonstrated how to flash an LED.
There are two important commands with which surprisingly much
can be achieved: cat and echo. The command cat displays the contents of a file; echo, conversely, lets us write any desired text to a
file. To put it more precisely, the output of echo can be redirected
to any desired file.
So, for example, if we want to write some data directly to a hard disk
(that is, not via a file system) or send data to a serial port, we can use
echo in commands of the form
echo Data for the hard disk > /dev/sda
or
to a hard disk or memory card, make sure you use one without any
important information stored on it!
Some device files correspond to block devices and some to character devices. A hard disk, for example, is always treated as a block
device, whereas an RS-232 interface is treated as a character device
like a normal terminal, receiving and processing characters one at a
time. Table1 gives a list of example device files that might be found
in a typical UNIX or Linux system.
Readers may be wondering exactly what mechanism is hiding
behind the commands echo and cat . How exactly does a driver
send or receive data? Now, since everything is a file, we can access
the device by simply reading from or writing to it as we would a
file. This means that each driver simply has to provide three functions: Read, Write and Ioctl (which allows the devices settings
to be changed). Read and Write are the standard functions for
reading and writing files, the process in the case of a device being
exactly analogous to that for a normal file. The first step is to open
the device or file and obtain a handle (a number which identifies
the opened device or file). The handle is then passed to subsequent
Read or Write operations on the device or file.
Kernel space and user space
Classical operating systems (in which category we include Linux) are
generally constructed with a clear separation between the memory area that is used by the kernel and the memory area available
for applications. An application is not permitted to access the kernels memory. This is another important fact for developers working close to the system level to bear in mind. In Linux, the memory
areas are called kernel space and user space. Usually in the case
of a device driver, all the software will be in kernel space. So how
does a user application program get its hands on the data received
over an interface?
The answer lies in the kernel functions such as copy_to_user[6],
which copies a block of data from kernel space into user space. So,
when an application reads a device file the driver must use this function to make the information available to the application.
Integrating a device driver
echo Hello there, world > /dev/ttyS0
But be careful: if you wish to experiment with writing data directly
We now know the basic principles of operation of a kernel device driver.
The question remains, however, of how the kernel knows to associate
a file such as /dev/ttyS0 with the correct driver for the serial port.
27
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
cat /proc/devices
The result is a list of device drivers, sorted into character devices and
block devices (Figure2).
Now we can look in the /dev directory using ls -l to display its
contents (Figure3). There will be a large number of files in the directory representing our various devices. Let us look at the information
provided alongside each device. At the beginning is the letter c
or b which indicates whether this is a character device or a block
device. Then there are the permissions as usual: we will look at these
in more detail in a later article in this series. Then the user who is
the owner of the file. Shortly after that there are two numbers
separated by a comma, and these are the major and minor device
numbers. In this example the major numbers are 1, 4, 5, 8, 89, 253
and254; the minor numbers are 0, 1, 2, 3, 9, 12, 24 and64.
If we access one of these device files from user space, the operating
system will be able to determine from these numbers and from the
entries in /proc/devices exactly which driver is needed.
It is easy to create device files using the tool mknod[7]. We will see
an example of this below.
Integrating a USB-to-serial converter
Figure2. List of available drivers.
To this end so-called major and minor device numbers are used.
These are unique identifying numbers that are defined in the kernel
documentation. The numbers are also present inside the drivers for
each type of device.
The following command can be used to display which drivers with
which major numbers are currently available:
Figure3. These files represent our devices.
Its now time to turn theory into practice. Let us drive a simple USBto-serial converter from our Linux board (from the USB end!): see
Figure4. The basic steps in the process are as follows.
Enable the driver in the kernel.
Compile the kernel.
Copy the new kernel to the SD card.
Check the driver is available in the new kernel.
Create a device file.
Test the device.
Figure4. A CP2102-based USB-to-serial converter
connected to the Elektor Linux board.
28
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX MADE EASY
Enable the driver in the kernel
First switch to the Linux source tree:
cd ElektorLinuxBoardDownload_20120509/
cd linux-2.6.33-lpc313x/
. ./set.h
make menuconfig
Figure5. The 'Device drivers' menu item.
In the first step (Figure5) select the menu item Device drivers.
Then select USB support (Figure6). With the default configuration
there will be anM here instead of the* shown in the screenshot.
Use the space bar to cycle through the three options M(compile as
module), *(compile into kernel), or neither. We select* to avoid
the complexity of having to hunt down the module later. The next
menu item is USB serial converter support and here also we must
turn the M into a* (see Figure7 and Figure8). Pressing the Enter
key brings up a submenu to select the right driver (Figure9). Most
readers will probably want to use the FTDI devices or the Silicon
Labs CP210x controllers, and so we set both of these drivers to*.
To apply these changes, go repeatedly to Exit and then, on the last
page, confirm that you want to save a new configuration file.
Compiling the kernel
We can now set off the kernel compilation process.
make zImage
Typical resulting output is shown in Figure10.
Figure6. The 'USB support' menu item.
Copy the kernel to the SD card
Put the SD card from the board into an SD card reader and then
connect the reader to the PC. Now copy the kernel to the SD card
as follows
sudo cp arch/arm/boot/zImage /
media/386b3be7-00f3-45e0-832e-1f48c2c3065e/
with, as before, the appropriate change to the long hex string.
When the copy operation is complete unmount the file system
manually in order to ensure that all the blocks are safely written to
the SD card[4]:
umount /media/386b3be7-00f3-45e0-832e-1f48c2c3065e
Check that the driver is available
Now we can boot up the Elektor Linux board as usual. It is important to check that JP1 is fitted, so that we force the USB interface to
start up in host mode. We must also make sure that the device is
provided with power over the USB connector from the Linux board.
This is done by fitting J3 in position 3-2.
We can see the first differences during the boot process itself. The
Figure7. Selecting 'USB serial converter support'
to be built in to the kernel.
29
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
mknod /dev/ttyUSB0 c 188 0
Testing the device
If we now plug the USB-to-serial adaptor into the board we get the
following output.
usb 1-1: New USB device found, idVendor=10c4,
idProduct=ea60
Figure8. Selecting 'USB serial converter support' to be built
as a dynamically-loadable module.
usb 1-1: New USB device strings: Mfr=1, Product=2,
SerialNumber=3
usb 1-1: Product: CP2102 USB to UART Bridge
Controller
usb 1-1: Manufacturer: Silicon Labs
usb 1-1: SerialNumber: 0001
cp210x 1-1:1.0: cp210x converter detected
usb 1-1: reset full speed USB device using lpc-ehci
and address 2
usb 1-1: cp210x converter now attached to ttyUSB0
Figure9. Here we select the USB-to-serial adapter(s)
that will be supported.
two new drivers make their appearance as follows.
cp210x: v0.09:Silicon Labs CP210x RS232 serial
adaptor driver
USB Serial support registered for FTDI USB Serial
Device
Creating a device file
The command cat /proc/devices reveals a new entry 188ttyUSB. We create a device file with this major number as follows.
The last line shows that the device file has been correctly created,
and that the kernel has associated the newly-recognized device with
the right device file.
Our Linux operating system includes a small terminal emulator program called microcom. By adding a jumper to bridge the RXD and
TXD lines of the serial port on the USB-to-serial adaptor we can use
this to test the driver. Start up the terminal program using the following parameters.
microcom -s 9600 /dev/ttyUSB0
If you now type characters at the keyboard they will be sent to the
adaptor and then returned to the board. If the driver is working
properly these characters should then be echoed to the screen.
Break the connection between RXD and TXD on the USB-to-serial
adaptor and the characters should no longer be echoed, demonstrating that the device driver is working. Press control-X to close
microcom.
Internet Links
[1] sauter@embedded-projects.net
[4] www.elektor.com/120180
[2] http://en.wikipedia.org/wiki/
Operating_Systems:_Design_and_Implementation
[5] www.elektor.com/120146
[3] http://en.wikipedia.org/wiki/Process_control_block
[7] http://wiki.linuxquestions.org/wiki/Mknod
[6] www.gnugeneration.com/mirrors/kernel-api/r4299.html
30
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX MADE EASY
Figure10. Output when compiling the (newly extended) kernel.
In the same way we can modify the kernel to use USB adaptors for
LAN and WLAN, USB sound cards and many other USB devices.
In the next installment in this series we will look at more devices
and interfaces and test them out with a few simple experiments.
(120181)
Advertisement
powered by Eurocircuits
s
B
C
P
r
o
t
k
e
l
E
w
e
n
5% Discount on
Benefit now: Elektor PCB Service offers a permanent
90-day launch discount on new Elektor PCBs!
Check www.elektor.com/pcb for an overview of all Elektor PCBs
31
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
Embedded Linux
Made Easy (5)
I/O, ADC, PWM, LAN & Web server
The Elektor Linux board is proving to be a big hit. The boards versatility makes it an ideal learning
tool and platform for Linux application development. In this installment we set about reading digital
and analog signals then we hook up a network adapter and build a small web server which generates
dynamic HTML pages. Using this we can read the status of remote LEDs amongst other things and
display the information in a browser.
By Benedikt Sauter [1]
Weve already spoken of boot loaders and Kernels and many readers have already taken their first steps with the file system and SD
cards (check out the text box SD card image). In this installment
we move onto the first real world task. Embedded Linux solutions
+3V3
GPA1
P1
are often found in applications such as process control and data collection. We start by showing how to input and output both analog
and digital values. Next we set up a network connection to allow
remote access of the board and remote control from a web page.
Digital I/O pins
In the second installment of this series we have already managed to
turn LED1 on and off. The LED is connected to the GPIO3 pin of the
processor. These GPIO-Pins can be configured as either input or output and also as an interrupt input. The procedure for initializing the
I/O pins should be familiar to the majority of Elektor readers by now:
activate the pins as GPIO;
initialize the data direction;
output a value or read in the signal level on the pin.
Under Embedded Linux we can talk to the GPIO-Pins via the device
driver from the console.
First we go to the communications folder with the GPIO driver:
Figure 1. Use a pot to test the A/D converters
cd /sys/class/gpio
Next we activate any pins connected to LEDs as GPIO (see circuit
diagram in [2]):
echo 3 > export
Now we must activate the pin connected to the pushbutton as a
GPIO:
echo 15 > export
Next configure the pins as either output or input:
cd gpio3
echo out > direction
Figure 2. The analog inputs are connected
via a connector block
cd ../gpio15
32
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX MADE EASY
echo in > direction
Now we can control the LED (as already shown) using the following
command to turn the LED on and off:
cd ../gpio3
echo 1 > value
echo 0 > value
The pushbutton status is contained in a (virtual) file called value.
The value of which can be read using the cat command:
cd ../gpio15
cat value
Figure 3. Initialization of the A/D converters and
output of two values.
ADC_INPUT
Now you can send commands to control the relay on the board. It
is connected to GPIO18 and the pin can be configured as an output
in the same way as above.
Analog/Digital converter
The LPC3131 provides four analog inputs with up to 10-bit resolution. The range of measured values therefore lie between 0 and
1023 (or 0 to 0x3FF in Hexadecimal). The 3.3V supply is used as the
voltage reference and also powers the I/O bank.
Reading the converter output value is similar to reading a push button status. The A/D converter has its own driver which can only output the value from one channel at a time. Therefore it will first be
necessary to set up which channel is to be read.
For simplicity the A/D converter function can be tested with a simple pot or preset resistor to supply the variable analog voltage. The
circuit is shown in Figure1. The track ends are connected between
3.3V and ground while the wiper connects to pin GPA1 via the terminal blocks.
The setup should look roughly like Figure2. Now the A/D converter can be initialized and successive measurements taken (see
Figure3).
During testing it can be tedious to keep entering the same commands. The program watch automates this procedure. With an
input of:
10k
ANALOG_INPUT
D1
3V3
Figure 4. Protection for the A/D converter inputs.
Listing1: PWM.
#include <stdio.h>
#include <stdlib.h>
#ifndef abs
#define abs(x) ((x) < 0 ? -(x) : (x))
#endif
int pwm(int value) {
FILE* f = fopen(/dev/lpc313x_pwm, wb);
fputc(value & 0xff, f);
fputc((value >> 8) & 0xff, f);
fclose(f);
}
watch -n 1 cat /dev/lpc313x_adc
The tool calls the chosen command once per second. Use Ctrl-C to
stop the process.
Next to GPA1 on the Elektor Linux board can be found the ADC channels GPA0 and GPA3 on header J5 (GPA2 is not brought out).
To protect the A/D input (to some extent) from damage by over
voltage or over current connect a 10K resistor in series with the
input and also a 3.3 V zener diode down to ground (Figure4).
Generating PWM signals
int main() {
int value = 0;
int b;
while(1) {
b = abs(63 - 2*value);
pwm(b * b);
value = (value + 1) % 64;
usleep(1000);
}
}
Many applications such as servo controllers, switched-mode voltage
33
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
Figure 5. PWM output when a value of 1000 is used as a
comparison value.
Figure 6. PWM with 50% mark/space ratio.
generators and digital audio (and much more [3]) need a generator
of pulse width modulated signals. The Elektor Linux board outputs
PWM signals from header J5. For test purposes use an oscilloscope
to view the generated signals.
In PWM mode the controller increments a 12-bit counter on each
clock pulse. When the counter value reaches a predetermined
value it switches state of the PWM pin from high to low (when
the counter overflows it is reset and the bit goes high) The predetermined value can be any 12-bit value i.e. in the range from 0
to 4095. When the value of 0 is specified the PWM output will go
low immediately. A value of 2000 gives a square wave with a markspace ratio of around 50%.
In contrast to both the I/O and ADC drivers the PWM driver expects
a binary input value so it is not simple to use echo or cat because the
value supplied will be interpreted as a character (ASCII). We need
the assistance of a small help program.
We were able to quickly write this on board using C. We have
included a copy of this program (Listing1). In the Home folder,
where you will always find yourself after logging in, you will find
the file pwm.c. In the code it is necessary to change the name of
a device file.
First open the file using an editor on the board:
nano pwm.c
Navigate along the following line using the arrow keys
FILE* f = fopen(/dev/pwm, wb);
change the line to read:
FILE* f = fopen(/dev/lpc313x_pwm, wb);
Figure 7. PWM with 1% mark/space ratio.
Now save the edited version using Ctrl-o and end the editing session
with Ctrl-x. The code can be compiled on the PC or directly on the
Linux board which also contains its own compiler:
gcc -o pwm pwm.c
Once compiled (this takes a few moments) it can be directly
executed:
./pwm
The oscilloscope display shows how the mark/space ratio changes.
When a signal with a fixed mark/space ratio is required this can be
achieved for example with a small script written in the programming language Python. The file pwm.py can be found in the home
folder.
First it is necessary to start the Python interpreter:
Figure 8. PWM with 99% mark/space ratio.
python
34
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX MADE EASY
SD card image
When experimenting which we like to encourage! you can
sometimes find yourself backed down a one-way street with no way
out. In this situation there may be no alternative but to take a fresh
version of the original SD card. For this reason we are offering the SD
card contents as an extra download. First download the image from
the Elektor web [8] (Download SD Card Image, 120180-12.zip).
When the download is complete, unpack the archived files:
unzip 120180-12.zip
This takes a while before the following message appears:
Archive: ../120180-12.zip inflating: Elektor_Linux_
Board - Build_New_SD_Card.txt inflating: gnublin.img
Now take the SD to be written to and plug it into the PC or card reader. The system will read the card but we are not interested in this, we
just need a 1:1 image of the downloaded file on the memory card.
For this it will be necessary to manually configure the card.
The best way is to plug the card in the reader and give the command
dmesg via the console to find out what the card has been mapped to.
The response to this command will be something like the following:
In the interpreter (already we are in interactive mode) we can load
the PWM module (a library of Python functions):
import pwm
Now it is possible to call the module functions. One of these functions allows direct input of the counter compare value:
pwm.pwm_raw(1000)
The signal on the oscilloscope should now look like Figure5.
[ 1069.427374] sdf: sdf1 sdf2 [ 1069.430857] sd
5:0:0:0: [sdf] No Caching mode page present [
1069.430863] sd 5:0:0:0: [sdf] Assuming drive cache:
write through [ 1069.430868] sd 5:0:0:0: [sdf] Attached
SCSI removable disk [ 1070.002620] EXT2-fs (sdf1):
warning: mounting unchecked fs, running e2fsck is
recommended
In the last line there is an indication of the device name that the kernel has given to the SD card (sdf1 in this case).
Now it is necessary to manually unmount all partitions by using
umount /dev/sdf1
replace sdf1 with the device name assigned to your card (exactly
as the name assigned to the first partition).
Now the downloaded image can be written to the SD card:
sudo dd if=gnublin.img of=/dev/sdf
sdf is the description of the whole card as a block device.
NB: The process of writing can take up to 10minutes.
are many different models on the market but they mostly use the
same or similar chipsets. The one we are using here is the D-Link
DUB-E100 [4].
In the last installment of this series we integrated the driver for the
USB/UART adapter in the kernel. As we have already shown in this
series the kernel can also load a device driver as a module during
run time. We will use this approach for the network adapter. The
file system already contains many different drivers.
In the case of D-Link adapters it is necessary to give the following
command:
Alternatively the mark-space ratio can be given as a percentage:
modprobe asix
pwm.pwm(50)
the waveform on the screen now looks like Figure6.
Now we should see an output as shown in Figure10.
There are three drivers in the file system:
Using the command
asix
pegasus
net1080
pwm.pwm(1)
pwm.pwm(99)
is interpreted as mark then space so produces 1 to 99% (Figure7
/ Figure8).
Use Ctrl-d to close the Python-Interpreter.
Be aware that at the start Python takes a little while to fully initialize
but once running it responds quite smoothly.
When a different adapter is used try loading different drivers one
after the other. To check if the correct one has been found we should
see this response to the input:
Network interfacing
A response of eth0 indicates the driver has been correctly loaded
and the network is ready to transfer data.
When all of the drivers fail it is possible to go to Device Drivers ->
Network device support -> USB Network Adapters and load the
drivers by hand. The drivers can either be compiled into the kernel
In the last installment of this series we have already shown how to
interface a USB/UART adapter. Now we install another USB adapter
to the Linux board which can connect to an Ethernet network. We
will be using an off-theshelf USB/LAN adapter (Figure9). There
ifconfig -a
35
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
Windows) from the console by issuing the command ping:
ping 192.168.0.7
When the program reports that no device responded
2 packets transmitted, 0 received, 100% packet loss, time
1006ms
Then this IP address is free to be used.
So we give this to the Linux board:
ifconfig eth0 192.168.0.7
Once this has been set up another ping attempt by the PC should
now elicit a positive response. (Figure11).
Optionally the DHCP server can be allowed to allocate addresses
automatically (Figure12).
Figure 9. A USB/LAN adapter gives the board a network interface.
(recommended for beginners, see [2]) or after the modules have
been converted, copied into the file system and then later loaded
into the kernel.
All modules can be converted using:
make modules
The new module can be installed on the card using:
To ensure the driver is automatically loaded at every start it is necessary to add its name to the /etc/modules file. All of the modules
in this file will be automatically loaded during Linux boot process.
The network IP address is stored in the file /etc/network/interfaces. This file already exists in our file system. Use an editor to
enter your in-house IP address after eth0.
Now each time the Elektor Linux card is started it will be ready to
communicate with your local network.
Web server
Now that a network connection is available we can start a small
web server to view our first demo page with a browser. In the home
folder of the users root is a small script which starts the well-known
web server lighttpd:
make modules_install INSTALL_MOD_PATH=/mnt
root@gnublin:~# ./lighttpd-init.sh
Instead of /mnt the SD card path should be used here
Syntax OK
Now that the network adapter has been recognized it can be given
a temporary IP address. It is worth hooking up a PC to the network
to check just which addresses have already been assigned before the
temporary IP address is chosen. This can be performed in Linux (or
root@gnublin:~#
Using the browser to visit the IP address mentioned above will show
Figure 10. Automatic recognition of the USB/LAN adapter.
36
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX MADE EASY
the web site in Figure13.
Figure 11. A reply following a ping.
A web server typically generates static HTML web pages. When we
show the status of an LED in a browser for example, the web server
must output a dynamically assembled HTML page (depending on
the LED status). We require an interface between the web server
and an external program that is capable of detecting whether the
LED is on or off and can generate a corresponding web page.
The simplest link is called CGI. This Common Gateway Interface
is an interface which enables the web server to access almost any
program. One condition is that it is command-line orientated, and
can also be started (with any parameters) from the console. CGI
scripts also most commonly return an HTML page. As a CGI program
you can use a simple Linux shell script, a PHP or Python program or
even a C program.
Figure 12. The DHCP server automatically assigns the address.
Switching an LED from the browser
This simple application will show how it is possible to change the
state of an LED from a browser. We create a simple script file that
the shell (console) can directly execute. First it is necessary to setup
the CGI interface on the web server.
In the file /etc/lighttpd/modules.conf identify the entry
#include conf.d/cgi.conf
Using an editor (e.g. nano or vi) change it to:
include conf.d/cgi.conf
Next, in the file /etc/lighttpd/conf.d/cgi.conf it is necessary to edit
the entry
#alias.url += ( /cgi-bin => server_root + /cgi-bin )
Figure 13. Test page of the web server.
To
alias.url += ( /cgi-bin => var.server_root + /cgi-bin
)
Now the web server knows that the files in the /cgi-bin folder can
be treated as programs (and not as HTML pages to be sent to the
browser)
And add the line .sh
cgi.assign
Next for us to finally use a simple shell script as a CGI program it is
necessary to identify the region in the file
eruby,
cgi.assign
python )
=> /bin/sh
= ( .pl
.cgi
.rb
.sh
.erb
.py
= ( .pl
.cgi
.rb
.erb
=>
=>
=>
=>
/usr/bin/perl,
/usr/bin/perl,
/usr/bin/ruby,
/usr/bin/
eruby,
/usr/bin/perl,
/usr/bin/perl,
/usr/bin/ruby,
/bin/sh,
/usr/bin/
=> /usr/bin/
The next step is to create a directory for the CGI programs:
mkdir -p /srv/www/htdocs/cgi-bin
.py
python )
=>
=>
=>
=>
=>
=> /usr/bin/
And lastly to add the program in Listing2, after that the editor can
37
Personal Download for Fernando Faria Corra | copyright Elektor
MICROCONTROLLERS
When the browser accesses the previously set up IP address, the
page shown in Figure14 is opened and the LED status displayed.
We have built a little control demonstration with the help of a mini
HTML form which in this case only contains a submit button. Pressing the button transfers the form control elements to our web
browser. In this case we use this mechanism to inform the web
server that it must call the CGI script /cgi-bin/example.sh. This
toggles LED1 on the board and builds the new web page with the
changed status message.
Figure 14. LED switching in the browser.
be started with the following command:
Coming up
In the next installment we will build a more complex HTML user
interface which will allow us to control more functions of the board.
It goes without saying that this will not be performed from a nice
clean user interface where you cant see whats happening under
the surface; our control requires a little more intelligence. This can
nano /srv/www/htdocs/cgi-bin/example.sh
The listing is included in the downloads for this part of the course
[5], so you can just copy these to save wear and tear on your
keyboard.
In order for the web server to control the LED it is necessary to set
the configuration and the data direction from the console:
echo 3 > /sys/class/gpio/export
echo out > /sys/class/gpio/gpio3/direction
From security reasons the web server never runs as user root but
we must temporarily allow access to allow the web server access to
control the LED:
chown
lighttpd:lighttpd /sys/class/gpio/gpio3/value
Now everyone in the system has access to the LED. This is not an
optimal solution but to selectively assign access rights is something
we will not go into for the time being. More information on this
topic can be found at [6].
Apart from this the web server must be granted appropriate privileges to store log files in a previously generated directory:
mkdir /var/log/lighttpd
chown -R lighttpd:lighttpd /var/log/lighttpd
Start the web server now
root@gnublin:~# /etc/init.d/lighttpd restart
Then you should get the message:
Syntax OK
Listing2: CGI script to generate a website.
#!/bin/sh
if [ "$REQUEST_METHOD == "POST ]
then
if [ cat /sys/class/gpio/gpio3/value == 1 ]
then
echo 0 > /sys/class/gpio/gpio3/value
else
echo 1 > /sys/class/gpio/gpio3/value
fi
fi
echo Content-Type: text/html; charset=utf-8
echo
echo <html>
echo <head>
echo
<title>Webserver CGI Port 3 (LED)</title>
echo </head>
echo <body>
echo <h1>Control-Panel CGI Port 3</h1>
if [ cat /sys/class/gpio/gpio3/value == 1 ]
then
echo Port: On
else
echo Port: Off
fi
echo <br><br>
echo <form action\/cgi-bin/example.sh \ method =
\POST\>
echo <input type=\submit\ value=\Click\>
echo </form>
echo </body>
echo </html>
38
Personal Download for Fernando Faria Corra | copyright Elektor
EMBEDDED LINUX MADE EASY
be achieved by a small program running in the background.
For the edition after that and then for the final installment of this
series (the first edition of the year 2013) we have planned something special: The subjects we intend to cover will be entirely up to
you. Go to our special web site [7] and tell us what you would like
to see! A good deal of user talk on the project may also be found on
our forum [9].
(120182)
Internet Links
[1] sauter@embedded-projects.net
[2] www.elektor.com/120181
[3] http://en.wikipedia.org/wiki/Pulse-width_modulation
[4] http://shop.embedded-projects.net/gnublin
[5] www.elektor.com/120182
[6] http://en.gnublin.org/index.php/Permissions_GPIO
[7] www.elektor.com/linux-feedback
[8] www.elektor.com/120180
[9] www.elektor.com/forum/elektor-forums/fields-of-interest/microcontrollers-embedded.1543738.lynkx
Advertisement
Android Apps
BESTSELLER
programming step-by-step
When it comes to personalizing your smartphone you should not
feel limited to off the shelf applications because creating your own
apps and programming Android devices is easier than you think.
This book is an introduction to programming apps for Android
devices. The operation of the Android system is explained in a
step by step way, aiming to show how personal applications can
be programmed. A wide variety of applications is presented based
on a solid number of hands-on examples, covering anything from
simple math programs, reading sensors and GPS data, right up to
programming for advanced Internet applications. Besides writing
applications in the Java programming language, this book also
explains how apps can be programmed using Javascript or
PHP scripts.
244 pages ISBN 978-1-907920-15-8
34.95 39.95 US $56.40
10% OFF
fo
GR EEN a r
nd
G OLD Me
mbe r
Further information and ordering at www.elektor.com/android
39
Personal Download for Fernando Faria Corra | copyright Elektor
Projects
Embedded Linux
Made Easy(6)
Networks and servers
Linuxs modular structure makes it the
By
Benedikt Sauter[1]
ideal basis for all sorts of projects in
measurement and control. A custom
control system can be written in virtually any programming language you
choose, with the user interface served
up as a web page to a PC, tablet or
smartphone. In this article we will connect the Elektor Linux board to a typical
home network and then show how to
write our own server application. We
finish with a look at the Lua scripting
language.
Figure1.
Overview of the system.
This series has already covered the most important aspects of Linux from the bootloader and
kernel to the installation of device drivers. However, we have not yet seen how everything can
be pulled together to construct a full-scale application, including for example the control logic
running in the background, a web interface for
configuration and operation, and a simple control
panel using a display and buttons.
Messaging between applications
Figure1 shows a typical scenario. In the middle
we have the Linux system running the custom
application and a web server to allow control
from a wide range of devices using a browser.
We can also add a display and a couple of buttons, the display being controlled using a separate
program. The main application program is just
an ordinary process, running in the background
among many others. Such a program is known in
the Linux world as a daemon or service. Once
a daemon has been started it continues to oper-
ate without further action on the part of the user.
There are various ways to implement the interface between the daemon and other programs:
such messaging between threads or processes is
called inter-process communication, or IPC. For
example, we can use pipes, FIFOs, shared memory, sockets, stdin and stdout (standard input
and standard output), files, databases or any
number of auxiliary programs. Which approach
we choose depends on several factors. Are the
programs written in the same language? Does the
software have to be portable to other operating
systems? What data rate is required?
One widely-used approach is the classical network
socket. Sockets have the significant advantage
that data and commands can be sent over a network if desired. It is very easy to send and receive
messages using sockets and suitable functions
and classes are available in a wide variety of different programming languages.
We have therefore chosen to use sockets as
the basis for communications between the pro-
40
Personal Download for Fernando Faria Corra | copyright Elektor
Embedded Linux made easy
grams described in this article. Since the web is
also based on standard sockets, we will also see
along the way how to access data on the internet directly. So, as a first step, we will equip the
Linux board with an internet connection.
Connecting to the Internet
We assume that you already have a router providing a connection to the internet and acting as
a gateway for the other devices on your network.
You will also need to connect a USB-to-LAN adaptor to the USB interface on the board and make
sure that the driver for it is loaded[2].
To integrate a new device into a network it needs
to know its IP address and other parameters
such as the gateway and DNS server addresses.
This can be done manually or by using DHCP
(Dynamic Host Configuration Protocol). When
DHCP is used the router on the local network will
configure the Elektor Linux board automatically
(see Figure2). To test whether an IP address
has been successfully assigned using DHCP, you
can use the ping command to see whether you
can establish contact with (for example) the web
server at www.elektor.com (see Figure3).
If no DHCP server is available the network parameters have to be entered manually.
IP address
To set the boards IP address, enter the command
ifconfig eth0 <ipaddress>
with the placeholder <ipaddress> replaced by
an unused IP address on your network. To check
that this has been successful, you can try pinging your router using the command
ping <routeraddress>
with the placeholder <routeraddress> replaced
by the IP address of your router.
Gateway address
In order to send packets over the Internet, the
board has to know the address of the router
which provides the gateway for network traffic.
This is configured using the command
route add default gw <routeraddress>
with the placeholder <routeraddress> again
replaced by the IP address of the router. To test
Figure2.
Configuration of IP
addresses using DHCP.
Figure3.
Testing access to a server
using the ping command.
this step, you can try pinging any other computer
on the internet, for example by typing
ping 94.236.12.177
which will ping the Elektor web server. Assuming this is successful (that is, that no packets are
lost), we can proceed to the final step.
DNS server address
In order to navigate the internet using domain
names rather than IP addresses, each device
on the network needs access to a DNS server.
A DNS server can convert a string like www.
elektor.com into its corresponding IP address.
To configure a DNS server for the Elektor Linux
board to use, it is necessary to edit the file /
etc/resolv.conf (see Figure4). Run the editor
by typing
nano /etc/resolv.conf
and look for the nameserver entry in the file.
One option is to enter your routers address here,
as most routers these days provide DNS services.
Alternatively, if you do not have a local DNS
server, it is possible to use one on the internet.
One that is easy to remember and ideal for testing
is run by Google on the IP address 8.8.8.8. Save
the file using control-O and then type control-X
to leave the editor.
You should now find that you can reach any server
on the internet using the ping command followed by the name of the machine.
Writing your own server
Now that we have established a connection
41
Personal Download for Fernando Faria Corra | copyright Elektor
Projects
command prompt so that you can execute other
programs or commands.
The console will show
[1]+
Stopped
./a.out
and if you now type
fg
Figure4.
Configuration of the DNS
server.
(where fg means foreground) the console will
become blocked again. Suspend the program
again using control-Z and then type
Figure5.
Downloading the file
server.c.
bg
which will set the server program running again,
but this time in the background.
between the board and the internet, we can
directly download some example source code
for a server. The command wget, which will be
familiar to users of Linux on PCs, lets us transfer
any file we like from the internet onto our board.
Figure5 shows the command being used to
download the server example code.
The server is written in the C programming language. For initial testing we will use the compiler
on the board; if you plan to develop the code to
any significant extent it is worth using the toolchain on the development PC.
Listing1 shows the source code for the program. The basic function of the server is to listen
on port 5000. If it receives a connection on this
port from another device, it will reply with the
current time. The program could be extended to
include a command interpreter so that different
replies can be given or different actions carried
out in response to different commands sent by
the client.
To test the server we first need to compile it:
gcc server.c
When the compiler has finished, we start the
server by typing:
./a.out
The program will now apparently become unresponsive to the console (apart from control-C,
which exits the program). However, this is not
quite true: you can type control-Z, which suspends the server program and returns you to the
As we mentioned above, the example server code
sends the current time to any client that connects
to it. We can try this out from the console using
the telnet program, which takes as its parameters the IP address of the server and its port:
root@gnublin:~/c# telnet 127.0.0.1 5000
Tue Sep 27 21:34:49 2011
Connection closed by foreign host
To bring the server program back to the foreground (for example in order to terminate it),
simply type
fg
at the console.
Of course, you can equally well access the server
from a web browser. The server could also be
modified so that instead of simply returning the
time it carries out some other action, such as
turning an LED on and off. We showed how this
can be done in the previous installment in the
series[2].
Remote access over the network
So far we have used picocom or similar terminal
program to provide a console over the boards
serial port. However, now that we have a network interface, we can run as many consoles as
we like over the network. This makes life much
easier, especially when developing applications.
42
Personal Download for Fernando Faria Corra | copyright Elektor
Embedded Linux made easy
Listing1: A simple server program.
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
<sys/socket.h>
<netinet/in.h>
<arpa/inet.h>
<stdio.h>
<stdlib.h>
<unistd.h>
<errno.h>
<string.h>
<sys/types.h>
<time.h>
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(5000);
bind(listenfd, (struct sockaddr*)&serv_addr,
sizeof(serv_addr));
listen(listenfd, 10);
while(1)
{
connfd = accept(listenfd, (struct sockaddr*)
NULL, NULL);
int main(int argc, char *argv[])
{
int listenfd = 0, connfd = 0;
struct sockaddr_in serv_addr;
ticks = time(NULL);
snprintf(sendBuff, sizeof(sendBuff),
%.24s\r\n, ctime(&ticks));
write(connfd, sendBuff, strlen(sendBuff));
char sendBuff[1025];
time_t ticks;
listenfd = socket(AF_INET, SOCK_STREAM, 0);
memset(&serv_addr, 0, sizeof(serv_addr));
memset(sendBuff, 0, sizeof(sendBuff));
For example, we can be editing the source code
in one console while simultaneously running and
testing the program in another.
We use SSH (the secure shell) to make the
connection to the Elektor Linux board over the
network. This console automatically provides an
encrypted communication channel to the board,
which has the advantage that other devices on the
network cannot intercept the information being
sent to and from the board.
SSH from Linux
Under Linux ssh is one of the standard tools.
Type the following into the console on the host
computer:
ssh root@192.168.0.190
close(connfd);
sleep(1);
}
}
address of the board. Next click on Open and you
will be asked to verify that the remote devices
fingerprint (part of the SSH key for checking
the identity of the remote device). Click on Yes.
In response to the prompt login as enter root.
Listing2: A start-up script.
#!/bin/sh
if [ ! -d /var/log/lighttpd ]
then
mkdir /var/log/lighttpd
chown -R lighttpd:lighttpd /var/log/lighttpd
chmod 777 /var/log/lighttpd/
touch /var/log/lighttpd/error.log
fi
modprobe asix
udhcpc
with the IP address replaced by that of your Linux
board. When the connection has been established
with the board, type the word yes (see Figure6). Shortly the standard console will appear,
and you should feel at home!
SSH from Windows
Under Windows we can use the PuTTY program[3]. Figure7 shows the programs main
window, and again we have to enter the IP
echo 3 > /sys/class/gpio/export
echo "out" > /sys/class/gpio/gpio3/direction
chown lighttpd:lighttpd /sys/class/gpio/gpio3/value
chown lighttpd:lighttpd /dev/lpc313x_adc
echo 1 > /dev/lpc313x_adc
chmod 666 /var/log/lighttpd/error.log
/etc/init.d/lighttpd start
43
Personal Download for Fernando Faria Corra | copyright Elektor
Projects
ing games programs. The Lua interpreter is just
120Kbyte in size but nevertheless implements
many of the most up-to-date constructs in programming. Accessing files (which in the Linux
world also means accessing devices and hardware
functions) is done using the commands io.open,
io.read, io.write and io.close. The author has written a Lua library for the Elektor Linux board providing easy access to the main hardware functions, including the GPIO and A/D converter.
Figure6.
Accessing the board using
SSH under Linux.
The advantage of using an interpreted language
over a compiled language like C to build a simple
control application is huge: you can easily edit
your programs directly on the board using its
built-in editor (nano or vi) and execute them
immediately.
Using our newly-configured internet connection
we can download the Lua example programs onto
the SD card as follows.
Figure7.
Accessing the board using
SSH under Windows.
When you press enter you will be logged in to
the system.
Using the Lua scripting language for
simple applications
The previous example used the C programming
language. C is very commonly used, fast and popular. However, on a Linux system there are many
other languages available besides C: each has
its advantages and disadvantages and is aimed
at different applications.
One very flexible, fast and easy-to-use language is Lua. The language is interpreted, and
has become best known for its use in scriptFigure8.
The "blink.lua" example
program.
Figure9.
The "adc_relay.lua" example
program.
mkdir lua
cd lua
wget http://www.gnublin.org/downloads/elektor.lua
wget http://www.gnublin.org/downloads/blink.lua
wget http://www.gnublin.org/downloads/button.lua
wget http://www.gnublin.org/downloads/adc_relay.lua
Figure8 shows the listing of a simple Lua program that flashes the LED. First the Elektor library
has to be loaded. The LED is initialised with a
call to initLED() and then switched on and off
in an infinite loop.
The program can be run from the console as
follows:
lua blink.lua
The program button.lua is a simple demonstration of how to control a relay using the IO15 button on the board. And Figure9 shows the code
of the example program adc_relay.lua, which
reads voltage values from the A/D converter in an
infinite loop. If the reading is above the threshold
of500 the relay contacts are closed; otherwise
they are opened. The program can be tested by
connecting a potentiometer to the analog input
as described in the previous installment in this
series[2].
There is plenty of information about Lua on the
internet[4] as well as some good books[5].
44
Personal Download for Fernando Faria Corra | copyright Elektor
Embedded Linux made easy
Starting the web server and other
programs automatically
Figure10.
Downloading the web server
start-up script.
It is rather inconvenient to have to restart a
server application (and perhaps also reconfigure
the hardware) manually each time the board is
rebooted. The solution to this is to include the
relevant commands in a start-up script. Listing2
shows a suitable script to start up the web server
application from the previous installments in this
series[2]. The file start.sh can be downloaded
from the internet (see Figure10).
In order to have this script run automatically
when the Linux system is booted, we can for
example add an entry in the file /etc/rcS.d/
S55bootmisc.sh (the directory /etc/rcS.d is
where the system start-up scripts are kept). First
open the file using an editor.
Figure11.
Accessing the board from an
iPhone.
nano /etc/rcS.d/S55bootmisc.sh
touching the HTML button.
At the end of the file you will see the following
entries.
/bin/mkdir /var/run/sshd
/usr/sbin/sshd
After these add the line
/home/root/start.sh
and then save the file by pressing control-O and
exit the editor with control-X. To make sure that
the file start.sh can be found it must be placed
in the directory /root. The file must also be
made executable by setting its x flag as follows.
chmod +x /home/root/start.sh
From now on whenever the board is booted it
will automatically establish a network connection
and start up the web server.
Access from a smartphone or tablet
In the previous installment in the series we
showed how to access a demonstration web
application from a browser running on a PC. Of
course, such an application can equally well be
accessed from a smartphone or tablet, as long
as the network on which the Elektor Linux board
sits includes a WLAN access point to which the
mobile device can connect. Figure11 shows the
HTML control interface running on an iPhone. The
LED on the board can be turned on and off by
At this point it is useful to note that the Gnublin
project community wiki[6] covers a very wide
range of hardware and software topics. It includes
guides to transmitting and receiving data over
the I2C, SPI and UART interfaces (including moving data to and from user space) and much more
besides.
We have invited readers to propose a topic for
the next installment in this series: at the time of
going to press our poll was still open. We have
been eagerly awaiting your suggestions! We can
also announce that we are working on an expansion board that increases the range of interfaces
available: more on that also in the next issue.
(120578)
Internet Links
[1] sauter@embedded-projects.net
[2] http://www.elektor.com/120182
[3] http://www.putty.org
[4] http://www.lua.org
[5] http://www.lua.org/pil
[6] http://wiki.gnublin.org
45
Personal Download for Fernando Faria Corra | copyright Elektor
Projects
Embedded Linux
Made Easy(7)
by
Benedikt Sauter[1]
I2C, serial ports and RS485
In the previous installment in this series we called on our readers to tell us
what they would like to see in this final part. Many thanks to the well over one
hundred readers who responded to the call! By popular demand, therefore, we
will look more closely at how a real application is developed. The I2C and serial
interfaces, which are often needed in real-world projects, provide the other main
focus of this article.
3D Model of the Elektor
Linux board created using
the EagleUp tool [14].
Open source softwares lifeblood is open development in the community. Individual project websites therefore form a source of updates, patches
and extensions, and they often also provide useful
hints and tips and even user forums.
The Elektor Linux board is based on the Gnublin
project[2], which was launched in 2009 by the
author in association with the Augsburg University
of Applied Sciences, Germany, as a platform for
embedded Linux development. Since then several
variations on the board have been produced, as
well as compatible extension circuits to drive
stepper motors and interface to the CAN bus.
And, of course, the software has been extended
in many different ways, including updates to the
kernel and to the toolchain, including the C/C++
compiler. Lets install these first!
Updates
In the third part of this series[3] we installed
a C/C++ compiler on the PC to avoid having to
compile programs directly on the Elektor Linux
board. We can now update to the most recent
version of this compiler, along with the other
components in the toolchain.
The easiest way to do this is to open a terminal
46
Personal Download for Fernando Faria Corra | copyright Elektor
embedded Linux
window on the Ubuntu Linux machine using the
control-alt-T key combination. Type the following command into the console:
wget http://gnublin.org/downloads/eldkeglibc-i686-arm-toolchain-qte-5.2.1.tar.
bz2
When the archive has finished downloading, we
can unpack it on top of the local file system:
sudo tar xjf eldk-eglibc-i686-armtoolchain-qte-5.2.1.tar.bz2 -C /
Before we can use the new compiler we have
to write a small script to automatically set the
environment variables to point to the new installations directories. Create a file called set.sh in
the directory /opt/eldk-5.2.1:
sudo gedit /opt/eldk-5.2.1/set.sh
and, if you wish, the development environment itself.
Since devices appear just like files within the
Linux directory hierarchy, it is natural to want
to have the remote storage devices appear in
the directory structure of the development computer just like any other directory or subdirectory. Under Linux there are (of course) many
options open to achieve this, including NFS, FTP
and Samba.
In the previous installment of this series[4] we
looked at remote maintenance of the Linux board
using SSH. SSH is a client-server system that
allows access to a console on the board over a
network connection. SSH is similar to Telnet, but
provides a higher level of security by encrypting the connection between server and client. It
also provides a mechanism for transferring files
between the server and the client, using (on the
client side) a program called scp.
Figure1 shows how the new directories are
added to the PATH environment variable. Save
the script file you have created and run it from
the console as follows (watch the period-spaceforward slash syntax at the start):
. /opt/eldk-5.2.1/set.sh
From now on this command must be re-entered
every time you want to compile a program for
the Elektor Linux board on the development PC.
The compiler in the new toolchain can be tested
using the following command:
If the Linux board has the IP address
192.168.0.190, then we can copy a file directly
from the development PC into the Linux boards
file system using the following command:
Figure1.
Environment variables for
the new toolchain.
scp test.c root@192.168.0.190:/root
arm-linux-gnueabi-gcc -v
C/C++ development environment
Many readers asked about the possibility of developing C/C++ code for the board using a userfriendly integrated development environment
(IDE) such as Eclipse. We will therefore briefly
look now at an easy way to develop C/C++ programs on the development machine and then
test them immediately on the board.
You will need the following:
console access to the Linux board (over its
serial port or over the network);
a network file system (to allow access to
files from the development PC);
To copy an entire directory, use a command such
as:
scp -r myproject root@192.168.0.190:/root
The first argument to the command is the file or
directory to be copied. If a whole directory is to
be copied, it must be preceded by the -r option,
which causes a recursive copy of all files and
directories found under the specified directory.
The second argument consists of the user name,
followed by an @ character and the IP address,
and then a colon and the destination path on the
Linux board for the copy operation.
This gives us a convenient way to copy compiled
47
Personal Download for Fernando Faria Corra | copyright Elektor
Projects
code to the board whenever we wish. However,
we can make things even easier!
The slickest approach is to install the Linux program sshfs. This is done by typing the following
command on the development PC:
root identity on the development machine. The
simplest way to do this is to start a new console
with root privileges:
sudo apt-get install sshfs
In this console you will be free to examine, create and delete any file you choose.
Now, in order to write programs for the board
on the development PC, we simply need to put
the project directory in the mounted file system.
You can then use Eclipse or other environment
as normal: see[5] for more information.
It is preferable to create a dedicated user on the
Elektor Linux board before commencing a new
project. This can be done as follows:
The tool allows us to access the entire file system
of the Linux board live from the development PC,
completely avoiding the need to copy files across
to the SD card by hand. All that is needed (as
indeed for SSH and scp) is of course a working
network connection.
We first have to create a so-called mount point
within the development PCs file system. This is
the point in the file system where the files that
are actually on the remote system will appear.
The mount point is normally an empty directory,
which we can create either using the mkdir
command at the console or using a graphical
file manager.
First switch to your home directory:
cd ~
and then create the mount point:
mkdir elektor-linux-board
The next command will make the whole file system of the Linux board available on the development PC:
sshfs root@192.168.0.190:/
elektor-linux-board
When the command is entered you will be
prompted one last time for the password. When
this has been entered successfully, you will be
able to navigate around the remote file system
using commands like cd and ls as usual. However, as you wander around you may occasionally
be greeted by the message Permission denied.
This is because the system programs on the Elektor Linux board are owned by the user root
and are not readable by ordinary users; as far as
the remote file system is concerned your identity
(or uid) is that which you have on the development machine. In order to be able to roam freely
around the remote file system you must adopt the
sudo /bin/bash
adduser elektor
You can then mount this users home directory
in the development PCs file system:
sshfs elektor@192.168.0.190:/home/elektor
elektor-linux-board
If you are the only user on both the development PC and the board, in each case your uid will
be1000. This means that you will not encounter
any problems with file permissions between the
two machines. In any case, as a fall-back, it is
always possible to run programs as the root user.
Kernel update
The kernel provides the interface between applications and hardware. If you want to use additional or new hardware, it will sometimes be necessary to download a new version of the kernel
and compile it manually.
The Gnublin projects kernel archive is the first
port of call for Elektor Linux board users. The
kernel has been extended since the first article
in this series was published, adding support for
CAN, SPI and I2C devices.
The source code is kept centrally in a version control system, which provides a place where developers can easily make changes themselves and
track changes made by others. The git version
control software is used to manage the archive. To
access a git repository the git package is needed:
on the development machine type the following:
sudo apt-get install git-core
48
Personal Download for Fernando Faria Corra | copyright Elektor
embedded Linux
You can now clone the archive from the repository into any directory you choose:
git clone http://code.google.com/p/
gnublin-develop-kernel/
This operation may take some time. When the
downloading has finished and the prompt returns,
you are ready to build the most recent stable
kernel for the board. Change to the directory for
version 2.6.33 of the kernel:
I2C tools
The I2C bus has been popular in the microcontroller world for decades. Compatible devices
available range from analog-to-digital converters and I/O expanders to temperature sensors. To
drive the I2C bus from Linux we first have to activate the right driver in the kernel configuration:
Device Drivers I2C support I2C
Hardware Bus support I2C bus support
for Philips PNX targets
cd linux-2.6.33-lpc313x
make elektor_defconfig
If you wish to make manual changes to the kernel configuration, you can do this as before with:
make menuconfig
Finally you can build the kernel (not forgetting
to run set.sh first):
Measuring voltages more accurately
In the fifth installment of this series[15] we showed how to
measure external voltages using the analog-to-digital converter in
the microcontroller. Naturally many readers tried this out, and the
unanimous response was that the 3.3V supply voltage on the board,
which is used as a reference by the converter, is not stable enough
to obtain accurate readings. There is a simple solution to this,
generating a more stable voltage by adding a 10F and a 100nF
capacitor in parallel to ground at one end of R19: see Figure18
and Figure19. This should give a noticeable improvement in the
accuracy of the conversions.
LPC_CAS
LPC_RAS
make zImage
C27
C26
C25
10u
100n
10n
+3V3
D5
D7
LPC_DQM1
sudo cp arch/arm/boot/zImage /media/
M9
C13
LPC_CS
Assuming the kernel builds without errors, it
can now be copied onto the Linux boards SD
card using an SD card reader connected to the
development machine. In the following command substitute the appropriate string for
SD_CARD_LABEL:
M7
C23
LPC_CKE
C22
10u
C17
100n
10n
E8
G12
L10
K11
+3V3
R19
10R
A13
C9
C6
100n
10u
VDDE_IOB
VSSE_IOB
VDDE_IOB
VSSE_IOB
VSSE_IOB
VDDE_IOC
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VDDE_IOC
VSSE_IOC
VSSE_IOC
VDDE_ESD
ADC10B_VDDA33
J5
GPA0
GPA3
4 PWM_DATA
GPA1
VSSE_IOC
ADC10B_GNDA
LED1
(Alternatively, if you are feeling brave, you can
try out the experimental version 3.3.0.) In the
directory create a suitable default configuration:
LED2
SD_CARD_LABEL/
Compile the modules using the command
make modules
and then install them in a temporary directory:
make modules_install INSTALL_MOD_PATH=/
tmp
You can now copy the modules you have built
from the temporary directory onto the SD card:
sudo cp -r /tmp/lib /media/SD_CARD_LABEL/
The driver is already included in the standard
configuration.
Tools are available for Linux to carry out simple I2C bus tests. These have to be downloaded
from the internet, unpacked and then written
onto the SD card.
First download the I2C tools to the development
PC
wget http://ftp.de.debian.org/debian/
pool/main/i/i2c-tools/i2c-tools_3.0.2-5_
armel.deb
49
Personal Download for Fernando Faria Corra | copyright Elektor
Projects
As before[6] we create the nodes in the file
system corresponding to the devices using the
mknod command. To be on the safe side, find
out the correct major number using:
+3V3
R3
C2
C1
100n
2u2
R4
cat /proc/devices
JP2
2k2
2k2
24
SV1
VDD
GPA0
GPA1
+3V3
I/O0.0
I/O0.1
GPA3
4 PWM_DATA
I2C_SCL
I2C_SDA
23
SPI_MOSI
SPI_MISO
22
SYSCLOCK_0
10 SPI_SCK
GPIO14
11
12 GPIO11
+3V3
13
14 GND
I/O0.2
SDA
I/O0.3
SCL
I/O0.4
I/O0.5
I/O0.6
I/O0.7
I/O0.0 2
I/O0.1 3
I/O0.2 4
I/O0.3 5
I/O0.4 6
I/O0.5 7
10
I/O0.6 8
11
I/O0.7 9
PCA9555D
+3V3
I/O1.0
I/O1.1
INT
I/O1.2
21
2
3
I/O1.3
A0
I/O1.4
A1
I/O1.5
A2
I/O1.6
VSS
I/O1.7
and check that the relevant entry reads 89. If
the value is different, the following commands
will need to be amended suitably:
mknod /dev/i2c-0 c 89 0
mknod /dev/i2c-1 c 89 1
I/O1.0 10
IC1
I/O1.1 11
13
JP6
14
15
I/O1.2 1
16
I/O1.3 2
17
I/O1.4 3
18
I/O1.5 4
19
I/O1.6 5
20
I/O1.7 6
12
+3V3
JP1 JP3
A0 7
A1 8
A2 9
INT 10
Address
Jumper
GND 11
R15
10k
Figure2.
Circuit for the PCA9555 port
expander.
120518 - 11
With the device nodes created and the software
installed, we can connect our first I2C device and
try to talk to it. Simple tests can be carried out
using a PCA9555 port expander chip, connected
as shown in Figure2 to the 14-pin expansion
header on the Linux board.
For the tests the device will need a 3.3V power
supply, which can be obtained from the expansion header. Jumpers can be fitted to inputs A0
to A2 of the port expander, allowing each to be
taken to 3.3V or to GND. The levels on these
pins determine the address of the device.
Having connected the chip to the SDA and SCL
signals on the Elektor Linux board, you can use
the tool i2cdetect to scan all the addresses on
the bus:
and then convert it to a standard archive:
i2cdetect 1
alien -t i2c-tools_3.0.2-5_armel.deb
The result is shown in Figure3. The address
0x6e is assigned to the LPC3131s interface itself
for when it is used as an I2C slave. Since all the
address selection pins of the PCA9555 are taken
high in our circuit, it responds to address 0x27.
We can continue to test the function of the I2C
bus by wiring three LEDs to port0 of the chip
(see Figure4). The command i2cset can be
used to send short I2C bus messages using the
command line. For example, to define all the port
expanders I/O pins as outputs, type
This will create a file in your current directory
with the name i2c-tools-3.0.2.tgz. Using the SD
card reader, copy this file onto the card:
cp i2c-tools-3.0.2.tgz /media/
SD_CARD_LABEL/
After rebooting the Linux board the converted
package archive has to be unpacked, and the I2C
device nodes need to be created. These actions
only have to be done once.
i2cset 1 0x27 0x06 0x00
First, then, we unpack the Linux I2C tools:
and then to set the outputs high type
cd /
i2cset 1 0x27 0x02 0xff
tar xvzf i2c-tools-3.0.2.tgz
and the result should be that the LEDs light.
Now that we have checked from the command
50
Personal Download for Fernando Faria Corra | copyright Elektor
embedded Linux
line that we can issue simple I2C commands, we
would like to move on to controlling the bus from
a program written inC.
I2C in C
The LPC3131 has two physically separate I2C
interfaces, of which only the second is made
available on the 14-pin expansion header on
the board.
The device files created above corresponding to
these interfaces are /dev/i2c-0 and /dev/i2c1. Accessing the hardware is done in the usual
way: open the device file and write to it or read
from it as needed.
Listing1 shows a short Cprogram that will light
our LEDs. The program can be compiled on the
development PC with the command
arm-linux-gnueabi-gcc -o i2ctest
i2ctest.c
and then the compiled code can be copied to the
Linux board, either via the SD card or using the
sshfs or scp tools. Alternatively, the program
can be compiled directly on the Linux board using
its built-in Ccompiler gcc.
It is best to copy the compiled program into the /
usr/bin directory so that it can be run from anywhere on the system without having to specify
a path explicitly:
cp i2ctest /usr/bin
The program can then be run on the board, giving
the device file for the I2C bus as its parameter:
buffer[0] = 0x06;
buffer[1] = 0x00;
The commands to turn the LEDs on and off are
enclosed in an infinite loop, with a 100ms pause
between each call.
Figure3.
Output of the i2cdetect
command.
Other devices on the I2C bus can be communicated with in the same way. Cexperts may want
to take a look at the libi2c library[7], which
provides a very flexible way of accessing the bus.
But under the hood this library also just uses the
same ioctl(), read() and write() functions.
Controlling serial ports from the
command line
In the fourth part of this series[6] we installed
a USB-to-RS232 converter. There are various
ways in which serial interfaces can be integrated
into a Linux system. For example, the microcontrollers built-in serial ports are usually accessed
as /dev/ttyS0, /dev/ttyS1 and so on, with the
default console, where system messages and
the initial login prompt appear, usually being /
i2ctest /dev/i2c-1
The LEDs on the expansion circuit should blink
on and off.
In the first part of the source code in Listing1
you can see how the open() function is used to
obtain a handle for the device file. The ioctl()
function, which is used to send configuration
information to the device driver, is then used
to set the address of the desired target device.
Once the address is set, the write() function is
then used to send a simple message over the
I2C bus to the device. The byte sequence to set
the port expanders pins to outputs is the same
as we used above:
Figure4.
Testing the circuit with
three LEDs.
51
Personal Download for Fernando Faria Corra | copyright Elektor
Projects
Listing1: Driving an I2C port expander
#include
#include
#include
#include
//prepare communication
if (ioctl(fd, I2C_SLAVE, slave_address) < 0) {
printf(ioctl I2C_SLAVE error);
return -1;
}
<stdio.h>
<fcntl.h>
<linux/i2c.h>
<linux/i2c-dev.h>
write(fd, buffer, 2);
//Slave Address
#define ADDR 0x27
int main (int argc, char **argv)
{
int fd;
char filename[32];
char buffer[128];
int n, err;
if (argc == 0) {
printf(usage: %s <device>\n, argv[0]);
exit(1);
}
sprintf(filename, argv[1]);
printf(device = %s\n, filename);
int slave_address = ADDR;
if ((fd = open(filename, O_RDWR)) < 0) {
printf(i2c open error);
return -1;
}
printf(i2c device = %d\n, fd);
//Port-0 GPIO as Output
buffer[0] = 0x06;
buffer[1] = 0x00;
write(fd, buffer, 2);
n = 0;
while (1)
{
buffer[0] = 0x02; /* command byte: write
output regs */
buffer[1] = 0x00; /* port1 data */
write(fd, buffer, 2);
usleep(100000);
buffer[0] = 0x02; /* command byte: write
output regs */
buffer[1] = 0xff; /* port1 data */
write(fd, buffer, 2);
printf(%d\n, n++);
usleep(100000);
}
}
dev/ttyS0. USB-to-serial interfaces appear as
/dev/ttyUSB0, /dev/ttyUSB1 and so on. Nevertheless, whatever type of serial interface we
use, the interface through which it is accessed
is always the same.
The way Linux treats devices as files gives us a
certain amount of platform independence: for
example, we can develop and simulate an application that uses a serial port on the development
PC, addressing the USB-to-serial converter as
/dev/ttyUSB0 just as we would on the Linux
board. Then it is an easy job to transfer the software from the PC to the Linux board by crosscompiling it and then running it supplied with the
correct parameters.
ing driver has already been loaded:
If a device file is missing it can be created using
mknod as before, assuming that the correspond-
However, we would like to be able to drive the
port without using a ready-made program. Send-
mknod /dev/usb/ttyUSB0 c 188 0
mknod /dev/usb/ttyUSB1 c 188 1
mknod /dev/usb/ttyUSB2 c 188 2
In the fourth part of this series we used microcom, a simple terminal emulator program for
communicating over serial ports. The desired
baud rate and device are given as parameters
to the command. When the program has been
started any received characters are displayed
immediately on the screen, and any characters
entered on the keyboard are transmitted on the
serial port.
52
Personal Download for Fernando Faria Corra | copyright Elektor
embedded Linux
ing and receiving data is not complicated: we can
simply use normal commands to write to and
read from the device file /dev/ttyUSB0. However, setting the baud rate of the port requires
using a special technique.
Under Linux the command we need is called stty.
For example, to set the port speed to 38400baud,
the following command must be issued:
stty -F /dev/ttyUSB0 38400
We can now send a simple string of characters
like this:
for this program. The first part of the program
sets up the basic configuration using the data
structure struct termios options. The function
tcgetattr() first writes the current configuration into the data structure so that it can subsequently be modified. The data rate is set using
the functions cfsetispeed() (for the input data
rate) and cfsetospeed() (for the output data
rate). At this point it is also possible to modify
the number of data bits, start bits and stop bits.
Finally the new configuration is put into action
by passing it to the driver using the function
tcsetattr().
echo Hello world > /dev/ttyUSB0
Sending and receiving data works in the same
way as with any other standard device. Using the
and receive characters like this:
function write() (in conjunction with a handle
previously obtained from a call to open()) you
can send data, and using read() you can receive
data. In the example here the connected device
is expecting a command six bytes in length: as
you can see the variable buffer[0] is set to
zero: this value is not so convenient to generate
from the keyboard.
cat < /dev/ttyUSB0
You may want to experiment a little with these
commands, which are more than adequate for
the manual testing of communications with simple protocols that only involve printable ASCII
characters. However, if non-printable characters
are involved things get more complicated, and
a short Cprogram will be an easier way to go.
Controlling serial ports
from a Cprogram
It is a relatively small step to move to using a
Cprogram to control a serial port. As we saw
above when driving the port from the command
line we must first configure the interface, the
most important configuration parameter being
the baud rate. In the download accompanying
this article[8] is a Cprogram (called Listing2)
that shows how things are done.
The examples from[9] were used as the model
Author Benedikt Sauter
in an Elektor interview.
There were lots of cameras
in action on the Farnell/
element14 stand at
electronica 2012. You can
watch the video at[13].
The program expects a reply from the connected
device, also six bytes in length. Reception is done
in a loop which assembles the six characters as
they are read in. The program waits at the function (a blocking call) for each character to arrive.
This is not the ideal solution, as it limits the data
rate to 19200baud. The microcontroller may be
fast and have little to do in the loop except wait
idly for the next character, but nevertheless we
soon run out of precious processor time.
The usual solution to this problem is to use an
interrupt. When a data byte is available the processor is notified and the character is fetched
from the receive buffer. Under an operating sys-
53
Personal Download for Fernando Faria Corra | copyright Elektor
Projects
Figure5.
RS485 connection to the
ElektorBus.
Figure6.
Elektor Linux board enclosure.
Figure7.
The expansion board that we will be describing in the
next edition.
tem like Linux this can result in a program that
makes efficient use of resources. If no data bytes
are available, the receiving program is free to
get on with something else or allow itself to be
descheduled; when characters arrive the program
is woken up to process the data.
The third program in the download directory[8]
uses interrupts in this way. It is essentially the
same as the example above, but includes a new
function signal_handler_IO() which is called
only when data bytes have been received.
the sixteen bytes that are to be sent. To control
relay1 on the ElektorBus board, send the following commands:
170,0, 0,5,0,10, 96,1,0,0, 0,0,0,0, 0,0
(relay on)
170,0, 0,5,0,10, 96,0,0,0, 0,0,0,0, 0,0
(relay off)
For relay2 the sequences are:
170,0, 0,5,0,10, 0,0,96,1, 0,0,0,0, 0,0
RS485 and the ElektorBus
(relay on)
Now that we have worked out how to drive a
serial port, we should also be able to send and
receive data over an RS485 interface. It should
just be a matter of replacing the USB-to-RS232
converter on our boards USB port with a USBto-RS485 converter. A suitable converter can be
ordered from Elektor (order code 110258-91)[8].
This device is based around an FTDI chip, which
already has a suitable driver in the kernel that
simply needs to be activated, as described in the
fourth installment of this series.
170,0, 0,5,0,10, 0,0,96,0, 0,0,0,0, 0,0
The remote device on the RS485 bus for this
experiment is an ElektorBus relay module[8]
[10], which includes two relays (Figure5). Listing2 needs to be modified slightly to implement
the ElektorBus protocol, for example to change
the transmission speed to 9600baud and the
variable LENGTH to16. Before sending a message the array Buffer must be populated with
(relay off)
More about the ElektorBus can be found at[11].
What the future holds
If you are looking for a suitable enclosure for
the Elektor Linux board (Figure6), the internet is your friend[12]. At the link you will find
a design that can be printed using a 3D printer.
The enclosure is designed not to require any
screws for assembly: the lid and the base simply snap together.
This is the last installment in our embedded Linux
course, but it is certainly not the end of the project. A new board (Figure7) is in the works to
extend the Elektor Linux board with many handy
functions:
54
Personal Download for Fernando Faria Corra | copyright Elektor
display (two rows of sixteen characters);
three buttons to provide a user interface or
menu control;
sixteen extra digital inputs and outputs;
a real-time clock with battery supply;
a buzzer to provide acoustic indications;
a prototyping area for additional circuitry.
Internet links
[1]sauter@embedded-projects.net
[2]www.gnublin.org
[3]www.elektor-magazine.com/120180
[4]www.elektor-magazine.com/120578
[5]http://en.gnublin.org/index.php/Eclipse
[6]www.elektor-magazine.com/120181
If you want to learn more about this extension
board, please go to
(120518)
www.elektor.com/120596-91.
[7]http://opensource.katalix.com/libi2c/
[8]www.elektor-magazine.com/120518
[9]www.tldp.org/HOWTO/Serial-Programming-HOWTO/
[10]www.elektor-magazine.com/110727
[11]www.elektor-magazine.com/elektorbus
[12]www.thingiverse.com/thing:29314
[13]www.element14.com/community/community/events/electronica
[14]http://eagleup.wordpress.com/
[15]www.elektor-magazine.com/120182
Advertisement
Take out a FREE
membership to
Elektor.POST
The latest on electronics and
information technology
Videos, hints, tips, offers and more
Exclusive bi-weekly project for
GREEN and GOLD members only
Elektor behind the scenes
In your email inbox each Friday
Register today at www.elektor.com/newsletter
55
Personal Download for Fernando Faria Corra | copyright Elektor