Skip to content

Instantly share code, notes, and snippets.

@RickKimball
Last active December 30, 2017 22:36
Show Gist options
  • Save RickKimball/7183254 to your computer and use it in GitHub Desktop.
Save RickKimball/7183254 to your computer and use it in GitHub Desktop.
msp430 example showing timer used to periodically send 50 baud async data
/*
* 50baud.c - example of sending 50 baud async serial output on P1.1
* code uses timer sourced from VLO and sleeps between reports.
*
* P1.0 - optionally output ACLK for measurement of VLO
* P1.1 - 50 baud async serial output
* P1.6 - indicator led, on during serial transmission
*
* TIMER0 - continuous up mode sourced with ACLK (VLO)
* TIMER0_A0 - 50 baud ISR enabled and disable for 8-N-1 transmission
* TIMER0_A1 - periodic timer, counts seconds and exits sleep mode when
* target time reached, and sets auto timer
*
* Author: rick@kimballsoftware.com
* Date: 10/27/2013
* msp430-elf-gcc -g -Os 50baud_asm.c _start.S -mmcu=msp430g2231 \
* -minrt -nostartfiles \
* -L /home/user/Msp430GCCopensource/msp430-elf/lib/ldscripts -T msp430g2231.ld
* ||
* msp430-gcc -g -Os 50baud_asm.c -mmcu=msp430g2231 -mdisable-watchdog \
* -ffunction-sections -fdata-sections -Wl,--gc-sections
*/
#include <msp430.h>
#include <stdint.h>
static const uint32_t MCLK_HZ = 1000000; /* how fast to run the cpu */
static const uint32_t XTAL_HZ = 11680; /* VLO frequency, measure your chip and set here */
/*
* ---- timer based serial code ----
*/
#define BAUD 300
#define BAUD_2 (BAUD/2)
#define TICKS_PER_BIT ((XTAL_HZ+(BAUD_2))/BAUD)
#define TICKS_PER_BIT_DIV2 ((XTAL_HZ+(BAUD_2))/BAUD/2)
static const unsigned data_mask = 0xff;
static const unsigned stop_mask = 0x01;
static volatile unsigned int TX_BUF; /* 1 character tx buffer, used by ISR */
#if 0
#define ALWAYS_INLINE __attribute__((always_inline))
#else
#define ALWAYS_INLINE inline
#endif
static void ALWAYS_INLINE init_serial(void);
static void ALWAYS_INLINE init_systick(void);
static void ALWAYS_INLINE send_packet(void);
void init_serial(void)
{
TA0CCTL0 = OUT; /* set idle state of P1.1 to high */
P1DIR |= BIT1; P1SEL |= BIT1; /* configure TA0.0/P1.1 as output */
TA0CTL = TASSEL_1 | MC_2 | TACLR; /* continuous up based on VLOCLK */
}
/*
* putchar() - send one serial byte
*
* NOTE: by implementing putchar(), printf will work on larger msp430 G series
*/
int putchar(int c)
{
register unsigned value;
value = (stop_mask << 8 )| (c & data_mask); /* stop bit '1' and data */
value <<= 1; /* add start bit '0' */
while(TA0CCTL0 & CCIE); /* busy wait for previous transmission to finish */
TA0CCR0 = TA0R; /* get current timer count */
TA0CCR0 += TICKS_PER_BIT; /* set up next bit transition time */
TA0CCTL0 = OUTMOD0 | CCIE; /* default TX_PIN HIGH and re-enable interrupts */
TX_BUF = value; /* queue up the byte for ISR */
return 1;
}
void putstr(const uint8_t *s)
{
while(*s) {
putchar(*s++);
}
}
/*
* send_packet - TODO: take your data sample and create your custom packet data
*/
void send_packet(void)
{
static uint8_t packet_id; /* persistent packet number, starts at 0 */
static char hex_tbl[]="0123456789abcdef";
P1OUT ^= BIT6;
++packet_id;
putchar('U');
putchar(hex_tbl[(packet_id >> 4) & 0b1111]);
putchar(hex_tbl[packet_id & 0b1111]);
putchar('\r');
putchar('\n');
P1OUT ^= BIT6;
}
/*
* putch_bit_isr() - use timer interrupt to send next bit
*
* outputs each bit using LSB order
*/
__attribute__ ((interrupt(TIMER0_A0_VECTOR)))
void putch_bit_isr(void)
{
__asm__ volatile
(
" add %[ticks],%[ccr0]\n"
" bis %[outmod2],%[cctl0]\n"
" rra %[tx_buf], %[tx_buf]\n"
" jnc 1f\n"
" bic %[outmod2],%[cctl0]\n"
"1:\n"
" jnz 2f\n"
" bic %[ccie],%[cctl0]\n"
"2:\n"
: /* return value */
: /* named constants / variables */
[ticks] "i" (TICKS_PER_BIT),
[outmod2] "i" (OUTMOD2),
[ccie] "i" (CCIE),
[ccr0] "m" (TA0CCR0),
[cctl0] "m" (TA0CCTL0),
[tx_buf] "m" (TX_BUF)
: /* collobered registers */
"cc"
);
}
#if 0
/* original c version */
__attribute__ ((interrupt(TIMER0_A0_VECTOR)))
void putch_bit_isr(void)
{
TA0CCR0 += TICKS_PER_BIT; /* setup next time to send next bit, OUT is set then */
TA0CCTL0 |= OUTMOD2; /* reset OUT (set to 0) OUTMOD2|OUTMOD0 (0b101) */
if (TX_BUF & 0x01) { /* look at LSB, if 1 */
TA0CCTL0 &= ~OUTMOD2; /* then set OUT (set to 1) OUTMOD0 (0b001) */
}
if (!(TX_BUF >>= 1)) { /* if all bits transmitted ? */
TA0CCTL0 &= ~CCIE; /* then disable interrupt, send no more bits */
/* NOTE: this indicates byte transmit complete */
}
}
#endif
/*
* --- periodic timer code ---
*/
static const long xmit_every_n_secs = 3; /* send a report ever n seconds */
static volatile unsigned long secs; /* total running seconds */
static volatile unsigned long secs_til_wakeup; /* countdown time in seconds */
/*
* enable_systick() -
*
* NOTE: assumes a low frequency clock source of less than < 64kHz
* also assumes some other routines initializes the timer ctl.
*/
void init_systick(void)
{
secs_til_wakeup = xmit_every_n_secs;
TA0CCR1 = XTAL_HZ;
TA0CCTL1 = CCIE;
}
#if 0
#pragma vector=TIMER0_A1_VECTOR
__interrupt
#else
__attribute__ ((interrupt(TIMER0_A1_VECTOR)))
#endif
void systick_isr(void)
{
volatile unsigned reset_flag = TA0IV; /* reset the intr flag by reading */
(void) reset_flag; /* get rid of unused warning */
TA0CCR1 += XTAL_HZ; /* add 1 second to target time */
secs++; /* seconds since start */
if (!(--secs_til_wakeup)) { /* decr seconds count, exit when 0 */
secs_til_wakeup = xmit_every_n_secs;
LPM3_EXIT; /* let main code run */
}
}
/*
*
*/
int main(void)
{
WDTCTL = WDTHOLD | WDTPW;
BCSCTL1 = CALBC1_1MHZ; /* Set DCO to 1MHz */
DCOCTL = CALDCO_1MHZ;
BCSCTL3 = (BCSCTL3 & ~LFXT1S_3) | LFXT1S_2; /* configure ACLK with VLO */
init_serial(); /* configure timer as a serial device */
init_systick(); /* configure timer as periodic timer */
P1OUT &= ~BIT6; /* configure led indicator */
P1DIR |= BIT6;
#if 1 /* enable to measure clocks */
P1DIR |= BIT0 | BIT4;
P1SEL |= BIT0 | BIT4;
#endif
__enable_interrupt(); /* most things are handled in the isr routines */
while( 1 ) {
send_packet(); /* send a bunch of bits at 50 baud */
LPM3; /* deep sleep until xmit_every_n_secs reached */
}
return 0;
}
/* -*- Mode: Asm -*- */
;-----------------------------------------------------
; _start.S - minimal c runtime for msp430-elf-gcc
; This is a hacked up version of the crt0.S from msp430-gcc
; modified to work with msp430-elf-gcc. This allows the
; smaller msp430g2xxx chips to link successfully with
; very little extra code
;
.section .text, "ax", @progbits
.global _start
_start:
;-----------------------------------------------------
__init_stack:
mov #__stack, r1
mov #0x5a80, &0x120 ; WDTCTL = WDTPW|WDTHOLD
;-----------------------------------------------------
__copy_data:
mov #__romdatacopysize, r12
tst r12
jz 2f
1:
decd r12 ; assumes data section aligned to word boundary
mov.w __romdatastart(r12), __datastart(r12)
jne 1b
2:
;-----------------------------------------------------
__clear_bss:
mov #__bsssize, r12
tst r12
jz 2f
1:
decd r12 ; assumes bss section aligned to word boundary
clr __bssstart(r12)
jne 1b
2:
jz main
;-----------------------------------------------------
.section .resetvec, "ax", @progbits
.word _start ; point reset vector to init code above
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment