Skip to end of metadata
Go to start of metadata

You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 6 Next »

This content was presented May 23rd, 2020

Introduction

Welcome to firmware 102! By now you should’ve completed your environment setup and run the command to build and run a project on x86.

In this lesson we’ll be covering:

  1. Running code

  2. Writing code

  3. Tests + Validation

  4. GitHub and JIRA process

Recap

First, as a quick recap of the last lesson, here’s what you should know:

  • Firmware is code that runs as soon as a computer is powered on

The non bolded lines in the above diagram are the projects we have as firmware, the bolded lines are categories

  • We work remotely by using x86 versions of our libraries, which don’t necessarily work the same as the real versions we use on hardware

  • We have a 6 step process for completing a project, half of which is testing

  • We collaborate using GitHub and JIRA

Running code

Let’s talk about what “running on hardware” actually means. 

Microcontrollers

At the end of the day, this is what runs your code:

STM32f0 Microcontroller (AKA MCU, controller, STM, STM32, Integrated Circuit [IC], or other)

Each pin has a specific purpose, but at a high level, the pins are what connect to the other electrical parts of the car. Looking back at the firmware system overview, each project is programmed onto one of these, which then runs the code upon being powered, as discussed previously. These are like the CPU in your laptop, but 50x cheaper and simpler.

Hardware interlude

Looking at things from a different perspective, if you have to solder (gluing parts onto the board) these onto a PCB, you probably hate your life. One small thing goes wrong and you have to redo the whole board. Also, boards often undergo revisions from human error or design errors, so every time you make a new version of a board you’d have to redo the soldering for this. This is a PCB.

Instead, we have a universal controller board. Pictured below is the underside of the board.

Outlined in red below is the controller board mount, so you can imagine the above picture being flipped over and placed on top.

Pictured below is a controller board placed into its mount.

We have lots of these. So, hardware makes all their boards compatible with this one controller board, then we can reuse them for every project. The pins on the STM are passed through the connections in the slot so it can access the board’s functionality.

Getting code onto the controller boards

This doesn’t matter so much if you’re working remotely, but it’s still good to have a conceptual idea of how your code runs on real hardware.

(this isn’t our hardware, just a picture I found online)

The usb looking thing pictured above is called a programmer: it connects your computer directly to pins on the microcontroller.

Ours connects to the pins outlined below:

If you ever work with a hardware member to test your code, they’ll be using one of these to “program” a controller board. Programming a controller board means writing your code into its memory, then rebooting it so it runs your code.

Writing code

Next, let’s talk about actually writing  code! We’ll go through a simple example to show how our programs are laid out.

This is from the project controller_board_blinking_leds, which you can find here: https://github.com/uw-midsun/firmware_xiv/blob/master/projects/controller_board_blinking_leds/src/main.c . Don’t worry too much about the details, I just want to point out the main building blocks. This is an example project, but there’s some patterns we always follow.

1. We use an infinite loop structure. This is so we never have to restart the controller board. Otherwise the program would end and the controller board would just do nothing until it was restarted.

2. If a function takes a lot of arguments, we group them all together in a struct. If you ever see a struct called “settings”, it’s for wrapping together variables that would have to be passed as arguments. The parts the struct is used for are underlined in green.

3. We use libraries for doing things like interacting with hardware. Here we see the GPIO (general purpose input/output) library in action. Libraries are code someone else has written that help you do something. Here, the GPIO library sends the controller instructions to toggle a pin on and off. The GPIO library calls are underlined in blue. We’ll be covering exactly what this library does in more detail in a later lesson

The fact that we use libraries is actually really important to letting us work remotely - recall how we use different versions of the libraries depending on if we’re running the program on our computer or on the STM.

Looking at a real project, we split our files into three folders:

Any .c files go in src (source), any .h files go in inc (include), and any test files go in test. The rules.mk file is for compiling the project, but you shouldn’t need to worry about it for a while.

Pictured above: header files should include public functions, while the source .c files may include extra private functions.

Here, the red underline is a private helper function not in the header file, while the blue underline is the function from the header file. Private functions should be prefixed prv_.

A full list of our coding standards are here to review at your leisure: Coding Standards

Tests + Validation

There are three parts to this. Yes, testing is that important.

  • Unit testing: testing small parts of our code by writing more code. Ideally, each .c file other than main should have an associated test file that tests the logic. These test files are meant to be run on x86. They’re run every time you open a pull request on GitHub.

Here’s an example of a unit test from the tests for our GPIO library. It sets up the pin, asserts that it was set up correctly, then ensures checking the state returns the value it was initially set to (notice that in the init settings the state is HIGH).

  • Hardware testing: running the project on the actual hardware. Also, creating validation projects that test specific parts of the hardware, but not necessarily implementing the logic. We call these smoke tests. More documentation can be found here: Smoke Tests .

  • Integration testing: Wiring a bunch of boards together and making sure they play nice. Hopefully by this stage our code is well tested and the logic is bullet proof, but there’s always something that comes up here.

When working remotely, hardware testing will occur by either asking a hardware member to flash your code and then telling you the results, or by video calling and debugging the project together.

GitHub and JIRA process

JIRA

In JIRA we use what are called tickets for tracking tasks. There are five locations a ticket can be:

When tickets are created, they go to the backlog. Assigned means there’s a deadline, in progress means the ticket is currently being worked on. We’ll talk about code review in the GitHub portion. After code review is complete, the ticket is moved to done.

Tickets are then organized into ‘Epics’: groups of tickets that go together. Pictured above, Front Power Distribution in yellow is an epic, with two tickets falling under that epic.

We want to keep JIRA tickets just for concrete tasks. If you need to have a meeting, don’t create a JIRA ticket for it, note it somewhere else like on confluence.

Each JIRA ticket has a number, which will be important for GitHub.

For now, only the leads will create tickets. If you find an issue, report it to a lead.

GitHub

Once you’re assigned a ticket on JIRA, the next step is to create a branch on GitHub. If this is unfamiliar to you, read up on git or watch Arshan’s video lectures on the subject.

Branches should be prefixed soft_xyz, where xyz is the ticket number, and should have a descriptive name following. For example, soft_148_center_console_fault_handling.

Once you complete your task, you’ll open a pull request. This lets others see your changes before merging them into our master firmware branch. Before you do, make sure to run ‘make lint’ and ‘make format’ on your box! This formats your code and checks for formatting errors.

When you open a pull request, our continuous integration tool called Travis runs. This basically runs your code, and makes sure all the unit tests pass.

The blue outline means that Travis successfully ran your code and that all unit tests passed. The red highlight means changes were requested, so the person who opened the pull request needs to make those changes before merging it in.

This is where the code review part comes in: we’ll look through your changes and maybe ask for edits to be made. Once that’s done, you can merge in your changes and delete the branch.

Conclusion

That was a lot to get through, hopefully you made it! In conclusion, here’s what you should now have an understanding of:

  1. How we run code on hardware

  2. How we structure our files and projects

  3. How we test and validate our code

  4. How we use JIRA and GitHub to manage tasks

In the next tutorial, we’ll be covering how to read datasheets and schematics, as well as our basic libraries for GPIO, interrupts, and ADCs.

Homework

Follow as much of the Software 101 modules as you can while working remotely. Start here: Module 2: Hello World

Notes: Don’t worry too much about GDB debugging, in reality print statements (LOG_DEBUG) is very often used. Also, don’t worry about the “Blinking LED - Registers” section, it’s more advanced and definitely not necessary going forward.

Next, create a new project in a new branch soft_999_{firstname_lastname}_fw_102 that does the following:

  • Defines a struct that stores two uint8_t counters, counter_a and counter_b. typedef this struct Counters.

  • Uses a single soft timer to increment counter_a every half a second, and counter_b every second.

  • LOG_DEBUG the value of each counter whenever they’re updated.

  • Doesn’t use any global variables

  • Implements all code in main.c

Your output should look something like this:

If you run into problems, let someone know! No need to bash your head against a wall.

Hints:

  • Refer to the blinking LED tutorial for how to use soft timers.

  • Use a while (true) {} loop to keep the program running.

  • Don’t forget to start another soft timer within your soft timer function call.

  • In your main function, create a counters struct, and pass a pointer to that struct to the soft_timer_start_... call as the context.

  • Check the supplementary material: Firmware 102 Supplementary Material for more hints on how to use logging and soft timers

Once you’re done with the project, open a pull request and request a code review from a lead. We won’t actually merge your pull request, but it’ll be a good chance to learn how code review works.

[Optional]: watch Arshan’s videos on GPIO, interrupts, and ADCs.

GPIO: https://www.youtube.com/watch?v=JXQVHOYHbdk&list=PLwHCeNgf9lKdt6LN6D54__moOb4Mkj5NQ&index=9

Interrupts:

https://www.youtube.com/watch?v=zGIE8bLAFqQ&list=PLwHCeNgf9lKdt6LN6D54__moOb4Mkj5NQ&index=10

ADCs:

https://www.youtube.com/watch?v=pIitjg4jfJg&list=PLwHCeNgf9lKdt6LN6D54__moOb4Mkj5NQ&index=11

  • No labels