Kolega Dawe mne předehnal Než jsem stačil dopsat svůj díl seriálu o použití USART a STM32 VL Discovery kitu, tak napsal příklad programu sám, ale v assembleru (nebojte, článek o použití USART v C jazyku je neplánován na pozítří). Alespoň můžete porovnat délku obou programů! Text článku k programu v assembleru jsem si dovolil doplnit.

Úvodem

Ti, kteří si prostudovali první díl (a nebylo Vás málo, překvapilo mne že si článek zatím už přečetlo 146 lidí!), tak mají povědomí jak program v assembleru pro Cortex M3 napsat. Zkusím to zde trochu probrat.

Start programu nastavení překladače

Musíme dát překladači najevo, jaký máme mcu, jak máme řešit matematické operace v plovoucí čárce atd. Takže začátek programu bude vypadat následovně:
        .syntax unified
        .cpu cortex-m3
        .fpu softvfp
        .thumb
        .text



Adresy skoků

Další součástí programu musí být adresy skoků pro ošetření chyb a mimořádných událostí při běhu programu:
@-----------------------------------------------------------------------------
@ Vektor skoku STM32
@-----------------------------------------------------------------------------
        .word   _estack                         @ SP inicializace na konec SRAM
        .word   Reset_Handler +1                @ Vstupni bod po resetu
        .word   None_Handler +1                 @ NMI_Handler
        .word   None_Handler +1                 @ HardFault_Handler
        .word   None_Handler +1                 @ MemManage_Handler
        .word   None_Handler +1                 @ BusFault_Handler
        .word   None_Handler +1                 @ UsageFault_Handler
        .word   0, 0, 0, 0
        .word   None_Handler +1                 @ SVC_Handler
        .word   None_Handler +1                 @ DebugMon_Handler
        .word   0
        .word   None_Handler +1                 @ PendSV_Handler
        .word   None_Handler +1                 @ SysTick_Handler
        .word   None_Handler +1                 @ WWDG_IRQHandler
        .word   None_Handler +1                 @ PVD_IRQHandler
        .word   None_Handler +1                 @ TAMPER_IRQHandler
        .word   None_Handler +1                 @ RTC_IRQHandler
        .word   None_Handler +1                 @ FLASH_IRQHandler
        .word   None_Handler +1                 @ RCC_IRQHandler
        .word   None_Handler +1                 @ EXTI0_IRQHandler
        .word   None_Handler +1                 @ EXTI1_IRQHandler
        .word   None_Handler +1                 @ EXTI2_IRQHandler
        .word   None_Handler +1                 @ EXTI3_IRQHandler
        .word   None_Handler +1                 @ EXTI4_IRQHandler
        .word   None_Handler +1                 @ DMA1_Channel1_IRQHandler
        .word   None_Handler +1                 @ DMA1_Channel2_IRQHandler
        .word   None_Handler +1                 @ DMA1_Channel3_IRQHandler
        .word   None_Handler +1                 @ DMA1_Channel4_IRQHandler
        .word   None_Handler +1                 @ DMA1_Channel5_IRQHandler
        .word   None_Handler +1                 @ DMA1_Channel6_IRQHandler
        .word   None_Handler +1                 @ DMA1_Channel7_IRQHandler
        .word   None_Handler +1                 @ ADC1_IRQHandler
        .word   0, 0, 0, 0
        .word   None_Handler +1                 @ EXTI9_5_IRQHandler
        .word   None_Handler +1                 @ TIM1_BRK_TIM15_IRQHandler
        .word   None_Handler +1                 @ TIM1_UP_TIM16_IRQHandler
        .word   None_Handler +1                 @ TIM1_TRG_COM_TIM17_IRQHandler
        .word   None_Handler +1                 @ TIM1_CC_IRQHandler
        .word   None_Handler +1                 @ TIM2_IRQHandler
        .word   None_Handler +1                 @ TIM3_IRQHandler
        .word   None_Handler +1                 @ TIM4_IRQHandler
        .word   None_Handler +1                 @ I2C1_EV_IRQHandler
        .word   None_Handler +1                 @ I2C1_ER_IRQHandler
        .word   None_Handler +1                 @ I2C2_EV_IRQHandler
        .word   None_Handler +1                 @ I2C2_ER_IRQHandler
        .word   None_Handler +1                 @ SPI1_IRQHandler
        .word   None_Handler +1                 @ SPI2_IRQHandler
        .word   None_Handler +1                 @ USART1_IRQHandler
        .word   None_Handler +1                 @ USART2_IRQHandler
        .word   None_Handler +1                 @ USART3_IRQHandler
        .word   None_Handler +1                 @ EXTI15_10_IRQHandler
        .word   None_Handler +1                 @ RTCAlarm_IRQHandler
        .word   None_Handler +1                 @ CEC_IRQHandler
        .word   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
        .word   None_Handler +1                 @ TIM6_DAC_IRQHandler
        .word   None_Handler +1                 @ TIM7_IRQHandler  
        .word   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0


Kolega Dawe je poctivec, napsal tam adresy i které nebudete v tomto případě používat, ale do jiných programů, pak stačí tento kus kódu překopírovat a nad ničím už nemusíte spekulovat. Kdo potřebuje ušetřit každý bajt, tak pochopitelně nepoužité handlery může vyházet.


Nastavení hodin (24 MHz)

@-----------------------------------------------------------------------------
@ Vstupni bod po resetu
@-----------------------------------------------------------------------------
Reset_Handler:
                        @ ----------------------------------------------------
                        @ inicializace high-speed clock na 24Mhz
                        @ pokud je pritomen vnejsi krystal tak HSE*3
                        @ pokud neni tak HSI*3
                        @ ----------------------------------------------------
        movw    r12, #0x1000            @ adresa RCC registers bude v r12
        movt    r12, #0x4002
        ldr     r2, [r12, #0]
        orr.w   r2, r2, #1              @ povoli HSI clock v RCC_CR
        str     r2, [r12, #0]
        ldr     r2, [r12, #4]
        movw    r3, #0
        movt    r3, #0xf8ff
        and.w   r2, r2, r3              @ vynuluje RCC_CFGR krome nastaveni PLL
        str     r2, [r12, #4]
        ldr     r2, [r12, #0]
        bic.w   r2, r2, #0x01080000     @ zakaze PLL a zakaze CSS v RCC_CR
        bic.w   r2, r2, #0x00010000     @ zakaze HSE clock v RCC_CR
        str     r2, [r12, #0]
        ldr     r2, [r12, #0]
        bic.w   r2, r2, #0x00040000     @ nastavi HSE oscillator not bypassed
        str     r2, [r12, #0]
        ldr     r2, [r12, #4]
        bic.w   r2, r2, #0x003f0000     @ vynuluje v RCC_CFGR nastaveni PLL
        str     r2, [r12, #4]
        mov.w   r2, #0x009f0000         @ vymaze priznaky v RCC_CIR
        str     r2, [r12, #8]
        mov.w   r2, #0                  @ vynuluje PREDIV v RCC_CFGR2
        str     r2, [r12, #0x2c]
        ldr     r2, [r12, #0]
        orr.w   r2, r2, #0x10000        @ povoli HSE clock v RCC_CR
        str     r2, [r12, #0]
        movw    r3, #0x500              @ pocitadlo delky cekani na HSE
SetSysClockTo24_001:
        subs    r3, r3, #1              @ snizeni pocitadla
        beq     SetSysClockTo24_002     @ kdyz je konec cekani skoci s priznakem zapnuti HSI jako zdroje pro PLL R3=0
        ldr     r2, [r12, #0]
        ands.w  r2, r2, #0x00020000     @ je HSE ready?
        beq     SetSysClockTo24_001
        movw    r3, #0
        movt    r3, #1                  @ priznak zapnuti HSE jako zdroje pro PLL R3=0x10000
SetSysClockTo24_002:
        ldr     r2, [r12, #4]
        orr.w   r2, r2, r3              @ nastavi zdroj PLL podle R3
        orr.w   r2, r2, #0x00120000     @ nastavi pllmul*6 a PREDIV1 na /2
        str     r2, [r12, #4]
        ldr     r2, [r12, #0]
        orr.w   r2, r2, #0x01000000     @ zapne PLL
        str     r2, [r12, #0]
SetSysClockTo24_003:
        ldr     r2, [r12, #0]
        and.w   r2, r2, #0x02000000
        cmp     r2, #0                  @ cekame az bude PLL ready
        beq     SetSysClockTo24_003
        ldr     r2, [r12, #4]
        bic.w   r2, r2, #0x00000003
        orr.w   r2, r2, #0x00000002     @ zdroj hodin bude z PLL
        str     r2, [r12, #4]
SetSysClockTo24_004:
        ldr     r2, [r12, #4]
        and.w   r2, r2, #0x0000000c
        cmp     r2, #8                  @ cekame az bude zdroj nastaven
        bne     SetSysClockTo24_004


Ti kteří mají jinou desku než VL Discovery kit a jiný mcu než Value Line, tak můžou nastavit pochopitelně vyšší kmitočet clk pro jádro mcu.


Nastavení portu a USART

Stejně jako v C programu, musíme pustit hodiny do portu a USART jednotky a pak nastavit GPIO piny a USART.

        movw    r2, #0x4014             @ povoleni clk pro usart1 port A a port C
        str     r2, [r12, #0x18]

        movw    r11, #0x0800            @ GPIO portu A bude v R11
        movt    r11, #0x4001
        movw    r2, #0x44b4             @ inicializacni hodnota pro port A15-A8 (hight) A9=>TX, A10<=RX, A11<=CTS, A12=>RTS
        movt    r2, #0x444b
        str     r2, [r11, #0x04]

        movw    r9, #0x3800             @ adresa USART1 registers
        movt    r9, #0x4001

        movw    r2, #0x00d0             @ rychlost 115200
        str     r2, [r9, #0x08]

        movw    r2, #0x0300             @ usart_cr3 - EN cts, EN rts
        str     r2, [r9, #0x14]

        movw    r2, #0x200c             @ usart_cr1 - EN usart, EN transmitter, EN receiver
        str     r2, [r9, #0x0c]



Vlastní program

Vlastní program přijme znak a odešle jej zpět (dělá ECHO). Je to klasické demo pro předvedení funkčnosti USART.

cekejznak:
        ldr     r2, [r9, #0]
        ands.w  r2, r2, #0x00000020
        beq     cekejznak               @ cekej na znak
        ldr     r1, [r9, #4]            @ precti znak
posliznak:
        ldr     r2, [r9, #0]
        ands.w  r2, r2, #0x00000080
        beq     posliznak               @ cekej na moznost poslat znak
        str     r1, [r9, #4]            @ posli znak
        b       cekejznak



Závěr programu

Ten už je nezajímavý, tam dáme jen falešný odkaz na nerealizovanou obsluhu přerušení, aby překladač nenadával.

@-----------------------------------------------------------------------------
@ Nepouzite preruseni
@-----------------------------------------------------------------------------
None_Handler:
        b       None_Handler
@-----------------------------------------------------------------------------
        .data



Odkazy

Předcházející díl Na ARM s assemblerem 1. je zde.
Celý zdrojový kód v assembleru je ke stažení zde.


Program zaslal: Dawe