Shop OBEX P1 Docs P2 Docs Learn Events
Looking for an Audio Output (PWM?) Example - Page 2 — Parallax Forums

Looking for an Audio Output (PWM?) Example

24

Comments

  • I ran the sine wave example that @Ariba posted and it works fine on my P2 Eval board so I know that my hardware is working. Now I just have to get *my* code working!
  • Ahle2 wrote: »
    David,

    Here's a very basic stereo audio example code in pasm...
    CON
      L_PIN = 0
      R_PIN = 1
      SYSTEM_CLOCK = 250000000
      SAMPLE_RATE = 250000
    
      'DAC_MODE             = %00001_0 ' DAC 8-bit noise
      'DAC_MODE             = %00010_0 ' DAC 16-bit dither, noise
      DAC_MODE             = %00011_0 ' DAC 16-bit dither, PWM
      DIR_MODE             = %01      ' Output enabled, overrides DIR
      INIT_8BIT_DAC_VALUE  = 128
      'ANALOG_OUT_MODE      = %10100 ' 990 ohm, 3.3V DAC mode
      'ANALOG_OUT_MODE      = %10101 ' 600 ohm, 2.0V DAC mode
      'ANALOG_OUT_MODE      = %10110 ' 123 ohm, 3.3V DAC mode
      ANALOG_OUT_MODE      = %10111 '  75 ohm, 2.0V DAC mode
      SMARTPIN_DAC_MODE    = (ANALOG_OUT_MODE << 16) | (INIT_8BIT_DAC_VALUE << 8) | (DIR_MODE << 6) | DAC_MODE
      SAMPLE_PERIOD        = SYSTEM_CLOCK  / SAMPLE_RATE  'CPU cycles between sample updates 
    
    DAT
        org
    
        wrpin   ##SMARTPIN_DAC_MODE, #L_PIN   ' Config smartpin DAC mode on left pin
        wrpin   ##SMARTPIN_DAC_MODE, #R_PIN   ' Config smartpin DAC mode on right pin
        wxpin   ##SAMPLE_PERIOD, #L_PIN       ' Set sample period for left audio channel
        wxpin   ##SAMPLE_PERIOD, #R_PIN       ' Set sample period for right audio channel
        dirh    #L_PIN                        ' Enable smartpin DAC mode on left pin
        dirh    #R_PIN                        ' Enable smartpin DAC mode on right pin
        setse1  #%001_000000 | L_PIN          ' Event triggered every new sample period (when "left in pin rises")
    
    loop
        wypin    sawWave, #L_PIN              ' Output sample on left channel
        wypin    sawWave, #R_PIN              ' Output sample on right channel
        waitse1                               ' Wait for smart pin NCO wrap, new period
        add      sawWave, #1
        jmp      #loop
    
    sawWave	 res      1
    
    I tried this slightly modified version of your code but all I get are pops on my speaker about every two seconds. What did I do wrong? My main changes were using the output pins for the A/V accessory to the P2 Eval board and stripping out code that was commented out in your version.
    CON
      L_PIN = 14
      R_PIN = 15
      _clkfreq = 250_000_000
      SAMPLE_RATE = 250_000
    
      DAC_MODE             = %00011_0 ' DAC 16-bit dither, PWM
      DIR_MODE             = %01      ' Output enabled, overrides DIR
      INIT_8BIT_DAC_VALUE  = 128
      ANALOG_OUT_MODE      = %10101 ' 600 ohm, 2.0V DAC mode
      SMARTPIN_DAC_MODE    = (ANALOG_OUT_MODE << 16) | (INIT_8BIT_DAC_VALUE << 8) | (DIR_MODE << 6) | DAC_MODE
      SAMPLE_PERIOD        = _clkfreq  / SAMPLE_RATE  'CPU cycles between sample updates 
    
    DAT
        org
    
        wrpin   ##SMARTPIN_DAC_MODE, #L_PIN   ' Config smartpin DAC mode on left pin
        wrpin   ##SMARTPIN_DAC_MODE, #R_PIN   ' Config smartpin DAC mode on right pin
        wxpin   ##SAMPLE_PERIOD, #L_PIN       ' Set sample period for left audio channel
        wxpin   ##SAMPLE_PERIOD, #R_PIN       ' Set sample period for right audio channel
        dirh    #L_PIN                        ' Enable smartpin DAC mode on left pin
        dirh    #R_PIN                        ' Enable smartpin DAC mode on right pin
        setse1  #%001_000000 | L_PIN          ' Event triggered every new sample period (when "left in pin rises")
    
    loop
        wypin    sawWave, #L_PIN              ' Output sample on left channel
        wypin    sawWave, #R_PIN              ' Output sample on right channel
        waitse1                               ' Wait for smart pin NCO wrap, new period
        add      sawWave, #1
        jmp      #loop
    
    sawWave	 res      1
    

  • David BetzDavid Betz Posts: 14,511
    edited 2020-05-24 02:23
    I'm assuming that in the p_dac_dither_rnd mode, I write a signed 16 bit value to Y to set the output level. Is that correct? I'm having a hard time understanding the Smartpin description.

    For example, the pin mode I'm using is described like this:
    %00010 and DAC_MODE = DAC 16-bit with pseudo-random dither

    This mode overrides P[7:0] to feed the pin's 8-bit DAC with pseudo-randomly-dithered data on every clock. P[12:10] must be set to %101 to configure the low-level pin for DAC output.
    First, what is "P"? and the description talks about overriding P[7:0] which is only 8 bits but the name of the mode is "DAC 16-bit". Do I write 8 bits to the Y register or 16?
  • evanhevanh Posts: 15,187
    They both produce a sawtooth for me.

    Note: The sysclock frequency is not being set for you when in pure pasm2 assembly mode. You have to do your own code for it. Default is usually RCFAST.
  • evanh wrote: »
    They both produce a sawtooth for me.

    Note: The sysclock frequency is not being set for you when in pure pasm2 assembly mode. You have to do your own code for it. Default is usually RCFAST.
    Ah, okay. That is likely my problem. I'm probably "hearing" a 2 hz sawtooth. What's the official PASM incantation to set the clock frequency?

  • evanhevanh Posts: 15,187
    P field is a portion of the whole 32 bits passed in D operand of WRPIN. It's the low level custom pin mode settings. The field size is 13 bits, numbered as P[12:0]. The upper bits form the mode, with the lower bits the parameters. Also referenced as M[12:0] in the pad I/O sheet.

    Y register is a 32-bit smartpin parameter. Some smartpin modes don't use all 32-bits. In this case it's unsigned 16-bit only. The upper 8 bits of Y are used directly for the DAC output. The lower 8 bits are compared against an 8-bit timer/RND to produce the extended dither.

  • evanh wrote: »
    P field is a portion of the whole 32 bits passed in D operand of WRPIN. It's the low level custom pin mode settings. The field size is 13 bits, numbered as P[12:0]. The upper bits form the mode, with the lower bits the parameters. Also referenced as M[12:0] in the pad I/O sheet.

    Y register is a 32-bit smartpin parameter. Some smartpin modes don't use all 32-bits. In this case it's unsigned 16-bit only. The upper 8 bits of Y are used directly for the DAC output. The lower 8 bits are compared against an 8-bit timer/RND to produce the extended dither.
    Thanks for the explanation!

  • evanhevanh Posts: 15,187
    I've historically used Cluso's well worn code for setting sysclock but Chip did do a tidy general formula a few months back. I should go find it ...

  • evanhevanh Posts: 15,187
    edited 2020-05-24 03:01
    David Betz wrote: »
    Thanks for the explanation!
    Err, correction, some modes don't have all 32-bits of Y ... Meaning you also can't store extra bits in there.
  • evanhevanh Posts: 15,187
    edited 2020-05-24 03:06
    Here's an example of me using Cluso's sysclock setting method using a bunch of constants and a short list of HUBSETs - https://forums.parallax.com/discussion/comment/1496516/#Comment_1496516

    You might want to check out Ariba's spin code just below too.

    EDIT: And here's a document type write up of PAD_IO_Modes sheet - https://forums.parallax.com/discussion/comment/1452036/#Comment_1452036
    And a block diagram - https://forums.parallax.com/discussion/171420/smartpin-diagram/p1

  • evanhevanh Posts: 15,187
    edited 2020-05-24 03:14
    evanh wrote: »
    I've historically used Cluso's well worn code for setting sysclock but Chip did do a tidy general formula a few months back. I should go find it ...
    Found it - https://forums.parallax.com/discussion/171044/pll-settings-calculator/p1
    I'll have to port that to pasm2 now ...
  • jmgjmg Posts: 15,144
    ManAtWork wrote: »
    ... But if you want to play high quality music I'd go for PWM with good filtering. A 3rd order sallen-key filter can be built with only one OP amp.
    Someone chasing high quality will be better with an I2S system. Consumer Volumes mean those are quite cheap.

    You are going to be 'listening to your power supplies' in a direct P2-to-Analog Audio design, with both DAC and Fast PWM.



  • Ahle2Ahle2 Posts: 1,178
    @David

    The saw wave steps through all the 65536 steps of the 16 bit DAC's. (add #1) This means you that you will get a frequency of 250000 / 65536 = 3.81 Hz if the system clock is 250 Mhz. The constant doesn't reflect the real system clock. Change the add value, constants or system clock to get other results.
  • AribaAriba Posts: 2,682
    jmg wrote: »
    ...
    You are going to be 'listening to your power supplies' in a direct P2-to-Analog Audio design, with both DAC and Fast PWM.

    On the P2, every 4 IO pins can have their own clean supply voltage connected.
    The sample rate of a P2 16bit DAC can go much higher than usual I2S ICs. But they mostly have 24bit resolution. So it depends what exactly you want to do, I find the audio quality of the P2 DACs very good.
  • While I use dual 3.3V LDOs to A & B ports after my 3.6V switcher to clean up the ripple and improve transient response etc, I am thinking I should allow for resistors or maybe ferrite beads in each I/O group in conjunction with the bypass caps to help filter out any supply noise. Effective enough or not, they are just another 0603 that can be substituted and experimented with.

    So far I have been impressed with the DACs for audio.
  • cgraceycgracey Posts: 14,133
    On the P2 Eval board we have separate LDO's for each set of 8 pins. This has worked out really well, as there is no discernible noise in the audio spectrum from those LDO's. Well, none that I've noticed.

    The highest-quality DAC setting for audio is 75-ohm PWM-dither mode with a multiple-of-256-clocks update period.

  • Ahle2 wrote: »
    @David

    The saw wave steps through all the 65536 steps of the 16 bit DAC's. (add #1) This means you that you will get a frequency of 250000 / 65536 = 3.81 Hz if the system clock is 250 Mhz. The constant doesn't reflect the real system clock. Change the add value, constants or system clock to get other results.
    Duh. I should have noticed that. The waveform isn't in the audible range. I tried changing the increment and, as you suggest, it works fine.

  • David BetzDavid Betz Posts: 14,511
    edited 2020-05-24 15:43
    I'm trying to convert the sawtooth wave pasm2 program to C and am having trouble.

    This works:
    CON
      L_PIN = 14
      R_PIN = 15
      _clkfreq = 160_000_000
      SAMPLE_RATE = 250_000
    
      DAC_MODE             = %00010_0 ' DAC 16-bit pseudo-random dither
      DIR_MODE             = %01      ' Output enabled, overrides DIR
      INIT_8BIT_DAC_VALUE  = 128
      ANALOG_OUT_MODE      = %10101 ' 600 ohm, 2.0V DAC mode
      SMARTPIN_DAC_MODE    = (ANALOG_OUT_MODE << 16) | (INIT_8BIT_DAC_VALUE << 8) | (DIR_MODE << 6) | DAC_MODE
      SAMPLE_PERIOD        = _clkfreq  / SAMPLE_RATE  'CPU cycles between sample updates 
    
    DAT
        org
    
        wrpin   ##SMARTPIN_DAC_MODE, #L_PIN   ' Config smartpin DAC mode on left pin
        wxpin   ##SAMPLE_PERIOD, #L_PIN       ' Set sample period for left audio channel
        dirh    #L_PIN                        ' Enable smartpin DAC mode on left pin
    
    loop
        wypin    sawWave, #L_PIN              ' Output sample on left channel
         
    wait
        testp    #L_PIN wc
    if_nc jmp    #wait
    
        
        add      sawWave, ##100
        jmp      #loop
    
    sawWave	 res      1
    

    but this doesn't:
    #include <stdio.h>
    #include <stdint.h>
    
    #define P2_TARGET_MHZ 160
    #include <sys/p2es_clock.h>
    
    #define AV_BasePin  8
    #define LeftPin     (AV_BasePin+6)
    #define RightPin    (AV_BasePin+7)
        
    #define SAMPLE_RATE 250000
    
    void main()
    {
        uint16_t output = 0;
        
        _clkset(_SETFREQ, _CLOCKFREQ);
    
        _pinstart(LeftPin, p_dac_dither_rnd | p_dac_600r_2v | p_oe, _CLOCKFREQ / SAMPLE_RATE, 0);
        
        for (;;) {
        
            _wypin(LeftPin, output);
            
            output += 100;
            
            while (!_pinr(LeftPin))
                ;
        }
    }
    

    Aren't they doing pretty much the same thing? Why doesn't the C code work?
  • AribaAriba Posts: 2,682
    > Why doesn't the C code work?

    You need to include "propeller.h" and call clkset() at begin.
    See "cdemo.c"

    Andy
  • Ariba wrote: »
    > Why doesn't the C code work?

    You need to include "propeller.h" and call clkset() at begin.
    See "cdemo.c"

    Andy
    I just updated my message with the _clkset function call. I had already tried that and it didn't make any difference.

  • Also, I guess I should mention that I'm compiling both the PASM2 code and the C code with "fastspin -2". I didn't ask for any optimization.
  • I know this doesn't address your problem directly, but maybe removing some stuff will help isolate the issue:
    FlexC will default to 160Mhz for P2 if you don't do any of the p2es_clock.h/_clkset stuff. Also, you can call _clockfreq() to read the frequency when you don't use the p2es_clock.h/_clkset stuff (you can use it if you do also).


    Also, have you looked at the listing file for the C version, might reveal some clues.
  • ersmithersmith Posts: 5,910
    edited 2020-05-24 20:56
    @"David Betz" : I don't see the clock being set in the pure assembly version. You have a _clkfreq declaration, but that's only going to have an effect if there's some Spin code: in pure assembly (if the file is DAT only) then what you see is what you get. So perhaps there's a timing issue?

    Another difference is that the PASM sawWave variable is a full 32 bits, whereas the C output variable is only 16 bits.
  • David BetzDavid Betz Posts: 14,511
    edited 2020-05-24 21:03
    ersmith wrote: »
    @"David Betz" : I don't see the clock being set in the pure assembly version. You have a _clkfreq declaration, but that's only going to have an effect if there's some Spin code: in pure assembly (if the file is DAT only) then what you see is what you get. So perhaps there's a timing issue?

    Another difference is that the PASM sawWave variable is a full 32 bits, whereas the C output variable is only 16 bits.
    I'll try changing the variable size to 32 bits. I've already tried the code with and without the _clkset call with no observable difference in the behavior.

    Edit: It still doesn't seem to work after I changed the output variable 32 bits. I guess I should look at the generated code to see if I can figure out what's going wrong.
  • AribaAriba Posts: 2,682
    Eric

    If I include the "propeller2.h" file, it works, but if this file is not included, there is no error message.
  • David Betz wrote: »
    I'll try changing the variable size to 32 bits. I've already tried the code with and without the _clkset call with no observable difference in the behavior.
    Perhaps I wasn't clear. The C version will always have the clock set to 160 MHz, even without an explicit _clkset call, because the C startup code does this.

    The assembly code has no clock frequency setting, and so it's going to be running at RCFAST speed (25 MHz) instead.
  • Ariba wrote: »
    Eric

    If I include the "propeller2.h" file, it works, but if this file is not included, there is no error message.

    Do you mean the code functions correctly if "propeller2.h" is included, but not if it is not included?

    Or do you mean that you're surprised that there are no errors because things like _wypin are not declared?

    If it's the second, then it's just because _wypin and co. are builtin functions in FlexC and so don't actually need to be declared.

  • Roy ElthamRoy Eltham Posts: 2,996
    edited 2020-05-24 21:22
    The C version also does not include the INIT_8BIT_DAC_VALUE bit.

    Also, Ariba, I tried including propeller2.h in the C version and it doesn't appear to change anything... I was wrong, including propeller2.h makes it actually work with the sawtooth output.
  • AribaAriba Posts: 2,682
    ersmith wrote: »
    Ariba wrote: »
    Eric

    If I include the "propeller2.h" file, it works, but if this file is not included, there is no error message.

    Do you mean the code functions correctly if "propeller2.h" is included, but not if it is not included?

    Or do you mean that you're surprised that there are no errors because things like _wypin are not declared?

    Both of them.
    Something is missing if propeller2.h is not included, but the compiler will not say me what ;-)

    Andy
  • Roy ElthamRoy Eltham Posts: 2,996
    edited 2020-05-24 21:26
    maybe it's this bit of propeller2.h:
    /* smart pin controls */
    #ifdef __FLEXC__
    #define _wrpin(pin, val) __builtin_propeller_wrpin(val, pin)
    #define _wxpin(pin, val) __builtin_propeller_wxpin(val, pin)
    #define _wypin(pin, val) __builtin_propeller_wypin(val, pin)
    
    note the different param order.

    Yep that's it. If I swap the order of params on David's _wypin() call then it works without the propeller2.h
Sign In or Register to comment.