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

Была задача измерять частоту а потом усреднять результат.
реализация на прерываниях не понравилась — сделал чисто аппаратно + дма
суть такая первый таймер отмеряет нужное время( по захвату), и дергает ДМА, ДМА забирает текущее значение и складывает в память, по переполнения счетчика ( следующий за захватом такт) происходит сброс счетчика импульсов (реализация 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);  
}

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