|
|
  |
Опять таймер, ADC и DMA на STM32F4 (Discovery), Заезженная уже, наверное, тема. |
|
|
|
Feb 12 2013, 07:53
|

Профессионал
    
Группа: Свой
Сообщений: 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 Не виснет, но в буфере нули ... В той теме пошли разговоры на религиозные темы - "регистры против библиотек и функций"  . Да и нужен мне не ручной запуск, а по таймеру, с определённой частотой. Сам таймер работает, если убрать триггер и повесить прерывание, то оно исправно тикает с указанной частотой. Я также пытался ставить ADC_SoftwareStartConv(ADC1); в прерывание таймера (метод а-ля AVR), получил пустой буфер с нулями. На ноге PA0 (канал 0) висит потенциометр для отладки. От его положения соответственно ничего не зависит. Также интересует, как установить прерывание DMA по заполнению буфера. Примеров не нашёл. В примере ADC3_DMA от ST (архив STM32F4xx_DSP_StdPeriph_Lib_V1.0.1) тупо выводит на дисплей 2-байтовую переменную, которую заполняет DMA. Но это ж некошерно ... Помогите, кто может. Спасибо. PS. Плата STM32F4Discovery, проц STM32F4VGT6. Дисплей графический, ILI9320, работает по FSMC. С этим всё в норме.
--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса. (с) Мария Эдуарда
|
|
|
|
|
Feb 13 2013, 08:24
|

Профессионал
    
Группа: Свой
Сообщений: 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, что и в примере выше. У меня ведь один АЦП и один канал. В итоге пока что хрень  . Кто-нибудь знает, как написать обработчик прерываний DMA? Обработчик прерываний DMA для F4: // dma2 stream 0 irq handler void DMA2_Stream0_IRQHandler ( void ) { } Только что в нём писать ?...
--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса. (с) Мария Эдуарда
|
|
|
|
|
Feb 13 2013, 18:53
|

Профессионал
    
Группа: Свой
Сообщений: 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 молчит.
--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса. (с) Мария Эдуарда
|
|
|
|
|
Feb 14 2013, 07:04
|

Профессионал
    
Группа: Свой
Сообщений: 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 } Флажки готовности и прочие программные удобства пока не приделал, но оно работает. Вечером или завтра выложу полный исходник, чтобы не уподобляться вышеуказанной теме.
--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса. (с) Мария Эдуарда
|
|
|
|
|
Feb 14 2013, 07:22
|

Гуру
     
Группа: Модераторы
Сообщений: 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)
|
|
|
|
|
Feb 15 2013, 07:37
|

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

|
Ну вот опять пошла религия  ... Пропустить амперсанд можно как в команде 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
Лично мне этот стиль не нравится. Здесь хоть комментарии есть  . А некоторым "спецам" вообще писать их облом. В итоге, чтобы понять как что делается, надо полдня посидеть глядя одним глазом в эту простыню, а другим в 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
--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса. (с) Мария Эдуарда
|
|
|
|
|
Feb 15 2013, 08:28
|

Гуру
     
Группа: Модераторы
Сообщений: 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)
|
|
|
|
|
May 20 2013, 08:19
|
Участник

Группа: Участник
Сообщений: 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);
|
|
|
|
|
May 20 2013, 11: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); если я не ошибся  Или использовать TIM2, TIM3. АЦП от них умеет запускаться и TRGO там есть ....
--------------------
Чтобы возить такого пассажира, необходим лимузин другого класса. (с) Мария Эдуарда
|
|
|
|
|
May 20 2013, 11:25
|
Участник

Группа: Участник
Сообщений: 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
|
|
|
|
|
May 20 2013, 13:15
|
Участник

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

|
Обобщаю вопрос: Кто делал запуск АЦП по любому ADC_ExternalTrigConv_Tх_CCх, а не ADC_ExternalTrigConv_Tх_TRGO любого таймера? Поделитесь кодом плз. Таймер тикает как надо, пробовал ШИМ с него, прерывания работают, а вот запустить АЦП - никак... Перепробовал все мыслимые и не очень комбинации, но лыжи не едут
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|