Страница 6 из 12
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Пн ноя 24, 2014 11:52:42
dosikus
Леонид Иванович писал(а):
Начнем с того, что он не будет ничем хорош. Программный справляется точно так же, плюс имеем бонусы в виде свободного назначения ног, легкой переносимости кода и т.д., обо всем этом я уже писал. Есть случаи, когда аппаратное решение весьма желательно, например, при реализации 1-Wire, там критичны времена. Но тот же 1-Wire реализуется в STM32 через одно место, возмущает, что при столь развитой периферии не нашлось места нескольким аппаратным портам 1-Wire.
Все эти плюсы актуальны лишь для приверженцев софтового ногодрыга и ЛУТ.
Касательно 1wire это вообще смех.
Вам предоставлена мощнейшая периферия , своего рода конструктор - соберите из этих элементов 1wire.
Озвучу сразу - оно есть и работает (аппаратный 1wire) :

Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Пн ноя 24, 2014 12:09:51
Galizin
alexf58 писал(а): Если использовать DMA для чтения состояния счетчика
Я не очень понял Вашу идею. Она выглядит как успеть прочитать захваченное состояние до того как пришел следующий импульс входного сигнала? По моему основные копья здесь связаны не с успеть прочитать, а сформировать нужным образом сигнал захвата. Не слишком рано, что бы прошел хотя бы 1 период входной частоты от предыдущего, и не поздно, что бы не слишком вылезти из интервала измерения и не потерять информацию, которую можно было бы померять.
Время захвата рассчитывается исходя из входной частоты, которая может меняться в широких пределах. А расчитывать может только процессор например в прерывании.
Леонид Иванович писал(а):Было бы заманчиво построить частотомер на STM32 с аналоговым интерполятором
По моему неразрешимых проблем нет. Единственно использовать его можно будет до Fref/2. Могут возникнуть задачи синхронизации внутренних счетчиков и интерполятора. Но по моему они разрешимы. Если есть желание - можно пообсуждать. Я еще пока не очень хорошо сформулировал как это должно работать для себя.
Леонид Иванович писал(а):Девиация Аллана - это всего лишь мера нестабильности частоты
Да похоже что действительно так. Я так закопался что бы узнать что это такое, что забыл зачем оно было нужно в статье. Оказывается только что бы показать что их частотомер ее измеряет.
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Пн ноя 24, 2014 12:40:16
Леонид Иванович
dosikus писал(а):Вам предоставлена мощнейшая периферия , своего рода конструктор - соберите из этих элементов 1wire.
Я не могу собрать - не получается. Поэтому это плохая периферия, раз собрать из нее что-то могут лишь редкие люди. Для реализации 1-Wire использую UART, но их мало, если понадобится несколько портов - труба. Можно немного улучшить положение, использовав remap, но не сильно. Да и реализация на UART не такая простая. Хотелось бы проще - записал байт в регистр - он улетел по 1-Wire.
Спойлер
Код: Выделить всё
//----------------------------------------------------------------------------
//Модуль поддержки термометра DS18B20, заголовочный файл
//----------------------------------------------------------------------------
#ifndef THERM_H
#define THERM_H
//----------------------------- Константы: -----------------------------------
#define TEMP_MIN 0 //минимальная измеряемая температура, *0.1°C
#define TEMP_MAX 999 //максимальная измеряемая температура, *0.1°C
#define TEMP_FAIL -1000 //код ошибки температуры
enum OwpAct_t //коды операций на шине 1-Wire
{
OWP_NONE,
OWP_RESET,
OWP_RW,
OWP_ACT,
OWP_READY,
OWP_FAIL
};
//----------------------------------------------------------------------------
//--------------------- Абстрактный класс TOwpAction: ------------------------
//----------------------------------------------------------------------------
extern "C" void USART2_IRQHandler(void);
class TOwpAction
{
private:
static char DataRd;
friend void USART2_IRQHandler(void);
protected:
volatile static OwpAct_t Action;
static char BitCounter;
static char DataWr;
char Data;
public:
TOwpAction(char data);
OwpAct_t Result;
char Value;
virtual void Start(void) = 0;
void Execute(void);
};
//----------------------------------------------------------------------------
//----------------------------- Класс TOwpReset: -----------------------------
//----------------------------------------------------------------------------
class TOwpReset : public TOwpAction
{
private:
public:
TOwpReset(char data = 0xF0) : TOwpAction(data) {};
void Start(void);
};
//----------------------------------------------------------------------------
//------------------------------- Класс TOwpRW: ------------------------------
//----------------------------------------------------------------------------
class TOwpRW : public TOwpAction
{
private:
public:
TOwpRW(char data = 0xFF) : TOwpAction(data) {};
void Start(void);
};
//----------------------------------------------------------------------------
//------------------------------ Класс TOwpTask: -----------------------------
//----------------------------------------------------------------------------
class TOwpTask
{
private:
char MaxActions;
char ActCount;
char Index;
OwpAct_t State;
bool Error;
public:
TOwpTask(char maxact);
TOwpAction** Actions;
void AddAction(TOwpAction *act);
void Start(void);
void Execute(void);
bool Done(void);
bool Fail(void);
};
//----------------------------------------------------------------------------
//----------------------------- Класс TTherm: --------------------------------
//----------------------------------------------------------------------------
class TTherm
{
private:
TGpio<PORTA, PIN2> Pin_OWPO;
TGpio<PORTA, PIN3> Pin_OWPI;
TSoftTimer *ThermTimer;
TOwpTask *OwpStartTherm;
TOwpTask *OwpReadTherm;
int16_t CalculateT(void);
bool Upd;
public:
TTherm(void);
void Execute(void);
bool Update(void);
int16_t Value;
};
//----------------------------------------------------------------------------
extern TTherm *Therm;
//----------------------------------------------------------------------------
#endif
//----------------------------------------------------------------------------
//Модуль поддержки термометра DS18B20
//----------------------------------------------------------------------------
#include "main.h"
#include "therm.h"
//------------------------------ Константы: ----------------------------------
#define BR_RESET 10417 //скорость порта для формирования RESET
#define BR_TSLOT 166667 //скорость порта для формирования TIME SLOT
#define CONVERT_TM 800 //время преобразования температуры, мс
#define BRR_RESET (APB1_CLOCK + BR_RESET / 2) / BR_RESET;
#define BRR_TSLOT (APB1_CLOCK + BR_TSLOT / 2) / BR_TSLOT;
//----------------------------------------------------------------------------
//--------------------- Абстрактный класс TOwpAction: ------------------------
//----------------------------------------------------------------------------
//----------------------------- Конструктор: ---------------------------------
TOwpAction::TOwpAction(char data)
{
Data = data;
Result = OWP_NONE;
Action = OWP_NONE;
}
//--------------------- Проверка завершения операции: ------------------------
void TOwpAction::Execute(void)
{
if((Action == OWP_READY) ||
(Action == OWP_FAIL))
{
Value = DataRd;
Result = Action;
Action = OWP_NONE;
}
}
//-------------------------- Прерывание USART2: ------------------------------
volatile OwpAct_t TOwpAction::Action;
char TOwpAction::BitCounter;
char TOwpAction::DataRd;
char TOwpAction::DataWr;
void USART2_IRQHandler(void)
{
//прерывание USART по завершению передачи:
if(USART2->SR & USART_SR_TC)
{
//очистка флага прерывания:
USART2->SR &= ~USART_SR_TC;
//выполняется сброс:
if(TOwpAction::Action == OWP_RESET)
{
TOwpAction::DataRd = USART2->DR;
//импульс presence находится в одном из трех битов D4..D6:
if(((TOwpAction::DataRd & 0x70) != 0x70) &&
//бит D7 должен быть единичным, иначе это замыкание линии
//на землю, а не присутствие устройства:
((TOwpAction::DataRd & 0x80) == 0x80))
TOwpAction::Action = OWP_READY;
else TOwpAction::Action = OWP_FAIL;
}
//выполняется чтение/запись:
if(TOwpAction::Action == OWP_RW)
{
TOwpAction::DataRd >>= 1;
//считывается бит D1 (а не D0), так как при этом момент опроса
//лежит ближе всего к отметке 15 мкс после начала тайм-слота:
TOwpAction::DataRd |= ((USART2->DR & 2)? 0x80 : 0x00);
if(++TOwpAction::BitCounter < 8)
{
TOwpAction::DataWr >>= 1;
USART2->DR = (TOwpAction::DataWr & 1)? 0xFF : 0x00;
}
else
{
TOwpAction::Action = OWP_READY;
}
}
}
}
//----------------------------------------------------------------------------
//---------------------------- Класс TOwpReset: ------------------------------
//----------------------------------------------------------------------------
//---------------------------- Запуск операции: ------------------------------
void TOwpReset::Start(void)
{
DataWr = Data;
USART2->BRR = BRR_RESET;
USART2->DR = DataWr;
Result = OWP_NONE;
Action = OWP_RESET;
}
//----------------------------------------------------------------------------
//----------------------------- Класс TOwpRW: --------------------------------
//----------------------------------------------------------------------------
//---------------------------- Запуск операции: ------------------------------
void TOwpRW::Start(void)
{
DataWr = Data;
USART2->BRR = BRR_TSLOT;
USART2->DR = (DataWr & 1)? 0xFF : 0x00;
BitCounter = 0;
Result = OWP_NONE;
Action = OWP_RW;
}
//----------------------------------------------------------------------------
//---------------------------- Класс TOwpTask: -------------------------------
//----------------------------------------------------------------------------
//----------------------------- Конструктор: ---------------------------------
TOwpTask::TOwpTask(char maxact)
{
MaxActions = maxact;
Actions = new TOwpAction*[MaxActions];
ActCount = 0;
State = OWP_NONE;
}
//------------------------ Добавление операции: ------------------------------
void TOwpTask::AddAction(TOwpAction *act)
{
if(ActCount < MaxActions)
Actions[ActCount++] = act;
}
//---------------- Запуск последовательности операций: -----------------------
void TOwpTask::Start(void)
{
Index = 0;
Error = 0;
State = OWP_ACT;
Actions[Index]->Start();
}
//-------------------- Процесс выполнения операций: --------------------------
void TOwpTask::Execute(void)
{
if(State == OWP_ACT)
{
if(Actions[Index]->Result == OWP_READY)
{
Index++;
if(Index == ActCount)
State = OWP_READY;
else Actions[Index]->Start();
}
else if(Actions[Index]->Result == OWP_FAIL)
{
Error = 1;
State = OWP_READY;
}
else
{
Actions[Index]->Execute();
}
}
}
//------------ Проверка завершения последовательности операций: --------------
bool TOwpTask::Done(void)
{
if(State == OWP_READY)
{
State = OWP_NONE;
return(1);
}
return(0);
}
//----------------------------- Чтение ошибки: -------------------------------
bool TOwpTask::Fail(void)
{
return(Error);
}
//----------------------------------------------------------------------------
//----------------------------- Класс TTherm: --------------------------------
//----------------------------------------------------------------------------
//----------------------------- Конструктор: ---------------------------------
TTherm::TTherm(void)
{
//настройка портов:
Pin_OWPO.Init(AF_OD_2M);
Pin_OWPI.Init(IN_FLOAT);
//настройка USART2:
RCC->APB1ENR |= RCC_APB1ENR_USART2EN; //включение тактирования USART2
USART2->BRR = BRR_RESET;
//USART2->CR3 = USART_CR3_HDSEL; //работа через один пин USART2_TX (PA2)
USART2->CR1 =
USART_CR1_RE | //разрешение приемника
USART_CR1_TE | //разрешение передатчика
USART_CR1_TCIE | //разрешение прерывания по концу передачи
USART_CR1_UE; //разрешение USART2
//настройка прерываний:
NVIC_SetPriority(USART2_IRQn, 15);
NVIC_EnableIRQ(USART2_IRQn);
ThermTimer = new TSoftTimer(CONVERT_TM);
ThermTimer->Oneshot = 1;
OwpStartTherm = new TOwpTask(3);
OwpStartTherm->AddAction(new TOwpReset()); //RESET
OwpStartTherm->AddAction(new TOwpRW(0xCC)); //SKIP ROM
OwpStartTherm->AddAction(new TOwpRW(0x44)); //START CONVERSION
OwpReadTherm = new TOwpTask(5);
OwpReadTherm->AddAction(new TOwpReset()); //RESET
OwpReadTherm->AddAction(new TOwpRW(0xCC)); //SKIP ROM
OwpReadTherm->AddAction(new TOwpRW(0xBE)); //READ SCRATCHPAD
OwpReadTherm->AddAction(new TOwpRW()); //READ TL
OwpReadTherm->AddAction(new TOwpRW()); //READ TH
Upd = 0;
Value = TEMP_FAIL;
OwpStartTherm->Start(); //запуск команды START
}
//-------------- Выполнение процесса измерения температуры: ------------------
void TTherm::Execute(void)
{
OwpStartTherm->Execute(); //выполнение команды START
if(OwpStartTherm->Done()) //если команда START выполнена,
ThermTimer->Start(CONVERT_TM); //запуск таймера
if(ThermTimer->Over()) //если интервал истек,
OwpReadTherm->Start(); //запуск команды READ
OwpReadTherm->Execute(); //выполнение команды READ
if(OwpReadTherm->Done()) //если команда READ выполнена,
{
if((OwpStartTherm->Fail()) || //если ошибка,
(OwpReadTherm->Fail()))
Value = TEMP_FAIL; //код ошибки температуры
else Value = CalculateT(); //преобразование температуры
Upd = 1; //установка флага обновления
OwpStartTherm->Start(); //запуск команды START
}
}
//---------------------- Вычисление температуры: -----------------------------
int16_t TTherm::CalculateT(void)
{
char tl = OwpReadTherm->Actions[3]->Value;
char th = OwpReadTherm->Actions[4]->Value;
int16_t temp = 10 * (tl | (th << 8)) / 16;
if(temp < TEMP_MIN) temp = TEMP_MIN;
if(temp > TEMP_MAX) temp = TEMP_MAX;
return(temp);
}
//----------------------- Чтение флага обновления: ---------------------------
bool TTherm::Update(void)
{
if(Upd)
{
Upd = 0;
return(1);
}
return(0);
}
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
Galizin писал(а):По моему неразрешимых проблем нет. Единственно использовать его можно будет до Fref/2.
Понятно, что до Fref/2, но и это не представляю, как сделать. Вот на внешней логике - представляю.
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Пн ноя 24, 2014 13:00:30
Galizin
Леонид Иванович писал(а):Вот на внешней логике - представляю
Никто не говорил что без внешней логики, с использованием 1 корпуса 74ас74.
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Пн ноя 24, 2014 21:13:45
alexf58
Galizin писал(а):
Я не очень понял Вашу идею. Она выглядит как успеть прочитать захваченное состояние до того как пришел следующий импульс входного сигнала?
Если я правильно понял, то можно настроить DMA на срабатывание по событию таймера. В данном случае входной фронт измеряемого сигнала вызывает захват в счетчике, работающем на тактовой частоте (72 МГц) и очень хочется по возможности одновременно захватить значение счетчика входных импульсов. Прерывание - долго. Ждать в цикле - тоже. А DMA вроде за 6 тактов справится. Так что до 10 МГц проблем нет.
А чем плохо внутри асинхронно предварительно поделить входной сигнал, скажем на 4? Вроде точности не уменьшит?
А зачем вообще рассуждать, если HHIMERA все придумал? Интелектуальное упражнение. И для лучшего понимания переферии STM32.
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Пн ноя 24, 2014 21:23:48
HHIMERA
alexf58 писал(а):
Если я правильно понял, то можно настроить DMA на срабатывание по событию таймера. В данном случае входной фронт измеряемого сигнала вызывает захват в счетчике, работающем на тактовой частоте (72 МГц) и очень хочется по возможности одновременно захватить значение счетчика входных импульсов.
Одновременно никак не получится...
А DMA вроде за 6 тактов справится.
Не уверен... даже сильно... надо мерять...
А чем плохо внутри асинхронно предварительно поделить входной сигнал, скажем на 4? Вроде точности не уменьшит?
В 4 раза и уменьшит...
А зачем вообще рассуждать, если HHIMERA все придумал?
Рассуждайте... не отвлекайтесь...
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Пн ноя 24, 2014 22:30:36
alexf58
To что совсем одновременно не выйдет - понятно. Вопрос как минимизировать задержку, вернее успеть до следующего фронта. Про 6 тактов - так понял appnote. Не проверял.
Поясните подробнее, почему делитель на 4 уменьшит точность в 4 раза? Если прямой счет, то естественно так. А если мы измеряем время между N фронтов, частота входного сигнала не влияет на случайную погрешность. Мы отслеживаем не каждый фронт, а каждый четвертый. И что?
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Вт ноя 25, 2014 08:08:00
Леонид Иванович
А разве можно делать захват с асинхронным предделением?
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Вт ноя 25, 2014 09:31:11
balmer
alexf58 писал(а):Про 6 тактов - так понял appnote.
ПЛИС так и просится для этого проекта. Маленькая, но высокочастотная

Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Вт ноя 25, 2014 10:27:32
HHIMERA
... и коллайдер... карманный, но с перламутровыми пуговицами...

Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Вт ноя 25, 2014 11:52:10
alexf58
Леонид Иванович писал(а):А разве можно делать захват с асинхронным предделением?
Похоже нет. Не посмотрел внимательно. Ну да ладно, снаружи 74LV161A

Практического смысла по сравнению с CPLD не много конечно, но чисто умозрительно по моему делитель не ухудшит разрешения. Или я ошибаюсь?
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Вт ноя 25, 2014 12:15:08
Леонид Иванович
Делитель не ухудшит относительную погрешность измерения.
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Вт ноя 25, 2014 17:08:17
Galizin
А вот если мы имеем таймер, тактируемый внешним сигналом допустим 1 герц. Один из каналов CC настроен на захват фронта (с формированием прерывания с переключением светодиода для наглядности). На вход захвата кнопкой подаем импульс(ы). Вопрос такой, будут ли синхронизированы включения светодиода с фронтами тактирующего сигнала или с фронтами кнопки? То есть захват будет происходить по фронту тактирующего внешнего сигнала, или внутреннего?
Если захват будет синхронизирован с внешним тактированием, то есть появляться только по фронту внешнего тактирующего синала счетчика, то частотомер сделать очень просто. Таймер Fref (Tref) осуществляет захват по триггеру от таймера входного сигнала (Tin). Tin тактируется измеряемым сигналом. Один из его каналов CC настроен на захват. На этот вывод подаем фронт. Таймер Tin осуществляет захват не сразу, а по фронту тактирования (это то и надо проверить), то есть входного сигнала. По этому сигналу он генерирует сигнал триггера (011: Compare Pulse - The trigger output send a positive pulse when the CC1IF flag is to be set (even if it was already high), as soon as a capture or a compare match occurred.). Номер входного периода в регистре захвата TIn. Номер выходного периода в регистре захвата Tref. Второй импульс на вход захвата - есть вторая точка.
Но что то мне кажется, что это не будет работать. Слишком просто.
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Вт ноя 25, 2014 18:11:39
HHIMERA
Galizin писал(а):Вопрос такой, будут ли синхронизированы включения светодиода с фронтами тактирующего сигнала или с фронтами кнопки? То есть захват будет происходить по фронту тактирующего внешнего сигнала, или внутреннего?
От внутреннего... Все таймеры в STM32 синхронные... Отсюда и ограничения до половины тактирующей... Асинхронные есть у Микрочипа... да и то не везде... и унылые до не хочу...
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Вт ноя 25, 2014 19:45:36
HHIMERA
Кстати о птичках...
Леонид Иванович писал(а):
В Вашем случае формирование интервала зависимо от входного сигнала. Я не говорю про урезание интервала измерения до целого количества входных периодов - это другое. При Вашем алгоритме могут быть грубые ошибки формирования измерительного интервала. На входе может быть все, что угодно. За первую половину интервала в TIM2->CNT может насчитаться одно, а за другую половину - другое, если частота на входе меняется. Частотомер должен показать среднюю частоту, но за фиксированный интервал, а не за какое-то неопределенное время.
Посмотрел у себя что и как творится... Вот так мы плаваем...
Код: Выделить всё
// Freq Tin Tpause
//-----------------------
// 0.5Hz 2s 2s
// 0.9Hz 1.1s 1.1s
// 1Hz 1s 1s
// 2Hz 1s 0.5s
// 5Hz 1s 0.2s
// 10Hz 1s 0,1s
// 100Hz 1s 0,01s
Выше 1Hz время измерения везде 1Сек. ...
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Вт ноя 25, 2014 21:17:07
Леонид Иванович
Я имел в виду немного не то. При входном сигнале постоянной частоты нет сомнений в правильности работы алгоритма. Но сигнал на входе частотомера имеет право быть каким угодно. Скажем, пачки импульсов. Частотомер должен показать среднее значение частоты за измерительный интервал. И вот тут предложенный алгоритм может сформировать совсем неправильный измерительный интервал. Но эти проблемы встанут только при попытке допиливания алгоритма до уровня измерительного прибора.
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Вт ноя 25, 2014 23:12:08
alexf58
Если я правильно понял, вместо того чтобы смотреть сколько импульсов набрали за 0.5 сек, можно посмотреть через 1 сек и в качестве конца счета использовать это N плуюс скажем 2. Тогда даже при сильно неравномерной входной частоте будет интервал близкий с 1 сек, если, конечно, частота хотя бы 10 Гц.
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Ср ноя 26, 2014 10:06:41
Galizin
alexf58 писал(а):в качестве конца счета использовать это N плуюс скажем 2
Это если ограничить частоту сверху что бы таймер не успел прощелкнуть эти 2 такта пока производятся расчеты. Поэтому лучше все таки оценивать текущую частоту (кол-во импульсов) и на ее основе принимать решение о задержке. Задержка должна обеспечивать гарантированную настройку таймера за это время процессором.
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Ср ноя 26, 2014 10:13:29
alexf58
Естественно в зависимости от грубо определенной частоты. Я написал "скажем 2" а не 1 именно чтобы не проскочить. Если большая частота, то хоть 100. Единственная разница с "классическим методом HHIMERA" в том, что определать ме по 1/2 интервала, а по почти полному.
Re: STM32. Быстрый частотомер. Reciprocal counter.
Добавлено: Ср ноя 26, 2014 10:33:26
Леонид Иванович
Galizin писал(а):захват будет происходить по фронту тактирующего внешнего сигнала, или внутреннего?
В микроконтроллере все внешние сигналы проходят через синхронизаторы. За редким исключением, типа сигналов счетных входов, где есть асинхронные прескалеры. В частотомере на ПЛИС у меня есть два разных клок-домена, один - опорная частота, второй - входная. При этом я не имею возможности прочитать счетчик входных импульсов "на лету", он в другом домене. Сигнал GATE должен поступать на счетчики обоих доменов, его нужно пропускать через синхронизаторы. Но, к сожалению, входная частота является нерегулярной, ее может совсем не быть, или она может быть очень низкой, полноценный двухтактовый синхронизатор мы здесь себе позволить не можем. Поэтому остается риск метастабильности. Можно было, конечно, перейти полностью в домен опорной частоты, но тогда бы наложилось ограничение для входной частоты в Fref/2, как и для микроконтроллера.