This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

PWM as DAC

Other Parts Discussed in Thread: CONTROLSUITE

I have the F28027 Launchpad and am trying to vary the duty cycle of pulse width modulation, and run it through a low pass filter to achieve a desired signal to be played on a speaker, but am running into trouble.  The technique is described in this article: http://www.ti.com/lit/an/spraa88a/spraa88a.pdf  but the sample program is not compatible with my device.  When using HRPWM it is recommended to use an up-down counter.  But I'm not understanding when to change the CMPA value to set the duty cycle.  My current code is located here: https://github.com/VoiceOvers/VoiceModule/blob/master/VoiceModule.c  Can anyone help?  I was able to get a triangle wave by just running the Example_F2802xEPwmUpDownAQ from ControlSUITE.  But I need to be able to any waveform.

  • Hi Edward,

    As you might already know DACs do not come out to a pin on F2802x devices, but are instead inputs to comparators 1 and 2.  You can thus use this feature with DSP2802x_Comp.h file to extract the DAC value from DACVAL register.

    Regards,

    Gautam

  • Mr. Lynch,

    > But I'm not understanding when to change the CMPA value to set the duty cycle. 

    You should update CMPA at the sample rate of your audio.  This sets the duty cycle of the PWM, and is roughly equivalent to setting the level of a DAC.

    You can set CMPA inside an interrupt-service-routine.

    Regards,

    Bill

  • Gautam,

    Interesting technique, I did know there was a DAC buried in there somewhere but did not know there was software access to it.  Thanks!  I will look into this technique in the event that the current method does not work out.

    Thanks,

    Edward

  • Bill,

    Right, so I am guessing that if my PWM ratio with SYSCLKOUT is one, then the setPeriod() function is how many PWM clock cycles it takes to get to the next interrupt.  So if I want 44.1kHz with the clock at 60MHz, I would want the period at 1361.  And with that I can change CMPA during the interrupt.  However, I don't think I fully understand the way the up-down mode works.  Do I have to change CMPA manually every interrupt or wait until it reaches it max?

    It would help very much if you could take a look at my update_compare() method which gets called every interrupt.  The part that is commented out is what I think the code should be, but it doesn't work, and the part that is not commented is from an example and it moves CMPA linearly, thus making a triangle wave on the other side of the filter.  As a bonus, I am also a little curious if my calculate_duty_cycle() method is correct - my goal is to eventually play any file.

    Thanks for the help,

    Edward

  • Edward,

    >  if my PWM ratio with SYSCLKOUT is one, then the setPeriod() function is how many PWM clock cycles it takes to get to the next interrupt.

    Correct.

    > So if I want 44.1kHz with the clock at 60MHz, I would want the period at 1361.

    In up counting mode, yes.  In up-down counting mode, no.  You may want to start out with up-counting mode until you get your feet wet.  You should verify that the sample rate is what you expect with an oscilloscope.

    > Do I have to change CMPA manually every interrupt or wait until it reaches it max

    You should really read the PWM user's guide for this part; it will explain everything with graphs.  In short, CMPA is the comparison value.  In up-counting mode, the output will be high (or low, it's configurable) until the counter reaches CMPA; then it will go low.  It will not change automatically; you have to change it in the interrupt.

    > The part that is commented out is what I think the code should be

    I've looked the code over a little.  I can't see your data, but unless it ranges from 0 to the PWM period, it isn't going to work.  I wouldn't put any printfs in your interrupts either: disaster.  Keep it short, like:

    void interrupt pwm_isr(void) {

      PWM_setCmpA(epwm_info->myPwmHandle, data[indexToPlay]);

      if (++indexToPlay >= data_length) {
           indexToPlay = 0;

           ++loopCount;

      }
    }

    and then in your main look to see if the loop count is the right number, and if so, stop PWM.  Good luck.

    Regards,

    Bill

  • I have made good progress on this and have produced the sine output shown below.  Could somebody help me work on being able to manipulate its frequency?  It is at 20 Hz, but I want it to be in audible range.  I am aware that I am not using HRPWM, and would like to eventually but am not quite ready.  I thought that changing my sampling rate variables on lines 110 and 248 would change the frequency but they are not.  I am also working towards being able to play files by putting in arrays of data.  Here is my code: https://github.com/VoiceOvers/VoiceModule/blob/e24167400cdbef06c4d8e121805de9203d3d714f/VoiceModule.c

    Any advice would help, but especially about changing the frequncy - thanks!!

  • I have successfully created a sine wave by running PWM with varying duty cycle through a low pass filter.  I would like some advice on how I could change its frequency.  I posted on a previous thread ( http://e2e.ti.com/support/microcontrollers/c2000/f/171/p/376436/1343818.aspx#1343818 ) but think it is going unnoticed due to its age.

    Latest post:

    "I have made good progress on this and have produced the sine output shown below.  Could somebody help me work on being able to manipulate its frequency?  It is at 20 Hz, but I want it to be in audible range.  I am aware that I am not using HRPWM, and would like to eventually but am not quite ready.  I thought that changing my sampling rate variables on lines 110 and 248 would change the frequency but they are not.  I am also working towards being able to play files by putting in arrays of data.  Here is my code:https://github.com/VoiceOvers/VoiceModule/blob/e24167400cdbef06c4d8e121805de9203d3d714f/VoiceModule.c

    Any advice would help, but especially about changing the frequency - thanks!!"

    As for the audio files, I can now get the PCM data from wav files with this.

  • Edward,

    Have you looked at the example : epwm_up_aq in the control suite for your device?  This should get you started with the basics of changing the PWM frequency.

    Also, the workshop has some good material here as well:

    http://processors.wiki.ti.com/index.php/C2000_Piccolo_One-Day_Workshop_Home_Page

  • Hi Matthew,

    Yes, I have looked at that example, and in fact, a lot of my code for this based on this.  Before even getting to the point of making a sine wave I was able to run this example and run the PWM through a low pass filter to obtain a triangular wave, since the program moves the widths linearly back and forth.  So now I'm at a point where I'm trying to fix specifics of my code in relation to getting the sound into the audible range.  The sine wave I produced was only 20 Hz - not high enough to really come out of our speaker well and I'm not sure what I need to do to raise its pitch.  I thought changing the period would do so but it didn't seem to work.  Please let me know if you have any more suggestions related to the code I have posted on Github.

    Thanks!

  • I have narrowed down the source of my issue.  For some reason, interrupts are not being called proportionally to the length of my period.  For example, by using a sine wave made of 256 samples, printing when every few thousand interrupts occur and tapping it out on a metronome I did find out that in fact the played frequency should be 20 Hz!!!  But changing the period directly did not affect the rate that the interrupts occurred.  The only progress I have made is that by removing PWM2A and leaving 2B, and removing CMPA, I have doubled the frequency to 40 Hz.  Does anyone have any insight on this?  I am happy I found the root of the problem but am unsure why the interrupt wouldn't be occurring more frequently.  Thanks to anyone who can help!

  • Mr. Lynch,

    It sounds like your ISR is too slow.  Try reducing it to just toggling a GPIO pin and watch that pin with an oscilloscope.  Make sure the period is what you expect.  Then add back in lines of code until you find the one that appears to change the period.

    Regards,

    Bill

  • PS: never print from an ISR.  It takes hundreds of msec.

  • Bill,

    Thanks for the suggestion.  I will give that a try.  About the printing, I had grouped it to only print every several thousand (6000) interrupts, thinking that that would be enough to not slow it down, but I could see how hundreds of milliseconds could be a problem.  I think that when I originally measured 20 Hz with a scope that there were no print statements but I will verify that.  Related to this, could you maybe link me to or explain how to use Code Composer to monitor variable values without breakpoints and without print statements properly so that I can avoid those latencies altogether?

    Thanks,

    Edward

  • Your hunch definitely put me in the right direction.  Though the 20 Hz was achieved without any print statements as I said, I began thinking that my interrupt was doing too many calculations.  By taking out half of the calculations I noticed an immediate jump, and then by pre-calculating all the duty cycles before playing any audio, the same signal jumped all the way up to 486 Hz!  Thanks!

  • It looks like fitting even a few seconds of .wav files onto the C2000's 32 KB of flash is out of the question.  When I get my data from the file using another program on my PC and paste the data into an array of unsigned integers, I can't compile at all unless the array size is around 640 elements or less.  Even a file downsampled to a very low quality of 6000 Hz can only be played through by a tenth of a second which is indistinguishable.  I can successfully loop tiny amounts of data like repeating waveforms but I guess I would need a full DSP processor to fit files on there?  Any tricks on how to manage memory?  I didn't realize that most of the 32 KB would be taken up by other code... is it the driverlib.lib that is taking up so much?

  • Mr. Lynch,

    What are you trying to do?  If you're trying to play large, arbitrary sound files then you probably want a design with an SD card.  If you're just trying to generate an alert tone there are many techniques for doing that in real time that require little or no RAM at all.  Small .wav files can be fit into the FLASH of larger C2000 devices; make sure you don't try and load them into RAM though!

    Regards,

    Bill

  • Hi Bill,

    I am trying to just fit just a few small files, in the form of unsigned integer arrays into the flash memory so they can be played.  I have alert tones as ascending or descending pitches for now, but would like to play a few spoken words which would only take up about 10KB since they are downsampled so much.  Does setting the array of integers as constant not have it put in flash?  If not, how can ensure it is stored in flash and not RAM?


    Thanks,

    Edward

  • Edward,

    When you deploy the program to Flash, the constants that make up your tone will be part of the deployment.  If you are trying to deploy to RAM, there might not be enough for your program and the data, particularly if you use printf, which has a huge footprint.

    Regards,

    Bill

  • Bill,

    I think I have achieved what I wanted my defining a segment in memory in the .cmd file.  I set the segment location to 0x3F0000 which according to documentation is the start of free flash.

    So in the .cmd I have:

    MEMORY

    {

    PAGE 0:

    PAGE 1:

    .......

    MY_FLASH: origin = 0x3F0000, length 0x000924

    }

    SECTIONS

    {

    .....

    .myFlash : {} > MY_FLASH, PAGE = 1

    }

    and in Audio.h

    #pragma DATA_SECTION(data, ".myFlash")

    const unsigned int data[] = {//really long array...};

    and with this I was able to successfully retrieve thousands of numbers, so unless there is an issue with this, then I think this is what I was going for.  I'll be sure to not use printf too.

    }