Wednesday, November 26, 2014

New Firmware for the IR Trigger

Some days ago I posted an article about a RC-1 clone infrared remote trigger for Canon SLR cameras. You can find it in this link.

The design included a quick and dirty firmware developed using Energia, an Arduino like environment for the MPS430 MCUs. This firmware was enough for testing most of the hardware but it could be improved. Mainly:

  • It didn't used the 32768 Hz watch crystal
  • It didn't used the resistors included to measure the battery voltage
  • It consumed 3.2mA in idle state so it was not efficient at all
Before talking about the code I should comment that there were a pair of errors in the schematic of the previous article. There are two changes that are indicated in blue in the following figure: The 22k resistors to measure the battery voltage were at incorrect pins. When soldering the components I changed the pins on the fly to ease the connections and I forget to update the schematic. In the same way the resistor and IR LED diode are swapped to ease, also, the connections.

IR Trigger Schematic v1.4

GCC Firmware

The new firmware was developed on the Portable MSP320 Environment I set up some days ago. The Texas Instruments sponsored Red Hat MSP430 GCC I used is a mixed bag. On one hand it provides an easy setup and an up to date selection of MCUs but on the other hand is not fully compatible with previous MSP430 code I developed for the MSP430 GCC at Sourceforge

The firmware code is distributed in three files:

This file includes some macros to ease the acces to bit fields on the MCU hardware registers.
I developed this file long time ago but I needed to do some modifications for this project due to the change of toolchain. Due to that, it is not guaranteed that all this file works in a particular toolchain.

/*********************************************
Masks for MSP430 register bit fields
(c) VJS
Version 23-11-2014
History:
28-06-2011 : First version
17-04-2012 : Some mask definition changes
14-11-2014 : Now all is in english
23-11-2014 : CLEAR_FLAG alias and ALL_BITS
**********************************************/
/*********************************************
Example for a 8 bit register REG with
1 bit field BIT
2 bit field F0,F1
REG|=BIT To set BIT
REG&=~BIT To clear BIT
For F0,F1 a mask is defined:
F01_MASK = (0xFF-3*F0)
REG&=F01_MASK To clear field
REG=(REG&F01_MASK)+F0 To set F0 and not F1
REG=(REG&F01_MASK)+F1 To set F1 and not F0
REG=(REG&F01_MASK)+F0+F1 To set F0 and F1
REF=(REG&F01_MASK)+(F0*value) To set value 0..3
***********************************************/
/*************** BASIC DEFINITIONS *****************
This definitions define the following macros:
SET_FLAG(REGISTER,FLAG) Set a 1 bit flag
RESET_FLAG(REGISTER,FLAG) Reset a 1 bit flag
CLEAR_FLAG(REGISTER,FLAG) Reset a 1 bit flag
CLEAR_FIELD(REGISTER,MASK) Clear a multibit field
SET_FIELD(REGISTER,MASK,VALUE)
Set a multibit field (value must be consistent with mask)
SET_FIELD_WOFFSET(REGISTER,MASK,VALUE,OFFSET)
Set a multibit field adding an offset
Field name references the last bit in the field, not the first one
** In the future macros can be changed to use BIS and BIC
***************************************************/
#define SET_FLAG(REGISTER,FLAG) REGISTER|=(FLAG)
#define RESET_FLAG(REGISTER,FLAG) REGISTER&=~(FLAG)
#define CLEAR_FIELD(REGISTER,MASK) REGISTER&=(MASK)
#define SET_FIELD(REGISTER,MASK,VALUE) REGISTER=(REGISTER&(MASK))+VALUE
#define SET_FIELD_WOFFSET(REGISTER,MASK,VALUE,OFFSET) REGISTER=(REGISTER&(MASK))+((VALUE)*(OFFSET))
#define GET_FIELD(REGISTER,MASK) (REGISTER&(MASK))
#define CLEAR_FLAG(REGISTER,FLAG) RESET_FLAG(REGISTER,FLAG)
#define ALL_BITS (BIT0|BIT1|BIT2|BIT3|BIT4|BIT5|BIT6|BIT7)
/**************** ADC 10 **********************/
#ifdef __MSP430_HAS_ADC10__
/* ADC10CTL0 @ 0x01B0 ADC10 Control 0 */
#define ADC10SHT_OFFS (0x800u)
#define ADC10SHT_MASK (0xFFFF-3*ADC10SHT_OFFS) /* 2 bit mask */
#define ADC10SREF_OFFS (0x2000u)
#define ADC10SREF_MASK (0xFFFF-7*ADC10SREF_OFFS) /* 3 bit mask */
/* ADC10CTL1 @ 0x01B2 ADC10 Control 1 */
#define ADC10CONSEQ_OFFS (2u)
#define ADC10CONSEQ_MASK (0xFFFF-3*ADC10CONSEQ_OFFS) /* 2 bit mask */
#define ADC10SSEL_OFFS (8u)
#define ADC10SSEL_MASL (0xFFFF-3*ADC10SSEL_OFFS) /* 2 bit mask */
#define ADC10DIV_OFFS (0x20u)
#define ADC10DIV_MASK (0xFFFF-7*ADC10DIV_OFFS) /* 3 bit mask */
#define SHS_OFFS (0x400u)
#define SHS_MASK (0xFFFF-3*SHS_OFFS) /* 2 bit mask */
#define INCH_OFFS (0x1000u)
#define INCH_MASK (0xFFFFu-0xF*INCH_OFFS) /* 4 bit mask */
#endif
/********* BASIC CLOCK MODULE ****************/
#ifdef __MSP430_HAS_BC2__
/* DCOCTL @ 0x0056 DCO Clock Frequency Control */
#define MOD_OFFS (MOD0)
#define MOD_MASK (0xFF-0x1F*MOD0) /* 5 bit mask */
#define DCO_OFFS (DCO0)
#define DCO_MASK (0xFF-7*DCO0) /* 3 bit mask */
/* BCSCTL1 @ 0x0057 Basic Clock System Control 1 */
#define RSEL_OFFS (RSEL0)
#define RSEL_MASK (0xFF-0xF*RSEL0) /* 4 bit mask */
#define DIVA_OFFS (DIVA0)
#define DIVA_MASK (0xFF-0x3*DIVA0) /* 2 bit mask */
/* BCSCTL2 @ 0x0058 Basic Clock System Control 2 */
#define DIVS_OFFS (DIVS0)
#define DIVS_MASK (0xFF-0x3*DIVS0) /* 2 bit mask */
#define DIVM_OFFS (DIVM0)
#define DIVM_MASK (0xFF-0x3*DIVM0) /* 2 bit mask */
#define SELM_OFFS (SELM0)
#define SELM_MASK (0xFF-0x3*SELM0) /* 2 bit mask */
/* BCSCTL3 @ 0x0053 Basic Clock System Control 3 */
#define XCAP_OFFS (XCAP0)
#define XCAP_MASK (~(XCAP0|XCAP1)) /* 2 bit mask */
#define LFXT1S_OFFS (LFXT1S0)
#define LFXT1S_MASK (~(LFXT1S0|LFXT1S1)) /* 2 bit mask */
#define XT2S_OFFS (XT2S0)
#define XT2S_MASK (0xFF-0x3*XT2S0) /* 2 bit mask */
#endif
/******** FLASH MEMORY *************************/
#ifdef __MSP430_HAS_FLASH2__
/* FCTL2 @ 0x012A FLASH Control 2 */
#define FN_OFFS (FN0)
#define FN_MASK (0xFF-0x3F*FN0) /* 6 bit mask */
#define FSSEL_OFFS (FSSEL0)
#define FSSEL_MASK (0xFF-0x3*FSSEL0) /* 2 bit mask */
#endif
/******* DIGITAL I/O PORTS 1/2 *****************/
/* No definitions are needed here */
/********** TIMER A2 ***************************/
#ifdef __MSP430_HAS_TA2__
#define __MSP430_HAS_TA__
#endif
#ifdef __MSP430_HAS_TA3__
#define __MSP430_HAS_TA__
#endif
#ifdef __MSP430_HAS_TA__
/* TAIV @ 0x012E Timer A Interrupt Vector Word (Read Only) */
#define IRQ_OFFS (0x0x2)
#define IRQ_MASK (0xFFFF-7*IRQ_OFFS) /* 3 bit mask */
/* Mask not normally needed as it is read only !!! */
/* TACTL @ 0x0160 Timer A Control */
#define TAMC_OFFS (0x10u)
#define TAMC_MASK (0xFFFF-3*TAMC_OFFS) /* 2 bit mask */
#define TAID_OFFS (0x40u)
#define TAID_MASK (0xFFFF-3*TAID_OFFS) /* 2 bit mask */
#define TASSEL_OFFS (0x100u)
#define TASSEL_MASK (0xFFFF-3*TASSEL_OFFS) /* 2 bit mask */
/* TACCTL0 @ 0x0162 Timer A Capture/Compare Control 0 */
#define OUTMOD_OFFS (0x20u)
#define OUTMOD_MASK (0xFFFF-0x7*OUTMOD_OFFS) /* 3 bit mask */
#define CCIS_OFFS (0x1000u)
#define CCIS_MASK (0xFFFF-0x3*CCIS_OFFS) /* 2 bit mask */
#define CM_OFFS (0x4000u)
#define CM_MASK (0xFFFF-0x3*CM_OFFS) /* 2 bit mask */
/* TACCTL1 @ 0x0164 Timer A Capture/Compare Control 1 */
/* Masks are the same as in TACCTL0 */
/* Aditional Definitions */
#define TAIV_NONE 0x00
#define TAIV_TACCR1 0x02
#define TAIV_TACCR2 0x04
#define TAIV_TAIFG 0x0A
#endif
/************** USI ****************************/
#ifdef __MSP430_HAS_USI__
/* USICKCTL @ 0x007A USI Clock Control Register */
#define USISSEL_OFFS (USISSEL0)
#define USISSEL_MASK (0xFF-7*USISSEL0) /* 3 bit mask */
#define USIDIV_OFFS (USIDIV0)
#define USIDIV_MASK (0xFF-7*USIDIV0) /* 3 bit mask */
/* USICNT @ 0x007B USI Bit Counter Register */
#define USICNT_OFFS (USICNT0)
#define USICNT_MASK (0xFF-0x1F*USICNT0) /* 5 bit mask */
#endif
/************** WATCHDOG **********************/
/* Mask cannot be used as it is protected by a password */
/************** COMPARATOR A *****************/
#ifdef __MSP430_HAS_COMPA__
/* CACTL1 @ 0x0059 Comparator A Control 1 */
#define CAREF_OFFS (CAREF0)
#define CAREF_MASK (0xFF-0x3*CAREF0) /* 2 bit mask */
/* CACTL2 @ 0x005A Comparator A Control 2 */
#define P2CA_OFFS (P2CA0)
#define P2CA_MASK (0xFF-0x3*P2CA0) /* 2 bit mask */
/* Definitions for missing signals in .h */
#define P2CA2 BIT4
#define P2CA3 BIT5
#define P2CA4 BIT6
#define CASHORT BIT7
#define SELP_MASK (0xFF-P2CA0-P2CA4)
#define SELP_NONE (0x00)
#define SELP_CA0 (P2CA0)
#define SELP_CA1 (P2CA4)
#define SELP_CA2 (P2CA0+P2CA4)
#define SELN_MASK (0xFF-7*P2CA1)
#define SELN_NONE (0x00)
#define SELN_CA1 (1*P2Ca1)
#define SELN_CA2 (2*P2Ca1)
#define SELN_CA3 (3*P2Ca1)
#define SELN_CA4 (4*P2Ca1)
#define SELN_CA5 (5*P2Ca1)
#define SELN_CA6 (6*P2Ca1)
#define SELN_CA7 (7*P2Ca1)
#endif
view raw io430masks.h hosted with ❤ by GitHub


This is the main file for the firmware. It practically contains all the code. It also includes some test code not used in the final firmware but that was left  behind.

/***************/
/* IR TRigger */
/***************/
// RC-1 Equivalent trigger for Canon EOS
// Frequency: 32700 Hz (29800 to 35500 Hz)
// Pulses/Burst: 16 (9 to 22)
// Two burst start delay: Immediate 7.82ms
// Delayed (2s) 5.86ms
// History
// 25/11/2014 First version (v1.0)
// Use low power LPM4 mode
// Energia first firmware had 3.2mA idle current
// This firmware has a 0.1uA idle current
// This is just the typical value t 25ºC reported on the datasheet
// TODO : The first burst is not always correct
// Perhaps we can go to LPM3 and then to LPM4 after several seconds
// Use IR LED TSHF5210
#include <msp430.h>
#include "io430masks.h"
// Main configuration definitions
//#define RELEASE // Release version (Don't include test functions)
// Comment it during product debugging
// Hardware definitions
// PORT 1 USAGE
#define LED BIT0 // Active High Red LED at pin 2 (P1.0)
#define SW1 BIT3 // Button between pin 5 (P1.3) and GND
#define SW2 BIT5 // Button between pin 7 (P1.5) and GND
#define IRa BIT4 // Active High IR LED at pin 6 (P1.4)
// P1.4 can be used as SMCLK output
//#define RD_H BIT7 // Upper end of resistor divider at pin 15 (P1.7)
//#define RD_M BIT6 // Middle point of resistor divider at pin 14 (P1.6) (A6)
#define RD_M BIT7 // Middle point of resistor divider at pin 15 (P1.7) (A7)
#define UNUSED_P1 ( BIT1 | BIT2 | BIT6 ) // Unused P1 lines
// PORT 2 USAGE
#define IRb BIT1 // Active High IR LED also on pin 9 (P2.1)
// P2.1 is Timer1_A Out1 output in compare mode
#define RD_H BIT3 // Upper end of resistor divider at pin 11 (P2.3)
// P2.6 and P2.7 are used by the 32768 Xtal
#define UNUSED_P2 ( BIT0 | BIT2 | BIT4 | BIT5 ) // Unused P2 lines
// PORT 3 USAGE
//
// There is no available Port3 in the 20 pin DIP package
// however we must disable port3 to obtain low current LPM4
#define UNUSED_P3 ( ALL_BITS )
/************* STATUS MACHINE DEFINITIONS *******************/
#define ST_NONE 0 // Nothing to do
#define ST_NFB 1 // Normal trigger first burst ( 16 cycles)
#define ST_ND 2 // Normal trigger delay between bursts (240 cycles)
#define ST_SB 3 // Normal/Delayed trigger second burst ( 16 cycles)
#define ST_DFB 5 // Delayed trigger first burst ( 16 cycles)
#define ST_DD 6 // Delayed trigger delay between bursts (176 cycles)
/**************** VARIABLES *********************************/
unsigned int fclk_MHz=1; // Clock frequency in MHz
// Used by the delay function
volatile int buttons=0; // Buttons to be processed
// Status variable for timer
//
// 0 : Nothing to do
//
// Immediate trigger codes
// 1 : First burst
// 2 : Wait for second burst
// 3 : Second burst
//
// Delayed trigger codes
// 5 : First burst
// 6 : Wait for second burst
// 7 : Second burst
//
volatile int status=ST_NONE;
/************** FUNCTION PROTOTYPES *************************/
int checkVdd(void);
/**************** FUNCTIONS *********************************/
// Delay of about len ms
void delay(unsigned int len)
{
if (!len) return; // Nothing to do if len=0
// Set number of cycles depending on clk frequency
len*=fclk_MHz;
do
{
// 1000 cycles delay
__delay_cycles (1000);
len--; // Decrease counter
}
while(len);
}
// Sends several blinks to the RED LED
// Parameters:
// n : Number of blinks
// length : ON, OFF Duration in ms
void doBlinks(unsigned int n,unsigned int length)
{
do
{
SET_FLAG(P1OUT,LED); // Turn on led
delay(length);
CLEAR_FLAG(P1OUT,LED); // Turn off led
delay(length);
}
while(--n);
}
/**************** TEST FUNCTIONS ***************************/
// These functions are used only during the development
// They can be eliminated in the release product
#ifndef RELEASE
// Changes blink frequency depending on SW1 and SW2
// This is a test function. Never returns
void blinkTest(void)
{
while (1)
{
SET_FLAG(P1OUT,LED); // Turn on led
if (P1IN&SW1)
delay(1000); // Time ON 1s if SW1 not pressed
else
delay(200); // Time ON 0.2s if SW1 pressed
CLEAR_FLAG(P1OUT,LED); // Turn off led
if (P1IN&SW2)
delay(1000); // Time OFF 1s if SW2 not pressed
else
delay(200); // Time OFF 0.2s if SW2 pressed
}
}
// Test outputting SMCLK at P1.4 (IRa)
// This is a test function. Never returns
void smclkTest(void)
{
// Configurations to set SMCLK
SET_FLAG(P1DIR,IRa);
SET_FLAG(P1SEL,IRa);
}
#endif /* RELEASE */
/********** CAMERA TRIGGER FUNCTIONS **********************/
// Normal camera trigger switch SW1 is pressed
// Two burst of 16 ACLK cycles
// Starts separated 7.82ms (256 cycles)
void normalTrigger(void)
{
// doBlinks(2,200); // Test that we enter OK
SET_FLAG(P1SEL,IRa); // Activate burst
SET_FLAG(TACTL,TACLR); // Clear the counter
TACCR0=16; // First interrupt set at 16 cycles
status=ST_NFB; // This is first burst of normal trigger
SET_FIELD(TACTL,TAMC_MASK,MC_2); // Set mode to Up Continuous
delay(100);
if (checkVdd())
SET_FLAG(P1OUT,LED); // Turn on led
delay(400);
CLEAR_FLAG(P1OUT,LED); // Turn off led
}
// Delayed camera trigger switch SW2 is pressed
// Two burst of 16 ACLK cycles
// Starts separated 5.86ms (192 cycles)
void delayedTrigger(void)
{
// doBlinks(4,200); // Test that we enter OK
SET_FLAG(P1OUT,LED); // Turn on led
SET_FLAG(P1SEL,IRa); // Activate burst
SET_FLAG(TACTL,TACLR); // Clear the counter
TACCR0=16; // First interrupt set at 16 cycles
status=ST_DFB; // This is first burst of delayed trigger
SET_FIELD(TACTL,TAMC_MASK,MC_2); // Set mode to Up Continuous
delay(100);
if (checkVdd())
SET_FLAG(P1OUT,LED); // Turn on led
delay(400);
CLEAR_FLAG(P1OUT,LED); // Turn off led
}
/*************** ADC FUNCTIONS ****************************/
unsigned int value;
// Reads the indicated channel on the ADC10
// Uses an internal reference of 1,5V
unsigned int readADC10(int channel)
{
unsigned int val;
//ADC10CTL0
// Activation of the ADC10 core
//SET_FLAG(ADC10CTL0,ADC10ON);
// Use default internal clock source
// Use default divider
// Enable the internal 2.5V reference
//SET_FLAG(ADC10CTL0,REFON);
// Set the maximum S/H
//SET_FIELD(ADC10CTL0,ADC10SHT_MASK,ADC10SHT_3);
// Use VR+ = VREF+ VR- = AVSS
// SET_FIELD(ADC10CTL0,ADC10SREF_MASK,SREF_1);
// More compact solution
// Default triggering with ADC10SC
// Default single channel single conversion
ADC10CTL0=ADC10ON|REFON|ADC10SHT_3|SREF_1;
// Enable the channel
ADC10AE0=(1<<channel);
// Set the input
ADC10CTL1=(((unsigned int)channel)*INCH_OFFS)|ADC10DIV_7;
delay(10);
// Set the conversion
ADC10CTL0|=(ENC|ADC10SC);
// Wait for conversion termination
while (ADC10CTL1&ADC10BUSY) {};
// ENC must be cleared before accessing
// the rest of the flags on ADC10CTL0
// Failing to do that can leave some peripherals
// on ADC activated and ruin low power in LPM4 mode
RESET_FLAG(ADC10CTL0,ENC);
// Get the converted value
val=ADC10MEM;
// Disable the channel
ADC10AE0=0;
// Disable OSC ADC core and Reference
CLEAR_FLAG(ADC10CTL0,ADC10SC|ADC10ON|REFON);
//ADC10CTL0=0;
//ADC10CTL1=0;
return val;
}
// Check the Vdd voltage
// TODO Test that it works ok
// Returns 0 if below 2.6V
// Returns 1 otherwise
int checkVdd(void)
{
// Activate the resistor divider
SET_FLAG(P2OUT,RD_H);
// Wait 10ms to settle
delay(10);
// Read ADC at center point
value=readADC10(7);
value=readADC10(7);
// Deactivate the divider
CLEAR_FLAG(P2OUT,RD_H);
// We consider the battery nearly depleted when
// the voltage falls below 2.6V
// That means 1024*(2.6/2/1.5) = 890
if (value<890) return 0;
return 1;
}
/*************** MAIN FUNCTION ****************************/
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
P1DIR |= LED | IRa; // Output mode pins
P1OUT = 0; // Guarantee that LED and IR are off
P1REN |= SW1 | SW2; // Activate pull-up/pull-down
P1OUT |= SW1 | SW2; // Set to pull-up
// Invalidate unused pins using pull-downs
P1REN |= UNUSED_P1;
CLEAR_FLAG(P1OUT,UNUSED_P1);
CLEAR_FLAG(P1DIR,UNUSED_P1);
P2REN |= UNUSED_P2;
CLEAR_FLAG(P2OUT,UNUSED_P2);
CLEAR_FLAG(P2DIR,UNUSED_P2);
P3REN |= UNUSED_P3;
CLEAR_FLAG(P3OUT,UNUSED_P3);
CLEAR_FLAG(P3DIR,UNUSED_P3);
// Set resistor divider high node
P2DIR |= RD_H;
CLEAR_FLAG(P2OUT,RD_H);
// Set 32768kHz Xtal capacitance to 12.5pF
SET_FIELD(BCSCTL3,XCAP_MASK,XCAP_3);
// Select 32768kHz LFXTCLK for SMCLK
SET_FLAG(BCSCTL2,SELS);
// Load calibrated data for 16MHz clock
DCOCTL=CALDCO_16MHZ; // DCOCTL Calibration Data for 16MHz
BCSCTL1=CALBC1_16MHZ; // BCSCTL1 Calibration Data for 16MHz
fclk_MHz=16; // Set clk data for timings
// Wait 1 second
delay(1000);
// Interrupt configuration for SW1 and SW2
P1IES |= SW1|SW2; // Sets pins SW1 and SW2 port 1 to falling edge
P1IE |= SW1|SW2; // Enables interrupts on SW1 and SW2
P1IFG=0; // Clear interrupt flags
// Configure Timer0A
SET_FIELD(TACTL,TASSEL_MASK,TASSEL_1); // Set clock to ACLK (32768Hz)
SET_FIELD(TACTL,TAID_MASK,ID_0); // Set clock divider to :1
SET_FIELD(TACTL,TAMC_MASK,MC_0); // Set mode to STOP
SET_FLAG(TACTL,TACLR); // Clear the counter
// Configure Capture block 0
CLEAR_FLAG(TACCTL0,CAP); // Mode set to compare
TACCR0=16; // First interrupt set at 16 cycles
SET_FLAG(TACCTL0,CCIE); // Enable interrupt
// Blink test
// At 25/11/2014 it works OK, we don't need it anymore
// blinkTest();
// SMCLK Test
// At 25/11/2014 it works OK, we get 32768Hz on the IR LED
// smclkTest();
// Signal the end of the configuration
doBlinks(2,200); // 2 blinks of 200ms
_enable_interrupts(); // Enable interrupts
// Infinite loop
while (1)
{
// Check if there is any SW to respond to
if (!buttons)
{
LPM4; // Go to sleep if there are no buttons
delay(200); // Little delay going out of sleep
}
if (buttons&SW1) // Normal camera trigger
{
normalTrigger();
buttons=0;
}
if (buttons&SW2) // Delayed camera trigger
{
delayedTrigger();
buttons=0;
}
};
}
/*********************** GPIO INTERRUPT **************************/
// The port 1 is associated with vectors PORT1_VECTOR
// The RSI must test the different bits
// It seems that the interrupt() macro in iomacros.h is broken
// #define __interrupt(vec) __attribute__((__interrupt__(vec)))
// We can redefine a new macro:
#define interruptNew(vec) __attribute__((interrupt(vec)))
// PORT 1 RSI
void interruptNew(PORT1_VECTOR) PORT1_ISR (void)
{
// Don't do anything if we have not processed last order
if (!buttons)
{
buttons|=P1IFG&(SW1|SW2);
// Exit LPM4 mode on exit
LPM4_EXIT;
}
// Clear the flags
P1IFG=0;
}
/************** TIMER0A COMPARE 0 INTERRUPT ********************/
void interruptNew(TIMER0_A0_VECTOR) TA0Capture0_ISR(void)
{
switch (status)
{
case ST_NONE:
// Nothing to do
break;
case ST_NFB:
// We were in the first burst
CLEAR_FLAG(P1SEL,IRa); // Deactivate burst
TACCR0+=240; // 240 cycles to next change
status=ST_ND; // Now in Delay between Bursts
break;
case ST_ND:
// We were in the space between bursts
SET_FLAG(P1SEL,IRa); // Activate burst
TACCR0+=16; // 16 cycles to next change
status=ST_SB; // Now in Second Burst
break;
case ST_SB:
// We were in the second burst
CLEAR_FLAG(P1SEL,IRa); // Deactivate burst
SET_FIELD(TACTL,TAMC_MASK,MC_0); // Set mode to STOP
status=ST_NONE; // Now in Idle mode
break;
case ST_DFB:
// We were in the first burst
CLEAR_FLAG(P1SEL,IRa); // Deactivate burst
TACCR0+=176; // 176 cycles to next change
status=ST_DD; // Now in Delay between bursts
break;
case ST_DD:
// We were in the space between bursts
SET_FLAG(P1SEL,IRa); // Activate burst
TACCR0+=16; // 16 cycles to next change
status=ST_SB; // Now in Second Burst
break;
default:
// Default case if anything goes wrong
SET_FIELD(TACTL,TAMC_MASK,MC_0); // Set mode to STOP
CLEAR_FLAG(P1SEL,IRa); // Deactivate burst
status=ST_NONE; // Go to a valid state
break;
}
}
view raw main.c hosted with ❤ by GitHub


