@GNU AS
@.CHARSET  CP1251

@.DESC     type=module
@ +------------------------------------------------------------------+
@ |             Модуль таймера SysTick для sysclk 24 мгц             |
@ +------------------------------------------------------------------+
@ | Для использования необходимо инициализировать модуль вызовом:    |
@ |     BL   SYSTICK_INIT                                            |
@ |                                                                  |
@ | В дальнейшем при необходимости осуществления задержки:           |
@ |    MOV R0, <время задержки в милисекундах>                       |
@ |    BL  SYSTICK_DELAY                                             |
@ +------------------------------------------------------------------+
@.ENDDESC

.SYNTAX   unified             @ синтаксис исходного кода
.THUMB                        @ тип используемых инструкций Thumb
.CPU      cortex-m3           @ процессор

@ определение констант
.EQU  SCB_BASE      , 0xE000ED00                   @ System control block (SCB)
.EQU  SHPR3         , 0x20                         @ System handler priority registers (SHPRx)
.EQU  SYSTICK_BASE  , 0xE000E010                   @ System timer
.EQU  STK_CTRL      , 0x00                         @ Регистр статуса и управления
.EQU  STK_LOAD      , 0x04                         @ Значение для перезагрузки счетчика
.EQU  STK_VAL       , 0x08                         @ Текущее значение счетчика
.EQU  STK_CTRL_CLKSOURSE, 0x00000004               @ (RW) источник тактирования: 0: AHB/8; 1: AHB
.EQU  STK_CTRL_TICKINT, 0x00000002                 @ (RW) при установке генерирует прерывание при переходе через 0
.EQU  STK_CTRL_ENABLE, 0x00000001                  @ (RW) включает счетчик.


.SECTION  .bss
.align (4)
@ Переменная в ОЗУ
SYSTICK_COUNTER:
                    .WORD      0                  @ Значение необходимой задержки


@.DESC     name=IRQ_SysTick type=proc
@ +----------------------------------------------------------------------+
@ |            Обработчик прерывания системного таймера SysTick          |
@ +----------------------------------------------------------------------+
@ |                                                                      |
@ | Прерывание уменьшает значение счетчика SYSTICK_COUNTER на "1" (в     |
@ | случае если значение счетчика больше "0"                             |
@ |                                                                      |
@ +----------------------------------------------------------------------+
@.ENDDESC

.SECTION  .asmcode

.GLOBAL   IRQ_SysTick
IRQ_SysTick:
                    LDR        R1, = SYSTICK_COUNTER
                    LDR        R0, [ R1, 0 ]
                    ORRS       R0, R0, 0          @ Проверка R0 на 0
                    ITT        NE                 @ Если R0<>0 уменьшаем его на 1
                    SUBNE      R0, R0, 1
                    STRNE      R0, [ R1, 0 ]
                    BX         LR

@.DESC     name=SYSTICK_INIT type=proc
@ +----------------------------------------------------------------------+
@ |               Инициализация системного таймера SysTick               |
@ +----------------------------------------------------------------------+
@ |                                                                      |
@ | Для частоты AHB=24 Мгц                                               |
@ | Частота счета 1000 Гц                                                |
@ |                                                                      |
@ | Период таймера определяется значением пересчета, SysTick считает     |
@ | от установленного значения до нуля и возникает прерывание, зна-      |
@ | чение пересчета напрямую регулирует частоту                          |
.EQU  systick_val   , 24000                        @                     |
@ | частота будет равна:  AHB / systick_val                              |
@ | по умолчанию 24 000 000 гц / 24 000 = 1000 гц (период 1 мс)          |
@ |                                                                      |
@ +----------------------------------------------------------------------+
@.ENDDESC

@ Включение SysTick
.GLOBAL   SYSTICK_INIT

SYSTICK_INIT:
                    PUSH       { R0, R1, LR }
                    LDR        R0, = SYSTICK_BASE

     @ установка значения пересчета для получения частоты 1000 гц
                    LDR        R1, = systick_val - 1
                    STR        R1, [ R0, STK_LOAD ]

     @ источник частоты AHB (24 мгц) + прерывания + включаем SYSTICK
                    LDR        R1, = ( STK_CTRL_CLKSOURSE + STK_CTRL_TICKINT + STK_CTRL_ENABLE )    @
                    STR        R1, [ R0, STK_CTRL ]

     @ установка приоритета прерываний от SysTick
                    LDR        R0, = SCB_BASE
                    LDR        R1, [ R0, SHPR3 ]
                    ORR        R1, R1, 0x0F << 28
                    STR        R1, [ R0, SHPR3 ]

                    POP        { R0, R1, PC }

@.DESC     name=SYSTICK_DELAY type=proc
@ +----------------------------------------------------------------------+
@ |             Задержка средствами системного таймера SysTick           |
@ +----------------------------------------------------------------------+
@ |                                                                      |
@ | Входной параметр: R0 - задержка в милисекундах                       |
@ | Выходной параметр: R0 = 0                                            |
@ | Изменение других регистров: нет                                      |
@ |                                                                      |
@ +----------------------------------------------------------------------+
@
@.ENDDESC

.GLOBAL   SYSTICK_DELAY

SYSTICK_DELAY:
                    PUSH       { R1, R2, LR }

                    LDR        R2, = SYSTICK_BASE    @ сбросим текущий счетчик
                    STR        R0, [ R2, STK_VAL ]

                    LDR        R1, = SYSTICK_COUNTER    @ адрес счетчика
                    STR        R0, [ R1, 0 ]      @ сохраним начальное значение
DELAY_LOOP:
                    LDR        R0, [ R1, 0 ]      @ ждем обнуления счетчика
                    ORRS       R0, R0, 0
                    BNE        DELAY_LOOP
                    POP        { R1, R2, PC }
