From b09fbc9f735fc28ebcf88c57c68470486187c6b1 Mon Sep 17 00:00:00 2001 From: Blake Hildebrand Date: Tue, 9 Jan 2024 08:28:31 -0500 Subject: [PATCH] content: publish zephyr basics article Also tweaks the title and adds some grammar changes. --- .../2024-01-10-practical_zephyr_basics.md | 124 +++++++++--------- 1 file changed, 62 insertions(+), 62 deletions(-) rename _drafts/practical_zephyr_00_basics.md => _posts/2024-01-10-practical_zephyr_basics.md (71%) diff --git a/_drafts/practical_zephyr_00_basics.md b/_posts/2024-01-10-practical_zephyr_basics.md similarity index 71% rename from _drafts/practical_zephyr_00_basics.md rename to _posts/2024-01-10-practical_zephyr_basics.md index e63b937ff..e88f9f0a6 100644 --- a/_drafts/practical_zephyr_00_basics.md +++ b/_posts/2024-01-10-practical_zephyr_basics.md @@ -1,6 +1,6 @@ --- -title: Practical Zephyr - Series start and Zephyr basics -description: Article series "Practical Zephyr", first part about West, CMake and Zephyr application types. +title: Practical Zephyr - Zephyr Basics (Part 1) +description: Article series "Practical Zephyr", the first part about West, CMake, and Zephyr application types. author: lampacher --- @@ -12,19 +12,19 @@ This is the start of a new article series about _Zephyr's basics_: It will walk So why another article series, and what's different in this one? -When I first started looking into Zephyr the learning curve felt quite overwhelming. Even as an embedded developer with a decent amount of experience, understanding key concepts and tools like _Kconfig_ or _devicetree_ for creating even a simple application, had me chasing down the rabbit hole, following link after link after link, night after night - and it was exhausting. Personally, existing article series felt as if they would only brush over core concepts like _Kconfig_ and _devicetree_ and would leave me with more questions than answers; I still didn't understand those tools since I had no previous experience whatsoever with them. +When I first started looking into Zephyr, the learning curve felt overwhelming. Even as an embedded developer with a decent amount of experience, understanding key concepts and tools like _Kconfig_ or _devicetree_ for creating even a simple application had me chasing down the rabbit hole, following link after link after link, night after night - and it was exhausting. The existing article series would only brush over core concepts like _Kconfig_ and _devicetree_ and leave me with more questions than answers; I still needed to understand those tools since I had no previous experience with them. What I'm trying to achieve with this article series is the following: -If you're working a full-time job and would still like to get started with Zephyr, but don't have the energy to set up your environment, to dive deeper into the docs, this article series will guide you through the __Zephyr basics__. Of course, you'll learn most if you follow along with programming, but all code, including snippets of generated code and build logs, are included in the articles of this series. Thus, even just reading this series should give you a good idea about how Zephyr works. +If you're working a full-time job and would still like to get started with Zephyr but don't have the energy to set up your environment to dive deeper into the docs, this article series will guide you through the __Zephyr basics__. Of course, you'll learn most if you follow along with programming, but all code, including snippets of generated code and build logs, are included in the articles of this series. Thus, even just reading this series should give you a good idea about how Zephyr works. -After this series, you should understand Zephyr's build and configuration tools. The learning curve flattens, and you'll be able to continue learning without getting lost in Zephyr's documentation. +After this series, you should understand Zephyr's build and configuration tools. The learning curve flattens, and you can continue learning without getting lost in Zephyr's documentation. -> **Note:** This series is **not** for you if you're not interested in details and how things work, but rather just want to _use_ Zephyr. We'll be going through _generated code and configuration files_, and we'll be learning _devicetree_ from first principles! Also, if you're experienced with Linux and already know how _Kconfig_ and _devicetree_ work, you might find that the articles of this series are just not for you. +> **Note:** This series is **not** for you if you're not interested in details and how things work but instead want to _use_ Zephyr. We'll go through _generated code and configuration files and learn _devicetree_ from first principles! Also, if you're experienced with Linux and already know how _Kconfig_ and _devicetree_ work, the articles of this series are just not for you. {% include newsletter.html %} @@ -36,23 +36,23 @@ As mentioned, this series covers "_Zephyr's basics_", so what is it that we'll e __Setup__ -First, we walk through the very basics of installing Zephyr and creating a minimal, skeleton application. We'll see how we can compile, flash, and debug an application skeleton, and how Zephyr's _West_ tool fits into all of this. We'll improve this setup in a later article, where we'll leverage the true potential of _West_. +First, we walk through the basics of installing Zephyr and creating a minimal skeleton application. We'll see how to compile, flash, and debug an application skeleton and how Zephyr's _West_ tool fits into this. We'll improve this setup in a later article, leveraging the true potential of _West_. __Kconfig__ -In our skeleton application, we'll already stumble upon the configuration tool [Kconfig](https://docs.zephyrproject.org/latest/build/kconfig/index.html). We'll now see how it works and how to explore, modify, and persist so-called _Kconfig symbols_, and will create our own application-specific symbol. Since _Kconfig_ is also used to configure build settings, we'll see how to use _Kconfig_ for specifying _build types_. +In our skeleton application, we'll already stumble upon the configuration tool [Kconfig](https://docs.zephyrproject.org/latest/build/kconfig/index.html). We'll now see how it works and how to explore, modify, and persist so-called _Kconfig symbols_, and will create our application-specific symbol. Since _Kconfig_ is also used to configure build settings, we'll see how to use _Kconfig_ for specifying _build types_. __Devicetree basics__ -Zephyr uses a tree data structure called _devicetree_ to describe hardware: All supported microcontrollers and boards are represented by a set of _devicetree sources_, which use their own syntax. First, we'll skim through some devicetree files in Zephyr, and then create our own files to explore the devicetree _syntax_. +Zephyr uses a tree data structure called _devicetree_ to describe hardware: All supported microcontrollers and boards are represented by a set of _devicetree sources_, which use their syntax. First, we'll skim through some device tree files in Zephyr and then create our files to explore the devicetree _syntax_. __Devicetree bindings__ -After understanding the _devicetree_ syntax, we'll now use so-called _bindings_ to add _semantics_ and therefore _meaning_ to our devicetree. Only via devicetree _bindings_, Zephyr knows how to translate the provided devicetree into source code. Creating bindings for our own nodes, we'll look at the generated output, and finally how we can access it via the Zephyr devicetree API. +After understanding the _devicetree_ syntax, we'll now use so-called _bindings_ to add _semantics_ and, therefore _meaning_ to our devicetree. Only via devicetree _bindings_, Zephyr knows how to translate the provided devicetree into source code. Creating bindings for our nodes, we'll look at the generated output, and finally how we can access it via the Zephyr devicetree API. __Workspaces__ -Throughout this article series, for the sake of simplicity, we'll mostly use so-called _freestanding_ applications: Such applications require Zephyr's sources to be installed outside of your application. For professional applications, Zephyr's _West_ tool supports so-called _workspaces_: In workspaces, _all_ sources, their location, and their versions are specified in a _manifest_ file that is used by _West_ to populate the workspace, eliminating references to files outside of the workspace. We'll see how we can create a _minimal_ workspace application using _West_. +Throughout this article series, for the sake of simplicity, we'll mostly use so-called _freestanding_ applications. Such applications require Zephyr's sources to be installed outside of your application. For professional applications, Zephyr's _West_ tool supports so-called _workspaces_: In workspaces, _all_ sources, their location, and their versions are specified in a _manifest_ file that is used by _West_ to populate the workspace, eliminating references to files outside of the workspace. We'll see how to create a _minimal_ workspace application using _West_. This is where the journey **ends**. After this series, you should find it easy - or at least easier - to explore, understand, and apply advanced concepts such as: @@ -70,50 +70,50 @@ This is where the journey **ends**. After this series, you should find it easy - ## Prerequisites -In general, you should be familiar with embedded software since we won't cover the basics. Some knowledge in using build systems such _CMake_ doesn't hurt but is not necessary. In case you don't know at all what Zephyr is, have a short look at the [introduction in the official documentation](https://docs.zephyrproject.org/latest/introduction/index.html). +You should be familiar with embedded software since we won't cover the basics. Some knowledge in using build systems such _CMake_ doesn't hurt but is unnecessary. If you don't know what Zephyr is, look briefly at the [introduction in the official documentation](https://docs.zephyrproject.org/latest/introduction/index.html). -The practical examples in this series use [Nordic's](https://www.nordicsemi.com/) [nRF52840 development kit](https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dk). In case you don't have such a board, don't worry. You should be able to follow along using most of the boards in [Zephyr's long list of supported boards](https://docs.zephyrproject.org/latest/boards/index.html) - or using an emulation target, though we won't cover using emulation targets in this article series. Most of the content of this series does not require any hardware at all, we'll just _compile_ the project for a specific board for simplicity. +The practical examples in this series use [Nordic's](https://www.nordicsemi.com/) [nRF52840 development kit](https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dk). If you don't have such a board, don't worry. You should be able to follow along using most of the boards in [Zephyr's long list of supported boards](https://docs.zephyrproject.org/latest/boards/index.html) - or using an emulation target. However, we won't cover using emulation targets in this article series. Most of the content of this series does not require any hardware; we'll just _compile_ the project for a specific board for simplicity. -Whenever relevant, we'll use [Visual Studio Code](https://code.visualstudio.com/) as the editor of choice. However, **using `vscode` is of course entirely optional** and not required at all. Only in certain sections, e.g., debugging, we'll show how using `vscode` and some plugins can make your life easier - and we won't cover any other editor. +Whenever relevant, we'll use [Visual Studio Code](https://code.visualstudio.com/) as the editor of choice. However, **using `vscode` is, of course, entirely optional** and not required at all. Only in certain sections, e.g., debugging, we'll show how using `vscode` and some plugins can make your life easier - and we won't cover any other editor. -We won't assume any type of operating system, but - for the sake of simplicity - we'll be exclusively using _Linux_ shell commands. Windows users should have a "`bash`-compatible" environment - or _WSL_ - installed. We won't jump through the hoops of creating Windows-compatible shell scripts, batch files, or other atrocities. +We won't assume any operating system, but - for the sake of simplicity - we'll be exclusively using _Linux_ shell commands. Windows users should have a "`bash`-compatible" environment - or _WSL_ - installed. We won't jump through the hoops of creating Windows-compatible shell scripts, batch files, or other atrocities. ## Installation -Let's get started - with a shortcut. Installation and toolchain management is - and probably always will be - a very biased topic. This article series is not about solving that problem. Therefore, I'm deliberately choosing a shortcut: We'll download "all things Zephyr" using Nordic's [toolchain manager](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/installation/assistant.html#install-toolchain-manager) instead of following the complete installation procedure. This is not required, but convenient. +Let's get started - with a shortcut. Installation and toolchain management is - and probably always will be - a very biased topic. This article series is about something other than solving that problem. Therefore, I'm deliberately choosing a shortcut: We'll download "all things Zephyr" using Nordic's [toolchain manager](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/installation/assistant.html#install-toolchain-manager) instead of following the complete installation procedure. This is optional but convenient. -Instead of using Nordic's full-blown IDE solution, however, we'll create our own shell script that sets up our terminal. This demonstrates some of the environment variables used by Zephyr and allows you to use a plain terminal to follow along. +Instead of using Nordic's full-blown IDE solution, we'll create our shell script to set up our terminal. This demonstrates some of the environment variables used by Zephyr and allows you to use a plain terminal to follow along. > **Note:** In case you want a "Zephyr only" installation without downloading Nordic's software, feel free to follow the installation procedure in the [official documentation](https://docs.zephyrproject.org/latest/develop/getting_started/index.html) - and I'll meet you again in the [next section](#creating-an-empty-application-skeleton)! -As mentioned in the introduction, later in this series we'll see a much better approach where Zephyr's sources will be pulled into our _workspace_ rather than separately installed: The only thing you'll need are the host tools documented in Zephyr's [getting started guide](https://docs.zephyrproject.org/latest/develop/getting_started/index.html#install-dependencies). For now, I'm using Nordic's toolchain installer simply to have a running setup that we can use to explore Zephyr's sources and tools. +As mentioned in the introduction, later in this series, we'll see a much better approach where Zephyr's sources will be pulled into our _workspace_ rather than separately installed: The only thing you'll need are the host tools documented in Zephyr's [getting started guide](https://docs.zephyrproject.org/latest/develop/getting_started/index.html#install-dependencies). For now, I'm using Nordic's toolchain installer simply to have a running setup that we can use to explore Zephyr's sources and tools. ### Setup using Nordic's toolchain manager -Nordic provides the [nRF Connect SDK](https://www.nordicsemi.com/Products/Development-software/nrf-connect-sdk) for its microcontrollers. This SDK is based on Zephyr and therefore its own version of it (plus some vendor-specific extras), but instead of having to go through all installation steps of the [getting started guide](https://docs.zephyrproject.org/latest/develop/getting_started/index.html), it comes with a very handy [toolchain manager](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/installation/assistant.html#install-toolchain-manager) to manage different versions of the SDK and thus also Zephyr. We'll also use it as a reference when checking the necessary steps to set up a Zephyr environment. +Nordic provides the [nRF Connect SDK](https://www.nordicsemi.com/Products/Development-software/nrf-connect-sdk) for its microcontrollers. This SDK is based on Zephyr and, therefore, its version of it (plus some vendor-specific extras). Still, instead of having to go through all the installation steps of the [getting started guide](https://docs.zephyrproject.org/latest/develop/getting_started/index.html), it comes with a very handy [toolchain manager](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/installation/assistant.html#install-toolchain-manager) to manage different versions of the SDK and thus also Zephyr. We'll also use it as a reference when checking the necessary steps to set up a Zephyr environment. -The nRF Connect SDK also comes with a [`vscode` extension pack](https://nrfconnect.github.io/vscode-nrf-connect/): We'll see several of the extensions throughout this series, but won't use the fully integrated solution since we want to understand what's going on underneath the hood. Go ahead and: +The nRF Connect SDK also comes with a [`vscode` extension pack](https://nrfconnect.github.io/vscode-nrf-connect/): We'll see several of the extensions throughout this series but won't use the fully integrated solution since we want to understand what's going on underneath the hood. Go ahead and: - Follow the [toolchain manager's instructions](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/installation/assistant.html#install-toolchain-manager) to install the current toolchain, - _[optional]_ install the [nRF Kconfig extension for `vscode`](https://marketplace.visualstudio.com/items?itemName=nordic-semiconductor.nrf-kconfig), - _[optional]_ install the [nRF DeviceTree extension for `vscode`](https://marketplace.visualstudio.com/items?itemName=nordic-semiconductor.nrf-kconfig). -- If you're planning to follow along using a board from Nordic, install the [command line tools](https://www.nordicsemi.com/Products/Development-tools/nrf-command-line-tools). +- If you plan to follow along using a board from Nordic, install the [command line tools](https://www.nordicsemi.com/Products/Development-tools/nrf-command-line-tools). > **Note:** If you like Nordic's [nRF Connect for `vscode` extension pack](https://nrfconnect.github.io/vscode-nrf-connect/), you can now also skip the _toolchain manager_ installation step and install the toolchain straight from within `vscode`. -If you're not convinced and want to install the SDK manually, you can also have a look at the steps performed in the [Dockerfile](https://github.com/lmapii/practical-zephyr/blob/main/builder.Dockerfile) in the [accompanying GitHub repository](https://github.com/lmapii/practical-zephyr). Notice that this _Dockerfile_ is tailored to the needs of this article series and is _not_ a generic solution. If you're looking for something more generic, have a look at [Zephyr's docker-image repository](https://github.com/zephyrproject-rtos/docker-image). +If you're not convinced and want to install the SDK manually, you can also have a look at the steps performed in the [Dockerfile](https://github.com/lmapii/practical-zephyr/blob/main/builder.Dockerfile) in the [accompanying GitHub repository](https://github.com/lmapii/practical-zephyr). Notice that this _Dockerfile_ is tailored to the needs of this article series and is _not_ a generic solution. If you want something more generic, look at [Zephyr's docker-image repository](https://github.com/zephyrproject-rtos/docker-image). ### Loading the development environment -After installing the toolchain manager you can install different versions of the nRF Connect SDK. E.g., my current list of installations looks as follows: +After installing the toolchain manager, you can install different versions of the nRF Connect SDK. E.g. my current list of installations looks as follows: ![]({% img_url practical-zephyr/nrf-toolchain-mgr.png %}) -However, when trying to use any of Zephyr's tools in your normal command line, you should notice that the commands cannot be located yet. E.g., this is the output of my `zsh` terminal when trying to execute [Zephyr's meta-tool _West_](https://docs.zephyrproject.org/latest/develop/west/index.html) (we'll see the tool later): +However, when using Zephyr's tools in your normal command line, you should notice that the commands cannot be located yet. E.g., this is the output of my `zsh` terminal when trying to execute [Zephyr's meta-tool _West_](https://docs.zephyrproject.org/latest/develop/west/index.html) (we'll see the tool later): ```zsh $ west --version @@ -123,24 +123,24 @@ zsh: command not found: west As you can see in the above screenshot of the [toolchain manager](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/installation/assistant.html#install-toolchain-manager), Nordic gives you several options to launch your development environment with all paths set up correctly: - Open a terminal, -- generate your own `env.sh` which you can then `source` in your terminal of choice, -- open a dedicated `vscode` instance set up for the chosen SDK version. +- generate your own `env.sh`, which you can then `source` in your terminal of choice, +- open a dedicated `vscode` instance for the chosen SDK version. -Since we want to have a better idea of what's going on underneath it all, we'll use *none* of the above options. Instead, we create our own `setup.sh` script which we base on the `env.sh` that the toolchain manager generates for you. +Since we want a better idea of what's happening underneath it all, we'll use *none* of the above options. Instead, we create our own `setup.sh` script based on the `env.sh` the toolchain manager generates for you. ### Analyzing the `env.sh` script provided by Nordic -First, go ahead and generate an `env.sh` script using the toolchain manager. At the time of writing, the script configures the following variables when `source`d: +First, generate an `env.sh` script using the toolchain manager. At the time of writing, the script configures the following variables when `source`d: - `PATH` is extended by several `bin` folders containing executables used by the nRF Connect SDK (and Zephyr). -- `GIT_EXEC_PATH` and `GIT_TEMPLATE_DIR` are set to the versions used by the SDK. These environment variables are used by Git for [sub-commands](https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables), and the default repository setup when using [templates](https://git-scm.com/docs/git-init#_template_directory), e.g., for the initial content of your `.gitignore`. +- `GIT_EXEC_PATH` and `GIT_TEMPLATE_DIR` are set to the versions used by the SDK. Git uses these environment variables for [sub-commands](https://git-scm.com/book/en/v2/Git-Internals-Environment-Variables) and the default repository setup when using [templates](https://git-scm.com/docs/git-init#_template_directory), e.g., for the initial content of your `.gitignore.` - `ZEPHYR_TOOLCHAIN_VARIANT`, `ZEPHYR_SDK_INSTALL_DIR`, and `ZEPHYR_BASE` are Zephyr-specific environment variables which we'll explain just now. ### Creating a setup script -Instead of using the provided `env.sh` script, we'll use a stripped-down version of that script to explore the ecosystem and also to get to know the most important environment variables. You can find the complete [setup script](https://github.com/lmapii/practical-zephyr/blob/main/setup.sh) in the root folder of the [accompanying GitHub repository](https://github.com/lmapii/practical-zephyr). +Instead of using the provided `env.sh` script, we'll use a stripped-down version to explore the ecosystem and get to know the most important environment variables. You can find the complete [setup script](https://github.com/lmapii/practical-zephyr/blob/main/setup.sh) in the root folder of the [accompanying GitHub repository](https://github.com/lmapii/practical-zephyr). After installing a toolchain with the [toolchain manager](https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/nrf/installation/assistant.html#install-toolchain-manager) you should see a tree similar to the following in your installation directory, in my case `/opt/nordic/ncs`: @@ -148,8 +148,8 @@ After installing a toolchain with the [toolchain manager](https://developer.nord tree /opt/nordic/ncs --dirsfirst -L 2 -I downloads -I tmp /opt/nordic/ncs ├── toolchains -│   ├── 4ef6631da0 -│   └── toolchains.json +│ ├── 4ef6631da0 +│ └── toolchains.json └── v2.4.0 ├── bootloader ├── modules @@ -210,27 +210,27 @@ Instead of setting `ZEPHYR_BASE` manually as, e.g., done by the `env.sh` script The final command, `west zephyr-export`, is a ["Zephyr extension command"](https://docs.zephyrproject.org/latest/develop/west/zephyr-cmds.html#installing-cmake-packages-west-zephyr-export) that installs the required _CMake_ packages that we'll need in the next steps when using `find_package` to locate Zephyr's sources in a `CMakeLists.txt` file. -That's it for the environment, we can now go ahead and create applications. One last time I'd like to emphasize that this was just a warm-up to get started with Zephyr. It is not required or recommended to use such a `setup.sh` script, it's just one of many possibilities you have when bringing up your development environment. +That's it for the environment; we can now create applications. One last time, this was just a warm-up to start with Zephyr. It is not required or recommended to use such a `setup.sh` script; it's just one of many possibilities you have when bringing up your development environment. ## Creating an empty application skeleton -Now that we have a working installation, we can start with the important parts of this article series. Before creating our first files, let's review Zephyr's supported application _types_. +Now that we have a working installation, we can start with the important parts of this article series. Before creating our first files, review Zephyr's supported application _types_. ### Zephyr application types -Zephyr supports different [application types](https://docs.zephyrproject.org/latest/develop/application/index.html#application-types), differentiated by the location of your application in relation to Zephyr's sources: +Zephyr supports different [application types](https://docs.zephyrproject.org/latest/develop/application/index.html#application-types), differentiated by the location of your application in relation to Zephyr's sources -- [Freestanding applications](https://docs.zephyrproject.org/latest/develop/application/index.html#zephyr-freestanding-app) exist independent of Zephyr's location, meaning Zephyr is not part of the application's repository or file tree. The relation with Zephyr is only established in the build process, when using `find_package` to locate Zephyr, typically based on the `ZEPHYR_BASE` environment variable. -- [Workspace applications](https://docs.zephyrproject.org/latest/develop/application/index.html#zephyr-workspace-app) use a [_West_ workspace](https://docs.zephyrproject.org/latest/develop/west/workspaces.html#west-workspaces): For such applications, _West_ is used to initialize your workspace, e.g., based on a `west.yml` manifest file. The manifest file is used by _West_ to create complex workspaces: For each external dependency that is used in your project, the location and revision are specified in the manifest file. West then uses this manifest to populate your workspace. Kind of like a very, very powerful version of [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). +- [Freestanding applications](https://docs.zephyrproject.org/latest/develop/application/index.html#zephyr-freestanding-app) exist independent of Zephyr's location, meaning Zephyr is not part of the application's repository or file tree. The relation with Zephyr is only established in the build process when using `find_package` to locate Zephyr, typically based on the `ZEPHYR_BASE` environment variable. +- [Workspace applications](https://docs.zephyrproject.org/latest/develop/application/index.html#zephyr-workspace-app) use a [_West_ workspace](https://docs.zephyrproject.org/latest/develop/west/workspaces.html#west-workspaces): For such applications, _West_ is used to initialize your workspace, e.g., based on a `west.yml` manifest file. _ West _ uses the manifest file to create complex workspaces: The location and revision are specified in the manifest file for each external dependency used in your project. West then uses this manifest to populate your workspace—like a very, very powerful version of [Git submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules). - [Zephyr repository applications](https://docs.zephyrproject.org/latest/develop/application/index.html#zephyr-repo-app) live within the Zephyr repository itself, e.g., demo applications that are maintained with Zephyr. You could add applications in a fork of the Zephyr repository, but this is not very common. -The most common and easiest approach is to **start** with a freestanding application: You'll only need a couple of files to get started. As with the nRF Connect SDK, Zephyr is located in a known installation directory that is configured via the environment. +The most common and easiest approach is to **start** with a freestanding application: You'll only need a couple of files to get started. As with the nRF Connect SDK, Zephyr is configured via the environment in a known installation directory. -In more advanced projects you typically want to have all dependencies in your workspace and will therefore switch to a workspace application type: External dependencies will be cloned directly into your project by _West_ to avoid depending on separate local installations. We'll get to that at the end of this article series. +In more advanced projects, you typically want all dependencies in your workspace. You will, therefore, switch to a workspace application type: External dependencies will be cloned directly into your project by _West_ to avoid depending on separate local installations. We'll get to that at the end of this article series. -For now, we'll rely entirely on the [freestanding application type](https://docs.zephyrproject.org/latest/develop/application/index.html#zephyr-freestanding-app). +We'll rely entirely on the [freestanding application type](https://docs.zephyrproject.org/latest/develop/application/index.html#zephyr-freestanding-app). ### A skeleton freestanding application @@ -245,7 +245,7 @@ $ tree --charset=utf-8 --dirsfirst └── prj.conf ``` -One thing that you should notice immediately, is that we're using a [CMakeLists.txt](CMakeLists.txt). This is because Zephyr's build system is based on [CMake](https://cmake.org/). You've read that right: While Zephyr provides its own meta-tool _West_, which uses yet another meta build system _CMake_, which will in turn, e.g., use `make` or `ninja`. +You should immediately notice that we're using a [CMakeLists.txt](CMakeLists.txt). Zephyr's build system is based on [CMake](https://cmake.org/). You've read that right: Zephyr provides its own meta-tool _West_, which uses yet another meta build system _CMake_, which will, in turn, e.g., use `make` or `ninja`. > **Note:** You can find the complete application skeleton, including some additional comments, in the [`00_basics` folder of the accompanying GitHub repository](https://github.com/lmapii/practical-zephyr/tree/main/00_basics). @@ -253,17 +253,17 @@ One thing that you should notice immediately, is that we're using a [CMakeLists. [West](https://docs.zephyrproject.org/latest/develop/west/index.html) is Zephyr's "Swiss army knife tool": It is mainly used for managing multiple repositories but also provides [Zephyr extension commands](https://docs.zephyrproject.org/latest/develop/west/zephyr-cmds.html) for building, flashing, and debugging applications. [CMake](https://cmake.org/), on the other hand, is used exclusively for building your application. -Since we want to learn how things work underneath, for the following steps we'll be using _CMake_ first, and circle back to _West_ after exploring the build process. +Since we want to learn how things work underneath, for the following steps, we'll be using _CMake_ first and circle back to _West_ after exploring the build process. #### Kconfig -In our application skeleton, there is also a very suspicious [prj.conf](prj.conf) file. This file must exist for any application, even if empty, and is used by the [Kconfig configuration system](https://docs.zephyrproject.org/latest/build/kconfig/index.html). We'll go into detail about [Kconfig](https://docs.zephyrproject.org/latest/build/kconfig/index.html) (and *devicetree*) later in this series. +Our application skeleton also has a very suspicious [prj.conf](prj.conf) file. This file must exist for any application, even if empty, and is used by the [Kconfig configuration system](https://docs.zephyrproject.org/latest/build/kconfig/index.html). We'll go into detail about [Kconfig](https://docs.zephyrproject.org/latest/build/kconfig/index.html) (and *devicetree*) later in this series. -For now, simply keep in mind that we'll be using this configuration file to tell the build system which modules and which features we want to use in our application. Anything that we don't plan on using will be deactivated, decreasing the size of our application and reducing compilation times. +For now, simply keep in mind that we'll be using this configuration file to tell the build system which modules and which features we want to use in our application. Anything we don't plan on using will be deactivated, decreasing the size of our application and reducing compilation times. #### Sources -Aside from our build and configuration file, we've created a still empty [`src/main.c`](https://github.com/lmapii/practical-zephyr/blob/main/00_basics/src/main.c) file. The convention is to place all sources in this `src` folder, but as your application grows you might need a more complex setup. For now, that'll do. +Aside from our build and configuration file, we've created a still empty [`src/main.c`](https://github.com/lmapii/practical-zephyr/blob/main/00_basics/src/main.c) file. The convention is to place all sources in this `src` folder, but you might need a more complex setup as your application grows. For now, that'll do. @@ -285,11 +285,11 @@ void main(void) } ``` -Notice that this is a plain infinite loop and it doesn't make any use of threads yet - something that you'll definitely change in a more complex application: In the end, it makes limited sense to use Zephyr without its operating system. For now keep in mind that [applications _can_ operate without threads](https://docs.zephyrproject.org/latest/kernel/services/threads/nothread.html#nothread), though this severely limits the functionality provided by Zephyr. +Notice that this is a plain infinite loop and it doesn't make any use of threads yet - something that you'll change in a more complex application: In the end, it makes limited sense to use Zephyr without its operating system. For now, keep in mind that [applications _can_ operate without threads](https://docs.zephyrproject.org/latest/kernel/services/threads/nothread.html#nothread), though this severely limits the functionality provided by Zephyr. ### Build configuration -Now we come to the heart of this first step towards Zephyr: The build configuration. In case you're not familiar at all with *CMake*, there are several articles and even books about ["Effective Modern CMake"](https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1): *CMake* has evolved significantly, and only certain patterns should be used for new applications. We won't explain *CMake* here, but if you're an experienced developer you should understand the basics just fine when reading along. +Now we come to the heart of this first step towards Zephyr: The build configuration. In case you're not familiar at all with *CMake*, there are several articles and even books about ["Effective Modern CMake"](https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1): *CMake* has evolved significantly, and only certain patterns should be used for new applications. We won't explain *CMake* here, but if you're an experienced developer, you should understand the basics just fine when reading along. Let's go through the steps to create our [CMakeLists.txt](CMakeLists.txt) for this empty application: @@ -300,9 +300,9 @@ set(BOARD nrf52840dk_nrf52840) find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) ``` -- `cmake_minimum_required` just indicates the allowed CMake versions. Zephyr has its own requirements and maintains its minimal required CMake version in `zephyr/cmake/modules/zephyr_default.cmake`. -- The `set` function writes the board that we're building our application for to the variable `BOARD`. This step is optional and can actually be specified during the build step as a parameter. `nrf52840dk_nrf52840` is Zephyr's name for the [nRF52840 development kit](https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dk). -- Then we go ahead and load Zephyr using the `find_package` function. In our [setup script][#creating-a-setup-script] we've exported the `ZEPHYR_BASE` environment variable, which is now passed as a hint to the `find_package` function to locate the correct Zephyr installation. +- `cmake_minimum_required` just indicates the allowed CMake versions. Zephyr has its requirements and maintains its minimal required CMake version in `zephyr/cmake/modules/zephyr_default.cmake`. +- The `set` function writes the board we're building our application for to the variable `BOARD`. This step is optional and can be specified during the build step as a parameter. `nrf52840dk_nrf52840` is Zephyr's name for the [nRF52840 development kit](https://www.nordicsemi.com/Products/Development-hardware/nrf52840-dk). +- Then, we load Zephyr using the `find_package` function. In our [setup script][#creating-a-setup-script], we've exported the `ZEPHYR_BASE` environment variable, which is now passed as a hint to the `find_package` function to locate the correct Zephyr installation. > **Note:** There are several ways to use the Zephyr CMake package, each of which is described in detail in [Zephyr's CMake package documentation](https://docs.zephyrproject.org/latest/build/zephyr_cmake_package.html). You can also have a look at the [Zephyr CMake package source code](https://docs.zephyrproject.org/latest/build/zephyr_cmake_package.html#zephyr-cmake-package-source-code) for details. @@ -330,7 +330,7 @@ target_sources( - The `project` function defines the name and settings of the application project. - Then, we simply need to add our sources to the `app` target. -But wait. What's this `app` target and why don't we need to specify an executable? This is all done within the Zephyr CMake package: The package already defines your executable and also provides this `app` library target which is intended to be used for all application code. At the time of writing, this `app` target is constructed by the following CMake files in the Zephyr project: +But wait. What's this `app` target, and why don't we need to specify an executable? This is all done within the Zephyr CMake package: The package already defines your executable and also provides this `app` library target which is intended to be used for all application code. At the time of writing, this `app` target is constructed by the following CMake files in the Zephyr project: `zephyr/cmake/modules/kernel.cmake` ```cmake @@ -349,7 +349,7 @@ endmacro() ### Building the application -With the [CMakeLists.txt](CMakeLists.txt) in place we can go ahead and build the application. There are several ways to do this and if you're familiar with CMake, the below commands are no surprise. What we want to show, however, is the slight differences between using CMake or _West_ when building your application. Let's first use CMake: +With the [CMakeLists.txt](CMakeLists.txt) in place, we can build the application. There are several ways to do this and if you're familiar with CMake, the below commands are no surprise. What we want to show, however, is the slight differences between using CMake or _West_ when building your application. Let's first use CMake: #### Building with CMake @@ -379,14 +379,14 @@ Note: to silence the above message, run 'west config build.board_warn false' > **Note:** The default build directory of _West_ is a `build` folder within the same directory. If run without `-d ../build`, _West_ will create a `build` folder in the current directory for the build. -The build seems to succeed, but there's a warning indicating that no board has been specified. How is that possible? _West_ itself typically still expects a "board configuration" or the `--board` parameter. The reason for this is simple: _West_ is much more than just a wrapper for CMake and therefore doesn't parse the provided `CMakeLists.txt` file - where we specified the `BOARD`. It just warns you that you may have forgotten to specify the `board` but still proceeds to call CMake. +The build seems to succeed, but there's a warning indicating that no board has been specified. How is that possible? _West_ typically still expects a "board configuration" or the `--board` parameter. The reason for this is simple: _West_ is much more than just a wrapper for CMake and therefore doesn't parse the provided `CMakeLists.txt` file - where we specified the `BOARD`. It just warns you that you may have forgotten to specify the `board` but still proceeds to call CMake. -Before looking yet another step deeper into West, comment or **delete** the `set(BOARD ...)` instruction in your `CMakeLists.txt` file. +Before looking another step deeper into West, comment or **delete** the `set(BOARD ...)` instruction in your `CMakeLists.txt` file. #### Building with West -As mentioned, _West_ needs an indication of which board is being used by your project. Just like plain CMake, this could be done by specifying the `BOARD` as an environment variable, or by passing the board to `west build`: +As mentioned, _West_ needs an indication of which board is being used by your project. Just like plain CMake, this could be done by specifying the `BOARD` as an environment variable or by passing the board to `west build`: ```bash # pass the board as argument @@ -410,7 +410,7 @@ build directory exists! $ west build -d ../build ``` -Alternatively, we can use [West's own build configuration options](https://docs.zephyrproject.org/latest/develop/west/build-flash-debug.html#configuration-options) to fix the board in the executing terminal. These options are similar to environment variables but are configuration options _only_ relevant to West. We can list the current West configuration options using `west config -l`: +Alternatively, we can use [West's build configuration options](https://docs.zephyrproject.org/latest/develop/west/build-flash-debug.html#configuration-options) to fix the board in the executing terminal. These options are similar to environment variables but are configuration options _only_ relevant to West. We can list the current West configuration options using `west config -l`: ```bash $ west config -l @@ -419,7 +419,7 @@ manifest.file=west.yml zephyr.base=zephyr ``` -The listed configuration options are part of [West's built-in configuration options](https://docs.zephyrproject.org/latest/develop/west/config.html#built-in-configuration-options) and come from the West workspace configuration. Since we're using a freestanding application we'll ignore these options for now and focus on our build settings. We can add a default board configuration to our settings as follows: +The listed configuration options are part of [West's built-in configuration options](https://docs.zephyrproject.org/latest/develop/west/config.html#built-in-configuration-options) and come from the West workspace configuration. Since we're using a freestanding application, we'll ignore these options and focus on our build settings. We can add a default board configuration to our settings as follows: ```bash $ west config build.board nrf52840dk_nrf52840 @@ -429,18 +429,18 @@ manifest.file=west.yml zephyr.base=zephyr build.board=nrf52840dk_nrf52840 # It is also possible to delete an option -# e.g., using `west config -d build.board` +#, e.g., using `west config -d build. board` ``` > **Note:** The _West_ configuration options can also be used to define arguments for CMake. This can be useful when debugging or when defining your own CMake caches, refer to the [documentation](https://docs.zephyrproject.org/latest/develop/west/build-flash-debug.html#permanent-cmake-arguments) for details. -Now we can run `west build` in our current terminal without specifying a board. +Now, we can run `west build` in our current terminal without specifying a board. ```bash $ west build -d ../build ``` -Notice that CMake does not pick up _West_'s configuration options, and therefore executing `cmake -B ../build` would fail. One last parameter for `west build` that is worth mentioning is `--pristine`: Just like the `--pristine` option for `CMake`, instead of deleting the `build` folder, you can also use the `--pristine` to generate a new build: +Notice that CMake does not pick up _West_'s configuration options, so executing `cmake -B ../build` would fail. One last parameter for `west build` that is worth mentioning is `--pristine`: Just like the `--pristine` option for `CMake`, instead of deleting the `build` folder, you can also use the `--pristine` to generate a new build: ```bash $ west build -d ../build --pristine @@ -450,7 +450,7 @@ $ west build -d ../build --pristine ## A quick glance at IDE integrations -In the [prerequisites](#prerequisites) we've promised that we won't have a detailed look at IDEs - and we won't. In this section, we'll just have a look `vscode` and some information that is relevant for any type of editor. Let's start with the editor-independent information: +In the [prerequisites](#prerequisites), we've promised that we won't have a detailed look at IDEs - and we won't. In this section, we'll just have a look `vscode` and some information that is relevant for any type of editor. Let's start with the editor-independent information: Zephyr configures CMake to export a _compilation database_ and thus produces a `compile_commands.json` file in the build directory: @@ -462,7 +462,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE CACHE BOOL ) ``` -This compilation database essentially contains the commands that are used for each and every file that is compiled in the project. The database is not only used by tools such as [`clang-tidy`](https://clang.llvm.org/extra/clang-tidy), but also by IDEs and their extensions, e.g., to resolve include paths. +This compilation database essentially contains the commands that are used for each and every file that is compiled in the project. The database is not only used by tools such as [`clang-tidy`](https://clang.llvm.org/extra/clang-tidy) but also by IDEs and their extensions, e.g., to resolve include paths. E.g., `vscode` has a [C/C++ extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools), which can be configured to pick up the provided compilation database as follows in `vscode`'s [`settings.json`](https://github.com/lmapii/practical-zephyr/blob/main/.vscode/settings.json):