Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Плавная перестройка чатоты генерации меандра.
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
misyachniy
Для поиска чатоты резонанса поисковой катушки (ориентировочно 5..20 кГц) нужно генерировать меандр с помошью таймера.
Понятно что можно менять как предделитель таймера, так и с помощью регистров OCR.

Прошерстил интернет в поисках готового решения или "рыбы" но не нашел.

Предлагается в основном формирование синусоиды даже с дискретностю до 0.1 или 0.01 Гц.

Меня бы устроила шкала в 10Гц.

Есть ли где "рыба"?
dac
делал одну поделку, шаг зависит от частоты, в районе 20кгц как раз около 10Гц, на нижних частотах шаг лучше
кусок который выставляет частоту
CODE
#define PWMDEVMAX 6000
#define PWMDEVMIN 960
#define SYSFREQ 48007000

int frequency = 27850;
unsigned short pwmDevider = 1700;


frequency = SYSFREQ/(unsigned int)(pwmDevider + 1);
pwm_config(pwmDevider);

void pwm_config(unsigned short dev)
{
unsigned int i =10000;

#define PERIOD_VAL 861
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_BDTRInitTypeDef TIM_BDTRInitStructure;
uint16_t CCR1_Val;

if (dev < PWMDEVMIN) dev = PWMDEVMIN;
if (dev > PWMDEVMAX) dev = PWMDEVMAX;
CCR1_Val = dev/2;
TIM_DeInit(TIM1);
i = 10000;
while (i--);
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = dev;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

/* Channel 1 Configuration in PWM mode */
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR1_Val;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Reset;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;

TIM_OC1Init(TIM1, &TIM_OCInitStructure);

/* Automatic Output enable, Break, dead time and lock configuration*/
TIM_BDTRInitStructure.TIM_OSSRState = TIM_OSSRState_Enable;
TIM_BDTRInitStructure.TIM_OSSIState = TIM_OSSIState_Enable;
TIM_BDTRInitStructure.TIM_LOCKLevel = TIM_LOCKLevel_1;
TIM_BDTRInitStructure.TIM_DeadTime = 35;
TIM_BDTRInitStructure.TIM_Break = TIM_Break_Disable;
TIM_BDTRInitStructure.TIM_BreakPolarity = TIM_BreakPolarity_High;
TIM_BDTRInitStructure.TIM_AutomaticOutput = TIM_AutomaticOutput_Enable;

TIM_BDTRConfig(TIM1, &TIM_BDTRInitStructure);

/* TIM15 counter enable */
TIM_Cmd(TIM1, ENABLE);

/* Main Output Enable */
TIM_CtrlPWMOutputs(TIM1, ENABLE);



}
ViKo
Если для поиска резонанса подавать прямоугольный сигнал, то резонансов найдется много. Когда каждая гармоника (там они нечетные только будут) попадет на резонанс...
misyachniy
Цитата(ViKo @ Jun 24 2014, 11:51) *
Если для поиска резонанса подавать прямоугольный сигнал, то резонансов найдется много. Когда каждая гармоника (там они нечетные только будут) попадет на резонанс...


В первый раз слышу. чтобы контур имел несколько резонансных частот.

По поводу моего вопроса.
Я сегодня написал программу "полного" перебора частот выдаваемых таймером с помощью перебора коэффициентов предделителя и значения до которого считает таймер.

Я ограничил значения от 1 до 256.
На ПК перебирает за доли секунды. На МК займет намного больше. Завтра проверю.

Но задача типичная - нахождение двух множителей произведение которых максимально близко приближаются к требуемому значению делителя.
jcxz
Цитата(ViKo @ Jun 24 2014, 14:51) *
Если для поиска резонанса подавать прямоугольный сигнал, то резонансов найдется много. Когда каждая гармоника (там они нечетные только будут) попадет на резонанс...

Основная гармоника всё равно имеет макс. амплитуду. Так что и при попадании основной гармоники на резонанс получится максимальная из всех резонансных амплитуд.
ТС же собирается проходить основной гармоникой весь заданный диапазон в процессе поиска.

Цитата(misyachniy @ Jun 24 2014, 21:37) *
Я сегодня написал программу "полного" перебора частот выдаваемых таймером с помощью перебора коэффициентов предделителя и значения до которого считает таймер.

Только непонятно - зачем так сложно, при том что ещё и ограниченно?
Если есть стандартный путь - формирование синусоиды на ЦАП.
И дискретность задания частоты можно получить практически любую, и со всякими делителями/предделителями таймеров заморачиваться не надо.
Да и КПД аналоговой части после МК повыше будет, так как не нужно расходовать энергию на кучу гармоник, которые будут в спектре
прямоугольного сигнала.
misyachniy
Цитата(jcxz @ Jun 24 2014, 19:06) *
Только непонятно - зачем так сложно, при том что ещё и ограниченно?
Если есть стандартный путь - формирование синусоиды на ЦАП.
И дискретность задания частоты можно получить практически любую, и со всякими делителями/предделителями таймеров заморачиваться не надо.
Да и КПД аналоговой части после МК повыше будет, так как не нужно расходовать энергию на кучу гармоник, которые будут в спектре
прямоугольного сигнала.


Стандартный путь конечно есть. Но расходы на него тоже есть.
Сейчас я формирую двумя транзисторами, а так нужно еще ОУ ставить.
Scientificer
Цитата(misyachniy @ Jun 24 2014, 18:37) *
В первый раз слышу. чтобы контур имел несколько резонансных частот.


Резонировать будет несколько раз, я это гарантирую. У меня лежит прибор по мотивам "Юного радиолюбителя", основанный на том, что у реального меандра много гармоник, до мегагерца, так точно. Я этим прибором буду промежуточную частоту 465 кГц настраивать в ламповом приемнике на досуге, хотя основная частота этого пробника1 кГц. И таки настрою, не сомневайтесь.

Синусоиду нужно делать, а то будете гадать на кофейной гуще. Мы же серьезные люди.
misyachniy
Написал на Builder несколько ускоренную программу поиска коэффициентов файл "calc_presc.rar"

Функция на STM32F103 на 72 Мгц рассчитывает перебирает варианты за время около секунды.

CODE
#define MAX_PRESC 65535
#define MAX_PERIOD 256
//---------------------------------------------------------------------------
void calc_presc(int target_freq, unsigned short *presc_value, unsigned short *half_period_value)
{
int i, j;

float freq, best_calc_freq;
int presc, period;

// разница между заданой чаcтотой и рассчитаной
// в предыдущей итерации и а текущей
float prev_dif, cur_dif;

best_calc_freq=0;

for (i=0;i<MAX_PRESC; i++)
for (j=1; j<=MAX_PERIOD/2; j++)
{

prev_dif = cur_dif;

freq = (SYSCLK_FREQ_72MHz)/ ((1+i) * j*2);
/*
if (sign(target_freq - freq) == sign(target_freq - (SYSCLK_FREQ_72MHz)/ ((1+MAX_PRESC) * MAX_PERIOD)))
{
j = MAX_PERIOD/2;
}
*/
cur_dif = fabs(freq - target_freq);

if (cur_dif < fabs((best_calc_freq - target_freq)))

{
best_calc_freq = freq;
presc = i;
period =j;
}
else
{
if (j>1) // если уже есть с сравнивать
{
if (fabs(prev_dif) < fabs(cur_dif)) j=MAX_PERIOD/2; //результат ухудшается
}
}

}

*presc_value = presc;
*half_period_value = period;
}



Прескалер прямо пишется в регистры таймера, half_period_value прямо в CCR а удвоенное значение в элемент структуры TIM_Period

CODE
// Инициализация таймера управления катушкой
void coil_generator_init(int prescaller, int period)
{
GPIO_InitTypeDef GPIO_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;


GPIO_InitStructure.GPIO_Pin = COIL_MASK;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(COIL_PORT, &GPIO_InitStructure);

// Map TIM5 OC4 to PA3
GPIO_PinRemapConfig(GPIO_Remap_TIM5CH4_LSI,DISABLE);


// Init PWM TIM5
// Enable Timer1 clock and release reset
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);
RCC_APB1PeriphResetCmd(RCC_APB1Periph_TIM5, DISABLE);

TIM_InternalClockConfig(TIM5);

// Time base configuration
TIM_TimeBaseStructure.TIM_Prescaler = prescaller;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseStructure.TIM_Period = period*2 -1;
TIM_TimeBaseStructure.TIM_ClockDivision = 0;
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM5, &TIM_TimeBaseStructure);

// Channel 2 Configuration in PWM mode
//TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //vovka TIM_OCMode_Toggle

TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;


TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
TIM_OCInitStructure.TIM_Pulse = 0x00;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_High;
TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;

TIM_OC4Init(TIM5,&TIM_OCInitStructure);
// Double buffered
TIM_ARRPreloadConfig(TIM5, ENABLE);

// TIM5 counter enable
TIM_Cmd(TIM5,ENABLE);

TIM5->CCR4 = period;
}



