Skip to content

Instantly share code, notes, and snippets.

@RickKimball
Last active November 28, 2016 19:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RickKimball/7c5fe28382d8f62a2917fdcf9933eda0 to your computer and use it in GitHub Desktop.
Save RickKimball/7c5fe28382d8f62a2917fdcf9933eda0 to your computer and use it in GitHub Desktop.
msp430fr5969 - 60Hz Sine Wave using PWM
/*
* 60Hz.c - generate a 60Hz PWM sine wave msp430frxxxx
*
* This code written for msp430-elf-gcc
*
* Note: make sure to compile with -ffixed-R6 -ffixed-R7.
*/
#include <msp430.h>
#include <inttypes.h>
#define __always_inline __attribute__((always_inline))
#define __flatten __attribute__((flatten))
#define max_min(a,min,max) ((a > max) ? max :(a < min) ? min : a)
#define sizeofs(a) (sizeof(a)/sizeof(a[0]))
//#define MCLK_FREQ 8000000
#define MCLK_FREQ 7970000
/*
* Note: min_value is the lowest CCR2 value that can be used
* without causing a glitch. This is based on how long the
* TIMER1_A0 routine runs in cpu cycles.
*/
unsigned sine_table[] =
{
1021,1121,1220,1317,1412,1502,1588,1669,
1743,1810,1870,1921,1964,1998,2022,2037,
2042,2037,2022,1998,1964,1921,1870,1810,
1743,1669,1588,1502,1412,1317,1220,1121,
1021,921,822,725,630,540,454,373,
299,232,172,121,78,44,20,5,
0,5,20,44,78,121,172,232,
299,373,454,540,630,725,822,921
};
unsigned scaled_table[sizeofs(sine_table)];
static const unsigned ISR_CYCLES=32; // 32/8000000 ~4uS
static const unsigned HZ_FREQ=60;
static const unsigned PERIOD=(MCLK_FREQ / sizeofs(sine_table) / HZ_FREQ)-1;
register unsigned work_reg6 asm("R6");
register unsigned accum asm ("R7"); // alias R7 = accum
static
unsigned scale_value(unsigned x)
{
return sine_table[x] + ISR_CYCLES;
}
static void init()
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
PM5CTL0 &= ~LOCKLPM5; // unlock gpio pins
if (MCLK_FREQ > 7600000 && MCLK_FREQ < 8400000UL) {
// unlock CS registers, change default divider from 8 to 1, lock
CSCTL0_H = CSKEY >> 8;
CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1;
CSCTL0_H = 0;
}
unsigned i;
for (i = 0; i < sizeofs(sine_table); ++i) {
unsigned v = scale_value(i);
scaled_table[i] = max_min(v, ISR_CYCLES, PERIOD);
}
}
int main(void)
{
init(); // setup clocks and tables
P1DIR |= BIT0|BIT3|BIT6; // P1.0 output P1.0 led, P1.3 (CCR2 output), P1.6 timing debug
P1SEL0 |= BIT3; // use as CCR2 timer output
P1OUT = 0; // clear all pins
accum=0; // not initialized, so we do it
TA1CCTL0=CCIE; // CCR0 interrupt enabled
TA1CCTL2=OUTMOD_7; // CCR2, Set Hi P1.3 on CCR0==0, Cleared at CCR2 value
TA1CCR0=PERIOD; // Set PWM period to 256 clock ticks
TA1CCR2=scaled_table[0]; // Set first duty cycle value
TA1CTL=TASSEL__SMCLK | MC__UP | TAIE | TACLR;
// 8MHz clk, UpMode, TAIFG enabled, clear TA1R
_enable_interrupts();
while (1) {
P1OUT ^= BIT0;
__delay_cycles(MCLK_FREQ/2);
}
}
/**
* Timer1_A0 interrupt handler CCR0
*
* Set the next duty cycle for CCR2 before it happens. We use
* a fixed work register (R6) so we can minimize the time spent
* in this routine. Otherwise a work register using a push and
* pop would be used increasing the time spent here. Using a
* fixed register allows for a larger range of PWM values and
* the minimum time in this routine. As written, this routine
* takes about 4 uSeconds running @ 8MHz
*
* Note: this ISR triggers before the the TAIFG, and we rely on that
*/
__attribute__((interrupt(TIMER1_A0_VECTOR), flatten))
void Load_Next_PWM_Phase_value_ISR(void)
{
// the following asm is the same as:
// TA1CCR2 = scaled[counter];
__asm__ volatile (
".global scaled_table\n" // reference scaled_table symbol\n"
"mov.w R7, R6\n" // load index into work reg\n"
"rla.w R6\n" // double index offset, values are uint16_t\n"
"mov.w scaled_table(r6),%[ta1ccr2]\n"
// index into values using r6 and set CCR2 value\n"
: [ta1ccr2] "=m" (TA1CCR2)
);
// timing debug - indicate end of this routine
//__asm__ volatile ("xor.b %1,%0" : "=m" (P1OUT) : "i" (1<<6));
}
/**
* Timer1_A1 interrupt handler (CCRx,TAIFG)
*
* Increment the phase accumulator at a non-time critical time using
* the TAIFG interrupt. This event occurs after the CCR0 ISR because
* of ISR priority.
*/
__attribute__((interrupt(TIMER1_A1_VECTOR), flatten))
void Increment_ACCUM_ISR(void)
{
//__asm__ volatile (" bis.b %1,%0 # time the isr routine" : "=m" (P1OUT) : "i" (1<<6));
switch (TA1IV) {
case 0: // NO interrupt
case 2: // CCR1 interrupt
case 4: // CCR2 interrupt
case 6: // CCR3 interrupt
break; // fall through for all except
case 14: // TAIFG triggers after CCR0 value but after CCR0 ISR
{
//accum = (accum+1) & (sizeofs(sine_table)-1);
__asm__ volatile (
"inc.w r7\n"
"and.b %[mask],r7\n"
:: [mask] "i" (sizeofs(sine_table)-1)
);
}
break;
}
//__asm__ volatile ("bic.b %1,%0" : "=m" (P1OUT) : "i" (1<<6));
}
sine_wave.elf: file format elf32-msp430
Disassembly of section __interrupt_vector_41:
0000ffe0 <__interrupt_vector_41>:
ffe0: 50 45 interrupt service routine at 0x4550
Disassembly of section __interrupt_vector_42:
0000ffe2 <__interrupt_vector_42>:
ffe2: 42 45 interrupt service routine at 0x4542
Disassembly of section .text:
00004482 <__crt0_start>:
4482: 31 40 00 24 mov #9216, r1 ;#0x2400
00004486 <__crt0_init_bss>:
4486: 3c 40 80 1c mov #7296, r12 ;#0x1c80
0000448a <.Loc.74.1>:
448a: 0d 43 clr r13 ;
0000448c <.Loc.75.1>:
448c: 3e 40 82 00 mov #130, r14 ;#0x0082
00004490 <.Loc.79.1>:
4490: b0 12 ae 45 call #17838 ;#0x45ae
00004494 <__crt0_movedata>:
4494: 3c 40 00 1c mov #7168, r12 ;#0x1c00
00004498 <.Loc.116.1>:
4498: 3d 40 00 44 mov #17408, r13 ;#0x4400
0000449c <.Loc.119.1>:
449c: 0d 9c cmp r12, r13 ;
0000449e <.Loc.120.1>:
449e: 04 24 jz $+10 ;abs 0x44a8
000044a0 <.Loc.122.1>:
44a0: 3e 40 80 00 mov #128, r14 ;#0x0080
000044a4 <.Loc.124.1>:
44a4: b0 12 74 45 call #17780 ;#0x4574
000044a8 <__crt0_call_just_main>:
44a8: 0c 43 clr r12 ;
000044aa <.Loc.181.1>:
44aa: b0 12 ae 44 call #17582 ;#0x44ae
000044ae <main>:
return sine_table[x] + min_value;
}
static void init()
{
WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer
44ae: b2 40 80 5a mov #23168, &0x015c ;#0x5a80
44b2: 5c 01
000044b4 <.Loc.54.1>:
PM5CTL0 &= ~LOCKLPM5; // unlock gpio pins
44b4: 92 c3 30 01 bic #1, &0x0130 ;r3 As==01
000044b8 <.Loc.58.1>:
if (MCLK_FREQ > 7600000 && MCLK_FREQ < 8400000UL) {
// unlock, change default divider to 1 from 8, lock
CSCTL0_H = CSKEY >> 8;
44b8: f2 40 a5 ff mov.b #65445, &0x0161 ;#0xffa5
44bc: 61 01
000044be <.Loc.59.1>:
CSCTL3 = DIVA__1 | DIVS__1 | DIVM__1;
44be: 82 43 66 01 mov #0, &0x0166 ;r3 As==00
000044c2 <.Loc.60.1>:
CSCTL0_H = 0;
44c2: c2 43 61 01 mov.b #0, &0x0161 ;r3 As==00
000044c6 <.LVL0>:
44c6: 4c 43 clr.b r12 ;
000044c8 <.L4>:
{
unsigned i;
for(i=0; i < sizeofs(sine_table); ++i) {
unsigned v = scale_value(i);
scaled_table[i] = max_min(v, min_value, period);
44c8: 1d 4c 00 1c mov 7168(r12),r13 ;0x01c00
44cc: 3d 50 20 00 add #32, r13 ;#0x0020
44d0: 8c 4d 82 1c mov r13, 7298(r12); 0x1c82
44d4: 3d 90 20 00 cmp #32, r13 ;#0x0020
44d8: 03 2c jc $+8 ;abs 0x44e0
44da: 7d 40 20 00 mov.b #32, r13 ;#0x0020
44de: 05 3c jmp $+12 ;abs 0x44ea
000044e0 <.L2>:
44e0: 3e 40 1a 08 mov #2074, r14 ;#0x081a
44e4: 0e 9d cmp r13, r14 ;
44e6: 01 2c jc $+4 ;abs 0x44ea
44e8: 0d 4e mov r14, r13 ;
000044ea <.L3>:
44ea: 8c 4d 82 1c mov r13, 7298(r12); 0x1c82
44ee: 2c 53 incd r12 ;
000044f0 <.LBE18>:
}
{
unsigned i;
for(i=0; i < sizeofs(sine_table); ++i) {
44f0: 3c 90 80 00 cmp #128, r12 ;#0x0080
44f4: e9 23 jnz $-44 ;abs 0x44c8
000044f6 <.LBE14>:
int main(void)
{
init(); // setup clocks and tables
P1DIR |= BIT0|BIT3|BIT6; // P1.0 output
44f6: f2 d0 49 00 bis.b #73, &0x0204 ;#0x0049
44fa: 04 02
000044fc <.Loc.78.1>:
P1SEL0 |= BIT3; // use as timer output
44fc: f2 d2 0a 02 bis.b #8, &0x020a ;r2 As==11
00004500 <.Loc.79.1>:
P1OUT = 0; // clear all pins
4500: c2 43 02 02 mov.b #0, &0x0202 ;r3 As==00
00004504 <.Loc.81.1>:
TA1CCTL0 = CCIE; // CCR0 interrupt enabled
4504: b2 40 10 00 mov #16, &0x0382 ;#0x0010
4508: 82 03
0000450a <.Loc.82.1>:
TA1CCTL2 = OUTMOD_7; // CCR2, Set Hi P1.3 on CCR0==0, Cleared at CCR2 value
450a: b2 40 e0 00 mov #224, &0x0386 ;#0x00e0
450e: 86 03
00004510 <.Loc.83.1>:
TA1CCR0 = period; // Set PWM period to 256 clock ticks
4510: b2 40 1a 08 mov #2074, &0x0392 ;#0x081a
4514: 92 03
00004516 <.Loc.84.1>:
TA1CCR2 = scaled_table[0]; // Set first duty cycle value
4516: 92 42 82 1c mov &0x1c82,&0x0396 ;0x1c82
451a: 96 03
0000451c <.Loc.85.1>:
TA1CTL = TASSEL__SMCLK | MC__UP | TAIE | TACLR;
451c: b2 40 16 02 mov #534, &0x0380 ;#0x0216
4520: 80 03
00004522 <.Loc.87.1>:
// 8MHz clk, UpMode, TAIFG enabled, clear TA1R
_enable_interrupts();
4522: 32 d2 eint
4524: 03 43 nop
00004526 <.L5>:
while (1) {
P1OUT ^= BIT0;
4526: d2 e3 02 02 xor.b #1, &0x0202 ;r3 As==01
0000452a <.Loc.91.1>:
__delay_cycles(MCLK_FREQ/2);
452a: 1d 14 pushm.a #2, r13 ;20-bit words
452c: 3d 40 96 73 mov #29590, r13 ;#0x7396
4530: 3e 40 0e 00 mov #14, r14 ;#0x000e
00004534 <.L11>:
4534: 1d 83 dec r13 ;
4536: 0e 73 sbc r14 ;
4538: fd 23 jnz $-4 ;abs 0x4534
453a: 0d 93 cmp #0, r13 ;r3 As==00
453c: fb 23 jnz $-8 ;abs 0x4534
453e: 1c 16 popm.a #2, r13 ;20-bit words
4540: f2 3f jmp $-26 ;abs 0x4526
00004542 <Load_Next_PWM_Phase_value_ISR()>:
4542: 16 42 80 1c mov &0x1c80,r6 ;0x1c80
00004546 <L0>:
"mov.w scaled_table(r6),%[ta1ccr2]\n"
//; index into values using r6 and set CCR2 value\n"
: [ta1ccr2] "=m" (TA1CCR2)
: [accum] "m" (accum)
);
4546: 06 56 rla r6 ;
4548: 92 46 82 1c mov 7298(r6),&0x0396 ;0x01c82
454c: 96 03
0000454e <.Loc.129.1>:
// timing debug - indicate end of this routine
//__asm__ volatile ("xor.b %1,%0" : "=m" (P1OUT) : "i" (1<<6));
}
454e: 00 13 reti
00004550 <Increment_ACCUM_ISR()>:
* the TAIFG interrupt. This event occurs after the CCR0 ISR because
* of ISR priority.
*/
__attribute__((interrupt(TIMER1_A1_VECTOR), flatten))
void Increment_ACCUM_ISR(void) {
4550: 1d 15 pushm #2, r13 ;16-bit words
00004552 <.LCFI0>:
//__asm__ volatile (" bis.b %1,%0 # time the isr routine" : "=m" (P1OUT) : "i" (1<<6));
switch (TA1IV) {
4552: 1c 42 ae 03 mov &0x03ae,r12 ;0x03ae
4556: 3c 90 0e 00 cmp #14, r12 ;#0x000e
455a: 0a 20 jnz $+22 ;abs 0x4570
0000455c <.LBB21>:
case 14: // TAIFG triggers after CCR0 value but after CCR0 ISR
{
// use a local variable to make the code faster than what is produced
// using a volatile variable. We are in an interrupt, so no one else
// is going to run making an alias safe.
register unsigned accum_ = accum+1;
455c: 1c 42 80 1c mov &0x1c80,r12 ;0x1c80
4560: 1c 53 inc r12 ;
00004562 <.LVL2>:
if ( accum_ > (sizeofs(sine_table)-1) )
4562: 7d 40 3f 00 mov.b #63, r13 ;#0x003f
4566: 0d 9c cmp r12, r13 ;
4568: 01 2c jc $+4 ;abs 0x456c
0000456a <.Loc.156.1>:
accum_ = 0;
456a: 4c 43 clr.b r12 ;
0000456c <.L12>:
accum = accum_;
456c: 82 4c 80 1c mov r12, &0x1c80 ;
00004570 <.L9>:
}
break;
}
//__asm__ volatile ("bic.b %1,%0" : "=m" (P1OUT) : "i" (1<<6));
}
4570: 1c 17 popm #2, r13 ;16-bit words
4572: 00 13 reti
00004574 <memmove>:
4574: 0b 4c mov r12, r11 ;
4576: 0b 5e add r14, r11 ;
00004578 <.Loc.69.1>:
4578: 0d 9c cmp r12, r13 ;
457a: 02 28 jnc $+6 ;abs 0x4580
0000457c <.L4>:
457c: 0e 4c mov r12, r14 ;
0000457e <.LVL2>:
457e: 0f 3c jmp $+32 ;abs 0x459e
00004580 <.L2>:
4580: 0f 4d mov r13, r15 ;
4582: 0f 5e add r14, r15 ;
4584: 0c 9f cmp r15, r12 ;
4586: fa 2f jc $-10 ;abs 0x457c
00004588 <.LVL4>:
4588: 0d 4f mov r15, r13 ;
0000458a <.LVL5>:
458a: 0e 8f sub r15, r14 ;
0000458c <.L5>:
458c: 0f 4d mov r13, r15 ;
458e: 0f 5e add r14, r15 ;
4590: 0f 93 cmp #0, r15 ;r3 As==00
4592: 0c 24 jz $+26 ;abs 0x45ac
00004594 <.Loc.76.1>:
4594: 3b 53 add #-1, r11 ;r3 As==11
4596: 3d 53 add #-1, r13 ;r3 As==11
00004598 <.LVL8>:
4598: eb 4d 00 00 mov.b @r13, 0(r11) ;
459c: f7 3f jmp $-16 ;abs 0x458c
0000459e <.L3>:
459e: 0e 9b cmp r11, r14 ;
45a0: 05 24 jz $+12 ;abs 0x45ac
000045a2 <.LVL10>:
45a2: ee 4d 00 00 mov.b @r13, 0(r14) ;
45a6: 1e 53 inc r14 ;
000045a8 <.LVL11>:
45a8: 1d 53 inc r13 ;
45aa: f9 3f jmp $-12 ;abs 0x459e
000045ac <.L9>:
45ac: 30 41 ret
000045ae <memset>:
45ae: 0f 4c mov r12, r15 ;
45b0: 0e 5c add r12, r14 ;
000045b2 <.L2>:
45b2: 0f 9e cmp r14, r15 ;
000045b4 <L0>:
45b4: 04 24 jz $+10 ;abs 0x45be
000045b6 <.LVL3>:
45b6: cf 4d 00 00 mov.b r13, 0(r15) ;
45ba: 1f 53 inc r15 ;
000045bc <.LVL4>:
45bc: fa 3f jmp $-10 ;abs 0x45b2
000045be <.L5>:
45be: 30 41 ret
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment