Multi-PWM-Output Object for Audio Library?

Status
Not open for further replies.

syso2342

Well-known member
I am trying to make a multi-output (let's say 6 outputs) object for the Audio Library to allow output
of PWM control signals for analog components like Filters etc. (with filtering the PWM of course).

Paul, did you work on some Object like that? Could your PWMOutput object be modified to do that?
 
Last edited:
Paul, did you work on some Object like that?

No, but I've thought about working on it someday. But I've dreamed of doing a LOT of stuff... far more than I'll ever be able to do.

Could your PWMOutput object be modified to do that?

Well, yes, for some definition of "modified". You'd probably also grab code from the I2S object, for dealing with more than 1 input stream and writing them into the DMA buffer.
 
No, but I've thought about working on it someday. But I've dreamed of doing a LOT of stuff... far more than I'll ever be able to do.



Well, yes, for some definition of "modified". You'd probably also grab code from the I2S object, for dealing with more than 1 input stream and writing them into the DMA buffer.

Ok, can you provide a little snippet on how to write to multiple PWM outputs with DMA?
I will take care of writing the code for the Object then.
 
The basic idea is the update() function takes the 128 audio samples from incoming blocks and copies them to a buffer in the order they'll need to be written to the PWM hardware.

Then the ISR does a DMA transfer with that buffer as the source and the PWM registers as the destination. I believe the PWM registers have control/status registers between them, so you'll need to configure the DMA to increment more than 4 bytes as it does each write, so it ends up writing the audio onto the DMA registers.

The existing PWM code actually does something similar, where it puts PWM output for both pins 3 and 4, for the MSB and LSB of the data. In theory, both can be combined with resistors for 16 bit output. In practice, even 0.1% matched resistors give only about 10-11 bits, and the resistors are in series with the output impedance (approx 16 to 20 ohms) of the transistors in the chip. But the code is already there to output 2 PWM streams, so a first easy step would be converting the already-working DMA code to output stereo instead of 2 streams computed from a single input.

There's also a minor issue of the DMA trigger happening by the compare match that generates the PWM, which means you can't write a zero (no match ever happens) and you can't write numbers very close to the max (not enough time for the trigger cause another transfer before the next PWM waveform). Look at the PWM code, which converts the audio to a limited range of the PWM duty cycle. The range can be expanded a bit, as I was quite conservative for a 1.0 release, but beware zero and triggering the DMA too close to the end of the cycle.

I'm currently working on another really important project, so I can't get directly involved in the coding at this time. Hopefully this message will help get you started?
 
If you get stereo working, which should be fairly easy, a simple way to get to 4 channels would be to copy the entire object for the FTM2 timer. Then you'd get output on pins 25 & 32 (on the bottom side) and of course pins 3 & 4.
 
If I see correctly, if I want to relocate it to pins A8 A9 from 3&4, i need to relocate the FTM to another FTM and change:
CORE_PIN3_CONFIG = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE;
CORE_PIN4_CONFIG = PORT_PCR_MUX(3) | PORT_PCR_DSE | PORT_PCR_SRE;
to
CORE_PIN23_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
CORE_PIN22_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;

etc?

For example:
//Serial.println("AudioPwmOutput constructor");
block_1st = NULL;
FTM0_SC = 0;
FTM0_CNT = 0;
FTM0_MOD = 543;
FTM0_C0SC = 0x69; // send DMA request on match
FTM0_C1SC = 0x28;
FTM0_SC = FTM_SC_CLKS(1) | FTM_SC_PS(0);
CORE_PIN23_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
CORE_PIN22_CONFIG = PORT_PCR_MUX(4) | PORT_PCR_DSE | PORT_PCR_SRE;
FTM0_C0V = 120; // range 120 to 375
FTM0_C1V = 0; // range 0 to 255
for (int i=0; i<256; i+=2) {
pwm_dma_buffer = 120; // zero must not be used
pwm_dma_buffer[i+1] = 0;
}
SIM_SCGC7 |= SIM_SCGC7_DMA;
SIM_SCGC6 |= SIM_SCGC6_DMAMUX;
DMA_CR = 0;
DMA_TCD_SADDR(AUDIO_OUT_PWM_DMA_CHANNEL) = pwm_dma_buffer;
DMA_TCD_SOFF(AUDIO_OUT_PWM_DMA_CHANNEL) = 4;
DMA_TCD_ATTR(AUDIO_OUT_PWM_DMA_CHANNEL) = DMA_TCD_ATTR_SSIZE(2)
| DMA_TCD_ATTR_DSIZE(2) | DMA_TCD_ATTR_DMOD(4);
DMA_TCD_NBYTES_MLNO(AUDIO_OUT_PWM_DMA_CHANNEL) = 8;
DMA_TCD_SLAST(AUDIO_OUT_PWM_DMA_CHANNEL) = -sizeof(pwm_dma_buffer);
DMA_TCD_DADDR(AUDIO_OUT_PWM_DMA_CHANNEL) = &FTM0_C0V;
DMA_TCD_DOFF(AUDIO_OUT_PWM_DMA_CHANNEL) = 8;
DMA_TCD_CITER_ELINKNO(AUDIO_OUT_PWM_DMA_CHANNEL) = sizeof(pwm_dma_buffer) / 8;
DMA_TCD_DLASTSGA(AUDIO_OUT_PWM_DMA_CHANNEL) = 0;
DMA_TCD_BITER_ELINKNO(AUDIO_OUT_PWM_DMA_CHANNEL) = sizeof(pwm_dma_buffer) / 8;
DMA_TCD_CSR(AUDIO_OUT_PWM_DMA_CHANNEL) = DMA_TCD_CSR_INTHALF | DMA_TCD_CSR_INTMAJOR;
DMAMUX0_CHCFG(AUDIO_OUT_PWM_DMA_CHANNEL) = DMAMUX_DISABLE;
DMAMUX0_CHCFG(AUDIO_OUT_PWM_DMA_CHANNEL) = DMAMUX_SOURCE_FTM0_CH0 | DMAMUX_ENABLE;
DMA_SERQ = AUDIO_OUT_PWM_DMA_CHANNEL;
update_responsibility = update_setup();
NVIC_ENABLE_IRQ(IRQ_DMA_CH(AUDIO_OUT_PWM_DMA_CHANNEL));
 
Last edited:
I still have trouble getting it work with other PWM pins. I am changing to the corresponding timer and
mux, but I get only garbage.
 
Status
Not open for further replies.
Back
Top