измеряем частоту аппаратно

Была задача измерять частоту а потом усреднять результат.
реализация на прерываниях не понравилась — сделал чисто аппаратно + дма
суть такая первый таймер отмеряет нужное время( по захвату), и дергает ДМА, ДМА забирает текущее значение и складывает в память, по переполнения счетчика ( следующий за захватом такт) происходит сброс счетчика импульсов (реализация stm32f100) но перенести на другие — не проблема, главное разобраться кто какой дма дергает)
вот рабочий код. делает 256 отсчетов и складывает в память, и так по кругу. процессор не задействовано
по завершению одного прохода ДМА, взводится флаг — данные готовы и уже можно что то считать. Прерывание ДМА дергается только один раз

//размер циклического буфера
#define FREQ_BUF_SIZE 256
//тут будут лежать частоты

unsigned short FREQ_BUF[FREQ_BUF_SIZE];

// данные уже готовы
void DMA1_Channel5_IRQHandler ( void)
{
	DMA1->IFCR |=DMA_IFCR_CGIF5;
        DMA1_Channel5->CCR &= ~DMA_CCR5_TCIE; //заблокируем повторное использование прерывания от  дма
	ValidData=1;
}

void Init()
{
//счетные таймера

TIM15->PSC=   8000-1;			//пред делитель
TIM15->ARR=   10001 ;		    //считаем до....
TIM15->CR2=   TIM_CR2_MMS_1;	//TRGO дергаем по перезагрузке таймера - для ресета счетчика 2
//TIM15->SMCR=   TIM_SMCR_MSM;
TIM15->DIER=  TIM_DIER_CC1DE;   // запрос ДМА по каналу сравнения 1
TIM15->DIER |=  TIM_DIER_CC2IE; //прерывание
TIM15->CCMR1= TIM_CCMR1_OC1M_0; //TIMx_CNT==TIMx_CCR) OCxREF =1
TIM15->CR1 =  TIM_CR1_ARPE;	
TIM15->CCR1=  1000; //кода формируем запрос ДМА
TIM15->DIER |= TIM_DIER_UIE;  //прерывание по переполнению

//таймер 2 - измеритель частоты
TIM2->ARR = 0xffff;              // макс. значение до которого считаем																 //потом прерывание по переполнению
TIM2->PSC =0 ;                // пред делитель 
TIM2->CR1 =  TIM_CR1_ARPE;
TIM2->SMCR |=TIM_SMCR_TS_0;    //в режиме ведомого от таймера 15 ITR1 (TS = 001)
TIM2->SMCR |=TIM_SMCR_SMS_2;  // режим 100:  “Reset”
TIM2->SMCR |= TIM_SMCR_ECE;   // Включить режим внешнего тактирования 
TIM2->SMCR |=TIM_SMCR_ETPS_1; //входную частоту делим на 4(это для моего проекта)

//настроим ДМА  для канала  5 таймера 15 
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
DMA1_Channel5->CMAR = (uint32_t) FREQ;       //адрес буфера приемника
//Определяемся с устройствами, которые будут подключены к каналам 2 и 3:
DMA1_Channel5->CPAR = (uint32_t) &TIM2->CNT;      //адрес регистра данных передатчика 
//Задаем количество данных для обмена:
DMA1_Channel5->CNDTR = FREQ_BUF_SIZE;            //размер буфера
DMA1_Channel5->CCR = DMA_CCR5_MINC;                //запись в память, инкремент указателя в памяти
DMA1_Channel5->CCR |= DMA_CCR5_TCIE;               //канал 5
DMA1_Channel5->CCR|= DMA_CCR5_CIRC;//циклически
DMA1_Channel5->CCR|= DMA_CCR5_MSIZE_0;
DMA1_Channel5->CCR|= DMA_CCR5_PSIZE_0; //по 16 бит
DMA1_Channel5->CCR|= DMA_CCR5_TCIE; //прерывание по завершению
DMA1_Channel5->CCR |= DMA_CCR5_EN; //разрешим работу
TIM15->CR1 |= TIM_CR1_CEN ;
TIM2->CR1 |=TIM_CR1_CEN;
NVIC_EnableIRQ (DMA1_Channel5_IRQn);  
}

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

Комментарии (14)

RSS свернуть / развернуть
Какая наибольшая измеряемая частота?
0
определяется только возможностями кристала
В данной реализации — 8 мГц кварц, процессор работает на 8 мГц время измерения 1 сек.
предделитель на 8 — входня частото до 512 кГц
0
Позвольте немного поворчать:
мГц — это миллиГерц? Или всё-таки Мега? Да, мы конечно понимаем, что Мега. Миллигерцами тактировать контроллер никто не будет. Ну а 10 мОм — это сколько? Вот тут уже сложнее…
Дядюшку Герца и дядюшку Ома уважаем, пишем с большой буквы. Так давайте же уважать и десятичные приставки.
+2
Добрый день!
В этой строке правильно ли указано значение? вроде 1001 должно быть!
TIM15->ARR= 10001; //считаем до…
Я повторил ваш проект на 103с8 но вместо таймера 15 использовал 3 все вроде работает,
но никак не могу разобраться нет сброса CNT второго таймера, правильно ли я понял он ведь должен сбрасываться в конце каждой секунды?
0
проблема в том, что есть жесткая связь между ведущий-ведомый, и в разных сериях — эта зависимость разная.
TIM15->ARR= 10001; //считаем до — ту да — нолик лишний
если сообщите какие таймеры используете, я покажу как настроить ваш.
0
Таймер2 так же измеритель частоты, Таймер3 ведущий.
0
смотрим референс мануал
www.st.com/web/en/resource/technical/document/reference_manual/CD00171190.pdf
страница 401
табличка 86 кто кому что передает
для TIM2 — ведущий TIM3 — ITR2 (TS = 010)
TIM2->SMCR |=TIM_SMCR_TS_1;
0
TIM2->SMCR |=TIM_SMCR_TS_0;
C этим значением работает видно что в ОЗУ пишутся значения из CNT.
если меняю на TIM2->SMCR |=TIM_SMCR_TS_1; то в ОЗУ одни ноли!
0
правильны ли эти значения?
TIM3->DIER |= TIM_DIER_CC4IE;
TIM3->CCMR1= TIM_CCMR1_OC1M_0;
0
вот как в рабочем проекте
TIM2->ARR = 0xffff;              // считаем до. нужен, ловить переполнение счетчика
TIM2->PSC =0 ;                // предделитель
TIM2->CR1 =  TIM_CR1_ARPE;
TIM2->SMCR |=TIM_SMCR_TS_0;    //получаем команды  от 15 ITR1 (TS = 001)
TIM2->SMCR |=TIM_SMCR_SMS_2;  // это команда   “Reset”

то есть, если по переполнению 15 таймера — сбросить таймер 2
так надо еще дма заставить дергать от таймера 3
таймер 3 сомпаре 1 это ДМА 1 канал 6 (стр 281)
0
Вдогонку, если дма6 занят, можно перевести на другие, но изменить используемый канал захвата
(в той же табличке видно, что кому соответствует)
0

Так и не добился я сброса CNT Tim2 Вот на рисунке видно весь код и память при отладке, счетчик досчитал до определенного значения и при каждой следующей записи пишет только его, не сбрасывая! а кроме обработчика прерывания от ДМА, нужно ли создавать обработчик прерывания от Tim3?
0
ну если частота не плавает, то и должно так быть быть. если бы счетчик не сбрасывался, то было бы увеличение значения.
давай код в удобоворимом варианте, я посмотрю на макетке
0
Нет частота подавалась только в начале на первых секундах! а дальше не подавалась поэтому должны быть 0000
вот код:
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
TIM3->PSC = 36000-1; // Настраиваем делитель чтоб таймер тикал 2000 раз в секунду
TIM3->ARR = 2001; // Чтоб прерывание случалось раз в секунду
TIM3->CR2 = TIM_CR2_MMS_1; // сброс ТIM2
TIM3->DIER= TIM_DIER_CC4DE; // запрос ДМА по каналу сравнения
TIM3->DIER |= TIM_DIER_CC4IE; //прерывание
TIM3->CCMR1= TIM_CCMR1_OC1M_0; //TIMx_CNT==TIMx_CCR) OCxREF =1
TIM3->CR1 = TIM_CR1_ARPE;
TIM3->CCR1= 2000; //кода формируем запрос ДМА
TIM3->DIER |= TIM_DIER_UIE; //разрешаем прерывание от таймера
//таймер 2 — измеритель частоты
TIM2->ARR = 0xffff; // макс. значение до которого считаем
TIM2->PSC =0; // пред делитель
TIM2->CR1 = TIM_CR1_ARPE;
TIM2->SMCR |=TIM_SMCR_TS_0; //в режиме ведомого от таймера 3
TIM2->SMCR |=TIM_SMCR_SMS_2; // режим 100
TIM2->SMCR |= TIM_SMCR_ECE; // Включить режим внешнего тактирования

//настроим ДМА
RCC->AHBENR |= RCC_AHBENR_DMA1EN;
DMA1_Channel3->CMAR = (uint32_t) 0X20001000; //адрес буфера приемника
DMA1_Channel3->CPAR = (uint32_t) &TIM2->CNT; //регистр данных
DMA1_Channel3->CNDTR = FREQ_BUF_SIZE; //размер буфера
DMA1_Channel3->CCR = DMA_CCR3_MINC; //запись в память, инкремент указателя в памяти
DMA1_Channel3->CCR |= DMA_CCR3_TCIE; //канал 3
DMA1_Channel3->CCR |= DMA_CCR3_CIRC; //циклически
DMA1_Channel3->CCR |= DMA_CCR3_MSIZE_0;
DMA1_Channel3->CCR |= DMA_CCR3_PSIZE_0; //по 16 бит
DMA1_Channel3->CCR |= DMA_CCR3_TCIE; //прерывание по завершению
DMA1_Channel3->CCR |= DMA_CCR3_EN; //разрешим работу
TIM3->CR1 |= TIM_CR1_CEN; // Начать отсчёт!
TIM2->CR1 |=TIM_CR1_CEN;
NVIC_EnableIRQ (DMA1_Channel3_IRQn);
//NVIC_EnableIRQ (TIM3_IRQn);
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.