cancel
Showing results for 
Search instead for 
Did you mean: 

Sinewave using Dual DAC basically doesn't work

antoine
Associate II
Posted on June 27, 2016 at 19:01

Hi.

I am trying to output a sine using the DAC of my STM32 F334R8. I spent days trying to do so with the example in the SPL. This example uses a button (which is not present on my board) to switch between, in one hand, a sine and an escalator waveform (using respectively channel 1 and 2), and in other hand noise and triangle waveform (using both channels too). I thought it would be ok if I just isolated the waveform I wanted, but it turned out it was not. What I did just now is to use a code kindly provided on [DEAD LINK /public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/Flat.aspx?RootFolder=/public/STe2ecommunities/mcu/Lists/cortex_mx_stm32/sawtooth&FolderCTID=0x01200200770978C69A1141439FE559EB459D7580009C4E14902C3CDE46A77F0FFD06506F5B&currentviews=97]an older thread by clive1 to generate a sinewave using both channels. It was not the same device, so I adjusted it the best I could, and it almost works, I think. The problem is, my amplitude is ridiculously low: circa 3 mV. When I do the proper adjustments on the oscilloscope, it almost looks like a very ugly sine, but I think this is mainly noise. Honestly I don't know what I could do. My goal is to have a sine with an amplitude of 3.3 V, and a frequency of, let's say 1 or 10 kHz. I have tried many things, many codes, tried to get familiar with the device by using some easier examples, but eventually I'm always stuck there. Here is the code I came up with, if you see some huge mistake or anything please point them out:


#include ''main.h''


const
uint16_t Sine12bit[32] = {

2047, 2447, 2831, 3185,

3498, 3750, 3939, 4056,

4095, 4056, 3939, 3750,

3495, 3185, 2831, 2447,

2047, 1647, 1263, 909,

599, 344, 155, 38,

0, 38, 155, 344,

599, 909, 1263, 1647};


uint16_t SineTable[64];


/**************************************************************************/


void
RCC_Config(
void
)

{

/* DMA1 clock and GPIOA clock enable (to be used with DAC) */

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);



/* DAC and TIM6 Peripheral clock enable */

RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC | RCC_APB1Periph_TIM6, ENABLE);

}


/**************************************************************************/


void
GPIO_Config(
void
)

{

GPIO_InitTypeDef GPIO_InitStructure;


/* DAC channel 1 & 2 (DAC_OUT1 = PA.4)(DAC_OUT2 = PA.5) configuration */

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;

GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;

GPIO_Init(GPIOA, &GPIO_InitStructure);

}


/**************************************************************************/


void
TIM6_Config(
void
)

{

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

int
Period;


Period = ((SystemCoreClock / 2) / 200000); 
// 200 KHz timebase, 6.25 KHz Sine


/* Time base configuration */

TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);

TIM_TimeBaseStructure.TIM_Period = Period - 1;

TIM_TimeBaseStructure.TIM_Prescaler = 0;

TIM_TimeBaseStructure.TIM_ClockDivision = 0;

TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);


/* TIM6 TRGO selection */

TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);


/* TIM6 enable counter */

TIM_Cmd(TIM6, ENABLE);

}


/**************************************************************************/


#define DAC_DHR12RD_ADDRESS 0x40007428 // Right Align 12-bit Dual


void
DAC_Config(
void
)

{

DAC_InitTypeDef DAC_InitStructure;

DMA_InitTypeDef DMA_InitStructure;


/* DAC channel 1 and 2 Configuration */

DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;

DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;

// DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Enable; //doesn't exist with standard libraries

DAC_InitStructure.DAC_Buffer_Switch = DAC_BufferSwitch_Enable;

DAC_Init(DAC1, DAC_Channel_1, &DAC_InitStructure);

DAC_Init(DAC1, DAC_Channel_2, &DAC_InitStructure);


/* DMA1_channel3 configuration **************************************/

DMA_DeInit(DMA1_Channel3);

DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12RD_ADDRESS;

DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&SineTable;

DMA_InitStructure.DMA_BufferSize = 32; 
// 16-bit x 64 / 2

DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; 
// 32-bit 16x2

DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;

DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;

DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;

DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;

DMA_InitStructure.DMA_Priority = DMA_Priority_High;

DMA_Init(DMA1_Channel3, &DMA_InitStructure);



/* Enable DMA1_Channel3 */

DMA_Cmd(DMA1_Channel3, ENABLE);


/* Enable DAC Channel 1 and 2 */

DAC_Cmd(DAC1, DAC_Channel_1, ENABLE);

DAC_Cmd(DAC1, DAC_Channel_2, ENABLE);


/*If I comment the two lines above, almost nothing change in the signal I see. I take it as an evidence this code doesn't work at all */


/* Enable DMA for DAC Channel1 */

DAC_DMACmd(DAC1, DAC_Channel_1, ENABLE);

}


/**************************************************************************/


int
main(
void
)

{

int
i, j;


j = 0;


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

{

SineTable[j++] = Sine12bit[i];

SineTable[j++] = Sine12bit[(i + 8) % 32]; 
// 90 degree phase shift on Channel 2

}


RCC_Config();


GPIO_Config();


TIM6_Config();


DAC_Config();


while
(1); 
// Do not exit

}

- i don't understand how the dual mode works: how does the DAC know it is supposed to outputs each case of the table alternatively in each channel? - What is the point of doing that, rather than using just one channel? - My stm32f30x_tim.h specifically says that the basic TIM_TimeBaseInitTypeDef; is '' used with all TIMx except for TIM6 and TIM7.'' So i changed it for TIM2, which doesn't work either. Besides, how am I supposed to use the Timer 6 and 7? How do they work? I only know they are basic timers dedicated to DAC, but nothing about how to use it, how to implement it. - If i have in fact the value 4095 in the table, I suppose I should see a max of 3.3 V. Why is it not the case? - How do i know if the table is even read? I'm not sure I know how to interprete correctly the debug mode. I have more or less the same result when I use the code from the example. I wonder if something could be wrong in the startup file or something like that. Thank you.
11 REPLIES 11
Posted on June 27, 2016 at 19:29

Posting source with line numbers makes a simple cut-n-paste difficult.

The two-channels used by the original example write a 32-bit value to a 32-bit register that describes two 16-bit samples written to the left/right (1/2) channels simultaneously. In much the way stereo sample would be found in a .WAV, and how the hardware pairs them together in this common register. The architecture is described in the reference manual.

This enables a single DMA operation, on a single DMA channel, to write both channels synchronously. Other methods using two DMA channels could be used, but this wastes both bandwidth and resources.

The signal level will depend on what load you are driving the signal into, and what output buffering you have chosen. I would expect 4095 to be close to +VREF(VDDA) on a pin that is only connected to a scope input.

The max sample rate on the F2/F4 is around 1MSps. 64 points to get 10 KHz (640 KSps) would be doable there.

0690X00000605Q3QAI.png

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on June 27, 2016 at 19:35

#define DAC_DHR12RD_ADDRESS (DAC_BASE + 0x20) // Right Align 12-bit Dual

Not +0x28, which is two 8-bit channels in the low order word.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
antoine
Associate II
Posted on June 27, 2016 at 20:28

Sorry for the line numbers, I thought it would make explanations easier.

Where can I adjust the load? I thought it would be proportional step between 0 and 3.3V, depending of my number of samples. I'm not I understand that correctly.

Also, i don't know how to handle the buffer. For what I know, it was only a matter of enable or disable it.

64 points allowing 10kHz sounds reasonnable to me too, however I just wanted to verify something on an older which actually works, an example on mbed that I don't use because it's pretty much a black box (can't see anything beside the main.cpp), and here is the thing: 

After several attempts, the best I could have is circa 3kHz. When I go higher it becomes weird (4kHz results in 4.5kHz, and 4.5kHz and higher just stuck at 5.1kHz).

So maybe these are two very different problems, but I thought maybe there could be a connection.

So I'm not sure how to achieve this amount of samples with this frequency height.

Also, you are right about the DHR address, so I changed it, but now, instead of having just noise, I have a triangular waveform, I don't know what I did. I changed nothing but the DHR address.

EDIT: The triangle's amplitude is more or less 120 mV and a frequency alternatively of 1.4kHz and 2.5kHz (more or less).

Thank you for your help.

Posted on June 27, 2016 at 20:55

Load is what you have attached externally to the STM32, into which it is driving the signal. Review Data Sheet for parameters.

The Data Sheet also specifies the max sample rate to be 1MSps, where CLoad is less than 50pF and RLoad is greater than 5KOhm

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on June 27, 2016 at 21:47

I don't have any F334 boards, this works on a STM32F3-DISCO, providing 3 KHz 3 Vpp Sine waves on PA4 and PA5

// STM32F3-Discovery Dual DAC 3 KHz Sine - sourcer32@gmail.com
//****************************************************************************
#include ''stm32f3_discovery.h''
const uint16_t Sine12bit[32] = {
2047, 2447, 2831, 3185,
3498, 3750, 3939, 4056,
4095, 4056, 3939, 3750,
3495, 3185, 2831, 2447,
2047, 1647, 1263, 909,
599, 344, 155, 38,
0, 38, 155, 344,
599, 909, 1263, 1647};
uint16_t SineTable[64];
//****************************************************************************
void RCC_Config(void)
{
/* DMA1 clock and GPIOA clock enable (to be used with DAC) */
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);
/* DAC and TIM6 Peripheral clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_DAC | RCC_APB1Periph_TIM6, ENABLE);
}
//****************************************************************************
void GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
/* DAC channel 1 & 2 (DAC_OUT1 = PA.4)(DAC_OUT2 = PA.5) configuration */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
//****************************************************************************
void TIM6_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
int Period;
Period = (SystemCoreClock / 96000); // 96 KHz timebase, 3 KHz Sine
/* Time base configuration */
TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
TIM_TimeBaseStructure.TIM_Period = Period - 1;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
/* TIM6 TRGO selection */
TIM_SelectOutputTrigger(TIM6, TIM_TRGOSource_Update);
/* TIM6 enable counter */
TIM_Cmd(TIM6, ENABLE);
}
//****************************************************************************
#define DAC_DHR12RD_ADDRESS 0x40007420 // Right Align 12-bit Dual (F3)
void DAC_Config(void)
{
DAC_InitTypeDef DAC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
/* DAC channel 1 and 2 Configuration */
DAC_InitStructure.DAC_Trigger = DAC_Trigger_T6_TRGO;
DAC_InitStructure.DAC_WaveGeneration = DAC_WaveGeneration_None;
DAC_InitStructure.DAC_LFSRUnmask_TriangleAmplitude = DAC_LFSRUnmask_Bits11_0;
DAC_InitStructure.DAC_OutputBuffer = DAC_OutputBuffer_Disable;
DAC_Init(DAC_Channel_1, &DAC_InitStructure);
DAC_Init(DAC_Channel_2, &DAC_InitStructure);
/* DMA1_channel3 configuration **************************************/
DMA_DeInit(DMA1_Channel3);
DMA_InitStructure.DMA_PeripheralBaseAddr = DAC_DHR12RD_ADDRESS;
DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)&SineTable;
DMA_InitStructure.DMA_BufferSize = 32; // 16-bit x 64 / 2
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word; // 32-bit 16x2
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_High;
DMA_Init(DMA1_Channel3, &DMA_InitStructure);
/*Remap TIM6/DAC1 DMA requests from DMA2 channel 3 to DMA1 channel 3 */
SYSCFG_DMAChannelRemapConfig(SYSCFG_DMARemap_TIM6DAC1, ENABLE);
/* Enable DMA1_Channel3 */
DMA_Cmd(DMA1_Channel3, ENABLE);
/* Enable DAC Channel 1 and 2 */
DAC_Cmd(DAC_Channel_1, ENABLE);
DAC_Cmd(DAC_Channel_2, ENABLE);
/* Enable DMA for DAC Channel1 */
DAC_DMACmd(DAC_Channel_1, ENABLE);
}
//****************************************************************************
int main(void)
{
int i, j;
j = 0;
for(i=0; i<32; i++)
{
SineTable[j++] = Sine12bit[i];
SineTable[j++] = Sine12bit[(i + 8) % 32]; // 90 degree phase shift on Channel 2
}
RCC_Config();
GPIO_Config();
TIM6_Config();
DAC_Config();
while(1); // Do not exit
}
//****************************************************************************
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t* file, uint32_t line)
{
/* User can add his own implementation to report the file name and line number,
ex: printf(''Wrong parameters value: file %s on line %d

'', file, line) */
/* Infinite loop */
while (1)
{
}
}
#endif
//****************************************************************************

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
antoine
Associate II
Posted on June 27, 2016 at 23:10

Thank you very much, clive!

It's getting late here, and I can't test the code right now (I don't have my device), but I looked at it.

Are you sure the line about remapping the DMA2 is mandatory since there is no DMA2 on the f334?

Isn't it safer to define the period as a hard value than to make it dependent of the system clock, which may vary?

Also, what about the config of the timer 6? Is it really to use it that way while the _tim.h states that the typedef struct is not fitted for tim6 and tim7?

Posted on June 28, 2016 at 01:57

I'm building for hardware I have and can test, ie F303 w/RM0316

For the F334 you'll need to use RM0364

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
antoine
Associate II
Posted on June 28, 2016 at 11:20

Hi Clive.

I just ran your code, and I must tell you that, in a very platonic way, I love you.

It works.

So: Thank you, very, very much.

I just still have a couple of questions to be sure I can manipulate it on my own:

- I used it with timer 2 at first, then timer 6 (although it is expressed that the typedef doesn't fit for timer 6), and I don't see any difference. Is there actually any, (if not, what is the point)? Or is it just because my program is at this moment too simple to impact the choice of the timer?

- Why do I need the PA.05 (DAC1_OUT2) since it seems that I have a good sinewave on PA.04? The waveform I have on channel 2 is not a sine, it's... I don't really know. I took a picture of the oscilloscope to show it to you.

0690X000006039UQAQ.jpg

- What I understood at first was that the sine waveform (the values from the table) was separated between the two channels. Looking at the signal on channel 1, I seem to misunderstand. I only have something on channel 2 when I enable the buffer. Flatline when it's disabled. Can you explain (briefly) how does it work?

- I see the importance of that line:  

SYSCFG_DMAChannelRemapConfig(SYSCFG_DMARemap_TIM6DAC1, ENABLE);

because I pretty much have flatline/noise when I comment it, but I don't get why does it work? When I talk about timer 2 everywhere else in the code (init, config and all) except here, it still does work. Also, the comment of the function says it's for remapping from DMA2 to DMA1, but I'm almost sure there is no DMA2 on F334 (I saw no mention of it anywhere). How is it possible?

Thank you again, I know this is a lot of questions, but I can feel I'm almost there!

Posted on June 28, 2016 at 17:04

The original example seeks to demonstrate the output of two sine waves a) to provide an example of left/right audio channels, and b) the binding of the DACs using a single DMA, single register, and pacing the sample rate with a timer. The two waveforms are phase shifted to permit observation/validation on a scope. The 90 degree shift equates to sine/cosine type output. I tend to favour clear and concise examples that hit the target with a high degree of accuracy.

This could easily be retasked to doing just a single channel.

I'm going to assume you have a

http://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-eval-tools/stm32-mcu-eval-tools/stm32-mcu-nucleo/nucleo-f334r8.html

, in this case PA5 is connected to an LED, and this will present significant load to a DAC output. The DISCO board I'm testing doesn't have anything other than scope probes attached.

http://www.st.com/content/ccc/resource/technical/document/user_manual/98/2e/fa/4b/e0/82/43/b7/DM00105823.pdf/files/DM00105823.pdf/jcr:content/translations/en.DM00105823.pdf

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..