Простой пример АЦП+ПДП (ADC+DMA) для STM32F4-Discovery

29 Апреля 2013 К комментариям

Привожу простой пример совместной работы АЦП+ПДП для платы STM32F4-Discovery.

Задача (источник http://kazus.ru/forums/showthread.php?t=104295):

  1. По событию от таймера запускать преобразования регулярного канала АЦП.
  2. По окончании преобразования АЦП, контроллер ПДП должен скопировать данные в ОЗУ.

Запускать преобразования АЦП в STM32F4 можно от нескольких источников, для примера я выбрал Таймер 3:

image Запросы от АЦП “подключены” к нулевому каналу нулевого потока контроллера ПДП:

image

Примечание: Возможно “поток” не очень хороший перевод в данном случае.

Исходный код:

/*
 * File: main.c
 * Date: 29.04.2013
 * Denis Zheleznyakov http://ziblog.ru
 */

#include "main.h"

uint16_t adc_buffer[8];

//------------------------------------------------------------------------------

inline static void gpio(void)
{
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIODEN;

	// светодиод

	PIN_CONFIGURATION(PIN_LED);

	// кнопка

	PIN_CONFIGURATION(PIN_BUTTON);

	// АЦП

	PIN_CONFIGURATION(PIN_ADC_CH1);
}

//------------------------------------------------------------------------------

inline static void adc(void)
{
	ADC_InitTypeDef ADC_InitStructure;
	ADC_CommonInitTypeDef ADC_CommonInitStructure;

	// разрешаем тактирование

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

	//

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

	// выбор канала

	ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_3Cycles);

	// базовая настройка

	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);

	//

	ADC_DiscModeCmd(ADC1, DISABLE);
	ADC_EOCOnEachRegularChannelCmd(ADC1, ENABLE);
	ADC_DMARequestAfterLastTransferCmd(ADC1, ENABLE);
	ADC_DMACmd(ADC1, ENABLE);

	// включаем АЦП

	ADC_Cmd(ADC1, ENABLE);
}

//------------------------------------------------------------------------------

inline static void dma(void)
{
	DMA_InitTypeDef DMA_InitStructure;

	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2, ENABLE);

	// АЦП

	DMA_DeInit(DMA2_Stream0);
	DMA_InitStructure.DMA_Channel = DMA_Channel_0;
	DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &ADC1->DR;
	DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t) &adc_buffer[0];
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
	DMA_InitStructure.DMA_BufferSize = ARRAY_LENGHT(adc_buffer);
	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);

	DMA_Cmd(DMA2_Stream0, ENABLE);
}

//------------------------------------------------------------------------------

inline static void timer(void)
{
	TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);

	// базовая нстйрока

	TIM_TimeBaseStructInit(&TIM_TimeBaseStructure);
	TIM_TimeBaseStructure.TIM_Period = 625 - 1;
	TIM_TimeBaseStructure.TIM_Prescaler = 2000 - 1;
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV4;
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);

	// выход синхронизации

	TIM_SelectOutputTrigger(TIM3, TIM_TRGOSource_Update);

	// запуск таймера

	TIM_Cmd(TIM3, ENABLE);
}

//------------------------------------------------------------------------------

void main(void)
{
	gpio();

	adc();

	dma();

	timer();

	while (1)
	{
		if (PIN_SIGNAL(PIN_BUTTON))
		{
			PIN_ON(PIN_LED);
		}
		else
		{
			PIN_OFF(PIN_LED);
		}
	}
}

Полный проект можно получить используя данный код совместно с шаблоном к конце записи:

http://ziblog.ru/2012/09/09/linii-vvoda-vyivoda-stm32-chast-3.html

2013-05-08

Небольшое дополнение от читателя блога (http://kazus.ru/forums/showthread.php?t=104295&page=4).

Кстати по поводу статьи, для полного эффекта если будет время то добавьте в статью этот код(использование только CMSIS):

void DMA2_Stream4_IRQHandler(void)
{
        GPIOD->ODR        ^=((1>>12)|(1>>13)|(1>>14)|(1>>15));
}

uint16_t BUFF[100];
int main(void)
{
    SystemInit();

//*********************GPIO*************************

        RCC->AHB1ENR    |= RCC_AHB1ENR_GPIOAEN;
        RCC->AHB1ENR    |= RCC_AHB1ENR_GPIODEN;
        GPIOA->MODER    |= GPIO_MODER_MODER6;        //ADC1 CH6 PA6



//*********************TIM3*************************

        RCC->APB1ENR    |= RCC_APB1ENR_TIM3EN;        // тактирование таймера

        TIM3->PSC         = 160-1;                    // предделитель

        TIM3->ARR         = 1000-1;                    // переполнение

        TIM3->CR2         |= TIM_CR2_MMS_1;            // output (TRGO)

        TIM3->DIER         |= TIM_DIER_UDE;
        TIM3->CR1         |= TIM_CR1_CEN;                //запуск счета



//********************DMA***************************

        RCC->AHB1ENR    |= RCC_AHB1ENR_DMA2EN;
        DMA2_Stream4->CR&=~ DMA_SxCR_CHSEL;            // 000: channel 0 selected

        DMA2_Stream4->PAR= (uint32_t)&ADC1->DR;     //

        DMA2_Stream4->M0AR=(uint32_t)&BUFF[0];        // Массив

        DMA2_Stream4->NDTR=100;                        // Длина буфера

        DMA2_Stream4->CR|= DMA_SxCR_MINC;            // Инкремент адреса

        DMA2_Stream4->CR|= DMA_SxCR_MSIZE_0;        // 16 бит

        DMA2_Stream4->CR|= DMA_SxCR_PSIZE_0;        // 16 бит

        DMA2_Stream4->CR|= DMA_SxCR_CIRC;            // Непрерывный режим

        DMA2_Stream4->CR&=~ DMA_SxCR_DIR;            // 01: peripheral-to-Memory

        DMA2_Stream4->CR|= DMA_SxCR_PL;                // 11: Very high приоритет

        DMA2_Stream4->CR|= DMA_SxCR_TCIE;            // Transfer complete interrupt enable

        DMA2_Stream4->CR|= DMA_SxCR_EN;                 // Вкл. передачу

        NVIC_EnableIRQ(DMA2_Stream4_IRQn);
        NVIC_SetPriority(DMA2_Stream4_IRQn,5);


//********************ADC1**CH6 PA6***************

        RCC->APB2ENR    |= RCC_APB2ENR_ADC1EN;
        ADC1->CR1         |=ADC_CR1_SCAN;                // Scan mode

        ADC1->CR2        |=(ADC_CR2_EXTSEL_3);        //tim 3 (TRGO)

        ADC1->SQR3        =6;                            //выбор канала PA6

        ADC1->CR2         |=ADC_CR2_DMA;                //разрешаем рабуту DMA

        ADC1->CR2         |=ADC_CR2_DDS;                //DMA disable selection (for single ADC mode)

        ADC1->CR2        &=~ADC_CR2_CONT;            //Continuous conversion

        ADC1->CR2        |=ADC_CR2_EXTEN_0;            //01: Trigger detection on the rising edge

        ADC1->CR2        |= ADC_CR2_EOCS;            //разрешаем прерывания

        ADC1->CR2         |=ADC_CR2_ADON;                //Вкл. переобразования


comments powered by Disqus