|
Плавная перестройка чатоты генерации меандра., STM32F103 |
|
|
|
Jun 23 2014, 15:51
|
Знающий
   
Группа: Свой
Сообщений: 600
Регистрация: 27-05-05
Пользователь №: 5 482

|
делал одну поделку, шаг зависит от частоты, в районе 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); }
Сообщение отредактировал IgorKossak - Jun 23 2014, 17:55
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
|
|
|
|
|
Jun 24 2014, 15:37
|
Знающий
   
Группа: Свой
Сообщений: 716
Регистрация: 27-05-05
Из: Kyiv
Пользователь №: 5 454

|
Цитата(ViKo @ Jun 24 2014, 11:51)  Если для поиска резонанса подавать прямоугольный сигнал, то резонансов найдется много. Когда каждая гармоника (там они нечетные только будут) попадет на резонанс... В первый раз слышу. чтобы контур имел несколько резонансных частот. По поводу моего вопроса. Я сегодня написал программу "полного" перебора частот выдаваемых таймером с помощью перебора коэффициентов предделителя и значения до которого считает таймер. Я ограничил значения от 1 до 256. На ПК перебирает за доли секунды. На МК займет намного больше. Завтра проверю. Но задача типичная - нахождение двух множителей произведение которых максимально близко приближаются к требуемому значению делителя.
|
|
|
|
|
Jun 24 2014, 16:06
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(ViKo @ Jun 24 2014, 14:51)  Если для поиска резонанса подавать прямоугольный сигнал, то резонансов найдется много. Когда каждая гармоника (там они нечетные только будут) попадет на резонанс... Основная гармоника всё равно имеет макс. амплитуду. Так что и при попадании основной гармоники на резонанс получится максимальная из всех резонансных амплитуд. ТС же собирается проходить основной гармоникой весь заданный диапазон в процессе поиска. Цитата(misyachniy @ Jun 24 2014, 21:37)  Я сегодня написал программу "полного" перебора частот выдаваемых таймером с помощью перебора коэффициентов предделителя и значения до которого считает таймер. Только непонятно - зачем так сложно, при том что ещё и ограниченно? Если есть стандартный путь - формирование синусоиды на ЦАП. И дискретность задания частоты можно получить практически любую, и со всякими делителями/предделителями таймеров заморачиваться не надо. Да и КПД аналоговой части после МК повыше будет, так как не нужно расходовать энергию на кучу гармоник, которые будут в спектре прямоугольного сигнала.
|
|
|
|
|
Jun 24 2014, 16:32
|
Знающий
   
Группа: Свой
Сообщений: 716
Регистрация: 27-05-05
Из: Kyiv
Пользователь №: 5 454

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

Участник

Группа: Участник
Сообщений: 44
Регистрация: 6-07-13
Из: Минск
Пользователь №: 77 417

|
Цитата(misyachniy @ Jun 24 2014, 18:37)  В первый раз слышу. чтобы контур имел несколько резонансных частот. Резонировать будет несколько раз, я это гарантирую. У меня лежит прибор по мотивам "Юного радиолюбителя", основанный на том, что у реального меандра много гармоник, до мегагерца, так точно. Я этим прибором буду промежуточную частоту 465 кГц настраивать в ламповом приемнике на досуге, хотя основная частота этого пробника1 кГц. И таки настрою, не сомневайтесь. Синусоиду нужно делать, а то будете гадать на кофейной гуще. Мы же серьезные люди.
Сообщение отредактировал Scientificer - Jun 24 2014, 17:54
--------------------
Мысли и действия должны чередоваться как вдох и выдох.
|
|
|
|
|
Jun 25 2014, 15:44
|
Знающий
   
Группа: Свой
Сообщений: 716
Регистрация: 27-05-05
Из: Kyiv
Пользователь №: 5 454

|
Написал на 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(). Но меня и такой вариант устраивает.
Сообщение отредактировал IgorKossak - Jun 25 2014, 19:56
Причина редактирования: [codebox] для длинного кода, [code] - для короткого!!!
|
|
|
|
|
Jun 25 2014, 19:24
|

Профессионал
    
Группа: Участник
Сообщений: 1 620
Регистрация: 22-06-07
Из: Санкт-Петербург, Россия
Пользователь №: 28 634

|
Просто для информации... Около секунды! Это что-то... Ловите мой вариант. 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 или другую с нужными характеристиками...
Сообщение отредактировал IgorKossak - Jun 25 2014, 19:57
|
|
|
|
|
Jun 26 2014, 18:54
|
Знающий
   
Группа: Свой
Сообщений: 716
Регистрация: 27-05-05
Из: Kyiv
Пользователь №: 5 454

|
Цитата Просто для информации... Около секунды! Это что-то... Ловите мой вариант. Чувствуется математическая школа - "абсолютно точный" и абсолютно бесполезный ответ. Цитата Эта функция и с атмегой справляется... Не могу понять. Зачем функция должна справляться с атмегой? Цитата Получаемые значения выдавать в ЦАП. Или использовать микросхему DDS - вроде AD9834 или другую с нужными характеристиками... Не кошерно как-то. Может лучше 4-х ядерный процессор с частотой тактирования 1,5 ГГц использовать?
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|