Last active
November 28, 2016 19:19
-
-
Save RickKimball/7c5fe28382d8f62a2917fdcf9933eda0 to your computer and use it in GitHub Desktop.
msp430fr5969 - 60Hz Sine Wave using PWM
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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)); | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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