In my last post we built our own, very up-to-date, image for our target device (a RaspberryPi3 fitted with Pimoroni's Automation-HAT) using Yocto/OE and poky. This image included the latest revisions of Pimoroni's Python libraries for driving the Automation-HAT, thanks to the enormous amount of help devtool provides for creating new recipes for existing code available in repositories.
If there exists a repository of code you'd like to add to your image, you need a recipe that will fetch, build, deploy, etc the software. If you're lucky a recipe will already exist in the layer index. If you have to write your own recipe, devtool is a powerful and invaluable tool. Simply invoke devtool, choose a recipe name, point devtool at the repository, and it does much of the work for you (including identifying dependencies)! The tooling understands dozens of fetchers for common repository types (git, cvs, clearcase, perforce, wget, bzr, etc…), and it provides dozens of handlers for dealing with various build systems (autotools, make, cmake, meson, npm, setuptools, etc…). As you can imagine, this exercise becomes more challenging if the software isn't using a common build system, or isn't using it correctly.
But what if the code doesn't already exist? If you're creating your own product and writing your own software, there will not be any pre-existing repository at which you can point devtool. This is the scenario we're going to focus on in this post. The assumptions are that you're writing code that needs to be compiled (i.e. not interpreted), and that you're using a much more powerful build host for development therefore you will need to cross-compile your code. Since the hardware we're using has some "toys" to "play" with (i.e. LEDs, ADCs, relays, inputs, and outputs), the goal of our code will be to "play" with some of the "toys" found on the Automation-HAT. Also, since my goal is to focus on cross-compiling and embedded development, I'm going to ignore the pre-exisiting python libraries that are available for this hardware.
There are two broad scenarios:
- you are comfortable using OpenEmbedded and would like to create an image and write the software entirely on your own host machine
- you are working as part of a team and have no interest in the overall image or knowing how the images are created, you only want to focus on writing the software
In either case all developers need access to the cross-development tools that will be used to build the final image. This will help alleviate many of the "gotchas" that crop up late in the development cycle when code gets integrated into the final image. If developers are using different versions of a given compiler, it's not uncommon for newer versions of the same tool to find more problems/errors with the code than older versions, or for newer versions to compile the code differently or make different assumptions. Having all developers use the same versions of all the same tools and libraries throughout the development process goes a long way towards improving your release process and improving the quality of your testing.
As I mentioned in my previous post, I like to start small and make incremental changes as I go. Which means I like to build my code often and test it often (preferably on the device itself). So let's start with a "Hello, world!". In any "good" software project, the code is but one component of the overall effort. Therefore I like to put my code in a src subdirectory of my project (…in anticipation of a doc directory for documentation, a test subdirectory for tests, etc).
1 2 3 | $ mkdir src $ cd src $ vi i2ctest.c |
…and for the code…
1 2 3 4 5 6 7 8 9 10 11 12 | /* * Copyright (C) 2020 Trevor Woerner <twoerner@gmail.com> */ #include <stdio.h> int main (void) { printf("Hello, world!\n"); return 0; } |
As good developers we should be thinking about using a standard build system. There are many from which to choose; for this example I'm going to select the autotools. In order for our code to be built using the autotools we need to put project-specific metadata in various files which the autotools process so we can then use regular make to build.
In the top-level directory we need to create:
- the project's configure.ac (which eventually gets transformed into the familiar ./configure script)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
dnl Copyright (C) 2020 Trevor Woerner <twoerner@gmail.com> AC_PREREQ(2.57) AC_INIT([automationhat-doodles], 0.1.0, twoerner@gmail.com, automationhat-doodles) AC_CONFIG_SRCDIR(src/i2ctest.c) AC_CONFIG_AUX_DIR(cfg) AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-bzip2 1.9]) AM_CONFIG_HEADER(cfg/config.h) SUBDIRS="src" dnl ********************************** dnl checks for programs dnl ********************************** AC_PROG_CC AC_PROG_CPP AC_PROG_MAKE_SET AC_PROG_INSTALL AC_PROG_LN_S dnl ********************************** dnl checks for header files dnl ********************************** AC_HEADER_STDC AC_CHECK_HEADERS(stdio.h stdlib.h string.h unistd.h fcntl.h errno.h getopt.h) AC_CHECK_HEADERS(sys/types.h sys/stat.h sys/ioctl.h linux/i2c.h linux/i2c-dev.h) dnl ********************************** dnl checks for typedefs, structs, and dnl compiler characteristics dnl ********************************** AC_TYPE_SIZE_T dnl ********************************** dnl other stuff dnl ********************************** AC_SUBST(SUBDIRS) dnl ********************************** dnl output dnl ********************************** AC_OUTPUT(Makefile cfg/Makefile src/Makefile)
- the top-level Makefile.am
1 2 3 4 5 6 7
## Copyright (C) 2020 Trevor Woerner <twoerner@gmail.com> ######################## ## top-level Makefile.am ######################## SUBDIRS = @SUBDIRS@ DIST_SUBDIRS = cfg @SUBDIRS@
I prefer to put the configuration-related things into a separate cfg directory, so I create that directory and create a Makefile.am in there too:
1 2 | $ mkdir cfg $ vi cfg/Makefile.am |
1 2 3 4 5 6 | ## Copyright (C) 2020 Trevor Woerner <twoerner@gmail.com> ######################## # cfg/Makefile.am ######################## SUBDIRS = |
Finally, we need a Makefile.am in the src directory as well:
1 2 3 4 5 6 7 8 9 10 | ## Copyright (C) 2020 Trevor Woerner <twoerner@gmail.com> ######################## ## src/Makefile.am ######################## SUBDIRS = AM_CFLAGS = -Wall -Werror -Wextra -Wconversion -Wreturn-type -Wstrict-prototypes bin_PROGRAMS = i2ctest i2ctest_SOURCES = i2ctest.c |
Our code is all ready to go, our target device is standing by, now how do we cross-compile this code with the tools we've used to create the rest of our image, and how can we test the code to verify it's working correctly? Earlier I outlined two scenarios: you're either going to be both writing some code and building the images, or you're only interested in writing code. Similarly there is a solution for each scenario:
- use devtool to help write a recipe for the code you're writing (which is very similar to how we used it in my previous blog post) the difference is that instead of pointing devtool to a repository "out there" on the internet, you point it at the top-level directory of wherever you're writing your code
- the person who is creating the images and running bitbake generates an SDK and hands it to the developer who uses it like any other SDK
Image And Code
As before, in this case we can use devtool to create a recipe for us and simply build our code like any other package:
1 2 3 4 5 6 7 8 9 10 11 12 | $ devtool add automationhat-doodles /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/ NOTE: Starting bitbake server... INFO: Creating workspace layer in /z/build-master/6951/automation-hat/build/workspace NOTE: Starting bitbake server... NOTE: Reconnecting to bitbake server... NOTE: Retrying server connection (#1)... NOTE: Reconnecting to bitbake server... NOTE: Reconnecting to bitbake server... NOTE: Retrying server connection (#1)... NOTE: Retrying server connection (#1)... NOTE: Starting bitbake server... INFO: Recipe /z/build-master/6951/automation-hat/poky/build/workspace/recipes/automationhat-doodles/automationhat-doodles_0.1.0.bb has been automatically created; further editing may be required to make it fully functional |
Looking at the workspace:
1 2 3 4 5 6 7 8 9 10 11 12 | $ tree workspace workspace ├── README ├── appends │ └── automationhat-doodles_0.1.0.bbappend ├── conf │ └── layer.conf └── recipes └── automationhat-doodles └── automationhat-doodles_0.1.0.bb 4 directories, 4 files… |
…the appends:
1 2 | inherit externalsrc EXTERNALSRC = "/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles" |
…and the raw recipe devtool created for us:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # Recipe created by recipetool # This is the basis of a recipe and may need further editing in order to be fully functional. # (Feel free to remove these comments when editing.) # Unable to find any files that looked like license statements. Check the accompanying # documentation and source headers and set LICENSE and LIC_FILES_CHKSUM accordingly. # # NOTE: LICENSE is being set to "CLOSED" to allow you to at least start building - if # this is not accurate with respect to the licensing of the software being built (it # will not be in most cases) you must specify the correct value before using this # recipe for anything other than initial testing/development! LICENSE = "CLOSED" LIC_FILES_CHKSUM = "" # No information for SRC_URI yet (only an external source tree was specified) SRC_URI = "" # NOTE: if this software is not capable of being built in a separate build directory # from the source, you should replace autotools with autotools-brokensep in the # inherit line inherit autotools # Specify any options you want to pass to the configure script using EXTRA_OECONF: EXTRA_OECONF = "" |
Everything we have is enough to build our software, no changes required:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | $ devtool build automationhat-doodles NOTE: Starting bitbake server... NOTE: Reconnecting to bitbake server... NOTE: Retrying server connection (#1)... Loading cache: 100% | | ETA: --:--:-- Loaded 0 entries from dependency cache. Parsing recipes: 100% |#########################################################################################################################################################################| Time: 0:00:13 Parsing of 2044 .bb files complete (0 cached, 2044 parsed). 3215 targets, 123 skipped, 0 masked, 0 errors. Removing 1 recipes from the cortexa53 sysroot: 100% |###########################################################################################################################################| Time: 0:00:00 Removing 1 recipes from the raspberrypi3_64 sysroot: 100% |#####################################################################################################################################| Time: 0:00:00 Loading cache: 100% |###########################################################################################################################################################################| Time: 0:00:03 Loaded 3214 entries from dependency cache. Parsing recipes: 100% |#########################################################################################################################################################################| Time: 0:00:00 Parsing of 2044 .bb files complete (2043 cached, 1 parsed). 3215 targets, 123 skipped, 0 masked, 0 errors. NOTE: Resolving any missing task queue dependencies Build Configuration: BB_VERSION = "1.47.0" BUILD_SYS = "x86_64-linux" NATIVELSBSTRING = "universal" TARGET_SYS = "aarch64-poky-linux" MACHINE = "raspberrypi3-64" DISTRO = "poky" DISTRO_VERSION = "3.1+snapshot-20201018" TUNE_FEATURES = "aarch64 armv8a crc cortexa53" TARGET_FPU = "" meta meta-poky meta-yocto-bsp = "master:7cad26d585f67fa6bf873b8be361c6335a7db376" meta-raspberrypi = "master:6f85611576b7ccbfb6012631f741bd1daeffc9c9" workspace = "master:b1a0414a6df77674a860c365825a4500e6cd698b" meta-oe meta-python = "master:86a7820b7964ff91d7a26ac5c506e83292e347a3" devtool-additions = "master:b1a0414a6df77674a860c365825a4500e6cd698b" Initialising tasks: 100% |######################################################################################################################################################################| Time: 0:00:00 Sstate summary: Wanted 0 Found 0 Missed 0 Current 116 (0% match, 100% complete) NOTE: Executing Tasks NOTE: automationhat-doodles: compiling from external source tree /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles NOTE: Tasks Summary: Attempted 495 tasks of which 487 didn't need to be rerun and all succeeded. NOTE: Writing buildhistory NOTE: Writing buildhistory took: 1 seconds NOTE: Build completion summary: NOTE: do_populate_sysroot: 0.0% sstate reuse(0 setscene, 1 scratch) NOTE: do_deploy_source_date_epoch: 0.0% sstate reuse(0 setscene, 1 scratch) NOTE: do_package: 0.0% sstate reuse(0 setscene, 1 scratch) NOTE: do_packagedata: 0.0% sstate reuse(0 setscene, 1 scratch) |
Success! The exact same build system that OE created to generate our entire image has now been used in exactly the same way to build the code we just wrote. As a developer I don't have to know anything about having to find or build an appropriate cross-compiler, or worry about whether I've passed the correct flags to the cross-compiler to ensure my build is using the correct headers (for example).
Now we need to get our code over to the target device so we can test it. In theory we could generate a new image with each build and test our code that way. That would create quite a long development cycle! Provided we have networking working on the target and an ssh server running, we could just scp the file over and run it. With a simple program like this one, the proposed solution would work fine. But as the code gets more complicated (e.g. multiple pieces, libraries, etc) copying everything over could increase the possibilities of errors. Also, once we're done testing a given build, it would be nice of having a way to clear everything off the target device between uploads.
Once again, devtool is very helpful in this situation! It includes not only a devtool deploy-target command, but also a devtool undeploy-target command!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | $ devtool deploy-target automationhat-doodles root@10.0.0.50 NOTE: Starting bitbake server... NOTE: Reconnecting to bitbake server... NOTE: Retrying server connection (#1)... NOTE: Reconnecting to bitbake server... NOTE: Previous bitbake instance shutting down?, waiting to retry... NOTE: Retrying server connection (#2)... Loading cache: 100% |###########################################################################################################################################################################| Time: 0:00:00 Loaded 3209 entries from dependency cache. Parsing recipes: 100% |#########################################################################################################################################################################| Time: 0:00:01 Parsing of 2040 .bb files complete (2039 cached, 1 parsed). 3210 targets, 165 skipped, 0 masked, 0 errors. tar: ./usr/bin/i2ctest: time stamp 2020-10-09 07:07:44.128603897 is 274194.69420039 s in the future tar: ./usr/bin: time stamp 2020-10-09 07:07:44.128603897 is 274194.693851432 s in the future tar: ./usr: time stamp 2020-10-09 07:07:44.124603877 is 274194.689302922 s in the future tar: .: time stamp 2020-10-09 07:07:44.124603877 is 274194.689052297 s in the future INFO: Successfully deployed /z/build-master/6951/automation-hat/build/tmp-glibc/work/cortexa53-oe-linux/automationhat-doodles/0.1.0-r0/image |
Once our code has been uploaded to the target we simply would run it on the target like any other piece of software:
1 2 | root@raspberrypi3-64:~# i2ctest Hello, world! |
Once we're done with our tests we clean up the target like so:
1 2 3 | $ devtool undeploy-target automationhat-doodles root@10.0.0.50 NOTE: Starting bitbake server... INFO: Successfully undeployed automationhat-doodles |
NOTE: you don't need to undeploy-target after each test. If you're compiling and testing in a tight loop you can simply devtool deploy-target repeatedly. The first thing a devtool deploy-target does is to check for and devtool undeploy-target any previous deploys of the same package. It keeps track of all the files that were copied over on each deploy, so that they can all be removed either when you undeploy-target explicitly, or when your next deploy-target implicitly removes the previous.
Code Only
If you have a team of developers, it is possible to extract the cross-development tools (and all the other relevant pieces) from your build and bundle everything together into an SDK. Using this SDK, the developers will be using the same tools as the image build, and all your developers will be using the same tools. Additionally, the SDK you create will have all the correct header files for all of the components of the software that forms your specific image!
An off-the-shelf SDK you find randomly on the internet will advertise the version of the compiler it's using, but what versions of which other packages are included? Which C library is it using, and what version? Which kernel headers were used? And what if your application sits on top of a bunch of high-level libraries (i.e. a GUI application)? With many off-the-shelf SDKs, getting the flags right to ensure your host system isn't contaminating your cross-build is often left as an exercise for the user! The SDK you generate from your OE image takes care of all those details for you.
To generate an SDK from any of your specific images, build the image with bitbake as usual, but indicate you want the populate_sdk task run specifically:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | $ bitbake core-image-full-cmdline -c populate_sdk Loading cache: 100% |###########################################################################################################################################################################| Time: 0:00:00 Loaded 3214 entries from dependency cache. Parsing recipes: 100% |#########################################################################################################################################################################| Time: 0:00:00 Parsing of 2044 .bb files complete (2043 cached, 1 parsed). 3215 targets, 123 skipped, 0 masked, 0 errors. NOTE: Resolving any missing task queue dependencies Build Configuration: BB_VERSION = "1.47.0" BUILD_SYS = "x86_64-linux" NATIVELSBSTRING = "universal" TARGET_SYS = "aarch64-poky-linux" MACHINE = "raspberrypi3-64" DISTRO = "poky" DISTRO_VERSION = "3.1+snapshot-20201018" TUNE_FEATURES = "aarch64 armv8a crc cortexa53" TARGET_FPU = "" meta meta-poky meta-yocto-bsp = "master:7cad26d585f67fa6bf873b8be361c6335a7db376" meta-raspberrypi = "master:6f85611576b7ccbfb6012631f741bd1daeffc9c9" workspace = "master:b1a0414a6df77674a860c365825a4500e6cd698b" meta-oe meta-python = "master:86a7820b7964ff91d7a26ac5c506e83292e347a3" devtool-additions = "master:b1a0414a6df77674a860c365825a4500e6cd698b" Initialising tasks: 100% |######################################################################################################################################################################| Time: 0:00:02 Sstate summary: Wanted 464 Found 0 Missed 464 Current 982 (0% match, 67% complete) NOTE: Executing Tasks WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218-src went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards] WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218-dbg went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards] WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218-staticdev went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards] WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218-dev went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards] WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218-doc went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards] WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218-locale went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards] WARNING: python3-sn3218-1.2.7+gitAUTOINC+d497c6e976-r0 do_packagedata: QA Issue: Package version for package python3-sn3218 went backwards which would break package feeds (from 0:1.2.7+git999-r0 to 0:1.2.7+git0+d497c6e976-r0) [version-going-backwards] WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat-src went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards] WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat-dbg went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards] WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat-staticdev went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards] WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat-dev went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards] WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat-doc went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards] WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat-locale went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards] WARNING: automation-hat-0.2.3+gitAUTOINC+a41084cb4d-r0 do_packagedata: QA Issue: Package version for package automation-hat went backwards which would break package feeds (from 0:0.2.3+git999-r0 to 0:0.2.3+git0+a41084cb4d-r0) [version-going-backwards] NOTE: Tasks Summary: Attempted 4171 tasks of which 3016 didn't need to be rerun and all succeeded. NOTE: Writing buildhistory NOTE: Writing buildhistory took: 15 seconds NOTE: Build completion summary: NOTE: do_populate_sysroot: 0.0% sstate reuse(0 setscene, 67 scratch) NOTE: do_deploy_source_date_epoch: 0.0% sstate reuse(0 setscene, 98 scratch) NOTE: do_package: 0.0% sstate reuse(0 setscene, 99 scratch) NOTE: do_packagedata: 0.0% sstate reuse(0 setscene, 99 scratch) NOTE: do_package_write_ipk: 0.0% sstate reuse(0 setscene, 99 scratch) Summary: There were 14 WARNING messages shown. |
The resulting SDK is found in ${TMPDIR}/deploy/sdk:
1 2 3 4 5 6 | $ ls -lh tmp/deploy/sdk/ total 254M -rw-r--r-- 2 trevor users 9.4K Oct 18 00:33 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.host.manifest -rwxr-xr-x 2 trevor users 253M Oct 18 00:35 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.sh -rw-r--r-- 2 trevor users 160K Oct 18 00:32 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.target.manifest -rw-r--r-- 2 trevor users 339K Oct 18 00:32 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.testdata.json |
The SDK is bundled as a self-extracting shell script. Give the script to your developers (in this case it's the poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.sh file) and have them simply execute the file. It will ask the person installing the file for a target location. In the following example, I've decided to install the SDK into the directory /opt/toolchains/automation-hat/poky/sdk:
1 2 3 4 5 6 7 8 9 10 | $ ./poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.sh Poky (Yocto Project Reference Distro) SDK installer version 3.1+snapshot ======================================================================== Enter target directory for SDK (default: /opt/poky/3.1+snapshot): /opt/toolchains/automation-hat/poky You are about to install the SDK to "/opt/toolchains/automation-hat/poky". Proceed [Y/n]? Y Extracting SDK..................................................................................done Setting it up...done SDK has been successfully set up and is ready to be used. Each time you wish to use the SDK in a new shell session, you need to source the environment setup script e.g. $ . /opt/toolchains/automation-hat/poky/environment-setup-cortexa53-poky-linux |
As the installer so helpfully reminds us, anytime you want to use the SDK you just installed, in a fresh shell environment you'll have to source the environment file in order to correctly use the cross-compiler. In the following example I'm going to use the SDK I just installed in order to build my code. I'll start a new shell for this demonstration, but this isn't shown.
NOTE: in the following example my SDK is installed to /opt/toolchains/automation-hat/poky/sdk my source code is found in /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles and I will be building my code "out of tree" at /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | $ cd /opt/toolchains/automation-hat/poky/sdk/ $ . ./environment-setup-cortexa53-poky-linux $ export PS1="${PS1}sdk> " $ sdk> cd /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/ $ sdk> autoreconf -i configure.ac:15: installing 'cfg/compile' configure.ac:7: installing 'cfg/install-sh' configure.ac:7: installing 'cfg/missing' src/Makefile.am: installing 'cfg/depcomp' $ sdk> mkdir _build $ sdk> cd _build $ sdk> ../configure --host=x86_64 configure: loading site script /opt/toolchains/automation-hat/poky/sdk/site-config-cortexa53-poky-linux checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for x86_64-strip... aarch64-poky-linux-strip checking for a thread-safe mkdir -p... /usr/bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking whether make supports nested variables... yes checking for x86_64-gcc... aarch64-poky-linux-gcc -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux checking whether the C compiler works... yes checking for C compiler default output file name... a.out checking for suffix of executables... checking whether we are cross compiling... yes checking for suffix of object files... o checking whether we are using the GNU C compiler... yes checking whether aarch64-poky-linux-gcc -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux accepts -g... yes checking for aarch64-poky-linux-gcc -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux option to accept ISO C89... none needed checking whether aarch64-poky-linux-gcc -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux understands -c and -o together... yes checking whether make supports the include directive... yes (GNU style) checking dependency style of aarch64-poky-linux-gcc -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux... gcc3 checking how to run the C preprocessor... aarch64-poky-linux-gcc -E -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux checking whether make sets $(MAKE)... (cached) yes checking whether ln -s works... yes checking for grep that handles long lines and -e... /usr/bin/grep checking for egrep... /usr/bin/grep -E checking for ANSI C header files... yes checking for sys/types.h... yes checking for sys/stat.h... yes checking for stdlib.h... yes checking for string.h... yes checking for memory.h... yes checking for strings.h... yes checking for inttypes.h... yes checking for stdint.h... yes checking for unistd.h... yes checking stdio.h usability... yes checking stdio.h presence... yes checking for stdio.h... yes checking for stdlib.h... (cached) yes checking for string.h... (cached) yes checking for unistd.h... (cached) yes checking fcntl.h usability... yes checking fcntl.h presence... yes checking for fcntl.h... yes checking errno.h usability... yes checking errno.h presence... yes checking for errno.h... yes checking getopt.h usability... yes checking getopt.h presence... yes checking for getopt.h... yes checking for sys/types.h... (cached) yes checking for sys/stat.h... (cached) yes checking sys/ioctl.h usability... yes checking sys/ioctl.h presence... yes checking for sys/ioctl.h... yes checking linux/i2c.h usability... yes checking linux/i2c.h presence... yes checking for linux/i2c.h... yes checking linux/i2c-dev.h usability... yes checking linux/i2c-dev.h presence... yes checking for linux/i2c-dev.h... yes checking for size_t... yes configure: creating ./config.status config.status: creating Makefile config.status: creating cfg/Makefile config.status: creating src/Makefile config.status: creating cfg/config.h config.status: executing depfiles commands $ sdk> make Making all in src make[1]: Entering directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src' make[2]: Entering directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src' aarch64-poky-linux-gcc -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux -DHAVE_CONFIG_H -I. -I../../src -I../cfg -Wall -Werror -Wextra -Wconversion -Wreturn-type -Wstrict-prototypes -O2 -pipe -g -feliminate-unused-debug-types -MT i2ctest.o -MD -MP -MF .deps/i2ctest.Tpo -c -o i2ctest.o ../../src/i2ctest.c mv -f .deps/i2ctest.Tpo .deps/i2ctest.Po aarch64-poky-linux-gcc -mcpu=cortex-a53 -march=armv8-a+crc -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security -Werror=format-security --sysroot=/opt/toolchains/automation-hat/poky/sdk/sysroots/cortexa53-poky-linux -Wall -Werror -Wextra -Wconversion -Wreturn-type -Wstrict-prototypes -O2 -pipe -g -feliminate-unused-debug-types -Wl,-O1 -Wl,--hash-style=gnu -Wl,--as-needed -Wl,-z,relro,-z,now -o i2ctest i2ctest.o make[2]: Leaving directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src' make[1]: Leaving directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src' make[1]: Entering directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build' make[1]: Nothing to be done for 'all-am'. make[1]: Leaving directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build' $ sdk> file src/i2ctest src/i2ctest: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, BuildID[sha1]=47845aa25dff34453e7b1cb367c9b59883f8292c, for GNU/Linux 3.14.0, with debug_info, not stripped |
Yay! We've demonstrated generating an SDK tuned specifically for our image, giving this SDK to an independent developer, and that developer using the SDK on their own, separate machine, to cross-compile their code (in which they've used the autotools build system). This developer doesn't have OE installed on their system, isn't using OE to generate images, and is only focused on writing and cross-compiling their code for the target device.
Although this is a very small example, it does scale. If the code being written was, say, a GUI app that was using (for example) boost and GTK or Qt, simply add those libraries to your image and when you generate the SDK from this image, the SDK will include everything the developer needs to write their application (i.e. all the appropriate headers and cross-libraries).
On-target testing, though, is up to the developer. For a small example such as this, it wouldn't be too hard to copy the one file over and test it. But anything more complex could get messy. If only there was some way to create an SDK that also contained devtool …
Code Only… extended
Turns out there is a way:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | $ bitbake core-image-full-cmdline -c populate_sdk_ext Loading cache: 100% | | ETA: --:--:-- Loaded 0 entries from dependency cache. Parsing recipes: 100% |#########################################################################################################################################################################| Time: 0:00:14 Parsing of 2042 .bb files complete (0 cached, 2042 parsed). 3213 targets, 123 skipped, 0 masked, 0 errors. NOTE: Resolving any missing task queue dependencies Build Configuration: BB_VERSION = "1.47.0" BUILD_SYS = "x86_64-linux" NATIVELSBSTRING = "universal" TARGET_SYS = "aarch64-poky-linux" MACHINE = "raspberrypi3-64" DISTRO = "poky" DISTRO_VERSION = "3.1+snapshot-20201018" TUNE_FEATURES = "aarch64 armv8a crc cortexa53" TARGET_FPU = "" meta-raspberrypi = "master:6f85611576b7ccbfb6012631f741bd1daeffc9c9" devtool-additions = "master:b1a0414a6df77674a860c365825a4500e6cd698b" meta meta-poky meta-yocto-bsp = "master:7cad26d585f67fa6bf873b8be361c6335a7db376" meta-oe meta-python = "master:86a7820b7964ff91d7a26ac5c506e83292e347a3" Initialising tasks: 100% |######################################################################################################################################################################| Time: 0:00:03 Sstate summary: Wanted 2 Found 0 Missed 2 Current 1952 (0% match, 99% complete) NOTE: Executing Tasks NOTE: Tasks Summary: Attempted 5092 tasks of which 5079 didn't need to be rerun and all succeeded. NOTE: Writing buildhistory NOTE: Writing buildhistory took: 1 seconds |
Note, however, that there is a significant size difference between an SDK and an eSDK:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | $ ls -lh tmp/deploy/sdk/ total 1.3G -rw-r--r-- 2 trevor users 9.4K Oct 18 00:33 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.host.manifest -rwxr-xr-x 2 trevor users 253M Oct 18 00:35 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.sh -rw-r--r-- 2 trevor users 160K Oct 18 00:32 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.target.manifest -rw-r--r-- 2 trevor users 339K Oct 18 00:32 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-3.1+snapshot.testdata.json -rw-r--r-- 1 trevor users 9.6K Oct 18 11:53 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-ext-3.1+snapshot.host.manifest -rwxr-xr-x 2 trevor users 999M Oct 18 11:53 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-ext-3.1+snapshot.sh -rw-r--r-- 1 trevor users 6.6K Oct 18 11:53 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-ext-3.1+snapshot.target.manifest -rw-r--r-- 2 trevor users 335K Oct 18 11:49 poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-ext-3.1+snapshot.testdata.json -rw-r--r-- 2 trevor users 6.6K Oct 18 02:06 x86_64-buildtools-nativesdk-standalone-3.1+snapshot-20201018.host.manifest -rwxr-xr-x 2 trevor users 26M Oct 18 02:07 x86_64-buildtools-nativesdk-standalone-3.1+snapshot-20201018.sh -rw-r--r-- 2 trevor users 0 Oct 18 02:05 x86_64-buildtools-nativesdk-standalone-3.1+snapshot-20201018.target.manifest -rw-r--r-- 2 trevor users 306K Oct 18 02:05 x86_64-buildtools-nativesdk-standalone-3.1+snapshot-20201018.testdata.json |
You could install and use an eSDK exactly the same way you would use the regular SDK:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | $ /z/build-master/6951/automation-hat/poky/build/tmp/deploy/sdk/poky-glibc-x86_64-core-image-full-cmdline-cortexa53-raspberrypi3-64-toolchain-ext-3.1+snapshot.sh Poky (Yocto Project Reference Distro) Extensible SDK installer version 3.1+snapshot =================================================================================== Enter target directory for SDK (default: ~/poky_sdk): /opt/toolchains/automation-hat/poky/esdk You are about to install the SDK to "/opt/toolchains/automation-hat/poky/esdk". Proceed [Y/n]? Y Extracting SDK................................................done Setting it up... Extracting buildtools... Preparing build system... Loading cache: 100% | | ETA: --:--:-- Parsing recipes: 100% |###############################################################| Time: 0:00:21 Initialising tasks: 100% |############################################################| Time: 0:00:02 Checking sstate mirror object availability: 100% |####################################| Time: 0:00:00 Loading cache: 100% |#################################################################| Time: 0:00:00 Initialising tasks: 100% |############################################################| Time: 0:00:00 done SDK has been successfully set up and is ready to be used. Each time you wish to use the SDK in a new shell session, you need to source the environment setup script e.g. $ . /opt/toolchains/automation-hat/poky/esdk/environment-setup-cortexa53-poky-linux $ export PS1="${PS1}esdk> " $ esdk> $ esdk> cd /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/ $ esdk> autoreconf -i configure.ac:15: installing 'cfg/compile' configure.ac:7: installing 'cfg/install-sh' configure.ac:7: installing 'cfg/missing' src/Makefile.am: installing 'cfg/depcomp' $ esdk> mkdir _build $ esdk> cd _build $ esdk> ../configure --host=x86_64 configure: loading site script /usr/share/site/x86_64-unknown-linux-gnu checking for a BSD-compatible install... /usr/bin/install -c checking whether build environment is sane... yes checking for x86_64-strip... no checking for strip... strip checking for a thread-safe mkdir -p... /usr/bin/mkdir -p checking for gawk... gawk checking whether make sets $(MAKE)... yes checking whether make supports nested variables... yes checking for x86_64-gcc... no checking for gcc... gcc checking whether the C compiler works... yes checking for C compiler default output file name... a.out checking for suffix of executables... checking whether we are cross compiling... no checking for suffix of object files... o checking whether we are using the GNU C compiler... yes checking whether gcc accepts -g... yes checking for gcc option to accept ISO C89... none needed checking whether gcc understands -c and -o together... yes checking for style of include used by make... GNU checking dependency style of gcc... gcc3 checking how to run the C preprocessor... gcc -E checking whether make sets $(MAKE)... (cached) yes checking whether ln -s works... yes checking for grep that handles long lines and -e... /usr/bin/grep checking for egrep... /usr/bin/grep -E checking for ANSI C header files... yes checking for sys/types.h... yes checking for sys/stat.h... yes checking for stdlib.h... yes checking for string.h... yes checking for memory.h... yes checking for strings.h... yes checking for inttypes.h... yes checking for stdint.h... yes checking for unistd.h... yes checking stdio.h usability... yes checking stdio.h presence... yes checking for stdio.h... yes checking for stdlib.h... (cached) yes checking for string.h... (cached) yes checking for unistd.h... (cached) yes checking fcntl.h usability... yes checking fcntl.h presence... yes checking for fcntl.h... yes checking errno.h usability... yes checking errno.h presence... yes checking for errno.h... yes checking getopt.h usability... yes checking getopt.h presence... yes checking for getopt.h... yes checking for sys/types.h... (cached) yes checking for sys/stat.h... (cached) yes checking sys/ioctl.h usability... yes checking sys/ioctl.h presence... yes checking for sys/ioctl.h... yes checking linux/i2c.h usability... yes checking linux/i2c.h presence... yes checking for linux/i2c.h... yes checking linux/i2c-dev.h usability... yes checking linux/i2c-dev.h presence... yes checking for linux/i2c-dev.h... yes checking for size_t... yes checking that generated files are newer than configure... done configure: creating ./config.status config.status: creating Makefile config.status: creating cfg/Makefile config.status: creating src/Makefile config.status: creating cfg/config.h config.status: executing depfiles commands $ esdk> make Making all in src make[1]: Entering directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src' make[2]: Entering directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src' gcc -DHAVE_CONFIG_H -I. -I../../src -I../cfg -Wall -Werror -Wextra -Wconversion -Wreturn-type -Wstrict-prototypes -g -O2 -MT i2ctest.o -MD -MP -MF .deps/i2ctest.Tpo -c -o i2ctest.o ../../src/i2ctest.c mv -f .deps/i2ctest.Tpo .deps/i2ctest.Po gcc -Wall -Werror -Wextra -Wconversion -Wreturn-type -Wstrict-prototypes -g -O2 -o i2ctest i2ctest.o make[2]: Leaving directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src' make[1]: Leaving directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build/src' make[1]: Entering directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build' make[1]: Nothing to be done for 'all-am'. make[1]: Leaving directory '/opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles/_build' $ esdk> file src/i2ctest src/i2ctest: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, BuildID[sha1]=4199ed513fc41043d24d05b43075b73fc9037e87, for GNU/Linux 3.2.0, with debug_info, not stripped |
However, note that in order to take full advantage of devtool (e.g. devtool deploy-target) the independent developer needs to create a recipe for the code they're writing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | $ esdk> devtool add automationhat-doodles /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles NOTE: Starting bitbake server... NOTE: Starting bitbake server... NOTE: Reconnecting to bitbake server... NOTE: Retrying server connection (#1)... NOTE: Reconnecting to bitbake server... NOTE: Reconnecting to bitbake server... NOTE: Retrying server connection (#1)... NOTE: Retrying server connection (#1)... NOTE: Starting bitbake server... INFO: Recipe /opt/toolchains/automation-hat/poky/esdk/workspace/recipes/automationhat-doodles/automationhat-doodles_0.1.0.bb has been automatically created; further editing may be required to make it fully functional $ esdk> devtool build automationhat-doodles NOTE: Starting bitbake server... NOTE: Reconnecting to bitbake server... NOTE: Retrying server connection (#1)... Loading cache: 100% |#################################################################| Time: 0:00:00 Loaded 3213 entries from dependency cache. Parsing recipes: 100% |###############################################################| Time: 0:00:00 Parsing of 2043 .bb files complete (2042 cached, 1 parsed). 3214 targets, 123 skipped, 0 masked, 0 errors. Loading cache: 100% |#################################################################| Time: 0:00:01 Loaded 3213 entries from dependency cache. Parsing recipes: 100% |###############################################################| Time: 0:00:00 Parsing of 2043 .bb files complete (2042 cached, 1 parsed). 3214 targets, 123 skipped, 0 masked, 0 errors. NOTE: Resolving any missing task queue dependencies Initialising tasks: 100% |############################################################| Time: 0:00:00 Sstate summary: Wanted 8 Found 1 Missed 7 Current 108 (12% match, 93% complete) NOTE: Executing Tasks NOTE: automationhat-doodles: compiling from external source tree /opt/oe/configs/z/build-master/6951/automation-hat/devel/automationhat-doodles NOTE: Tasks Summary: Attempted 495 tasks of which 487 didn't need to be rerun and all succeeded. NOTE: Build completion summary: NOTE: do_populate_sysroot: 0.0% sstate reuse(0 setscene, 1 scratch) NOTE: do_deploy_source_date_epoch: 0.0% sstate reuse(0 setscene, 1 scratch) NOTE: do_package: 0.0% sstate reuse(0 setscene, 1 scratch) NOTE: do_packagedata: 0.0% sstate reuse(0 setscene, 1 scratch) $ esdk> devtool deploy-target automationhat-doodles root@10.0.0.50 NOTE: Starting bitbake server... NOTE: Reconnecting to bitbake server... NOTE: Retrying server connection (#1)... Loading cache: 100% |#################################################################| Time: 0:00:00 Loaded 3213 entries from dependency cache. Parsing recipes: 100% |###############################################################| Time: 0:00:00 Parsing of 2043 .bb files complete (2042 cached, 1 parsed). 3214 targets, 123 skipped, 0 masked, 0 errors. tar: ./usr/bin/i2ctest: time stamp 2020-10-18 18:16:41 is 82401236.305274304 s in the future tar: ./usr/bin: time stamp 2020-10-18 18:16:41 is 82401236.304915762 s in the future tar: ./usr: time stamp 2020-10-18 18:16:41 is 82401236.304415189 s in the future tar: .: time stamp 2020-10-18 18:16:41 is 82401236.304187064 s in the future INFO: Successfully deployed /opt/toolchains/automation-hat/poky/esdk/tmp/work/cortexa53-poky-linux/automationhat-doodles/0.1.0-r0/image |
Turning On An LED
We now have the mechanics in place for writing, cross-compiling, and on-device testing of your code as it is being written, all using the same bleeding-edge, state-of-the-art tools being used to build your image.
Working through the code to get an LED lit we end up with:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | /* * Copyright (C) 2020 Trevor Woerner <twoerner@gmail.com> */ #include "config.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/ioctl.h> #include <i2c/smbus.h> #include <linux/i2c.h> #include <linux/i2c-dev.h> #include <getopt.h> static char *i2cDevice_pG = "/dev/i2c-1"; static void print_funcs(unsigned long); static void process_cmdline_args(int,char**); static void usage(const char*); int main (int argc, char *argv[]) { int ret; int i2cFd; unsigned long funcs; /* process cmdline - get i2c device */ process_cmdline_args(argc, argv); if (optind == argc) ; else if ((optind+1) == argc) { i2cDevice_pG = strdup(argv[optind]); if (i2cDevice_pG == NULL) { perror("strdup()"); return 1; } } else { fprintf(stderr, "bad cmdline args\n"); usage(argv[0]); return 1; } printf("device: %s\n", i2cDevice_pG); /* open i2c device */ i2cFd = open(i2cDevice_pG, O_RDWR); if (i2cFd < 0) { perror("open()"); return 1; } /* figure out and show i2c capabilities */ ret = ioctl(i2cFd, I2C_FUNCS, &funcs); if (ret < 0) { perror("ioctl()"); close(i2cFd); return 1; } printf("funcs: 0x%08lx\n", funcs); print_funcs(funcs); /* interract with device */ ret = ioctl(i2cFd, I2C_SLAVE, 0x54); if (ret < 0) { perror("I2C_SLAVE"); return 1; } i2c_smbus_write_byte_data(i2cFd, 0x17, 0xff); // reset i2c_smbus_write_byte_data(i2cFd, 0x00, 0x01); // enable i2c_smbus_write_byte_data(i2cFd, 0x12, 0x10); // some pwm on channel 0x12 i2c_smbus_write_byte_data(i2cFd, 0x15, 0x20); // turn on channel 0x12 i2c_smbus_write_byte_data(i2cFd, 0x16, 0xff); // update close(i2cFd); return 0; } static struct { unsigned long flag; const char *str_p; } I2CFlags_G[] = { {I2C_FUNC_I2C, "I2C_FUNC_I2C"}, {I2C_FUNC_10BIT_ADDR, "I2C_FUNC_10BIT_ADDR"}, {I2C_FUNC_PROTOCOL_MANGLING, "I2C_FUNC_PROTOCOL_MANGLING"}, {I2C_FUNC_SMBUS_PEC, "I2C_FUNC_SMBUS_PEC"}, {I2C_FUNC_NOSTART, "I2C_FUNC_NOSTART"}, {I2C_FUNC_SLAVE, "I2C_FUNC_SLAVE"}, {I2C_FUNC_SMBUS_BLOCK_PROC_CALL, "I2C_FUNC_SMBUS_BLOCK_PROC_CALL"}, {I2C_FUNC_SMBUS_QUICK, "I2C_FUNC_SMBUS_QUICK"}, {I2C_FUNC_SMBUS_READ_BYTE, "I2C_FUNC_SMBUS_READ_BYTE"}, {I2C_FUNC_SMBUS_WRITE_BYTE, "I2C_FUNC_SMBUS_WRITE_BYTE"}, {I2C_FUNC_SMBUS_READ_BYTE_DATA, "I2C_FUNC_SMBUS_READ_BYTE_DATA"}, {I2C_FUNC_SMBUS_WRITE_BYTE_DATA, "I2C_FUNC_SMBUS_WRITE_BYTE_DATA"}, {I2C_FUNC_SMBUS_READ_WORD_DATA, "I2C_FUNC_SMBUS_READ_WORD_DATA"}, {I2C_FUNC_SMBUS_WRITE_WORD_DATA, "I2C_FUNC_SMBUS_WRITE_WORD_DATA"}, {I2C_FUNC_SMBUS_PROC_CALL, "I2C_FUNC_SMBUS_PROC_CALL"}, {I2C_FUNC_SMBUS_READ_BLOCK_DATA, "I2C_FUNC_SMBUS_READ_BLOCK_DATA"}, {I2C_FUNC_SMBUS_WRITE_BLOCK_DATA, "I2C_FUNC_SMBUS_WRITE_BLOCK_DATA"}, {I2C_FUNC_SMBUS_READ_I2C_BLOCK, "I2C_FUNC_SMBUS_READ_I2C_BLOCK"}, {I2C_FUNC_SMBUS_WRITE_I2C_BLOCK, "I2C_FUNC_SMBUS_WRITE_I2C_BLOCK"}, {I2C_FUNC_SMBUS_HOST_NOTIFY, "I2C_FUNC_SMBUS_HOST_NOTIFY"}, }; #define I2CFLAGS_SZ (sizeof(I2CFlags_G) / sizeof(I2CFlags_G[0])) static void print_funcs (unsigned long funcs) { size_t i; for (i=0; i<I2CFLAGS_SZ; ++i) if (funcs & I2CFlags_G[i].flag) printf("- %s\n", I2CFlags_G[i].str_p); } static void usage (const char *pgm_p) { fprintf(stderr, "%s\n", PACKAGE_STRING); if (pgm_p != NULL) fprintf(stderr, "\nusage: %s [opts] [device]\n", pgm_p); fprintf(stderr, " where [device]:\n"); fprintf(stderr, " optionally provide the i2c device node to use\n"); fprintf(stderr, " (default: %s)\n", i2cDevice_pG); fprintf(stderr, " where [opts]:\n"); fprintf(stderr, " -h|--help print this help and exit successfully\n"); } static void process_cmdline_args (int argc, char *argv[]) { int c; struct option longOpts[] = { {"help", no_argument, NULL, 'h'}, {NULL, 0, NULL, 0}, }; while (1) { c = getopt_long(argc, argv, "h", longOpts, NULL); if (c == -1) break; switch (c) { case 'h': usage(argv[0]); exit(0); break; default: fprintf(stderr, "unknown getopt: %d (0x%02x)\n", c, c); break; } } } |
This C code for i2c has a dependency on i2c-tools, so the following updates are required.
configure.ac:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | dnl Copyright (C) 2020 Trevor Woerner <twoerner@gmail.com> AC_PREREQ(2.57) AC_INIT([automationhat-doodles], 0.1.0, twoerner@gmail.com, automationhat-doodles) AC_CONFIG_SRCDIR(src/i2ctest.c) AC_CONFIG_AUX_DIR(cfg) AM_INIT_AUTOMAKE([foreign no-dist-gzip dist-bzip2 1.9]) AM_CONFIG_HEADER(cfg/config.h) SUBDIRS="src" dnl ********************************** dnl checks for programs dnl ********************************** AC_PROG_CC AC_PROG_CPP AC_PROG_MAKE_SET AC_PROG_INSTALL AC_PROG_LN_S dnl ********************************** dnl checks for libraries dnl ********************************** AC_CHECK_LIB(i2c, i2c_smbus_write_block_data, ,AC_MSG_ERROR([Can't find library i2c]), ) dnl ********************************** dnl checks for header files dnl ********************************** AC_HEADER_STDC AC_CHECK_HEADERS(stdio.h stdlib.h string.h unistd.h fcntl.h errno.h getopt.h) AC_CHECK_HEADERS(sys/types.h sys/stat.h sys/ioctl.h linux/i2c.h linux/i2c-dev.h) AC_CHECK_HEADERS(i2c/smbus.h) dnl ********************************** dnl checks for typedefs, structs, and dnl compiler characteristics dnl ********************************** AC_TYPE_SIZE_T dnl ********************************** dnl other stuff dnl ********************************** AC_SUBST(SUBDIRS) dnl ********************************** dnl output dnl ********************************** AC_OUTPUT(Makefile cfg/Makefile src/Makefile) |
and modify the automationhat-doodles recipe:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | # Recipe created by recipetool # This is the basis of a recipe and may need further editing in order to be fully functional. # (Feel free to remove these comments when editing.) # Unable to find any files that looked like license statements. Check the accompanying # documentation and source headers and set LICENSE and LIC_FILES_CHKSUM accordingly. # # NOTE: LICENSE is being set to "CLOSED" to allow you to at least start building - if # this is not accurate with respect to the licensing of the software being built (it # will not be in most cases) you must specify the correct value before using this # recipe for anything other than initial testing/development! LICENSE = "CLOSED" LIC_FILES_CHKSUM = "" # No information for SRC_URI yet (only an external source tree was specified) SRC_URI = "" # NOTE: if this software is not capable of being built in a separate build directory # from the source, you should replace autotools with autotools-brokensep in the # inherit line inherit autotools # Specify any options you want to pass to the configure script using EXTRA_OECONF: EXTRA_OECONF = "" DEPENDS += "i2c-tools" |
Running the code on the target:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | root@raspberrypi3-64:~# i2ctest device: /dev/i2c-1 funcs: 0x0eff0009 - I2C_FUNC_I2C - I2C_FUNC_SMBUS_PEC - I2C_FUNC_SMBUS_QUICK - I2C_FUNC_SMBUS_READ_BYTE - I2C_FUNC_SMBUS_WRITE_BYTE - I2C_FUNC_SMBUS_READ_BYTE_DATA - I2C_FUNC_SMBUS_WRITE_BYTE_DATA - I2C_FUNC_SMBUS_READ_WORD_DATA - I2C_FUNC_SMBUS_WRITE_WORD_DATA - I2C_FUNC_SMBUS_PROC_CALL - I2C_FUNC_SMBUS_WRITE_BLOCK_DATA - I2C_FUNC_SMBUS_READ_I2C_BLOCK - I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
and looking at the target: