Jump to content

    
Sign in to follow this  
hd44780

Опять таймер, ADC и DMA на STM32F4 (Discovery)

Recommended Posts

Привет всем.

Надо запускать ADC1 по таймеру, забирать результаты через DMA с получением какого-нибудь прерывания по заполнению буфера.

 

По мотивам доки и форумов написал:

volatile uint16_t adcBuffer[4096]; // <- the results are here

........................................

// ADC1 CH0 - PA0
void adcInit ( void )
{
 ADC_InitTypeDef        ADC_InitStructure;
 ADC_CommonInitTypeDef  ADC_CommonInitStructure;
 DMA_InitTypeDef        DMA_InitStructure;
 GPIO_InitTypeDef       GPIO_InitStructure;
 NVIC_InitTypeDef       NVIC_InitStructure;  

 // Enable ADC3, DMA2 and GPIO clocks
 RCC_AHB1PeriphClockCmd ( RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOA, ENABLE );
 RCC_APB2PeriphClockCmd ( RCC_APB2Periph_ADC1, ENABLE );

  // DMA2 Stream0 channel0 configuration
  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)&adcBuffer;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  DMA_InitStructure.DMA_BufferSize = 4096;
  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 );

  // Enable the DMA Stream IRQ Channel
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init ( &NVIC_InitStructure );     

  DMA_ITConfig ( DMA2_Stream0, DMA_IT_HT | DMA_IT_TC, ENABLE );

  // Configure ADC1 Channel1 (PA0) pin as analog input
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
  GPIO_Init ( GPIOA, &GPIO_InitStructure );

  // 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 Init
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T8_TRGO;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfConversion = 0;
  ADC_Init ( ADC1, &ADC_InitStructure );

  // ADC1 regular channel1 configuration
  ADC_RegularChannelConfig ( ADC1, ADC_Channel_0, 1, ADC_SampleTime_3Cycles );

  // Enable DMA request after last transfer (Single-ADC mode)
  ADC_DMARequestAfterLastTransferCmd ( ADC1, ENABLE );

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

  // Enable ADC1
  ADC_Cmd ( ADC1, ENABLE );

 // Инициализация TIM8, запускающего АЦП
 TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

 RCC_APB2PeriphClockCmd ( RCC_APB2Periph_TIM8, ENABLE );

 TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
 // делитель 33600 - частота прерываний = 84MHz/33600= 2500 = 2.5 kHz 
 TIM_TimeBaseStructure.TIM_Prescaler = 33600-1;

 // Период загружается в ARR - значение перезагрузки. Счётчик считает 0..ARR
 TIM_TimeBaseStructure.TIM_Period = 2; //период 2 импульсов - частота 1250 гц

 TIM_TimeBaseStructure.TIM_ClockDivision = 0;
 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;

 TIM_TimeBaseInit ( TIM8, &TIM_TimeBaseStructure );
 TIM_SelectOutputTrigger ( TIM8, TIM_TRGOSource_Update );
} // adcInit

// Старт
void adcStart ( void )
{
  TIM_Cmd ( TIM8, ENABLE );
} // adcStart

 

Из main-а вызываю adcInit, adcStart (т.е. запускаю таймер).

Результат - зависание на последней команде adcInit TIM_SelectOutputTrigger ( TIM8, TIM_TRGOSource_Update );

До запуска и всего прочего дело не доходит ....

Наверное в инициализации накосячил. Но не пойму где.

 

Пробовал ручной запуск, типа как здесь - http://electronix.ru/forum/index.php?showtopic=104701 и в примерах от ST, т.е. вместо таймера команда

ADC_SoftwareStartConv(ADC1); // start ADC conversions

Не виснет, но в буфере нули ... В той теме пошли разговоры на религиозные темы - "регистры против библиотек и функций" :) .

 

Да и нужен мне не ручной запуск, а по таймеру, с определённой частотой. Сам таймер работает, если убрать триггер и повесить прерывание, то оно исправно тикает с указанной частотой.

Я также пытался ставить ADC_SoftwareStartConv(ADC1); в прерывание таймера (метод а-ля AVR), получил пустой буфер с нулями.

На ноге PA0 (канал 0) висит потенциометр для отладки. От его положения соответственно ничего не зависит.

 

Также интересует, как установить прерывание DMA по заполнению буфера. Примеров не нашёл. В примере ADC3_DMA от ST (архив STM32F4xx_DSP_StdPeriph_Lib_V1.0.1) тупо выводит на дисплей 2-байтовую переменную, которую заполняет DMA. Но это ж некошерно ...

 

Помогите, кто может.

 

Спасибо.

 

PS. Плата STM32F4Discovery, проц STM32F4VGT6. Дисплей графический, ILI9320, работает по FSMC. С этим всё в норме.

Share this post


Link to post
Share on other sites

Может из-за этого: ADC_InitStructure.ADC_NbrOfConversion = 0; ?

 

Хотя это где как - где-то видел 1, где-то 0 ... В доке пока не понял, на что оно влияет :( .

Share this post


Link to post
Share on other sites

Вот, нашёл такое на одном из форумов:

 

// адрес регистра данных, откуда будем брать результат конвертации
#define ADC_CDR_ADDRESS    ((uint32_t)0x40012308)

 // переменные, описывающие свойства периферии
 ADC_InitTypeDef ADC_InitStructure;
 ADC_CommonInitTypeDef ADC_CommonInitStructure;
 DMA_InitTypeDef DMA_InitStructure;
 GPIO_InitTypeDef GPIO_InitStructure;
 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;

u32 ADCDualConvertedValue;

void main(void)
{
 ADCDualConvertedValue=0;  

// разрешаем тактирование используемых периферий
 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_DMA2 | RCC_AHB1Periph_GPIOC, ENABLE);
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_ADC2, ENABLE);

// описываем свойства порта ввода-вывода по которому будем производить оцифровку
 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AN;
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL ;
 GPIO_Init(GPIOC, &GPIO_InitStructure);

// описываем свойства DMA через который будет вестись обмен
 DMA_InitStructure.DMA_Channel = DMA_Channel_0;  
 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)ADC_CDR_ADDRESS;       // адрес регистра данных АЦП
 DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&ADCDualConvertedValue;
 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
 DMA_InitStructure.DMA_BufferSize = 1;
 DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
 DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Disable;
 DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Word;
 DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Word;
 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
 DMA_Cmd(DMA2_Stream0, ENABLE);

 // отключаем АЦП1 2
 ADC_Cmd(ADC1, DISABLE);
 ADC_Cmd(ADC2, DISABLE);  

 // описываем АЦП
 // общие параметры работы АЦП
 ADC_CommonInitStructure.ADC_Mode = ADC_DualMode_RegSimult;
 ADC_CommonInitStructure.ADC_TwoSamplingDelay = ADC_TwoSamplingDelay_6Cycles;
 ADC_CommonInitStructure.ADC_DMAAccessMode = ADC_DMAAccessMode_2;
 ADC_CommonInitStructure.ADC_Prescaler = ADC_Prescaler_Div2;
 ADC_CommonInit(&ADC_CommonInitStructure);  
 ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;

 // настраиваем АЦП1
 ADC_InitStructure.ADC_ScanConvMode = DISABLE;
 ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
 ADC_InitStructure.ADC_ExternalTrigConvEdge =  ADC_ExternalTrigConvEdge_Rising;   
 ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T8_TRGO;
 ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
 ADC_InitStructure.ADC_NbrOfConversion = 1;
 ADC_Init(ADC1, &ADC_InitStructure);

 // указывем канал, подлежащий оцифровке (температура)
 ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_3Cycles); 

 // разрешаем работу датчика температуры
 ADC_TempSensorVrefintCmd (ENABLE);

 // настраиваем АЦП2
 ADC_Init(ADC2, &ADC_InitStructure);

 // указывем канал, подлежащий оцифровке
 ADC_RegularChannelConfig(ADC2, ADC_Channel_12, 1, ADC_SampleTime_3Cycles);

 // разрешаем выполнять запросы DMA
 ADC_MultiModeDMARequestAfterLastTransferCmd(ENABLE);

 // запускаем АЦП1
 ADC_Cmd(ADC1, ENABLE);
 // запускаем АЦП2
 ADC_Cmd(ADC2, ENABLE);

 // настраиваем таймер
 TIM_Cmd(TIM8, DISABLE);
 // подключаем тактирование
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM8, ENABLE);
 // описываем таймер
//  TIM_TimeBaseStructure.TIM_Period = (560000 /125);
//  TIM_TimeBaseStructure.TIM_Prescaler = 0x00;

 // делитель 33600 - частота прерываний = 84MHz/33600= 2500 = 2.5 kHz 
 TIM_TimeBaseStructure.TIM_Prescaler = 33600-1;

 // Период загружается в ARR - значение перезагрузки. Счётчик считает 0..ARR
 TIM_TimeBaseStructure.TIM_Period = 2; //период 2 импульсов - частота 1250 гц

 TIM_TimeBaseStructure.TIM_ClockDivision = 0x0;
 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
 TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

 // записываем описание в регистры
 TIM_TimeBaseInit(TIM8, &TIM_TimeBaseStructure);

 // сбрасываем, указываем выходной триггер (для запуска АЦП)
 TIM_SetCounter(TIM8, 0);
 TIM_SelectOutputTrigger(TIM8, TIM_TRGOSource_Update);

 // запуcкаем таймер
 TIM_Cmd(TIM8, ENABLE);  

 while (1)
 {
 } // while
} // main

ADC1 цифрует свой канал 12 (PC2), ADC2 внутренний датчик температуры. Запуск по TIM8, результат в ADCDualConvertedValue.

Оно работает вроде нормально, наблюдал переменную под отладчиком. Осталось только понять, как повесить на DMA обработчик по заполнению буфера.

И непонятно, откуда взялся адрес ADC_CDR_ADDRESS. Из него DMA результаты читает.

 

У меня тут написано

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(ADC1->DR);

Т.е. регистр данных ADC1.

 

Мой код зависал из-за отсутствия этого самого обработчика прерываний DMA.

Убрал у себя вот этот кусок:

  // Enable the DMA Stream IRQ Channel
   NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
   NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
   NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
   NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
   NVIC_Init ( &NVIC_InitStructure );     

   DMA_ITConfig ( DMA2_Stream0, DMA_IT_HT | DMA_IT_TC, ENABLE );

Виснуть перестало, но в буфере нули...

Может потому, что я с разгону написал тот же ADC_CDR_ADDRESS, что и в примере выше. У меня ведь один АЦП и один канал.

 

В итоге пока что хрень :) .

Кто-нибудь знает, как написать обработчик прерываний DMA?

 

 

Обработчик прерываний DMA для F4:

 

// dma2 stream 0 irq handler

void DMA2_Stream0_IRQHandler ( void )

{

}

 

Только что в нём писать ?...

 

Share this post


Link to post
Share on other sites

Вот так заработал:

 

  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
  DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)&adcBuffer;
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralToMemory;
  DMA_InitStructure.DMA_BufferSize = 4096;
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;

 

Ошибка была в первой строчка. Я взятие адреса & пропустил...

Буфер заполняется нормально.

Но как только включаю прерывание DMA, всё сразу наглухо виснет даже под отладчиком. Даже IAR аварийно вылетает.

 

Инициализация прерывания:

  // Enable the DMA Stream IRQ Channel
  NVIC_InitStructure.NVIC_IRQChannel = DMA2_Stream0_IRQn;
  NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
  NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
  NVIC_Init ( &NVIC_InitStructure );     
  DMA_ITConfig ( DMA2_Stream0, DMA_IT_HT | DMA_IT_TC, ENABLE );

 

Обработчик прерывания:

// dma2 stream 0 irq handler
void DMA2_Stream0_IRQHandler ( void )
{
  // Test on DMA Stream Transfer Complete interrupt
  if ( DMA_GetITStatus(DMA2_Stream0, DMA_FLAG_HTIF0) )
  {
    // Clear Stream0 HalfTransfer
    DMA_ClearITPendingBit ( DMA2_Stream0, DMA_FLAG_HTIF0 );  
    
    // Control LED
    STM_EVAL_LEDToggle ( LED_ORANGE );
  } // if    
}

 

Светодиодик LED_ORANGE молчит.

Share this post


Link to post
Share on other sites

Да-а, видать не по сеньке шапка...... Ладно, я разобрался.

Работающее и не виснущее прерывание:

 

void DMA2_Stream0_IRQHandler ( void )
{
     // Test on DMA Stream Transfer Complete interrupt
  if ( DMA_GetITStatus(DMA2_Stream0, DMA_IT_TCIF0) )
  {
   // Clear Stream0 HalfTransfer
    DMA_ClearITPendingBit ( DMA2_Stream0, DMA_IT_TCIF0 );

  } // if

  // Test on DMA Stream HalfTransfer Complete interrupt
  if ( DMA_GetITStatus(DMA2_Stream0, DMA_IT_HTIF0) )
  {
    // Clear Stream0 HalfTransfer
    DMA_ClearITPendingBit ( DMA2_Stream0, DMA_IT_HTIF0 );
  } // if
}

 

Флажки готовности и прочие программные удобства пока не приделал, но оно работает.

Вечером или завтра выложу полный исходник, чтобы не уподобляться вышеуказанной теме.

Share this post


Link to post
Share on other sites
Ошибка была в первой строчка. Я взятие адреса & пропустил...
Еще один гвоздь в крышку гроба этой библиотеки. Если уж делали структуру и одно из полей должно содержать указатель, то какого черта они не объявили это поле как void * и не спрятали приведение к uintptr_t внутрь библиотеки?

 

Нет уж, нафиг.

 

Share this post


Link to post
Share on other sites

Ну вот опять пошла религия :biggrin: ...

Пропустить амперсанд можно как в команде

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);

так и в команде

DMA2_Stream0->PAR = (uint32_t)&(ADC1->DR);

 

А неправильное значение Вы хоть сразу в регистр впишите, хоть ещё через десяток структур и пяток функций прогоните, ничего не изменится.

Поверьте мне, как программисту с почти 15-летним стажем. И не только на МК...

 

И ещё немного оффтопа:

Я видел на разных форумах немало "простыней" типа этой:

	
	/* Peripheral clock enable */
	RCC->AHB1ENR |= (1<<4);				// GPIOE clock enable
	RCC->AHB1ENR |= (1<<3);				// GPIOD clock enable
	RCC->AHB1ENR |= (1<<2);				// GPIOC clock enable
	RCC->AHB1ENR |= (1<<1);				// GPIOB clock enable
	RCC->AHB1ENR |= (1<<0);				// GPIOA clock enable
	RCC->APB1ENR |= (1<<18);			// USART3 clock enable
	RCC->APB1ENR |= (1<<15);			// SPI3 clock enable
	RCC->APB1ENR |= (1<<14);			// SPI2 clock enable
	RCC->APB2ENR |= (1<<12);			// SPI1 clock enable
	RCC->AHB1ENR |= (1<<21);			// DMA1 clock enable
	RCC->APB1ENR |= (1<<4);				// TIM6 clock enable
	RCC->APB1ENR |= (1<<5);				// TIM7 clock enable
	RCC->APB2ENR |= (1<<1);				// TIM8 clock enable
	RCC->AHB2ENR |= (1<<6);				// RNG clock enable
	RCC->APB1ENR |= (1<<29);			// DAC interface clock enable
	RCC->APB2ENR |= (1<<14);			// SYSCFGEN clock enable
	RCC->APB2ENR |= (1<<8);				// ADC1 clock enable
	RCC->APB1ENR |= (1<<21);			// I2C1 clock enable
	RCC->AHB1ENR |= (1<<18);			// SRAM backup enable
	RCC->APB1ENR |= (1<<28);			// POWER enable
	RCC->AHB2ENR |= (1<<7);				// USB OTG_FS clock enable

	/* Set I/O mode for GPIOA */
	GPIOA->MODER = 0x6AAAAA54;		// PORT A OUT PA14 - PA4 AF PA0 digital input for USER BUTTON
	GPIOA->OSPEEDR = 0xABFFAAAA;	// 50 MHz all and PA12 - PA8 100 MHz speed
	GPIOA->PUPDR = 0x24000100;		// pull-up PA13 PA4 pull-down PA14
	GPIOA->AFR[1] |= (0xA<<16);		// PA12 how to alternative function AF10 USB_OTG_FS_DP
	GPIOA->AFR[1] |= (0xA<<12);		// PA11 how to alternative function AF10 USB_OTG_FS_DM
	GPIOA->AFR[1] |= (0xA<<8);		// PA10 how to alternative function AF10 USB_OTG_FS_ID
	GPIOA->AFR[1] |= (0xA<<4);		// PA9 how to alternative function AF10 USB_OTG_FS_VBUS
	GPIOA->AFR[1] |= (0xA<<0);		// PA8 how to alternative function AF10 USB_OTG_FS_SOF
	GPIOA->AFR[0] |= (0x5<<28);		// PA7 how to alternative function AF5 SPI1_MOSI
	GPIOA->AFR[0] |= (0x5<<24);		// PA6 how to alternative function AF5 SPI1_MISO
	GPIOA->AFR[0] |= (0x5<<20);		// PA5 how to alternative function AF5 SPI1_SCK
	GPIOA->AFR[0] |= (0x5<<16);		// PA4 how to alternative function AF5 SPI1_NSS

	/* PA0 configuration interrupt input set */
	EXTI->IMR |= (1<<0);							// MR0 interrupt request from line 0 is not masked
	EXTI->RTSR |= (1<<0);							// TR0 rising trigger enabled for input line
	SYSCFG->EXTICR[0] &= ~(0xF<<0);		// EXTI0 PA pins set
	NVIC_SetPriority(EXTI0_IRQn, 0);	// PA0 priority set 0
	NVIC_EnableIRQ(EXTI0_IRQn);				// EXTI0 interrupt enable

	/* Set I/O mode for GPIOB */
	GPIOB->MODER = 0x55696555;		// port B OUT PB10 AF
	GPIOB->OSPEEDR = 0xAAAAAAAA;	// 50 MHz speed
	GPIOB->OTYPER |= (1<<9);			// PB9 open-drain
	GPIOB->OTYPER |= (1<<6);			// PB6 open-drain
	GPIOB->PUPDR = 0x00000000;		// no pull-up or pull-down
	GPIOB->AFR[1] |= (0x5<<8);		// PB10 how to alternative function AF5 I2S2_CK
	GPIOB->AFR[1] |= (0x4<<4);		// PB9 how to alternative function AF4 I2C1_SDA
	GPIOB->AFR[0] |= (0x4<<24);		// PB6 how to alternative function AF4 I2C1_SCL

	/* Set I/O mode for GPIOC */
	GPIOC->MODER = 0x5669AD94;		// port C OUT PC12 PC10 PC9 PC7 PC6 and PC3 AF PC5 analog mode ADC1 IN15 PC0 digital input 
	GPIOC->OSPEEDR = 0xAAAAAAAA;  // 50 MHz speed
	GPIOC->PUPDR = 0x00000080;		// pull-down PC3
	GPIOC->AFR[1] |= (0x6<<16);		// PC12 how to alternative function AF6 I2S3_SD
	GPIOC->AFR[1] |= (0x6<<8);		// PC10 how to alternative function AF6 I2S3_CK
	GPIOC->AFR[0] |= (0x6<<28);		// PC7 how to alternative function AF6 I2S3_MCK
	GPIOC->AFR[1] &= ~(0xF<<0);		// PC9 how to alternative function AF0 MCO_2
	GPIOC->AFR[0] |= (0x3<<24);		// PC6 how to alternative function AF3 TIMER_8
	GPIOC->AFR[0] |= (0x5<<12);		// PC3 how to alternative function AF5 I2S2_SD

	/* Set I/O mode for GPIOD */
	GPIOD->MODER = 0x555A5155;		// port D OUT PD5 digital input PD8 PD9 AF
	GPIOD->OSPEEDR = 0xAAAAAAAA;	// speed 50 MHz
	GPIOD->PUPDR = 0x00040000;		// pull-up PD9 USART3_RX
	GPIOD->AFR[1] |= (0x7<<4);		// PD9 how to alternative function AF7 USART3_RX
	GPIOD->AFR[1] |= (0x7<<0);		// PD8 how to alternative function AF7 USART3_TX
	GPIOD->BSRRL |= (1<<4);				// PD4 set 1 for RESET OFF EXTERNAL AUDIO DAC

	/* Set I/O mode for GPIOE */
	GPIOE->MODER = 0x55555550;		// port E OUT PE0 and PE1 digital input from ACCELEROMETER
	GPIOE->OTYPER = 0x00000008;		// PE3 open-drain output type
	GPIOE->OSPEEDR = 0xAAAAAAAA;	// speed 50 MHz
	GPIOE->PUPDR = 0x00000000;		// no pull-down or pull-up
	GPIOE->BSRRL |= (1<<3);				// PE3 set 1

	/* PE1 configuration interrupt input set */
	EXTI->IMR |= (1<<1);							// MR1 interrupt request from line 1 is not masked
	EXTI->RTSR |= (1<<1);							// TR1 rising trigger enabled for input line 1
	EXTI->FTSR |= (1<<1);							// TR1 failing trigger enabled for input line 1
	SYSCFG->EXTICR[0] |= (0x4<<4);		// EXTI1 PE pins set
	NVIC_SetPriority(EXTI1_IRQn, 1);	// PE1 priority set 1
	NVIC_EnableIRQ(EXTI1_IRQn);				// EXTI1 interrupt enable

	/* USART3 configuration set*/
	USART3->BRR |= 0x00000030;	// USART3 speed 2 Mb/c set dev 3
	USART3->CR1 |= (1<<15);			// Oversampling by 8 set
	USART3->CR1 &= ~(1<<12);		// Word length 8 bit set
	USART3->CR1 &= ~(1<<10);		// Parity control disable
	USART3->CR1 &= ~(1<<8);			// Parity interrupt disable
	USART3->CR1 &= ~(1<<7);			// Transmit data register empty interrupt disable
	USART3->CR1 &= ~(1<<6);			// Transmission complete interrupt disable
	USART3->CR1 |= (1<<5);			// Read data register interrupt enable
	USART3->CR1 |= (1<<3);			// TX set On
	USART3->CR1 |= (1<<2);			// RX set On
	USART3->CR1 &= ~(1<<1);			// Receiver set in active mode
	USART3->CR2 &= ~(0x3<<12);	// Set 1 stop bit
	USART3->CR3 &= ~(1<<11);		// 3 sample bit method set
	USART3->CR3 &= ~(1<<7);			// DMA transmitter disable
	USART3->CR3 &= ~(1<<6);			// DMA receiver disable
	USART3->CR1 |= (1<<13);			// USART3 On

	NVIC_SetPriority(USART3_IRQn, 2);			// USART3 priority set 2
	NVIC_EnableIRQ(USART3_IRQn);					// USART3 interrupt enable

               /* Timer8 PWM PC6 configuration set */
	TIM8->CR1 |= (1<<7);				// Timer 8 TIM8 ARR register is buffered set
	TIM8->PSC = 0xFDE7;					// Timer 8 prescaler configuration set 65000 - 1
	TIM8->ARR = 0x0171;					// Timer 8 auto-reload value before 1 c
	TIM8->CCR1 = 0x00B8;				// Timer 8 capture compare register 500 mc set 1
	TIM8->CCMR1 = 0x0068;				// Timer 8 PWM output mode set
	TIM8->CCER &= ~(1<<1);			// Timer 8 OC1 active high output polarity
	TIM8->CCER |= (1<<0);				// Timer 8 OC1 signal On
	TIM8->BDTR |= (1<<15);			// Timer 8 MOE main output enable
	TIM8->CR1 |= (1<<0);				// Timer 8 On

	/* ADC1 IN15 configuration set */
	ADC1->CR1 |= (1<<26);				// ADC1 overrun interrupt enabled
	ADC1->CR1 &= ~(0x3<<24);		// ADC1 resolution 12-bit 15 ADCCLK cycles
	ADC1->CR1 |= (1<<23);				// ADC1 analog watchdog enabled on regular channels
	ADC1->CR1 &= ~(1<<22);			// ADC1 analog watchdog disabled on injected channels
	ADC1->CR1 &= ~(0x7<<13);		// ADC1 discontinuous mode channel count 1 channel
	ADC1->CR1 &= ~(1<<12);			// ADC1 discontinuous mode on injected channels disabled
	ADC1->CR1 &= ~(1<<11);			// ADC1 discontinuous mode on regular channels disabled
	ADC1->CR1 &= ~(1<<10);			// ADC1 automatic injected group conversion disabled
	ADC1->CR1 |= (1<<9);	 			// ADC1 analog watchdog enabled on one channel
	ADC1->CR1 &= ~(1<<8);				// ADC1 scan mode disabled
	ADC1->CR1 &= ~(1<<7);				// ADC1 interrupt disable for injected channels
	ADC1->CR1 |= (1<<6);				// ADC1 analog watchdog interrupt enabled		
	ADC1->CR1 |= (1<<5);				// ADC1 EOC interrupt enabled
	ADC1->CR1 |= (0xF<<0);			// ADC1 analog watchdog input channel 15
	ADC1->CR2 &= ~(0x3<<28);		// ADC1 external trigger detection disabled for regular channels
	ADC1->CR2 &= ~(0xF<<24);		// ADC1 external event select for regular group timer 1 CC1 event
	ADC1->CR2 &= ~(0x3<<20);		// ADC1 external trigger detection disabled for injected channels
	ADC1->CR2 &= ~(0xF<<16);		// ADC1 external event select for regular group timer 1 CC4 event
	ADC1->CR2 &= ~(1<<11);			// ADC1 data alignment right set
	ADC1->CR2 |= (1<<10);				// ADC1 EOC bit is set at the end of each regular conversion overrun detection is enabled
	ADC1->CR2 &= ~(1<<9);				// ADC1 no new DMA request is issued after the last transfer
	ADC1->CR2 &= ~(1<<8);				// ADC1 DMA mode disabled
	ADC1->CR2 &= ~(1<<1);				// ADC1 single conversion mode set
	ADC1->SMPR1 = 0x00000000;		// ADC1 channels 18 - 10 sampling time selection 3 cycles
	ADC1->SMPR2 = 0x00000000;		// ADC1 channels 9 - 0 sampling time selection 3 cycles
	ADC1->HTR = 0x00000100;			// ADC1 analog watchdog higher threshold 0x100
	ADC1->LTR = 0x00000000;			// ADC1 analog watchdog lower threshold 0x000
	ADC1->SQR1 &= ~(0xF<<20);		// ADC1 regular channel sequence length 1 conversion
	ADC1->SQR3 |= (0xF<<0);			// ADC1 1st conversion in regular sequence channel 15
	ADC1->JSQR = 0x00000000;		// ADC1 injected sequence register set
	ADC->CCR &= ~(1<<23);				// ADC1 temperature sensor and Vref INT channel disabled
	ADC->CCR &= ~(1<<22);				// ADC1 Vbat channel disabled
	ADC->CCR |= (0x2<<16);			// ADC1 prescaler PCLK2 divided by 6 speed equal 2 MHz
	ADC->CCR &= ~(0x3<<14);			// ADC1 DMA mode disabled
	ADC->CCR &= ~(1<<13);				// ADC1 DMA disable selection for multi-ADC mode
	ADC->CCR &= ~(0xF<<8);			// ADC1 delay between 2 sampling phases 5*TADCCLK
	ADC->CCR &= ~(0x1F<<0);			// ADC1 multi ADC mode selection 'independent mode'
	ADC1->CR2 |= (1<<0);				// ADC1 On

	NVIC_SetPriority(ADC_IRQn, 7);	// ADC1 IN15 priority set 7
	NVIC_EnableIRQ(ADC_IRQn);				// ADC1 IN15 interrupt enable

 

Лично мне этот стиль не нравится. Здесь хоть комментарии есть :rolleyes: . А некоторым "спецам" вообще писать их облом. В итоге, чтобы понять как что делается, надо полдня посидеть глядя одним глазом в эту простыню, а другим в RefMan. А если надо перенести код с проца на проц - вообще мрак .... Приходилось.

 

Такой способ я поддержу в одном единственном случае - только если я буду на 100% уверен, что в стандартных библиотеках в каком-то месте имеет место ошибка или нестабильность.

Справедливости ради отмечу, что RTC на этом проце у меня не заработал со стандартной библиотекой, а с манипуляциями регистрами заработал. Но подобные разовые случаи не повод повсеместно и навсегда отказываться от библиотек.

Это ж не AVR, где таймер в большинстве случаев программируется через 2-3 простеньких регистра и т.п. ...

 

Извините, если обидел. Не выдержал...

 

По делу.

 

Прикладываю полный вариант библиотеки ADC_DMA_Timer. Методов для задания частоты таймера (дискретизации) нету, всё наглухо вшито в инициализацию, но это разделить не проблема.

Кому надо, берите.

Компилятор - IAR.

 

Пример использования:

 

#include "Adc.h"

...............
// Данные АЦП
extern uint16_t adcBuffer[4096];
// Флаг готовности данных
extern bool isDataReady;

.......................
  uint32_t adcVal;
.......................
    
    if (isDataReady)
    {
      isDataReady = false;
      
      adcVal = 0;
      for ( i = 0; i < 4096; i ++ )
        adcVal += adcBuffer [ i ];
      adcVal /= 4096;
  
      sprintf ( sBuffer, "ADCVal = %05lu", adcVal );
      DrawString ( sBuffer, 10, 70, 0xFFE0, 0x0000, 1 );
    } // if
...........................

 

PS. Если у кого будут замечания, буду рад услышать.

АЦП пока цифрует только один канал, вообще в планах сделать одновременный опрос разных каналов 2-х АЦП (а-ля 2-х канальный осциллограф), есть наброски примитивного кода для этого, оно вроде работает, но до конца я не дотестировал.

Сделаю, выложу.

Adc.zip

Share this post


Link to post
Share on other sites
Пропустить амперсанд можно как в команде

DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);

так и в команде

DMA2_Stream0->PAR = (uint32_t)&(ADC1->DR);

Согласен. Но в первом случае этого можно легко избежать, правильно описав поле структуры. Во втором, кстати, тоже - достаточно поле PAR описать как void *. Почему этого не сделано - вопрос уже к авторам заголовочного файла. Хорошая мысль, кстати. Пошел лопатить заголовочные файлы в своих проектах...

Я видел на разных форумах немало "простыней" типа этой:
Ужас. Это уже другая крайность - все в "магических цифрах".

Вот тут комментарии, по большому счету, уже лишние:

    RCC->APB1ENR = 0
        | 1 * RCC_APB1ENR_TIM2EN    //  Timer 2 clock
        | 1 * RCC_APB1ENR_TIM3EN    //  Timer 3 clock
        | 0 * RCC_APB1ENR_TIM4EN    //  Timer 4 clock
        | 0 * RCC_APB1ENR_TIM6EN    //  Timer 6 clock
        | 0 * RCC_APB1ENR_TIM7EN    //  Timer 7 clock
        | 0 * RCC_APB1ENR_WWDGEN    //  Window Watchdog clock
        | 1 * RCC_APB1ENR_SPI2EN    //  SPI 2 clock
        | 1 * RCC_APB1ENR_USART2EN  //  USART 2 clock
        | 0 * RCC_APB1ENR_USART3EN  //  USART 3 clock
        | 0 * RCC_APB1ENR_I2C1EN    //  I2C 1 clock
        | 0 * RCC_APB1ENR_I2C2EN    //  I2C 2 clock
        | 0 * RCC_APB1ENR_BKPEN     //  Backup interface clock
        | 0 * RCC_APB1ENR_PWREN     //  Power interface clock
        | 1 * RCC_APB1ENR_DACEN     //  DAC interface clock
        | 0 * RCC_APB1ENR_CECEN     //  CEC interface clock
         ;

И вся инициализация укладывается в одну команду вместо трех (чтение-маска-запись) на каждое поле в вашем примере.

 

Простите за отвлечение от темы, тоже не удержался.

 

PS. Если у кого будут замечания, буду рад услышать.
Можно сделать буфер циклическим и выдавать результат усреднения на каждый входной отсчет, тогда частота выдачи результатов не уменьшится. Примерно так:

template <uint_fast8_t const samples, typename in_t = int16_t, typename sum_t = int32_t>
class moving_average
{
public:
    moving_average();
    void sample(in_t new_value);
    int_fast16_t result() { return Sum / samples; }
    uint_fast8_t length() { return samples; }
private:
    in_t Buffer[samples];
    uint_fast8_t Index;
    sum_t Sum;
};

template <uint_fast8_t samples, typename in_t, typename sum_t>
moving_average<samples, in_t, sum_t>::moving_average()
: Index(0)
, Sum(0)
{
    memset(Buffer, 0, sizeof(Buffer));
}

template <uint_fast8_t samples, typename in_t, typename sum_t>
void moving_average<samples, in_t, sum_t>::sample(in_t new_value)
{
    Sum -= Buffer[Index];
    Buffer[Index] = new_value;
    Sum += new_value;
    if(++Index >= samples)
        Index = 0;
}

