Voice changer

What are you developing?
User avatar
RogerClark
Posts: 6673
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Voice changer

Post by RogerClark » Sun Jun 12, 2016 10:25 pm

I hadn't realised that 35khz was the minimum sample rate.

But I think it should be OK, it just gives far less time to process the audio when in the Fourier domain. So rather than around 200ms, there would only be around 30mS

Input FFT takes 2mS for 1024 points. Output FFT will take about the same.

So that leaves around 25mS for processing 1024 points (512 pairs)

Which sounds like it should be enough to do some processing.

But, there is always a chance I overlooked something which kills the whole idea

User avatar
ddrown
Posts: 133
Joined: Sat Jan 09, 2016 4:49 am

Re: Voice changer

Post by ddrown » Sun Jun 12, 2016 10:33 pm

racemaniac wrote:
Pito wrote:Again:
When you sample ADC into the ping buffer, you need 204.8ms to fill it full (5KHz sampling rate, 1024samples).
How do you plan getting the sampling rate down to 5Khz?
without slowing the clock of the processor, i don't think it can be done. The code i gave is the slowest i know how to get it, and is about 35khz sampling rate, didn't find any other dividers or so i could use to get it even slower.
If you only need a sample rate of 5KHz and you're sampling at 35KHz, you can use a 7x Decimation filter.

User avatar
RogerClark
Posts: 6673
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Voice changer

Post by RogerClark » Sun Jun 12, 2016 10:46 pm

Ok about decimation.

I think it may be best to try to filter extenally, e.g. just use a normal R C filter, at perhaps 5kHz, so that any signals at 17khz would be so small they would be inaudible

User avatar
RogerClark
Posts: 6673
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Voice changer

Post by RogerClark » Mon Jun 13, 2016 5:48 am

racemaniac wrote:
Pito wrote:Again:
When you sample ADC into the ping buffer, you need 204.8ms to fill it full (5KHz sampling rate, 1024samples).
How do you plan getting the sampling rate down to 5Khz?
without slowing the clock of the processor, i don't think it can be done. The code i gave is the slowest i know how to get it, and is about 35khz sampling rate, didn't find any other dividers or so i could use to get it even slower.
Looking at the setup code for the ADC its set to

Code: Select all

ADC_PRE_PCLK2_DIV_6
in boards_setup.c

but there is a ADC_PRE_PCLK2_DIV_8, so it looks like it can be slowed a little to around 26khz


I've taken a look at the FFT, and it is taking just over 2mS for a 1024 point FFT (512 pairs), so thats good news.

So I'm now looking at how I can DMA out to the DAC on my F103V


@racemaniac

You've done a lot of work with the DMA... Do you know if the same DMA controller can be used or 2 completely different things at the same time.

i.e there only DMA controller 1 and 2.

At the moment to save complication, I thought I'd use DMA controller 2 for the DMA to the DAC, but as the ADC and the DAC are on different DMA channels, perhaps the same controller e.g. DMA 1 could be used for both.

But I suppose if there are 2 DMA controllers, it doesnt do any harm to use both of them, as DMA2 would otherwise not be doing anything at all.

Anyway. I'm still a long way from DMA'ing to the DAC, but I'm slowly working out how to configure the regs to make this happen ;-)

racemaniac
Posts: 432
Joined: Sat Nov 07, 2015 9:09 am

Re: Voice changer

Post by racemaniac » Mon Jun 13, 2016 6:09 am

RogerClark wrote:
racemaniac wrote:
Pito wrote:Again:
When you sample ADC into the ping buffer, you need 204.8ms to fill it full (5KHz sampling rate, 1024samples).
How do you plan getting the sampling rate down to 5Khz?
without slowing the clock of the processor, i don't think it can be done. The code i gave is the slowest i know how to get it, and is about 35khz sampling rate, didn't find any other dividers or so i could use to get it even slower.
Looking at the setup code for the ADC its set to

Code: Select all

ADC_PRE_PCLK2_DIV_6
in boards_setup.c

but there is a ADC_PRE_PCLK2_DIV_8, so it looks like it can be slowed a little to around 26khz


I've taken a look at the FFT, and it is taking just over 2mS for a 1024 point FFT (512 pairs), so thats good news.

So I'm now looking at how I can DMA out to the DAC on my F103V


@racemaniac

You've done a lot of work with the DMA... Do you know if the same DMA controller can be used or 2 completely different things at the same time.

i.e there only DMA controller 1 and 2.

At the moment to save complication, I thought I'd use DMA controller 2 for the DMA to the DAC, but as the ADC and the DAC are on different DMA channels, perhaps the same controller e.g. DMA 1 could be used for both.

But I suppose if there are 2 DMA controllers, it doesnt do any harm to use both of them, as DMA2 would otherwise not be doing anything at all.

Anyway. I'm still a long way from DMA'ing to the DAC, but I'm slowly working out how to configure the regs to make this happen ;-)
there are 2 dma controllers (but only one on the maple mini), and DMA1 has 7 channels, and DMA2 i think has 5 channels.
You can check the documentation about what is mapped to which channel, but all channels can be used simultaneously.
The total bandwith is pretty high (and this ADC conversion is hardly using any), so you can even still use DMA1, but just another channel. Check the documentation for which controller & channel you need for what you want to control, and it's likely on another channel (and maybe even controller) than the ADC (DMA1, channel1)

User avatar
Pito
Posts: 1498
Joined: Sat Mar 26, 2016 3:26 pm
Location: Rapa Nui

Re: Voice changer

Post by Pito » Mon Jun 13, 2016 7:05 am

I am not an expert for ADC and DMA with STM32, but I am quite surprised you cannot set sampling rate to say 5kHz (or 100Hz or 0.5Hz).
Usually you do not set ADC sampling rate by the ADC clock dividers, but via ADC conversion trigger, which could be, for example, driven by a timer's output pulse/edge/period/PWM, etc..

Example> Set ADC clock to 13 clock cycles (as fast as you can for given ADC precision), set timerX to trigger the ADC conversion every 200usecs (5KHz Sampling rate), and do direct the ADC output via DMA to a buffer called Buffer_Ping[1024]. A nice to have would be if the DMA can signal the Buffer_PIng is full (in order to switch to Pong).
The same story with DAC (shoot data from Buffer_Pong out through DAC via DMA, the sampling is given by timerY set to ie 200usecs).

A typical setup I've found somewhere:
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; <<< here you set the ADC Sampling rate as you wish :) (or better to say as the Timer1 "allows") :)

Code: Select all

 /* TIM1 configuration ------------------------------------------------------*/ 
  /* Time Base configuration */
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 
  TIM_TimeBaseStructure.TIM_Period = 0xFF;          
  TIM_TimeBaseStructure.TIM_Prescaler = 0x4;       
  TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;    
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
  /* TIM1 channel1 configuration in PWM mode */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;                
  TIM_OCInitStructure.TIM_Pulse = 0x7F; 
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;         
  TIM_OC1Init(TIM1, &TIM_OCInitStructure);

  /* DMA1 Channel1 Configuration ----------------------------------------------*/
  DMA_DeInit(DMA1_Channel1);
  DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_RegularConvertedValueTab;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  DMA_InitStructure.DMA_BufferSize = 32;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel1, &DMA_InitStructure);
  
  /* Enable DMA1 channel1 */
  DMA_Cmd(DMA1_Channel1, ENABLE);

  /* ADC1 configuration ------------------------------------------------------*/
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfChannel = 1;
  ADC_Init(ADC1, &ADC_InitStructure);

  /* ADC1 regular channel14 configuration */ 
  ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 1, ADC_SampleTime_13Cycles5);
Last edited by Pito on Mon Jun 13, 2016 7:28 am, edited 1 time in total.
Pukao Hats Cleaning Services Ltd.

racemaniac
Posts: 432
Joined: Sat Nov 07, 2015 9:09 am

Re: Voice changer

Post by racemaniac » Mon Jun 13, 2016 7:28 am

Pito wrote:I am not an expert for ADC and DMA with STM32, but I am quite surprised you cannot set sampling rate to say 5kHz (or 100Hz or 0.5Hz).
Usually you do not set ADC sampling rate by the ADC clock dividers, but via ADC conversion trigger, which could be, for example, driven by a timer's output pulse/edge/period/PWM, etc..
Example> Set ADC clock to 10MHz, set timerX to trigger the ADC conversion every 200usecs (5KHz), and do direct the ADC output via DMA to a buffer called Buffer_Ping[1024]. A nice to have would be if the DMA can signal the Buffer_PIng is full (in order to switch to Pong).
The same story with DAC (shoot data from Buffer_Pong out through DAC via DMA, the sampling is given by timerY set to ie 200usecs).
A typical setup I've found somewhere:
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1; <<< here you set the ADC Sampling rate as you wish :) (or better as the Timer1 allows) :)

Code: Select all

 /* TIM1 configuration ------------------------------------------------------*/ 
  /* Time Base configuration */
  TIM_TimeBaseStructInit(&TIM_TimeBaseStructure); 
  TIM_TimeBaseStructure.TIM_Period = 0xFF;          
  TIM_TimeBaseStructure.TIM_Prescaler = 0x4;       
  TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;    
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  
  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
  /* TIM1 channel1 configuration in PWM mode */
  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; 
  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;                
  TIM_OCInitStructure.TIM_Pulse = 0x7F; 
  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;         
  TIM_OC1Init(TIM1, &TIM_OCInitStructure);

  /* DMA1 Channel1 Configuration ----------------------------------------------*/
  DMA_DeInit(DMA1_Channel1);
  DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADC_RegularConvertedValueTab;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
  DMA_InitStructure.DMA_BufferSize = 32;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
  DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  DMA_InitStructure.DMA_Priority = DMA_Priority_High;
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
  DMA_Init(DMA1_Channel1, &DMA_InitStructure);
  
  /* Enable DMA1 channel1 */
  DMA_Cmd(DMA1_Channel1, ENABLE);

  /* ADC1 configuration ------------------------------------------------------*/
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC1;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfChannel = 1;
  ADC_Init(ADC1, &ADC_InitStructure);

  /* ADC1 regular channel14 configuration */ 
  ADC_RegularChannelConfig(ADC1, ADC_Channel_14, 1, ADC_SampleTime_13Cycles5);
That could indeed be a solution :)
I know a lot about the DMA, but not much about the ADC yet. Atm i configured the ADC to go as slow as possible in continuous mode, but if you can make it triggered, and have a slower trigger, that would indeed be perfect :). Since i could use the code of the oscilloscope project, i didn't do much research on the ADC, so i don't know all of its features yet :).
We'll have to give this triggered mode a try :)

Btw, if you want to DMA to the DAC, won't we need something similar? With the SPI&I2C ports, that was one of my annoyances with DMA: it just sends data as fast as it can, and there doesn't seem to be a way to slow it down. For ADC there's the triggers, do the DACS have something similar?

User avatar
Pito
Posts: 1498
Joined: Sat Mar 26, 2016 3:26 pm
Location: Rapa Nui

Re: Voice changer

Post by Pito » Mon Jun 13, 2016 7:35 am

Yes, exactly as you wrote.
"ADC sampling speed/rate" is one thing, and the number of CPU/AHB clocks (conversion speed) for a single ADC conversion is an another one.
They are not related (until some limits).
So I set ADC to full conversion speed (ie 15MHz for 12bit, it depends on MCU) and the Sampling rate I set via TimerX pwm to any value I want.
The same with DAC (there must be a way how to do it).

For example you may have ADC (set to 15MHz conversion speed) working at 4KHz Sampling rate, doing DSP, and outputting the signal via DAC with Sampling speed (DAC sampling speed) of 6KHz (it may sound like Mickey Mouse) :P
Last edited by Pito on Mon Jun 13, 2016 7:43 am, edited 1 time in total.
Pukao Hats Cleaning Services Ltd.

User avatar
RogerClark
Posts: 6673
Joined: Mon Apr 27, 2015 10:36 am
Location: Melbourne, Australia
Contact:

Re: Voice changer

Post by RogerClark » Mon Jun 13, 2016 7:41 am

@racemaniac

It must be possible to control the rate at which DMA data is sent to the DAC,

STM have an application note on the DAC, but its really just an overview showing the capabilities of the DAC

http://www.st.com/content/ccc/resource/ ... 259245.pdf

I suspect that the DMA controller will need to be triggered by a Timer, and hence the Timer freq sets the sample rate.

Or in this case, if possible, perhaps the best method is to use the ADC (conversion complete) to trigger the DAC DMA; as both the input an output are supposed to be running at precisely the same sample rate

Or perhaps run both the ADC and the DAC on the same Timer

Edit. I found some OpenLibCM3 code that uses the timer to control the DMA for the DAC

https://github.com/libopencm3/libopencm ... /dac-dma.c

racemaniac
Posts: 432
Joined: Sat Nov 07, 2015 9:09 am

Re: Voice changer

Post by racemaniac » Mon Jun 13, 2016 7:46 am

RogerClark wrote:@racemaniac

It must be possible to control the rate at which DMA data is sent to the DAC,

STM have an application note on the DAC, but its really just an overview showing the capabilities of the DAC

http://www.st.com/content/ccc/resource/ ... 259245.pdf

I suspect that the DMA controller will need to be triggered by a Timer, and hence the Timer freq sets the sample rate.

Or in this case, if possible, perhaps the best method is to use the ADC (conversion complete) to trigger the DAC DMA; as both the input an output are supposed to be running at precisely the same sample rate

Or perhaps run both the ADC and the DAC on the same Timer

Edit. I found some OpenLibCM3 code that uses the timer to control the DMA for the DAC

https://github.com/libopencm3/libopencm ... /dac-dma.c
afaik, the dma is always triggered by a dma request by the peripheral, and so i'd expect it to be something on the side of the peripheral that will have to control when this dma request is sent. I don't think you can substitute it with another signal. But we'll have some reading to do to figure this out :D.

Post Reply

Who is online

Users browsing this forum: No registered users and 4 guests