nodecum - learning

Language selector

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.

Transfer the program