hd44780 0 12 февраля, 2013 Опубликовано 12 февраля, 2013 · Жалоба Привет всем. Надо запускать 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. С этим всё в норме. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
hd44780 0 12 февраля, 2013 Опубликовано 12 февраля, 2013 · Жалоба Может из-за этого: ADC_InitStructure.ADC_NbrOfConversion = 0; ? Хотя это где как - где-то видел 1, где-то 0 ... В доке пока не понял, на что оно влияет :( . Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
hd44780 0 13 февраля, 2013 Опубликовано 13 февраля, 2013 · Жалоба Вот, нашёл такое на одном из форумов: // адрес регистра данных, откуда будем брать результат конвертации #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 ) { } Только что в нём писать ?... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
hd44780 0 13 февраля, 2013 Опубликовано 13 февраля, 2013 · Жалоба Вот так заработал: 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 молчит. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
hd44780 0 14 февраля, 2013 Опубликовано 14 февраля, 2013 · Жалоба Да-а, видать не по сеньке шапка...... Ладно, я разобрался. Работающее и не виснущее прерывание: 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 } Флажки готовности и прочие программные удобства пока не приделал, но оно работает. Вечером или завтра выложу полный исходник, чтобы не уподобляться вышеуказанной теме. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 119 14 февраля, 2013 Опубликовано 14 февраля, 2013 · Жалоба Ошибка была в первой строчка. Я взятие адреса & пропустил...Еще один гвоздь в крышку гроба этой библиотеки. Если уж делали структуру и одно из полей должно содержать указатель, то какого черта они не объявили это поле как void * и не спрятали приведение к uintptr_t внутрь библиотеки? Нет уж, нафиг. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
hd44780 0 15 февраля, 2013 Опубликовано 15 февраля, 2013 · Жалоба Ну вот опять пошла религия ... Пропустить амперсанд можно как в команде 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 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 119 15 февраля, 2013 Опубликовано 15 февраля, 2013 · Жалоба Пропустить амперсанд можно как в команде 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; } Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
digitAll 0 20 мая, 2013 Опубликовано 20 мая, 2013 · Жалоба А есть ли какие-нибудь заморочки при запуске АЦП от Т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); Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
hd44780 0 20 мая, 2013 Опубликовано 20 мая, 2013 · Жалоба Вы задаёте АЦП триггер 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 там есть .... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
digitAll 0 20 мая, 2013 Опубликовано 20 мая, 2013 (изменено) · Жалоба Нужен именно Т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); не запускается. скорее всего где-то что-то не взвел... но где? Изменено 20 мая, 2013 пользователем digitAll Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
digitAll 0 20 мая, 2013 Опубликовано 20 мая, 2013 · Жалоба Обобщаю вопрос: Кто делал запуск АЦП по любому ADC_ExternalTrigConv_Tх_CCх, а не ADC_ExternalTrigConv_Tх_TRGO любого таймера? Поделитесь кодом плз. Таймер тикает как надо, пробовал ШИМ с него, прерывания работают, а вот запустить АЦП - никак... Перепробовал все мыслимые и не очень комбинации, но лыжи не едут :( Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Polaris 0 26 июня, 2020 Опубликовано 26 июня, 2020 · Жалоба On 5/20/2013 at 3:15 PM, digitAll said: Обобщаю вопрос: Кто делал запуск АЦП по любому ADC_ExternalTrigConv_Tх_CCх, а не ADC_ExternalTrigConv_Tх_TRGO любого таймера? Поделитесь кодом плз. Таймер тикает как надо, пробовал ШИМ с него, прерывания работают, а вот запустить АЦП - никак... Перепробовал все мыслимые и не очень комбинации, но лыжи не едут :( Аналогичная ситуация, STM32F446, ADC_ExternalTrigConv_T1_CCх не захотел работать ни под каким соусом. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
ivan24190 0 26 июня, 2020 Опубликовано 26 июня, 2020 (изменено) · Жалоба 5 hours ago, Polaris said: Аналогичная ситуация, STM32F446, ADC_ExternalTrigConv_T1_CCх не захотел работать ни под каким соусом. Для того чтобы АЦП запускался по событию 'CaptureCompare', необходимо в регистре CCER таймера установить бит разрешения нужного канала, например TIM3->CCER |= TIM_CCER_CC1E; При этом АЦП уже должен быть настроен на запуск от данного события. Изменено 26 июня, 2020 пользователем ivan24190 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Polaris 0 15 июля, 2020 Опубликовано 15 июля, 2020 · Жалоба On 6/26/2020 at 8:59 PM, ivan24190 said: Для того чтобы АЦП запускался по событию 'CaptureCompare', необходимо в регистре CCER таймера установить бит разрешения нужного канала, например TIM3->CCER |= TIM_CCER_CC1E; При этом АЦП уже должен быть настроен на запуск от данного события. Ставил, эффект нулевой, что работает с третьим, не работает с первым. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться