======Generátor signálu s přímou číslicovou syntézou====== =====Hardware===== 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===== 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í. 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===== 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 */ } return acc1; } =====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. 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; } =====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í. //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); } =====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}}