Sei sulla pagina 1di 132

Real-Time

Applications
using MSP432P401R LaunchPad
and Raspberry Pi


By Yury Magda


Copyright 2015 by Yury Magda. All rights reserved.


The programs, examples, and applications presented in this
book have been included for their instructional value. The
author offer no warranty implied or express, including but not
limited to implied warranties of fitness or merchantability for
any particular purpose and do not accept any liability for any
loss or damage arising from the use of any information in this
book, or any error or omission in such information, or any
incorrect use of these programs, procedures, and applications.

No part of this publication may be reproduced, stored in a
retrieval system, or transmitted in any form or by any means,
electronic, mechanical, photocopying, recording, or otherwise,
without the prior written permission of the author.

Contents

Contents
Introduction
Disclaimer
Raspberry Pi GPIO pins
Designing Real-Time applications: project 1
Designing Real-Time applications: project 2
Designing Real-Time applications: project 3
Designing Real-Time applications: project 4
Designing Real-Time applications: project 5
Designing Real-Time applications: project 6
Designing Real-Time applications: project 7
Designing Real-Time applications: project 8
Designing Real-Time applications: project 9
Using an MSP432 32-bit timer in Real-Time applications
Designing Real-Time applications: project 10
Designing a Digital-to-Analog converter with MSP432
Designing Real-Time applications: project 11

Introduction

A popular Raspberry Pi miniature computer is often used for designing measurement and
control systems by professionals and hobbyists. Such systems can be driven via a generalpurpose input/output port (GPIO) with pins accessible through header J8 on the Raspberry
Pi board. With GPIO, user applications can process signals from various sensors and
control external loads (relays, motors, etc.).

The only drawback is that Linux OS running on the Raspberry Pi is not suited for
processing signals travelling via GPIO pins in real time. This prevents the Raspberry Pi
from using in real-time applications that should process signals in a very short time.
Meanwhile, Linux provides a powerful high-level environment for the development of
GUI applications performing high-level data processing. It would be attractive to combine
Linux high-level interfaces with real-time signal processing while building real-time
applications.

One way to build real-time applications is to apply a combination of the Raspberry Pi and
a stand-alone microcontroller (MCU) board capable of performing real-time tasks. In this
configuration, the MCU board could process signals from sensors in real time, drive
motors and actuators, etc. Such development board could execute commands from the
Raspberry Pi and return data via some interface (parallel, UART, SPI, I2C, etc.).

On the other hand, the Raspberry Pi could perform non-critical operations including highlevel processing the data obtained from an MCU board, data visualization and networking.
Such configuration can be used, for example, in image, video and audio processing
systems. In those systems, the Raspberry Pi could process the image or video from
cameras, while a stand-alone microcontroller module could control a precision motor
positioning a camera.

This book is dedicated to designing real-time applications using the Raspberry Pi 2 board
and very low-cost but effective MSP-EXP432PR401 LaunchPad by Texas Instruments.

There are a few reasons why MSP-EXP432P401R could be selected for developing realtime applications described in this book.
1. This is a very powerful board with the MSP432P401R (Cortex-M4F) processor
and numerous peripherals for only $12.99.
2. There are advanced (and free) development tools available for users. We can use
either the Energia MT IDE (Arduino-like IDE) or very powerful Code Composer

Studio (CCS) v.6.1.0.


3. A developer can achieve better performance for an application by direct
programming CPU and peripheral registers using simple and clear commands and
macros. Well-organized MSP432 Technical Reference Manual may come in handy.

The demo projects of real-time application described in this book may be used as the
templates for building various measurement and control systems. Each project includes
both hardware and software accompanied by the detailed description of what is doing.

The material of the book assumes that the readers are familiar, at least, with basics of
designing and assembling electronic circuits. For most projects having some basic skills in
electronics and low-level MCU programming will serve the readers well and allow them
to understand what is going on behind the scenes. Each project is accompanied by the
brief description that helps to make things clear.

All projects were designed using the Raspberry Pi 2 board running Raspbian OS, although
developers can use the earlier versions of the Raspberry Pi board as well. All of the source
code for the Raspberry Pi projects was developed in the Python environment running in
Raspbian OS. The program code for MSP-EXP432P401R LaunchPad was developed
using Energia MT IDE and Code Composer Studio 6.1.0 by Texas Instruments. Most
projects described in this guide can be easily improved or modified if necessary.

Disclaimer

The design techniques described in this book have been tested on the Raspberry Pi 2 board
and MSP-EXP432P401R LaunchPad without damage of the equipment. I will not accept
any responsibility for damages of any kind due to actions taken by you after reading this
book.

Raspberry Pi GPIO pins



Development platforms like Raspberry Pi provide an environment for software and
hardware experimenting. This is possible because availability of General Purpose
Input/Output pins (GPIO) on the Raspberry Pi 2 board.
The Raspberry Pi 2 is equipped with the header labeled J8. This header runs along the
edge of the board and has 40 pins. Most GPIO pins can operate in different modes thus
accommodating different possible functions. Note that digital pins must be either high or
low.
Each GPIO pin may be put in input or output mode. When output, a pin can be pulled
either LOW (log.0) or HIGH (log.1) by a program, so it is possible to control external
circuitry connected to this pin. Log.0 corresponds to the voltage level close to 0 volts,
while the log.1 corresponds to 3.3 volts. Reading the state of a loose pin returns the
unpredictable result. All the GPIO pins can also be reconfigured to provide alternate
functions (SPI, PWM, I2C, etc.).


Note that GPIO voltage levels are 3.3 V and are not 5 V tolerant. The 3.3 V logic level
of the Raspberry Pi 2 assumes that external logic components use only 3.3 volt.
The Raspberry Pi board has no over-voltage protection the intention is that people
interested in serious interfacing will use an external board with buffers, level
conversion and analog I/O rather than soldering directly onto the main board.

Chances are that external circuitry are be poorly assembled and have short circuits,
so I highly recommend you to apply a stand-alone DC 3.3V power supply for
powering your electronic circuits to prevent the permanent damage of the Raspberry
Pi.

Another aspect of interfacing the Raspberry Pi is maximum allowable current
through a single GPIO pin. The current has to be less than 8 mA, so any load that
draws more current can damage the board. When connecting external circuits to the
Raspberry Pi, you have to apply some CMOS buffer IC (for example, 74HC00,
74HC08, 74HC14, etc.) which isolates external circuitry from low-current GPIO
pins.
Be very careful when wiring the GPIO pins it would be a good idea to strip short
pieces of insulation off a wire and push them over the 3.3V pins so you dont
accidentally short them with a probe.

When attaching your electronic circuits to the Raspberry Pi GPIO pins use short

wires (10-12cm long) so that to ensure minimum distortion of digital signals. Using
long wires may cause your circuit to stop functioning properly.

Designing Real-Time applications: project 1



This section contains the first project of a real-time application using the Raspberry Pi and
MSP432P401 LaunchPad. Before we start off, lets consider in brief the key aspects of
developing real-time applications.
Nowadays, a growing number of microcontroller applications use Real-Time Operating
Systems (RTOS).
As you may know, common microcontroller/microprocessor (MCU) applications when
running use sequential processing loops and state machines. This approach works quite
well when a microprocessor board is performing a rather limited number of functions. A
different approach is required when an application involves more memory and peripherals.
If the application is about 64KB in size or less, there is no need to use an RTOS.
Conversely, if the size of the application reaches 1 MB, an RTOS should likely be used.

Using an RTOS gives a few key advantages.
Mostly an RTOS uses a preemptive multitasking approach which allows to increase the
performance of complex real-time applications, especially those with a code base that is
progressively enhanced in each release. The key driver of RTOS adoption is application
complexity. An RTOS will often be used when there are more interrupt sources, more
functions, and more standard communications interfaces that need to be supported.

A preemptive multitasking approach makes response-times to each real-time event
relatively independent of each other. As a result, new functions can be introduced without
disrupting existing hard real-time ones. This is impossible in a sequential processing loop
where each event is checked by polling, so the addition of a new event will affect the
response times for all events.

Although real-time response for critical events can be handled using background or
foreground loops to more frequently poll critical events, such approach significantly
complicates the maintenance of complex real-time applications.

Using an RTOS also allows to use MCU resources more efficiently. A simple loop-based
application typically does a lot of polling to check if interrupts have occurred. As a result,
a great deal of processor time is occupied doing nothing. Multitasking RTOS-based
applications are interrupt-driven, so it is possible to largely eliminate polling from the
application. This frees up processor resources for useful work and enables power-saving
modes to be invoked during idle periods.

As an application becomes more complex, it can cause a growing number of problems


such as excessive memory usage or leaks, delayed response to real-time events, or greater
than expected MCU loads, etc. Such problems are often difficult to diagnose since they
require a high-level understanding of system behavior and resource usage. Most RTOSs
have associated system-level debugging tools allowing to examine application behavior
and resource usage.

Our next projects will use the Real-Time Operation System by Texas Instruments called
TI-RTOS. TI-RTOS has associated tools that enable developers to look at stack usage and
compare it against how much stack was assigned to a task. This makes it straightforward
to detect stack overflows or to optimize task stack sizes to free up RAM for other parts of
the application.

To process analog and digital signals in real time we will use the MSP-EXP432P401R
LaunchPad by Texas Instruments (Fig.1).

Fig.1

This is an easy-to-use Evaluation Module (EVM) that is based upon the MSP432P401R
microcontroller. It contains everything needed to start developing on the MSP432 lowpower and high performance ARM 32-bit Cortex-M4F microcontroller (MCU), including
on-board emulation for programming, debugging, and energy measurements. The
MSP432P401R device supports low-power applications requiring increased CPU speed,
memory, analog, and 32-bit performance.

The MSP-EXP432P401R LaunchPad will be configured to run real-time tasks using the
multitasking capability of the free Energia MT IDE environment (the newest version
0101E0016 was used).
The Energia MT IDE is the open-source prototyping platform that uses the Wiring and
Arduino framework for programming Texas Instruments launchpads. The Energia MT
IDE is the cross platform and supported on Mac OS, Windows, and Linux. Energia MT
includes an integrated development environment (IDE) that is based on Processing.
Together with Energia MT, LaunchPad can be used to develop interactive objects, taking
inputs from a variety of switches or sensors, and controlling a variety of lights, motors,
and other physical outputs. LaunchPad projects can be stand-alone (only run on the Target
Board, i.e. your LaunchPad), or they can communicate with software running on your
computer (Host PC). Much more details about Energian can be found on http://energia.nu.



Multitasking is the ability to run multiple tasks or threads concurrently on a
microcontroller. In Energia MT, multitasking is achieved by treating each tab as a
separate task (thread) and letting TI-RTOS and Energia take care of the rest. Currently,
multitasking is supported on the MSP-EXP432P401R LaunchPad.

Below is a brief desription of the multitasking capability of the Energia MT environment.
Each tab in Energia MT will be treated as a new task, assuming the tab has a setup() and
loop() function with a matching and unique name. For example, an application with 2
tasks could be called setupTask1() / loopTask1() and setupTask2() / loopTask2().
Energia MT will look for these keyword pairs and automatically turn them into tasks. It
is also important that each setup/loop pair should be placed in a file with a .ino
extension. Setup/loop pairs that are in .c files will not be treated as a new task and
therefore will not execute at run time.
Interprocess communication between tasks can easily be done by using global variables.
Global variables are truly global (so you cannot use the variable name again in any other
tab) and globals must be declared in the first tab. The examples of multitasking can be
found in Energia MT IDE under File -> Examples -> MultiTasking.

Our demo project illustrates how a multitasking application works. In this project, a red
LED (pin P2.0), green LED (pin P2.1) and blue LED (pin P2.2) of the MSPEXP432P401R board are driven by three separate threads. All LEDs are enabled while
the signal on pin P1.6 of MSP432 stays HIGH. The LOW level on P1.6 disables blinking.
The control signal to pin P1.6 comes from pin GPIO18 of the Raspberry Pi. The LOW
level on pin GPIO18 disables all LEDS, while the HIGH level enables blinking. The

circuit diagram for this system is shown in Fig.2.


Fig.2

The MSP432 MCU application was developed in Energia MT IDE. The source code of
the main thread (file LED_MultiBlink.ino) is shown in Listing 1.

Listing 1.

int inState;
int blinkON = 1;


void setup() {
pinMode(15, INPUT); // Pin P1.6 is set as input to pick up the signal on pin GPIO18
}

void loop()
{
inState = digitalRead(15);
if(inState != 0x0)
blinkON = 1;
else
blinkON = 0x0;
delay(1000);
}

In this application, pin P1.6 (15) of MSP432P401R LaunchPad is configured as input. The
signal level on this pin is read into the inState variable. When this signal is LOW, the
inState is assigned 0x0, otherwise inState is assigned 0x1. The blinkON variable is used
to simultaneously enable/disable all threads driving LEDS.

The source code of the thread driving the blue LED (file BlueLed.ino) is shown in
Listing 2.

Listing 2.

#define LED BLUE_LED

void setupBlueLed() {
pinMode(LED, OUTPUT);
}

// the loop routine runs over and over again forever as a task.


void loopBlueLed() {
if (blinkON)
{
digitalWrite(LED, HIGH); // turn the LED ON
delay(100); // wait for 100 ms
digitalWrite(LED, LOW); // turn the LED OFF by pulling the LED pin LOW
delay(100); // wait for 100 ms
}
}

With this source code, the pin corresponding to the blue LED is configured as ouput. The
loop() procedure repeatedly evaluates the value of the global variable blinkON. The LED
will be blinking only if blinkON = 1.

The source code of the thread driving the green LED (file GreenLed.ino) is shown in
Listing 3.

Listing 3.

#define LED GREEN_LED

void setupGreenLed() {
// initialize the digital pin as an output.
pinMode(LED, OUTPUT);
}

// the loop routine runs over and over again forever as a task.

void loopGreenLed() {
if (blinkON)
{

digitalWrite(LED, HIGH); // turn the LED ON


delay(500); // wait for half a second
digitalWrite(LED, LOW); // turn the LED OFF by making the voltage LOW
delay(500); // wait for half a second
}
}

This source code is almost the same as that shown in Listing 2, except the pin number
used by the green LED.

The source code of the thread driving the red LED (file RedLed.ino) is shown in Listing
4.

Listing 4.

#define LED RED_LED

void setupRedLed() {
// initialize the digital pin as an output.
pinMode(LED, OUTPUT);
}

// the loop routine runs over and over again forever as a task.
void loopRedLed() {
if (blinkON)
{
digitalWrite(LED, HIGH); // turn the LED ON
delay(1000); // wait for a second
digitalWrite(LED, LOW); // turn the LED OFF by making the voltage LOW
delay(1000); // wait for a second
}
}


