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: SW-DRL, TIDM-TM4C129POEAUDIO, SEGGER

hello everone,

We are building a project for  playing sound samples

To do that we decided to use the pwm output as dac by changing the d.t cycle values while keeping the preiod const.

We set the clock to 4.Mhz and the PWM period to 256 ( all to support our smaples sampling rate and bit per sample)

We are facing a problem making sure that new d.t  values are not set before the PWM has finished the current cycle.

To be more clear, the clock rate is much faster then the PWM frequency, resulting in faster then desired d.t update.

example code:

for ( i=0; i<sample_size;i++)

{

    PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1,data[i]);

}

We tried to read the PWM counter register to sync the d.t update, but unfortunately the counter value is stuck on zero.

any suggestions on the reasom OR maybe a diffrent way to accomplish the above.

thank you very much,

izik

  • Might the call to, "PWMSyncUpdate()" achieve your objective?
    This should "que" your update and apply it only when the PWM counter becomes zero.

    SW-DRL User Guide details care/handling of said function...

  • Hello Itzhak,

    Please look at the "synth" example which shows how to use PWM output to reproduce sound

    D:\ti\TivaWare_C_Series-2.1.3.156\examples\boards\dk-tm4c129x\synth

    Another resource is TIDM-TM4C129POEAUDIO which also uses a timer output for audio output.
  • Hi Amit,

    May those of us "not" blessed w/a "lisp" be authorized to "look at Synth?" Pleaseth...
  • thank you and amit for the quick reply,
    i guess my intention was not clear enough. i'm not looking to sync several pwm outputs ( i think that's what the update is meant for)
    but instead making sure that values are not read from the buffer before a PWM period is done.
    Like i mentioned i thought the best way is to read the counter value and make sure that a new value is read only when the counter gets to zero.

    i did that, by reading the register value using hwreg( pwm_base + offset) but that's not working properly either.
    Any suggestions why is that?
    I thougnt about using a timer to count the clock cycles but i'm not sure it would work.

    Itzhak
  • Hello Itzhak

    When using the PWM or the Timer in PWM mode, there are certain bits which control the action of the underlying timer. These bits are called Synchronization bits. These give an option to load the counters immediately or on a time out. It is based on how these bits have been programmed can you load the values to the counter registers to update when the main counter reaches the terminal value.

    Can you please share the PWM configurations. Also I would suggest to search for a very interesting post from cb1 which shows clearly the operation of the PWM under different modes of operation with waveforms.
  • If you read/review the function suggested - use of (multiple/different) PWM outputs is not strictly mandated! You've rejected via your interpretation of "multiple" outputs - which I do not believe to have been confirmed.

    If the function performs as I suspect - it achieves your goal - even as - and especially as - it does not meet (your [untested]) judgement.
  • Thanks again for your replies. I tried using the update function with no success.

    Anyway, i came across the "reloaded_interrupt" example, and it looks that might work for my needs

    is set the parameters as folows :

    /// set the sysyem clock to 4 Mhz

    SysCtlClockFreqSet(SYSCTL_OSC_INT | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_320,4000000);

       SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);

       PWMClockSet(PWM0_BASE,PWM_SYSCLK_DIV_1);

       SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);

       GPIOPinConfigure(GPIO_PF1_M0PWM1);

     

       GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1);

       PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC);

       PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, 256);

       PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1, 128);

     Here are the interrupt parameters:

       IntMasterEnable();

       PWMIntEnable(PWM0_BASE, PWM_INT_GEN_0);

       PWMGenIntTrigEnable(PWM0_BASE, PWM_GEN_0, PWM_INT_CNT_ZERO);

       IntEnable(INT_PWM0_0);

       PWMOutputState(PWM0_BASE, PWM_OUT_0_BIT, true);

       PWMGenEnable(PWM0_BASE, PWM_GEN_0);

    ///  here i load the nex value for the pwm d.t

       while(1)

       {

        value = sound_sample[i+1];

       }

    where my interrupthandler :

    void PWM0IntHandler(void)

    {

    PWMGenIntClear(PWM0_BASE, PWM_GEN_0, PWM_INT_CNT_ZERO);

    PWMGenIntClear(PWM0_BASE, PWM_GEN_0, PWM_INT_CNT_ZERO);

    PWMPulseWidthGet(PWM0_BASE, PWM_OUT_0,value) ;

           i++;

    }

    i also set the PWM0IntHandler(void) as follows:

    in the tm4c129ncpdt_setup_ccs.c file ( is that the corrects file to edit):

    i added the function decleration :

    extern void _c_int00(void);
    extern void PWM0IntHandler(void);

    and also added the funciton in the corrects place in the v.t

    PWM0IntHandler,                      // PWM Generator 0

    The problem is that the code exits right after entering the while loop and setting a breakpoint inside the handler deos'nt work.

    i tried changing  diffrent paraments, including setting the PWM_GEN_0, PWM_INT_CNT_LOAD but the prog keeps exiting.

    I would  really appreciate if someone could point me to the error.

    Itzhak

  • itzhak hengel said:
    I tried using the update function with no success.

    Mea Culpa - you were correct - that Sync Update does appear to be confined to multiple PWM Generators.

    Firm/I have no experience w/129 family.   (we employ 50% faster device (w/DAC) from another)

    Much of the code you show is equivalent to our code (used on LX4F & TM4C123) but for:

    "IntEnable(INT_PWM0_0);"   

    Our code: "IntEnable(INT_PWM0);"

    We use (only) older, more robust "StellarisWare" - the function parameters may have changed over time.   I've scanned your code 2 times - that's all I can find.

  • Hi, once again thanks for your time and try.

    INT_PWM0_0 holds 26 which is the correct location of the handler in the vector table.
    oddly, even if i change the function to "loop for ever" like "IntDefaultHandler" i still get "static void loader_exit(void)" after entering the while mentioned above. I guess i did'nt decalre something properly.
    there are two start up file (" tm4c1294ncdpt_startup_ccs.c and "tm4c1294ncdpt_startup_ccs_gcc.c") under ccs/arm/include and i added the "intpwm0handler" to both with no luck.
    Any suggestions or comments are wellcomed..

    Itzhak
  • Amit's your best bet both w/the MCU & general CCS. (you may wish to post to the CCS forum re: startup file.)

    I recall that we succeeded w/20KHz (50µS) PWM0 interrupt rates.   And then experimented - moving up to 50KHz.   I wonder if your issue may be too high a frequency PWM interrupt rate?   Surely you can dial that down - test/verify.

    May I suggest that you consider IAR or Keil - both long pre-existed CCS - and most all "pro firms" we deal with much prefer these more comprehensive, robust, and vendor agnostic (any ARM - any maker). Why limit yourself to one vendor - especially when that vendor offers ZERO for M0, M0+, M3, M7 etc?

    A spectacularly discounted Segger "J-Link" speeds, eases & enhances your (non-vendor restricted) ARM Development as well...

  • itzhak hengel said:
    oddly, even if i change the function to "loop for ever" like "IntDefaultHandler" i still get "static void loader_exit(void)" after entering the while mentioned above

    Are you using SYS/BIOS, or TI-RTOS which is built upon SYS/BIOS?

    With SYS/BIOS loader_exit will end up being called if SYS/BIOS detects an error.

  • hi guys your help is highly appreciated,

    well after your reply i opened the  "interrupt" example from tivaware and copied the part of my project regarding the PWM interuupts.

    Now my code does'nt exit anymore but i still can't get the handler to function correctly ( As though the IntDefaultHandler is entered instead)

    inside the startup file at the begining i have "--retain=g_pfnVectors" which unless masked prevents compiling.

    Any thoughts on the matther?

    I will also try and post at the ccs forum

  • Hello Itzhak,

    Can you please attach your code that is exiting right away to loader_exit(void)?
  • Hi , well the code does'nt exit anymore and it looks like the interrupt is working ( was able to set a breakpoint inside).

    Chester's comment set me in the right direction (Thank you!!)

    Unfortunately all this trouble seems for nothing since the values on the pwm  still updating too fast.

    I attach my code once again:

    void
    PWM0IntHandler(void)
    {
       
        PWMGenIntClear(PWM0_BASE, PWM_GEN_0, PWM_INT_CNT_LOAD);
        PWMGenIntClear(PWM0_BASE, PWM_GEN_0, PWM_INT_CNT_LOAD);
        PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1, Sound_Sample[i]);
     
        i++;
    }
    int
    main(void)
    {
       SysCtlClockFreqSet(SYSCTL_OSC_INT | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_320, 4000000) ; ////->>>>4Mhz
       SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);
       SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
       //Configure PWM Clock to match system
       PWMClockSet(PWM0_BASE, PWM_SYSCLK_DIV_1);
        //Configure PF1Pin as PWM
        GPIOPinConfigure(GPIO_PF1_M0PWM1);
        GPIOPinTypePWM(GPIO_PORTF_BASE, GPIO_PIN_1);
        PWMGenConfigure(PWM0_BASE, PWM_GEN_0, PWM_GEN_MODE_DOWN | PWM_GEN_MODE_NO_SYNC | PWM_GEN_MODE_DBG_STOP);
        PWMGenPeriodSet(PWM0_BASE, PWM_GEN_0, 255);
        PWMPulseWidthSet(PWM0_BASE, PWM_OUT_1,125); //// 
        // Turn on the Output pins
        PWMOutputState(PWM0_BASE, PWM_OUT_1_BIT, true);
        //***** THE INTERRUPT PART*****/////////
        PWMIntEnable(PWM0_BASE, PWM_INT_GEN_0);
        PWMGenIntTrigEnable(PWM0_BASE, PWM_GEN_0 , PWM_INT_CNT_LOAD);
        IntMasterEnable();
        // Enable the PWM0 interrupts on the processor (NVIC).
        IntEnable(INT_PWM0_0);
        // Enable the PWM generator
        PWMGenEnable(PWM0_BASE, PWM_GEN_0);
        i=0;
        while (1)
        {
        }
    }
    Now what i expect is that each time the PWM counter reaches the LOAD value the idex is incremented and a new D.T cycle value will be set!
    I loaded a 3 sec audio sample ( 16k*3 = 48Kb) but the entire sample is output in less then a sec( played on my speaker using LPF) i.e the update is much faster then 16KHz
  • Hello Itzhak

    The samples should be 48K samples and not 48 Kb (kilo bits) for the PWM comparison.
  • Hi Amit,
    I meant 48 kylobytes - 8 bit per sample - this is why i set the PWM period to 256.
    Any chance the interrupt handler is not returning because the int register takes time to clear ( i.e it takes the PWMGenIntClear function time to actually clear the interrupt)
  • In a case as you present - cannot you toggle a GPIO upon Entry to the handler and then again upon Exit? That would confirm the "completeness" of the interrupt service and you could then observe the frequency of interrupt occurrence.
  • Hello Itzhak

    The function " PWMGenIntClear(PWM0_BASE, PWM_GEN_0, PWM_INT_CNT_LOAD);" is being called twice in the interrupt handler. I would suggest removing the same first.

    As cb1 rightly identified, check the frequency of the interrupt. Also which TM4C device and board are you using?
  • Hi Amit,

    Strangely enough - w/in LMI's original BLDC code - it was suggested that (same) call be repeated! (the claim was that it could be "missed" - I too recall seeing that - and we used the back to back calls. (which proved little - yet our BLDC Controller did perform...)
  • Hello again,
    I will try and check the frequency the first time i'll have access to an oscilloscope.
    i'm using the tm4c129ncpdt.
    I also tried using the PWMIntStatus to with no luck.

    Thank you