Перейти к содержанию
    

Там должно стать понятным, почему этот код внутри должен исполняться за детерминированное кол-во тактов
+1 к предыдущему оратору.

Лучше пусть с детерминированным периодом DMA выбрасывает данные в DAC-и, а цикл с временем "лишь бы успевал" эти данные готовит.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А что надо получить в конечном итоге, прямоугольник заданной частоты? Может быть как-то на таймерах это сделать, неужели синус + компаратор лучше (не спорю, просто не сталкивался).

 

Мне нужны разные виды модуляции, в том числе ЛЧМ.

typedef struct
{
    u32 freq_start; //начальная частота, Гц
    u32 freq_stop; //конечная частота, Гц        
    u32 tau_f;       //время изменения от начальной до конечной, мкс        
    u32 tau_d;      //длительность радиоимпульса,мкс            
    u32 freq_carrier;//частота гетеродина, Гц    
}DDS_CONFIG;

Таймера в стм32, конечно, хорошие, учитывая 16-битные прескалеры и авторелоады (кстати, для гетеродина использую), позволяют с достаточной точностью получить требуемые частоты, но не годятся для получения ЛЧМ-сигнала: при частоте 400кГц и тактовой таймера 72МГц прескалер=0, ARR=180-1. Следующее возможное значение по частоте 72000/179=402,234кГц, ниже 397,790кГц, - порядок шага по частоте ясен. Сравним с разрешающей способностью DDS (аккумулятор 32-битный) dF=Fd/2^32=72000000/(кол-во тактов на выполнение кода DDS)/2^32=72000000/20/2^32=0,00083819 Гц.

Это почему не таймеры, с приращением ARR в обработчике.

А почему именно синусы (комплементарные), кажется пояснял выше, может нет, повторюсь: кажется очевидным простой ход - вместо таблицы синуса подсунуть DDS-у таблицу меандра (наполовину 0х000, наполовину 0хFFF), как вариант, можно выводить на пин старший бит из таблицы сигнала. На низких формируемых частотах можете не заметить, но на высоких выползает джиттер (дрожат фронты). Причина в большой величине приращения аккумулятора фазы на каждом шаге... вобщем, тут нужно прочувствовать и увидеть :)

А побороть это можно сделав 2 синуса, отфильтровать и подать на компаратор - так сделано у Analog Devices в некоторых DDS-чипах.

 

 

На счет цапов я бы предложил попробовать реализовать используя возможности стм32, а именно дма + синхр. по таймеру.

Не знаю.... В итоге нужно в ЦАП-ы засовывать, посредством дма или софтом, значение из таблицы сигнала, причём не значения по порядку, а вычислять индекс на основании значения аккумулятора фазы, так что сдаётся мне, дма по таймеру здесь не помогут. Собсно код:

....
register union
    {
        struct
        {
            uint32_t unused:24;
            uint32_t index:8;
        }field;
        uint32_t total;
    }acc;
......
while(bStopDSS == 0)
            {
                if( dp > freq_stop )
                {
//                    acc.total = 0; 
                    dp = freq_start;
                    ddp = tau;
//                    break;
                }
            
                DAC->DHR12RD = _sinewave[acc.field.index];
                acc.total += dp;
                dp += ddp;    
            }

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Не знаю.... В итоге нужно в ЦАП-ы засовывать, посредством дма или софтом, значение из таблицы сигнала, причём не значения по порядку, а вычислять индекс на основании значения аккумулятора фазы,
Значения из таблицы по индексу из аккумулятора фазы заталкивать в массив в памяти, из которого выбирать при помощи DMA с активацией по таймеру. Буфер FIFO, выравнивающий неодинаковость времени вычислений.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Значения из таблицы по индексу из аккумулятора фазы заталкивать в массив в памяти, из которого выбирать при помощи DMA с активацией по таймеру. Буфер FIFO, выравнивающий неодинаковость времени вычислений.