The simple Python application running on the Raspberry Pi simply stops/resumes blinking
the LEDS by pulling pin GPIO18 HIGH/LOW, respectively. The source code of the script
is shown in Listing 5.

Listing 5.

from time import sleep
import RPi.GPIO as GPIO

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)

while (True):
GPIO.output(18, GPIO.HIGH)
sleep(5)
GPIO.output(18, GPIO.LOW)
sleep(5)

The Energia MT IDE window will look like the following (Fig.3).

Fig.3

As it is seen, there are 4 tabs opened in our project. You should place all source files of the
project in the same directory. The Energia MT with the built-in TI-RTOS allows to build
the multitask application where each tab represents its own task. Recall that a task in such
application can communicate with each other using global variables.

Designing Real-Time applications: project 2



The following demo project of a real-time application illustrates low-level programming
techniques and using interrupts and threads in a system with the Raspberry Pi and
MSP432P401R MCU.
The circuit diagram of the project is shown in Fig.4.

Fig.4

In this circuit, the signals from pins GPIO18 and GPIO23 of the Raspberry Pi will
control two on-board LEDs of the MSP432P401R LaunchPad. The input pins P1.6 and
P1.7 of MSP432P401R are configured as the interrupt sources activating by a falling edge
coming from GPIO18 and GPIO23, respectively.

Each interrupt when activated calls an associated interrupt service routine (ISR) which
drives a corresponding LED ON/OFF.
The Energia MT source code of the application consists of three modules. The source
code of the main thread (file MSP432_P1_6_7_ISR.ino) is shown in Listing 6.

Listing 6.

#include msp.h

volatile int redSW, blueSW;

void setup()
{
Serial.begin(9600);
// put your setup code here, to run once:
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
// Configuring pins P1.6 and P1.7
P1DIR &= ~BIT6; // Set P1.6 as input
P1DIR &= ~BIT7; // Set P1.7 as input

P1OUT |= BIT6 | BIT7; // Allow pull-up resistor on pins P1.6 and P1.7
P1REN |= BIT6 | BIT7; // Select pull-up mode for pins P1.6 and P1.7
P1IES |= BIT6 | BIT7; // P1.6 and P1.7 are active by a falling edge
P1IFG = 0; // Clear all P1 interrupt flags
P1IE |= BIT6 | BIT7; // P1.6 and P1.7 interrupts enabled

// Configuring pins P1.0 and P2.2
P1DIR |= BIT0; // P1.0 is set to output (Red LED)
P2DIR |= BIT2; // P2.2 is set to output (Blue LED);

redSW = 0;
blueSW = 0;

attachInterrupt(15, P16_ISRHandler, FALLING); // Interrupt is activated


// when pin P1.6 (15) goes from HIGH to LOW
attachInterrupt(14, P17_ISRHandler, FALLING); // Interrupt is activated
// when pin P1.7 (14) goes from HIGH to LOW
}

void loop(){}

// Interrupt service routine for pin P1.6
void P16_ISRHandler() {
P1IFG &= ~BIT6; // Clear P1.6 IFG
P1OUT ^= BIT0; // P1.0 is toggled
redSW = 1;
}

// Interrupt service routine for pin P1.7
void P17_ISRHandler() {
P1IFG &= ~BIT7; // Clear P1.7 IFG
P2OUT ^= BIT2; // P2.2 (Blue LED) is toggled
blueSW = 1;
}

In this source code, we include the header file msp.h containing definitions for the
MSP432P401R MCU hardware resources. When assigned 1, the global variables redSW
and blueSW enable data transfer through a serial interface in the corresponding threads.

When the P16_ISRHandler() interrupt handler is entered, it clears the corresponding
interrupt flag by the statement:

P1IFG &= ~BIT6;

This is followed by


P1OUT ^= BIT0;

which drives pin P1.0 ON/OFF. The last statement enables the data transfer in the
corresponding thread:

redSW = 1;

The P17_ISRHandler() interrupt handler when entered clears the corresponding interrupt
flag by the statement:

P1IFG &= ~BIT6;

This is followed by

P2OUT ^= BIT2;

which drives pin P2.2 ON/OFF. The last statement enables data transfer in the
corresponding thread:

blueSW = 1;

The source code used for switching the red LED (file Thread1.ino) is shown in Listing 7.

Listing 7.

void setupRed()
{
// put your setup code here, to run once:
Serial.begin(9600);
}

void loopRed()
{
// put your main code here, to run repeatedly:
if (redSW != 0)
{
Serial.println(Red LED was toggled.);
redSW = 0;
}
}

This thread is idle until the variable redSW holds 0. After redSW has been assigned 1 by
the P16_ISRHandler() interrupt handler, a text string is transferred via a serial interface.
Then the variable redSW is cleared to avoid transferring data repeatedly.

The source code responsible for switching the blue LED (file Thread2.ino) is shown in
Listing 8.

Listing 8.

void setupBlue()
{
// put your setup code here, to run once:
Serial.begin(9600);
}

void loopBlue()
{
// put your main code here, to run repeatedly:
if (blueSW != 0)
{
Serial.println(Blue LED was toggled.);
blueSW = 0;

}
}

This thread is idle until the blueSW variable holds 0. When blueSW ia assigned 1 by the
P17_ISRHandler() interrupt handler, a text string is transferred via a serial interface. The
variable blueSW is cleared to avoid transferring data repeatedly.

Designing Real-Time applications: project 3



The demo project in this section illustrates how to control PWM signals generated by
MSP432P401R microcontroller. The MSP432 application produces two PWM pulse trains
whose duty cycles can be adjusted on the fly by the Raspberry Pi application.
To generate PWM signals we will use a built-in module Timer_A of MSP432. Below is a
brief description of the module taken from the MSP432P401R datasheet. Timer_A has a
few 16-bit timers/counters with up to seven capture/compare registers. The Timer_A
module can support multiple capture/compares, PWM outputs, and interval timing. This
module also has extensive interrupt capabilities. Interrupts may be generated from the
counter on overflow conditions and from each of the capture/compare registers.

Timer_A features include:
Asynchronous 16-bit timer/counter with four operating modes;
Selectable and configurable clock source;
Up to seven configurable capture/compare registers;
Configurable outputs with pulse width modulation (PWM) capability;
Asynchronous input and output latching;
The block diagram of a single 16-bit timer/counter of the Timer_A module is shown in
Fig.5.

Fig.5

Operation of each timer in the Timer_A module can be configured programmatically
using the set of registers associated with this device. Timer_A can operate in one of
modes listed below. Note that all operations with peripheral devices of MSP432P401R
MCU are completely determined by values written to the control registers dedicated to
these devices.

There may be multiple instantiations of Timer_A on a given device. The prefix TAx is
used, where x is a greater than equal to zero indicating the Timer_A instantiation. For
devices with one instantiation, x = 0. The suffix n, where n = 0 to 6, represents the
specific capture/compare registers associated with the Timer_A instantiation.
For example, the abbreviation TA0CTL means that we refer to the control register of
timer TimerA_0 in the Timer_A module. The abbreviation TA0CCR1 means that we
refer to the compare/capture register CCR1 of timer TimerA_0.

When operating as a 16-Bit Timer Counter, the 16-bit timer/counter register, TAxR,
increments or decrements (depending on mode of operation) with each rising edge of the
clock signal. TAxR can be read or written by software. Additionally, the timer can
generate an interrupt when it overflows. TAxR may be cleared by setting the TACLR bit
in the TAxCTL control register. Setting TACLR also clears the clock divider and count
direction for up/down mode.

When we need to modify the Timer_A registers, it is recommended to stop the timer
before modifying its operation (with exception of the interrupt enable, interrupt flag, and
TACLR) to avoid errant operating conditions.
When the timer clock is asynchronous to the CPU clock, any read from TAxR should
occur while the timer is not operating or the results may be unpredictable. Alternatively,
the timer may be read multiple times while operating, and a majority vote taken in
software to determine the correct reading. Any write to TAxR takes effect immediately.

The timer can operate in one of modes described below.
The Capture mode is selected when bit CAP of the corresponding TAxCCTLx control
register is set. Capture mode is used to record time events. It can be used for speed
computations or time measurements. The capture inputs CCIxA and CCIxB are
connected to external pins or internal signals and are selected with the CCIS bits. The CM
bits select the capture edge of the input signal as rising, falling, or both. A capture occurs
on the selected edge of the input signal. If a capture occurs:
The timer value is copied into the TAxCCRn register.
The interrupt flag CCIFG of the register TAxCCTLx is set.

The Compare mode is selected when the CAPbit of the corresponding TAxCCTLx control
register is clear (=0). The compare mode is used to generate PWM output signals or
interrupts at specific time intervals. The desired value to be compared is written by
software in the corresponding TAxCCRn register, where n represents the specific
capture/compare register.
When the value in the corresponding TAxR counter register reaches the value held in the
TAxCCRn register, the following events occur:
Interrupt flag CCIFG is set;
Internal signal EQUn = 1;
EQUn affects the output according to the output mode;
The input signal CCI is latched into SCCI.

Timer_A can be fed by a clock source ACLK, SMCLK, or externally via TAxCLK or
INCLK. The clock source is selected with the TASSEL bits in the TAxCTL register. The

selected clock source may be used directly by the timer or divided by 2, 4, or 8, using the
ID bits of TAxCTL. The frequency of the selected clock source can be further divided by
2, 3, 4, 5, 6, 7, or 8 using the TAIDEX bits of the TAxEX0 register. The timer clock
divider logic is reset when bit TACLR of the TAxCTL register is set.

The timer may be started or restarted in the following ways:
The timer counts when MC field (bits 4-5 of the TAxCTL control register) is
greater than 0 and the clock source is active. Note that depending on these bits the
timer can be put into one of the following modes: stop, up, continuous, and
up/down.
When the timer mode is either up or up/down, the timer may be stopped by writing
0 to the TAxCCR0 register. The timer may then be restarted by writing a nonzero
value to TAxCCR0. In this scenario, the timer starts incrementing in the up
direction from zero.

Lets back to our project.



Our system will produce two PWM pulse trains whose duty cycles will be configured
programmatically. While developing an application running on MSP432 MCU, we will
perform the following steps:
1. Select a timer/counter block. We want to use TimerA_0, so the associated control
register for this block will be TA0CTL.
2. Select two channels to generate PWM outputs. There will be channel 1 and 2, so
we need to configure the associated control/capture control registers {TA0CCTL1,
TA0CCR1} and {TA0CCTL2, TA0CCR2}, respectively.
3. Assign the PWM functions to output pins P2.4 (TA0CCR1) and P2.5
(TA0CCR2). The PWM pulse trains will appear on these pins after TimerA_0 has
started.

TimerA_0 will be configured to operate in the Up mode (MC bits = 01) when the timer
counts up to the value written in the TA0CCR0 register. The value in this register and the
clock source determine the frequency of PWM signals.
In our application, we select the ACLK clock source (32768 Hz) for TimerA_0 by
writing 1 to the TASSEL field (bits 8-9 of the TA0CTL control register). We also write a
value 100-1 to the TA0CCR0 register that gives us the period of both PWM signals equal
to 3 mS. This corresponds to the frequency of 32768/100 or 330 Hz. The TA0CCR1 and
TA0CCR2 registers determine the PWM duty cycle for each output signal.
To generate PWM signals we must configure TA0CCTL1 and TA0CCTL2 control
registers to operate in the Compare mode by clearing their CAP bits (CAP = 0).

The duty cycle of each PWM signal will be configured using an 8-bit binary code read
from port P4 of MSP432 MCU. The data byte to port P4 goes from the GPIO pins of the
Raspberry Pi board.

The circuit diagram of this demo project is shown in Fig.6.


Fig.6

The connections between the MSP-EXP432P401R LaunchPad and the Raspberry Pi are
detailed in the table below.

Raspberry Pi 2 J8
pin

MSP432P401R LaunchPad pin

GPIO18

P4.0 (bit 0 of the 8-bit code)

GPIO23

P4.1 (bit 1 of the 8-bit code)

GPIO24

P4.2 (bit 2 of the 8-bit code)

GPIO25

P4.3 (bit 3 of the 8-bit code)

GPIO8

P4.4 (bit 4 of the 8-bit code)

GPIO7

P4.5 (bit 5 of the 8-bit code)

GPIO12

P4.6 (bit 6 of the 8-bit code)

GPIO20

P4.7 (bit 7 of the 8-bit code)

GPIO26

P1.5 (interrupt line to control TA0.1)

GPIO19

P1.6 (interrupt line to control TA0.2)



The PWM duty cycle of each output signal is adjusted via 8 signal lines (GPIO18,
GPIO23, GPIO24, GPIO25, GPIO8, GPIO7, GPIO12 and GPIO20) of the Raspberry
Pi. The binary code on these lines can range from 0 through 255, although our application
will operate in the range 0 90. This allow us to set the duty cycle for any PWM signal in
the range 5 95% using the formula

Duty_cycle = 5 + [bit7:bit0]

To adjust the duty cycle of the signal on pin P2.4 (register TA0CCR1) the Raspberry Pi
application should activate the interrupt line P1.5 by bringing the level on pin GPIO26
from HIGH to LOW. The duty cycle for the signal on pin P2.5 (register TA0CCR2) will
be configured after activating the interrupt on pin P1.6. In this case, the GPIO19 pin goes
HIGH, then LOW. It is seen, that both interrupts are activated by the falling edge on the
corresponding line.

The Energia TM source code of the main thread of the MSP432P401R application is
shown in Listing 9 (file TwoPWM_Control_ISR.ino).

Listing 9.

#include msp.h

int bDuty;

volatile int IF15 = 0; // Interrupt flag for P1.5
volatile int IF16 = 0; // Interrupt flag for P1.6

void setup()
{
// put your setup code here, to run once:

WDTCTL = WDTPW | WDTHOLD; // Stop WDT

// Assign the timer outputs to pins P2.4 - P2.5
P2SEL0 |= BIT4 | BIT5;

// Configure all bits of port P4 as inputs
P4DIR = 0x0; // Port P4 is input
P4REN = 0xFF; // Select pull-up mode for P4
P4OUT = 0xFF; // pull-up resistors on P4

// Configure P1.5 & P1.6 Interrupts
P1DIR = 0x0;
P1REN |= BIT5 | BIT6; // Select pull-up mode for P1.5/P1.6
P1OUT |= BIT5 | BIT6; // pull-up resistors on P1.5/P1.6

P1IES |= BIT5 | BIT6; // P1.5 Hi/Lo edge
P1IFG = 0; // Clear all P1 interrupt flags
P1IE |= BIT5 | BIT6; // P1.6 interrupt enabled

// Initialize LFXT1

PJSEL0 |= BIT0 | BIT1; // Select for LFXT ports


CSKEY = 0x695A; // Unlock CS module for register access
CSCTL2 |= LFXT_EN;

// Loop until XT1, XT2 & DCO fault flag is cleared
do
{
// Clear XT2,XT1,DCO fault flags
CSCLRIFG |= CLR_DCORIFG | CLR_HFXTIFG | CLR_LFXTIFG;
SYSCTL_NMI_CTLSTAT &= ~ SYSCTL_NMI_CTLSTAT_CS_SRC;
} while (SYSCTL_NMI_CTLSTAT & SYSCTL_NMI_CTLSTAT_CS_FLG); // Test
oscillator
// fault flag

CSKEY = 0; // Lock CS module from unintended accesses

// Setup TimerA_0
TA0CCR0 = 100-1; // PWM Period
TA0CCTL1 = OUTMOD_7; // CCR1 reset/set
TA0CCR1 = 45; // CCR1 PWM initial duty cycle
TA0CCTL2 = OUTMOD_7; // CCR2 reset/set
TA0CCR2 = 55; // CCR2 PWM initial duty cycle
TA0CTL = TASSEL_1 | MC_1 | TACLR; // selection: ACLK, up mode, clear the
counter and reset

attachInterrupt(7, p15_ISRHandler, FALLING); // interrupt on P1.5
attachInterrupt(15, p16_ISRHandler, FALLING); // interrupt on P1.6
}

void loop(){}

void p15_ISRHandler()

{
P1IFG &= ~BIT5;
bDuty = P4IN;
IF15 = 1;
}

void p16_ISRHandler()
{
P1IFG &= ~BIT6;
bDuty = P4IN;
IF16 = 1;
}

In this code, all definitions are determined by the msp.h header file. The MSP432 MCU
registers are accessed through the variables with the same names. The variable bDuty
holds the 8-bit code for configuring the duty cycle of both PWM signals. bDuty takes its
value from port P4 using the following statement:

bDuty = P4IN;

Once Port P4 is used as input, it should be configured by the following sequence:

P4DIR = 0x0;
P4REN = 0xFF;
P4OUT = 0xFF;


The output PWM signals appear on pins P2.4 and P2.5, so we need to associate these pins
with TimerA_0 outputs by the statement:

P2SEL0 |= BIT4 | BIT5;

The following statement writes the value of the period of both PWM signals in the register
TA0CCR0:

TA0CCR0 = 100-1;

The TimerA_0 is fed by the ACLK clock source with a frequency of 32768 Hz, so the
frequency of both PWM signals will be 32768 Hz / 100 = 328 Hz.
The sequence

TA0CCTL1 = OUTMOD_7;
TA0CCR1 = 45;

configures the parameters of the PWM signal that appears on pin P2.4. Here
OUTMOD_7 enables the reset/set output mode for the CCR1 signal (bits 5 7 of the
control register TA0CCTL1). The TA0CCR1 variable is assigned the value equal to 45
that determines the initial duty cycle of signal on pin P2.4.

The sequence

TA0CCTL2 = OUTMOD_7;
TA0CCR2 = 55;

configures the PWM output on pin P2.5 associated with CCR2. The initial duty cycle of
the PWM signal on pin P2.5 is 55%.
Configuring TimerA_0 is performed by setting the corresponding fields in the TA0CTL
control register:

TA0CTL = TASSEL_1 | MC_1 | TACLR;

The TASSEL_1 parameter is (=1, bits 8 9 of TA0CTL) selects ACLK (32768 Hz) as a
clock source for the TimerA_0 timer. Parameter MC_1 (=1, bits 4 5 in TA0CTL) puts
TimerA_0 in Up mode in this mode, the timer counts up to the value kept in the
TA0CCR0 register. TACLR (bit 2 of the TA0CTL control register) resets the TimerA_0
timer.

Each counter of TimerA_0 will be controlled by activating the interrupt lines P1.5 and
P1.6. These interrupt lines are configured by the sequence:

P1DIR = 0x0;
P1REN |= BIT5 | BIT6;
P1OUT |= BIT5 | BIT6;

P1IES |= BIT5 | BIT6;
P1IFG = 0;
P1IE |= BIT5 | BIT6;




We also need to configure two interrupt handlers, p15_ISRHandler() and
p16_ISRHandler(), associated with interrupt pins P1.5 and P1.6, respectively:

attachInterrupt(7, p15_ISRHandler, FALLING); // interrupt on P1.5
attachInterrupt(15, p16_ISRHandler, FALLING); // interrupt on P1.6

Each interrupt is activated by the falling edge of a pulse arriving on the corresponding pin.
Interrupts handlers are set the variables IF15 and IF16 thus enabling to process the 8-bit
codes in the corresponding threads. IF15 when set enables configuring the TA0CCR1
register, IF16 enables configuring TA0CCR2. Within the handlers, the variable bDuty is
assigned the value read from port P4.

Each interrupt handler when entered has to clear its corresponding interrupt flags. This is
done by the statement

P1IFG &= ~BIT5;

for the p15_ISRHandler() interrupt handler and

P1IFG &= ~BIT6;



for p16_ISRHandler().

The source code of the thread configuring register TA0CCR1 is shown in Listing 10 (file
PWM1_Thread.ino).

Listing 10.

void setup1()
{
// put your setup code here, to run once:

}

void loop1()
{
// set PWM 1 (P2.4) duty

if (IF15 == 1)
{
IF15 = 0;
if ((bDuty >= 0) && (bDuty <= 90))
TA0CCR1 = 5 + bDuty;
}
}

Here the statements within the if() statement are executed only after activating the
interrupt on pin P1.5 (flag IF15 is set). The control register TA0CCR1 is assigned the
value (5 + bDuty). In this application, the duty cycle can range from 5% to 95%.

The source code of the thread configuring TA0CCR2 is shown in Listing 11 (file

PWM2_Thread.ino).

Listing 11.

void setup2()
{
// put your setup code here, to run once:

}

void loop2()
{
// set PWM 2 (P2.5) duty
if (IF16 == 1)
{
IF16 = 0;
if ((bDuty >= 0) && (bDuty <= 90))
TA0CCR2 = 5 + bDuty;
}
}

Here the statements within the if() control structure are executed only after activating the
interrupt on pin P1.6 (flag IF16 is set). The control register TA0CCR2 is assigned the
value (5 + bDuty). In this application, the duty cycle can range from 5% to 95%.

The Python source code for configuring PWM outputs (TA0CCR1 and TA0CCR2
registers of MSP432) is shown in Listing 12.

Listing 12.

from time import sleep
import RPi.GPIO as GPIO


GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

GPIO.setup(18, GPIO.OUT) # bit0
GPIO.setup(23, GPIO.OUT) # bit 1
GPIO.setup(24, GPIO.OUT) # bit2
GPIO.setup(25, GPIO.OUT) # bit3
GPIO.setup(8, GPIO.OUT) # bit4
GPIO.setup(7, GPIO.OUT) # bit5
GPIO.setup(12, GPIO.OUT) # bit6
GPIO.setup(20, GPIO.OUT) # bit7

GPIO.setup(26, GPIO.OUT) # interrupt line P1.5 (P2.4/TA0.1)
GPIO.setup(19, GPIO.OUT) # interrupt line P1.6 (P2.5/TA0.2)

# configuring a duty cycle as 5 + [bit7:bit0]

GPIO.output(26, GPIO.HIGH)

GPIO.output(18, GPIO.HIGH)
GPIO.output(23, GPIO.HIGH)
GPIO.output(24, GPIO.HIGH)
GPIO.output(25, GPIO.LOW)
GPIO.output(8, GPIO.HIGH)
GPIO.output(7, GPIO.LOW)
GPIO.output(12, GPIO.HIGH)
GPIO.output(20, GPIO.LOW)

GPIO.output(26, GPIO.LOW) # activate interrupt on P1.5 of MSP432
sleep(0.2)


GPIO.output(19, GPIO.HIGH)

GPIO.output(18, GPIO.HIGH)
GPIO.output(23, GPIO.HIGH)
GPIO.output(24, GPIO.LOW)
GPIO.output(25, GPIO.HIGH)
GPIO.output(8, GPIO.LOW)
GPIO.output(7, GPIO.LOW)
GPIO.output(12, GPIO.HIGH)
GPIO.output(20, GPIO.LOW)

GPIO.output(19, GPIO.LOW) # activate interrupt on P1.6


With this source code, we can adjust the duty cycle for PWM signals on pins P2.4 and
P2.5 of the MSP432 LaunchPad. As you know, the duty cycle is determined by the
content of the TA0CCRx registers. The following sequence determines the value of 87 to
be written to the TA0CCR1 control register of MSP432P401R MCU:

GPIO.output(18, GPIO.HIGH)
GPIO.output(23, GPIO.HIGH)
GPIO.output(24, GPIO.HIGH)
GPIO.output(25, GPIO.LOW)
GPIO.output(8, GPIO.HIGH)
GPIO.output(7, GPIO.LOW)
GPIO.output(12, GPIO.HIGH)
GPIO.output(20, GPIO.LOW)

The resulting duty cycle of the PWM signal on pin P2.4 thus will be 5 + 87 = 92%. The
interrupt line (GPIO26) is initially brought HIGH, then LOW thus allowing the new value
of the duty cycle to be written to TA0CCR1.

The duty cycle of the PWM signal on pin P2.5 is determined by the value written to
TA0CCR2:

GPIO.output(18, GPIO.HIGH)
GPIO.output(23, GPIO.HIGH)
GPIO.output(24, GPIO.LOW)
GPIO.output(25, GPIO.HIGH)
GPIO.output(8, GPIO.LOW)
GPIO.output(7, GPIO.LOW)
GPIO.output(12, GPIO.HIGH)
GPIO.output(20, GPIO.LOW)

The resulting duty cycle will be 5 + 75 = 80. The interrupt line (GPIO19) is initially
brought HIGH, then LOW thus allowing the new value of the duty cycle to be written to
TA0CCR2.

The Energia MT IDE window for this project is shown in Fig.7.

Fig.7

Designing Real-Time applications: project 4



Using a stand-alone microcontroller board allows a developer to realize real-time
algorithms that dont require any intervention from the Raspberry Pi. MSP432P401R
MCU can perform some specific tasks completely within a separate thread such
approach can increase the overall performance of the system consisting of the MSP432
and Raspberry Pi. The following project illustrates such approach.

This project is based upon the previous one just discussed. Here we will develop the
application running an additional thread processing an analog signal. This signal will be
fed to the channel A1 (pin P5.4 on the MSP432 board). This thread will perform analogto-digital conversion and drive the on-board red LED (pin P1.0) ON when the input
voltage exceeds 1.4V.
As the input voltage drops below this threshold (1.4), the red LED is driven OFF. The
thread driving the red LED will operate without any interaction with the Raspberry Pi and
independently of other sections of the MSP432 program code.
The circuit diagram of the project is shown in Fig.8.

Fig.8

The connections between the MSP-EXP432P401R LaunchPad and Raspberry Pi 2 are


detailed in the table below.

Raspberry Pi 2 J8
pin

MSP432P401R LaunchPad pin

GPIO18

P4.0 (bit 0 of the 8-bit code)

GPIO23

P4.1 (bit 1 of the 8-bit code)

GPIO24

P4.2 (bit 2 of the 8-bit code)

GPIO25

P4.3 (bit 3 of the 8-bit code)

GPIO8

P4.4 (bit 4 of the 8-bit code)

GPIO7

P4.5 (bit 5 of the 8-bit code)

GPIO12

P4.6 (bit 6 of the 8-bit code)

GPIO20

P4.7 (bit 7 of the 8-bit code)

GPIO26

P1.5 (interrupt line to control TA0.1)

GPIO19

P1.6 (interrupt line to control TA0.2)



In this circuit, the analog signal to the channel A1 is fed from the wiper of the
potentiometer Rp connected as a voltage divider.

The MSP432 MCU source code of the main thread of the application is shown in Listing
13
(file Two_PWM_Control_ISR_w_ADC_5_4.ino).

Listing 13.

#include msp.h

int bDuty;

volatile int IF15 = 0; // Interrupt flag for P1.5
volatile int IF16 = 0; // Interrupt flag for P1.6

void setup()
{
// put your setup code here, to run once:

WDTCTL = WDTPW | WDTHOLD; // Stop WDT

// Configure pins P2.4 - P2.5
P2SEL0 |= BIT4 | BIT5;

// Configure all bits of port P4 as inputs
P4DIR = 0x0; // Port P4 is input
P4REN = 0xFF; // Select pull-up mode for P4
P4OUT = 0xFF; // pull-up resistors on P4

// Configure P1.5 & P1.6 Interrupts
P1DIR = 0x0;
P1REN |= BIT5 | BIT6; // Select pull-up mode for P1.5/P1.6
P1OUT |= BIT5 | BIT6; // pull-up resistors on P1.5/P1.6

P1IES |= BIT5 | BIT6; // P1.5 Hi/Lo edge
P1IFG = 0; // Clear all P1 interrupt flags
P1IE |= BIT5 | BIT6; // P1.6 interrupt enabled



// Initialize LFXT1

PJSEL0 |= BIT0 | BIT1; // Select for LFXT ports


CSKEY = 0x695A; // Unlock CS module for register access
CSCTL2 |= LFXT_EN;

// Loop until XT1, XT2 & DCO fault flag is cleared
do
{
// Clear XT2,XT1,DCO fault flags
CSCLRIFG |= CLR_DCORIFG | CLR_HFXTIFG | CLR_LFXTIFG;
SYSCTL_NMI_CTLSTAT &= ~ SYSCTL_NMI_CTLSTAT_CS_SRC;
} while (SYSCTL_NMI_CTLSTAT & SYSCTL_NMI_CTLSTAT_CS_FLG); // Test
oscillator
// fault flag

CSKEY = 0; // Lock CS module from unintended accesses

// Setup TimerA_0
TA0CCR0 = 100-1; // PWM Period
TA0CCTL1 = OUTMOD_7; // CCR1 reset/set
TA0CCR1 = 45; // CCR1 PWM initial duty cycle
TA0CCTL2 = OUTMOD_7; // CCR2 reset/set
TA0CCR2 = 55; // CCR2 PWM initial duty cycle
TA0CTL = TASSEL_1 | MC_1 | TACLR; // ACLK, up mode, clear the
counter and reset

attachInterrupt(7, p15_ISRHandler, FALLING); // interrupt on P1.5
attachInterrupt(15, p16_ISRHandler, FALLING); // interrupt on P1.6
}

void loop(){}

void p15_ISRHandler()

{
P1IFG &= ~BIT5;
bDuty = P4IN;
IF15 = 1;
}

void p16_ISRHandler()
{
P1IFG &= ~BIT6;
bDuty = P4IN;
IF16 = 1;
}
In this source code, the variable bDuty is assigned the 8-bit binary code for configuring
the duty cycle of PWM signals (pins P2.4 P2.5).

The MSP432 source code of the thread configuring the TA0CCR1 register (file
PWM1_Thread.ino) is shown in Listing 14.

Listing 14.

void setup1()
{
// put your setup code here, to run once:

}

void loop1()
{
// set duty of TA0.1 output on pin P2.4

if (IF15 == 1)
{

IF15 = 0;
if ((bDuty >= 0) && (bDuty <= 90))
TA0CCR1 = 5 + bDuty;
}
}

With this code, the IF15 global variable when set allows to configure the duty cycle of
TimerA_0 output. The duty cycle can be adjusted between 5% (bDuty = 0) and 95%
(bDuty = 90) using the statement:

if ((bDuty >= 0) && (bDuty <= 90))
TA0CCR1 = 5 + bDuty;

The MSP432 source code of the thread configuring TA0CCR2 (file PWM2_Thread.ino)
is shown in Listing 15.

Listing 15.

void setup2()
{
// put your setup code here, to run once:

}

void loop2()
{
// set duty of TA0.2 output on pin P2.5
if (IF16 == 1)
{
IF16 = 0;
if ((bDuty >= 0) && (bDuty <= 90))
TA0CCR2 = 5 + bDuty;

}
}

With this code, the IF16 global variable when set allows to configure the duty cycle of
TimerA_0 output 2. The duty cycle will be adjusted between 5% (bDuty = 0) and 95%
(bDuty = 90) using the statement:

if ((bDuty >= 0) && (bDuty <= 90))
TA0CCR2 = 5 + bDuty;

The MSP432P401R source code of the thread driving A/D conversion on the channel A1
is shown in Listing 16 (file ADC_Thread.ino).

Listing 16.

#include msp.h

#define LSB 3.35/4096
float vout;
int binCode;

void setup3()
{
// put your setup code here, to run once:
delay(300);
Serial.begin(9600);

P5SEL1 |= BIT4; // Configure P5.4 for ADC
P5SEL0 |= BIT4;
// Configure ADC14
ADC14CTL0 |= ADC14SHT0_2 | ADC14SHP | ADC14ON; // Sampling time,
S&H=16,

// ADC14 on
ADC14CTL1 = ADC14RES_2; // Use sampling timer, 12-bit conversion
results

ADC14MCTL0 |= ADC14INCH_1; // A1 ADC input select; Vref = AVCC
(3.3V)
}

void loop3()
{
ADC14CTL0 |= ADC14ENC | ADC14SC; // Start sampling/conversion
while (ADC14BUSY == 0x1); // waiting until A/D conversion is complete
binCode = ADC14MEM0;
vout = LSB * binCode;
if (vout > 1.4)
P1OUT |= BIT0; // P1.0 = 1
else
P1OUT &= ~BIT0; // P1.0 = 0
Serial.print(A1 input, V: );
Serial.println(vout, 3);
delay(5000);
}

Here the following two statements allow to configure pin P1.0 as output and write 0 to this
pin:

P1DIR |= BIT0;
P1OUT &= ~BIT0;

The next statements configure pin P5.4 as an analog input:

P5SEL1 |= BIT4;

P5SEL0 |= BIT4;

The statement

ADC14CTL0 |= ADC14SHT0_2 | ADC14SHP | ADC14ON;

configures several A/D conversion parameters through the ADC14CTL0 control register.
The ADC14SHT0_2 field (bits 8-11 of ADC14CTL0) determines a sample-and-hold
time. These bits define the number of ADC14CLK cycles in the sampling period for
conversion memory register ADC14MEM0. In our particular case, we take the number of
cycles equal to 16.

The ADC14SHP parameter (bit 26 of ADC14CTL0) allows to select the source of the
sampling signal (SAMPCON) to be either the output of the sampling timer or the sampleinput signal directly. If ADC14SHP is clear (= 0), the SAMPCON signal is sourced from
the sample-input signal.
If ADC14SHP is set (= 1), the SAMPCON signal is sourced from the sampling timer. In
our case, we take the sampling timer (SAMPCON = 1).
Both parameters, ADC14SHT0_2 and ADCC14HP, can be applied only when the
ADC14ENC parameter (bit 1 of ADC14CTL0 control register) is equal to 0.
Several parameters of A/D conversion are configured using the control register
ADC14CTL1. The statement

ADC14CTL1 = ADC14RES_2;

selects the 12-bit resolution (bits 5 4 of ADC14CTL1). Other bits of the control register
ADC14CTL1 are left unchanged.
The result of A/D conversion appears as a 12-bit binary code that is stored in the memory
register ADC14MEM0 (by default). Before conversion, we also need to indicate the
physical channel to pick up the input signal. This is done by using the control register
ADC14MCTL0 associated with the ADC14MEM0 memory register. The following
statement selects the channel A1 as the input to be measured:

ADC14MCTL0 |= ADC14INCH_1;

We also need to select the desired reference voltage Vref that is used for calculating the

Least Significant Bit (LSB). In this application, we use the default Vref that is AVcc
(3.3V). You can select other values of Vref by setting the bits 11 8 of the field
ADC14VRSEL in the register ADC14MCTL0.

The A/D sampling/conversion process itself is executed within a loop3() function.
The statement

ADC14CTL0 |= ADC14ENC | ADC14SC;

allows the sampling/conversion process to begin. The application needs to know when
A/D conversion is complete, therefore the flag ADC14BUSY (bit 16 in the control
register ADC14CTL0) is continuously checked. This bit indicates an active sample or
conversion operation. The ADC14BUSY flag stays LOW (log.0) when no operation is
active. While A/D sampling/conversion is active, this bit stays HIGH (log.1).

The following while() loop repeats until A/D conversion is complete:

while (ADC14BUSY == 0x1);

Then the calculated value of the analog input (variable vout) is compared with the
threshold selected to 1.4 (in volts). Once the threshold is exceeded, pin P1.0 is driven ON,
otherwise P1.0 stays OFF. The value on the A0 analog input is also transferred through the
serial interface to the Raspberry Pi.

The Energia MT IDE window for this project is shown in Fig.9.

Fig.9

Designing Real-Time applications: project 5



This project illustrates transferring data from MSP432P401R microcontroller to Raspberry
Pi using several serial virtual channels. The MSP432 application reads the state of inputs
P2.3, P2.4 and P2.5 and transfers the data obtained via serial interfaces. Each of pins P2.3
P2.5 is processed by a separate thread.
The circuit diagram for this project is shown in Fig.10.

Fig.10

In this circuit, pins P2.3 P2.5 pick up signals simulated by mechanical switches or
electronic circuits. Before transferring data to the Raspberry Pi, we should first identify
the serial interfaces available in Raspbian OS by the following command:

pi@raspberrypi ~/Developer/MSP432 $ ls -l /dev/ttyA*
crw-rwT 1 root dialout 166, 1 Aug 6 13:44 /dev/ttyACM1
crw-rwT 1 root dialout 166, 2 Aug 6 13:44 /dev/ttyACM2
crw-rwT 1 root dialout 204, 64 Jan 1 1970 /dev/ttyAMA0

Here the /dev/ttyACM1 /dev/ttyACM2 devices are associated with the MSP432P401R
serial interfaces. We can use /dev/ttyACM1 in our application.
The Python application running on the Raspberry Pi is represented by the following source
code (Listing 17).

Listing 17.

from time import sleep
import serial

s1 = serial.Serial(port=/dev/ttyACM1,
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1)
try:
while 1:
rLine = s1.readline()
print rLine
except KeyboardInterrupt:
print Terminating the application on Ctr-C
s1.close()
sys.exit(0)


This application reads the data via the serial interface /dev/ttyACM1 until Ctrl-C has been
pressed.
The Energia MT source code of the main thread running on MSP432P401R MCU is
shown in Listing 18.

Listing 18.

int done = 0;
int p23State, p24State, p25State;

void setup()
{
pinMode(34, INPUT); // P2.3 is set as input
Serial.begin(9600);
}

void loop()
{
p23State = digitalRead(34);
while (done != 0);
done = 1;
Serial.print(Pin P2.3 is );
if (p23State == 0x0)
Serial.println(LOW);
else
Serial.println(HIGH);
done = 0;
delay(1000);
}

In this application, the program code reads the state of the digital input 34 (pin P2.3) of
MSP432 by the statement:

p23State = digitalRead(34);

p23State will then contain either 0 or 1 depending on the signal level on pin P2.3. The
value of p23State will be transferred through a serial interface.
In this source code, the global variable done is used for synchronizing threads. The data
transfer over a serial interface takes some time, so we need to exclude overlapping and

possible data loss when the particular thread is transferring the data.
Any of the threads can use the serial interface only when it is free (done = 0x0). Therefore
the while() loop

while (done != 0);

repeats until done is set to 0. This statement is included in the source code for each thread.
Once done = 0, this means the resource (a serial device) is free and can be used by the
thread. To prevent from using the resource by other threads, the current thread assigns 1 to
the done variable.
After data has been processed, done is cleared (done = 0), so other threads can access a
serial interface.

The data taken from the digital input 38 (pin P2.4) of MSP432 are processed by the thread
whose source code is shown in Listing 19.

Listing 19.

void setup2()
{
pinMode(38, INPUT); // P2.4 is set as input
Serial.begin(9600);
delay(200);
}

void loop2()
{
p24State = digitalRead(38);
while (done != 0);
done = 1;
Serial.print(Pin P2.4 is );
if (p24State == 0x0)
Serial.println(LOW);

else
Serial.println(HIGH);
done = 0;
delay(1000);
}

The data taken from the digital input 19 (pin P2.5) of MSP432 are processed by the thread
whose source code is shown in Listing 20.

Listing 20.

void setup3()
{
pinMode(19, INPUT); // P2.5 is set as input
Serial.begin(9600);
delay(400);
}

void loop3()
{
p25State = digitalRead(19);
while (done != 0);
done = 1;
Serial.print(Pin P2.5 is );
if (p25State == 0x0)
Serial.println(LOW);
else
Serial.println(HIGH);
done = 0;
delay(1000);
}

The Energia MT IDE window for this project is shown in Fig.11.


Fig.11

Designing Real-Time applications: project 6



Many real-time applications need to read several digital inputs at a time. This could be a
case when an application should process signals from sensors of the same type. The
following demo application illustrates reading several digital inputs of MSP432P401R
MCU at a time and transferring the data obtained to the Raspberry Pi via a serial interface.

The circuit diagram of the application is shown in Fig.12.

Fig.12

In this circuit, three digital inputs (pins P2.5 P2.7) of MSP432P401R MCU are read
after an interrupt on pin P2.4 of MSP432 has been activated by the falling edge on pin
GPIO18 of the Raspberry Pi.
The Energia MT source code of MSP432 application consists of two parts. One of them
representing a main thread (file Reading_ParallelInput.ino) is shown in Listing 21.

Listing 21.

#include msp.h


int p2Read;
int bReady = 0;

void setup()
{
// put your setup code here, to run once:
P2DIR = 0x0; // Port P2 is set as INPUT
P2OUT |= BIT5 | BIT6 | BIT7; // Pull-up resistors on P2.5 - P2.7
P2REN |= BIT5 | BIT6 | BIT7; // Select pull-up mode for P2.5 - P2.7
attachInterrupt(38, P24IsrHandler, FALLING); // configuring the interrupt on pin P2.4 of
MSP432
}

void loop()
{
// put your main code here, to run repeatedly:
}

void P24IsrHandler(void)
{
P2IFG &= ~BIT4; // Clear interrupt flag for pin P2.4
bReady = 1;
}

In this source code, the sequence

P2DIR = 0x0;
P2OUT |= BIT5 | BIT6 | BIT7;
P2REN |= BIT5 | BIT6 | BIT7;

configures pins P2.5 P2.7 as inputs with the pull-up resistors attached. We also need to

configure the interrupt handler processing events on pin P2.4 by the following statement:


attachInterrupt(38, P24IsrHandler, FALLING);

The interrupt handler P24IsrHandler when entered clears the interrupt flag assigned to
the P2.4 interrupt line and set the bReady flag to 1 thus allowing the data transfer to the
Raspberry Pi.

The Energia MT source code of the thread processing digital inputs (file
Serial_Thread.ino) is shown in Listing 22.

Listing 22.

void setup1()
{
// put your setup code here, to run once:
Serial.begin(9600);
delay(500);
}

void loop1()
{
// put your main code here, to run repeatedly:
if (bReady == 1)
{
p2Read = P2IN;
p2Read = p2Read >> 5;
Serial.print(BITS 7-5 of port P2: 0x);
Serial.println(p2Read, HEX);
bReady = 0;
}

}

The program code of the thread is running within the loop1() function. If the global
variable bReady = 1, the state of digital inputs P2.5 P2.7 is saved in bits 0 2 of the
p2Read variable. The value obtained is then transferred to the Raspberry Pi through a
serial interface.

The project window in Energia MT IDE looks like the following (Fig.13).

Fig.13

The source code of the Python application running in Raspbian OS is shown in Listing 23.

Listing 23.

from time import sleep
import RPi.GPIO as GPIO
import serial
import sys


GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)
GPIO.setup(18, GPIO.OUT)

s1 = serial.Serial(port=/dev/ttyACM0,
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=3)
try:
while 1:
GPIO.output(18, GPIO.HIGH)
GPIO.output(18, GPIO.LOW)
rLine = s1.readline()
print rLine
sleep(5)
except KeyboardInterrupt:
print Terminating the application on Ctr-C
s1.close()
sys.exit(0)

The application will perform two actions: activating an interrupt and reading the data
through a serial interface. The object s1 associated with the serial interface /dev/ttyACM0
will be used for reading data transferred by an MSP432 application. The GPIO18 pin
driving the interrupt line to pin P2.4 is configured as output. The sequence

GPIO.output(18, GPIO.HIGH)
GPIO.output(18, GPIO.LOW)

within the while loop activates the interrupt on line P2.4. Then the data is passed through

the serial interface and output on the screen by the sequence:



rLine = s1.readline()
print rLine

The loop repeats infinitely every 5 s until Ctrl-C is pressed.

Designing Real-Time applications: project 7



This section is dedicated to processing analog signals in real-time systems with
MSP432P401R MCU and Raspberry Pi. Below is a brief description of the analog module
of MSP432P401R CPU taken from the corresponding datasheet on the device.
The MSP432P401R CPU contains the ADC14 module that supports fast 14-bit analog-todigital conversions. The module implements a 14-bit SAR core, sample select control, and
up to 32 independent conversion-and-control buffers. The conversion-and-control buffer
allows up to 32 independent analog-to-digital converter (ADC) samples to be converted
and stored without any CPU intervention.
ADC14 features include:
1 Msps maximum conversion rate at maximum resolution of 14-bits;
Monotonic 14-bit converter with no missing codes;
Sample-and-hold with programmable sampling periods controlled by software
or timers;
Conversion initiation by software or timers;
Software-selectable on-chip reference voltage generation (1.2 V, 1.45 V, or 2.5
V) with option to make available externally;
Software-selectable internal or external reference;
Up to 32 individually configurable external input channels, single-ended or
differential input selection available;
Internal conversion channels for internal temperature sensor and AVcc and
four more internal channels available on select devices (see device data sheet for
availability as well as function);
Independent channel-selectable reference sources for positive reference;
Selectable conversion clock source;
Single-channel, repeat-single-channel, sequence (autoscan), and repeatsequence (repeated
Autoscan conversion modes;
Interrupt vector register for fast decoding of 38 ADC interrupts;
32 conversion-result storage registers;
Window comparator for low power monitoring of input signals of the
conversion-result register.
The block diagram of ADC14 is shown in Fig.14.

Fig.14

The above diagram may come in handy when configuring analog-to-digital converter for
our application.
The demo application is very simple it will read 3 channels named A0 A2 and transfer
the data obtained via a serial interface to the Raspberry Pi. The circuit diagram of such a
system is shown in Fig.15.

Fig.15

In this circuit, the analog signals on pins P5.5 P5.3 are simulated by potentiometers Rp1
Rp3. Analog signals are taken from their wipers and fed to analog inputs of MSP432.
The table below details the connections.

MSP432P401R LaunchPad pin

Analog channel of
MSP432P40R

P5.5

A0

P5.4

A1

P5.3

A2


The Energia MT source code of the main thread of the MSP432 application is shown in
Listing 24 (file A0_A1_A2_MT.ino).

Listing 24.

#include msp.h
#define ADC14CSTARTADD_1 (1 << 16) // the constant to select a memory register
// ADC14MEM1 in ADC14CTL1

const float LSB = 3.345/4096;
float vout[3];

int bincode[3];
int chs[3] = {ADC14INCH_0, ADC14INCH_1, ADC14INCH_2};
int i, cBusy;


void setup()
{
// put your setup code here, to run once:

P5SEL1 |= BIT3 | BIT4 | BIT5; // Configure P5.5-P5.3 (A0-A2) for ADC
P5SEL0 |= BIT3 | BIT4 | BIT5;

// Configure ADC14
ADC14CTL0 |= ADC14SHT0_3 | ADC14SHP | ADC14ON; // Sampling time,
S&H=32,
// ADC14 is on
ADC14CTL1 = ADC14RES_2 | ADC14CSTARTADD_1; // Use sampling timer,
// 12-bit conversion results
// and using ADC14MEM1 mem. reg.
cBusy = 0;
}

void loop()
{
cBusy = 1;

for (i = 0; i < 3; i++)


{
ADC14CTL0 &= ~ADC14ENC;
delayMicroseconds(10);
ADC14MCTL1 = 0x0;
delayMicroseconds(10);
ADC14MCTL1 += chs[i]; // Select an input channel from the chs array, Vref = AVcc
delayMicroseconds(10);
ADC14CTL0 |= ADC14ENC | ADC14SC; // Start sampling and conversion
delayMicroseconds(10);
while(ADC14BUSY == 0x1); // waiting until A/D conversion is complete

bincode[i] = ADC14MEM1;
vout[i] = LSB * bincode[i];
delayMicroseconds(10);
}
cBusy = 0;
delay(1000);
}

This source code uses definitions from the header file msp.h. The constant
ADC14CSTARTADD_1 defined by the following directive

#define ADC14CSTARTADD_1 (1 << 16)

allows to select the address of a memory register ADC14MEM1 which holds the result of
A/D conversion.
The array bincode consisting of 3 elements holds the results of A/D conversion for
channels A0 A2. The vout variable of a float type is assigned the value of a voltage level
on an analog input calculated as

bincode[i] * LSB,


where bincode[i] is a binary code for each channel (A0 A2) and LSB
(Least Significant Bit) = 3.345/212 = 3.345/4096. The constant 3.345 determines a
reference voltage Vref (by default). In this particular case, the measured value of Vref
turned out to be 3.345V.
The array chs[3] contains the numbers of physical channels being selected via a
multiplexor (see Fig.14).
The variable cBusy is needed for synchronizing threads. When cBusy = 1, the A/D
conversion is still running. When cBusy = 0, A/D conversion is complete, so the result can
be transferred to the Raspberry Pi.
The sequence

P5SEL1 |= BIT3 | BIT4 | BIT5;
P5SEL0 |= BIT3 | BIT4 | BIT5;

configures pins P5.5 P5.3 as analog inputs for channels A0 A2.
The following statement configures the ADC14 mode through the ADC14CTL0 control
register:

ADC14CTL0 |= ADC14SHT0_3 | ADC14SHP | ADC14ON;

The parameter ADC14SHT0_3 (bits 11 8 of ADC14CTL0) determines the sample-andhold time of ADC14. These bits define the number of ADC14CLK cycles in the sampling
period for the memory registers ADC14MEM0 to ADC14MEM7 and ADC14MEM24 to
ADC14MEM31. In our case, this parameter is selected equal to 3 that corresponds to 32
clock cycles.
This parameter can be modified only when ADC14 device is disabled by clearing the
parameter ADC14ENC (bit 1 of ADC14CTL0 = 0).
ADC14SHP (bit 26 of the ADC14CTL0 control register) allows to select sample-andhold pulse-mode. This bit selects the source of the sampling signal (SAMPCON) to be
either the output of the sampling timer or the sample-input signal directly. In our case, the
SAMPCON signal will be sourced from the sampling timer. This bit can be modified only
when ADC14ENC = 0.
The ADC14ON parameter when set allows to drive ADC14 ON. Clearing this bit drives
ADC14 OFF. In our case, ADC14 device is driven ON before A/D conversion begins.

A/D conversion is executed within an for() loop running 3 iterations. In each iteration, the
ADC14 is initially disabled by the statement

ADC14CTL0 &= ~ADC14ENC;

After a short delay (about 10 uS) the program code clears the memory control register by
the statement:

ADC14MCTL1 = 0x0;

After a short delay, the program code selects the physical channel to be processed:

ADC14MCTL1 += chs[i];

This is followed by a delay of 10 uS. The next statement enables A/D conversion for the
selected channel and starts sampling/conversion:

ADC14CTL0 |= ADC14ENC | ADC14SC;

Bit ADC14ENC in the above statement enables A/D conversion; bit ADC14SC starts
sampling/conversion process.
To know when A/D conversion is complete, the application periodically checks bit
ADC14BUSY (bit 16 of the ADC14CTL0 control register). This bit indicates an active
sample or conversion operation. If ADC14BUSY = 0, no operation is active. Otherwise, if
ADC14BUSY = 1, a sample or conversion is active.
The application is waiting until A/D conversion is complete by executing the following
statement:

while(ADC14BUSY == 0x1);

The result of A/D conversion is saved in memory register ADC14MEM1. The following
statement moves the result to the element corresponding to the selected channel:

bincode[i] = ADC14MEM1;


The float value is then saved in the corresponding element of the vout array:

vout[i] = LSB * bincode[i];

After the for() loop terminates, the cBusy flag is cleared thus allowing the data transfer.
The measurement cycle for A0 A2 channels repeats each 1 s (the delay(1000)
statement).
The following source code (file Serial_Thread.ini) represents the thread performing data
transfer over a serial interface to the Raspberry Pi (Listing 25).

Listing 25.

void setup1()
{
// put your setup code here, to run once:
Serial.begin(9600);
delay(500);
}

void loop1()
{
// put your main code here, to run repeatedly:
while (cBusy == 1);
Serial.print(A0 input, V: );
Serial.println(vout[0], 3);

Serial.print(A1 input, V: );
Serial.println(vout[1], 3);

Serial.print(A2 input, V: );
Serial.println(vout[2], 3);

delay(3000);
}

The above code is periodically (every 3 s) transfers the data obtained from channels A0
A2 through the serial interface after A/D conversion is complete (cBusy = 0).

The source code of the Python application for reading data from MSP432P401R is shown
in Listing 26.

Listing 26.

from time import sleep
import serial

s1 = serial.Serial(port=/dev/ttyACM1,
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1)
try:
while 1:
rLine = s1.readline()
print rLine
except KeyboardInterrupt:
print Terminating the application on Ctr-C
s1.close()
sys.exit(0)

The Energia MT IDE window is shown in Fig.16.

Fig.16

Designing Real-Time applications: project 8



An application to be discussed in this section allows to continuously read 3 analog
channels on the MSP432P401R board. The data collected from all channels are transferred
to the Raspberry Pi if interrupt signal on pin P1.6 of MSP432 is activated. The interrupt
line P1.6 is driven by pin GPIO19 of the Raspberry Pi.
The circuit diagram for this project is shown in Fig.17.

Fig.17

In this circuit, analog signals are fed to channels A0 A2 from the wipers of
potentiometers Rp1 Rp3. The interrupt line assigned to pin P1.6 of MSP432 is
connected to pin GPIO19 of the Raspberry Pi. The interrupt to MSP432 is activated when
GPIO19 goes from HIGH to LOW.

The Energia MT source code for the application running on the MSP432P401R board is
as follows (Listing 27).

Listing 27.


#include msp.h
#define ADC14CSTARTADD_1 (1 << 16) // select a memory register ADC14MEM1

const float LSB = 3.345/4096;
float vout[3];

int bincode[3];
int chs[3] = {ADC14INCH_0, ADC14INCH_1, ADC14INCH_2};
int i, bRead;

void setup()
{
// put your setup code here, to run once:
Serial.begin(9600);
// Configure pin P1.6 for interrupt
P1DIR = 0x0;
P1REN |= BIT6; // Select pull-up mode for P1.6
P1OUT |= BIT6; // pull-up resistors on P1.6

P1IES |= BIT6; // P1.6 Hi/Lo edge
P1IFG = 0; // Clear all P1 interrupt flags
P1IE |= BIT6; // P1.6 interrupt enabled

// Configure analog channels A0-A2 of ADC14 (P5.5 - P5.3)

P5SEL1 |= BIT3 | BIT4 | BIT5;
P5SEL0 |= BIT3 | BIT4 | BIT5;

// Configure ADC14
ADC14CTL0 |= ADC14SHT0_3 | ADC14SHP | ADC14ON; // Sampling time,
S&H=32, ADC14 on

ADC14CTL1 = ADC14RES_2 | ADC14CSTARTADD_1; // Use sampling timer,


// 12-bit conversion results
// and ADC14MEM1 memory reg.
attachInterrupt(15, P16_ISRHandler, FALLING); // an interrupt is activated
// when a falling edge arrives on pin P1.6 (15)
bRead = 0;
}

void loop()
{
for (i = 0; i < 3; i++)
{
ADC14CTL0 &= ~ADC14ENC;
delayMicroseconds(10);
ADC14MCTL1 = 0x0;
delayMicroseconds(10);
ADC14MCTL1 += chs[i];
delayMicroseconds(10);
ADC14CTL0 |= ADC14ENC | ADC14SC; // Start sampling and conversion
delayMicroseconds(10);
while(ADC14BUSY == 0x1); // wait until A/D conversion is complete

bincode[i] = ADC14MEM1;
vout[i] = LSB * bincode[i];
delayMicroseconds(10);
}
if (bRead == 1)
{
Serial.print(A0 input, V: );
Serial.println(vout[0], 3);

Serial.print(A1 input, V: );
Serial.println(vout[1], 3);

Serial.print(A2 input, V: );
Serial.println(vout[2], 3);

Serial.println();
bRead = 0;
}
delay(100);
}

void P16_ISRHandler()
{
P1IFG &= ~BIT6; // Clear P1.6 Interrupt flag
if (bRead == 0)
bRead = 1;
}

In this code, channels A0 A2 are processed within the for(i = 0; i < 3; i++) loop every
100 mS. If an interrupt on pin P1.6 has been activated, the variable bRead is set (=1) by
the P16_ISRHandler interrupt handler thus allowing to transfer the result of A/D
conversion via a serial interface.
The binary codes corresponding to all analog inputs are held in the array bincode, the
calculated values of the analog inputs are held in the array vout.

The source code of the Python application controlling the data transfer from
MSP432P401R MCU is shown in Listing 28.

Listing 28.

from time import sleep
import RPi.GPIO as GPIO

import serial
import sys

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

GPIO.setup(19, GPIO.OUT) # P1.6 control
s1 = serial.Serial(port=/dev/ttyACM0,
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=3)
try:
while 1:
GPIO.output(19, GPIO.HIGH) # activate interrupt on P1.6 of MSP432
sleep(0.1)
GPIO.output(19, GPIO.LOW)
for x in range (0, 4):
rLine = s1.readline()
print rLine
sleep(4)
except KeyboardInterrupt:
print Terminating the application on Ctr-C
s1.close()
sys.exit(0)



To access the serial interface the application creates the object s1 with predetermined
parameters. In our case, s1 is associated with the /dev/ttyACM0 device. The sequence

GPIO.output(19, GPIO.HIGH)
sleep(0.1)
GPIO.output(19, GPIO.LOW)

within the while loop periodically (every 4 s) activates the interrupt on pin P1.6 of
MSP432 through GPIO19. Then the for loop reads 4 lines from the serial interface and
output the results on the console.
The Python application provides the following outputs:

pi@raspberrypi ~/Developer $ sudo python ReadingAnalog_ISR.py
A0 input, V: 0.338
A1 input, V: 0.958
A2 input, V: 1.440

A0 input, V: 0.338
A1 input, V: 0.958
A2 input, V: 1.450

A0 input, V: 0.337
A1 input, V: 0.959
A2 input, V: 1.450


^C Terminating the application on Ctr-C

The Energia MT IDE window of the project is shown in Fig.18.

Fig.18

Designing Real-Time applications: project 9



This section illustrates how to transfer a command to MSP432 from the Raspberry Pi
using a serial interface. An application running on the MSP432P401R board will receive
the command, parse it and set the frequency of timer Timer_A3.
The circuit diagram of the project is shown in Fig.19.

Fig.19

In this circuit, the output signals of timer TimerA_3 are taken from pins P10.5 (channel 1)
and P8.2 (channel 2).

The Energia MT source code for MSP432P401R MCU is shown in Listing 29.

Listing 29.

#include msp.h


#define pACLK 32768 // a basic clock frequency taken from ACLK
int cmd, pA3, freq;

void setup()
{
// put your setup code here, to run once:
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
Serial.begin(9600);

// Configure pin P10.5
P10DIR |= BIT5; // P10.5 > TA3.1
P10SEL0 |= BIT5;

// Configure pin P8.2
P8DIR |= BIT2; // P8.2 > TA3.2
P8SEL0 |= BIT2;


// Initialize LFXT1
PJSEL0 |= BIT0 | BIT1; // Select for LFXT ports
CSKEY = 0x695A; // Unlock CS module for register access
CSCTL2 |= LFXT_EN;

// Loop until XT1, XT2 & DCO fault flag is cleared
do
{
// Clear XT2,XT1,DCO fault flags
CSCLRIFG |= CLR_DCORIFG | CLR_HFXTIFG | CLR_LFXTIFG;
SYSCTL_NMI_CTLSTAT &= ~ SYSCTL_NMI_CTLSTAT_CS_SRC;
} while (SYSCTL_NMI_CTLSTAT & SYSCTL_NMI_CTLSTAT_CS_FLG); // Test
oscillator

// fault flag

CSKEY = 0; // Lock CS module from unintended accesses

// Setup TimerA_3
TA3CCR0 = 80; // PWM Period (f = 409 Hz)
TA3CCTL1 = OUTMOD_7; // CCR1 reset/set
TA3CCR1 = 80 / 3; // CCR1 initial duty cycle = 33%
TA3CCTL2 = OUTMOD_7; // CCR2 reset/set
TA3CCR2 = 80 * 2 / 3; // CCR2 initial duty cycle = 66%
TA3CTL = TASSEL_1 | MC_1 | TACLR; // ACLK = 32768 Hz, up mode, clear the
counter and reset
}

void loop()
{

while (Serial.available() > 0)
{

// look for the next valid integer in the incoming serial stream:
cmd = Serial.parseInt(); // cmd line: <0,freq>
if (cmd == 3) // 3 is a code of a TimerA_3 write command
{
freq = Serial.parseInt();
if((freq >= 300) && (freq <= 1600))
{
pA3 = pACLK / freq;
TA3CCR0 = pA3-1;
TA3CCR1 = pA3 * 4 / 10;
TA3CCR2 = pA3 * 8 / 10;
}

}
}
delay(1000);
}

In this source code, the constant pACLK is used for calculating a value being written to
the TA3CCR0 register. The variable freq holds the actual value of a frequency to be
adjusted. The variable pA3 is assigned the value corresponding to the selected frequency
by the statement:

pA3 = pACLK / freq;

The TimerA_3 timer is configured by the sequence of commands, where first determines
the initial frequency of TimerA_3:

TA3CCR0 = 80;

With the value of 80, the initial frequency will be 32768 Hz / 80 = 409 Hz.
The mode and duty cycle for the CCR1 output are configured by the statements:

TA3CCTL1 = OUTMOD_7;
TA3CCR1 = 80 / 3;

The first statement of this sequence puts CCR1 in set/reset mode. The second adjusts the
initial duty cycle to be about 33%.
The CCR2 output is configured by the sequence:

TA3CCTL2 = OUTMOD_7;
TA3CCR2 = 80 * 2 / 3;

The duty cycle of a signal on the CCR2 output will be close to 66%.
The following statement enables timer TimerA_3:

TA3CTL = TASSEL_1 | MC_1 | TACLR;



Here the TASSEL_1 parameter determines the clock source (ACLK = 32768 Hz), the
MC_1 parameter puts TimerA_3 in Up mode and TACLR clears the counter and resets
the timer.

The new value of a frequency of timer TimerA_3 is passed to an application through a
serial interface in Comma-Separated Format (CSV):

<value1,value2>

where value1 is the command (=3, in our case) and value2 is a new frequency. For
example, the following sequence

3, 500

allows to adjust a frequency of 500 Hz for TimerA_3.
The data received by the MSP432 serial interface are parsed by the statements

cmd = Serial.parseInt();
. . .
freq = Serial.parseInt();

Off these statements, the former saves the command in the variable cmd, while the latter
saves the value of a new frequency in the variable freq.

The source code of a Python application controlling PWM sources is shown in Listing 30.

Listing 30.

from time import sleep
import serial

s1 = serial.Serial(port=/dev/ttyACM0,
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1)
s1.write(3,300)
sleep(5)
s1.write(3,780)
sleep(5)
s1.write(3,1140)
sleep(5)
s1.close()


In this code, the application associates the serial interface with an object s1. The
application writes three commands to the serial device. The first command determines the
frequency of 300 Hz for TimerA_3, the second determines the frequency of 780 Hz and
third determines the frequency of 1140 Hz.

Below is one more Python source code for controlling PWM. This application uses the
command line parameters (Listing 31).

Listing 31.

# This script illustrates passing command line parameters
# through a serial interface. The command line
# arguments are as follows:
# -t determines the timer (3 = Timer_A3)
# -f is the frequency to be set
# Cmd line: sudo <path> -t 3 -f <freq>
# Example: pi@raspberrypi ~/Developer $ sudo python Set_Freq.py -t 3 -f 340


import serial
import time
import argparse

parser = argparse.ArgumentParser(description=Configuring the frequency of a PWM
signal)
parser.add_argument(-t, timer, help=timer, required=True)
parser.add_argument(-f, frequency, help=Desired frequency, required=True)
args = parser.parse_args()
str1 = args.timer + , + args.frequency

s1 = serial.Serial(port=/dev/ttyACM0,
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1)
time.sleep(1)
s1.write(str1)
time.sleep(1)
s1.close()


The Python application takes two parameters and forms a CSV data line like the
following:

<value1>,<value2>

This is done by the statement:

str1 = args.timer + , + args.frequency


The command string str1 is then transferred to the MSP432P401R board through the
serial interface associated with the s1 object. After 1 s delay the serial device is closed.
These operations are performed by the following sequence:

s1.write(str1)
time.sleep(1)
s1.close()

The Python application is invoked by:

pi@raspberrypi ~/Developer $ sudo python Set_Freq.py -t 3 -f 340

This command sets the frequency of 340 Hz for timer Timer_A3.

Using an MSP432 32-bit timer in Real-Time applications



This section is dedicated to configuring and using an MSP432 32-bit timer called
Timer32. This built-in device provides the basis for implementing various timing
functions in real-time applications. Below is the brief description taken from an
MSP432P401 datasheet.

MSP432P4xx Timer32 is an AMBA compliant SoC peripheral developed, tested, and
licensed by ARM Limited. The Timer32 module consists of two programmable 32-bit or
16-bit down counters that can generate interrupts on reaching zero. The key features of
Timer32 include:
2 independent counters each configurable as 32-bit or 16-bit counter size;
Three different timer modes supported for each counter;
Prescaler to divide the input clock by 1, 16 or 256;
Independent Interrupts from each of the counter, as well as, a combined
interrupt from both the counters

Two independent timers are available as part of Timer32. For each of the times, the
following modes of operation are available:
Free-Running mode: The counter wraps after reaching its zero value, and
continues to count down from the maximum value. This is the default mode.
Periodic timer mode: The counter generates an interrupt at a constant interval,
reloading the original value after wrapping past zero.
One-Shot timer mode: The counter generates an interrupt once. When the
counter reaches zero, it halts until reprogrammed by the user. This can be achieved
by either clearing the One-Shot Count bit in the control register, in which case the
count proceeds according to the selection of Free-Running or Periodic mode, or
by writing a new value to the Load Value register.

Each timer has an identical set of registers and the operation of each timer is also identical.
The timer is loaded by writing to the load register and, if enabled, counts down to zero.
When a counter is already running, writing to the load register causes the counter to
immediately restart at the new value. Writing to the background load value has no effect
on the current count. The counter continues to decrement to zero, and then recommences
from the new load value, if in periodic mode, and One Shot Mode is not selected.

When zero has been reached, an interrupt is generated. The interrupt can be cleared by

writing to the clear register. If One-Shot Mode is selected, the counter halts on reaching
zero until you deselect One-Shot Mode, or write a new Load value. Otherwise, after
reaching a zero count, if the timer is operating in Free-Running Mode it continues to
decrement from its maximum value.

If Periodic Mode is selected, the timer reloads the count value from the Load Register
and continues to decrement. In this mode the counter effectively generates a periodic
interrupt. The mode is selected by a bit in the Timer Control Register. At any point, the
current counter value can be read from the Current Value Register. The counter is
enabled by a bit in the Control Register. At reset, the counter is disabled, the interrupt is
cleared, and the Load Register is set to zero.

By reset, the mode and prescale values are set to Free-Running, and clock divide of 1
respectively.
The timer clock enable is generated by a prescale unit. The enable is then used by the
counter to create a clock with a timing of one of the following:
The system clock;
The system clock divided by 16, generated by 4 bits of prescale;
The system clock divided by 256, generated by a total of 8 bits of prescale.

Fig.20 shows how the timer clock frequency is selected in the prescale unit. This enables
you to
clock the timer at different frequencies.

Fig.20

Lets develop a basic application to illustrate using timer Timer32. Unfortunately, the
current version of the Energia MT IDE cant provide the full capabilities to operate with
Timer32, so we will apply the powerful, but simple in use (and free!) Code Composer
Studio Integrated Development Environment (CCS) for developing applications

with Timer32.
A brief description of CCS provided by Texas Instruments is given below.

Code Composer Studio is an integrated development environment (IDE) that supports
TIs Microcontroller and Embedded Processors portfolio. Code Composer Studio
comprises a suite of tools used to develop and debug embedded applications. It includes
an optimizing C/C++ compiler, source code editor, project build environment, debugger,
profiler, and many other features. The intuitive IDE provides a single user interface taking
you through each step of the application development flow. Familiar tools and interfaces
allow users to get started faster than ever before. Code Composer Studio combines the
advantages of the Eclipse software framework with advanced embedded debug
capabilities from TI resulting in a compelling feature-rich development environment for
embedded developers.

To develop CCS projects, we need to install the Code Composer Studio environment
with MSP432 support. Additionally, we can install MSPWare examples containing a lot of
useful information. Assuming the CCS environment has already been installed, lets start
off with the basic project where Timer32 of MSP432P401R MCU will be used.

In the opened CCS window select the File>New>CCS Project (Fig.21).

Fig.21

Then we should configure the parameters of our CCS project as is shown in Fig.22.

Fig.22


In the left field of the Target parameter, we have to select MSP432 Family. The right
field should contain the string MSP432P401R. The Connection field should contain
the string Texas Instruments XDS110 USB Debug Probe [Default] which refers to the
built-in debugger hardware/software. We should also select the location where the project
files will be stored it is up to you to select this. Our project will be called
Timer32_Basic (the Project Name field). The Compiler version parameter was selected
automatically, so I didnt change this field.

Finally, we can select the type of the project offered by the Project Wizard. In the bottom
window select the option Empty Project (with main.c) and press Finish.
The Project Wizard creates several for our project as is shown in Fig.23.

Fig.23

In most cases, when dealing with CCS, we only need to modify two template files, main.c
and msp432_startup_ccs.c. When created, the main.c contains the following (Listing
32).

Listing 32.

//*****************************************************************************
//
// MSP432 main.c template - Empty main
//
//****************************************************************************

#include msp.h

void main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
}

