@.CHARSET  CP1251

@ GNU AS
.SYNTAX   unified
.CPU      cortex-m3
.THUMB

.INCLUDE   "/src/inc/gpio.inc"
.INCLUDE   "/src/inc/rcc.inc"

@.DESC     name=SOFT_SPI_INIT type=proc
@ +----------------------------------------------------------------------+
@ |              Процедура инициализации программного SPI                |
@ +----------------------------------------------------------------------+
@ | Модуль реализует программный SPI для отправки данных для любого GPIO |
@ | указанного в следующих настройках:                                   |
@ |                                                                      |
.EQU  spi_gpio_base , GPIOA_BASE         @ базовый (xxx_BASE) адрес gpio |
@ |                                                                      |
.EQU  gpio_enr_bit  , RCC_APB2ENR_IOPAEN_N    @ номер бита в RCC_APB2ENR |
@ |                               для включения выбранного spi_gpio_base |
@ |                                                                      |
@ | пины  интерфейса  могут  быть  назначены  произвольно,  но  оба пина |
@ | должны находится в одном из диапазонов: 0..7 или 8..15               |
@ | выбор пинов из разных диапазонов приведет к ошибке компиляции или    |
@ | некорректной работе модуля                                           |
.EQU  sck_pin       , 5                  @ номер пина для SCK            |
@ |                                                                      |
.EQU  mosi_pin      , 7                  @ номер пина MOSI               |
@ |                                                                      |
@ +----------------------------------------------------------------------+
@ | вызов SOFT_SPI_INIT параметров не требует, изменяет регистры R0-R2   |
@ +----------------------------------------------------------------------+
@ | видеоурок про данный модуль https://youtu.be/yp6tG4kkLZM             |
@ | ArmAsmEditor:  STM32  на  Ассемблере: Программная генерация сигналов |
@ | CLK и MOSI. Видео 14                                                 |
@ +----------------------------------------------------------------------+

@.ENDDESC

@.ITEM     APB2PERIPH_BASE_USER
.EQU  GPIOA_BASE         , APB2PERIPH_BASE + 0x0800  @ GPIO A
.EQU  GPIOB_BASE         , APB2PERIPH_BASE + 0x0C00  @ GPIO B
.EQU  GPIOC_BASE         , APB2PERIPH_BASE + 0x1000  @ GPIO C
.EQU  GPIOD_BASE         , APB2PERIPH_BASE + 0x1400  @ GPIO D
.EQU  GPIOE_BASE         , APB2PERIPH_BASE + 0x1800  @ GPIO E
.EQU  GPIOF_BASE         , APB2PERIPH_BASE + 0x1C00  @ GPIO F
.EQU  GPIOG_BASE         , APB2PERIPH_BASE + 0x2000  @ GPIO G
@.end

@.ITEM     RCC_APB2ENR_USER
.EQU  RCC_APB2ENR_IOPAEN_N, 2      @ GPIO A Enable Bit
.EQU  RCC_APB2ENR_IOPBEN_N, 3      @ GPIO B Enable Bit
.EQU  RCC_APB2ENR_IOPCEN_N, 4      @ GPIO C Enable Bit
.EQU  RCC_APB2ENR_IOPDEN_N, 5      @ GPIO D Enable Bit
.EQU  RCC_APB2ENR_IOPEEN_N, 6      @ GPIO E Enable Bit
.EQU  RCC_APB2ENR_IOPFEN_N, 7      @ GPIO F Enable Bit
.EQU  RCC_APB2ENR_IOPGEN_N, 8      @ GPIO G Enable Bit
@.END

@.CONFIG spi_gpio_base=APB2PERIPH_BASE_USER  gpio_enr_bit=RCC_APB2ENR_USER

@ блок расчета значений для настроек GPIO
.IF        sck_pin > 7
.EQU  spi_sck_pin   , sck_pin - 8
.EQU  spi_mosi_pin  , mosi_pin - 8
.EQU  gpio_crx      , GPIO_CRH                     @ имя регистра для настройки пинов: GPIO_CRH для пинов 8..15
.ELSE
.EQU  spi_sck_pin   , sck_pin
.EQU  spi_mosi_pin  , mosi_pin
.EQU  gpio_crx      , GPIO_CRL                     @ имя регистра для настройки пинов: GPIO_CRL для пинов 0..7
.ENDIF

.GLOBAL   SOFT_SPI_INIT
SOFT_SPI_INIT:      @ первоначальная настройка
                    MOV        R1, 1
                    MOV        R0, 0

                    @ включим gpio используемый для soft spi
                    LDR        R2, = PERIPH_BASE + PERIPH_BB_BASE + ( RCC_BASE + RCC_APB2ENR ) * 32
                    STR        R1, [ R2, gpio_enr_bit * 4 ]

                    @ настройка режима работы пинов GPIO
                    LDR        R2, = PERIPH_BASE + spi_gpio_base
                    LDR        R4, [ R2, gpio_crx ]    @ прочитаем состояние регистра GPIO_CRx ( x = [L, H] )
                    MOV        R3, GPIO_MODE_OUT50
                    BFI        R4, R3, spi_sck_pin * 4, 4
                    BFI        R4, R3, spi_mosi_pin * 4, 4
                    STR        R4, [ R2, gpio_crx ]

                    @ перевод пинов в первоначальное состояние
                    LDR        R2, = PERIPH_BASE + PERIPH_BB_BASE + ( spi_gpio_base + GPIO_ODR ) * 32

                    STR        R0, [ R2, sck_pin * 4 ]
                    STR        R0, [ R2, mosi_pin * 4 ]

                    BX         LR

@.DESC     name=SOFT_SPI_SEND type=proc
@ +-----------------------------------------------------------------------+
@ |     Процедура программной генерации сигналов отправки байта по SPI    |
@ +-----------------------------------------------------------------------+
@ | Передаваемые данные должны быть переданы в R4                         |
@ | Процедура по выходу изменяет значение R4, остальные регистры без      |
@ | изменений                                                             |
@ +-----------------------------------------------------------------------+
@ | Количество передаваемых бит:                                          |
.EQU  SPI_OUTSIZE   , 8      @ количество бит: 1..32                      |
@ |                                                                       |
@ | При генерации строба передаваемых данных не использована задержка     |
@ | Если частота gpio mcu перевысит 24 mhz то возможно придется поставить |
@ | несколько NOP между командами смены состояния SCK для того чтобы      |
@ | дисплей успевал обрабатывать сигналы с mcu                            |
@ |                                                                       |
@ | p.s. по  даташиту  PCD8544  может   воспринимать  данные  на  частоте |
@ | до 4 мбит в сек, но у меня экземпляры дисплеев обычно стабильно заво- |
@ | дятся и работают вплоть до частоты SPI в 24 мгц, 42 мгц уже не все... |
@ +-----------------------------------------------------------------------+
@.ENDDESC

.GLOBAL   SOFT_SPI_SEND
SOFT_SPI_SEND:      @ Отправка байта по spi
                    PUSH       { R0, R1, R2, R3 }
                    @ bitband значения
                    MOV        R0, 0
                    MOV        R1, 1
                    @ Регистр записи состояния пинов (GPIO_ODR) для bitband
                    LDR        R2, = PERIPH_BASE + PERIPH_BB_BASE + ( spi_gpio_base + GPIO_ODR ) * 32

                    MOV        R3, SPI_OUTSIZE    @ количество передаваемых бит
SOFT_SPI_LOOP:
                    @ установка состояния для MOSI
                    TST        R4, 0x80
                    ITE        EQ
                    STREQ      R0, [ R2, mosi_pin * 4 ]
                    STRNE      R1, [ R2, mosi_pin * 4 ]

                    @ импульс на SCK
                    STR        R1, [ R2, sck_pin * 4 ]    @ если дисплей "не успевает" воспринимать данные, то эти
                    STR        R0, [ R2, sck_pin * 4 ]    @ команды нужно разделить одной или несколькими NOP инструкциями

                    LSL        R4, R4, 1

                    SUBS       R3, R3, 1
                    BNE        SOFT_SPI_LOOP

                    POP        { R0, R1, R2, R3 }
                    BX         LR
