реклама на сайте
подробности

 
 
 
Reply to this topicStart new topic
> Опять таймер, ADC и DMA на STM32F4 (Discovery), Заезженная уже, наверное, тема.
hd44780
сообщение Feb 12 2013, 07:53
Сообщение #1


Профессионал
*****

Группа: Свой
Сообщений: 1 202
Регистрация: 26-08-05
Из: Донецк, ДНР
Пользователь №: 7 980



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

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

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
Не виснет, но в буфере нули ... В той теме пошли разговоры на религиозные темы - "регистры против библиотек и функций" sm.gif .

Да и нужен мне не ручной запуск, а по таймеру, с определённой частотой. Сам таймер работает, если убрать триггер и повесить прерывание, то оно исправно тикает с указанной частотой.
Я также пытался ставить ADC_SoftwareStartConv(ADC1); в прерывание таймера (метод а-ля AVR), получил пустой буфер с нулями.
На ноге PA0 (канал 0) висит потенциометр для отладки. От его положения соответственно ничего не зависит.

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

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

Спасибо.

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


--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса.
(с) Мария Эдуарда
Go to the top of the page
 
+Quote Post
hd44780
сообщение Feb 12 2013, 08:56
Сообщение #2


Профессионал
*****

Группа: Свой
Сообщений: 1 202
Регистрация: 26-08-05
Из: Донецк, ДНР
Пользователь №: 7 980



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

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


--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса.
(с) Мария Эдуарда
Go to the top of the page
 
+Quote Post
hd44780
сообщение Feb 13 2013, 08:24
Сообщение #3


Профессионал
*****

Группа: Свой
Сообщений: 1 202
Регистрация: 26-08-05
Из: Донецк, ДНР
Пользователь №: 7 980



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

CODE

// адрес регистра данных, откуда будем брать результат конвертации
#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, что и в примере выше. У меня ведь один АЦП и один канал.

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


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

// dma2 stream 0 irq handler
void DMA2_Stream0_IRQHandler ( void )
{
}

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


--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса.
(с) Мария Эдуарда
Go to the top of the page
 
+Quote Post
hd44780
сообщение Feb 13 2013, 18:53
Сообщение #4


Профессионал
*****

Группа: Свой
Сообщений: 1 202
Регистрация: 26-08-05
Из: Донецк, ДНР
Пользователь №: 7 980



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

Код
  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 молчит.


--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса.
(с) Мария Эдуарда
Go to the top of the page
 
+Quote Post
hd44780
сообщение Feb 14 2013, 07:04
Сообщение #5


Профессионал
*****

Группа: Свой
Сообщений: 1 202
Регистрация: 26-08-05
Из: Донецк, ДНР
Пользователь №: 7 980



Да-а, видать не по сеньке шапка...... Ладно, я разобрался.
Работающее и не виснущее прерывание:

Код
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
}


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


--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса.
(с) Мария Эдуарда
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Feb 14 2013, 07:22
Сообщение #6


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



QUOTE (hd44780 @ Feb 13 2013, 20:53) *
Ошибка была в первой строчка. Я взятие адреса & пропустил...
Еще один гвоздь в крышку гроба этой библиотеки. Если уж делали структуру и одно из полей должно содержать указатель, то какого черта они не объявили это поле как void * и не спрятали приведение к uintptr_t внутрь библиотеки?

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


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
hd44780
сообщение Feb 15 2013, 07:37
Сообщение #7


Профессионал
*****

Группа: Свой
Сообщений: 1 202
Регистрация: 26-08-05
Из: Донецк, ДНР
Пользователь №: 7 980



Ну вот опять пошла религия biggrin.gif ...
Пропустить амперсанд можно как в команде
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
так и в команде
DMA2_Stream0->PAR = (uint32_t)&(ADC1->DR);

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

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

/* 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.gif . А некоторым "спецам" вообще писать их облом. В итоге, чтобы понять как что делается, надо полдня посидеть глядя одним глазом в эту простыню, а другим в 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 ( 2.18 килобайт ) Кол-во скачиваний: 33
 


--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса.
(с) Мария Эдуарда
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Feb 15 2013, 08:28
Сообщение #8


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



QUOTE (hd44780 @ Feb 15 2013, 09:37) *
Пропустить амперсанд можно как в команде
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&(ADC1->DR);
так и в команде
DMA2_Stream0->PAR = (uint32_t)&(ADC1->DR);
Согласен. Но в первом случае этого можно легко избежать, правильно описав поле структуры. Во втором, кстати, тоже - достаточно поле PAR описать как void *. Почему этого не сделано - вопрос уже к авторам заголовочного файла. Хорошая мысль, кстати. Пошел лопатить заголовочные файлы в своих проектах...
QUOTE (hd44780 @ Feb 15 2013, 09:37) *
Я видел на разных форумах немало "простыней" типа этой:
Ужас. Это уже другая крайность - все в "магических цифрах".
Вот тут комментарии, по большому счету, уже лишние:
CODE
    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
         ;
И вся инициализация укладывается в одну команду вместо трех (чтение-маска-запись) на каждое поле в вашем примере.

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

QUOTE (hd44780 @ Feb 15 2013, 09:37) *
PS. Если у кого будут замечания, буду рад услышать.
Можно сделать буфер циклическим и выдавать результат усреднения на каждый входной отсчет, тогда частота выдачи результатов не уменьшится. Примерно так:
CODE
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;
}


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
digitAll
сообщение May 20 2013, 08:19
Сообщение #9


Участник
*

Группа: Участник
Сообщений: 17
Регистрация: 10-05-09
Пользователь №: 48 869



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


CODE
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);
Go to the top of the page
 
+Quote Post
hd44780
сообщение May 20 2013, 11:10
Сообщение #10


Профессионал
*****

Группа: Свой
Сообщений: 1 202
Регистрация: 26-08-05
Из: Донецк, ДНР
Пользователь №: 7 980



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

А таймер TRGO щёлкает:
TIM_SelectOutputTrigger(TIM1, TIM_TRGOSource_Update);

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

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

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


--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса.
(с) Мария Эдуарда
Go to the top of the page
 
+Quote Post
digitAll
сообщение May 20 2013, 11:25
Сообщение #11


Участник
*

Группа: Участник
Сообщений: 17
Регистрация: 10-05-09
Пользователь №: 48 869



Нужен именно Т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);

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

Сообщение отредактировал digitAll - May 20 2013, 11:35
Go to the top of the page
 
+Quote Post
digitAll
сообщение May 20 2013, 13:15
Сообщение #12


Участник
*

Группа: Участник
Сообщений: 17
Регистрация: 10-05-09
Пользователь №: 48 869



Обобщаю вопрос:
Кто делал запуск АЦП по любому ADC_ExternalTrigConv_Tх_CCх, а не ADC_ExternalTrigConv_Tх_TRGO любого таймера? Поделитесь кодом плз.
Таймер тикает как надо, пробовал ШИМ с него, прерывания работают, а вот запустить АЦП - никак...
Перепробовал все мыслимые и не очень комбинации, но лыжи не едут sad.gif
Go to the top of the page
 
+Quote Post

Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 19th July 2025 - 17:28
Рейтинг@Mail.ru


Страница сгенерированна за 0.01522 секунд с 7
ELECTRONIX ©2004-2016