Hey all, I have tried getting this code to work and filter it so it is a smooth sine wave using an active 2nd order Butterworth filter or a passive RC filter and I get the attached photo. My issues is that my math is not acting correctly. I have been able to get a constant 50Hz signal out but cannot get a smooth sine wave to exist. When I have been using a look-up table the frequency will not stay constant so right now the array I have set is only at one constant output value. Here is the math I have been using: Output Freq= Clock Frequency /(PWM clock ticks * size of array) PWM Frequency = (Clock frequency / PWM clock ticks) Using either of the filters I have been able to get this at a constant Frequency but unable to get a smooth wave out. Here is the code I have been using. /** Simple DAC for the MSP430F5438A* Author: Alexander Coffin* Date:* Code set at 650 to allow a constant 50Hz output. Variation is done in PWM output values*************************************************************************/ #include <msp430f5438a.h> unsigned char counter; // Current location in wave array// Wave range is from 0-255. 255 will give an output around 1v pk-pk(all above 0)//100 gives 250mv pk-pk(split between both)//150://200: 800mv pk-pk(half and half)unsigned char wave[4] ={ 100, 100, 100, 100}; unsigned int i; // Used for 'for' loops. void main(void){ WDTCTL = WDTPW + WDTHOLD; // Stop WDT P4DIR |= BIT1; // P4.1 output counter = 0; // Reset counter // Initialize Timer TB0CCTL0 = CCIE; // CCR0 interrupt enabled TB0CCTL1 = CCIE; // CCR1 interrupt enabled TB0CCR0 = 650; // Set PWM period to 650 clock ticks TB0CCR1 = wave[counter]; // Set first duty cycle value TB0CTL = TBSSEL_1 + MC_1 + TBIE + TBCLR; // ACLK, upmode, enable interrupt, clear TB1R _BIS_SR(LPM0_bits + GIE); // Enter LPM0 w/ interrupt} /*** TimerB0 interrupt service routine**/#pragma vector=TIMER0_B0_VECTOR__interrupt void TIMER0_B0_ISR(void){ P4OUT |= BIT1; // Set P4.0 TB0CCR1 = wave[counter]; // Set next duty cycle value counter += 1; // Add Offset to CCR0 if ( counter == 4) // If counter is at the end of the array { counter = 0; // Reset counter }} /*** TimerA1 Interrupt Vector (TAIV) handler**/#pragma vector=TIMER0_B1_VECTOR__interrupt void TIMER0_B1_ISR(void){ switch( TBIV ) { case 2: // CCR1 interrupt P4OUT &= ~BIT1; // Clear P4.0 to determine duty cycle. break; default: break; }}
If this code can be configured to allow for a steady wave to be produced I need to know how a long with how I could possibly build in a DC step.
Thanks!
I don't think the code can do what you want.
PWM means you're '1' for some time and '0' for the rest of one PWM cycle.
However, what you code does is to set the cycle time to 651(!) timer ticks. So the timer runs from 0 to 100, triggers TIMER0_B1_ISR which will invert the port pin, then counts to 650, triggers TIMER0_B0_ISR, which changes the TBCCR1 settign to (currently) the same as before. Then counts to 0, triggering TIMER0_B1_ISR again, but this time you don't handle the itnerrupt (it is case 14 or so, timer overflow, since you did set TBIE). When the timer continued to 100 again, and the output is toggled.
However, if you do it this way, the port pin is only toggled with a duty cycle of 50% (toggled each time the timer reaches 100 on its way to 650). With 651*2 being the cycle time. Changing the value for TBCCR1 only shifts the offset to TBR==0 (the 'phase') but has no effect on the duty cycle and therefore not on the output signal.
To change the duty cycle, you'll have to change the output on two points of a cycle. E.g. reset it on the B1_ISR and set it in the B0_ISR. Then TBCCR1 determines the amount of ticks the output is on during a full cycle of 651 ticks. However, the ISR latency will not allow for high precision and may also introduce quirks when you get near 0 or 100% duty cycle.Hint: read abotu the OUTMODE bits. The hardware PWM does this in hardware without any ISR. But you should change the DC then inside the B0_ISR to synchronize it.
Anyway: PWM output is always rectangular. You can filter it to a sinus-like signal by a low pass with ~ 1/2 of the PWM frequency, or to a DC voltage (equivalent to the Duty cycle) with a low-pass with a very low cutoff frequency. But then you cannot change the output signal fast, as these changes will be low-passed too.
If you want to 'form' the output wave by constantly changing the PWM DC, you'll need a PWM frequency much higher than the output frequency (at least factor 4, better more) and a low-pass filter with a cutoff frequency slightly above the desired output frequency.
_____________________________________Before posting bug reports or ask for help, do at least quick scan over this article. It applies to any kind of problem reporting. On any forum. And/or look here.If you cannot discuss your problem in the public, feel free to start a private conversation: click on my name and then 'start conversation'. But please do so only if you really cannot do it in a public thread, as I usually read all threads. And I prefer to answer where others can profit from it (or contribute to it) too.
Alexander Coffinunsigned char wave[4] ={ 100, 100, 100, 100};
Hi Alexander, if you wish to generate a sinusoidal signal I think you need some sinusoidal shaped samples, more than 4 and not constant.
Regards
Roberto
Please login & click Verify Answer if this post answered your question.
Hi Alexander,
I'm attaching a couple of examples I tested some time ago.
- One of them uses a 2 timers, one used to generate the PWM, and the other as a time base which triggers the DMA automatically to send the next sample, so there's no CPU intervention
- The other one uses the same timer to generate the PWM and update the duty cycle on every period interrupt
I'm also attaching a picture of the sine wave at 100Hz using an external RC filter. Note that you can achieve better resolutions and faster frequencies depending on the number of steps in the sine wave and the PWM frequency.
I hope you find it useful.
Regards,
Luis R
7115.msp430_test_sine_wave_pwm.c
5444.msp430_test_sine_wave_pwm_sw.c