I’m porting an application from a Broadcom IOT system (WICED) to the BW13. It needs to playback low quality audio data (8 bit 11 khz sample rate.)
On the Broadcom solution I am using an STM32F205 micro, with:
- audio data saved as unsigned 8 bit values, stored in flash memory
- a simple DAC using PWM at 62 KHz through a first order low pass filter
- FreeRTOS task reads flash data into alternating buffers (ping-pong style)
- timer interrupt at 11 khz reads the current sample and sets the PWM duty cycle equal to the value from the flash (0 = 0% duty cycle, 255 = 100% duty cycle.)
On the STM32 this resulted in reasonable audio quality.
After porting to the BW13, its working but the audio quality is much worse. It has a lot of noise and it sounds terrible.
The code is quite simple.
Period set once at boot using pwmout_period_us(&audio_pwm_out, (int)((1.0f / PWM_DAC_FREQ) * 1000000.0f));
11 kHz timer interrupt set the sample to DAC using:
// set duty, converting 0…255 to 0.0 to 1.0
pwmout_write(&audio_pwm_out, (float)(dac_value / 255.0f));
I captured the resulting line level output after the low pas filter using a PC line input and audio recording software. The STM32 has the expected waveform. The BW13 has the same basic shape but it clearly has much sharper edges and transients and is not smooth like the STM32.
It also has some “spikes”; places where the analog value very suddenly jumps to a large negative or positive value briefly before returning to the correct value.
Does anyone have any suggestions on how to fix this? I think it might be something to do with how the timer or current compare cycle is handled when the duty cycle is adjusted on the fly.
I did try enabling and disabling the ARR Preload Config, but it had no effect.
That is an interesting use of PWM, but I would like to confirm that you are using a BW13 module? I am unfamiliar with this module name, can you provide more details?
I do not have any experience with using the PWM in this way, but it looks like it may make sense to capture and compare the PWM output without the low pass filter.
The BW16’s PWM peripheral is probably not as advanced as a STM32F205, you might be right about additional noise when the duty cycle is changed.
Hence my recommendation to directly compare the PWM output. 62KHz PWM can be easily captured by cheap logic analyzers, such as Saleae clones using a Cypress chip that can capture up to 24MHz, and analyzed using Sigrok or Saleae software. You could toggle another GPIO pin when changing the duty cycle as a trigger, and capture off that.
I do have a Saleae analyser and had already been looking at waveforms.
I did some test code that changed the duty cycle every 92 usec, changing it by 1% at a time.
It’s clear from looking at the timing that there is not enough resolution in the PWM.
Values under 7.5% all show as 0. 7.5% to around 15% were all the same.
I need 8 bit resolution for the PWM, but I’m getting much less than this.
The timers in the STM32 are quite similar to this chip, except that its a 32 bit timer.
I can’t find much detailed info on the timer module and its registers.
From code it looks like its running at 40 MHz, with a prescaler of 1.
For 16 bit that’s about 610 Hz per bit. To get 8 bit it would need to be at least 155 kHz, which is too high for my filter.
I tried changing the prescaler in the MBed API code, but so far I can’t make it any better.
I was able to fix this. Two issues:
- PWM resolution. Optimised the pre-scaler value to improve the resolution
- Errors in audio data on flash memory, caused by page write overflow