Individální projekty MPOA

Mikroprocesory s architekturou ARM

Uživatelské nástroje

Nástroje pro tento web


2015:stm32-dds

Rozdíly

Zde můžete vidět rozdíly mezi vybranou verzí a aktuální verzí dané stránky.

Odkaz na výstup diff

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}}
2015/stm32-dds.1453051506.txt.gz · Poslední úprava: 2016/01/17 18:25 autor: Jindřich Ryšavý