Помогите, STM32 примитивный частотомер

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
Frosty85
Встал на лапы
Сообщения: 84
Зарегистрирован: Ср окт 22, 2014 04:24:01

Помогите, STM32 примитивный частотомер

Сообщение Frosty85 »

Доброго времени, форумчане. Решил изучить ARM (на камне stm32f105rb) дошел до таймеров, принцип работы более или менее мне понятен, в плане инициализации и работы.
Но вот настроить таймер на работу как обычный частотомер немогу ((((
Помогите решить поставленную задачку.
Есть сигнал частота его от 0,00Гц до 1600,00Гц нужно его вычислить и записать в переменную пределы которой 0х0000 до 0х0Е10
Или второй сигнал его частота от 0,00Гц до 400,00Гц который нужно записать в переменную с пределами 0х0000 до 0х1F40
Оба сигнала чистый меандр.

STM32 тактируется от внешнего кварца частотой 16МГц
SYSCLK=72МГц
HCLK=72МГц
PCCLK1/PCCLK2=36МГц
APB1P/APB2P=36МГц
В итоге на таймерах у меня тактовая частота = 72МГц

Попытался реализовать счет на таймере TIM6 с работой по внешнему прерыванию. Считает не корректно и значение обратно пропорционально получается (при махf - 0х0000 при minf - 0xFFFF) Вот код:
Спойлер

Код: Выделить всё

#include "st.h"
#include "hw_config.h"

#define OCCUPIED 0xFC
#define FREE     0
#define SET      0xEE



float K_div = 1; // коэфициент деления частоты
unsigned char Int_Market;
uint16_t Result, Int_period_time;
unsigned int temp;
uint16_t  dobavka =0;

uint16_t temp1;
uint16_t temp2;
unsigned char ex; 		//dobavka = 0x14FC  ex = 0xFC ex2 = 0x14
unsigned char ex2;



//-------------------------------------------------------------------------------------------------------
// расчет и преобразование сигнала
//--------------------------------------
//Обработка внешнего прерывания на PA10
//--------------------------------------

void EXTI15_10_IRQHandler(void)
{
			 /* нужно проверять состояние линии прерывания  */
			if (EXTI_GetITStatus(EXTI_Line10) != RESET) {
			EXTI_ClearITPendingBit(EXTI_Line10);  // сбрасываем флаг прерывания
			switch (Int_Market) {
	                          case FREE:
		                                  Int_period_time = TIM_GetCounter(TIM6);
					    TIM_SetCounter(TIM6, 0xCCCC);
	                                    Int_Market=SET;
		                                  break;		                                 
}
}
}

void Measurement(void)
{
  Result =(unsigned int)(((unsigned long)(Int_period_time/K_div )+1) >>1);
  Int_Market=FREE;
}	




void Basis_cycle(void)
{
if (Int_Market == SET)
{
	Measurement(); //вычисляем период входного импульса
	dobavka = Result;
	convert(dobavka);
}
}








//--------------------------------------
//// Настроим таимер для подсчета импульсов входящей скорости
//--------------------------------------

void Speed_init (void)
{
					
					TIM_TimeBaseInitTypeDef TIM_InitStructure;
	
					RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
					TIM_TimeBaseStructInit(&TIM_InitStructure);
					TIM_InitStructure.TIM_Prescaler=65535-1;
					TIM_InitStructure.TIM_Period=0xFFFF;
					TIM_InitStructure.TIM_CounterMode = TIM_CounterMode_Down;
					TIM_TimeBaseInit(TIM6,&TIM_InitStructure);
					TIM_Cmd(TIM6,ENABLE);
		
					NVIC_EnableIRQ(TIM6_IRQn);
					
					TIM_Cmd(TIM6, ENABLE);
}
//--------------------------------------
//Конвертация результата из 16 в два по 8
//--------------------------------------

void convert(uint16_t dobavka){
   
   for(x=0;x<8;x++){
    temp1=dobavka;
		 temp1=temp1>>8; 
		 temp1=temp1>>x; 
      temp1&=~0xFFFE;
   switch(temp1){
    case 0: ex2=ex2>>1; ex2&=~0x80; break;
    case 1: ex2=ex2>>1; ex2|=0x80;  break;
           default:break; }
  }
  x=0;
   
   for(x=0;x<8;x++){
     temp2=dobavka;
    temp2=temp2>>x; 
		 temp2&=~0xFFFE;
   switch(temp2){
    case 0: ex=ex>>1; ex&=~0x80; break;
    case 1: ex=ex>>1; ex|=0x80;  break;
           default:break; }
  }
  x=0;
}
Последний раз редактировалось Frosty85 Сб апр 09, 2016 12:50:37, всего редактировалось 2 раза.
Реклама
uk8amk
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Re: Помогите, STM32 примитивный частотомер

Сообщение uk8amk »

Frosty85 писал(а):В итоге на таймерах у меня тактовая частота = 72МГц
36 потому что таймеры не могут работать быстрее периферийной шины.
Frosty85 писал(а):Попытался реализовать счет на таймере TIM6 с работой по внешнему прерыванию.
Для реализации нормального частотомера нужно 2 таймера: один формирует интервал времени счёта, другой в режиме Slave с внешним тактированием. Или наоборот для метода обратного счёта.
Период слишком медленных сигналов можно фиксировать входом захвата.
Через прерывание EXTI в принципе тоже можно, но высокой точности не получить.
Реклама
Frosty85
Встал на лапы
Сообщения: 84
Зарегистрирован: Ср окт 22, 2014 04:24:01

Re: Помогите, STM32 примитивный частотомер

Сообщение Frosty85 »

Я так понимаю есть два варианта вычисления частоты
1. Прямой счет
2. Обратный счет (Reciprocal counting)
В моем данном случае, при низких частотах лучше использовать обратный счет?
Т.е. чтобы мне задействовать PA10 для входящего частотного сигнала мне нужно TIM1 настроить для считывания количества импульсов по фронту входного сигнала, а TIM6 перестроить на One pulse mode. Правильно ли я понимаю.

И попутно при прямом счете, что реализован в моем коде, какова будет погрешность в пределах заданных частот измерений? +- 1-2Гц не кретично будет для меня.
Frosty85
Встал на лапы
Сообщения: 84
Зарегистрирован: Ср окт 22, 2014 04:24:01

Re: Помогите, STM32 примитивный частотомер

Сообщение Frosty85 »

36 потому что таймеры не могут работать быстрее периферийной шины.
согласно Кубу на шине таймеров все 72МГц присутствуют
СпойлерИзображение
Вложения
SysCLK.jpg
(59.06 КБ) 991 скачивание
Реклама
Эиком - электронные компоненты и радиодетали
kybin
Первый раз сказал Мяу!
Сообщения: 28
Зарегистрирован: Пн мар 17, 2014 10:37:29

Re: Помогите, STM32 примитивный частотомер

Сообщение kybin »

На этом форуме есть пара тем по вашему вопросу и даже пара готовых реализаций, одна под F0, другая F4. И алгоритмы прописаны достаточно подробно.
В вашем случае обратный счёт предпочтительнее, особенно если это какой-нить тахометр.
2 таймера минимум - один считает импульсы, другой внутреннюю частоту. Для периода измерения 1 секунда опорный таймер должен иметь счётный регистр 32 бита, либо 16 бит + ещё один таймер расширяющий счётный регистр опорного.
Посмотрите реализацию Гализина https://github.com/OlegGalizin/fmeter-stm32f030f4
Реклама
Frosty85
Встал на лапы
Сообщения: 84
Зарегистрирован: Ср окт 22, 2014 04:24:01

Re: Помогите, STM32 примитивный частотомер

Сообщение Frosty85 »

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


P.S. Еще маленький вопросик как инвертировать значение, т.е. к примеру есть значение от 0x00 до 0xFF мне его нужно перевернуть в 0xFF до 0x00 (все разобрался обычное инвертирование temp=~temp)
Реклама
uk8amk
Поставщик валерьянки для Кота
Сообщения: 2222
Зарегистрирован: Вт ноя 27, 2007 11:32:06
Откуда: Tashkent

Re: Помогите, STM32 примитивный частотомер

Сообщение uk8amk »

Frosty85 писал(а):согласно Кубу
Я привык ориентироваться по даташитам(APB1 : Fmax = 36 MHz). Но в этих ваших кубах на картинке тоже на PCLK1 выдаётся не более 36МГц. Так что F TIM6=36МГц.

Для прямого счёта дискретность считывания зависит от интервала накопления. Для 0.1сек 10Гц, 1 сек 1Гц, 10 сек 0.1Гц и т.д. Не забываем что последняя цифра имеет право прыгать в диапазоне +/-1 единица.
Frosty85
Встал на лапы
Сообщения: 84
Зарегистрирован: Ср окт 22, 2014 04:24:01

Re: Помогите, STM32 примитивный частотомер

Сообщение Frosty85 »

:facepalm: Ребята простите за мою тупость, но я уже бьюсь над этой задачей неделю. Взяв во внимание что начал я свое знакомство с программированием да и в целом с АРМ три недели тому назад.

Итак если я правильно понимаю берется два таймера. Первый таймер отсчитывает ровно единицу времени (1сек) и второй таймер который считает количество поступивших импульсов на вход за единицу времени (1сек). первый таймер запускается внешним прерыванием от входящего импульса и по переполнению дает сброс второго таймера и одновременно записывает значение второго таймера в переменную. Эта переменная и есть искомое значение частоты на входе. Т.е пришло 3 импульса за секунду на выходе получаем 0х0003 -что равняется частоте в 3Гц.

Помогите решить задачу (((( у меня мозг уже кипит.........
Galizin
Мучитель микросхем
Сообщения: 478
Зарегистрирован: Ср окт 15, 2008 09:33:03
Откуда: Воронеж

Re: Помогите, STM32 примитивный частотомер

Сообщение Galizin »

http://radiokot.ru/forum/viewtopic.php?f=59&t=102175 - вот тема. Там есть сообщение тов HHIMERA и мое с описанием алгоритма. НУ и общую теорию про таймер с обратным отсчетом смотрите. Например у хоровица хилла. или где в интернете.
Аватара пользователя
scorpi_0n
Вымогатель припоя
Сообщения: 616
Зарегистрирован: Вс ноя 01, 2015 13:13:49

Re: Помогите, STM32 примитивный частотомер

Сообщение scorpi_0n »

Frosty85 писал(а): Итак если я правильно понимаю берется два таймера. Первый таймер отсчитывает ровно единицу времени (1сек) и второй таймер который считает количество поступивших импульсов на вход за единицу времени (1сек). первый таймер запускается внешним прерыванием от входящего импульса и по переполнению дает сброс второго таймера и одновременно записывает значение второго таймера в переменную. Эта переменная и есть искомое значение частоты на входе. Т.е пришло 3 импульса за секунду на выходе получаем 0х0003 -что равняется частоте в 3Гц.
Не так. Совсем не так. Выбираем первый, счётный, таймер. Особые требования к нему в вашем случае не нужны. Главное чтобы он мог быть слэйвом второго, временного окна. Если нужно мерять две частоты - берём два таких счётных таймера, если три частоты - три. Выбираем второй, временного окна, таймер. Здесь тоже главное чтобы он мог быть мастером. Дальше всё просто. Первый(е) настраиваем на внешний счёт и в режиме слэйва, второй в режиме мастера и ОРМ. Теперь стартуем второй, временного окна, таймер, который в свою очередь стартует хардварно первый(е), счётный(е), таймер(а). По окончанию временного окна второй таймер отключается хардварно и останавливает хардварно первый(е) таймера. Можно забирать данные.
Novice user
Мудрый кот
Сообщения: 1704
Зарегистрирован: Вт янв 05, 2016 10:14:25
Откуда: поселок Мелеуз

Re: Помогите, STM32 примитивный частотомер

Сообщение Novice user »

извините что вмешался-а какую максимальную частоту можно замерить частотомером на STM32?
Аватара пользователя
scorpi_0n
Вымогатель припоя
Сообщения: 616
Зарегистрирован: Вс ноя 01, 2015 13:13:49

Re: Помогите, STM32 примитивный частотомер

Сообщение scorpi_0n »

В общем случае в случае меандра теоретически до Fclk/2. Реально меньше. Где-то до ~Fclk/2,4.
По входу ETR до (Fclk/2)*8 и (Fclk/2,4)*8 соответственно. Длительность входных импульсов в данном случае уже такого влияния не имеет.
В случае необходимости получения наиболее точного результата от PLL придётся отказаться. Максимальная измеряемая частота тогда будет определяться частотой кварца или TCXO.
Frosty85
Встал на лапы
Сообщения: 84
Зарегистрирован: Ср окт 22, 2014 04:24:01

Re: Помогите, STM32 примитивный частотомер

Сообщение Frosty85 »

uk8amk писал(а):
Frosty85 писал(а):согласно Кубу
Я привык ориентироваться по даташитам(APB1 : Fmax = 36 MHz). Но в этих ваших кубах на картинке тоже на PCLK1 выдаётся не более 36МГц. Так что F TIM6=36МГц.

Для прямого счёта дискретность считывания зависит от интервала накопления. Для 0.1сек 10Гц, 1 сек 1Гц, 10 сек 0.1Гц и т.д. Не забываем что последняя цифра имеет право прыгать в диапазоне +/-1 единица.

Вот и внимательней прочитайте даташит в котором черным по белому написано что частота таймеров согласно структуре имеет свой предумножитель который имеет два значения *1 и *2 частоту шины APB1
СпойлерИзображение
А то я весь мозг себе сломал почему частоты не совпадают и получается полная чушь на выходе.
Вложения
Частота.jpg
(108.35 КБ) 1011 скачиваний
Frosty85
Встал на лапы
Сообщения: 84
Зарегистрирован: Ср окт 22, 2014 04:24:01

Re: Помогите, STM32 примитивный частотомер

Сообщение Frosty85 »

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

Код: Выделить всё

//-------------------------------------------------------------------------------------------------------
// расчет и преобразование сигнала
//--------------------------------------
//Обработка внешнего прерывания на PA10
//--------------------------------------

void EXTI15_10_IRQHandler(void)
{
          /* нужно проверять состояние линии прерывания  */
         if (EXTI_GetITStatus(EXTI_Line10) != RESET) {
         EXTI_ClearITPendingBit(EXTI_Line10);  // сбрасываем флаг прерывания
         switch (Int_Market) {
                             case FREE:
                                        Int_period_time = TIM_GetCounter(TIM6);
                   TIM_SetCounter(TIM6, 0xCCCC);
                                       Int_Market=SET;
                                        break;                                       
}
}
}

void Measurement(void)
{
  Result =(unsigned int)(((unsigned long)(Int_period_time/K_div )+1) >>1);
  Int_Market=FREE;
} 
В моем случае измерялся период и сбрасывался счетчик в 0 а результат заносился в переменную (я думал что это и есть частота) которая была периодом а не частотой.

Правильней надо так:
Спойлер

Код: Выделить всё

void EXTI15_10_IRQHandler(void)
{
			 /* нужно проверять состояние линии прерывания Speed */
			if (EXTI_GetITStatus(EXTI_Line10) != RESET) {
			EXTI_ClearITPendingBit(EXTI_Line10);  // сбрасываем флаг прерывания
			switch (Int_Market) {
	                          case FREE:
		                                  Int_Start_Time = TIM_GetCounter(TIM6);
	                                    Int_Market=OCCUPIED;
		                                  break;
	                          case OCCUPIED:
		                                  Int_Stop_Time = TIM_GetCounter(TIM6);
	                                    Int_Market=SET;
		                                  break;
}
}
}			
void Measurement(void)
{
	if (Int_Start_Time < Int_Stop_Time)
	{
		Result = Int_Stop_Time - Int_Start_Time;
	}
	else
	{
	  Result = (0xFFFF - Int_Start_Time) +Int_Stop_Time;
	}	
	FreqIn = 2000/ Result;  // 2000 - частота таймера после делителя.
	Speed = K_prav*(FreqIn/K_div);
  Int_Market=FREE;
Frosty85
Встал на лапы
Сообщения: 84
Зарегистрирован: Ср окт 22, 2014 04:24:01

Re: Помогите, STM32 примитивный частотомер

Сообщение Frosty85 »

Последний вопрос правильно ли я настроил внешние прерывания.
Спойлер

Код: Выделить всё

void	nvic_init(void)
{
		NVIC_InitTypeDef 				NVIC_InitStructure;	// контроллера прерываний
		EXTI_InitTypeDef   			EXTI_InitStructure;
//назначим прерывание при нажатии кнопки по отрицательному фронту
//PA10 может быть подключен к линии прерывания EXTI10
//PA9 может быть подключен к линии прерывания EXTI9
//Назначаем порт и вывод источника прерывания
		GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource10);
		GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource9);
		//выбираем линию для прерывания  EXTI
		EXTI_InitStructure.EXTI_Line = EXTI_Line10 | EXTI_Line9;
		//выбираем режим что прерывание 
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
		//выбираем по отрицательному фронту
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
		//разрешаем прерывание
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;
		//инициализируем
    EXTI_Init(&EXTI_InitStructure);

		//configure NVIC
    //select NVIC channel to configure
    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn |EXTI9_5_IRQn ;
    //set priority to lowest
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0x0F;
    //set subpriority to lowest
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0x0F;
    //enable IRQ channel
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    //update NVIC registers
    NVIC_Init(&NVIC_InitStructure);
		
    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn | EXTI9_5_IRQn;
    NVIC_Init(&NVIC_InitStructure);
}
Почему-то не работает ((((

Настройка портов:
Спойлер

Код: Выделить всё


//вход внешнего прерывания
//PA10
#define Speed_GPIO_PIN				GPIO_Pin_10	
#define Speed_GPIO_PORT 			GPIOA
#define Speed_PORT_CLK   			RCC_AHBPeriph_GPIOA

//вход внешнего прерывания
//PA9
#define Tacho_GPIO_PIN				GPIO_Pin_9	
#define Tacho_GPIO_PORT 			GPIOA
#define Tacho_PORT_CLK   			RCC_AHBPeriph_GPIOA

void InitPins(){

//это добавлено	
//	GPIO_InitTypeDef   			GPIO_InitStructure;

//PA10
//Настроим вход внешнего прерывания
		GPIO_StructInit(&GPIO_InitStructure);
		GPIO_InitStructure.GPIO_Pin = Speed_GPIO_PIN;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;
  	GPIO_Init(Speed_GPIO_PORT, &GPIO_InitStructure);

//PA9
//Настроим вход внешнего прерывания
		GPIO_StructInit(&GPIO_InitStructure);
		GPIO_InitStructure.GPIO_Pin = Tacho_GPIO_PIN;
  	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
  	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz ;
  	GPIO_Init(Tacho_GPIO_PORT, &GPIO_InitStructure);
}
Galizin
Мучитель микросхем
Сообщения: 478
Зарегистрирован: Ср окт 15, 2008 09:33:03
Откуда: Воронеж

Re: Помогите, STM32 примитивный частотомер

Сообщение Galizin »

Frosty85 писал(а):частота таймеров согласно структуре имеет свой предумножитель который имеет два значения *1 и *2 частоту шины APB1
Да этот штепсель подходит к розетке 110, 220 и 380.
Frosty85
Встал на лапы
Сообщения: 84
Зарегистрирован: Ср окт 22, 2014 04:24:01

Re: Помогите, STM32 примитивный частотомер

Сообщение Frosty85 »

так я и не смог настроить внешние прерывания по 9_5 портам. Кинул на 11 порт в этом же обработчике прерываний.
Galizin
Мучитель микросхем
Сообщения: 478
Зарегистрирован: Ср окт 15, 2008 09:33:03
Откуда: Воронеж

Re: Помогите, STM32 примитивный частотомер

Сообщение Galizin »

У меня получалось http://radiokot.ru/circuit/digital/measure/113/
Ответить

Вернуться в «ARM»