C++ STM32F4 Discovery ADC1 per Timer triggern und mit DMA auslesen

Status
Für weitere Antworten geschlossen.

chillking

Lieutenant
Registriert
Juni 2010
Beiträge
926
Hi zusammen,

ich hoffe mir kann hier jemand helfen!

Ich versuche per DMA die Werte des ADC1 einzulesen und abzuspeichern. Dabei soll der ADC1 per Timer2 getriggert werden (ich möchte eine bestimmte Abtastfrequenz einstellen können).
Zusätzlich soll double-buffering konfiguriert werden.

Folgendes hab ich:
zwei Arrays
Code:
#define ADC_BUFFER_SIZE 1000
uint16_t adc_buffer_0[ADC_BUFFER_SIZE]={0};
uint16_t adc_buffer_1[ADC_BUFFER_SIZE]={0};

meine setup-Funktion für den ADC1, TIM2, DMA2:
Code:
void setupADC1()
{
	ADC_InitTypeDef ADC_InitStructure;
	ADC_CommonInitTypeDef ADC_CommonInitStructure;
	DMA_InitTypeDef DMA_InitStructure;
	GPIO_InitTypeDef GPIO_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	TIM_TimeBaseInitTypeDef TIM_TimeBase_InitStructure;
	TIM_OCInitTypeDef OC_InitDef;

	// Clocks für DMA2, ADC1, TIM2, GPIOC->PC3
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOC, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);


	// DMA2 Stream0 globaler Interrupt-Channel konfigurieren
	NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);


	// Timer2 globaler Interrupt konfigurieren
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);


	// DMA2 Stream0 channel0 --> ADC1 	(alternativ DMA2 Stream4 Channel0)
	DMA_InitStructure.DMA_Channel = DMA_Channel_0;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC1_DR_ADDRESS;
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&adc_buffer_0;
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
	DMA_InitStructure.DMA_BufferSize = ADC_BUFFER_SIZE;
	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_Circular;
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;
	DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
	DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_HalfFull;
	DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
	DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;
	DMA_Init(DMA2_Stream0, &DMA_InitStructure);

	//DMA2 Stream0 DoubleBuffer
	DMA_DoubleBufferModeConfig(DMA2_Stream0, (uint32_t)&adc_buffer_1, DMA_Memory_0);
	DMA_DoubleBufferModeCmd(DMA2_Stream0, ENABLE);
	//<--muss evtl nach DMA-Start?!??

	//DMA2 Stream0 starten
	DMA_Cmd(DMA2_Stream0, ENABLE);

	//DMA bestimmte Interrupts anschalten (Transmission complete)
	//DMA_ITConfig(DMA2_Stream0, DMA_IT_TC | DMA_IT_HT, ENABLE);
	DMA_ITConfig(DMA2_Stream0, DMA2_Stream0_IRQn, ENABLE);	//alle Interrupts an


	// ADC1 Channel13 pin (PC3)
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
	GPIO_Init(GPIOC, &GPIO_InitStructure);


	//Timer2 als Trigger des ADC1
	//RCC_PCLK1Config (RCC_HCLK_Div1);
	TIM_TimeBaseStructInit(&TIM_TimeBase_InitStructure);	//auf standard stellen
	TIM_TimeBase_InitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBase_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBase_InitStructure.TIM_Period = 1750-1; //-->84MHz bzw der Takt von TIM2 wird vorgeteilt auf 21MHz (glaub ich)
	TIM_TimeBase_InitStructure.TIM_Prescaler = 1000-1;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBase_InitStructure);

	TIM_SelectOutputTrigger(TIM2, TIM_TRGOSource_Update);

	//Timer noch nicht starten
	//TIM_Cmd(TIM2, ENABLE);


	// ADC Common Init
	ADC_CommonInitStructure.ADC_Mode = ADC_Mode_Independent;
	ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
	ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_Disabled;
	ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_5Cycles;
	ADC_CommonInit(&ADC_CommonInitStructure);

	// ADC1
	ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
	ADC_InitStructure.ADC_ScanConvMode = DISABLE;
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
	ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T2_TRGO;
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
	ADC_InitStructure.ADC_NbrOfConversion = 1;
	ADC_Init(ADC1, &ADC_InitStructure);

	// ADC1 Channel13
	ADC_RegularChannelConfig(ADC1, ADC_Channel_13, 1, ADC_SampleTime_3Cycles);	//evtl auch ADC_SampleTime_15Cycles?!?


	// Alles einschalten

	// DMA Request nachdem ADC1 fertig
	ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);

	// ADC1 DMA anschalten
	ADC_DMACmd(ADC1, ENABLE);

	// ADC1 anschalten
	ADC_Cmd(ADC1, ENABLE);

	//------------------------------------------------brauch ich das?!?
	//ADC_SoftwareStartConv(ADC1);

	//ich glaube ich muss nur den Timer starten
	TIM_Cmd(TIM2, ENABLE);
}

Mein Interrupt der DMA:
Code:
//DMA2 Stream0 Channel0 --> ADC1
extern "C" void DMA2_Stream0_IRQHandler(void)
{
	GPIO_SetBits(GPIOD,GPIO_Pin_13);
	
	//Übertragung komplett fertig (ein Buffer vollständig beschrieben)
	if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) != RESET)
	{
		//TIM_Cmd(TIM2, DISABLE);

		//Bit zurücksetzen
		DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_TCIF0);

		//Flag setzten, welche Buffer jetzt bearbeitet werden darf
		if (flag_adc_buffer)
			flag_adc_buffer=0;
		else
			flag_adc_buffer=1;

		flag_adc_copy_ready=1;	//ADC ist fertig, buffer kann bearbeitet werden

		//Software-Interrupt für Filter und Buffer kopieren starten
		EXTI_GenerateSWInterrupt(EXTI_Line0);

		//-->und weiter gehts
		//TIM_Cmd(TIM2, ENABLE);
	}
	if (DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0) != RESET)
	{
		//Bit zurücksetzen
		DMA_ClearITPendingBit(DMA2_Stream0, DMA_IT_HTIF0);
	}
	GPIO_ResetBits(GPIOD,GPIO_Pin_13);
}


Habe im Interrupt zu Beginn ein GPIO auf SET und am Ende auf RESET um am Oszi den Verlauf zu sehen. Und hier kommt mein Problem:
Es ist vollkommen egal, wie ich den Timer einstelle (Prescaler und Period), am Oszi lese ich immer einen Takt von 1,8kHz ab, da der Interrupt nur alle 1000 Werte kommt arbeitet der ADC also mit 1,8MHz. Ziel sind 48kHz.
Ich komm einfach nicht darauf, was ich bei der Konfiguration falsch gemacht oder vergessen habe.

Hat jemand von euch einen Tipp? Wäre top, bin ein wenig am Verzweifeln.

Danke schon mal!
Markus
 
Status
Für weitere Antworten geschlossen.
Zurück
Oben