Blinky
Blinky is the “Hello World” sample program of the embedded world. Most embedded Devices have no display for writing a message, but a led which could be turned on and off is almost always a part of a development board.
Program files
We start with the code given from the <prog/blinky> directory.
prog/blinky
├── CMakeLists.txt
├── prj.conf
└── src
└── main.c
CMakeLists.txt
1cmake_minimum_required(VERSION 3.20.0)
2find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
3project(blinky)
4target_sources(app PRIVATE src/main.c)
This file contains the information where to find the sources of the project.
Here we have only one source file src/main.c
prj.conf
1CONFIG_GPIO=y
The project configuration file tells which components of the kernel are needed
and linked into the firmware.
Here we need the GPIO
- General Purpose In/Out Subsystem.
main.c
main.c
is the name of the main source file of our program which
is written in the C programming language.
1/*
2 * Copyright (c) 2016 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
This is the copyright remark. In C the /*
starts an comment which
is closed by an corresponding */
. This kind of comments can
span over multiple lines whereas an //
double slash gives room for
comments until the line ends.
6#include <zephyr.h>
7#include <drivers/gpio.h>
All keywords in C which start with #
are preprocessor
directives. #include <file.h>
loads an so called
header file which contains the interface of declarations
we will make use of. If <
and >
are used to surround
the filename, the path to the file will be searched by the preprocessor/compiler
using the given include pathes. In opposite, if the file is
enclosed with "
, then the path is relative to the file which
contains the include directive.
8/* 1000 msec = 1 sec */
9#define SLEEP_TIME_MS 1000
10
11/* The devicetree node identifier for the "led0" alias. */
12#define LED0_NODE DT_ALIAS(led0)
Here again we have a preprocessor directive. #define PLACEHOLDER value
creates an
macro which simply replaces all occurences of PLACEHOLDER
with the given value
.
Usually the placeholders are writen with capitals, but this is not mandatory. It is often
made this way for distinct macros from functions and variables which are often written with
lower case letters.
Macros can also be defined to have arguments. Here we use such a macro DT_ALIAS
with argument led0
as value for our macro LED0_NODE
.
led0
is an alias which has to be defined for the board we use and describes on
which port and pin the led 0 (Light Emiting Diode) is connected.
13/*
14 * A build error on this line means your board is unsupported.
15 * See the sample documentation for information on how to fix this.
16 */
17static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
This C declaration declares an identifier led0
for a fixed (static
) portion of
read only (const
) memory which can contain structured data (struct
) defined
with the name gpio_dt_spec
and initialized with the result of the macro call
GPIO_DT_SPEC_GET(LED0_NODE, gpios)
.
18void main(void)
19{
Here we define our main routine of the program, void main(void)
is the so called entry
point which will be called to start the application. void
designates an no value thus this
function will not return a value and will not have any argument. Writing void main()
is
equvalent to the former form with an void
argument. The body of the function will be enclosed
in the curly brackets {}
.
20 int ret;
This line says that we reserve memory for holding an integer - int
value which can be accessed with name ret
. This is uninitialized
memory, so no specific value was written there up to now.
21 if (!device_is_ready(led.port)) {
22 return;
23 }
Here we have a conditional clause called if
. If the condition in
the parentheses is true, then the statements following the curly braces
are executed.
Conditionals are described on page 79 in the C Programming Language.
Zephyr follows at least the C99 specification which has added the support
of boolean values with the type bool
to the language.
(See C Language Support)
The exclamation mark !
is a negation operator,
this will result to the behavior that
if the function device_is_ready
returns false, which means that the
device is not ready for use, than the directly following statement ( here
the block { return; }
will be executed.
See here for the description of the device_is_ready
function.
return
will leave our main
function, thus will end our program.
This means, in case that the device could not be initialized correctly,
our program will be terminated here.
24 ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
25 if (ret < 0) {
26 return;
27 }
Calling gpio_pin_configure_dt
and store the integer result into ret
.
Looking up the documentation of gpio_pin_configure_dt
in
GPIO Driver APIs shows that the function returns zero on success and negative
values which describe the occured error conditions. GPIO_OUTPUT_ACTIVE
configures the specified GPIO pin as output and initializes it to a logic 1.
Again the program will be terminated by calling return
if an error has occured.
28 while (1) {
29 ret = gpio_pin_toggle_dt(&led);
30 if (ret < 0) {
31 return;
32 }
33 k_msleep(SLEEP_TIME_MS);
34 }
The while
keyword describes a ‘while’ loop, if the condition in the braces ( 1 )
is true ( not zero ), the following statement, { ... }
will be executed.
Here the condition is always true, thus at the first glance an never ending
loop was programmed. But if we look further we see that if gpio_pin_toggle_dt
will return an error condition (ret < 0
), return
will be called which causes
the termination of the main
function.
Calling gpio_pin_toggle_dt
will vary the state of the pin between high and low,
lighting the LED or switching it off.
k_msleep
puts the current thread to sleep for the time given in milliseconds.
This means that the execution will be stoped for the given time. After this the pin
will be toggled again and so on.
35}
The closing brace of the body of the main
function declaration.
Here is the complete listing of the program:
1 /*
2 * Copyright (c) 2016 Intel Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <zephyr.h>
7 #include <drivers/gpio.h>
8 /* 1000 msec = 1 sec */
9 #define SLEEP_TIME_MS 1000
10
11 /* The devicetree node identifier for the "led0" alias. */
12 #define LED0_NODE DT_ALIAS(led0)
13 /*
14 * A build error on this line means your board is unsupported.
15 * See the sample documentation for information on how to fix this.
16 */
17 static const struct gpio_dt_spec led = GPIO_DT_SPEC_GET(LED0_NODE, gpios);
18 void main(void)
19 {
20 int ret;
21 if (!device_is_ready(led.port)) {
22 return;
23 }
24 ret = gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
25 if (ret < 0) {
26 return;
27 }
28 while (1) {
29 ret = gpio_pin_toggle_dt(&led);
30 if (ret < 0) {
31 return;
32 }
33 k_msleep(SLEEP_TIME_MS);
34 }
35 }
Building the program
First we set the build environment variables according to Setting Environment Variables.