Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: КИХ фильтр на Cortex M4
Форум разработчиков электроники ELECTRONIX.ru > Цифровая обработка сигналов - ЦОС (DSP) > Сигнальные процессоры и их программирование - DSP
Грендайзер
Здравствуйте. Появился вопрос по функциям CMSIS DSP, а именно по arm_fir_q15, которая реализует КИХ фильтр. Функция принимает следующие аргументы:
onst arm_fir_instance_q15 * S - указатель на какую то там структуру (с ней всё более мене ясно);
q15_t * pSrc - указатель на массив входных данных;
q15_t * pDst - указатель на массив, куда будут помещены данные с выхода фильтра;
uint32_t blockSize - количество отсчётов, которое должна будит генерить ф-ция после её вызова.
Не понятно как заталкивать данные в ф-цию, т.е. Вот я получил некоторое число с АЦП, я его записываю в массив Src, скажем он имеет 32 элемента (коплю 32 отсчёта). Затем я передаю его (массив) в ф-цию, она мне его посчитала и что дальше? Мне нужно ещё 32 значения с АЦП копить, или сдвигать имеющиеся? Как правильно сделать никак не допетрю. Видел на форуме похожий вопрос, но вот найти его не могу... 05.gif

kovigor
Цитата(Грендайзер @ Oct 27 2015, 15:27) *
... или сдвигать имеющиеся? Как правильно сделать никак не допетрю

Почти уверен, что нужно сдвигать имеющиеся отсчеты на один и дописывать в образовавшееся пустое место новый отсчет. Кстати, это ведь очень просто проверить, например, на реальном сигнале или на сигнале, сформированном искусственно. Например, если это ФНЧ на 1 КГц, подайте ему на вход (просто наберите выборку с клавиатуры) меандр с частотой 1 КГц и посмотрите на реакцию ...
Грендайзер
Я взял пример из книги "The Definitive Guide to ARM Cortex-M3 and Cortex-M4 Processors" Вообщем сначала я копил отсчёты. Потом передавал их в функцию, затем передавал на ЦАП. Выходила какая то бяка. В указанной книге, сигнал генерится внутри контроллера, а затем буфер с набором входных отсчётов вдвигается в функцию по 8 элементов. Так же заполняется буфер выходных отсчётов (по 8 штук). Если вы считаете, что отсчёты надо сдвигать вправо, то очевидно на количество отсчётов которое генерит ф-ция. Т.е. ели ф-ция даёт скажем 8 отсчётов за раз, значит надо эти 8 отсчётов сначала накопить... Вообще ЦОС только осваиваю, поэтому многое не ясно. Но где то читал, что передача данных блоками вызывает какие то проблемы...
Grizzzly
Судя по размерности блока, на который указывает pState, здесь реализована блочная обработка, что и логично:
https://developer.mbed.org/users/simon/code...group__FIR.html
Проверить можно на эталонном сигнале в Matlab.

Да, так оно и есть:
http://www.disca.upv.es/aperles/arm_cortex..._i_r_l_p_f.html
Посмотрите внизу пример - arm_fir_example_f32.c
Грендайзер
Буду разбираться! Спасибо за помощь.

Посмотрел пример. Ф-ция используется следующим образом:
Код
/* ----------------------------------------------------------------------
  ** Call the FIR process function for every blockSize samples  
  ** ------------------------------------------------------------------- */
  for(i=0; i < numBlocks; i++)  
    {    
      arm_fir_f32(&S, inputF32 + (i * blockSize), outputF32 + (i * blockSize), blockSize);


Т.е. так же, как и в указанной мной книге. Но и в книге и в примере просто один разик обрабатывается блок данных. А я хочу обрабатывать данные, которые постоянно приходят с АЦП. Выходи всё же надо накопить блок отсчётов, передать ф-ции, затем в ЦАП. Затем снова накопить данные, передать данные в ф-цию и опять на цап?
Grizzzly
Почему же один блок? Обрабатывается в цикле numBlocks блоков.
Да, с АЦП отсчеты собираете в блоки и подаете на вход фильтра.
ViKo
Не понимаю, как можно обрабатывать целый блок данных и выдавать целый блок результата. Откуда брать данные на краях блока, которые должны быть более ранние или более поздние, чем есть в блоке?
Ага, кажется, представляю. Там, внутри программы, данные не теряются, а хранятся нужные из предыдущего блока. То есть, там внутри они передвигаются на нужные входы фильтра.
Grizzzly
Цитата(ViKo @ Oct 27 2015, 18:05) *
Не понимаю, как можно обрабатывать целый блок данных и выдавать целый блок результата. Откуда брать данные на краях блока, которые должны быть более ранние или более поздние, чем есть в блоке?
Ага, кажется, представляю. Там, внутри программы, данные не теряются, а хранятся нужные из предыдущего блока. То есть, там внутри они передвигаются на нужные входы фильтра.

В Matlab именно так функция filter реализована. Чтобы увидеть затухающий хвост, нужно дополнить входной сигнал нулями. А функция conv выполняет честную свертку.
ViKo
Цитата(Grizzzly @ Oct 27 2015, 18:25) *
В Matlab именно так функция filter реализована. Чтобы увидеть затухающий хвост, нужно дополнить входной сигнал нулями. А функция conv выполняет честную свертку.

Ну, хвост-то - ладно. Как и начало. Но между блоками как честно фильтровать? Только, если помнить, то было раньше. Или наоборот, вытянуть будущие отсчеты. Или, на мой взгляд, еще правильнее, с обеих сторон взять, и никакой задержки на половину фильтра не делать.
Грендайзер
Простите, суть не уловил... sad.gif
ViKo
Цитата(Грендайзер @ Oct 28 2015, 10:39) *
Простите, суть не уловил... sad.gif

Так вы попробуйте, покажете результат. sm.gif Думаю, как там сделано, работать будет.
Грендайзер
Вообщем написал вот такой код. На осциллографе какието иголки на фоне исигнала очень отдалённо напоминающего синус (очень длинные ступеньки хотя частоты дискретизации должно хватать). Может ошибка в коэффициентах, хотя их брал из книги. Буду дальше думать.
Код
void DACC_Handler( void )
{
    DACC -> DACC_IDR = DACC_IDR_TXRDY;
    DACC -> DACC_ISR;
    out_numb ++;
    if(out_numb == L)
    {    
        out_numb = 0;
        ADC -> ADC_IER = ADC_IER_EOC5;
        ADC -> ADC_CR = ADC_CR_START;
        return;
    }
    DACC -> DACC_IER = DACC_IER_TXRDY;    
    DACC -> DACC_CDR = DACC_CDR_DATA((outSignalQ15[out_numb] << 5) + 1024);
    return;
}


void ADC_Handler( void )
{
    ADC -> ADC_IDR = ADC_IDR_EOC5;
    ADC -> ADC_ISR;
        
        
    inSignalQ15[in_numb] = ADC -> ADC_CDR[5] & ADC_CDR_DATA_Msk;
    in_numb ++;
    
    if (in_numb == L)
    {
        
        int samp = 0;
        
        in_numb = 0;
        for (samp = 0; samp < L; samp += BLOCKSIZE)    
        {arm_fir_q15(&DTMF_FIR, inSignalQ15 + samp, outSignalQ15 + samp, BLOCKSIZE);}
        
        DACC -> DACC_IER = DACC_IER_TXRDY;
        DACC -> DACC_CDR = DACC_CDR_DATA((outSignalQ15[0] << 5) + 1024);
        return;
    }
    
    ADC -> ADC_IER = ADC_IER_EOC5;
    ADC -> ADC_CR = ADC_CR_START;
    return;
}

В коде 2 подпрограммы, одна управляет прерываниями с АЦП другая с ЦАП. L = 512, BLOCKSIZE = 8.
Santik
Код
for (samp = 0; samp < L; samp += BLOCKSIZE)    
        {arm_fir_q15(&DTMF_FIR, inSignalQ15 + samp, outSignalQ15 + samp, BLOCKSIZE);}

Вот это как-то непонятно совсем...
1. arm_fir_q15 должна вызываться только после накопления BLOCKSIZE выборок
2. Структура inSignalQ15 должна иметь размерность:число коэффициентов + BLOCKSIZE-1. При инициализации заполняется нулями.
Выборки с АЦП, числом BLOCKSIZE, должны записываться в отдельный буфер, определяемый arm_fir_init_q15 *pState.
Порядок заполнения буфера- реверсивный, так же как у коэффициентов фильтра:
b[numTaps-1], b[numTaps-2], b[N-2], ..., b[1], b[0]
Перед вызовом arm_fir_q15 этот буфер должен переписываться в конец inSignalQ15. Т.е. по адресу inSignalQ15 должно лежать самое "старое" значение выборки
3. Входные данные в структуре inSignalQ15 автоматически сдвигаются влево на величину BLOCKSIZE, освобождая место для следующего блока.
4. outSignalQ15 должна иметь размер BLOCKSIZE
Грендайзер
Santik, спасибо за ответ. Пример как уже писал взял из книги. Сейчас постараюсь как следует осознать то, что Вы написали и переписать код. Как сделаю отпишусь.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2024 Invision Power Services, Inc.