Просто для информации...
Около секунды! Это что-то... Ловите мой вариант.
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 или другую с нужными характеристиками...