Implement a 2(or more)-input logic gate

1. Specification

Construct a 2-input logic gate that uses microcontroller input pins and outputs the result to another pin. The same application code shall run on two different platforms (MSP340 and ATmega328p) without modification.

Your main application code should be in the file app.c with a companion header file app.h and contain the functionality for this specification.

Each platform can have additional files which create an interface between hardware-specific configurations and functions that app.c calls which read input pins and change output pins. These files are part of your board support package.

Demonstrate that your app.{c,h} is platform-independent by running on both your LaunchPad and Nano clone.


This is a good way to demonstrate your code is running on hardware as expected. However, for this project, this task is not a must-do priority.

Measure the delay times for relevant transitions using your AD2 and Waveforms. Report the min/max/average (or histogram!) times for each platform..

2. Examples and suggestions

2.1. Contrast with the Ctrl-V method

Open Code Composer Studio with your code from Lab 2 that had the buttons -> LED logic gate. What that code does is essentially the same as this project, we are only changing the where and how.

Select all of the code in your main.c file, which should be the only file of code in your project, and copy it to the clipboard.

Next, open up the Arduino IDE, start a new project, and paste that code into the window. Then attempt to compile the code. This, of course will, fail, so proceed to make changes to the code so that it now works on your Arduino Nano.

Notice how you didn’t need to change everything? The changes to this code are what is considered platform dependent and the code that does not need to change is considered platform independent.

Take this idea one step farther by putting all of the code that remains the same between the two platforms into one file (app.{c,h}). The code that must be different is factored out into a separate file.

2.2. Suggested files

As a possible example, you can have a pair of board_support.{c,h} files. The header file contains the function signatures and there is a different C file for each platform that implements the the functions.

Perhaps it contains functions like:

  • init_board() - prepares the system configuration

  • read_inputs() - returns the state of the input(s)

  • set_output(state) - changes the output pin state

The specific type signatures of these functions is up to you. Even the function names are your choice as you deem appropriate. They must be the same for all of your platforms, however, so the header file board_support.h remains the same.

2.2.1. app

An example framework for your app.c is shown below. Notice how this code does not have main()? That function is part of your board-specific code.

#include "app.h"
#include "board_support.h"


void init_app(void)
{
	// setup the application
	// especially if it has configuration

	// (Project 1 does NOT have configuration)
	//		...unless you want it to
}


void app()
{
	int x;

	while (1) {
		// your application code that does the stuff
		// read inputs
		// set outpus
	}
}

Example content for app.h:

#if !defined(__APP_H)
#define __APP_H

void init_app(void);  // <-- probably *not* needed for this project
void app(void);

#endif

2.2.2. board_support

The key idea with a "board support package" is that it provides a known set of constants and functions that other applications can use irrespective of the specific platform.

This set of files has the same header file, but a different .C file for each platform the code should run on.

board_support.h example:

#if !defined(__BOARD_SUPPORT_H)
#define __BOARD_SUPPORT_H

void init_board(void);

int read_input_A(void);
int read_input_B(void);
void set_output(int pin_id);

#endif

board_support.c then includes the code specific to the platform that does the implements the functions:

#include "board_support.h"


#define PIN_A 0xGG
...

void init_board(void) {
	// set pin to input
	// set pin to ouput
	// disable watchdog timer
	// ...etc
}


/*
 * also constrain return value to be 0 or 1 (only)
 */
int read_input_A(void) {
	// read a port
	// bit-twiddling

	return state;
}


int read_input_B(void) {
	// more stuff

	return state;
}


void set_output(state) {
	// something appropriate
	PxOUT |= foo;
}

2.2.3. main

Example form of the main.c for the MSP430:

#include <msp340.h>

#include "board_support.h"
#include "app.h"


void main()
{
	// stuff specific to the MSP430 and LaunchPad
	init_board();


	// prepare the application for running
	// sending configuration as arguments, if needed
	init_app();

	// then call your application code
    app();

	// we are assuming that app() never exits
	// or you can wrap app() in a while (1) loop
}

The Arduino IDE version would be something like:

extern "C" {
	#include "board_support.h"
	#include "app.h"
}

void setup() {
	init_board();  // from board_support.h
	// or just do that setup here and not use init_board()

	init_app();  // from app.h.  not for this project
}


void loop() {
	app();
}

3. References

3.1. msp4th

A much more complex example of a single core application setup to run on two different platforms is https://github.com/etihwnad/msp4th

The code runs on

  • PC in a terminal window

  • Custom MSP430-like microcontroller

With respect to the project specifications, the two files msp4th.{c,h} correspond to app.{c,h}.

main.c was compiled for a custom MSP430-like processor embedded in a series of custom chips, which would run either code from an external SPI flash IC or an interactive Forth-like interpreter from ROM. The UART port provides the I/O. Eventually, this will be ported to run on a LaunchPad (help wanted!).

test4th.c uses the exact same code from msp4th.{c,h} but runs on a PC using the terminal as I/O.

The only interface between the interpreter and everything else except RAM is three functions with signatures:

void putchar(uint8_t);
uint8_t getchar();
void puts(uint8_t *);

Pointers to the specific versions of these functions are assigned to msp4th_config structure, which is then passed to msp4th_init() at startup. The interpreter is started by calling msp4th_processLoop(). It is possible with the structure configuration to change the configuration inside of the interpreter and restart into those new settings.

The file cosim.py demonstrates how it is (was) possible to run the same forth code on both the hardware and PC versions of msp4th at the same time. Such co-simulation helps ensure that the interpreter behaves exactly the same independent of the hardware platform.

  • Presently, only the PC version can be expected to work since the hardware was custom and only shared the instruction set, not all the other memory-mapped configuration registers. -> porting is still a work in progress.

3.2. BLE scanner

This is an Arduino IDE project using an ESP32 to sniff Bluetooth LE packets and log them to a server.

It is an example of having multiple files with the Arduino IDE.

  • Configuration constants are added to the application in config.h

  • A logging function is added using logger.h. This header includes the code and not just the function signature, so there is no corresponding logger.c file in this case.