INCLUDE stm32l1xx_constants.s
AREA lab10_DAC, CODE, READONLY
EXPORT __main
IMPORT SIN_LOOKUP
ALIGN
ENTRY
__main FUNCTION
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Enable HSI & set as system clock
BL HSI_SETUP
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; GPIO PA.4 and PA.5 as Analog
BL ANALOG_OUT_CONF
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Configure TIM4 as Master Trigger
BL TIM4_CONFIG
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Configure DAC (DAC_OUT1 = PA.4, DAC_OUT2 = PA.5)
BL DAC_CONFIG
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; NVIC Interrupt
BL TIM4_INT_CONF
stop B stop
ENDFUNC ; END MAIN
HSI_SETUP FUNCTION
;1. Turn on HSI (RCC_CR_HSION)
LDR r0, =(RCC_BASE+RCC_CR)
LDR r1, [r0]
ORR r1, r1, #RCC_CR_HSION
STR r1, [r0] ;v
;2. Wait for HSI ready (RCC_CR_HSIRDY)
LDR r0, =(RCC_BASE+RCC_CR)
hsi_wait LDR r1, [r0]
AND r1, r1, #RCC_CR_HSIRDY
CMP r1, #0
BLO hsi_wait ;v
;4. Select HSI as the system clock (RCC_CFGR_SW_HSI)
; 00: MSI oscillator used as system clock
; 01: HSI oscillator used as system clock RCC_CFGR_SW_HSI
; 10: HSE oscillator used as system clock
; 11: PLL used as system clock
LDR r0, =(RCC_BASE+RCC_CFGR)
LDR r1, [r0]
BIC r1, r1, #RCC_CFGR_SW
ORR r1, r1, #RCC_CFGR_SW_HSI
STR r1, [r0] ;v
;2. Wait for HSI to be selected as system clock (RCC_CFGR_SWS_HSI)
LDR r0, =(RCC_BASE+RCC_CFGR)
hsi_wait1 LDR r1, [r0]
AND r1, r1, #RCC_CFGR_SWS_HSI
CMP r1, #0
BLO hsi_wait1 ;v?
BX lr
ENDFUNC
ANALOG_OUT_CONF FUNCTION
; Note: DAC_OUT1 = PA.4, DAC_OUT2 = PA.5
; 1. Enable the clock of GPIO A (RCC_AHBENR_GPIOAEN)
LDR r0, =(RCC_BASE+RCC_AHBENR)
LDR r1, [r0]
ORR r1, r1, #RCC_AHBENR_GPIOAEN
STR r1, [r0] ;v
; 2. Set PA.4 and PA.5 as Analog (GPIO_MODER):: Analog: 0b11
LDR r0, =(GPIOA_BASE+GPIO_MODER)
LDR r1, [r0]
ORR r1, r1, #(GPIO_MODER_MODER4 :OR: GPIO_MODER_MODER5)
STR r1, [r0] ;v?
BX lr
ENDFUNC
TIM4_CONFIG FUNCTION
; 1. Enable the clock of TIM4 (RCC_APB1ENR_TIM4EN)
LDR r0, =(RCC_BASE+RCC_APB1ENR)
LDR r1, [r0]
ORR r1, r1, #RCC_APB1ENR_TIM4EN
STR r1, [r0] ;v
; 2. Set the prescaler (TIM4->PSC)
LDR r0, =(TIM4_BASE+TIM_PSC)
MOV r1, #(18+1)
STR r1, [r0] ;v
; 3. Set the auto reload value (TIM4->ARR)
LDR r0, =(TIM4_BASE+TIM_ARR)
MOV r1, #(65535)
STR r1, [r0] ;v
; 4. Set the compare register (TIM4->CCR1)
LDR r0, =(TIM4_BASE+TIM_CCR1)
LDR r1, [r0]
MOV r1, #19
STR r1, [r0] ;v
; 5. Set OC1M bits of TIM4->CCMR1 for Channel 1 to Toggle OC1REF when TIM4_CNT=TIM4_CCR1
; 011: Toggle - OC1REF toggles when TIMx_CNT=TIMx_CCR1. ;@ WHY?
LDR r0, =(TIM4_BASE+TIM_CCMR1)
LDR r1, [r0]
BIC r1, r1, #TIM_CCMR1_OC1M
ORR r1, r1, #(TIM_CCMR1_OC1M_0 :OR: TIM_CCMR1_OC1M_1)
STR r1, [r0] ;v
; 6. Enable compare output 1 (TIM_CCER_CC1E)
LDR r0, =(TIM4_BASE+TIM_CCER)
LDR r1, [r0]
ORR r1, r1, #TIM_CCER_CC1E
STR r1, [r0] ;v
; 7. Enable the update (TIM_EGR_UG)
LDR r0, =(TIM4_BASE+TIM_EGR)
LDR r1, [r0]
ORR r1, r1, #TIM_EGR_UG
STR r1, [r0] ;This looks like it goes into the wrong reg...
; 8. Clear the update flag (TIM_SR_UIF)
LDR r0, =(TIM4_BASE+TIM_SR)
LDR r1, [r0]
BIC r1, r1, #TIM_SR_UIF
STR r1, [r0] ;This cleared what we just did
; 9. Enable the TIM4 interrupts (TIM_DIER_UIE, TIM_DIER_CC1IE)
LDR r0, =(TIM4_BASE+TIM_DIER)
LDR r1, [r0]
ORR r1, r1, #(TIM_DIER_UIE :OR: TIM_DIER_CC1IE)
STR r1, [r0] ;v
; 10. Select the master mode as OC1REF signal is as trigger output TRGO (TIM_CR2_MMS)
; 100: Compare - OC1REF signal is used as trigger output (TRGO)
LDR r0, =(TIM4_BASE+TIM_CR2)
LDR r1, [r0]
BIC r1, r1, #(TIM_CR2_MMS)
ORR r1, r1, #(TIM_CR2_MMS_2)
STR r1, [r0] ;v
; 11. Enable the timer (TIM_CR1_CEN)
LDR r0, =(TIM4_BASE+TIM_CR1)
LDR r1, [r0]
ORR r1, r1, #(TIM_CR1_CEN)
STR r1, [r0] ;v
BX lr
ENDFUNC
DAC_CONFIG FUNCTION
; 1. Enable DAC clock (RCC_APB1ENR_DACEN)
LDR r0, =(RCC_BASE+RCC_APB1ENR)
LDR r1, [r0]
ORR r1, r1, #(RCC_APB1ENR_DACEN)
STR r1, [r0] ;v
; 2. Enable DAC output buffer (DAC_CR_BOFF1, DAC_CR_BOFF2)
LDR r0, =(DAC_BASE+DAC_CR)
LDR r1, [r0]
BIC r1, r1, #(DAC_CR_BOFF1 :OR: DAC_CR_BOFF2)
STR r1, [r0] ;v
; 3. Select TIM4 TRGO as trigger for both outputs (DAC_CR_TSEL1, DAC_CR_TSEL2)
;DAC_CR_TSEL1 ||| 101: Timer 4 TRGO event
LDR r0, =(DAC_BASE+DAC_CR)
LDR r1, [r0]
BIC r1, r1, #(DAC_CR_TSEL1)
ORR r1, r1, #(DAC_CR_TSEL1_0 :OR: DAC_CR_TSEL1_2)
STR r1, [r0] ;v
;DAC_CR_TSEL2 ||| 101: Timer 4 TRGO event
LDR r0, =(DAC_BASE+DAC_CR)
LDR r1, [r0]
BIC r1, r1, #(DAC_CR_TSEL2)
ORR r1, r1, #(DAC_CR_TSEL2_0 :OR: DAC_CR_TSEL2_2)
STR r1, [r0] ;v
; 4. Enable DAC1 and DAC2 (DAC_CR_EN1, DAC_CR_EN2)
LDR r0, =(DAC_BASE+DAC_CR)
LDR r1, [r0]
ORR r1, r1, #(DAC_CR_EN1 :OR: DAC_CR_EN2)
STR r1, [r0] ;v
BX lr
ENDFUNC
TIM4_INT_CONF FUNCTION
; 1. Enable TIM4_IRQn
LDR r0, =NVIC_BASE
LDR r1, [r0, #NVIC_ISER0]
ORR r1, r1, #1<<TIM4_IRQn
STR r1, [r0, #NVIC_ISER0]
; 2. Set priority for TIM4_IRQn
; LDR r3, =NVIC_BASE
; ADD r0, r3, #NVIC_IPR0
; LDR r1, [r0, #28]
; BIC r1, r1, #NVIC_IPR7_PRI_30
; STR r1, [r0, #28]
BX lr
ENDFUNC
TIM4_IRQHandler FUNCTION
EXPORT TIM4_IRQHandler
PUSH {lr,r4-r6}
LDR r0, =(TIM4_BASE+TIM_SR)
LDR r0, [r0]
AND r1, r0, #TIM_SR_CC1IF
CMP r1, #0 ; check if the CC1IF is set
POPEQ {pc,r4-r6} ; not the interrupt we want, exit out
LDR r0, =value
LDR r6, [r0]
MOV r0, #36 ; should be 36
MUL r6, r6, r0 ;r6 = counter * 36
MOV r0, #10
UDIV r6, r6, r0 ; r6 = r6 / 10
MOV r0, r6
BL SIN_LOOKUP ;r0 = x degrees lookup value
MOV r4, r0 ;Save the A value
; ADD r6, r6, #1 ;(counter *3) + 1
; MOV r0, r6
; BL SIN_LOOKUP ;r0 = x degrees lookup value
; MOV r5, r0 ;Save the B value
; LDR r0, =value
; LDR r6, [r0]
; MOV r1, #36 ; should be 36
; MUL r6, r6, r1 ; r6 = c*36
; ;frac = mod(r6, 10)
; ;;x = mod(x, 360);
; MOV r3, #10
; UDIV r1, r6, r3 ;r1 = Q
; MLS r6, r1, r3, r6 ;MLS{cond} Rd, Rn, Rm, Ra ;;ra - rn*rm
; ;r6 = frac ;;should I be handling two decimal places?
; SUB r0, r5, r4 ; r0 = B-A
; MUL r0, r0, r6 ; r0 = (B-A)*frac
; MOV r1, #10
; SDIV r0, r0, r6 ; r0 = [(B-A)*frac]/10
; ADD r0, r0, r4 ; r0 = [(B-A)*frac]/10 + A
; MOV r4, r0
; Increment the counter
LDR r1, =value
LDR r6, [r1]
ADD r6, r6, #1
STR r6, [r1]
;Put r0 into the DAC's Conversion Register
;DAC->DHR12RD = sin(v) << 16 | sin(v);
;When using dual channels, the values stored in a shared register
ORR r4, r4, r4, lsl #16 ; r4 = r4|r4<<16 ;;for both channels
LDR r0, =(DAC_BASE + DAC_DHR12RD)
STR r4, [r0]
; ADD r6, r6, #3 ;floor(3.6)
; MOV r0, r6
; BL SIN_LOOKUP ;r0 = x degrees lookup value
; MOV r4, r0 ;Save the A value
; ADD r6, r6, #1 ;ceil(3.6)
; MOV r0, r6
; BL SIN_LOOKUP
; MOV r5, r0 ;Save the B value
; SUB r0, r5, r4 ; r0 = B-A
; MOV r1, #6
; MUL r0, r0, r1 ; r0 = (B-A)*6
; MOV r1, #10
; SDIV r0, r0, r1 ; r0 = [(B-A)*6]/10
; ADD r0, r0, r4 ; r0 = [(B-A)*6]/10 + A
; ;Store the calculated value ; this is going to accumulate error
; LDR r1, =value
; STR r0, [r1]
; ;Put r0 into the DAC's Conversion Register
; ;DAC->DHR12RD = sin(v) << 16 | sin(v);
; ;When using dual channels, the values stored in a shared register
; MOV r4, r0
; ORR r4, r4, r4, lsl #16 ; r4 = r4|r4<<16 ;;for both channels
; LDR r0, =(DAC_BASE + DAC_DHR12RD)
; STR r4, [r0]
POP {pc,r4-r6}
ENDFUNC
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
AREA mydata, DATA
value DCD 0
END