Перебор прескаллера 16 бит, периода 8 бит, так как для частот около 15..20гКц никакой выгоды от двух 16 битных коэффициентов нет.
Закомментированый кусок кода еще должен ускорить перебор, но в IAR нет функции Sign().
Но меня и такой вариант устраивает.
Genadi Zawidowski
Просто для информации...
Около секунды! Это что-то... Ловите мой вариант.
CODE
// возврат позиции старшего значащего бита в числе
static uint_fast8_t
findleftbit(
unsigned long v // число на анализ
)
{
uint_fast8_t n;

for (n = 0; v != 0; ++ n)
v >>= 1;

return n;
}

static uint_fast8_t
calcdivider(
uint_fast32_t divider, // ожидаемый коэффициент деления всей системы
uint_fast8_t width, // количество разрядов в счётчике
uint_fast16_t taps, // маска битов - выходов прескалера. 0x01 - означает bypass, 0x02 - делитель на 2... 0x400 - делитель на 1024
uint_fast16_t * dvalue, // Значение для записи в регистр сравнения делителя
uint_fast8_t substract)
{
const uint_fast8_t rbmax = 16; //позиция старшего значащего бита в маске TAPS
uint_fast8_t rb, rbi;
uint_fast16_t prescaler = 1U;
for (rb = rbi = 0; rb <= rbmax; ++ rb, prescaler *= 2)
{
if ((taps & prescaler) != 0)
{
// такой предделитель существует.
const uint_fast32_t modulus = (divider / prescaler) - substract;
if (findleftbit(modulus) <= width)
{
// найдена подходящая комбинация
// rb - степень двойки - деление предделителя.
// rbi - номер кода для записи в регистр предделителя.
// modulus - что загрузить в регистр сравнения делителя.
* dvalue = (uint_fast16_t) modulus;
return rbi;
}
++ rbi; // переходим к следующему предделителю в списке.
}
}
// Не подобрать комбинацию прескалера и делителя для ожидаемого коэффициента деления.
* dvalue = 1 - substract; // просто пустышка
return (rbi - 1); // если надо обраьатывать невозможность подбора - возврат rbmax
}

...
Код
enum
{
        STM32F_GP_TIMER_WIDTH = 16,    STM32F_GP_TIMER_TAPS = (65535), // General-purpose timers
        STM32F_AC_TIMER_WIDTH = 16,    STM32F_AC_TIMER_TAPS = (65535), // Advanced-control timers
        STM32F_BA_TIMER_WIDTH = 16,    STM32F_BA_TIMER_TAPS = (65535), // Basic timers
        STM32F_SPIBR_WIDTH = 0,    STM32F_SPIBR_TAPS = (256 | 128 | 64 | 32 | 16 | 8 | 4 | 2),

        // LTDC dot clock parameters
        STM32F_LTDC_DIV_WIDTH = 3, // valid values for RCC_PLLSAICFGR_PLLI2SR: 2..7
            STM32F_LTDC_DIV_TAPS = (16 | 8 | 4 | 2),    // valid values for RCC_DCKCFGR_PLLSAIDIVR: 0: /2, 1: /4, 2: /8, 3: /16
};
...
Код
    uint_fast16_t value;
    const uint_fast8_t prei = calcdivider(calcdivround_pclk2(ticksfreq), STM32F_BA_TIMER_WIDTH, STM32F_BA_TIMER_TAPS, & value, 1);
    TIM3->PSC = (1UL << prei) - 1;
    TIM3->ARR = value;
    TIM3->DIER = TIM_DIER_UIE;    //разрешить событие от таймера.


Эта функция и с атмегой справляется... Принцип такой - найти максимально точную аппроксимацию - начиная с минимального зщначения прескалера, которая влезет в указанное количество битов.

зы: откройте для себя принцип NCO/DDS...

Код
#include "sinetable.h"

typedef uint32_t ncoftw_t;
#define VALUELOG2    16    // количество битов в значении из целочисленной таблицы
#define ASH (32 - TABLELOG2)    // 22 = 32 - log2(number of items in sintable)
#define FTW2ANGLEI(ftw)    ((uint32_t) (ftw) >> ASH)
#define FTW2ANGLEQ(ftw)    ((uint32_t) ((ftw) + 0x40000000L) >> ASH)
#define FTWROUND(ftw) ((uint32_t) (ftw))
#define FTW(freq) (((uint_fast64_t) (freq) << 32) / ARMSAIRATE)
#define FTWAF(freq) (((uint_fast64_t) (freq) << 32) / ARMI2SRATE)


CODE
static ncoftw_t anglestep_lout = FTWAF(700), anglestep_rout = FTWAF(2500);
static ncoftw_t angle_lout, angle_rout;

static ncoftw_t anglestep_lout2 = FTWAF(5600), anglestep_rout2 = FTWAF(6300);
static ncoftw_t angle_lout2, angle_rout2;

int get_rout16(void)
{
// Формирование значения для ROUT
const int v = (sintable [FTW2ANGLEI(angle_rout)]) * (1L << (16 - VALUELOG2));
angle_rout = FTWROUND(angle_rout + anglestep_rout);
return v;
}

int get_lout16(void)
{
// Формирование значения для LOUT
const int v = (sintable [FTW2ANGLEI(angle_lout)]) * (1L << (16 - VALUELOG2));
angle_lout = FTWROUND(angle_lout + anglestep_lout);
return v;
}

int get_rout24(void)
{
// Формирование значения для ROUT
const int v = (sintable [FTW2ANGLEI(angle_rout2)]) * (1L << (24 - VALUELOG2));
angle_rout2 = FTWROUND(angle_rout2 + anglestep_rout2);
return v;
}

int get_lout24(void)
{
// Формирование значения для LOUT
const int v = (sintable [FTW2ANGLEI(angle_lout2)]) * (1L << (24 - VALUELOG2));
angle_lout2 = FTWROUND(angle_lout2 + anglestep_lout2);
return v;
}

//////////////////////////////////////////

// American (AT&T)
// dial tone 350 and 440 Hz
//static ncoftw_t anglestep_af1 = FTWAF(440);
//static ncoftw_t anglestep_af2 = FTWAF(350);

// Dual tone signal generator for SSB TX tests
static ncoftw_t anglestep_af1 = FTWAF(850);
static ncoftw_t anglestep_af2 = FTWAF(1050);

static ncoftw_t angle_af1;
static ncoftw_t angle_af2;

// получение значений выборок для двухтонового сигнала
int get_dualtone16(void)
{
// Формирование значения для LOUT
const int v1 = (sintable [FTW2ANGLEI(angle_af1)]) * (1L << (16 - VALUELOG2));
const int v2 = (sintable [FTW2ANGLEI(angle_af2)]) * (1L << (16 - VALUELOG2));
angle_af1 = FTWROUND(angle_af1 + anglestep_af1);
angle_af2 = FTWROUND(angle_af2 + anglestep_af2);
return (v1 + v2) / 2;
}


Получаемые значения выдавать в ЦАП. Или использовать микросхему DDS - вроде AD9834 или другую с нужными характеристиками...
misyachniy
Цитата
Просто для информации...
Около секунды! Это что-то... Ловите мой вариант.

Чувствуется математическая школа - "абсолютно точный" и абсолютно бесполезный ответ.
Цитата
Эта функция и с атмегой справляется...

Не могу понять. Зачем функция должна справляться с атмегой?
Цитата
Получаемые значения выдавать в ЦАП. Или использовать микросхему DDS - вроде AD9834 или другую с нужными характеристиками...

Не кошерно как-то.
Может лучше 4-х ядерный процессор с частотой тактирования 1,5 ГГц использовать?
Genadi Zawidowski
Абсолютно бесполезный ответ - это мои эмоции по Вашим результатам и способу их получения.
Про атмегу я сказал к тому, что параметрами можно настроить данную функцию на поиск опитимального (по точности) результата даже на извратных делителях атмег.
AD9834 стоит 390 рублей. Общий тон Вашего ответа удивляет, но это уже моё личное. Отвечать не стоит.
alexf
DDS - безусловно правильное решение. Не внешний, а програмный. Примеров - куча. Любая разумная точность и хоть синус, хоть меандр с выхода DAC в зависимости от того что в таблице. Не надо искать делителей. Принцип очень простой.
Счетчик фазы 32 бита. Прибавляем произвольное число скажем раз в микросекунду. Младшие 8 бит - адрес в таблице синуса (или чего угодно). Если прибавлять по единице, переполнение произойдет с частотой примерно 1/4295 Гц. Если прибавлять по 4295, получим 1 Гц. Если по 4294967, то 1 КГц. И т.д.
Golikov A.
младшие ли биты? Если прибавлять по 256 то в них всегда 0.... скорее старшие 8 бит... В случае ключей, старший бит, если ключа 2, то старшие 2 бита.
rx3apf
Совершенно верно - именно старшие биты аккумулятора используются как индекс в таблице синуса.
Сергей Борщ
Из таблицы - в ЦАП, с ЦАПа - на аналоговый фильтр (RC), с фильтра - на компаратор. Вот тогда получится красивый меандр без дрожания фронтов.
misyachniy
Цитата(Сергей Борщ @ Jun 29 2014, 12:00) *
Из таблицы - в ЦАП, с ЦАПа - на аналоговый фильтр (RC), с фильтра - на компаратор. Вот тогда получится красивый меандр без дрожания фронтов.


Первичная задача нахождение резонансной частоты.
http://electronix.ru/forum/index.php?showtopic=121669
Вопрос генерации уже решен.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.