Спасибо, мысль понятна, только аккумулятор бегает по кругу, через переполнение (изменение аака от 0 до 2^32-1 соответсвует одному периоду сигнала - фаза от 0 до 2*PI), причём на бОльших частотах шаг приращения акка выше. Предвычисление индексов и массива некоторого конечного размера для ЦАП-а, натравливание на него дма, и так опять же по кругу? А в чём преимущество в сравнении с 3 строчками кода, делающими это в главном цикле? Алгоритм не должен никого ждать, кроме как в случае прерывания извне для приёма новой конфигурации - только в этом случае он приостанавливается.

Изменено пользователем pr0m

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо, мысль понятна, только аккумулятор бегает по кругу, через переполнение (изменение аака от 0 до 2^32-1 соответсвует одному периоду сигнала - фаза от 0 до 2*PI), причём на бОльших частотах шаг приращения акка выше. Предвычисление индексов и массива некоторого конечного размера для ЦАП-а, натравливание на него дма, и так опять же по кругу? А в чём преимущество в сравнении с 3 строчками кода, делающими это в главном цикле? Алгоритм не должен никого ждать, кроме как в случае прерывания извне для приёма новой конфигурации - только в этом случае он приостанавливается.

Преимущество - в DMA контроллере STM32, который умеет работать в режиме кольцевого буфера. Поэтому достаточно один раз предвычислить буфер и запрограммировать DMA. Дальше процессор может вообще не отвлекаться.

Если речь идет о выдаче синусоиды, то можно например иметь одну фиксированную таблицу синуса и манипулировать только частотой таймера.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Преимущество - в DMA контроллере STM32, который умеет работать в режиме кольцевого буфера. Поэтому достаточно один раз предвычислить буфер и запрограммировать DMA. Дальше процессор может вообще не отвлекаться.

Если речь идет о выдаче синусоиды, то можно например иметь одну фиксированную таблицу синуса и манипулировать только частотой таймера.

То ли я Вас не понимаю, то ли Вы не поняли принцип работы алгоритма DDS....... Знаю и использую дма, в том числе в circular mode в других задачах.

 

Попробуйте реализовать Вашим методом ЛЧМ сигнал (для начала - непрерывный, потом добавить длительность радиоимпульса) с параметрами: f1=394575Гц, f2=417876Гц, частота модулирующего сигнала 1кГц (время изменения от f1 до f2 - 1мс). Первая же сложность, с которой столкнётесь - невозможность с такой точностью задать частоты с использованием таймеров. Впрочем, частоту 400кГц табличным методом + тактируемая таймером дма, уже не сможите получить, если в таблице больше 5-10 значений.

Вот то, что сейчас может делать алгоритм.

post-6233-1297341699_thumb.jpg

Изменено пользователем pr0m

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Спасибо, мысль понятна, только аккумулятор бегает по кругу, через переполнение (изменение аака от 0 до 2^32-1 соответсвует одному периоду сигнала - фаза от 0 до 2*PI), причём на бОльших частотах шаг приращения акка выше. Предвычисление индексов и массива некоторого конечного размера для ЦАП-а, натравливание на него дма, и так опять же по кругу?
Да знаю я, как там фаза бегает.

 

У Вас - бегает, берётся индекс, выбирается из массива, заносится в ЦАП.

 

Проблема - нестабильность времени цикла.

 

Делаем - бегает, берётся индекс, выбирается из массива, заносится в небольшой кольцевой буфер в памяти. Оттуда по таймеру через ПДП в ЦАП. Даже прерывания всякие, дополнительная логика - не снесут работу ЦАП, если в кольцевом буфере достаточный запас и вычисления работают в среднем хотя бы немного быстрее, чем выборка из буфера по таймеру.

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Проблема - нестабильность времени цикла.

 

Делаем - бегает, берётся индекс, выбирается из массива, заносится в небольшой кольцевой буфер в памяти. Оттуда по таймеру через ПДП в ЦАП. Даже прерывания всякие, дополнительная логика - не снесут работу ЦАП, если в кольцевом буфере достаточный запас и вычисления работают в среднем хотя бы немного быстрее, чем выборка из буфера по таймеру.

Ага, прозрел, спасибо. Возможности стм32 ещё толком не изучил. Реализовал на 1-м таймере, но есть побочные эффекты, сформулирую - отпишусь. Опережая, скажу, что склоняюсь всё же к начальному варианту, без дма.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Красиво, получается, но основной недостаток - чуть медленнее, даже несмотря на то, что теперь код исполняется из флэш.

#define BUF_SIZE    256
........
register u8 iDMA,iBuff;
........
while(1)
    {
        iDMA = (u8)(BUF_SIZE - DMA1_Channel5->CNDTR);
        while(iBuff != iDMA)
        {            
            buff[iBuff++] = _sinewave[acc.field.index];
            acc.total += dp;
            dp += ddp;
            if( dp > freq_stop )
            {
                dp = freq_start;
                ddp = tau;
            }    
        }
        if(bStopDDS)
            break;            
    }

Потому что загрузка из одного массива в другой отъедает львиную долю тактов. Чтобы заполнение циклическго буфера успевало за ДМА, минимальный период таймера получился = 25тактов (против 20 при программной реализации, самостоятельно пишущей в ЦАПы).

 

И ещё - вчера долго пялился в эти несколько строчек, потому что не работало (полная пурга на выходе ЦАПов), пока опцию компилятора О3+Optimize for time не сменил на без оптимизации по времени. Ассемблер армовский толком ещё не изучил, так что разобраться, что там наворотил компилятор, не смог.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

В принципе, запись в буфер и запись в регистр ЦАП-а - одна и так же команда. Разницы быть не должно. Хотя в целом да, пересылок данных становится больше, дма ведь тоже ресурсы жрет. Так что учитывая жесткие временные рамки, может и правда оказаться, что этот вариант хуже.

 

А в чём преимущество в сравнении с 3 строчками кода, делающими это в главном цикле?

Стабильные тайминги + немножко больше свободы, например для оптимизации. Но если удалось получить стабильное время без дма, то может и нет этих преимуществ. С другой стороны получается, что толку от SMT32 никакого, только лишние сложности.

Изменено пользователем akimych

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Изменить тут можно кое-что, сделать меньше проверок на каждом витке цикла, заполнять буфер по неск. значений подряд.

Заполнять по половине (1/4 и т.п.), можно отслеживать флаги дма (для 1/2) или счетчик дма.

Борюсь за каждый такт, а так много проверок, каждая превращается в инструкцию типа branch - >=3 такта. Выбрал буфер 256 байт и счётчик 1байт по понятным причинам - чтобы избежать внутри цикла лишних движений:

if(iBuff>=BUZ_SIZE)
   iBuff = 0;

 

А так в принципе, запись в буфер и запись в регистр ЦАП-а - одна и так же команда.

Ну не одно и то же, говорит нам дизассемблер ).

 

Стабильные тайминги + немножко больше свободы, например для оптимизации. Но если удалось получить стабильное время без дма, то может и нет особых преимуществ. С другой стороны получается, что толку от SMT32 никакого, только лишние сложности.

Да, на ДМА остановился. И дело не в сложностях (один раз решаются), а в производительности. Для максимальной требуемой частоты известные мне 8-,16- разрядники не годятся, а стм32 показался оптимумом для моей задачи по соотношениям скорость-цена-доставабельность и т.п., не сочтите за рекламу. Вполне устроил бы шустрый камешек с 8 ногами наружу :)

Как дойдут руки и подучу ассемблер, вынесу в отдельный модуль и пооптимизирую цикл. Всем спасибо за советы, тему вроде исчерпал.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...