Introductory Labs

Lab: Periodic Scheduler

Objective of this assignment is:

  • Setup your development environment
  • Learn how to run unit-tests
  • Trial how to input your code to the Periodic Scheduler

For CmpE243, we will not be focusing on typical RTOS tasks like CmpE244. The reason is because we wish to use an approach that is typically seen in the Automotive industry which is to design logic of your autonomous RC car based on software instructions that occur periodically and consistently.


Part 0

Setup your development environment for this portion of the lab. Follow through and read all of the README files carefully that are linked here. Make sure you are able to run the unit-tests, and also compile a hex file that you can load onto your board.


Part 1

For this portion, edit the code such that it will start to blink four LEDs driven by the periodic scheduler. In particular, read the documentation of the main.c file, and enable the code for the periodic scheduler.

Study the overall structure of main.c, and then switch a #if (1) to #if (0) such that it will disable two blinky tasks, and instead run the periodic scheduler. The name "periodic scheduler" may sound more fancy than what it actually is, but this is just trivial piece of code that invokes function at periodic_callbacks.c file.

static void create_blinky_tasks(void) {
  /**
   * Use '#if (1)' if you wish to observe how two tasks can blink LEDs
   * Use '#if (0)' if you wish to use the 'periodic_scheduler.h' that will spawn 4 periodic tasks, one for each LED
   */
#if (0)
  // ...
#else
  const size_t stack_size_bytes = 2048 / sizeof(void *);
  periodic_scheduler__initialize(stack_size_bytes);
  UNUSED(blink_task);
#endif
}

There are a few things to note for future reference:

  • The stack size is chosen with a sane value, and depending on the complexity of the functions you invoke at the periodic_callbacks.c file, you may have to increase this memory size. Also note that there are five tasks total that run the periodic callbacks, so if you input 2K, then you will end up using 10K for the memory footprint.
  • The logic at periodic_callbacks.c file should be function calls into your other code modules. This way, unit tests of this file will remain simple. You do not want to input branch statements here because this would make your code less modular, and difficult to unit-test.

Part 2

Insert additional code to one of the periodic callbacks, and then observe its operation. In the example below, we are going to demonstrate the right way to build a module that reads a switch and lights up an LED.

DO NOT do the following because what you have done is that cluttered all the things that need to occur periodically. If we go down this path, you will end up creating a giant periodic_callbacks.c file that will be difficult to test, and your code will not be modular or broken down into this pieces. Unit-testing code will also be difficult because now you have to not only test the switch and LED logic, but also test more unrelated subsequent code.

// periodic_callbacks.c -- BAD example

static gpio_s my_led;
static gpio_s my_switch;

void periodic_callbacks__initialize(void) {
  my_led = gpio__construct_as_output(GPIO__PORT_2, 0);
  my_switch = gpio__construct_as_input(GPIO__PORT_2, 1);
}

void periodic_callbacks__1Hz(uint32_t callback_count) {
  gpio__toggle(board_io__get_led0());

  if (gpio__get(my_switch)) {
    gpio__set(my_led);
  } else {
    gpio__reset(my_led);
  }
}

Instead, follow good code design, and create "modules" for your code. Using this approach, you have refactored your switch and LED logic to a new code module: switch_led_logic.h. You can test this code module separately and then testing the periodic_callbacks.c code module is also straight forward since you only have to setup a couple of "expect" function calls.

// periodic_callbacks.c -- Good example

#include "switch_led_logic.h"

void periodic_callbacks__initialize(void) {
  switch_led_logic__initialize();
}

void periodic_callbacks__1Hz(uint32_t callback_count) {
  gpio__toggle(board_io__get_led0());

  switch_led_logic__run_once();
}

Of course, you are not done yet, and you also have to modify test_periodic_callbacks.c 

#include "Mockboard_io.h"
#include "Mockgpio.h"
  
// Add mock of your new code module
#include "Mockswitch_led_logic.h"
#include "periodic_callbacks.h"

// Add expect during the periodic_callbacks__initialize() function
void test__periodic_callbacks__initialize(void) { 
  switch_led_logic__initialize_Expect();
  periodic_callbacks__initialize(); 
}

void test__periodic_callbacks__1Hz(void) {
  gpio_s gpio = {};
  board_io__get_led0_ExpectAndReturn(gpio);
  gpio__toggle_Expect(gpio);

  switch_led_logic__run_once();
  periodic_callbacks__1Hz(0);
}

Part 3

Deliberately overrun one of the periodic tasks and observe that your board will reboot. Since this will be sort of a "throw-away" code, you can opt to skip the unit-tests. Here is sample code that will deliberately reboot the processor because of the missed deadline of the 1Hz function.

// periodic_scheduler.c

// Include these files for RTOS task delay function
#include "FreeRTOS.h"
#include "task.h"

void periodic_callbacks__1Hz(uint32_t callback_count) {
  gpio__toggle(board_io__get_led0());

  // On the fifth function call to this function, sleep for 1000ms
  if (callback_count >= 5) {
    vTaskDelay(1000);
  }
}

It is strong advised NOT to skip the unit-tests in general. But if you are purely doing a code prototype to try things out, then use the scons --no-unit-test command.

 


What did you learn?
  • Work with the periodic callbacks to add your code
  • Design small code modules, and setup their expectation in unit-test code
  • First-hand account of what happens when you miss the deadline of a periodic callback

 

Lab: Git

This is definitely not an exhaustive tutorial about learning Git ... Google would be better to reveal several great tutorials about Git. What we focus on instead is simplistic workflow about publishing a "Pull Request" in Git.


Part 0: Setup Gitlab account

For better or worse, we have decided to use Gitlab.com for the repository. You are also required to use this Gitlab repository because that keeps the entire class aligned to a single server type and reduces fragmentation while increasing efficiency of the teacher and the ISA team.

For this part, establish your Gitlab.com account. In addition, also install Git to your machine such that you can successfully execute the git command from a terminal.


Part 1: Fork SJ2-C Project

When you fork a project, you essentially create a copy of the original SJ2-C repository. This will be your version of the forked project, and you can use this throughout the semester for your private workspace to do the lab assignments.

Browse to the SJtwo-c repository, and click on the Fork button.

fork.png

After you fork the repository, make sure you set the permissions to "public". Do this by going into your newly forked repository settings, and look for "Visibility" setting.


Part 2: Branch Workflow

The process of checking-in new code to your forked repository will involve "Branch Workflow". There are actually a number of ways to contribute code to your repository, and the branch workflow is just one of them that we will choose to use.

We are not going to discuss that in detail because it is already captured well at this awesome article. We will summarize the process that you will use to do this. The $ indicates the commands you should try.

# See what is going on
$ git status
On branch master

# Create a new "branch" of code to work on
# You can use any name, and feature/foo is just a convention
$ git checkout -b feature/gpio_blinky_in_periodics
Switched to a new branch 'feature/gpio_blinky_in_periodics'

# Add or modify a file we want
$ touch file.txt

# Tell git to add it to be committed
$ git add file.txt

# Check what is going on
$ git status
On branch feature/gpio_blinky_in_periodics
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)

new file: file.txt

# Commit the change with a message
$ git commit -m "added file.txt"
[feature/gpio_blinky_in_periodics 5f76839] added file.txt
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 file.txt

# Check what is going on
$ git status
On branch feature/gpio_blinky_in_periodics
nothing to commit, working tree clean

Part 3: Merge Request (MR)

The typical name for a request to merge code is called a "Pull Request" or a "Merge Request". This is the chance to review the code, and merge the code. At the end of Part 2, you have a branch that only exists on your computer. In case you lose your computer or your storage device dies, then you will lose any work even though you have "committed" a change.

The distinction is that a commit only commits to your storage device, but does not send the data or the branch to the Git server. To actually push the code to the Git server, simply type git push origin head.

$ git push origin head
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Delta compression using up to 12 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (2/2), 262 bytes | 262.00 KiB/s, done.
Total 2 (delta 1), reused 0 (delta 0)
remote: 
remote: To create a merge request for feature/gpio_blinky_in_periodics, visit:
remote:   https://gitlab.com/sjtwo-c-dev/sjtwo-c/-/merge_requests/new?merge_request%5Bsource_branch%5D=feature%2Fgpio_blinky_in_periodics
remote: 
To gitlab.com:sjtwo-c-dev/sjtwo-c.git
 * [new branch]      head -> feature/gpio_blinky_in_periodics

This command will generate a URL for you, so copy and paste this URL to your web browser. For example, the URL above is: https://gitlab.com/sjtwo-c-dev/sjtwo-c/-/merge_requests/new?merge_request%5Bsource_branch%5D=feature%2Fgpio_blinky_in_periodics

This will lead you to generate your "Merge Request". At the end of the webpage that loads, click on "Submit Merge Request". At this point, you can view the changes, get feedback from others, and if the code looks good, you can then merge the code. But wait ... rarely will you be able to merge code without iterating and revising it, and that is what the Part 4 is for.


Part 4: Revise an MR

Granted that you have an MR already out there, and you have got feedback from others, this section will teach you how to revise or amend your code.

# Modify any code
# In this case, we will dump 'hello' to our previously committed file: file.txt
$ echo "hello" >> file.txt

# Check what is going on
$ git status
On branch feature/gpio_blinky_in_periodics
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

	modified:   file.txt

# Add the file we want to re-commit (another commit on top of previous)
$ git add file.txt
$ git commit -m "Added hello to file.txt"

# Update the remote branch and the Merge Request
$ git push origin head

After the git push command, your MR will be updated on the browser. This way, you can continue to revise your MR per suggestions of other people. When you are satisfied with your MR, you can seek approval and officially hit the Merge button on the Gitlab.com webpage.


Part 5: Final Step

After you have merged your MR, it is time to go back to the master branch and grab the latest changes. Other users may have merged their code also, so pulling the latest master branch is going to get you the latest and greatest code.

# Go to the master branch
$ git checkout master

# Pull the latest master
$ git pull origin master

Part 6: Going beyond . . .

There is of course A LOT more to Git, but once you master the basics, you can then Google your way through the rest of the world you will face such as:

  • Handling merge conflicts
  • Checkout other people's branches

Rebase on the latest master branch.

$ git status 
# Assume that you are on your feature branch

$ git checkout master
$ git pull origin master

# Go back to the previous branch you were working with (feature)
$ git checkout -

# Apply our commits to the latest master
$ git rebase master