Skip to content

Instantly share code, notes, and snippets.

@RickKimball
Created June 29, 2012 19:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • 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
}
@RickKimball
Copy link
Author

This is a modified version of the AVR910 ISP programmer from http://www.mikrocontroller.net/articles/Launchprog. This code is written to be able to run on the msp430g2211, however it will run on other chips with a TimerA peripheral.

The big modification in this version is that it has blocking TimerA serial routines that are much smaller than the previous async ringbuffer enabled version. Also, this version will run without an XTAL soldered in. See main.c comments for all the changes. Compiled flash size is about 1800 bytes and it uses a very small amount of ram.

From the command line you do something like this:

$ avrdude -c avr910 -P /dev/ttyACM0 -b 9600 -v -p m1284p -U flash:w:cyclon1284p.hex:a

-rick

@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