The header file msp.h contains all required definitions. The statement

WDTCTL = WDTPW | WDTHOLD;

stops the watchdog timer.

The template file msp432_startup_ccs.c contains the Interrupt Vector Table (Listing 33).
When we need to use interrupts in a project, we must indicate the Interrupt Service
Routine associated with the particular interrupt. By default, this file comprises only a few
default fault handlers.

Listing 33.

//*****************************************************************************
//
// Copyright (C) 2012 - 2014 Texas Instruments Incorporated - http://www.ti.com/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//
// Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the

// distribution.
//
// Neither the name of Texas Instruments Incorporated nor the names of
// its contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
CONTRIBUTORS
// AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
DAMAGE.
//
// MSP432 Family Interrupt Vector Table for CGT
//
//****************************************************************************

#include <stdint.h>

/* Forward declaration of the default fault handlers. */


static void resetISR(void);
static void nmiISR(void);
static void faultISR(void);
static void defaultISR(void);


/* External declaration for the reset handler that is to be called when the */
/* processor is started */
extern void _c_int00(void);


/* Linker variable that marks the top of the stack. */
extern unsigned long __STACK_END;


/* External declarations for the interrupt handlers used by the application. */

/* To be added by user */


/* Intrrupt vector table. Note that the proper constructs must be placed on this to */
/* ensure that it ends up at physical address 0x0000.0000 or at the start of */
/* the program if located at a start address other than 0. */
#pragma DATA_SECTION(interruptVectors, .intvecs)
void (* const interruptVectors[])(void) =
{
(void (*)(void))((uint32_t)&__STACK_END),
/* The initial stack pointer */
resetISR, /* The reset handler */
nmiISR, /* The NMI handler */

faultISR, /* The hard fault handler */


defaultISR, /* The MPU fault handler */
defaultISR, /* The bus fault handler */
defaultISR, /* The usage fault handler */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */
0, /* Reserved */
defaultISR, /* SVCall handler */
defaultISR, /* Debug monitor handler */
0, /* Reserved */
defaultISR, /* The PendSV handler */
defaultISR, /* The SysTick handler */
defaultISR, /* PSS ISR */
defaultISR, /* CS ISR */
defaultISR, /* PCM ISR */
defaultISR, /* WDT ISR */
defaultISR, /* FPU ISR */
defaultISR, /* FLCTL ISR */
defaultISR, /* COMP0 ISR */
defaultISR, /* COMP1 ISR */
defaultISR, /* TA0_0 ISR */
defaultISR, /* TA0_N ISR */
defaultISR, /* TA1_0 ISR */
defaultISR, /* TA1_N ISR */
defaultISR, /* TA2_0 ISR */
defaultISR, /* TA2_N ISR */
defaultISR, /* TA3_0 ISR */
defaultISR, /* TA3_N ISR */
defaultISR, /* EUSCIA0 ISR */
defaultISR, /* EUSCIA1 ISR */

defaultISR, /* EUSCIA2 ISR */


defaultISR, /* EUSCIA3 ISR */
defaultISR, /* EUSCIB0 ISR */
defaultISR, /* EUSCIB1 ISR */
defaultISR, /* EUSCIB2 ISR */
defaultISR, /* EUSCIB3 ISR */
defaultISR, /* ADC14 ISR */
defaultISR, /* T32_INT1 ISR */
defaultISR, /* T32_INT2 ISR */
defaultISR, /* T32_INTC ISR */
defaultISR, /* AES ISR */
defaultISR, /* RTC ISR */
defaultISR, /* DMA_ERR ISR */
defaultISR, /* DMA_INT3 ISR */
defaultISR, /* DMA_INT2 ISR */
defaultISR, /* DMA_INT1 ISR */
defaultISR, /* DMA_INT0 ISR */
defaultISR, /* PORT1 ISR */
defaultISR, /* PORT2 ISR */
defaultISR, /* PORT3 ISR */
defaultISR, /* PORT4 ISR */
defaultISR, /* PORT5 ISR */
defaultISR, /* PORT6 ISR */
defaultISR, /* Reserved 41 */
defaultISR, /* Reserved 42 */
defaultISR, /* Reserved 43 */
defaultISR, /* Reserved 44 */
defaultISR, /* Reserved 45 */
defaultISR, /* Reserved 46 */
defaultISR, /* Reserved 47 */
defaultISR, /* Reserved 48 */

defaultISR, /* Reserved 49 */
defaultISR, /* Reserved 50 */
defaultISR, /* Reserved 51 */
defaultISR, /* Reserved 52 */
defaultISR, /* Reserved 53 */
defaultISR, /* Reserved 54 */
defaultISR, /* Reserved 55 */
defaultISR, /* Reserved 56 */
defaultISR, /* Reserved 57 */
defaultISR, /* Reserved 58 */
defaultISR, /* Reserved 59 */
defaultISR, /* Reserved 60 */
defaultISR, /* Reserved 61 */
defaultISR, /* Reserved 62 */
defaultISR, /* Reserved 63 */
defaultISR /* Reserved 64 */
};