This is the makefile that coordinates the build of all the firmware.

# IR Trigger Makefile
# Folders to include in the path
# M:\MCU\msp430_Luna\UnxUtils\usr\local\wbin
# M:\MCU\msp430_Luna\msp430-gcc\bin
NAME = IR_Trigger
OBJECTS = main.o
MCU = msp430g2553
#Compiler
GCC_DIR = ../../msp430-gcc
CC = msp430-elf-gcc
# It seems that the GCC compiler don't know that
# the selected MCU has no hardware multiplier
# so we need to indicate that in the compiler flags
# Development version
#CFLAGS = -I $(GCC_DIR)/include -mmcu=$(MCU) -mhwmult=none -O0 -g
# Release version
CFLAGS = -I $(GCC_DIR)/include -mmcu=$(MCU) -mhwmult=none -O2
#Linker
LFLAGS = -L $(GCC_DIR)/include -Wl,-Map=$(NAME).map
#Object Dump
#OD = ${GCC_DIR}/bin/msp430-elf-objdump
OD = msp430-elf-objdump
.PHONY: all clean
all: ${NAME}.elf ${NAME}.lst
${NAME}.elf: ${OBJECTS}
$(CC) $(CFLAGS) $(LFLAGS) $? -o $(NAME).elf
${NAME}.lst: ${NAME}.elf
${OD} -dSt $^ > $@
clean:
rm -f ${NAME}.elf ${NAME}.lst ${OBJECTS}
#project dependencies
main.o: main.c io430masks.h Makefile
view raw Makefile hosted with ❤ by GitHub


The code starts stopping the Watchdog and configuring the I/O ports. In particular it is very important to leave guarantee than there is no I/O floating as they will ruin the low power figures. In order to tie the unused lines to GND, the pull-downs are enabled on these lines.

Next, the 32768 Hz ACLK clock is configured and set as SMCLK. Then the main DCO clock is configured to operate at 16MHz using the Flash stored factory calibrated values.

After that, the push buttons SW1 and SW2 are configured to generate an interrupt when pressed. The configuration ends with the Timer0A that is connected to the 32768 Hz ACLK as input and associated with a capture interrupt RSI.

At the end of the initialization the red LED gives two blinks to indicate that the system is ready to operate.

After the initialization there is an infinite event loop. There are two possible events, each one associated to a function. If there is no event to process, the system enters in deep sleep LPM4 mode.

The Port 1 RSI associated to the pushbuttons generates the two possible events and gets out of LPM4 mode if needed so that the main event loop can process them.

As it was indicated there are two kinds of events. The SW1 button generates Normal Trigger Events that sends through the IR LED the signal that makes the camera shoot immediately. The SW2 button generates the Delayed Trigger Event that sends a similar signal that makes the camera shoot with a 2 seconds delay.

The signals associated to each event are composed on two 32768 Hz 16 cycles square wave bursts separated with some delay. That delay is used by the camera to identify both signals. The Normal Trigger signal has both bursts starts separated 7.82ms whereas the Delayed Trigger signal uses a 5.86ms separation.

The signals associated to those events are generated with an state machine associated to the Timer. An ISR execution associated to the Capture channel 0 of the Timer 0 provides the change from one state to the following one. The entry point to the state machine for the Normal Trigger Event is the Normal First Burst (NFB) state that connects the ACLK to the P1.4 pin during 16 cycles. After the state times out, the following Normal Delay (ND) state waits during 240 clock cycles. Then the Second Burst (SB) state outputs the second burst of 16 clock cycles to the IR LED. That ends the state machine for the Normal Trigger.
There is a sencod path in the state machine for the Delayed Trigger signal that starts with the Delayed First Burst (DFB) state. The only difference is this path is the second Delayed Delay (DD) state that is 176 cycles long instead of the 240 cycles associated to the ND state.
To provide some security against random errors, any undefined state goes to the NONE estate that stops the machine and also the associated timer.   

Timer State Machine

The use of an state machine is an efficient way to sequence the operations associated to the generation of the IR signal. At the start of the first state NFB, the SMCLK clock is sent to the P1.4 pin, the Compare register TACCR0 is set to 16 cycles in the future and the timer counter TAR is started in countinuous up mode.

Timer 0 TAR evolution and States
When the TAR counter reaches the TACCR0 value, an interrupt is generated. That changes the state from NFB to ND. The P1.4 pin is disconnected from the SMCLK clock and the TACCR0 Compare register is set 176 cycles in the future. When TAR reaches the 256 cycle mark a new interrupt is generated giving start to the SB state. A new burst is generated and TACCR0 is set 16 cycles in the future. Finally, at the 272 cycle mark, we enter in the NONE state where the burst ends and the TAR timer counter is stopped. 


Measurements

I have made some measurements to test the operation of the system. The following image shows the voltage at the IR LED diode during the first burst. We can see that there are 16 pulses.

Burst signal at the IR LED

To measure the clock frequency we can zoom in the burst pulses at the LED. We can see from it that the period is 30.6us so that the frequency is about 32680 Hz. As the start of the signal is not synchronized with the clock, the first pulse is not equal to the rest. In practice this is not a problem but, if it were, we could add another state in the machine to synchronize the burst start.

Burst pulse detail
The following figure shows the IR LED voltage for the full signal in normal non delayed camera trigger mode. We can measure a 7.81ms delay time between the start of both bursts. This is just the desired value.

Normal Trigger Full signal
In the same way we can see that the delayed mode trigger features a 5.86 ms time between the start of the two bursts. So everything is ok.

Delayed Trigger Full signal
The above signals indicate that the IR LED has a non zero offset voltage. This is true but it doesn't affect its current. As the IR LED is a diode, it features an exponential relationship between its voltage and its current so by the time its voltage falls below 1V  the current is practically zero.

To test the real current on the diode we can measure the voltage on the current limiting 6.8Ohm resistor that is in series with the LED. We can see that the peak pulse voltage goes from 560mV at the start of the burst to about 470mV at its end. That means that the peak current goes from 82mA to 69mA. We planned for 100mA current and we could lower the current limiting resistor to increase the current, but in the end the CR2032 battery that powers the system could produce system resets due to the fall in the supply during the bursts.


Voltage at 6.8 Ohm resistor


Idle current

One of the most important drawbacks in the Energia firmware was the 3.2mA idle current. Using the GCC firmware we can use the low power LPM4 mode to significantly reduce this current.
From the information provided on the MSP430G2553 we know that the LPM4 current can be as low as 0.1uA.

Low Power figures in the MSP430G2553
Going to currents as low as that is not easy. 0.1uA at 3V means an equivalent resitance of 30 MOhm. Just touching the battery terminals will give a much greater current. That means that we must take care that no current in wasted during the sleep time. As we have commented, all unused pins are tied down to GND using the integrated pull-down resistors. That includes the port 3 lines that are not used in the DIP20 package.
Using this method I obtained in practice the datasheet indicated value of 0.1uA for the typical scenario at 25ºC as you can see in the image below. 0.1uA  is so low that we don't need to power down the circuit at all as the battery will live ages with that current.

0.1uA current at LPM4 mode

Power supply measurement

The firmware also includes a measurement of the power supply after each signal sent. The ADC uses the 1.5V internal reference because the 2.5V one cannot be used below 2.8V. That means that our resistor divider used to measure the supply will get just the maximum 1023 10 bit count at 3V. The system detects the low voltage condition when the voltage falls below 2.6V, that means that the voltage at the divider is 1.3V so that the ADC gives about 890 counts.

The firmware lights the red LED during 400ms after each transmited signal. When it detects a low battery voltage condition the LED is not turned on so that the user can see that the battery is nearly depleted.


Last words


That's all for now. The code currently ocupies about 2500 bytes with no optimization and with debug enabled. Disabling the debug and turning on the compiler optimizations its size falles to about 2000 bytes. That leaves a lot of room for future firmwares improvements as the MSP430G2553 features 16KB of flash memory.
In fact, the project could be developed in a smaller MCU but given the price differences in the MSP3250G budget line at low volumes there is little to gain in costs selecting another MCU.


Gist Update (30/11/2014)

Thanks to Gist, now the code, with syntax highlighting, is now included inside the blog entry.

Code on Github (11/02/2018)

This new firmware and the old energia one are now on Github:

https://github.com/R6500/Canon-IR-trigger



Sunday, November 23, 2014

LED Light Teardown

Today I have made a tear down and reverse engineering of a cheap 12V LED light that is designed to substitute a 12V halogen lamp. Let's see what it is inside.

12V LED Light
The LED light is composed of 21 small white LEDs. The package says it's 1W. Applying 12V we I get 87mA so it is trully 1W. But what efficiency we get on the circuit?


The insides

After breaking the enclosure we get the lamp internals.

Lamp internals
The lamp is internally composed on two boards. One round board holds the 21 LEDs and a smaller octagonal board holds the control circuit.

LED Board




The LED board is painted white at the LED side to give a better reflectance of stray light.
Before continuing the tear down, I liked to check the curren on one of the LEDs. To do that, I disordered a LED and re soldered only one terminal to put a multimeter in series.

Current meas setup
Measuring the LED current when operating it gives 10.5mA. Those are quite simple LEDs. More modern LED lamps use high current devices. That's why it needs 21 LEDs to get 1W of power.

Continuing the tear down, I have separated the two boards and removed all LEDs from the led board. The front side has markings so we can see where should be the anode and cathode of each led.

LED board: Front

The back of the led board holds all the connections. From them we can see that the 21 LEDs are arranged in seven parallel groups of 3 LEDs in series. As one white LED can have a forward current of up to 3.5V, we cannot use more than three LEDs in series if we use a linear control circuit. We also don't want to put less than three LEDs because that will give less efficiency.


LED board: Back
As a result of the tear down we get also a bunch of leds.


Bunch of LEDs
 


Reverse engineering of the circuit

The circuit used to power the LEDs is shown in the next figure.

Control Circuit
There are two input cables for the AC 12V input source and two output nodes (+)  and (-) to connect the seven stripes of three LEDs.
To get the circuit schematic I have taken a photo of the back of the board. I have mirrored it to have the same positions as the component side and I have written down all the components present on the board.

Component distribution on the board


The equivalent schematic of the board follows:

Circuit schematic
Nothing fancy. Just a linear current control regulator.
The four diodes take the 12V AC input source and rectify it to get, with the help of the capacitor E1, a constant voltage between Vdd and GND. 
The diodes have a marking RS1M. Those seems to be standard 1A silicon diodes. The voltage at 87mA should be around 0.7V. As we have two voltage drops, that gives a Vdd-Vss voltage of about 10.6V.
The LEDs are connected to the collector of the big transistor Q1 (D882). The base current of Q1 is fed by the resistor R2. 
The base of Q1 is at two Vbe drops over Vss, so, about 1.2V. That gives a maximum base current of  3.1mA. That means that we can drive the 87mA current of the 21 LEDs with a minimum forward beta (current gain) of  28.
We need, of course a way to limit the current at the LEDs. That's the mission of Q2.
The collector current in Q1 is similar to the emitter current that goes to R1. As the base-emitter junction of Q2 is connected to this resistor, when it reaches about 0.6V, Q2 will turn on and drain the base current of Q1. The equilibrium point of the circuit is where Q2 is just starting to turn on and the current at Q1 will be at about 0.6V/R1 that gives 80mA. This theoretical result is quite similar to the measured current of 87mA.

I have measured one of the LEDs and it gives about 3V at 10mA. For a series of three LEDs we get 9V. The voltage at R1 is 0.6V. That gives a voltage drop of about 10.6V - 9V - 0.6V = 1V between the collector and emitter of Q1. Enough room for voltage drop changes on the diodes as function of the temperature.

If we consider that we get a total voltage of 9V on the LED strips from a source voltage of 12V, that means that the circuit efficiency cannot be better than 75%. It could be better using a DC/DC voltage to current converter instead of using linear circuit but what can you expect from a 3€ LED lamp?

Tuesday, November 18, 2014

Portable environment for the MSP430 Launchpad





I love portable environments. I like programming using an external hard drive, moving the HDD from one PC to another, and continue programming using the same tools and data.

Here I explain the method I use to create a Windows portable Eclipse based development environment for the MSP340 family using the Launchpad as the programming/debugging link.

MSP430 Launchpad

As a compiler I will use MSP430 GCC. For every element in the toolchain I try to use as much open software tools as possible but I will also try to keep things simple so sometimes it will be a compromise.

This is a deep rewrite of the portable development environment for the MSP430 Launchpad board  I set up in 2012. I then wrote an article in my spanish blog AIM 65 and, since then, some things have changed. So, here I am repeating all the steps I made before. Some things are the same but most are quite different.

This procedure works in Windows 7 and it should work also in Windows XP. Don't know about other operating systems.

Basics


Unfortunately Eclipse is not fully portable between machines. Some paths in the projects are absolute, not relative. That means that if you want to move your Eclipse development environment from one machine to another you have to guarantee that the files have the same location in both places. The easiest way to do that is changing the drive letter in windows so that it is the same in any machine. In my case, my moving HDD uses the letter M, so its M: on all systems.

