Unit Testing The MSP430 Within A Desktop Environment: Kris - Dickie@clarius - Me
Unit Testing The MSP430 Within A Desktop Environment: Kris - Dickie@clarius - Me
The MSP430™ microcontroller (MCU) is a low- The architecture described here is a C++ based
power device designed and produced by Texas system that tries to encapsulate hardware modules
Instruments (TI), and comes in a variety of models. within instantiated objects. Because of the
The purpose of this technical paper is to explain relatively static nature of embedded programs, all
how to build and run a system for unit testing object instances are declared up front and no
MSP430 MCU code within a desktop environment. dynamic memory allocation is used. This is often a
For embedded designs, emulators are often the best good approach to use within environments outside
choice for testing software, however there may be of desktop computing as resources are generally
restrictions such as inability to deploy emulators more optimized.
easily to test systems, cross-platform restrictions,
or the complete non-existence of an emulator. This The tests are designed to instantiate objects at any
paper describes the setup and gives examples of point within the test execution and make
how to write and execute MSP430 MCU code appropriate calls to the public member functions of
within a Linux or Windows desktop environment, each object.
and has been tested using Google’s test suite
Google Test (gtest). It’s worth mentioning that the
examples and framework described make use of
some C++11 and standard thread support library
built into C++, though most native embedded
develop prohibits such use.
The testReg reference is a templated class which The testReg class creates two member variables,
allows the program to define registers of different val_ which holds the current register value
sizes (char, int, and long for example), and is (initialized to 0), and lock_ which is used for
derived from the C++ class thread synchronization when an outside value is
std::condition_variable which allows each
waiting for a register value to change.
register to individually notify any thread that is
waiting for it’s value to change. The class also makes use of operator overloading
to handle direct and bitwise assignments, when
The definition of all template classes need to be in these are used from within the MSP430 MCU
a header file in order for them to compile. program, a call to notify_one() will notify any
template <typename T>
thread waiting on that register value to change, so
class testReg : std::condition_variable that it may read back the value or continue it’s
{ execution.
public:
testReg() : val_(0) { } Example:
testReg(T val) : val_(val) { }
void thread1()
T wait() {
{ P1OUT = 0;
std::unique_lock<std::mutex> lk(lock_); // start thread 2
std::condition_variable::wait(lk);
while (!spi.finishedParsingBuffer());
Using similar methods can be used to test the I2C
t.join(); or UART bus, as well as ADCs. Each will have
their own nuances that need to be addressed, for
ASSERT(spi.buffer == buf); instance, I2C tests will likely have to deal with
setting of start and stop flags, and possibly handle
This is just one potential method to try and NACKs sent as an interrupt.
synchronize the SPI read, another could be to
actually wait for the receive buffer to receive a VII. Simulating Timers
value, and call a separate parsing function similar
to the following: As with communication buses, timers can also be
somewhat difficult to test because of their reliance
int count = 0;
while (count++ < buf.size());
on a temporal interrupt.
{
UCA2RXBUF.wait(); There are many C++ constructs for timers, but one
spi.parseNewData(); of the best for high-resolution timing lies within
} the boost::asio libraries, specifically the
deadline_timer.
To test the full response chain on a SPI bus, with
an object waiting for messages, parsing them, and To setup a system that will generate timer
then transmitting them back. For instance if there interrupts, a deadline_timer can be instantiated
was a query for the battery level of a device, we with a specific interval that is based on a
can use the above examples to implement the capture/compare register. An example for
receive side to respond to the request, and then Timer1_A3 running, capture/control register 0.
monitor the SPI transmissions in a separate thread.
class msptimer
void monitor(int sz, std::vector<char>& {
buf) public:
{ void testInterrupt() { onTick(); }
while (buf.size() < sz)
{ void init(int frequency)
UCA2TXBUF.wait(); {
buf.push_back(UCA2TXBUF); // perform setup
} TA1CCR0 = (ACLKFREQ / frequency) + 1;
} }