/* This is the code that gets called when the processor first starts execution */
/* following a reset event. Only the absolutely necessary set is performed, */
/* after which the application supplied entry() routine is called. Any fancy */
/* actions (such as making decisions based on the reset cause register, and */
/* resetting the bits in that register) are left solely in the hands of the */
/* application. */
void resetISR(void)
{
/* Jump to the CCS C Initialization Routine. */
__asm( .global _c_int00\n
b.w _c_int00);
}



/* This is the code that gets called when the processor receives a NMI. This */
/* simply enters an infinite loop, preserving the system state for examination */
/* by a debugger. */
static void nmiISR(void)
{
/* Fault trap exempt from ULP advisor */
#pragma diag_push
#pragma CHECK_ULP(-2.1)

/* Enter an infinite loop. */
while(1)
{
}

#pragma diag_pop
}


/* This is the code that gets called when the processor receives a fault */
/* interrupt. This simply enters an infinite loop, preserving the system state */
/* for examination by a debugger. */
static void faultISR(void)
{
/* Fault trap exempt from ULP advisor */
#pragma diag_push
#pragma CHECK_ULP(-2.1)

/* Enter an infinite loop. */
while(1)

{
}

#pragma diag_pop
}


/* This is the code that gets called when the processor receives an unexpected */
/* interrupt. This simply enters an infinite loop, preserving the system state */
/* for examination by a debugger. */
static void defaultISR(void)
{
/* Fault trap exempt from ULP advisor */
#pragma diag_push
#pragma CHECK_ULP(-2.1)

/* Enter an infinite loop. */
while(1)
{
}

#pragma diag_pop
}

Lets determine what our basic application will do. The Timer32 has two independent
identical timers, Timer 1 and Timer 2. Our application will use Timer 1. While running,
the application will generate the pulse train on pin P10.5 with the frequency that is half the
frequency of the Timer 1. Pin P10.5 will be driven ON/OFF each time the counter of
Timer 1 reaches 0. This algorithm can easily be implemented using the Timer32
interrupt.
It is time to add the functionality to our project. Open the file msp432_startup_ccs.c and
insert the definition of the Timer32 Interrupt Service Routine for Timer 1 (Listing 34).
Our ISR handler will be called Timer32IsrHandler.


Listing 34.

/* External declarations for the interrupt handlers used by the application. */


extern void Timer32IsrHandler(void);
/* To be added by user */


Also we should insert the pointer to our interrupt in the corresponding position in the
interruptVectors table as is shown in Listing 35.

Listing 35.

defaultISR, /* EUSCIB3 ISR */


defaultISR, /* ADC14 ISR */
Timer32IsrHandler, /* T32_INT1 ISR */
defaultISR, /* T32_INT2 ISR */
defaultISR, /* T32_INTC ISR */


Since Timer32 has two independent timers, Timer 1 and Timer 2, there are two possible
interrupt vectors allocated for Timer32 (T32_INT1 ISR and T32_INT2 ISR). The
address labeled T32_INT1 ISR is reserved for the interrupt vector of Timer 1, while
T32_INT2 ISR is reserved for Timer 2.
In our application, we will use only the interrupt vector for Timer 1
(Timer32IsrHandler).

Finally, we must modify the main.c file so that it appears as follows (Listing 36).

Listing 36.

#include msp.h


void main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
// Configure pin P10.5
P10DIR |= BIT5; // Pin P10.5 is set as output
P10OUT &= ~BIT5; // P10.5 is initially 0

// Timer32 set up in periodic mode, 32-bit, without pre-scale
TIMER32_CONTROL1 = TIMER32_CONTROL1_SIZE |
TIMER32_CONTROL1_MODE;

// Load Timer32 counter with period = 300
// The frequency of Timer32 will be 3MHz / 300 = 10 KHz
TIMER32_LOAD1 = 300;

__enable_interrupt();
NVIC_ISER0 = 1 << ((INT_T32_INT1 - 16) & 31);

SCB_SCR |= SCB_SCR_SLEEPONEXIT; // Enable sleep on exit from ISR

// Start Timer32 with interrupt enabled
TIMER32_CONTROL1 |= TIMER32_CONTROL1_ENABLE |
TIMER32_CONTROL1_IE;

while (1)
{
__sleep();
}
}

void Timer32IsrHandler(void)

{
TIMER32_INTCLR1 |= BIT0; // Clear Timer32 interrupt flag for Timer 1
P10OUT ^= BIT5; // Toggle pin P10.5
}

In this source, the following two statements at the beginning of Listing 36 configure pin
P10.5 of MSP432P401R as output with the initial LOW level:

P10DIR |= BIT5;
P10OUT &= ~BIT5;

The Timer32 Control Register for Timer 1 (variable TIMER32_CONTROL1) is
configured as follows:

TIMER32_CONTROL1 = TIMER32_CONTROL1_SIZE |
TIMER32_CONTROL1_MODE;

The TIMER32_CONTROL1_SIZE field (bit 1 of the TIMER32_CONTROL1 register)
allows to select 32-bit counter operation. The TIMER32_CONTROL1_MODE field (bit
6 of TIMER32_CONTROL1) puts Timer 1 in Periodic Mode.

The load register for Timer 1 (variable TIMER32_LOAD1) is assigned the value of 300:

TIMER32_LOAD1 = 300;

The TIMER32_LOAD1 register is a 32-bit register containing the value from which the
counter is to decrement. This is the value used to reload the counter when Periodic Mode
is enabled, and the current count reaches zero.
The value in load register determines the period (frequency) of a corresponding timer. In
this application, the default clock source at the Timer32 input provides the signal with the
frequency of 3 MHz. With the value = 300 in TIMER32_LOAD1, Timer 1 will overflow
at a frequency of 3 MHz / 300 = 10 KHz. This also means that an Interrupt Service
Routine for Timer 1 will be called 10000 times per 1 s. By altering the value in the
TIMER32_LOAD1 register, we can adjust the output frequency on pin P10.5, as well.

Since we will use the Timer32 interrupt, we need to enable them by executing the
following sequence:

__enable_interrupt();
NVIC_ISER0 = 1 << ((INT_T32_INT1 - 16) & 31);

__enable_interrupt() intrinsic operator enables all interrupts in systems. This is
followed by the statement that enables the Timer 1 interrupt within the Nested Vectored
The

Interrupt Controller (NVIC).


The following statement enables the interrupt for Timer 1 and starts counting:

TIMER32_CONTROL1 |= TIMER32_CONTROL1_ENABLE |
TIMER32_CONTROL1_IE;

The TIMER32_CONTROL1_ENABLE field (bit 7 in the TIMER32_CONTROL1
register) enables Timer 1, so the timer starts counting. The TIMER32_CONTROL1_IE
field (bit 5 in the TIMER32_CONTROL1 register) enables Timer 1 interrupt.

The ISR handler for Timer 1 is the function Timer32IsrHandler(). The first what the
program code should do when an ISR handler is entered is to clear the corresponding
interrupt flag. That is done by the following statement:

TIMER32_INTCLR1 |= BIT0;

The variable TIMER32_INTCLR1 represents the Timer 1 Interrupt Clear Register. Any
write to the TIMER32_INTCLR1 register clears the interrupt output from the counter of
Timer 1.
The statement that follows toggles pin P10.5 each time the Timer 1 interrupt is activated:

P10OUT ^= BIT5;

The pulse train on the P10.5 output will be half the frequency of Timer 1. In this
particular case, Timer 1 operates at 10 KHz, so the frequency on pin P10.5 will be 10
KHz / 2 = 5 KHz.


The running application produces the following output on pin P10.5 (Fig.24).

Fig.24

Designing Real-Time applications: project 10



This project illustrates how to configure the frequency of an MSP432P401R Timer32 from
Raspbian OS running on the Raspberry Pi.
The circuit diagram of the project is shown in Fig.25.

Fig.25

In this circuit, an 8-bit binary code on GPIO pins of the Raspberry Pi is read by MSP432
MCU after the interrupt on pin P1.5 has been activated by the falling edge on pin GPIO5.
This 8-bit code is then used to configure the frequency of Timer 1.
The connections between the MSP432P401R board and Raspberry Pi 2 are detailed in the
table below.

Raspberry Pi 2 J8
pin

MSP432P401R LaunchPad pin

GPIO18

P4.0 (Bit 0 of the 8-bit code)

GPIO23

P4.1 (Bit 1 of the 8-bit code)

GPIO24

P4.2 (Bit 2 of the 8-bit code)

GPIO25

P4.3 (Bit 3 of the 8-bit code)

GPIO8

P4.4 (Bit 4 of the 8-bit code)

GPIO7

P4.5 (Bit 5 of the 8-bit code)

GPIO12

P4.6 (Bit 6 of the 8-bit code)

GPIO20

P4.7 (Bit 7 of the 8-bit code)

GPIO5

P1.5 (Interrupt line triggered by a falling edge)


The CCS source code (file main.c) of the MSP432 application is shown in Listing 37.

Listing 37.

#include msp.h

volatile int bP4 = 0;

void main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
// Configure all pins of port P4 as inputs
P4DIR = 0x0; // Port P4 is input
P4REN = 0xFF; // Select pull-up mode for P4
P4OUT = 0xFF; // pull-up resistors on P4

// Configure pin P1.6
P1DIR |= BIT6; // P1.6 is set as output
P1OUT &= ~BIT6; // P1.6 is initially set to 0


// Configure pin P1.5 as an interrupt source

P1REN |= BIT5; // Select pull-up mode for P1.5
P1OUT |= BIT5; // pull-up resistors on P1.5

P1IES |= BIT5; // P1.5 Hi/Lo edge
P1IFG = 0; // Clear all P1 interrupt flags
P1IE |= BIT5; // P1.6 interrupt enabled

// Configuring Timer32 (Timer 1)
// Timer 1 is set up in periodic mode, 32-bit, without pre-scale
TIMER32_CONTROL1 = TIMER32_CONTROL1_SIZE |
TIMER32_CONTROL1_MODE;

// Load the Timer32 (Timer 1) counter with the initial period value of 300
// This gives us the Timer1 frequency of 3MHz/300 = 10 KHz
TIMER32_LOAD1 = 300;

// Enable all interrupts in the system
__enable_interrupt();
// Enable the interrupt of Timer32 (Timer 1) in NVIC Controller
NVIC_ISER0 = 1 << ((INT_T32_INT1 - 16) & 31);
// Enable port P1 interrupts in NVIC Controller
NVIC_ISER1 = 1 << ((INT_PORT1 - 16) & 31);

// Start Timer32 (Timer 1) with interrupt enabled
TIMER32_CONTROL1 |= TIMER32_CONTROL1_ENABLE |
TIMER32_CONTROL1_IE;

while (1)
{

__sleep();
}
}

void Timer32ISRHandler(void) {
TIMER32_INTCLR1 |= BIT0; // Clear Timer32 interrupt flag for Timer1
P1OUT ^= BIT6; // Toggle pin P1.6
}

/* Port 1 interrupt service routine */
void Port1IsrHandler(void) {
P1IFG &= ~BIT5; // Clear P1.5 IFG
bP4 = P4IN;
TIMER32_LOAD1 = 300 - bP4;
}

In this code, the 8-bit data are read via port P4, so we configure all its pins as inputs with
pull-up resistors using the following sequence:

P4DIR = 0x0;
P4REN = 0xFF;
P4OUT = 0xFF;

The output pulse train appears on pin P1.6, so we configure it as output with the initial
state 0:

P1DIR |= BIT6;
P1OUT &= ~BIT6;

Data will be read to port P4 after interrupt on pin P1.5 has been activated. Pin P1.5 is
configured as an interrupt source by the following sequence:

P1REN |= BIT5;
P1OUT |= BIT5;
P1IES |= BIT5;
P1IFG = 0;
P1IE |= BIT5;

Timer 1 of a Timer32 module will operate in periodic mode with a 32-bit counter,
without pre-scale. These parameters are set by the statement:

TIMER32_CONTROL1 = TIMER32_CONTROL1_SIZE |
TIMER32_CONTROL1_MODE;

The load register of Timer 1 will hold the initial value of 300 that corresponds to the
frequency 3MHz/300 = 10 KHz:

TIMER32_LOAD1 = 300;

To start Timer 1 and enable its interrupt we use the statement:

TIMER32_CONTROL1 |= TIMER32_CONTROL1_ENABLE |
TIMER32_CONTROL1_IE;

Our application uses 2 interrupts, so we must enable them in an Interrupt Controller:

__enable_interrupt();
NVIC_ISER0 = 1 << ((INT_T32_INT1 - 16) & 31);
NVIC_ISER1 = 1 << ((INT_PORT1 - 16) & 31);

In this code, the variable bP4 takes the 8-bit code from port P4 when Port1IsrHandler()
is invoked:

bP4 = P4IN;

Then the new frequency is written to the Timer 1 load register using the following
statement:

TIMER32_LOAD1 = 300 - bP4;

Listing 38 below shows the fragment of the Interrupt Vector table (file
msp432_startup_ccs.c) with Interrupt Service Routines Timer32ISRHandler and
Port1IsrHandler included.

Listing 38.

/* External declarations for the interrupt handlers used by the application. */
extern void Timer32ISRHandler(void);
extern void Port1IsrHandler(void);
/* To be added by user */

