Sie sind auf Seite 1von 55

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

Das könnte Ihnen auch gefallen