Check your operating system for the method to assign the external drive letters. It usually involves using the Disk Manager, selecting the drive and selecting one option with the right mouse button contextual menu.

Eclipse is a java application. That means that you need to have java installed in any computer where you want to use the development environment.

There are several free toolchains available for the MSP430 devices. Texas Instruments provide a free version of the Code Composer Studio. As they state:

"A free license will be generated that supports a 16KB code size limit for the optimized TI compiler or no code size limit with GCC."

You are limited to 16KB but as no MSP4340 value line MCU has more memory that that it's no big deal. Moreover, you can always USE GCC and not be constrained at all.

If you want to have an easy install of the tools needed to develop on the MSP430 value line of MCUs, nothing is easier that downloading and installing CCS.

This application is not, however, the target of this article.

One of the complex elements in the development environment is the debug chain. To successfully debug successfully a MSP430 device you usually need:

  •     USB drivers for the board of FET used
  •     DLLs needed to communicate with the USB drivers
  •     GDB Proxy to communicate with a GDB program
  •     GDB program

The GDB program is usually included with the compiler but the rest of the elements sometimes have to be found in different places.

Fortunately nowadays the USB drivers are detected and installed automatically and Texas Instruments provides a file that provides the rest of elements together with the GCC compiler.


Downloading the files


First, we will start downloading the required files.

Go to  the Eclipse donwload web page http://www.eclipse.org/downloads/ and download the required installer. At the time of writing this article it was Eclipse Luna.

Eclipse Luna Download
As I'm using Win7 32 bit I selected the 32 bit installer.

Then we need to download the make and other Unix-like companion commands from this link:


You should get a UnxUtils.zip file.

Finally you should get the Red Hat msp430 gcc compiler from Texas Instruments using this link:



MSP430 GCC at Texas Instruments

You could use the MSPGCC from the sourceforge location. The problem is that it doesn't include all the needed debug elements.

Second Step downloading MSP430 GCC

That will lead to a page were you need to use a TI account to download the software. It's free, but it could not suit everyone.
At the end you should get a file with a name like msp430-gcc-full-windows-installer-2.1.1.0.exe.

That are all the files needed to download. You should have something like this list:

  • eclipse-cpp-luna-SR1-win32.zip
  • UnxUtils.zip
  • msp430-gcc-full-windows-installer-2.1.1.0.exe

Creating and filling the directories

I locate all the microcontroller files inside a MCU folder insider M:. You can  do as you please but you'll need to change the directories.

Create the main directory, in my case, as I use Eclipse Luna it will be:

M:\MCU\msp430_Luna

Open the eclipse zip file and copy the eclise folder it contains over the newly created directory.
The main executable of all the environment will be M:\MCU\msp430_Luna\eclipse\eclipse.exe. Feel free to put a link to this file somewhere that is easy to reach like your desktop or your main folder in the external drive.

Create te following four directories inside M:\MCU\msp430_Luna:

  • UnxUtils
  • msp430-gcc
  • workspace
  • projects
Copy the contents of UnxUtils.zip inside the UnxItils directory.
Finally execute the msp430-gcc-full-windows-installer-2.1.1.0.exe GCC installer, accept the license and instruct it to put all the files inside the msp430-gcc folder.



MSP430 GCC Install
 So far we have filled the eclipse, UnxUtils and msp430-gcc folders and we have empty the workspace and projects folders.


Configuring Eclipse


Now it's time to run and configure eclipse. Run the eclipse.exe program located inside M:\MCU\msp430_Luna\eclipse (directly or using a previously created link).
You will see a window like the one shown that prompts for a workspace folder.

Eclipse workspace selection
Select M:\MCU\msp430_Luna\workspace and hit OK.

After some initialization you will see a window like that:

Eclipse Start Window
Before continuing we must install the needed debug elements inside eclipse. To do that, select "Install New Software" from the Help menu.

Install new software
Select "All Available Sites" as "Work with" option and write down "GDB Hardware Debugging" as is shown in the next window. It can take a while to do the search.

GDB Support Installation
Select the C/C++ GDB Hardware Debugging item and click Next.
Hit Next in the following window, accept the License in the next one and click Finish.

After the install it will prompt to restart, let it do it and use the same workspace when it restarts.

Restarting Eclipse
You will get the same start screen. Here click over the workbench icon to continue.

Workbench ICON
That will give the standard Eclipse C/C++ view:

Eclipse C/C++ view


Compiling a Blink example

Next step is to compile a simple example. In embedded systems a LED blink program is the equivalent of a hello world program in a normal console C program.
The msp430 GCC distribution comes with an example.
I don't like to compile the program in its provided location, so I suggest to copy it to the projects folder, that means copying the folder:

M:\MCU\msp430_Luna\msp430-gcc\examples\windows\msp430g2553

To the projects folder location:

M:\MCU\msp430_Luna\projects

And rename it from msp430g2553 to blink

That way you should have the following folder contents:

Blink project contents
Then we will include this project in Eclipse using the New "Makefile Project with Existing Code" option inside the File menu.

Creation of a Makefile Project

That will give a dialog window were you can use the browse button to locate the project directory. Eclipse will give it the "blink" name. You can leave as is or change it.

Creation of new project dialog
After clicking on "Finish" the project will be created and you return to the C/C++ view.
Then, you can use the project explorer to view the contents of the Makefile.

Original project Makefile
As we have moved the make file from its original location we need to change the paths.
You can use relative or absolute paths, using relative paths you can set:

GCC_DIR =  ../../msp430-gcc/bin
SUPPORT_FILE_DIRECTORY = $(GCC_DIR)/../include

Then we can change the -O2 optimization option to -O0 so that the compiler doesn't optimize the code. This is needed if we want to do step by step debugging as the compiler optimizations break the relation between written and executed code. After all debugging is done its best to replace -O0 with -O2 to optimize the program so that the memory is reduced and the program speed is increased.

Finally, we can change the name of the output file to do that, we will create a new variable called "EXECUTABLE" and use it in the GCC reference.
As we won't use the GDC target in the makefile and we could era its last two lines.
That will give the following Makefile.
Makefile modifications
This is the final Makefile after all modifications:

OBJECTS=blink.o
GCC_DIR = ../../msp430-gcc/bin
SUPPORT_FILE_DIRECTORY = $(GCC_DIR)/../include
EXECUTABLE = blink
DEVICE = msp430g2553
CC = $(GCC_DIR)/msp430-elf-gcc
GDB = $(GCC_DIR)/msp430-elf-gdb
CFLAGS = -I $(SUPPORT_FILE_DIRECTORY) -mmcu=$(DEVICE) -O0 -g
LFLAGS = -L $(SUPPORT_FILE_DIRECTORY)
all: ${OBJECTS}
$(CC) $(CFLAGS) $(LFLAGS) $? -o $(EXECUTABLE)
view raw Makefile hosted with ❤ by GitHub

 
Observe that the makefile is designed to use a MSP430G2553 MCU, this is the MCU mounted on the MSP430 Launchpad version 1.5 board. If you want to compile for another MCU you should change this line.

After all modifications are made you can save them using the save button.
You can also examine the main blink.c source file:

blink.c source file
 You will see that there are plenty of errors. As this is not an eclipse managed project, the error checker don't know how to process the includes needed to provide several literal definitions. As those errors are quite ugly we can remove them. To do that, select the Project properties on the contextual menu on the blink folder of the Project Explorer. And then select Code Analysis and unselect the 5 elements than end in "..resolved". Then hit OK.

Code Analysis
That will get us rid of those nasty error messages.
 
The last step to obtain an executable is to compile the project. Just select Build Project in the Project menu.

Building the project
 If all goes well you will see something like that in the console:

Console after compilation
You can now see in the project explorer that you have two new files in the blink directory . The blink.o object file obtained from the compilation of blink.c and the blink (with no extension) executable file.

Optionally we can create a command to see the memory usage of the program.
To do that, select External tools configuration in the contextual menu in the tools icon.

External tools
Select program and click the New Launch Configuration button. And fill the data indicated below:

Name: Resource Size
Location: M:/MCU/msp430_Luna/msp430-gcc/bin/msp430-elf-size.exe
Working Directory: ${container_loc}
Arguments: ${resource_loc}

Resource size main tab
In the build tab unselect Build before launch.

In the Common tab select External Tools in the Display in favorites menu section.

After that, just hit Apply and then Close.

To see, for instance, the size of the executable binary file we can select it in the Project explorer and select the Resource Size element in the External tools menu.

Resource size of the main binary file
Here we can see that the binary file includes 708 bytes of code for a total size of 746 bytes. As the MSP430G2553 features 16KB of flash memory we have plenty of space left.


Program Run and Debug

To program and run the program on the MSP430G2553 MCU included in the Launchpad board we need o use several software elements.

First we need the USB drivers for the launchpad board. They will normally be installed automatically when you connect the board to the computer, but if you need to manually locate them, there are some drivers in this location inside the GCC folder:

M:\MCU\msp430_Luna\msp430-gcc\emulation\drivers\msp430

Alternatively you can download the energia drivers from:


Then we need to start the GDB Agent that will communicate the gdb program with the USB drivers. I was not able to properly use the command line program gdb_agent_console.exe without debugging errors so I used the GUI version gdb_agent_gui.exe file instead.
You can run this program directly but I prefer to run it from eclipse. To do that you can create a new external tool configuration:

Name: GDB Agent GUI
Main TAB:
  Location: M:\MCU\msp430_Luna\msp430-gcc\bin\gdb_agent_gui.exe
  Working Directory: M:\MCU\msp430_Luna\msp430-gcc
Common TAB:
  Select tick in External Tools

Then click Apply and then close. You have now the GDB Agent GUI program next to the Resource Size one. Launch the GDB Agent GUI and then click on Configure and select msp430.dat. After that click on the Start button.
If all goes well you will see a "Waiting for client" message like in the following window:

GDB Agent
After the GDB agent is successfully running we need to create a debug configuration. Use the contextual menu on the debug icon to select Debug Configurations...

Select Debug Configurations
Selecting GDB Hardware Debugging, click on the New Launch Configuration button and fill the following data:

Name: Blink
Main TAB:
  C/C++ Application: blink
  Project: blink (Select it from the browse button)

Debug configuration Main tab
Then click on Select other... at the bottom of the window and select in the new window Use configuration specific settings and Legacy GDB Hardware Debugging Launcher. Then click OK.

Selecting the debugger launcher

Next continue with the other tabs:

Debugger TAB:
  GDB Command: M:\MCU\msp430_Luna\msp430-gcc\bin\msp430-elf-gdb.exe
  Port number: 55000



Debugger tab

Startup TAB:
  Set breakpoint at: main
  Resume

Startup tab

Common TAB:
  Set tick in Display in favorites menu "Debug"

After that, Apply all changes and then Close.

Now you have a new element in the debug icon contextual menu. Check that the GDB Agent is still running and select the Blink option in the debug icon contextual menu.

Launch the debugger
You will see message recommending to change the perspective to the debug one. Just say yes.


The Debug View will open and you will see something like the following window.

Debug View
We see that the program is stopped at the start of the main program. To continue running the program just push the play button.

You should see the red LED blinking on the Launchpad board. Congratulations!!!

You can now use the pause button to pause program execution. To continue, just push the play button another time. You can also see the variables contents and use breakpoints but all that goes out of the scope of this long article.

To end the debugging you can hit the Terminate red square icon and remove the terminate debugin session selecting it and hitting the DEL key.

Terminate the debugging session
After all debug is done, the GDB Agent can also be terminated selecting it and pushing the red square terminate button. After that, the DEL key will eliminate it from the debug view.

Closing Words

After all this work we have a blink project and a MSP430 portable development environment to be used with it. To create a new project you can duplicate the blink entry on the projects folder and repeat the needed steps to incorporate the project in eclipse.

You don't need to repeat the creation of the items in the run programs menu, but you must replicate the debug entry for each new project. To do that, the easiest way is have the blink and the new projects open at the same time and duplicate the debug entry in the debug configurations window.

Duplicating a debug configuration
That's all for now. The eclipse environment is so complex that there is no more space in this article to talk about it. I will probably write more on the subject in future articles.


Compiler woes   (update at  November, 25  2014)

It seems that the TI sponsored msp430 gcc compiler is not able to determine how to do multiplications on the MSP430G2553. I got strange results in multiplications and I suspect that the compiler is trying to use a hardware multiplier that this MCU lacks.

I needed to do some guesswork to find the needed  -mhwmult compiler option as I could not find where it is documented. Finally I know, by asking the gcc compiler, that there are 5 available options: 16bit, 32bit, auto, f5series and none.

In order to correctly compile the code I had to include the compiler option:

 -mhwmult=none

That way multiplications go as they should.

The interrup macro at iomacros.h is also broken. It is defined:

#define __interrupt(vec) __attribute__((__interrupt__(vec)))

but msp430-gcc don't like this format. I have developed a new definition:

#define interruptNew(vec) __attribute__((interrupt(vec)))

That way I can properly compile, for instance, the port 1 RSI:

void interruptNew(PORT1_VECTOR) PORT1_ISR (void)
 {

 // Do RSI things
 }


Gist Embedded Makefile (Update at 30/11/2014)

Now the Makefile code is embedded in the blog using Gist