/* Interrupt vector table. Note that the proper constructs must be placed on this to */
/* ensure that it ends up at physical address 0x0000.0000 or at the start of */
/* the program if located at a start address other than 0. */
#pragma DATA_SECTION(interruptVectors, .intvecs)
void (* const interruptVectors[])(void) =
{
(void (*)(void))((uint32_t)&__STACK_END),
/* The initial stack pointer */
resetISR, /* The reset handler */
nmiISR, /* The NMI handler */
faultISR, /* The hard fault handler */
defaultISR, /* The MPU fault handler */
defaultISR, /* The bus fault handler */
defaultISR, /* The usage fault handler */
0, /* Reserved */
. . .

defaultISR, /* EUSCIB3 ISR */


defaultISR, /* ADC14 ISR */
Timer32ISRHandler, /* T32_INT1 ISR */
defaultISR, /* T32_INT2 ISR */
defaultISR, /* T32_INTC ISR */
defaultISR, /* AES ISR */
defaultISR, /* RTC ISR */
defaultISR, /* DMA_ERR ISR */
defaultISR, /* DMA_INT3 ISR */
defaultISR, /* DMA_INT2 ISR */
defaultISR, /* DMA_INT1 ISR */
defaultISR, /* DMA_INT0 ISR */
Port1IsrHandler, /* PORT1 ISR */
defaultISR, /* PORT2 ISR */
. . .


The Python application for configuring Timer32 (Timer 1) has the following source code
(Listing 39).

Listing 39.

import RPi.GPIO as GPIO

GPIO.setwarnings(False)
GPIO.setmode(GPIO.BCM)

GPIO.setup(18, GPIO.OUT)
GPIO.setup(23, GPIO.OUT)
GPIO.setup(24, GPIO.OUT)
GPIO.setup(25, GPIO.OUT)
GPIO.setup(8, GPIO.OUT)

GPIO.setup(7, GPIO.OUT)
GPIO.setup(12, GPIO.OUT)
GPIO.setup(20, GPIO.OUT)

# This is an interrupt line to P1.5 of MSP432
GPIO.setup(5, GPIO.OUT)

# set the frequency of Timer32 (Timer 1) , Timer1_Freq =3 MHz / (300 [ bit7 : bit0 ] )
# the frequency on pin P1.6 will be Freq/2

GPIO.output(5, GPIO.HIGH) # An interrupt line to P1.5 goes HIGH

GPIO.output(18, GPIO.LOW) # bit 0
GPIO.output(23, GPIO.LOW) # bit 1
GPIO.output(24, GPIO.LOW) # bit 2
GPIO.output(25, GPIO.HIGH) # bit 3
GPIO.output(8, GPIO.LOW) # bit 4
GPIO.output(7, GPIO.LOW) # bit 5
GPIO.output(12, GPIO.HIGH) # bit 6
GPIO.output(20, GPIO.HIGH) # bit 7

GPIO.output(5, GPIO.LOW) # An interrupt line to P1.5 goes LOW

In this source code, 8 output pins (GPIO18, GPIO23, GPIO24, GPIO25, GPIO8,
GPIO7, GPIO12, GPIO20) determine the 8-bit binary code being used for configuring
the frequency of Timer32 of MSP432P401R MCU.
The table below shows the mapping between GPIO pins and data bits.
Raspberry Pi 2
pin

Bit in 8-bit data


byte

GPIO18

Bit 0

GPIO23

Bit 1

GPIO24

Bit 2

GPIO25

Bit 3

GPIO8

Bit 4

GPIO7

Bit 5

GPIO12

Bit 6

GPIO20

Bit 7


The Timer 1 frequency will be calculated as

Timer1_Freq = 3 MHz / (300 [ bit7 : bit0 ] )

In this application, bit7 bit0 are set by the following sequence:

GPIO.output(18, GPIO.LOW) # bit 0
GPIO.output(23, GPIO.LOW) # bit 1
GPIO.output(24, GPIO.LOW) # bit 2
GPIO.output(25, GPIO.HIGH) # bit 3
GPIO.output(8, GPIO.LOW) # bit 4
GPIO.output(7, GPIO.LOW) # bit 5
GPIO.output(12, GPIO.HIGH) # bit 6
GPIO.output(20, GPIO.HIGH) # bit 7

This binary code corresponds to value of 200, therefore the Timer 1 frequency will be
equal to 3 MHz / (300 200) = 30 KHz. The frequency on pin P1.6 will be 30 KHz / 2 =
15 KHz.

Pin GPIO5 controls the interrupt line connected to pin P1.5 of the MSP432P401R MCU.
Initially, GPIO5 is pulled HIGH by the statement:

GPIO.output(5, GPIO.HIGH)


Once the 8-bit code is ready to be loaded, the interrupt line is activated by bringing pin
GPIO5 LOW:

GPIO.output(5, GPIO.LOW)

The Timer32 interrupt handler of MSP432 application will then process the 8-bit binary
code read from port P4.

Designing a Digital-to-Analog converter with MSP432



This section is dedicated to designing a simple digital-to-analog converter controlled by an
MSP432P401R LaunchPad. Digital-to-analog converters (D/A converters, DACs) are very
useful for generating precision DC voltage, control of voltage-controlled oscillators
(VCO), synthesizing the signals of various waveforms, etc.
Microcontroller MSP432P401R has no an integrated D/A converter, so we will design a
simple D/A circuit with an 8-bit DAC AD5330 with parallel inputs. Below is a brief
description of the AD5330 device.
The AD5330 is a single 8-bit DAC which operates from a 2.5 V to 5.5 V supply
consuming just 115 A at 3 V and feature a power-down mode that further reduces the
current to 80 nA. The device incorporates an on-chip output buffer that can drive the
output to both supply rails, but AD5330 allows a choice of buffered or unbuffered
reference input.
The AD5330 has a parallel interface. The CS signal selects the device and data is loaded
into the input registers on the rising edge of the WR signal. The GAIN pin allows the
output range to be set at 0 V to V REF or 0 V to 2 VREF.
Input data to the DAC is double-buffered, allowing simultaneous update of multiple DACs
in a system using the LDAC pin. An asynchronous CLR input allows to reset the contents
of the input register and the DAC register to all zeros. This device also incorporates a
power-on reset circuit that ensures that the DAC output powers on to 0 V and remains
there until valid data is written to the device.
The AD5330 pin functions are described in the table below.
Pin
No.

Mnemonic Description

BUF

Buffer Control Pin. This pin controls whether the


reference input to the DAC is buffered or
unbuffered.

NC

No Connect.

VREF

Reference Input.

VOUT

Output of DAC. Buffered output with rail-to-rail


operation.

GND

Ground reference point for all circuitry on the part.

CS

Active Low Chip Select Input. This is used in

conjunction with WR to write data to the parallel


interface.
7

WR

Active Low Write Input. This is used in conjunction


with CS to write data to the parallel interface.

GAIN

Gain Control Pin. This controls whether the output


range from the DAC is 0 V to VREF or 0 V to 2
VREF.

CLR

Asynchronous active low control input that clears all


input registers and DAC registers to zero.

10

LDAC

Active low control input that updates the DAC


registers with the contents of the input registers.

11

PD

Power-Down Pin. This active low control pin puts


the DAC into power-down mode.

12

VDD

Power Supply Input. These parts can operate from


2.5 V to 5.5 V and the supply should be decoupled
with a 10 F capacitor in parallel with a 0.1 F
capacitor to GND.

13 to
20

DB0 to
DB7

Eight Parallel Data Inputs. DB7 is the MSB of these


eight bits.


The simple demo application illustrates use of the DAC AD5330 driven by
MSP432P401R MCU. The circuit diagram of the application is shown in Fig.26.

Fig.26

In this circuit, the parallel inputs (DB0 DB7) of AD5330 are connected to port P4 (pins
P4.0 P4.7). The CS control signal to AD5330 is taken from pin P1.5 of MCP432P401R,
the WR signal is taken from pin P1.6. The reference voltage to AD5330 (pin 3) is selected
to be +3.3V (VDD).

The Energia MT source code of the MSP432 application is shown in Listing.40.

Listing 40.

#include msp.h

float vout = 0.0;
float LSB = 3.3/256;

void setup()
{
// put your setup code here, to run once:


WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
// Configure GPIO Port 4
P4DIR = 0xFF; // Pins P4.0 - P4.7 (DB0>DB7) are set as outputs
// Configure P1.5 - P1.6
P1DIR |= BIT5 | BIT6; // P1.5 (CS) and P1.6(WR) are set as outputs
}

void loop()
{
// put your main code here, to run repeatedly:
if (vout <= 3.3)
{
P4OUT = (int)(vout/LSB);
delayMicroseconds(10);

P1OUT |= BIT5 | BIT6; // CS = WR = 1
P1OUT &= ~BIT5; // CS = 0
P1OUT &= ~BIT6; // WR = 0
delayMicroseconds(10);

P1OUT |= BIT6; // WR = 1
P1OUT |= BIT5; // CS = 1
vout += 0.4;
delay(3000);
}
else
vout = 0.0;
}

In this source code, all bits of port P4 are configured as outputs:


P4DIR = 0xFF;

Pins P1.5 (CS) and P1.6 (WR) provide the control signals to AD5330, so they should also
be configured as outputs by the statement:

P1DIR |= BIT5 | BIT6;

To understand how the DAC output is formed, lets look at the truth table for AD5330 (X
= dont care).

CLR

LDAC

CS

WR

Function

No data transfer

No data transfer

Clear all registers

01

Load input register

01

Load input register and DAC


register

Update DAC register


According to this table, the analog output of the D/A converter is updated by the rising
edge of the WR signal when both LDAC and CS signal lines stay LOW. The bits of port
P4 should already contain the valid data before the write operation starts off.
This is reflected by the following sequence in the program code:

P4OUT = (int)(vout/LSB);
delayMicroseconds(10);

P1OUT |= BIT5 | BIT6;
P1OUT &= ~BIT5;

P1OUT &= ~BIT6;


delayMicroseconds(10);

P1OUT |= BIT6;
P1OUT |= BIT5;

Initially, both LDAC and CS lines are pulled HIGH by the statement

P1OUT |= BIT5 | BIT6;

Then CS goes LOW:

P1OUT &= ~BIT5;

This is immediately followed by the statement

P1OUT &= ~BIT6;

that pulls the WR line LOW.

The following sequence completes updating the D/A output:

P1OUT |= BIT6;
P1OUT |= BIT5;

The short delays about 10uS provided by the delayMicroseconds(10) statements are
optional; they can increase the reliability when some level of noise is presented in the
circuit.

Designing Real-Time applications: project 11



This project allows to design the D/A converter with AD5330 controlled by the Python
application running on the Raspberry Pi.
The circuit diagram of the application is shown in Fig.27.

Fig.27

The circuit is almost the same as that shown in Fig.26. The only modification is the
connection between the MSP432P401R LaunchPad and Raspberry Pi. Here the D/A
converter will be controlled by a Python application running in Raspbian OS.

The Energia MT source code (file AD5330_DAC_UART.ino) for MSP432P401R
processor is shown in Listing 41.

Listing 41.


#include msp.h

float vout = 0.0;
float LSB = 3.3/256;
int cmd;

void setup()
{
// put your setup code here, to run once:

WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
// Configure GPIO Port 4
P4DIR = 0xFF; // Pins P4.0 - P4.7 (DB0>DB7) are set as outputs
// Configure P1.5 - P1.6
P1DIR |= BIT5 | BIT6; // P1.5 (CS) and P1.6(WR) are set as outputs
//Initial settings for DAC
P4OUT = 128; // vout = 1.6V
delayMicroseconds(10);

P1OUT |= BIT5 | BIT6; // CS = WR = 1
P1OUT &= ~BIT5; // CS = 0
P1OUT &= ~BIT6; // WR = 0
delayMicroseconds(10);

P1OUT |= BIT6; // WR = 1
P1OUT |= BIT5; // CS = 1

Serial.begin(9600);
}

void loop()
{
// put your main code here, to run repeatedly:
while (Serial.available() > 0)
{

// look for the next valid integer in the incoming serial stream:
cmd = Serial.parseInt(); // cmd line: <4,freq>
if (cmd == 4) // 4 = write DAC command
{
vout = Serial.parseFloat();
if((vout >= 0.1) && (vout <= 3.3))
{
P4OUT = (int)(vout/LSB);
delayMicroseconds(10);

P1OUT |= BIT5 | BIT6; // CS = WR = 1
P1OUT &= ~BIT5; // CS = 0
P1OUT &= ~BIT6; // WR = 0
delayMicroseconds(10);

P1OUT |= BIT6; // WR = 1
P1OUT |= BIT5; // CS = 1
}
}
}
delay(1000);
}

In this code, the data being received via the serial interface are in CSV format such as

<value1,value2>

where value1 is a command code and value2 is a parameter (new D/A output, in our
case).
In this application, the data string will be, for example, as follows:

4,1.33

The value of 4 indicates the write command to DAC, and the value of 1.33 will be a new
DAC output equal to 1.33 V.
Parsing the data string is performed by the following fragment of code:

. . .
cmd = Serial.parseInt();
if (cmd == 4) // 4 = write DAC command
{
vout = Serial.parseFloat();
. . .

The rest of code has already been discussed in the previous section, so we will move to the
Raspberry Pi software.

The Python application running in Raspbian OS has the following source code (Listing
42).

Listing 42.

# This script illustrates passing command line parameters
# through a serial interface. The command line
# arguments are as follows:
# -t determines the timer (3 = Timer_A3)
# -f is the frequency to be set

# Cmd line: sudo <path> -c 4 -v <DAC value>


#Ex.: pi@raspberrypi ~/Developer/MSP432 $ sudo python Set_DAC_UART.py -c 4 -v
1.22

import serial
import time
import argparse

parser = argparse.ArgumentParser(description=Configuring the DAC output)
parser.add_argument(-c, cmd, help=command, required=True)
parser.add_argument(-v, voltage, help=Desired voltage, required=True)
args = parser.parse_args()
str1 = args.cmd + , + args.voltage

s1 = serial.Serial(port=/dev/ttyACM0,
baudrate=9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1)
time.sleep(1)
s1.write(str1)
time.sleep(1)
s1.close()

To operate with command line parameters, we will use the functions from the argparse
module. The application simply forms the data string in CSV format by combining
command line parameters using the following sequence:

args = parser.parse_args()
str1 = args.cmd + , + args.voltage

Then the str1 string is sent via the serial interface associated with the object s1. The
Python application can be launched as shown below.

pi@raspberrypi ~/Developer $ sudo python Set_DAC_UART.py -c 4 -v 1.22
pi@raspberrypi ~/Developer $ sudo python Set_DAC_UART.py -c 4 -v 2.66
pi@raspberrypi ~/Developer/MSP432 $ sudo python Set_DAC_UART.py -c 4 -v 0.77

Potrebbero piacerti anche