Hackster is hosting Hackster Holidays, Ep. 6: Livestream & Giveaway Drawing. Watch previous episodes or stream live on Monday!Stream Hackster Holidays, Ep. 6 on Monday!
Graham Chow
Published © MIT

Raspberry PI with Windows 10 made realtime with a launchpad

Connecting a 16 bit MCU (launchpad) to Raspberry PI running Windows 10 to off load real time processing.

IntermediateFull instructions provided5,592
Raspberry PI with Windows 10 made realtime with a launchpad

Things used in this project

Hardware components

Raspberry Pi 2 Model B
Raspberry Pi 2 Model B
×1
MSP-EXP430G2 MSP430 LaunchPad
Texas Instruments MSP-EXP430G2 MSP430 LaunchPad
×1
Ultrasonic Sensor - HC-SR04 (Generic)
Ultrasonic Sensor - HC-SR04 (Generic)
ultrasonic range finder
×1
28BYJ48
Stepper Motor (optional)
×1
JY-MCU
Bluetooth uart
×1
X113647
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Breadboard (generic)
Breadboard (generic)
×1
resistor 10K
×2

Software apps and online services

Windows 10 IoT Core
Microsoft Windows 10 IoT Core
Visual Studio 2015
Microsoft Visual Studio 2015

Hand tools and fabrication machines

Code Composer Studio

Story

Read more

Schematics

Schematic

Code

main.c

C/C++
MSP430 code for the launchpad
#include "msp430g2553.h"

#define LED1 BIT0
#define TXD BIT2
#define RXD BIT1
#define SWITCH BIT7

#define STEPPER_orange BIT1
#define STEPPER_yellow BIT2
#define STEPPER_pink BIT3
#define STEPPER_blue BIT4

#define INTERRUPT_SIGNAL BIT5

#define ULTRASONIC_INPUT 0x1
#define ULTRASONIC_OUTPUT 0x10



/*
 * main.c
 * p1.0 led indicator
 * p1.1 uart
 * p1.2 uart
 * p1.6 i2c SCL clock
 * p1.7 i2c SDA data
 * p1.3 input switch (stop/start)
 * p1.4 ultrasonic trigger
 * p2.0 ultrasonic capture timer A1
 * p2.1-p2.4 stepper 0-3 timer A0 software gpio
 * p1.5 interrupt - data ready
 */

static char tx_buffer[16] = "XXXX_XX\r";
static char i2c_tx_buffer[3] = { 0 };
static char i2c_rx_address = 0;
unsigned int tx_buffer_i = 0;
unsigned int ultrasonic_start = 0;
unsigned int ultrasonic_period = 0;
unsigned int state = 0;

char ConvertIntToAscii(unsigned int v, unsigned int offset)
{
	v >>= offset;
	v &= 0xF;
	if(v < 10)
		return v + '0';
	else
		return (v-10) + 'A';
}

#define DELAY_100ms (50000)
#define DELAY_10ms (5000)
#define DELAY_60ms (30000)
#define DELAY_1ms (500)
#define DELAY_2ms (500)
#define DELAY_100us (50)

void delayUnits(unsigned int delay)
{
	TAR = 0;
	TACCR0 = delay;
	TACCTL0 = CCIE;                         // Compare-mode interrupt.
	TACTL = TASSEL_2 | MC_1 | ID_3;         // TACLK = SMCLK, Up mode, 4Mhz / 8 = 500kHz clock
	volatile unsigned int tactl;
	do
	{
		__bis_SR_register(CPUOFF + GIE); 		// Enter LPM0
		tactl =(TACTL & MC_3);
	}while(tactl != 0);
	TACCTL0 &= ~CCIE;
}

#define STEPPER_STEP(on, off, port) ((on) | (port & ~(off)))

void StepClockwise(unsigned int delay)
{
	// step 1
	P2OUT = STEPPER_STEP(STEPPER_orange, STEPPER_yellow | STEPPER_pink | STEPPER_blue, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_orange | STEPPER_yellow, STEPPER_pink | STEPPER_blue, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_yellow, STEPPER_pink | STEPPER_blue | STEPPER_orange, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_yellow | STEPPER_pink, STEPPER_blue | STEPPER_orange, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_pink, STEPPER_blue | STEPPER_orange | STEPPER_yellow, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_pink | STEPPER_blue, STEPPER_orange | STEPPER_yellow, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_blue, STEPPER_orange | STEPPER_yellow | STEPPER_pink, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_blue | STEPPER_orange, STEPPER_yellow | STEPPER_pink, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(0, STEPPER_blue | STEPPER_orange | STEPPER_yellow | STEPPER_pink, P2OUT);
}

void StepAntiClockwise(unsigned int delay)
{
	// step 1
	P2OUT = STEPPER_STEP(STEPPER_blue, STEPPER_orange | STEPPER_yellow | STEPPER_pink, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_pink | STEPPER_blue, STEPPER_orange | STEPPER_yellow, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_pink, STEPPER_blue | STEPPER_orange | STEPPER_yellow, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_yellow | STEPPER_pink, STEPPER_blue | STEPPER_orange, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_yellow, STEPPER_pink | STEPPER_blue | STEPPER_orange, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_orange | STEPPER_yellow, STEPPER_pink | STEPPER_blue, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_orange, STEPPER_yellow | STEPPER_pink | STEPPER_blue, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(STEPPER_blue | STEPPER_orange, STEPPER_yellow | STEPPER_pink, P2OUT);
	delayUnits(delay);
	P2OUT = STEPPER_STEP(0, STEPPER_blue | STEPPER_orange | STEPPER_yellow | STEPPER_pink, P2OUT);
}

void init_I2C()
{
    // pin 1.6 and 1.7 as i2C input for scl
	P1REN &= ~0xC0;
    P1DIR &= ~0xC0;
    P1SEL |= 0xC0;
    P1SEL2 |= 0xC0;
    P1REN &= ~0xC0;

    UCB0CTL1 = UCSWRST;
    UCB0CTL0 = UCMODE_3 + UCSYNC;     		// slave mode
    UCB0I2COA = 0x48; 						// address == 0x48
    UCB0CTL1 &= ~UCSWRST;
    UC0IE |= UCB0RXIE | UCB0TXIE;
    UCB0I2CIE |= UCSTTIE;
}

void ProcessState()
{
	int j;
	if((state < 10) || (state >= 30))
	{
		for(j=16;j>0;j--)
		{
			StepClockwise(DELAY_2ms);
		}
	}
	else
	{
		for(j=16;j>0;j--)
		{
			StepAntiClockwise(DELAY_2ms);
		}
	}
	state = state + 1;
	if(state >= 40)
	{
		state = 0;
	}
}


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

    WDTCTL = WDTPW + WDTHOLD; // Stop WDT
    DCOCTL = 0; // Select lowest DCOx and MODx settings
    BCSCTL1 = CALBC1_16MHZ; // Set DCO
    BCSCTL2 = DIVS1; // SMCLK runs at 16/4 = 4Mhz
    DCOCTL = CALDCO_16MHZ;

    // configure uart
    P2DIR |= 0xFF; // All P2.x outputs
    P2OUT &= 0x00; // All P2.x reset
    P1SEL |= RXD + TXD ; // P1.1 = RXD, P1.2=TXD
    P1SEL2 |= RXD + TXD ; // P1.1 = RXD, P1.2=TXD
    //P1DIR |= RXLED + TXLED;
    P1OUT &= 0x00;
    UCA0CTL1 |= UCSSEL_2; // SMCLK

    // pin 1.0 as output led
    P1DIR |= LED1;
    P1SEL &= ~LED1;

    // pin 1.5 interrupt signal data ready
    P1DIR |= INTERRUPT_SIGNAL;
	P1SEL &= ~INTERRUPT_SIGNAL;
	P1SEL2 &= ~INTERRUPT_SIGNAL;
	P1OUT &= ~INTERRUPT_SIGNAL;

    // pin 1.4 as ultrasonic output trigger
    P1DIR |= ULTRASONIC_OUTPUT;
    P1OUT &= ~ULTRASONIC_OUTPUT;
    P1SEL &= ~ULTRASONIC_OUTPUT;
    P1SEL2 &= ~ULTRASONIC_OUTPUT;

    // configure switch
    P1DIR &= ~SWITCH;
    P1OUT |= SWITCH;
    P1SEL &= ~SWITCH;
    P1SEL2 &= ~SWITCH;
    P1REN |= SWITCH;

    // pin 2.0 ultrasonic input
    P2DIR &= ~ULTRASONIC_INPUT;
    P2OUT &= ~ULTRASONIC_INPUT;
    P2SEL |= ULTRASONIC_INPUT;
    P2SEL2 &= ~ULTRASONIC_INPUT;
    P2REN |= ULTRASONIC_INPUT;

    // pin 2.1-2.4 stepper
    P2DIR |= (STEPPER_orange | STEPPER_yellow | STEPPER_pink | STEPPER_blue);
    P2OUT &= ~(STEPPER_orange | STEPPER_yellow | STEPPER_pink | STEPPER_blue);
    P2REN &= ~(STEPPER_orange | STEPPER_yellow | STEPPER_pink | STEPPER_blue);
    P2SEL2 &= ~(STEPPER_orange | STEPPER_yellow | STEPPER_pink | STEPPER_blue);
    P2SEL2 &= ~(STEPPER_orange | STEPPER_yellow | STEPPER_pink | STEPPER_blue);

    // uart
    // set to 115200 baud, UCBRx = 8, UCBRSx = 0, UCBRFx=11, UCOS16 = 1
    //UCA0BR0 = 8;
    //UCA0BR1 = 0;
    //UCA0MCTL = UCBRF3 | UCBRF1 | UCBRF0 | UCOS16; // UCBRSx = 0, UCBRFx=1, UCOS16 = 1
    // set to 115200 baud, UCBRx = 2, UCBRSx = 3, UCBRFx=2, UCOS16 = 1
    UCA0BR0 = 2;
    UCA0BR1 = 0;
    UCA0MCTL = UCBRS1 | UCBRS0 | UCBRF1 | UCOS16; // UCBRSx = 0, UCBRFx=1, UCOS16 = 1
    UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**

    // ultrasonic capture
    TA1CTL = TASSEL_2 | MC_2 | ID_0; // SMCLK, up mode, clock running at 4MHz
    TA1CCTL0 = CM_3 | CCIS_0 | SCS | CAP | OUTMOD_0 | CCIE ; // capture both, CCIxA, synchronised capture, capture mode SCCI

    init_I2C();

    // enable uart interrupts
    UC0IE |= UCA0TXIE;
    UC0IE |= UCA0RXIE; // Enable USCI_A0 RX interrupt

    while (1)
    {
    	// trigger
    	P1OUT |= ULTRASONIC_OUTPUT;
    	delayUnits(DELAY_100us);
    	P1OUT &= ~ULTRASONIC_OUTPUT;

    	delayUnits(DELAY_100ms);

    	__disable_interrupt();
    	i2c_tx_buffer[0] = (ultrasonic_period >> 8);
		i2c_tx_buffer[1] = (ultrasonic_period & 0xFF);
		i2c_tx_buffer[2] = state;
		tx_buffer[0] = ConvertIntToAscii(ultrasonic_period, 12);
		tx_buffer[1] = ConvertIntToAscii(ultrasonic_period, 8);
		tx_buffer[2] = ConvertIntToAscii(ultrasonic_period, 4);
		tx_buffer[3] = ConvertIntToAscii(ultrasonic_period, 0);
		tx_buffer[5] = ConvertIntToAscii(state, 4);
		tx_buffer[6] = ConvertIntToAscii(state, 0);
		if(tx_buffer_i == 0)
			UC0IE |= UCA0TXIE;
		__enable_interrupt();

		// signal data ready
		P1OUT |= INTERRUPT_SIGNAL;
		delayUnits(DELAY_1ms);
		P1OUT &= ~INTERRUPT_SIGNAL;

    	ProcessState();
    }
}

#pragma vector=USCIAB0TX_VECTOR
__interrupt void USCI0TX_ISR(void)
{
	// uart
	if(UC0IFG & UCA0TXIFG)
	{
		char txb = tx_buffer[tx_buffer_i++]; // TX next character
		UCA0TXBUF = txb;
		txb = tx_buffer[tx_buffer_i];
		if (txb == 0) // TX over?
		{
			tx_buffer_i = 0;
			UC0IE &= ~UCA0TXIE; // Disable USCI_A0 TX interrupt
		}
	}
    // i2c
    if (UC0IFG & UCB0TXIFG)
    {
    	 UCB0TXBUF = i2c_tx_buffer[i2c_rx_address];
    	 i2c_rx_address++;
    	 if(i2c_rx_address >= 3)
    	 {
    		 i2c_rx_address = 0;
    	 }
    }
    if (UC0IFG & UCB0RXIFG)
    {
    	char rx = UCB0RXBUF;
    }
}

#pragma vector=USCIAB0RX_VECTOR
__interrupt void USCI0RX_ISR(void)
{
	// uart
	if(UC0IFG & UCA0RXIFG)
	{
		char rx_char = UCA0RXBUF;
		//if (rx_char == 'r') // 'r' received?
		//{
		//   tx_buffer_i = 0;
		//   UC0IE |= UCA0TXIE; // Enable USCI_A0 TX interrupt
		//}
	}
    // i2c
    if (UCB0STAT & UCSTTIFG)
    {
    	i2c_rx_address = 0;
    	UCB0STAT &= ~UCSTTIFG;
    }
    if(UCB0STAT & UCSTPIFG)
    {
    	UCB0STAT &= ~UCSTPIFG;
    }
}

#pragma vector=TIMER1_A0_VECTOR
__interrupt void TIMER1_A0_ISR(void)
{
	volatile unsigned int tacct = TA1CCTL0;
	if(tacct & COV) // overflow
	{
		// just clear it
		TA1CCTL0 &= ~COV;
	}
	if((tacct & CCI) != 0)
	{
		// start of ultrasound
		ultrasonic_start = TA1CCR0;
		P1OUT |= 1;
	}
	else
	{
		ultrasonic_period = TA1CCR0 - ultrasonic_start;
		P1OUT &= ~1;
	}
}

#pragma vector=TIMER0_A0_VECTOR
__interrupt void ta0_isr(void)
{
	TACTL &= ~MC_3;
	__bic_SR_register_on_exit(CPUOFF);
}

Driver Code

C#
C# driver code for the raspberry pi
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Windows.Devices.Enumeration;
using Windows.Devices.I2c;
using Windows.Devices.Gpio;

namespace LaunchPadMsp430
{
    public class SonarEventArgs : EventArgs
    {
        public double Range { get; set; }
        public double Yaw { get; set; }
    }

    public partial class LaunchPadMsp430 : IDisposable
    {
        public event EventHandler<SonarEventArgs> SensorInterruptEvent;

        #region Constants

        public const byte ADDRESS = 0x48;

        #endregion

        private const Int32 INTERRUPT_PIN = 18; // GPIO_GEN1)
        I2cDevice _launchPadDevice = null;
        private GpioController IoController;
        private GpioPin InterruptPin;

        #region 12c

        private byte ReadByte(byte regAddr)
        {
            byte[] buffer = new byte[1];
            buffer[0] = regAddr;
            byte[] value = new byte[1];
            _launchPadDevice.WriteRead(buffer, value);
            return value[0];
        }

        private byte[] ReadBytes(byte regAddr, int length)
        {
            byte[] values = new byte[length];
            byte[] buffer = new byte[1];
            buffer[0] = regAddr;
            _launchPadDevice.WriteRead(buffer, values);
            return values;
        }

        void WriteByte(byte regAddr, byte data)
        {
            byte[] buffer = new byte[2];
            buffer[0] = regAddr;
            buffer[1] = data;
            _launchPadDevice.Write(buffer);
        }

        void writeBytes(byte regAddr, byte[] values)
        {
            byte[] buffer = new byte[1 + values.Length];
            buffer[0] = regAddr;
            Array.Copy(values, 0, buffer, 1, values.Length);
            _launchPadDevice.Write(buffer);
        }

        #endregion

        public async void InitHardware()
        {
            try
            {
                IoController = GpioController.GetDefault();
                InterruptPin = IoController.OpenPin(INTERRUPT_PIN);
                InterruptPin.Write(GpioPinValue.Low);
                InterruptPin.SetDriveMode(GpioPinDriveMode.Input);
                InterruptPin.ValueChanged += Interrupt;

                string aqs = I2cDevice.GetDeviceSelector();
                DeviceInformationCollection collection = await DeviceInformation.FindAllAsync(aqs);

                I2cConnectionSettings settings = new I2cConnectionSettings(ADDRESS);
                settings.BusSpeed = I2cBusSpeed.FastMode; // 400kHz clock
                settings.SharingMode = I2cSharingMode.Exclusive;
                _launchPadDevice = await I2cDevice.FromIdAsync(collection[0].Id, settings);

                await Task.Delay(100); // wait power up sequence
            }
            catch (Exception ex)
            {
                string error = ex.ToString();
            }
        }

        private void Interrupt(GpioPin sender, GpioPinValueChangedEventArgs args)
        {
            if (_launchPadDevice != null)
            {
                SonarEventArgs ea = new SonarEventArgs();
                ea.Range = 0;
                ea.Yaw = 0;
                byte[] data = ReadBytes(0x5A, 3);
                int raw = (data[0] << 8) + data[1];
                double microSeconds = raw / 4;
                ea.Range = microSeconds / 5800;
                ea.Yaw = data[2] * 5.625/64;

                if(SensorInterruptEvent != null)
                {
                    SensorInterruptEvent(this, ea);
                }
            }
        }

        #region IDisposable Support
        private bool disposedValue = false; // To detect redundant calls

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                InterruptPin.Dispose();
                if (_launchPadDevice != null)
                {
                    _launchPadDevice.Dispose();
                    _launchPadDevice = null;
                }
                disposedValue = true;

            }
        }

        ~LaunchPadMsp430()
        {
            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
            Dispose(false);
        }

        // This code added to correctly implement the disposable pattern.
        public void Dispose()
        {
            // Do not change this code. Put cleanup code in Dispose(bool disposing) above.
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion

    }
}

Main for the Raspberry PI

C#
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;

// The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=402352&clcid=0x409

namespace LaunchPadMsp430
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        LaunchPadMsp430 msp430 = new LaunchPadMsp430();
        double[] value = new double[256];
        int i = 0;

        public MainPage()
        {
            this.InitializeComponent();
            msp430.InitHardware();
            msp430.SensorInterruptEvent += Msp430_SensorInterruptEvent;
        }

        private void Msp430_SensorInterruptEvent(object sender, SonarEventArgs e)
        {
            var task = Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
            {
                textBlock.Text = String.Format("stats: {0} {1}", e.Range, e.Yaw);
                value[i++] = e.Range;
                if(i >= value.Length)
                {
                    double mean = 0;
                    foreach (double v in value)
                        mean += v;
                    mean /= value.Length;

                    double sd = 0;
                    foreach (double v in value)
                        sd += (v - mean) * (v - mean);

                    sd /= value.Length;
                    sd = Math.Sqrt(sd);

                    i = 0;
                }
            });
        }
    }
}

Credits

Graham Chow

Graham Chow

14 projects • 81 followers
I'm a software developer on a long sabbatical. I've had wide experience from banking to underwater military sonar systems.

Comments