Zde můžete vidět rozdíly mezi vybranou verzí a aktuální verzí dané stránky.
| Následující verze | Předchozí verze | ||
|
2015:stm32-dds [2016/01/17 18:25] Jindřich Ryšavý vytvořeno |
2015:stm32-dds [2016/01/17 19:55] (aktuální) Jindřich Ryšavý [Generování obrazu funkce] |
||
|---|---|---|---|
| Řádek 2: | Řádek 2: | ||
| =====Hardware===== | =====Hardware===== | ||
| - | Pro ovládání generátoru dle stávající verze firmwaru je třeba připojit pět tlačítek. Vzhledem k dosažené vzorkovací frekvenci je výstup signálu paralelní, pro plné rozlišení 16b je třeba specializovaný převodník. | + | Pro ovládání generátoru dle stávající verze firmwaru je třeba připojit pět tlačítek a externími pull-up rezistory, interní pull-upy nejsou dostatečně tvrdé a způsobují falešné detekce při generování. Vzhledem k dosažené vzorkovací frekvenci je výstup signálu paralelní, pro plné rozlišení 16b je třeba specializovaný převodník. |
| =====Program===== | =====Program===== | ||
| - | Středobodem programu je smyčka generování vzorků, k jejímu vytvoření byly použity techniky loop unrollingu a specifika práce programového řadiče procesorů ARM při násobné práci s pamětí současně s ALU operacemi. | + | Středobodem programu je smyčka generování vzorků, k jejímu vytvoření byly použity techniky loop unrollingu a specifika práce programového řadiče procesorů ARM při násobné práci s pamětí současně s ALU operacemi. Proti původní realizaci smyčky v projektu AVR DDS 2.0 je pro její opuštění použit přímo argument frekvence, smyčka je tedy připravena na FM modulaci. |
| Proti původnímu očekávání nebyl použit řadič DMA, toto rozhodnutí vyplynulo z použití 16b výstupního vzorku. Pozice vzorku v poli je získána posunem registru o patřičný počet bitů vpravo, avšak pro zisk adresy je třeba opětovně posunout o jeden bit vlevo. Instrukce načtení z paměti integruje tento posuv v sobě, nepřináší proto žádné navýšení počtu potřebných taktů, Naopak dochází k úspoře při ostatních operacích s pamětí. | Proti původnímu očekávání nebyl použit řadič DMA, toto rozhodnutí vyplynulo z použití 16b výstupního vzorku. Pozice vzorku v poli je získána posunem registru o patřičný počet bitů vpravo, avšak pro zisk adresy je třeba opětovně posunout o jeden bit vlevo. Instrukce načtení z paměti integruje tento posuv v sobě, nepřináší proto žádné navýšení počtu potřebných taktů, Naopak dochází k úspoře při ostatních operacích s pamětí. | ||
| - | Po všech optimalizacích bylo možno dosáhnout výstupního vzorku každých 5 taktů, s čipem STM32F401RET6 taktovaným na garantovaném kmitočtu 84 MHz tak bylo dosaženo 16,8 MSa/s. Při závěrečném testování se žel ukázalo, že provádění programu nezáleží jen na posloupnosti instrukcí, ale také na použitých registrech. Korektní funkce smyčky psané v C nebo assembleru bez konkrétního přiřazení registrů tak nezávisí jen na smyčce samé, ale i na okolí místa, ze kterého je funkce volaná. | + | Po všech optimalizacích bylo možno dosáhnout výstupního vzorku každých 5 taktů, s čipem STM32F401RET6 taktovaným na garantovaném kmitočtu 84 MHz tak bylo dosaženo 16,8 MSa/s. Při závěrečném testování se žel ukázalo, že provádění programu nezáleží jen na posloupnosti instrukcí, ale také na použitých registrech. Korektní funkce smyčky psané v C nebo assembleru bez konkrétního přiřazení registrů tak nezávisí jen na smyčce samé, ale i na okolí místa, ze kterého je funkce volaná. Pro replikovatelnost výstelku je tedy nutné přepsat smyčku do assembleru s vyhrazenými registry. |
| + | =====Zdrojový kód smyčky===== | ||
| + | <code c> | ||
| + | uint32_t dds_gen(volatile uint32_t *step_ptr, uint32_t init_phase) //returns phase of next (not generated) sample | ||
| + | { | ||
| + | register uint32_t acc1; | ||
| + | register uint32_t acc2; | ||
| + | register uint32_t acc3; | ||
| + | register uint32_t acc4; | ||
| + | |||
| + | register uint32_t step; | ||
| + | |||
| + | register uint32_t wf_offset1; | ||
| + | register uint16_t wf_sample1; | ||
| + | register uint32_t wf_offset2; | ||
| + | register uint16_t wf_sample2; | ||
| + | register uint32_t wf_offset3; | ||
| + | register uint16_t wf_sample3; | ||
| + | register uint32_t wf_offset4; | ||
| + | register uint16_t wf_sample4; | ||
| + | //most of these will be optimized out, still needed to describe exact instruction sequence | ||
| + | |||
| + | acc1 = init_phase; | ||
| + | step = *step_ptr; | ||
| + | |||
| + | acc2 = acc1 + step; | ||
| + | wf_offset1 = (acc1>>(32-WF_DEPTH)); | ||
| + | acc3 = acc2 + (step); | ||
| + | wf_offset2 = (acc2>>(32-WF_DEPTH)); | ||
| + | acc4 = acc3 + (step); | ||
| + | |||
| + | wf_sample1 = wf[wf_offset1]; | ||
| + | //reset timer for correct DAC latch sync | ||
| + | //not implemented yet | ||
| + | |||
| + | while (step) | ||
| + | { //action taken clocks | ||
| + | acc1 = acc4 + (step); //1 add 0 //ALU before memory operation not messing with its address/data is free | ||
| + | asm volatile("":::"memory"); | ||
| + | GPIO_Write(DAC_PORT, wf_sample1); //1 write 2 | ||
| + | |||
| + | asm volatile("":::"memory"); | ||
| + | |||
| + | acc2 = acc1 + step; //2 add 1 | ||
| + | wf_offset3 = (acc3>>(32-WF_DEPTH)); //3 shift 1 | ||
| + | acc3 = acc2 + (step); //3 add 0 | ||
| + | asm volatile("":::"memory"); | ||
| + | wf_sample2 = wf[wf_offset2]; //2 read 2 | ||
| + | GPIO_Write(DAC_PORT, wf_sample2); //2 write 1 | ||
| + | |||
| + | asm volatile("":::"memory"); | ||
| + | |||
| + | |||
| + | wf_offset4 = (acc4>>(32-WF_DEPTH)); //4 shift 1 | ||
| + | acc4 = acc3 + (step); //4 add 0 | ||
| + | asm volatile("":::"memory"); | ||
| + | step = *step_ptr; //step read 2 | ||
| + | wf_sample3 = wf[wf_offset3]; //3 read 1 | ||
| + | GPIO_Write(DAC_PORT, wf_sample3); //3 write 1 | ||
| + | |||
| + | asm volatile("":::"memory"); | ||
| + | |||
| + | wf_offset1 = (acc1>>(32-WF_DEPTH)); //1 shift 0 | ||
| + | wf_offset2 = (acc2>>(32-WF_DEPTH)); //2 shift 1 | ||
| + | asm volatile("":::"memory"); | ||
| + | wf_sample4 = wf[wf_offset4]; //4 read 2 | ||
| + | wf_sample1 = wf[wf_offset1]; //1 read 1 | ||
| + | GPIO_Write(DAC_PORT, wf_sample4); //4 write 1 | ||
| + | |||
| + | // compare 1 | ||
| + | // branch 2 | ||
| + | /* assembly equivalent(addresses, registers & constants may vary): | ||
| + | adds r0, r2, r6 | ||
| + | str.w lr, [r1, #20] | ||
| + | |||
| + | add.w lr, r0, r2 | ||
| + | mov.w r8, r5, lsr #29 | ||
| + | add.w r5, lr, r2 | ||
| + | ldrh.w r7, [r3, r7, lsl #1] | ||
| + | str r7, [r1, #20] | ||
| + | |||
| + | mov.w r12, r6, lsr #29 | ||
| + | adds r6, r5, r2 | ||
| + | ldrh.w r7, [r3, r8, lsl #1] | ||
| + | ldr r2, [r4, #0] | ||
| + | str r7, [r1, #20] | ||
| + | |||
| + | mov.w r7, lr, lsr #29 | ||
| + | mov.w lr, r0, lsr #29 | ||
| + | ldrh.w r0, [r3, r12, lsl #1] | ||
| + | ldrh.w lr, [r3, lr, lsl #1] | ||
| + | str r0, [r1, #20] | ||
| + | |||
| + | cmp r2, #0 | ||
| + | bne.n 0x80003fa <main+174> | ||
| + | */ | ||
| + | } | ||
| + | return acc1; | ||
| + | } | ||
| + | </code> | ||
| + | =====Generování obrazu funkce===== | ||
| + | Při předpokladu velké šířky výstupního vzorku je nutné rovněž disponovat obraz signálu s dostatečně jemným odstupňováním, specializované DDS generátory zpravidla obsahují tabulku o 4 bity širší než výstupní vzorek. Je lépe proto vzorky počítat, než nést v programu. | ||
| + | <code c> | ||
| + | if(menu==Sinus) | ||
| + | { for(i=0;(i<(1<<(WF_DEPTH)));i++) | ||
| + | { | ||
| + | float sin_value = sinf( ((float)i *2*PI) / (float)(1<<(WF_DEPTH)) ); | ||
| + | wf[i]=(0xFFFF/2)*( 1 + sin_value ); | ||
| + | } | ||
| + | } | ||
| + | if(menu==Square) | ||
| + | { | ||
| + | for(i=0;(i<(1<<(WF_DEPTH)));i++) | ||
| + | { | ||
| + | if(i<(1<<(WF_DEPTH-1))) | ||
| + | wf[i]=0xFFFF; | ||
| + | else | ||
| + | wf[i]=0x0; | ||
| + | } | ||
| + | //for extra something on edges | ||
| + | wf[0]=0x7FFF; | ||
| + | wf[(1<<(WF_DEPTH-1))]=0x7FFF; | ||
| + | } | ||
| + | </code> | ||
| + | =====Obsluha tlačítek===== | ||
| + | Během nastavování parametrů generátoru lze stisk detekovat běžnými nástroji RTOS, při generování je jim přiřazena priorita pomocí přerušení. | ||
| + | <code c> | ||
| + | //startup section | ||
| + | SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource15); | ||
| + | SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource14); | ||
| + | SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource13); | ||
| + | SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource12); | ||
| + | SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOB, EXTI_PinSource10); | ||
| + | |||
| + | EXTI_InitTypeDef EXTI_InitStruct; | ||
| + | |||
| + | EXTI_InitStruct.EXTI_Line = BTN_UP|BTN_DWN|BTN_RIGHT|BTN_LEFT|BTN_START; | ||
| + | EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt; | ||
| + | EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Falling; | ||
| + | EXTI_InitStruct.EXTI_LineCmd = ENABLE; | ||
| + | EXTI_Init(&EXTI_InitStruct); | ||
| + | | ||
| + | //interrupt handler | ||
| + | void EXTI15_10_IRQHandler(void) | ||
| + | { | ||
| + | btn_state = 0; | ||
| + | gen_step = 0; | ||
| + | |||
| + | if (EXTI_GetITStatus(BTN_UP) != RESET) | ||
| + | btn_state |= BTN_UP; | ||
| + | if (EXTI_GetITStatus(BTN_DWN) != RESET) | ||
| + | btn_state |= BTN_DWN; | ||
| + | if (EXTI_GetITStatus(BTN_LEFT) != RESET) | ||
| + | btn_state |= BTN_LEFT; | ||
| + | if (EXTI_GetITStatus(BTN_RIGHT) != RESET) | ||
| + | btn_state |= BTN_RIGHT; | ||
| + | /*if (EXTI_GetITStatus(BTN_START) != RESET) | ||
| + | btn_state |= BTN_START; | ||
| + | */ | ||
| + | NVIC_InitTypeDef NVIC_InitStruct; | ||
| + | |||
| + | NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn; | ||
| + | NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x02; | ||
| + | NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x03; | ||
| + | NVIC_InitStruct.NVIC_IRQChannelCmd = DISABLE; | ||
| + | NVIC_Init(&NVIC_InitStruct); | ||
| + | } | ||
| + | </code> | ||
| + | =====Fotografie===== | ||
| + | {{2015:stm32-dds:p1030965.jpg}} | ||
| + | {{2015:stm32-dds:p1030966.jpg}} | ||
| + | {{2015:stm32-dds:p1030967.jpg}} | ||
| + | |||
| + | =====Kompletní zdrojový kód===== | ||
| + | {{2015:stm32-dds:Main.c.doc}} | ||