Skip to content

Instantly share code, notes, and snippets.

@RickKimball
Created June 29, 2012 19:48
Show Gist options
  • Save RickKimball/3020240 to your computer and use it in GitHub Desktop.
Save RickKimball/3020240 to your computer and use it in GitHub Desktop.
msp430 based avr910 AVR ISP programmer
/*
*
* File : 910.c
* Author : Minifloat
* Project: Launchprog
* Version: 1.2 on 10 May 2012
*
*/
#include <msp430.h>
#include <stdint.h>
#include "devcodes.h"
#include "910.h"
#include "serial.h"
inline void usdelay(void)
{
__delay_cycles((DELAYTIME));
}
inline void delay(uint8_t delay)
{
uint16_t i;
while (delay--)
{
i = 64 * DELAYTIME;
while (i--)
asm(";");
}
}
inline void pulse_sck(void)
{
usdelay();
set_sck();
usdelay();
clr_sck();
usdelay();
}
uint8_t uart_getc(void)
{
return getchar();
}
inline void uart_putc(uint8_t data)
{
putchar(data);
}
void uart_puts(register const uint8_t *data)
{
while (*data)
{
uart_putc(*data);
data++;
}
}
uint8_t wrser(uint8_t send)
{
uint8_t rcv = 0x00;
uint8_t mask;
for (mask = 0x80; mask != 0x00; mask /= 2)
{
if(send & mask)
{
set_mosi();
}
else
{
clr_mosi();
}
if(rd_miso())
rcv |= mask;
pulse_sck();
}
return(rcv);
}
inline void spiinit(uint8_t device)
{
uint8_t count;
set_rst();
clr_sck();
port_get();
delay(0xff);
clr_rst();
delay(0xff);
wrser(0xac);
wrser(0x53);
//only these devices show the sync fail?!
if ((device >= 20) && (device <= 0x7F))
{
count = 32;
do
{
if (rdser() == 0x53)
break;
wrser(0x00);
pulse_sck();
wrser(0xac);
wrser(0x53);
}
while (--count);
}
else
{
wrser(0x00);
}
wrser(0x00);
delay(0x10);
}
inline void show_id(void)
{
const static uint8_t id[] = "AVR ISP";
uart_puts(id);
}
inline void init910(void)
{
set_rst();
led_init();
ledr_off();
ledg_off();
port_release();
}
inline void waitcmd(void)
{
// device specific stuff
uint8_t device = 0, pgm_mode = 0, dev_ok = 1;
// switch command var
uint8_t sw;
// counting or GP variables
uint8_t i, j, k, l;
// r/w address
uint16_t addr = 0;
// the outer while loop
while (1)
{
//ready for some action
ledg_on();
dev_ok = 1;
// 0x1b is 'ESC'
while ((sw = uart_getc()) == 0x1b)
;
//busy becaus of some action
ledg_off();
//commands taken from Serasidis' AVR910
//commands from other sources are marked separately
switch (sw)
{
case 'T': //device type
device = uart_getc();
//dev_ok = 0;
//look up device in list
//i do this as the device is selected
//so i do not have to prove everytime
//a command after 'P' is executed
//as in Serasidis' original AVR910 v3.3
for (i = 0; device_codes[i][0] != 0xFF ; i++)
{
if(device == device_codes[i][0])
{
pgm_mode = device_codes[i][1];
dev_ok = 1;
break;
}
else
{
dev_ok = 0;
}
}
if (dev_ok)
{
// found selected dev in list
uart_put_ret();
}
else
{
// didnt find device in list
uart_put_err();
}
break;
case 'S': //Return software id
show_id();
break;
case 'V': //Return SW version
uart_putc((uint8_t)SW_MAJOR);
uart_putc((uint8_t)SW_MINOR);
break;
case 'v': //Return HW version
uart_putc((uint8_t)HW_MAJOR);
uart_putc((uint8_t)HW_MINOR);
break;
case 't': //show supported devices
for (i = 0; device_codes[i][0] != 0xFF ; i++)
{
uart_putc(device_codes[i][0]);
}
uart_putc(0);
break;
case 'p': //Return programmer type
uart_putc((uint8_t)'S');
break;
case 'a': //Return address auto-increment
uart_putc((uint8_t)'Y');
break;
case 'b':
//Return Blockmode capability(from Leidingers AVR910 v3.8)
//this device is not capable of block mode(no buffer avail)
//avrdude needs this info, otherwise the dude
//crashes with report "programmer not answering"
uart_putc((uint8_t)'N');
break;
case 'x': //set LED ignored
uart_getc();
uart_put_ret();
break;
case 'y': //clear LED ignored
uart_getc();
uart_put_ret();
break;
default:
break;
}
//without a valid device
//commands below this won't work
if (!dev_ok)
{
// no or no valid device was selected
// or errorneous opcode was given
uart_put_err();
// continues the outer while loop
continue;
}
else
{
// valid device was selected
switch (sw)
{
case 'P': //enter programming mode
spiinit(device);
uart_put_ret();
ledr_on();
break;
case 'C': //write program memory hi-Byte
i = uart_getc();
wrser(0x48);
wrser((uint8_t) (addr >> 8)); //addr.hi
wrser((uint8_t) (addr & 0xFF)); //addr.lo
wrser(i);
addr++;
if (!pgm_mode)
delay(0x20);
uart_put_ret();
break;
case 'c': //write program memory lo-Byte
i = uart_getc();
wrser(0x40);
wrser((uint8_t) (addr >> 8)); //addr.hi
wrser((uint8_t) (addr & 0xFF)); //addr.lo
wrser(i);
if (!pgm_mode)
delay(0x20);
uart_put_ret();
break;
case 'R': //read program memory
wrser(0x28);
wrser((uint8_t) (addr >> 8)); //addr.hi
wrser((uint8_t) (addr & 0xFF)); //addr.lo
uart_putc(rdser());
wrser(0x20);
wrser((uint8_t) (addr >> 8)); //addr.hi
wrser((uint8_t) (addr & 0xFF)); //addr.lo
uart_putc(rdser());
addr++;
break;
case 'A': //load address
addr = 256 * uart_getc(); //read addr.hi
addr += uart_getc(); //read addr.lo
uart_put_ret();
break;
case 'D': //write data memory
i = uart_getc();
wrser(0xc0);
wrser((uint8_t) (addr >> 8)); //addr.hi
wrser((uint8_t) (addr & 0xFF)); //addr.lo
wrser(i);
delay(0x20);
addr++;
uart_put_ret();
break;
case 'd': //read data memory
wrser(0xa0);
wrser((uint8_t) (addr >> 8)); //addr.hi
wrser((uint8_t) (addr & 0xFF)); //addr.lo
uart_putc(rdser());
addr++;
break;
case 'L': //leave programming mode
port_release();
set_rst();
ledr_off();
uart_put_ret();
break;
case 'e': //chip erase
wrser(0xac);
wrser(0x80);
wrser(0x04);
wrser(0x00);
delay(0x30);
uart_put_ret();
break;
case 'l' : //write lock bits
i = uart_getc();
wrser(0xac);
wrser((i & 0x06) | 0xe0);
wrser(0x00);
wrser(0x00);
delay(0x30);
uart_put_ret();
break;
case 's': //read signature bytes
for(i = 0x02; i != 0xFF; i--)
{
wrser(0x30);
wrser(0x00);
wrser(i);
uart_putc(rdser());
}
break;
case 'm': //write program memory page
wrser(0x4c);
wrser((uint8_t) (addr >> 8)); //addr.hi
wrser((uint8_t) (addr & 0xFF)); //addr.lo
wrser(0x00);
delay(0xFF);
uart_put_ret();
break;
case ':': //universal command ":"
case '.': //universal command "."
i = uart_getc();
j = uart_getc();
k = uart_getc();
l = (sw == ((uint8_t)':'))?0:uart_getc();
wrser(i);
wrser(j);
wrser(k);
uart_putc(wrser(l));
delay(0xFF);
uart_put_ret();
break;
default:
//or errorneous opcode was given
//do we need this?
//uart_putc('?');
break;
}
}
}
}
/*
*
* File : 910.h
* Author : Minifloat
* Project: Launchprog
* Version: 1.2 on 10 May 2012
*
*/
#ifndef HEADER910
#define HEADER910
//version info
#define SW_MAJOR '3'
#define SW_MINOR '3'
#define HW_MAJOR '1'
#define HW_MINOR '0'
//delay val
#define DELAYTIME (F_CPU/1000000)
//portpin access macros
// LEDR = P1.0 the red led
// ~RESET = P1.3 there's also a button S2 to GND.
// MOSI = P1.4
// MISO = P1.5
// LEDG = P1.6 the green led
// SCK = P1.7
#define LEDRPIN BIT0
#define RESETPIN BIT3
#define MOSIPIN BIT4
#define MISOPIN BIT5
#define LEDGPIN BIT6
#define SCKPIN BIT7
#define set_rst() P1OUT|=RESETPIN
#define clr_rst() P1OUT&=~RESETPIN
#define ledr_on() P1OUT|=LEDRPIN
#define ledr_off() P1OUT&=~LEDRPIN
#define ledg_on() P1OUT|=LEDGPIN
#define ledg_off() P1OUT&=~LEDGPIN
#define led_init() P1DIR|=LEDGPIN+LEDRPIN
#define port_get() P1DIR|=MOSIPIN+RESETPIN+SCKPIN
#define port_release() P1DIR&=~(MOSIPIN+RESETPIN+SCKPIN+MISOPIN)
#define set_sck() P1OUT|=SCKPIN
#define clr_sck() P1OUT&=~SCKPIN
#define set_mosi() P1OUT|=MOSIPIN
#define clr_mosi() P1OUT&=~MOSIPIN
#define rd_miso() (P1IN&MISOPIN)
//global variables
//delay func
inline void usdelay(void);
inline void delay(uint8_t dly);
//pulse sck one time
inline void pulse_sck(void);
//uart functions compatibility
uint8_t uart_getc(void);
void uart_putc(uint8_t data);
void uart_puts(const uint8_t *data);
//send CR
#define uart_put_ret() uart_putc(0x0d)
//send '?'
#define uart_put_err() uart_putc(0x3f)
//write read soft spi
uint8_t wrser(uint8_t send);
//read soft spi
#define rdser() wrser(0)
//enter programming mode
inline void spiinit(uint8_t device);
//show "AVR ISP" on serial line
inline void show_id(void);
//RESET marker of 910
inline void init910(void);
//wait for a command
inline void waitcmd(void);
#endif //HEADER910
/*
*
* File : devcodes.h
* Author : Minifloat
* Project: Launchprog
* Version: 1.2 on 10 May 2012
*
*/
//These Device codes are directly taken from Serasidis' AVR910
//i know i shouldn't put this in a header file only
//but now its more like using a asm .inc file
//Device codes and program mode table (0(zero)=byte mode, <>0=page mode)
//i.e. 'P' can be used for page mode for easier reading.
#ifndef DEVCODES_H
#define DEVCODES_H
#include <stdint.h>
static const uint8_t device_codes[][2] __attribute__((section(".infomem"))) = {
//AT90S1200 rev. A //From 0x00-0x0f unused yet
{0x10, 0},
//AT90S1200 rev. B
{0x11, 0},
//AT90S1200 rev. C
{0x12, 0},
//AT90S1200
{0x13, 0}, //From 0x14-0x1f unused yet
//AT90S2313
{0x20, 0}, //From 0x21-0x27 unused yet
//AT90S4414
{0x28, 0}, //From 0x29-0x2f unused yet
//AT90S4433
{0x30, 0}, //From 0x31-0x33 unused yet
//AT90S2333
{0x34, 0}, //From 0x35-0x37 unused yet
//AT90S8515
{0x38, 0}, //0x39 unused yet
//ATmega8515
{0x3A, (uint8_t)'P'},
//ATmega8515 BOOT
{0x3B, (uint8_t)'P'}, //From 0x3c-0x40 unused yet
//ATmega103
{0x41, (uint8_t)'P'},
//ATmega603
{0x42, (uint8_t)'P'},
//ATmega128
{0x43, (uint8_t)'P'},
//ATmega128 BOOT
{0x44, (uint8_t)'P'},
//ATmega64
{0x45, (uint8_t)'P'}, // v1.40
//ATmega64 BOOT
{0x46, (uint8_t)'P'}, // v1.40 0x47 unused yet
//AT90S2323
{0x48, 0}, //From 0x49-0x4b unused yet
//AT90S2343
{0x4C, 0}, //From 0x4d-0x4f unused yet
//0x50,0x51 used. From 0x52-0x54 unused yet
//
//ATtiny12
{0x55, 0},
//ATtiny15
{0x56, 0}, //0x57 unused yet
//ATtiny19
{0x58, 0}, //From 0x59-0x5b unused yet
//ATtiny28
{0x5C, 0}, //0x5d unused yet
//ATtiny26
{0x5E, (uint8_t)'P'}, //0x5f unused yet
//
//ATmega161
{0x60, (uint8_t)'P'},
//ATmega161 BOOT
{0x61, (uint8_t)'P'}, //0x62-0x63 unused yet
//ATmega163
{0x64, (uint8_t)'P'},
//ATmega83
{0x65, (uint8_t)'P'},
//ATmega163 BOOT
{0x66, (uint8_t)'P'},
//ATmega83 BOOT
{0x67, (uint8_t)'P'},
//AT90S8535
{0x68, 0},
//ATmega8535
{0x69, (uint8_t)'P'}, // v1.40 From 0x6a-0x6b unused yet
//ATmega8535 BOOT??
// {0x6a, (uint8_t)'P'},
//
//AT90S4434
{0x6C, 0}, //From 0x6d-0x6f unused yet
//AT90C8534
{0x70, 0},
//AT90C8544
{0x71, 0},
//ATmega32
{0x72, (uint8_t)'P'},
//ATmega32 BOOT
{0x73, (uint8_t)'P'},
//ATmega16
{0x74, (uint8_t)'P'},
//ATmega16 BOOT
{0x75, (uint8_t)'P'},
//ATmega8
{0x76, (uint8_t)'P'},
//ATmega8 BOOT
{0x77, (uint8_t)'P'},
//ATmega169
{0x78, (uint8_t)'P'}, // v1.40
//ATmega169 BOOT
{0x79, (uint8_t)'P'}, // v1.40 From 0x7a-0x7f unused yet
//test
// {0x08, (uint8_t)'P'},
// {0x09, (uint8_t)'P'},
// {0x0a, (uint8_t)'P'},
// {0x0b, (uint8_t)'P'},
//These devices are not supported by this hardware
//ATtiny10
// {0x51, 0},
//ATtiny11
// {0x50, 0},
//AT89C1051
// {0x80, 0},
//AT89C2051
// {0x81, 0}, //From 0x82-0x85 unused yet
//AT89S8252
// {0x86, 0},
//AT89S53
// {0x87, 0}, //From 0x88-0x9f unused yet
//end_of_device_codes:
//.dw 0xffff
{0xFF, 0xFF}
//@@
};
#endif //DEVCODES_H
/**
* main.c - Launchpad compatible full-duplex software UART example program.
*
* This example program implements an async serial echo program.
* To test it, use a terminal program such as putty or
* hyperterminal, connect to the COM port associated with your
* launchpad device, and type away. Whatever you type will be
* echoed back to you. The default settings are 9600-8-N-1
* as defined in the config.h file.
*
* The code illustrates how to utilize the full-duplex interrupt
* driven software UART routines from softserial.c
*
* CPU speed and baud rate are set in config.h. The software
* might drive the TX and RX signals up to 230400 baud
* depending on the accuracy of your SMCLK. To get a baud rate
* greater than 9600 you will have to use something like
* an FT232RL device and a fast CPU speed. Using the integrated
* COM port of the launchpad, the baud rate is limited to 9600
* as stated in the launchpad user guide.
*
* This software monopolizes the TIMERA0_VECTOR and TIMERA1_VECTOR
* interrupts. This code assumes you are using a TI Launchpad
* with an msp430g2231 in the socket, and the external watch
* crystal soldered on the board. However it should work with
* any mps430 device you can put in the launchpad socket. It
* uses about 800 bytes of flash.
*
* This software is s mismash of various chunks of code
* available on the net, with my own special seasoning. Mostly
* inspired by Appnote sla307a, the arduino HardwareSerial.cpp
* source, and various postings on the TI e2e forum.
*
* License: Do with this code what you want. However, don't blame
* me if you connect it to a heart pump and it stops. This source
* is provided as is with no warranties. It probably has bugs!!
* You have been warned!
*
* Author: Rick Kimball
* email: rick@kimballsoftware.com
* Version: 1.00 Initial version 04-20-2011
* Version: 1.01 cleanup 04-21-2011
*
* Author: Minifloat
* Version: fork Launchprog 1.2 on 10 May 2012
*
* Author: Rick Kimball
* Version: 1.2a - replaced async serial routines with blocking ones, use
* DCO @ precalibrated 1MHz instead of 32.768kHz xtal.
* Located the device code table in .infomem flash. This frees
* up more space in the main flash for code. Created a define
* "OUTPUT_CLOCKS" to make it easier to measure the clock speed
* in setup() with a frequency counter so no xtal is required.
* Modified delays so they are based on the F_CPU frequency.
*/
/* comment minifloat:
* some "corrections" on header stuff
*/
#include <msp430.h>
#include <stdint.h>
#include "serial.h"
#include "910.h"
#define _enable_interrupts _EINT
#define _disable_interrupts _DINT
/**
* setup() - initialize timers and clocks
*/
void setup() {
WDTCTL = WDTPW + WDTHOLD; // Stop watchdog timer
#if CALIBRATED
DCOCTL = 0x00; // Use factory pre-calibrated DCOCLK to 1MHz
BCSCTL1 = CALBC1_1MHZ;
DCOCTL = CALDCO_1MHZ;
DCOCTL += 7; // I tweaked this value to make the DCO go faster. My
// factory calibrated value was 977kHz. '7' gets me right
// on 1.00MHz. You should adjust for your chip's speed
// or you could use an XTAL and calibrate it yourself.
#else
// 16.025000 MHz on my chip, measured
DCOCTL = 0;
BCSCTL1 = XT2OFF | (1 << 3 | 1 << 2 | 1 << 1) ;
DCOCTL = (1 << 7 | 1 << 6 | 1 << 5);
#endif
init_serial(F_CPU/BAUD_RATE); // RX=P1.2, TX=P1.1
#undef OUTPUT_CLOCKS // if defined, output clocks on pins so you can
// measure with oscilloscope or frequency counter
#ifdef OUTPUT_CLOCKS
P1DIR |= BIT0; P1SEL |= BIT0; // measure P1.0 for actual ACLK
P1DIR |= BIT4; P1SEL |= BIT4; // measure P1.4 for actual SMCLK
while( 1 ) { ; } // just stop here don't do anything else
#endif
_enable_interrupts(); // let the timers do their work
}
/*
* comment minifloat: loop() removed
* main - application main program
* loop runs in waitcmd()
*/
int main(void) {
setup();
init910();
waitcmd(); //has intrinsic loop
return (0); //never reached
}
# Name: Makefile
# Author: minifloat
# This file was made from a prototype Makefile.
# Modify it according to your needs.
# You should at least check the settings for
# MCU ........ defines the MCU you're using
# OBJECTS .... is your object list;
# for each *.c file a *.o entry exists
# DBGDEVICE .. specifies the driver for mspdebug
MCU = msp430g2231
OBJECTS = main.o timera_serial.o 910.o
DBGDEVICE = rf2500
# Tune the lines below only if you know what you're doing
COMPILE = msp430-gcc -Os -Wall -g -mmcu=$(MCU) -fdata-sections -ffunction-sections
all: $(OBJECTS)
$(COMPILE) -o main.elf $(OBJECTS) -Wl,--gc-sections -Wl,-Map=main.map
msp430-objdump -S main.elf >main.lst
# msp430-nm main.elf
msp430-size main.elf
%.o: %.c
$(COMPILE) -c $<
.PHONY: clean
clean:
rm -fr main.elf $(OBJECTS) main.lst main.map
.PHONY: flash
flash: all
mspdebug $(DBGDEVICE) "erase"
mspdebug $(DBGDEVICE) "prog main.elf"
# For debugging, these four steps ar needed:
# 1. open two terminal windows
# 2. navigate to the project's folder
# 3. in the first window, type "make debug" and wait until
# mspdebug states "waiting for connection on port 2000..."
# 4. in the second window, type "make gdb" and beat the hell
# out of your code (omg)
# 5. after terminating the debug session, tell me how
# i can automate this (also omg)
.PHONY: debug
debug: all
mspdebug $(DBGDEVICE) "gdb" &
.PHONY: gdb
gdb:
msp430-gdb --symbols=main.elf --eval-command=target\ remote\ localhost:2000
#EOF
//------------------------------------------------------------------------
// serial.h - function declarations for simple blocking serial routines
//------------------------------------------------------------------------
#ifdef __cplusplus
extern "C" {
#endif
#define CALIBRATED 0
#if CALIBRATED
#define F_CPU 1000000 // DCO speed
#else
#define F_CPU 16025000 // DCO speed
#endif
#define BAUD_RATE 9600 // serial port speed
static const int VER = 0x0100;
void init_serial(const unsigned duration) __attribute__((always_inline));
int getchar(void) __attribute__((always_inline));
int putchar(const int) __attribute__((always_inline));
#ifdef __cplusplus
} /* extern "C" */
#endif
/*
* timera_serial.c - TIMERA implementations of init_serial(), getchar(), putchar()
*
* Created on: Oct 30, 2011
* Author: kimballr - rick@kimballsoftware.com
*
* Desc: This code uses TimerA CCR0 and CCR1 to implement blocking serial
* send and receive functionality using interrupt helper routines.
*/
#include <msp430.h>
#include <stdint.h>
#include "serial.h"
/**
* tuart_config_t - configuration and runtime variables
*/
typedef struct {
const uint16_t TICKS_PER_BIT; // timer clock ticks per bit
const uint16_t TICKS_PER_BIT_DIV2; // timer clock ticks per half bit
volatile int16_t UARTRXBUF; // RX buffer, only valid after a received character
volatile int16_t UARTTXBUF; // TX buffer used to xmit character
volatile uint16_t RXFLAG; // flag to indicate we have data ready=0x01, data not ready=0x00
} tuart_config_t;
static tuart_config_t uart_config = {
.TICKS_PER_BIT = (F_CPU/BAUD_RATE),
.TICKS_PER_BIT_DIV2 = (F_CPU/BAUD_RATE/2),
.UARTRXBUF=0,
.UARTTXBUF=0,
.RXFLAG=0
};
/**
* init_serial(const unsigned bitduration)
*
* Really simple use of the TimerA hardware so we can have an
* API that is compatible with a simple software only version.
* We setup the TimerA CC0/CC1 using the default RX/TX pins
* for the launchpad and bitduration (F_CPU/BAUD_RATE).
*
* Note: This code use interrupts and needs the global interrupt flag enabled.
* However, this code doesn't modify the global interrupt flag.
*
*/
void init_serial(const unsigned bitduration) {
P1OUT |= BIT1; // Set output pin high (idle state)
P1SEL |= BIT1 | BIT2; // enable TA0.CCI0A & TA0.CCI1A
P1DIR &= ~BIT2; // rxPin input,
P1DIR |= BIT1; // txPin output
TA0CCTL0 = OUT; // OUTPUT
TA0CCTL1 = SCS | CM1 | CAP | CCIE; // INPUT SYNC/ CM1 / CAPTURE/ interrupt enabled
TA0CTL = TASSEL_2 | MC_2 | TACLR; // use SMCLK, in continuous UP mode, clear TACLR,
}
/**
* int getchar() - blocking read char from serial port
*
* TBD: we could really go to sleep and wait for Timer interrupt
* to wake us up when it is ready to do something.
*
*/
int getchar(void) {
// sit and spin waiting for an incoming character
while (!(uart_config.RXFLAG)) {
; // busywait on our volatile RX completion flag, set high when character available
}
uart_config.RXFLAG = 0; // reset to low
return uart_config.UARTRXBUF; // return RXed character
}
/**
* putchar(const int c) - write byte to serial port
*
* Desc: Use the TIMERA ISR to xmit a single character. Offloads
* the CPU and lets the timer do the work.
*
*/
int putchar(const int c) {
// When a transmit is in process the TIMERA interrupt flag
// will be set. The ISR disables the interrupt
// flag when it has sent the final stop bit.
while (TACCTL0 & CCIE) {
; // wait for previous XMIT to finish
}
// make the next output at least TICKS_PER_BIT in the future
// so we don't stomp on the the stop bit from our previous xmt
TACCR0 = TAR; // resync with current TimerA clock
TACCR0 += uart_config.TICKS_PER_BIT; // setup the next timer tick
TACCTL0 = OUTMOD0 | CCIE; // re-enable interrupts and use OUT bit
// now that we have set the next interrupt in motion
// we quickly need to set the TX data. Hopefully the
// next 2 lines happens before the next timer tick.
// Note: This code makes great use of the msp430 peripherals
//
// In the code above, we start with a busy wait on the CCIE
// interrupt flag. As soon as it is available, we setup the next
// send time and then re-enable the interrupt. Until that time happens,
// we have a few free cycles available to stuff the start and stop bits
// into the data buffer before the timer ISR kicks in and handles
// the event. Note: if you are using a really slow clock or a really
// fast baud rate you could run into problems if the interrupt is
// triggered before you have finished with the USARTTXBUF
uart_config.UARTTXBUF = c; // load our pseudo register used by the TIMERA0_VECTOR
uart_config.UARTTXBUF |= 0x100; // Add the stop bit '1'
uart_config.UARTTXBUF <<= 1; // Add the start bit '0'
return 0;
}
/**
* TIMER0_A1_RX_ISR - Receive Interrupt Handler
*
* This ISR works in two modes. It starts out in capture mode
* waiting for the RX line to go from HI to LO indicating a
* start bit from the sender. It then switches to
* compare mode. Each tick, it sets a future time to wake up and
* sample the RX line. The sample is done by calculating
* the time for the center of the data bit for the given
* baud rate and waking up and taking a sample at that time.
* Once the stop bit is received it goes back into
* capture mode waiting for the next start bit.
*
* Note: serial data is LSB first
*
*/
#ifdef __MSP430_HAS_TA3__
#define TAIV_TACCR1 TA0IV_TACCR1
#endif
__attribute__((interrupt(TIMER0_A1_VECTOR)))
void TIMER0_A1_VECTOR_RX_ISR(void) {
static uint8_t rxBitCnt = 8; //
static uint8_t rxData = 0; // recv buffer, so we don't stomp on the external character buffer
// reading TAIV clears the interrupt
if ( TAIV == TAIV_TACCR1 ) {
TA0CCR1 += uart_config.TICKS_PER_BIT; // Setup next time to sample
if (TA0CCTL1 & CAP) { // Is this the start bit?
TA0CCTL1 &= ~CAP; // Switch capture to compare mode
TA0CCR1 += uart_config.TICKS_PER_BIT_DIV2; // Sample from the middle of D0
}
else {
rxData >>= 1; // next bit, shift in a zero
if (TA0CCTL1 & SCCI) { // get bit waiting in receive latch
rxData |= 0x80; // if latch set, then set buffer high bit to 1
}
if ( !(--rxBitCnt) ) { // All bits RXed?
uart_config.UARTRXBUF = rxData; // Store in users recv buffer
rxBitCnt = 8; // Re-load bit counter
TA0CCTL1 |= CAP; // Switch compare to capture mode
uart_config.RXFLAG = 1; // trigger the received flag
}
}
}
}
/**
* TIMER0_A0_TX_ISR - TX Interrupt Handler
*
* Handle the sending of a data byte with one
* start bit + 8 data bits + stop bit.
*
* Note: We don't need to clear any interrupt flags here
* this isn't a vectored interrupt, only one event
* will get us here.
*
*/
__attribute__((interrupt(TIMER0_A0_VECTOR)))
void TIMER0_A0_TX_ISR(void) {
static uint8_t txBitCnt = 10; // 1 Start bit + 8 data bits + 1 stop bit
/**
* ready the value of the OUT bit which will be set when the TAR == TACCR0
*/
TACCTL0 |= OUTMOD2; // reset OUT (set to 0) OUTMOD2|OUTMOD0 (0b101)
if (uart_config.UARTTXBUF & 0x01) { // look at LSB if 1 then set OUT high
TACCTL0 &= ~OUTMOD2; // set OUT (set to 1) OUTMOD0 (0b001)
}
uart_config.UARTTXBUF >>= 1; // shift in the next bit for the next time through
if ( --txBitCnt == 0 ) { // all bits transmitted ?
TACCTL0 &= ~CCIE; // disable interrupt
txBitCnt = 10; // Re-load bit counter
}
TACCR0+=uart_config.TICKS_PER_BIT; // setup next time to send a bit, OUT will be set then
}
@danielgbat89
Copy link

Hi, i need same help. i tried to cmpile this code in CCS 5 for the msp430g2553 but it gets same error. What program did you use to compile??
same errors are 1) #115 function "usdelay" was referenced but not defined
2) Multiple markers at this line
- #71-D incomplete type is not
allowed
- #66 expected a ";"
3)Multiple markers at this line
- #249 function "attribute" has already been defined
- #80 expected a type specifier
- #142 unnamed prototyped parameters not allowed when body is
present
it is posible that new ccs is not compatible at all.. i guess
my email danielgbat89@gmail.com
pd: sorry about my english!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment