Zde můžete vidět rozdíly mezi vybranou verzí a aktuální verzí dané stránky.
Obě strany předchozí revize Předchozí verze Následující verze | Předchozí verze | ||
2019:audio-resample [2020/01/19 19:21] Filip Šterc [Implementace] |
2019:audio-resample [2020/01/20 19:41] (aktuální) Filip Šterc [Schéma zapojení] |
||
---|---|---|---|
Řádek 7: | Řádek 7: | ||
===== Teorie ===== | ===== Teorie ===== | ||
+ | |||
+ | V této části se budeme zabývat teorií kolem převzorkování signálu z jednoho kmitočtu na druhý pomocí celočíselného poměru vzorkovacích frekvencí. V tomto projektu je konkrétně použit poměr 2:3 s frekvencemi 8 kHz a 12 kHz. Avšak tento algoritmus s poměrem 2:3 lze bez jakýcholiv změn použít i pro další frekvence vzorkování v tomto poměru. | ||
==== Interpolace ==== | ==== Interpolace ==== | ||
Řádek 12: | Řádek 14: | ||
Pro převzorkování signálu na jiný kmitočet je nutné kmitočet nejdříve interpolovat na nejbližší společný celočíselný násobek kmitočtů (vstupního a výstupního), v tomto případě se jedná o kmitočet 24kHz. Toto je provedeno přidáním nulových vzorků mezi každý vzorek vstupního signálu. Pro interpolaci na N*fvz kmitočet je třeba přidat N-1 nul mezi každý vzorek.\\ | Pro převzorkování signálu na jiný kmitočet je nutné kmitočet nejdříve interpolovat na nejbližší společný celočíselný násobek kmitočtů (vstupního a výstupního), v tomto případě se jedná o kmitočet 24kHz. Toto je provedeno přidáním nulových vzorků mezi každý vzorek vstupního signálu. Pro interpolaci na N*fvz kmitočet je třeba přidat N-1 nul mezi každý vzorek.\\ | ||
- | x1…x2…x3…x4…x5… -> x1…0…0…x2…0…0…x3…0…0…x4…0…0…x5...0…\\ | + | x1…x2…x3…x4…x5… -> x1…0…0…x2…0…0…x3…0…0…x4…0…0…x5…0…\\ |
Toto sice způsobí přesun vzorkovacího kmitočtu na N násobek původního kmitočtu, ale spektra přítomna na nižších násobcích vzorkovací frekvence zůstanou stále přítomna a zároveň způsobují nulovou úroveň nově přidaných vzorků. Proto je pro rekonstrukci signálu potřeba tyto spektra odfiltrovat pomocí vhodného FIR filtru. | Toto sice způsobí přesun vzorkovacího kmitočtu na N násobek původního kmitočtu, ale spektra přítomna na nižších násobcích vzorkovací frekvence zůstanou stále přítomna a zároveň způsobují nulovou úroveň nově přidaných vzorků. Proto je pro rekonstrukci signálu potřeba tyto spektra odfiltrovat pomocí vhodného FIR filtru. | ||
Řádek 50: | Řádek 52: | ||
===== Schéma zapojení ===== | ===== Schéma zapojení ===== | ||
- | signál ze zvukové karty počítače je v napěťovém rozmezí (-1 - 1) V. Použitý procesor však zvládá zpracovávat pouze kladná napětí, proto je třeba úroveň signálu nejdříve zvednout nad zem. Toto lze snadno vyřešit pomocí sériového kondenzátoru a napěťového děliče, který úroveň signálu posune na úroveň poloviny napájecího napětí 3V. | + | Signál ze zvukové karty počítače je v napěťovém rozmezí (-1 - 1) V. Použitý procesor však zvládá zpracovávat pouze kladná napětí, proto je třeba úroveň signálu nejdříve zvednout nad zem. Toto lze snadno vyřešit pomocí sériového kondenzátoru a napěťového děliče, který úroveň signálu posune na úroveň poloviny napájecího napětí 3V. |
{{2019:sch_xsterc02.png?500}} | {{2019:sch_xsterc02.png?500}} | ||
- | Při výběru hodnot kondenzátoru, je třeba brát ohled na frekvenci signálu. Pro signál se toto zapojení jeví jako filtr horní propusti s odpory zapojenými paralelně, proto pro vybrané hodnoty je mezní frekvence f<sub>mez</sub> = 1/(2*pi*0,5R*C) = 28,9 Hz. Pro člověka je slyšitelný pouze zvuk v rozmezí 16 Hz - 20 kHz, tudíž kromě malé krajní oblasti mezi (16 - 29) Hz, slyšitelný signál může projít beze změny. ¨ | + | Při výběru hodnot kondenzátoru, je třeba brát ohled na frekvenci signálu. Pro signál se toto zapojení jeví jako filtr horní propusti s odpory zapojenými paralelně, proto pro vybrané hodnoty je mezní frekvence f<sub>mez</sub> = 1/(2*pi*0,5R*C) = 28,9 Hz. Pro člověka je slyšitelný pouze zvuk v rozmezí 16 Hz - 20 kHz, tudíž kromě malé krajní oblasti mezi (16 - 29) Hz, slyšitelný signál může projít beze změny. |
===== Implementace ===== | ===== Implementace ===== | ||
- | Pro realizaci kódu jsem použil prostředí STM32CubeIDE, které kombinuje IDE založené na Eclipse společně s prostředím STM32CubeMX pro generování inicializačního kódu jednotlivých periferií. Bohužel se mi při pokusu o realizaci nepovedlo zprovoznit ovladače DMA, které byly součástí zadání. A to ani při sledování nejrůznějších návodů, či pokusu importovat dříve fungující projekt do tohoto prostředí. Z tohoto důvodu je finální realizace vytvořena pomocí přímého čtení hodnot z periferií.\\ | + | Pro realizaci kódu jsem použil prostředí EmBitz, společně s nástrojem STM32CubeMX pro generování inicializačního kódu procesorů od STMicroelectronics. |
- | Systém je časován pomocí přerušení dvou časovačů. Tyto časovače jsou nastaveny na časy čtení a zápisu vstupu a výstupů, tudíž 8 a 12 kHz. Aby nedocházelo k blokování jádra přerušením, je obsluha přesunuta do hlavní smyčky a přistupuje se k ní pomocí příznaků v podobně globálních funkcí, které jsou nastavovány příslušnými přerušeními.\\ | + | Systém funguje následujícím způsobem. Vstupní signál je vzorkován periferií ADC1, kanál 1 (PA1), pomocí ovladače DMA, který ukládá data vždy do vrchní poloviny bufferu adc_buffer[]. Po naplnění bufferu jsou tyto data přesunuta do spodní poloviny odkud jsou čteny pomocí periferie DAC, kanálu 1 (PA4), opět přes ovladač DMA. Tento výstup slouží pouze pro srovnání s později převzorkovaným signálem. Oba ovladače DMA jsou řízeny pomocí timeru TIM2 nastaveného na frekvenci 8 kHz. Aby bylo možné data zpracovávat každých 100 ms, je celková délka adc_bufferu nastavena na 2*800 vzorků.\\ |
- | Pro zpracování dat je použit kruhový buffer adcBuffer[] s délkou 1024 vzorků. Do tohoto bufferu jsou ukládány interpolované data ze vstupu, avšak zatím bez rekonstrukce, která je prováděna až před ukládáním na výstup. | + | Při přesunu dat mezi polovinami adc_bufferu, jsou tyto vzorky zároveň zkopírovány do bufferu upsample_buffer[], společně s 82 starými vzorky a proloženy dvěma nulami, pro realizaci nadvzorkování 3x. Dodatečných 82 vzorků z nižší poloviny adc_bufferu se zde nachází kvůli FIR filtru, který vyžaduje paměť 244 předchozích vzorků. (Po nadvzorkování je 82*3 = 246).\\ |
- | Pro pohyb v tomto bufferu jsou použity 2 ukazatele/indexy: adc_readptr a adc_writeptr. Tyto ukazatele jsou incializovány s offsetem poloviny délky bufferu aby nedocházelo k dočasnému vzájemnému přebíhání. Tento offset však zavede do signálu zpoždění, které je ale pro člověka nevnímatelné:\\ t<sub>d</sub> = offset/(N<sub>dec</sub>*f<sub>resamp</sub>) = 21ms.\\ | + | Přesun dat je realizován funkcí buffer_prepare(): |
- | <code c> | + | <code c> |
- | static uint32_t adcBuffer[ADC_BUFFER_LEN] = {0}; | + | void buffer_prepare(void){ |
- | static uint16_t adc_readptr = ADC_BUFFER_LEN/2; | + | for(uint16_t i = 0; i < (ADC_BUFFER_LEN + 82); i++){ |
- | static uint16_t adc_writeptr = 0; | + | upsample_buffer[i*3] = adc_buffer[ADC_BUFFER_LEN - 82 + i]; |
+ | if(i >= 82){ | ||
+ | adc_buffer[i - 82] = adc_buffer[ADC_BUFFER_LEN - 82 + i]; | ||
+ | } | ||
+ | } | ||
+ | } | ||
</code> | </code> | ||
- | Vzhledem k délce bufferu, která je mocnina dvojky, lze kruhovost bufferu realizovat jednoduchým logickým součinem při inkrementaci/dekrementaci ukazatelů: | + | Po přesunu dat do upsample_buffer[] jsou tyto data následně interpolována FIR filtrem. Pro snížení náročnosti je společně s interpolací okamžitě prováděna decimace zpracováním pouze každého druhého vzorku (liché vzorky by stejně byly zahozeny). Převzorkované data jsou ukládány do posledního bufferu dac_buffer[]. Aby se zamezila nutnost čekání mezi navzorkováním nových dat a jejich převzorkováním, je použit buffer dvojnásobné délky 2*1200 vzorků, kdy do jedné poloviny jsou ukládány zpracovávané data a z druhé poloviny jsou data čtena do kanálu 2 (PA5), periferie DAC. Čtení je prováděno ovladačem DMA, který je řízen timerem TIM4 s frekvencí 12 kHz.\\ |
- | <code c> | + | Převzorkování je umístěno ve funkci resample() se vstupním argumentem dac_buff_side, který určuje do které poloviny dac_bufferu mají být data ukládána: |
- | adc_writeptr = (adc_writeptr + 1) & (ADC_BUFFER_LEN - 1); | + | <code c> |
- | </code> | + | void resample(uint8_t dac_buff_side){ |
+ | float sum; | ||
+ | uint16_t out; | ||
+ | uint16_t readBuf; | ||
+ | |||
+ | for(uint16_t i = 0; i < DAC_BUFFER_LEN*2; i+=2){ //inkrementování read pointeru +2 pro decimaci 2* | ||
+ | sum = 0; | ||
+ | out = 0; | ||
+ | readBuf = i + 246; //uložení ukazatele pro ctení | ||
- | Vstup je realizován pomocí periferie ADC2, kanálu 1 (pin PA1). Analogová hodnota na tomto pinu je čtena s frekvencí 8 kHz a okamžitě zapisována do pole adcBuffer[], následována dvěma nulovýma hodnotama pro okamžitou realizaci interpolace. Současně je tato hodnota i poslána na kanál 1 (pin PA4) periferie DAC pro možnost porovnání nepřevzorkovaného signálu s převzorkovaným.\\ | + | for(uint8_t j = 0; j < FIR_LENGTH; j++){ //rekonstrukce interpolovaného signálu |
+ | sum += upsample_buffer[readBuf-j]*fxcoef[j]; | ||
+ | } | ||
- | <code c> | + | if(sum > 4095){ //prevedení na 32 bit hodnotu požadovanou DAC, se saturací |
- | HAL_ADC_Start(&hadc2); | + | out = 4095; |
- | HAL_ADC_PollForConversion(&hadc2, HAL_MAX_DELAY); | + | }else if(sum < 0){ |
- | value = HAL_ADC_GetValue(&hadc2); | + | out = 0; |
- | HAL_ADC_Stop(&hadc2); | + | }else{ |
+ | out = (uint32_t)(sum); | ||
+ | } | ||
+ | dac_buffer[dac_buff_side*DAC_BUFFER_LEN + (i>>1)] = out; | ||
+ | } | ||
- | HAL_DAC_SetValue(&hdac, DAC_CHANNEL_1, DAC_ALIGN_12B_R, value); | ||
- | adcBuffer[adc_writeptr] = value; | ||
- | for(uint8_t i = 0; i < 2; i++){ //upsampling | ||
- | adc_writeptr = (adc_writeptr + 1) & (ADC_BUFFER_LEN - 1); | ||
- | adcBuffer[adc_writeptr] = 0; | ||
} | } | ||
</code> | </code> | ||
- | Převzorkovaný výstup je realizovaný pomocí kanálu 2 (pin PA5) periferie DAC. Obsluha je opět v hlavní smyčce a je na ni přistupováno s frekvencí 12kHz. Zároveň je z této obsluhy volána funkce resample(), která realizuje rekonstrukci vzorku předem interpolovaného signálu z pole adcBuffer[], pomocí předem navrženého FIR filtru a následnou saturaci vzorku předávaného na výstup. Saturace však v tomto případě není nezbytná, protože vzhledem k poklesu energie signálu vlivem interpolace by výstupní signál měl mít vždy nižší amplitudu než vstupní signál.\\ | + | ===== Video ===== |
- | Decimace je definována pouze jako vynechávání vzorků a tak je zde realizována pomocí inkrementace ukazatele pro čtení o 2. | + | Na následujícím videu je ukázána funkčnost tohoto projektu při zpracování signálu o frekvenci 1 kHz. Na osciloskopu jsou zobrazeny signály od vrchu dolů:\\ CH3 - vstupní signál na pinu PA1,\\ CH1 - nepřevzorkovaný výstupní signál na pinu PA4,\\ CH2 - převzorkovaný výstupní signál na pinu PA5.\\ |
- | <code c> | + | Na nastavení osciloskopu je možné si všimnou, že i přes to, že se signály zdají být podobné, tak převzorkovaný signál na CH2 má rozlišení pouze 200mV/div, oproti ostatním s 500mV/div. Tato nižší amplituda je vlastností interpolace, kdy je prováděna rekonstrukce signálu odfiltrováním přebytečných kopií spektra, což způsobí pokles energie signálu (pro interpolaci 3x, pokles energie na 1/3). Pokud by bylo požadavkem zachování amplitudy signálu, bylo by třeba převzorkovaný signál vynásobit třemi, což ale vede i ke zvýšení šumu signálu. |
- | float sum = 0; | + | |
- | uint32_t out; | + | |
- | uint16_t readBufTmp = adc_readptr; //uložení ukazatele pro čtení | + | |
- | for(uint8_t i = 0; i < FIR_LENGTH; i++){ //rekonstrukce interpolovaného signálu | + | [[https://www.youtube.com/watch?v=_VB4ujhArGU|Video ukázka]] |
- | sum += adcBuffer[readBufTmp]*fxcoef[i]; | + | |
- | readBufTmp = (readBufTmp - 1) & (ADC_BUFFER_LEN - 1); | + | |
- | } | + | |
- | adc_readptr = (adc_readptr + 2) & (ADC_BUFFER_LEN - 1); //inkrementování read pointeru +2 pro decimaci 2* | + | ===== Soubory ===== |
- | if(sum > 4095){ //převedení na 32 bit hodnotu požadovanou DAC, se saturací | + | == EmBitz projekt == |
- | out = 4095; | + | {{2019:xsterc02_projekt_mpoa.zip}} |
- | }else if(sum < 0){ | + | |
- | out = 0; | + | |
- | }else{ | + | |
- | out = (uint32_t)(sum); | + | |
- | } | + | |
- | return out; | + | |
- | </code> | + | |
- | ===== Soubory ===== | + | == Matlab soubory == |
+ | {{2019:xsterc02_matlab.zip}} | ||