Здравствуйте, необходимо создать генератор частоты на stm32f103c8 частота 72000000 Герца, столкнулся с тем что пытаясь задать частоту генерации сигналов таймеру через регистры TIMx_PSC (регистр предделителя) TIMx_ARR (регистр автоперегрузки), не все частоты можно задать точно, некоторые вообще не возможно, например: для того чтобы задать время работы таймера с частотой 1 сек нужно записать значения в TIMx_PSC = 7200 -1 и в TIMx_ARR = 10000 - 1, а для 2 герц нужно записать TIMx_PSC = 7200 -1 и в TIMx_ARR = 5000 - 1 можно и другими значениями это сделать, а вот чтобы задать частоту генерации 7 герц то самое точное значение можно получить только если записать в TIMx_PSC = 29138 -1 а в TIMx_ARR = 353 - 1 тогда частота генерации будет равна 7.0000001944 получается 0.0000001944 это погрешность, и чем выше частота, тем выше на некоторых частотах погрешность, а для например частоты 999999 Герц вообще не существует никаких чисел и для регистра предделителя TIMx_PSC и для регистра автоперегрузки TIMx_ARR Вопрос собственно такой так какже создаются генераторы частоты на цифровых микросхемах для всех частот хотя бы до 1 мегагерца, заранее извините за может быть глупый вопрос просто в интернете пока внятного ответа не нашёл, хотя искал много. Заранее благодарю за ответы.
Вопрос собственно такой так какже создаются генераторы частоты
Вариантов ДВА. 1. PLL (ФАПЧ) с дробным ДПКД. 2. DDS (прямой цифровой синтез), точнее, для импульсов достаточно ядра DDS - NCO. ПЗУ формы сигнала и ЦАП не требуются. При использовании обычных таймеров вы получите шаг ПЕРИОДА (а не частоты) равный периоду сигнала тактирования. Шкала частоты будет нелинейна и с достаточно большим шагом.
Вопрос собственно такой так какже создаются генераторы частоты
Вариантов ДВА. 1. PLL (ФАПЧ) с дробным ДПКД. 2. DDS (прямой цифровой синтез), точнее, для импульсов достаточно ядра DDS - NCO. ПЗУ формы сигнала и ЦАП не требуются. При использовании обычных таймеров вы получите шаг ПЕРИОДА (а не частоты) равный периоду сигнала тактирования. Шкала частоты будет нелинейна и с достаточно большим шагом.
1. PLL (ФАПЧ) с дробным ДПКД. Извените, а это как должно реализовываться, аппаратно или пожно програмно реализовать. Насколько я понимаю в stm32f103c8 нет аппаратного PLL дробным ДПКД
2. DDS (прямой цифровой синтез), точнее, для импульсов достаточно ядра DDS - NCO, я так понимаю что в stm32f103c8 аппаратно это не реализовано, а как тогда это реализовать програмно и возможно ли.
1. Насколько я понимаю в stm32f103c8 нет аппаратного PLL дробным ДПКД 2. я так понимаю что в stm32f103c8 аппаратно это не реализовано, а как тогда это реализовать програмно и возможно ли.
1. Правильно понимаете. Программно не реализуется. 2. Тоже правильно понимаете, но программно реализовать можно. Правда это сожрет значительную часть ресурсов МК. Аппаратный модуль NCO мне известен только в 8-битниках Микрочипа. От PIC10F322 до PIC18FxxQ43. У последнего даже ТРИ модуля NCO.
1. PLL (ФАПЧ) с дробным ДПКД. Извените, а это как должно реализовываться, аппаратно или пожно програмно реализовать. Насколько я понимаю в stm32f103c8 нет аппаратного PLL дробным ДПКД
2. DDS (прямой цифровой синтез), точнее, для импульсов достаточно ядра DDS - NCO, я так понимаю что в stm32f103c8 аппаратно это не реализовано, а как тогда это реализовать програмно и возможно ли.
Увы, но МК могут далеко не все. В вашем случае их разумно применять только для управления чипами синтезаторов PLL или DDS.
_________________ Астролябия-сама меряет, было бы что мерять!!!
1. Насколько я понимаю в stm32f103c8 нет аппаратного PLL дробным ДПКД 2. я так понимаю что в stm32f103c8 аппаратно это не реализовано, а как тогда это реализовать програмно и возможно ли.
1. Правильно понимаете. Программно не реализуется. 2. Тоже правильно понимаете, но программно реализовать можно. Правда это сожрет значительную часть ресурсов МК. Аппаратный модуль NCO мне известен только в 8-битниках Микрочипа. От PIC10F322 до PIC18FxxQ43. У последнего даже ТРИ модуля NCO.
А можете хотя бы в простой форме описать модуль NCO или аккумулятор фазы.
Легко. Есть некий регистр, который выполняет функции аккумулятора (фазы), то есть содержит результат некоей суммы. К текущему значению аккумулятора суммируют значение другого регистра (назовем его инкрементным регистром) и результат сохраняют в том же аккумуляторе. Это суммирование производят с тактом опорной частоты. Переполнение аккумулятора будет происходить с частотой равной произведению опорной частоты и значения в инкрементном регистре деленному на два в степени разрядности аккумулятора. Это все и есть модуль NCO. Он формирует события (синхронизации, триггера, прерывания) или выходной сигнал на пине с синтезируемой частотой. Если подключить этот модуль к каналу DMA в качестве реквестов транзакций, то можно с помощью таблицы формы сигнала получить полноценный DDS через DAC или PWM.
Легко. Есть некий регистр, который выполняет функции аккумулятора (фазы), то есть содержит результат некоей суммы. К текущему значению аккумулятора суммируют значение другого регистра (назовем его инкрементным регистром) и результат сохраняют в том же аккумуляторе. Это суммирование производят с тактом опорной частоты. Переполнение аккумулятора будет происходить с частотой равной произведению опорной частоты и значения в инкрементном регистре деленному на два в степени разрядности аккумулятора. Это все и есть модуль NCO. Он формирует события (синхронизации, триггера, прерывания) или выходной сигнал на пине с синтезируемой частотой. Если подключить этот модуль к каналу DMA в качестве реквестов транзакций, то можно с помощью таблицы формы сигнала получить полноценный DDS через DAC или PWM.
Получается, что нужно создать глобальную переменную инициализировать её 0. Ну допустим int a = 0; - это и будет аккумулятор фазы, далее нужно создать другую переменную и инициализировать её, ну например 10, int inc = 10; это и будет инкрементным регистром, далее в обработчике прерывания нужно каждый раз, как он вызывается прибавлять к аккумулятор фазы инкрементный регистр то есть a = a + inc; Если обработчик прерывания вызывается 1000000 раз в секунду, то переполнение аккумулятора будет происходить 1000000 * 10 = 10000000 то есть один раз в десять секунд или с частотой 0,1 Герца, хорошо, а если я хочу получить переполнение аккумулятора с частотой ну допустим 7 Герц то есть семь раз в секунду, тогда что нужно делать? Может нужно 1000000 / 7 = 142857,142857 взять ближайшее целое число делящееся на 7 это 142856 (142856 / 7 = 20408) тогда inc сделать равным 7 и тогда a = a + inc; будет a = a + 7; вызывая обработчик 1000000 раз в секунду нужно задать число для сравнения 20408/2 становясь равным которому будет инвертироваться выход микросхемы, тогда if (a == 10204) toggle // вывод микросхемы, но тогда получится большая погрешность.
У вас НЕВЕРНЫЙ расчет. При ваших условиях выходная частота будет равна 1000000*10/65536=152.587891 Гц Шаг частоты будет равен 15.2587891 Гц. А все потому, что разрядность аккумулятора мала. Теперь возьмем uint32_t a=0; Ситуация изменилась. Теперь шаг частоты стал 1000000/4294967296 = 0.00023283Гц Код частоты 7 Гц = 7/0.00023283 = 30065 Реально получаем 1000000*30065/4294967296=7.0000533 Гц. Устраивает? ЗЫ. И учтите, что вызов прерывания каждую 1 мкс при тактовой частоте МК 72МГц приведет к тому, что вы практически полностью блокируете остальные задачи МК. Из 72 машинных циклов между вызовами прерываний само прерывание будет занимать порядка 40...50 циклов.
У вас НЕВЕРНЫЙ расчет. При ваших условиях выходная частота будет равна 1000000*10/65536=152.587891 Гц Шаг частоты будет равен 15.2587891 Гц. А все потому, что разрядность аккумулятора мала. Теперь возьмем uint32_t a=0; Ситуация изменилась. Теперь шаг частоты стал 1000000/4294967296 = 0.00023283Гц Код частоты 7 Гц = 7/0.00023283 = 30065 Реально получаем 1000000*30065/4294967296=7.0000533 Гц. Устраивает? ЗЫ. И учтите, что вызов прерывания каждую 1 мкс при тактовой частоте МК 72МГц приведет к тому, что вы практически полностью блокируете остальные задачи МК. Из 72 машинных циклов между вызовами прерываний само прерывание будет занимать порядка 40...50 циклов.
Извините, не пойму, а откуда взялась цифра 65536 в выражении 1000000*10/65536=152.587891 Гц и почему ошибка в расчёте, если если действие (изменение состояния вывода) осуществляется на 10 000 000 счёт при частоте самого счёта 1 000 000 раз в секунду получается 0,1 Герц. По поводу "вызова прерывания каждую 1 мкс при тактовой частоте МК 72МГц" полностью согласен, это я для примера можно и с частотой 10 000 Герц вызывать прерывания, просто генератор будет максимум на 10 000 Герц, мне больше и не нужно лижбы максимально точно получилась.
"Реально получаем 1000000*30065/4294967296=7.0000533 Гц. Устраивает?" - В полне устраивает, лижбы такая погрешнасть была для всех частот Ещё вопрос разве тип данных int - 65536 он же вроде от −2 147 483 648 до 2 147 483 647; uint32_t а что если использовать uint64_t ещё будет лучше, или для этого обязательно нужен 64 битный контроллер?
Это два в степени разрядности аккумулятора. Чтобы переполнить 16-разрядный аккумулятор, нужно суммировать инкрементный регистр 65536/b раз, где b - значение инкрементного регистра. Именно во столько раз понизится частота тактирования NCO на его выходе.
а что если использовать uint64_t ещё будет лучше, или для этого обязательно нужен 64 битный контроллер?
Контроллер может вычислить практически любую разрядность операндов. Просто времени на это уйдет больше, чем при нативной разрядности. Однако при повышении разрядности аккумулятора фазы быстро уменьшается шаг частоты, но вместе с ним уменьшается частота выходного сигнала по отношению к частоте накачки при равном коде частоты. При том условии, что вы не в состоянии поднять частоту накачки из-за софтовой реализации, это становится неразрешимой проблемой. Так что не увлекайтесь разрядностью. Плюс к этому все это не гарантирует абсолютной точности синтезируемой частоты, поскольку: 1. Кварц формирующий тактирование сам имеет неточную номинальную частоту 2. Софтовая реализация добавляет джиттер переменной латентности прерываний 3. У DDS есть собственный джиттер, определяемый коэффициентом понижения частоты относительно тактирования. Не думаю, что есть смысл увеличивать разрешение по частоте выше семи...восьми десятичных разрядов.
разве тип данных int - 65536 он же вроде от −2 147 483 648 до 2 147 483 647;
Во первых, мы не обсуждали int. Речь постоянно шла об unsigned int. Во вторых, int не означает конкретной разрядности. Чаще всего - это НАТИВНАЯ разрядность контроллера. Чтобы код был переносим и читаем, ввели переопределяемый стандартный беззнаковый тип uintXX_t, где ХХ-разрядность. Я только о нем и говорил. В 32 разрядном варианте я расчет приводил. 2^32=4294967296
Это два в степени разрядности аккумулятора. Чтобы переполнить 16-разрядный аккумулятор, нужно суммировать инкрементный регистр 65536/b раз, где b - значение инкрементного регистра. Именно во столько раз понизится частота тактирования NCO на его выходе.
а что если использовать uint64_t ещё будет лучше, или для этого обязательно нужен 64 битный контроллер?
Контроллер может вычислить практически любую разрядность операндов. Просто времени на это уйдет больше, чем при нативной разрядности. Однако при повышении разрядности аккумулятора фазы быстро уменьшается шаг частоты, но вместе с ним уменьшается частота выходного сигнала по отношению к частоте накачки при равном коде частоты. При том условии, что вы не в состоянии поднять частоту накачки из-за софтовой реализации, это становится неразрешимой проблемой. Так что не увлекайтесь разрядностью. Плюс к этому все это не гарантирует абсолютной точности синтезируемой частоты, посколькурное 1. Кварц формирующий тактирование сам имеет неточную номинальную частоту 2. Софтовая реализация добавляет джиттер переменной латентности прерываний 3. У DDS есть собственный джиттер, определяемый коэффициентом понижения частоты относительно тактирования. Не думаю, что есть смысл увеличивать разрешение по частоте выше семи...восьми десятичных разрядов.
разве тип данных int - 65536 он же вроде от −2 147 483 648 до 2 147 483 647;
Во первых, мы не обсуждали int. Речь постоянно шла об unsigned int. Во вторых, int не означает конкретной разрядности. Чаще всего - это НАТИВНАЯ разрядность контроллера. Чтобы код был переносим и читаем, ввели переопределяемый стандартный беззнаковый тип uintXX_t, где ХХ-разрядность. Я только о нем и говорил. В 32 разрядном варианте я расчет приводил. 2^32=4294967296
Спасибо большое за разъяснения и за потраченное время, пока я не всё понимаю, видимо нужно ещё поучится, почитать книги. Просто трудно понять что вы пытаетесь объяснить не зная, наверное элементарных понятий, таких как "шаг частоты" "частота накачки" "код частоты""джиттер" буду дальше подбирать эту тему, как только получится обязательно выложу код для stm32f103c8, ещё раз спасибо.
Шаг частоты - величина изменения частоты при изменении кода частоты на единицу. Частота накачки - тактовая частота DDS/NCO с которой происходит суммирование в аккумуляторе. Код частоты - значение в инкрементном регистре (величина, на которую увеличивается значение аккумулятора фазы за один такт). Джиттер - дрожание фронта сигнала (паразитная частотная модуляция) относительно идеального положения в стабильном сигнале.
Если контроллер используется только для генерации можно накопление и вывод делать в основном цикле, а в прерываниях обеспечивать управление-настройку. Да, при управлении будет ломаться сигнал, но при обычной работе все будет чисто. При 72МГц можно попробовать получить частоту выдачи на ЦАП 2-3 МГц (за 24-36 тактов).
Я думал. Написать основной цикл, измерить время выполнения и к нему привязаться. Для пробы можно сгенерить 1 кГц и измерить частотомером, определить коррекцию. Думаю время выполнения основного while(1) не изменится от параметров задания частоты. Пробовать надо. Хотел на G071 одновременно с работой АЦП выдавать на ЦАП генерацию, не взлетело. Но это другая история.
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 19
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения