You are not Logged In!

Public:Onboarding Fall 2022 - Project 1 (LED Blinking)

From Illini Solar Car Wiki
Revision as of 22:48, 12 September 2022 by Alexdl2 (talk | contribs) (Created page with "If you're not familiar with or you'd like to brush up on C++ / object-oriented programming, please read through the Onboarding Fall 2021 - Basics of C++|basics of C++ (with...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

If you're not familiar with or you'd like to brush up on C++ / object-oriented programming, please read through the basics of C++ (with firmware examples) before starting this project.

Please read through the simplified explanation of firmware as well as the slightly more detailed explanation. Reading through the general firmware standards is optional for this project but good if you'd like to do more firmware later.

What You'll Be Doing

For this project, you’ll be writing C++ to blink an LED on and off once per second. The code is simple, but this will be an opportunity to get familiar with MCUXpresso IDE and the general structure of our firmware.

Getting Started

Make sure that you’ve set up MCUXpresso on your computer. This is an IDE, similar to Visual Studio, Eclipse, or IntelliJ, except that it provides more support for microcontroller programming. Follow the steps for your operating system carefully or you could encounter problems later.

Make sure that you’ve checked out the master_fall2021_onboarding_project1 branch on your computer (and the appropriate submodules). This branch has the starter code for this project, in the mbed15x9_skeleton folder.

Then you want to create your own branch “off” of this branch that contains your (or your group’s) work. Do not push any changes to the master_fall2021_onboarding_project1 branch on Github. That branch contains the starter code that everyone's using.

The Starter Code Structure

Now that you have everything set up, you’re ready to start programming. We’ll look at the actual code so you have a basic understanding of how the starter code is structured.

In the mbed15x9_skeleton folder, open up the inc and src folders in the project directory. Only modify the code in these 2 folders for this project.

As described in the "Basics of C++" page, the inc and src folders contain the header (.h) files and cpp (.cpp) files for this project, respectively. Header and cpp files are a feature of C/C++. At a basic level, header files contain the declarations (names) of objects/functions while cpp files implement the code for them. These inc and src folders are a basic version of what every firmware project look like - every firmware project will contain at least these files, plus other PCB-specific files.

The Starter Files

The inc folder should have these 3 files:

  1. peripherals.h - This file contains the names of objects that utilize the MCU pins to control things like timing and communication with other chips on the PCB.
  2. pins.h - This file renames MCU pins to easy-to-remember macros that we use throughout the rest of the code. It also declares some digital/analog objects which set certain pins to be digital or analog I/O.
  3. setup.h - This file defines macros (discussed in the "Basics of C++" page) that we use throughout the rest of the code.

The src folder should have these 3 files:

  1. peripherals.cpp - This file actually instantiates the objects that are declared in peripherals.h. The objects in peripherals.cpp must match those in peripherals.h, or else you might get build errors.
  2. pins.cpp - This file actually instantiates any digital/analog objects declared in pins.h. The objects in pins.cpp must match those in pins.h, or else you might get build errors.
  3. main.cpp - This is the heart of any ISC firmware project. You’ll see some basic function declarations, along with a main() method - this main() method is the code that actually gets executed on the MCU. Every other firmware project uses a main() method. The main() method works by using functions, variables, and objects declared in other files or other parts of main.cpp.

The main() method

Here’s a rundown of the main() method structure:

  1. It first calls the setup() method (also in the main.cpp file) - any functions to set up hardware, communication protocols, and timing are called here.
  2. You’ll then see some variable declarations, including the boolean shutdown variable.
  3. The next item is a while (!shutdown) {...} loop that gets run repeatedly. In this while loop is where most of the important firmware work (specific to each firmware project) gets done.
    1. For example, in the MCC firmware, code that sends the driver-requested RPM to the motor gets called in its while loop. In the steering wheel firmware, code to update the wheel screen is written here.
    2. In addition, code to process CAN messages and functions that get called at set time intervals are usually written in the while loop.
    3. The while loop should also check for things that might trigger a shutdown, such as turning the car off or a battery error. In that case, the while loop should set the shutdown boolean variable to true. As you can see in the while loop header condition, the while loop is exited once the shutdown variable is true.
  4. Once the while loop is exited, the shutdown_method() function is called. While not always necessary, this method calls other functions to shut down any hardware or electrical components that need it so that they’re not left in a weird state.

In essence, the main() method lifecycle in this project (and most firmware projects) is setup() -> while loop -> shutdown_method()

What You Need To Do

You need to blink an LED every second - essentially, you need to toggle its “state” between on and off every second. We'll set up an MCU board connected to an LED circuit - you'll write the code to actually make it work.

pins.h

In pins.h, notice the pin macro section (P_CAN_rd, P_CAN_td, P_LED1, etc.). Each macro is some name indicating what the pin is for followed by an actual pin number, which allows us to use the name in place of the pin number. Also notice the section where all the extern DigitalOut objects are declared. In pins.cpp, you can see the instantiation of all those DigitalOut objects, using the pin macros in pins.h. These DigitalOut objects set their pins to output digital data (bits). Thus, we use DigitalOut objects when we want the MCU to set pins high or low. You’ll have to set up a pin macro, using pin P0_4 which we’ll connect to an LED circuit for you to test, and a DigitalOut object so that the pin can output high/low bits to toggle the LED.

setup.h

Look at setup.h. You'll use the TASK_1_RATE_US macro to control the rate at which the LED blinks. You can name it something more descriptive if you'd like.

main.cpp

In main.cpp, you’ll have to include a certain header file to actually use the DigitalOut object that you made earlier. You'll have to include it where it says to at the top of the file.

Look at the while (!shutdown) {...} loop in the main() method. You should see an if statement that looks like if(timing.tickThreshold(last_task_1_time, TASK_1_RATE_US) {...}. Within that if statement, you'll have to write a line (or few) of code to make the LED blink on and off.

  1. Notice the use of the timing.tickThreshold(...) in the if statement condition. The "timing" variable is a TimingCommon object that we use for timing-related functions on the MCU. One of its methods is the tickThreshold(...) function. It may help to open up common/api/TimingCommon.h and common/common/TimingCommon.cpp to see function documentation and the function body.
  2. Essentially, the tickThreshold(...) method will compare the difference between the current time, an unsigned 32-bit integer (uint32_t) representing the time in microseconds since timing was initialized (stored privately in the TimingCommon object), and last, a reference uint32_t parameter which represents some other time in microseconds. If the current time - last is greater than interval, another uint32_t parameter, then the function sets last to the current time and returns true. Otherwise, the function does nothing and returns false.

Go back to that if statement in main.cpp and think about how a method like timing.tickThreshold(...) can be used in the if statement condition to call a function at a set interval. Remember that if the method returns true, then the if statement condition is true and the if statement code executes. If the method returns false, then the if statement condition is false and the if statement code doesn't run.

DigitalOut.h

Once you understand that, open mbed/libraries/mbed/api/DigitalOut.h and see what method(s) you can use there to set the DigitalOut object you created high or low. Also look at what method(s) you can use to read the DigitalOut pin state - you'll need to know whether the pin is currently high or low in order to toggle it to the opposite state.

By adding one (or a few) lines of code to the if statement in main.cpp, you can blink the LED on and off.

There's an alternate way you can blink the LED - look at the timing.addCallback(...)method in the main.cpp setup() function for a clue. You can try to figure out how to blink the LED this way if you'd like.