|
stm32 - генерация точной частоты таймера, задачка школьной программы |
|
|
|
May 17 2015, 07:49
|

Частый гость
 
Группа: Участник
Сообщений: 173
Регистрация: 11-05-08
Пользователь №: 37 414

|
Здравствуйте, уважаемые форумчане! Задача в следующем: есть таймер, например, 5-й, питается от 72 мГц шины. Требуется генерировать ШИМ частотой в пределах от 10 до 2000 Гц с минимально возможной погрешностью (разницы нет - в большую или меньшую сторону). Когда частота кратна 72 - все прекрасно, но если требуется вывести частоту, например, 13 Гц, то возникает погрешность. Настройка соответственно происходит регистрами PSC и ARR, комбинаций их может быть достаточно много. Самое первое, что приходит в голову - это тупой перебор всех возможных комбинаций с вычислением ошибки и выбором пары PSC и ARR с наименьшей их них, но это не наш метод Может существует формула для расчета? Или на крайний случай таблица значений регистров для каждой частоты с минимальной погрешностью?
|
|
|
|
|
 |
Ответов
|
May 17 2015, 16:34
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
В общем-то решается не только цепными дробями. Я тут тоже посидел, по случаю, накрапал вот: CODE #include <stdint.h> #include <stdlib.h> #include <stdio.h>
static unsigned int fast_sqrt(unsigned int arg) { unsigned int one,res = 0; for(one = 1UL << (sizeof(one)*8 - 2); one > arg; one >>=2);
while(one) { unsigned int k= res+one; if(arg >= k) { arg -= k; res += 2*one; } res >>= 1; one >>= 2; } return res; }
unsigned int factorize(unsigned int arg, unsigned int initial) { unsigned int j, limit = fast_sqrt(arg); if(initial == 1) initial += 1; for(j=initial; (j<=limit)&&(arg%j);++j); return (j<=limit)?j:0; }
void get_mpys(uint16_t *a, uint16_t *b, unsigned int num) { unsigned int koeff=1; *a = *b = 1; while(koeff) { unsigned int k = factorize(num,koeff); if(k) { koeff = k; num /= k; } else { k = num; koeff = 0; }
if(*a * k < 65536) *a *= k; else { if(*b * k < 65536) *b *= k; else { *a=*b=0; return; } } } }
/*test*/ int main() { uint16_t xa,xb; unsigned int nnn= 7200000/7; get_mpys(&xa,&xb, nnn); printf("numb %d a= %d b= %d a*b = %d\n",nnn,xa,xb,xa*xb); return 0; }
Вкратце. Имеем static unsigned int fast_sqrt(unsigned int arg) - быстрый целочисленный квадратный корень . Он нам нужен для оценки верхнего предела факторизации, то бишь разложения на множители. Далее, сам факторизатор - unsigned int factorize(unsigned int arg, unsigned int initial) Работает тупым перебором, находит множитель начиная с числа initial для параметра arg Наконец, void get_mpys(uint16_t *a, uint16_t *b, unsigned int num) - тут уже идет последовательное нахождение коэффициентов и домножение их к a или b То есть a*b=num Если число не помещается в 16 разрядов - пардон, пользуемся ARR 32 бита.  Можно сравнить погрешности если кто цепные дроби подымет здесь для тестинга.
Сообщение отредактировал _Pasha - May 17 2015, 16:37
|
|
|
|
|
May 21 2015, 17:16
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(_Pasha @ May 17 2015, 22:34)  Я тут тоже посидел, по случаю, накрапал вот: ... unsigned int factorize(unsigned int arg, unsigned int initial) { unsigned int j, limit = fast_sqrt(arg); if(initial == 1) initial += 1; for(j=initial; (j<=limit)&&(arg%j);++j); return (j<=limit)?j:0; } Это у Вас разложение на простые множители? При разложении большого arg будет много лишних операций деления. Возможно лучше после оценки предела (посредством sqrt()), найти все простые числа до него с помощью "решета Сундарама" и пытаться делить только на них. PS: Кстати - в какую сторону даёт погрешность fast_sqrt()? Если в меньшую - надо быть осторожным при разложении arg == n * n, где n - простое число. Увеличить бы предел на неск. процентов: limit = fast_sqrt(arg) * 11 / 10 или использовать: limit = 1 << (32 - __CLZ(arg) + 1) / 2
|
|
|
|
|
May 21 2015, 17:56
|
;
     
Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509

|
Цитата(jcxz @ May 21 2015, 20:16)  PS: Кстати - в какую сторону даёт погрешность fast_sqrt()? Если в меньшую - надо быть осторожным при разложении arg == n * n, где n - простое число. кстати да. нужна финальная проверка. По первому вопросу не вникал. Яснопонятно, что это все дороговатые операции, однако, таких приложений сферических , чтобы генерить произвольную частоту - еще поискать нужно. Проще таблицу простых чисел заиметь во флеше, например http://integraloff.net/simple/table_simpl_ch.phpдо 10**4 это всего-то 2460 байт Код unsigned int factorize(unsigned int arg, unsigned int initial) { unsigned int j, limit = fast_sqrt(arg); if(initial == 1) initial += 1; if(limit*limit < arg) limit +=1; for(j=initial; (j<=limit)&&(arg%j);++j); return (j<=limit)?j:0; }
|
|
|
|
|
May 22 2015, 07:47
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(_Pasha @ May 22 2015, 01:22)  Интересно. А у нас же ж факторизуемое число уменьшается и нижний предел поиска натуральных чисел растет. Получается, что и столько озу ему может быть не нужно. Нет. Для этого алгоритма надо сначала его полностью выполнить чтобы найти все простые не превышающие некоторого. Нельзя им найти одно простое, его использовать, а потом искать следующее. Только сразу все. Цитата(adnega @ May 22 2015, 03:46)  Если это число не будет простым. Любого - означает что даже если оно само будет простым. Цитата(_Pasha @ May 22 2015, 08:57)   Вчера мы не сообразили, что есть же еще компромисс, а именно: если таблицу всех простых чисел представить битовым полем, то до 65536 нужно 8к флеша. и это без RLE. Вы на шаг приблизились к господину Сундараму. Там именно карта флагов и используется (можно битовую).. А если-бы Вы хотя-бы набрали в гугле "решето Сундарама", то узнали-бы, что для представления всех простых до 65536 нужно не 8К, а только 4К, о чём я и писал выше.  Можно и так конечно - посчитать эту таблицу решетом на компе, а прошить как есть во флешь.
|
|
|
|
|
May 22 2015, 09:11
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(jcxz @ May 22 2015, 10:47)  Любого - означает что даже если оно само будет простым. А какой толк от такого разложения? В результате ведь нужно получить два числа так, чтобы их произведение равнялось некому числу, а каждое было не более 16-бит, причем, если вариантов много, то нужен какой-то дополнительный критерий выбора? Почему не подходит аппаратно-программный способ генерации частоты с точным делителем? Вопрос к ТС.
|
|
|
|
Сообщений в этой теме
Sprite stm32 - генерация точной частоты таймера May 17 2015, 07:49 Эдди Подсказываю: есть цепные дроби. Я, кстати, активно... May 17 2015, 12:32 adnega Цитата(Sprite @ May 17 2015, 10:49) есть ... May 17 2015, 15:49 BaN Цитата(Эдди @ May 17 2015, 18:32) Подсказ... May 17 2015, 16:07      _Pasha Цитата(jcxz @ May 22 2015, 10:47) Нет. Дл... May 22 2015, 08:44    adnega Цитата(jcxz @ May 21 2015, 21:56) А этого... May 21 2015, 21:46    _Pasha Цитата(jcxz @ May 21 2015, 21:56) Для реш... May 22 2015, 02:57 basileus_ предложу следующее решение:
CODE
#include <math... May 19 2015, 09:27 adnega Как вариант, для низких частот (когда делитель не ... May 19 2015, 12:11 Sprite Спасибо всем за советы! Сейчас буду перемалыва... May 20 2015, 12:21 _Pasha вишенка на торте
Кодchar isPrime(uint_least16_... May 22 2015, 04:50 _Pasha А знаете ли вы, что ...
http://cyberleninka.ru/art... May 22 2015, 09:43
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|