Share this post


Link to post
Share on other sites

А есть ли какие-нибудь заморочки при запуске АЦП от Т1?

Пробовал от Т8 - нормально запускается, а вот от Т1 не хочет...

 

 

   TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
  TIM_TimeBaseStructure.TIM_Period = MAX_PWM;
  TIM_TimeBaseStructure.TIM_Prescaler = PrescalerValue;
  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

  TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);

  TIM_Cmd(TIM1, ENABLE);



     /* ADC3 Init ****************************************************************/
  ADC_InitStructure.ADC_Resolution = ADC_Resolution_12b;
  ADC_InitStructure.ADC_ScanConvMode = DISABLE;
  ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
  ADC_InitStructure.ADC_ExternalTrigConvEdge = ADC_ExternalTrigConvEdge_Rising;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC3;	//ADC_ExternalTrigConv_T8_TRGO;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfConversion = 0;
  ADC_Init(ADC3, &ADC_InitStructure);

 /* ADC3 regular channel12 configuration *************************************/
 ADC_RegularChannelConfig(ADC3, ADC_Channel_12, 1, ADC_SampleTime_56Cycles);

/* Enable DMA request after last transfer (Single-ADC mode) */
 ADC_DMARequestAfterLastTransferCmd(ADC3, ENABLE);

 /* Enable ADC3 DMA */
 ADC_DMACmd(ADC3, ENABLE);

 /* Enable ADC3 */
 ADC_Cmd(ADC3, ENABLE);

Share this post


Link to post
Share on other sites

Вы задаёте АЦП триггер ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_T1_CC3;

 

А таймер TRGO щёлкает:

TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);

 

Но TRGO это не CC3 ....

 

По-моему, Вам надо написать TIM_GenerateEvent(TIM1, TIM_EventSource_CC1); если я не ошибся :)

 

Или использовать TIM2, TIM3. АЦП от них умеет запускаться и TRGO там есть ....

Share this post


Link to post
Share on other sites

Нужен именно Т1.

 

TIM_GenerateEvent(TIM1, TIM_EventSource_CC1) - это для возможности генерить событие таймера из программы.

 

пробовал TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_OC3Ref)):

 

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Active;

TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //TIM_OutputState_Disable;

TIM_OCInitStructure.TIM_Pulse = MAX_PWM/4;

TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;

TIM_OC3Init(TIM1, &TIM_OCInitStructure);

 

TIM_OC3PreloadConfig(TIM1, TIM_OCPreload_Enable);

 

TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_OC3Ref));

 

TIM_Cmd(TIM1, ENABLE);

 

не запускается.

скорее всего где-то что-то не взвел... но где?

Edited by digitAll

Share this post


Link to post
Share on other sites

Обобщаю вопрос:

Кто делал запуск АЦП по любому ADC_ExternalTrigConv_Tх_CCх, а не ADC_ExternalTrigConv_Tх_TRGO любого таймера? Поделитесь кодом плз.

Таймер тикает как надо, пробовал ШИМ с него, прерывания работают, а вот запустить АЦП - никак...

Перепробовал все мыслимые и не очень комбинации, но лыжи не едут :(

Share this post


Link to post
Share on other sites
On 5/20/2013 at 3:15 PM, digitAll said:

Обобщаю вопрос:

Кто делал запуск АЦП по любому ADC_ExternalTrigConv_Tх_CCх, а не ADC_ExternalTrigConv_Tх_TRGO любого таймера? Поделитесь кодом плз.

Таймер тикает как надо, пробовал ШИМ с него, прерывания работают, а вот запустить АЦП - никак...

Перепробовал все мыслимые и не очень комбинации, но лыжи не едут :(

Аналогичная ситуация, STM32F446, ADC_ExternalTrigConv_T1_CCх не захотел работать ни под каким соусом.

Share this post


Link to post
Share on other sites
5 hours ago, Polaris said:

Аналогичная ситуация, STM32F446, ADC_ExternalTrigConv_T1_CCх не захотел работать ни под каким соусом.

Для того чтобы АЦП запускался по событию 'CaptureCompare', необходимо в регистре CCER таймера установить бит разрешения нужного канала, например TIM3->CCER |= TIM_CCER_CC1E; При этом АЦП уже должен быть настроен на запуск от данного события.

Edited by ivan24190

Share this post


Link to post
Share on other sites
On 6/26/2020 at 8:59 PM, ivan24190 said:

Для того чтобы АЦП запускался по событию 'CaptureCompare', необходимо в регистре CCER таймера установить бит разрешения нужного канала, например TIM3->CCER |= TIM_CCER_CC1E; При этом АЦП уже должен быть настроен на запуск от данного события.

 

Ставил, эффект нулевой, что работает с третьим, не работает с первым.

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Sign in to follow this