Кстати, в модуле FPU тоже нет волшебных инструкций, только базовые арифметические операции, разве что только инструкция вычисления квадратного корня.
Cortex m4 dsp
- Сообщения: 535
- Зарегистрирован: Вт авг 28, 2012 22:21:33
Пользоваться библиотеками, которые используют эти преимущества (той же CMSIS DSP Software Library), или пользоваться средством разработки, которое позволяет писать на ассемблере.
Кстати, в модуле FPU тоже нет волшебных инструкций, только базовые арифметические операции, разве что только инструкция вычисления квадратного корня.
Кстати, в модуле FPU тоже нет волшебных инструкций, только базовые арифметические операции, разве что только инструкция вычисления квадратного корня.
- Реклама
Да, кажись стал догонять. Вот тут вроде все функции даны с их описанием. Причём как я понял есть как для девайсов с FPU так и без. Там же определены соответствующие типы данных. Всем большое спасибо за помощь. Буду курить описание дальше, может чё и откроется.
P.S.
Кому интересно в книжке "The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors" оказывается есть глава посвящённая DSP на кортексах. Правда она достаточна скупа на мой взгляд... но хотя бы без этих сра... дурацких ASF функций
P.S.
Кому интересно в книжке "The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors" оказывается есть глава посвящённая DSP на кортексах. Правда она достаточна скупа на мой взгляд... но хотя бы без этих сра... дурацких ASF функций
Снова здравствуйте, появился ещё вопрос, а именно: среди CMSIS ф-ций есть одна для расчёта значений синуса/косинуса - q31_t arm_sin_q31 (q31_t x), при том работает она с фиксированной запятой. В описании на функцию написано, что её аргумент должен быть выражен в радианах, т.е. допустим я захочу найти синус 180 градусов (да, знаю что он равен 0) т.е. в радиана это запишется так x = sin(3.14159265358979). Но как я понял, тип q31_t может принимать значения [-1; 1 - 2^-(n-1)] или примерно [-1;1]. Если же я засуну в эту ф-цию константу PI, то процессор начнёт обрабатывать плавающую запятую. т.к. #define PI 3.14159265358979f. Что делать то? 
- Сообщения: 535
- Зарегистрирован: Вт авг 28, 2012 22:21:33
Там написано, что оно не в радианах, а в приведенных радианах (scaled). Полуинтервал [0, 1) отображается на полуинтервал [0, 2·pi). Про отрицательные числа в описании я ничего не нашел.
И на всякий случай, надеюсь ты (надеюсь ничего, что я на ты) не собрался вызывать эту функцию к примеру вот так:
в надежде получить синус pi, потому что значения с фиксированной точкой хранятся в виде целых чисел, и чтобы получить синус pi в данном случае нужно делать вот так:
И на всякий случай, надеюсь ты (надеюсь ничего, что я на ты) не собрался вызывать эту функцию к примеру вот так:
Код: Выделить всё
arm_sin_q31(0.5f)Код: Выделить всё
arm_sin_q31(1073741824) // 1073741824 = 0.5 · 2^31Ничего ничего, так даже лучшенадеюсь ничего, что я на ты
В том то и дело что не собрался. То, что числа с фиксированной запятой представляются целыми числами я осознал, но вот как связать целые и дробные пока не допёр... Уже несколько статей прочёл но так и не понял. В книге про ядра кортекс написано, что для того что бы представить дробное число в виде q31_t нужно фот такую ф-лу посчитать F= I/2^(n-1), где:надеюсь ты (надеюсь ничего, что я на ты) не собрался вызывать эту функцию к примеру вот так
F - дробное число к пересчёту;
I - целое число которым представляется F;
n - разрядность.
Ну вот я и считал I = 3,1415 * 2147483648 = 6746319880,192 - число опять дробное... Ну я подставил 6746319880... ф-ция выдала число, я его пересчитал той же формулкой - какая то бяка... короче печаль тоска. Надо повнимательней про эту фиксированную запятую почитать. Спасибо за помощь сейчас поэкспериментирую.
P.S.
Слову "scaled" значения не придал - то ли трудности перевода, то ли увидел, что для функции с float просто радианы... Вообщем ещё раз спасибо, буду про scaled radians в купе с q31_t читать.
P.S.2
Вы совершенно правы, с числом 1073741824 ф-ция выдаёт 0
- Реклама
- Сообщения: 535
- Зарегистрирован: Вт авг 28, 2012 22:21:33
Всё правильно считал.Грендайзер писал(а):нужно фот такую ф-лу посчитать F= I/2^(n-1)... Ну вот я и считал I = 3,1415 * 2147483648 = 6746319880,192
Это нормально, не все числа могут быть точно представлены в двоичном формате с фиксированной точкой. В данном случае получившееся 6746319880.192 нужно округлить до ближайшего целого 6746319880. Понятно, что оно не будет точно представлять исходное 3.1415, это можно проверить проделав обратную операцию - разделив 6746319880 на 2³². В итоге мы получим число 3.141499999(9), а с полученной погрешностью придется смириться.Грендайзер писал(а):I = 3,1415 * 2147483648 = 6746319880,192 - число опять дробное...
Вернемся к нашем синусам. Дело в том, что 6746319880, которое как бы 3.14149, не может быть представлено в формате Q31. Под записью Qxx подразумевается, что значение хранится в виде 32-битного целого числа со знаком, которое может представлять диапазон от -2³¹ до 2³¹-1, а у нас целых 6 миллиардов - перебор. Теоретически, 3.14149 можно поместить Q29 или меньше, но к нашему случаю с функцией синуса это все не относится, потому что она принимает не радианы, а 2*pi радианы!
Это означает, что для вычисления sin(2*pi) нужно в качестве аргумента передать единицу, но единица тоже не может быть представлена в формате Q31. Однако, синус - периодическая функция и sin(2*pi) равен sin(0), то есть все значения выходящие за границы первого периода синуса должны быть отображены на него (вместо 2*pi нужно подставлять ноль). Теперь подытожим, что нужно сделать для использования этой функции:
1) Отобразить весь диапазон (-∞;+∞) входных значений на один период синуса [0, 2pi).
2) Перевести полученное значение в 2pi радианы, разделив его на 2pi.
3) Перевести полученное значение в формат Q31 умножив его на 2^31 и округлив до целого.
4) Только после всех этих шагов передавать итоговое значение в функцию.
Замечу, что переводить в Q31 можно где угодно, хоть в самом начале, только тогда не забудь подправить все последующие вычисления:
Потому что:Грендайзер писал(а):Вы совершенно правы, с числом 1073741824 ф-ция выдаёт 0
1) 1073741824(Q31) = 0.5
2) arm_sin_q31(1073741824) = sin(0.5*2*pi) = sin(pi) = 0
Последний раз редактировалось menzoda Пн авг 24, 2015 18:27:59, всего редактировалось 1 раз.
menzoda, огромное спасибо за Ваш комментарий
. К сожалению сегодня уже мозг отключился и информацию не усваивает. Но завтра обязательно всё переварю.
menzoda, спасибо ещё раз, всё получилось
. Наблюдаю на осциллографе ровненькую синусоиду частотой примерно 17 кГц. Правда после вычисления значений ф-цией пришлось сдвигать результат в право, что б обеспечить разрядность ЦАП. И сейчас вот дотумкал, что наверное надо было брать ф-цию arm_sin_q15 а не arm_sin_q31, один хрен ЦАП 12-ти разрядный... 
Снова здравствуйте. Опять упёрся. Чувствую, что разгатка где то рядом, а понять не могу. Вот есть у меня АЦП 12бит. Допустим, что я хочу принять из него данные, умножить их скажем на 0,7 и передать в ЦАП, опять таки 12 - ти разрядный. Как мне следует интерпретировать данные поступающие с АЦП и передаваемые на ЦАП. Т.е. Пусть данные с цап int data_adc, тогда для того, что бы умножить эти данные на 0,7 необходимо data_adc представить в формате скажем q15_t или data_adc(int) = (data_adc * 2^15)(q15_t). 0,7(10) = 0,7 * 2^15 = 22937. Тогда если данные для ЦАП int data_dac, то data_dac = (data_adc(q15_t) * 22937) >> 2^15. Однако в этом случае, наступает переполнение. Если же, данные поступающие с АЦП я интерпретирую как уже имеющие вид Q15 то всё нормально, однако если мне не нужно ничего ни на что умножать, то выходит: data_dac = data_adc(q15_t) >> 2^15 а в этом случае выходит, что данные пришедшие на ЦАП без умножения на 0,7 меньше, чем с умножением, что не верно. Читал про формат данных Qn однако, так и не дошёл до истины. 
- Сообщения: 535
- Зарегистрирован: Вт авг 28, 2012 22:21:33
Рассуждения по большей части верные. Первый вариант (как ты и описал) выглядит так:
Во-вторых, если мы хотим перемножать числа, то нужно выбрать такое n, чтобы результат произведения туда поместился. Кстати, совершенно необязательно иметь произведение в том же формате, что и множители. Более того, в разных форматах могут быть и оба множителя. В этом случае формула умножения будет выглядеть вот так:
Qp = Qq * Qr / 2^(q+r-p)
Но нам не нужны такие усложения, потому что оба множителя и произведение могут быть представлены в одном формате - Q15 вполне подходит. Этот вариант я привел просто для справки.
Теперь подходим к причине переполнения. Даже если исходные множители и произведение представимы в формате Qn, то промежуточный результат, который получается после умножения KQn на XQn, но до деления на 2^n может превосходить 32 бита. Проверяем с нашими цифрами:
4095 * 2^15 = 134184960
0.7 * 2^15 = 22938
134184960 * 22938 = 3077934612480 > 2^31
Это гораздо больше, чем может вместить 31-битное целое число со знаком, поэтому промежуточный результат нужно приводить к 64-битному знаковому типу:
Как видишь, можно сделать как написано выше и всё заработает, но можно так не делать. Значение АЦП необязательно переводить в формат с фиксированной точкой, потому что целое число, умноженное на любое Qn (по тем же самым правилам с последующим делением на 2^n) есть целая часть произведения. Обращаю внимание, что в случае с множителем 0.7 промежуточный результат не нужно будет приводить к 64-битному типу, однако в общем случае это делать нужно: стоит множителю увеличиться до некоторого значения и у нас опять возникнет промежуточный результат, не помещающийся в 32 бита. В итоге алгоритм выглядит так:
- 1) перевести значение АЦП в формат Qn: XQn=Xadc * 2^n
2) перевести множитель в тот же формат: KQn=K * 2^n
3) перемножить получившиеся значения: YQn=KQn * XQn / 2^n
4) перевести произведение в целое число: YDAC=YQn / 2^n
Во-вторых, если мы хотим перемножать числа, то нужно выбрать такое n, чтобы результат произведения туда поместился. Кстати, совершенно необязательно иметь произведение в том же формате, что и множители. Более того, в разных форматах могут быть и оба множителя. В этом случае формула умножения будет выглядеть вот так:
Qp = Qq * Qr / 2^(q+r-p)
Но нам не нужны такие усложения, потому что оба множителя и произведение могут быть представлены в одном формате - Q15 вполне подходит. Этот вариант я привел просто для справки.
Теперь подходим к причине переполнения. Даже если исходные множители и произведение представимы в формате Qn, то промежуточный результат, который получается после умножения KQn на XQn, но до деления на 2^n может превосходить 32 бита. Проверяем с нашими цифрами:
4095 * 2^15 = 134184960
0.7 * 2^15 = 22938
134184960 * 22938 = 3077934612480 > 2^31
Это гораздо больше, чем может вместить 31-битное целое число со знаком, поэтому промежуточный результат нужно приводить к 64-битному знаковому типу:
Код: Выделить всё
Y = (int32_t)(((int64_t)X * K) >> 15)- 1) переводим множитель в подходищий формат Qn: KQn=K * 2^n
2) умножаем значение АЦП на множитель (не забывая про приведение к int64_t): YDAC=KQn * XADC / 2^n
3) всё
Последний раз редактировалось menzoda Ср авг 26, 2015 17:22:54, всего редактировалось 1 раз.
Я так и думал, что где то, именно промежуточная операция всё портит, но вот к int64_t так и не пришёл
Теперь вроде всё прояснилось. menzoda, спасибо, снова всё разжевали! 
P.S.
menzoda, Вы опять правы. Действительно всё прекрасно заработало.
P.S.
menzoda, Вы опять правы. Действительно всё прекрасно заработало.
Снова здравствуйте. Появился новый вопрос по функциям CMSIS DSP, а именно по arm_fir_q15, которая реализует КИХ фильтр. Функция принимает следующие аргументы:
onst arm_fir_instance_q15 * S - указатель на какую то там структуру (с ней всё более мене ясно);
q15_t * pSrc - указатель на массив входных данных;
q15_t * pDst - указатель на массив, куда будут помещены данные с выхода фильтра;
uint32_t blockSize - количество отсчётов, которое должна будит сгенерить ф-ция после её вызова.
Вопросов несколько, а именно:
1) Не понятно как заталкивать данные в ф-цию, т.е. Вот я получил некоторое число с АЦП, я его записываю в массив Src, скажем он имеет 32 элемента. Затем я передал его ф-ции, она мне его посчитала и что дальше? Мне нужно ещё 32 значения с АЦП копить, или сдвигать имеющиеся?
2) Количество отсчётов которые сгенерит ф-ция... то же не совсем ясно... Вот у неё есть 32 входных отсчёта, если смотреть по структуре фильтра, то она мне должне по идее и выдать 32... или нет?
onst arm_fir_instance_q15 * S - указатель на какую то там структуру (с ней всё более мене ясно);
q15_t * pSrc - указатель на массив входных данных;
q15_t * pDst - указатель на массив, куда будут помещены данные с выхода фильтра;
uint32_t blockSize - количество отсчётов, которое должна будит сгенерить ф-ция после её вызова.
Вопросов несколько, а именно:
1) Не понятно как заталкивать данные в ф-цию, т.е. Вот я получил некоторое число с АЦП, я его записываю в массив Src, скажем он имеет 32 элемента. Затем я передал его ф-ции, она мне его посчитала и что дальше? Мне нужно ещё 32 значения с АЦП копить, или сдвигать имеющиеся?
2) Количество отсчётов которые сгенерит ф-ция... то же не совсем ясно... Вот у неё есть 32 входных отсчёта, если смотреть по структуре фильтра, то она мне должне по идее и выдать 32... или нет?


