@.CHARSET CP1251

@ GNU AS
.syntax unified
.cpu cortex-m4
.thumb
.fpu fpv4-sp-d16

.INCLUDE       "/src/inc/rcc.inc"
.INCLUDE       "/src/inc/i2c.inc"

.SECTION .asmcode

@.DESC     name=I2C_INIT type=proc
@ +--------------------------------------------------------------------+
@ |               Процедура инициализации интерфейса i2c1              |
@ +--------------------------------------------------------------------+
@ | скорость интерфейса около 400 кгц                                  |
@ +--------------------------------------------------------------------+
@.ENDDESC

.GLOBAL I2C_INIT
I2C_INIT:           @ включим тактирование i2c1
                    MOV             R2, 1
                    LDR             R1, = PERIPH_BB_BASE + ( RCC_BASE + RCC_APB1ENR ) * 32
                    STR             R2, [ R1, RCC_APB1ENR_I2C1EN_N * 4 ]

                    LDR             R1, =I2C1

                    @ сбросим I2C_CR1
                    MOV             R3, 0
                    STR             R3, [ R1, I2C_CR1]

                    @ установим частоту шины APB1 (смотреть sysclk, параметр RCC_CFGR_PPRE1_DIVx)
                    MOV             R0, 42 @ 42 - 100 кгц
                    STR             R0, [ R1, I2C_CR2 ]

                    @ установим скорость: th=tl=5 мкс clk=1/42 (APB1)=0.023809...
                    @ i2c_ccr=5\0.23809...=210
                    MOV             R0, 210  @ 210 - 100 кгц
                    STR             R0, [ R1, I2C_CCR ]

                    @ установим время ожидание (TRISE), это просто увеличенное на 1
                    @ значение частоты шины APB1
                    MOV             R0, 43  @ 43 - 100 кгц
                    STR             R0, [ R1, I2C_TRISE ]

                    @ включим i2c1
                    LDR             R0, = PERIPH_BB_BASE + ( I2C1_BASE + I2C_CR1 ) * 32
                    STR             R2, [ R0, I2C_CR1_PE_N * 4 ]

                    @ сбросим флаги перед выходом
                    LDR             R2, [ R1, I2C_SR1 ]
                    LDR             R2, [ R1, I2C_SR2 ]

                    BX              LR

@.DESC     name=I2C_WAIT_RELEASE type=proc
@ +--------------------------------------------------------------------+
@ |             Функция ожидания освобождения линий i2c                |
@ +--------------------------------------------------------------------+
@ | Функция ожидает освобождения линий i2c от сторонних передач        |
@ | Об освобождении говорит переход SDA SCL в высокий уровень          |
@ | (отсутствие прижима к земле, принятие последовательности STOP)     |
@ +--------------------------------------------------------------------+
@ | Функция не влияет на содержимое регистров                          |
@ |                                                                    |
@ | на выходе:                                                         |
@ |    Flag_Z = 1 : Успешное ожидание, шина свободна                   |
@ |    Flag_Z = 0 : Неуспех ! Шина занята !                            |
@ +--------------------------------------------------------------------+
@.ENDDESC

.GLOBAL I2C_WAIT_RELEASE
I2C_WAIT_RELEASE:
                    PUSH            { R0, R1, R2 }

                    MOV             R0, 2097152 @ время ожидания освобождения шины
                                                @ до генерации ошибки

                    LDR             R1, = PERIPH_BB_BASE + ( I2C1_BASE + I2C_SR2 ) * 32

I2C_WAIT_RELEASE_loop: @ цикл проверки сброса флага I2C_SR2_BUSY
                    LDR             R2, [ R1, I2C_SR2_BUSY_N * 4 ]
                    TST             R2, 1
                    BEQ             I2C_WAIT_RELEASE_exit   @ выходим с  Z=1

                    SUBS            R0, R0, 1
                    BNE             I2C_WAIT_RELEASE_loop
                    MOVS            R0, 1                   @ выходим с  Z=0
I2C_WAIT_RELEASE_exit:
                    POP            { R0, R1, R2 }
                    BX              LR


@.DESC     name=I2C_TARGET_BEGIN type=proc
@ +--------------------------------------------------------------------+
@ |         Процедура начала диалога с устройством на шине I2C         |
@ +--------------------------------------------------------------------+
@ | Процедура начинает диалог с устройством на шине I2C, для этого:    |
@ | - проверяется свободна ли шина                                     |
@ | - отправляется последовательность START                            |
@ | - отправляется адрес устройства                                    |
@ | - ожидается подтверждение от устройства ACK, либо генерируется     |
@ | ошибка обмена NACK                                                 |
@ +--------------------------------------------------------------------+
@ | на входе:                                                          |
@ |   R0: адрес устройства на шине (7 бит + бит RW)                    |
@ |                                                                    |
@ | на выходе:                                                         |
@ |    Flag_Z = 1 : Успех, устройство найдено и готово к обмену        |
@ |    Flag_Z = 0 : Неуспех !                                          |
@ +--------------------------------------------------------------------+
@.ENDDESC

.GLOBAL I2C_TARGET_BEGIN
I2C_TARGET_BEGIN:
                    PUSH            { R1, R2, R3, LR }
                    @ ожидаем освобождения шины
                    BL              I2C_WAIT_RELEASE
                    BNE             I2C_TARGET_DIALOG_exit

                    @ генерируем последовательность START
                    LDR             R1, = PERIPH_BB_BASE + ( I2C1_BASE + I2C_CR1 ) * 32
                    MOV             R2, 1
                    STR             R2, [ R1, I2C_CR1_START_N * 4 ]

                    @ ожидаем окончания генерации последовательности START
                    LDR             R1, = PERIPH_BB_BASE + ( I2C1_BASE + I2C_SR1 ) * 32
I2C_TARGET_DIALOG_wait_START:
                    LDR             R2, [ R1, I2C_SR1_SB_N * 4 ]
                    TST             R2, 1
                    BEQ             I2C_TARGET_DIALOG_wait_START
                    @ сбросим флаги, для этого просто читаем I2C_SR1
                    LDR             R3, = I2C1
                    LDR             R2, [ R3, I2C_SR1 ]

                    @ выставим адрес устройства на шине
                    STR             R0, [ R3, I2C_DR ]

                    @ ожидаем отправки адреса и подтверждения устройства
I2C_TARGET_DIALOG_wait_ADDR:
                    LDR             R2, [ R1, I2C_SR1_ADDR_N * 4 ]
                    TST             R2, 1
                    BEQ I2C_TARGET_DIALOG_check_AF         @ подтверждения нет
                                                           @ проверим нет ли ошибки

                    MOVS            R2, 0  @ установим Z=1,        устройство подтвердило работу
                    B               I2C_TARGET_DIALOG_exit @ и выставило ACK, выходим

I2C_TARGET_DIALOG_check_AF:
                    LDR             R2, [ R1, I2C_SR1_AF_N * 4 ]   @ читаем не прошел ли таймаут обмена
                    TST             R2, 1                          @ если 1 то произошла ошибка
                    BEQ             I2C_TARGET_DIALOG_wait_ADDR @ если 0 то ждем дальше

                    MOVS            R2, 1  @ установим Z=0,    у нас ошибка обмена, идем на выход

I2C_TARGET_DIALOG_exit:
                    @ сбросим флаги перед выходом
                    LDR             R2, [ R3, I2C_SR1 ]
                    LDR             R2, [ R3, I2C_SR2 ]

                    POP             { R1, R2, R3, PC }


@.DESC     name=I2C_SEND_BYTE type=proc
@ +--------------------------------------------------------------------+
@ |                       Процедура отправки байта                     |
@ +--------------------------------------------------------------------+
@ | Процедура отправляет байт на адресованное раньше устройство        |
@ +--------------------------------------------------------------------+
@ | на входе:                                                          |
@ |   R0: байт для отправки                                            |
@ |   R1: признак отправки последнего байта:                           |
@ |        R1=0 - не последний байт                                    |
@ |        R1=1 - последний байт                                       |
@ | Если байт последний (R1=1) то генерируется последовательность STOP |
@ |                                                                    |
@ +--------------------------------------------------------------------+
@.ENDDESC

.GLOBAL I2C_SEND_BYTE
I2C_SEND_BYTE:
                    PUSH            { R2, R3, R4 }

                    @ адреса для операций
                    LDR             R3, = I2C1
                    LDR             R4, = PERIPH_BB_BASE + ( I2C1_BASE + I2C_SR1 ) * 32

                    @ отправим байт данных
                    STR             R0, [ R3, I2C_DR ]

                    @ в зависимости от того последний байт или нет
                    @ выберем алгоритм завершения
                    CMP             R1, 1
                    BNE             I2C_SEND_BYTE_process @ байт не последний,
                                                          @ идем на проверку txe
I2C_SEND_BYTE_sended: @ ожидаем ухода данных из сдвигового регистра
                    LDR             R2, [ R4, I2C_SR1_BTF_N * 4 ]
                    TST             R2, 1
                    BEQ             I2C_SEND_BYTE_sended

                    @ генерируем последовательность STOP (завершение обмена с устройством)
                    LDR             R4, = PERIPH_BB_BASE + ( I2C1_BASE + I2C_CR1 ) * 32
                    STR             R2, [ R4, I2C_CR1_STOP_N * 4 ]

                    @ ожидаем завершения генерации последовательности STOP
I2C_SEND_BYTE_wait_STOP:
                    LDR             R2, [ R4, I2C_CR1_STOP_N * 4 ] @ бит STOP сброситься аппаратурой
                    TST             R2, 1                          @ после завершения последовательности
                    BNE             I2C_SEND_BYTE_wait_STOP

                    B               I2C_SEND_BYTE_exit             @ идем на выход

I2C_SEND_BYTE_process: @ проверим уход байта данных в хардварный буфер отправки (сдвиговый)
                    LDR             R2, [ R4, I2C_SR1_TXE_N * 4 ]
                    TST             R2, 1                  @ ждем пока установиться в 1 - это
                    BEQ             I2C_SEND_BYTE_process  @ значит байт ушел в буфер отправки

I2C_SEND_BYTE_exit:
                    POP             { R2, R3, R4 }
                    BX              LR
