... как и я сам когда-то
Code: Select all
;==============================================================================
;--- описатели портов устройства ---
_CS EQU P1.4
MOSI EQU P1.5
MISO EQU P1.6
SCK EQU P1.7
;==============================================================================
;--- описатели для работы SPI ---
CMD0 EQU 0 ;reset the sd-card
CMD1 EQU 1 ;activates the card's initialization process
CMD9 EQU 9 ;read card specific data
CMD10 EQU 10 ;read card identification (CID register)
CMD17 EQU 17 ;read single block from SD-card
CMD24 EQU 24 ;write single block to SD-card
CMD55 EQU 55 ;next command to be application
ACMD13 EQU 13 ;read status block
SPI_REG_LEN EQU 010h ;длина регистров CID И CSD
spcr data 0d5h ;SPI control register
spsr data 0aah ;SPI status register
spif equ 10000000b ;interrupt flag
spdr data 086h ;SPI data register
WMCON DATA 096h
PS2 EQU 10000000b
PS1 EQU 01000000b
PS0 EQU 00100000b
EEMWE EQU 00010000b
EEMEN EQU 00001000b
DPS EQU 00000100b
WDTRST EQU 00000010b
WDTEN EQU 00000001b
;==============================================================================
ccitt_crc:
mov b,#8 ; 8 Bits In A Byte
xrl CRC_H,a ; HI ^= Data
l_p1:
clr c ; 0 Into Low Bit
mov a,CRC_L ; D_CRC << 1
rlc a ; Shift Left
mov CRC_L,a ; Store Back
mov a,CRC_H ; Get High Byte
rlc a ; Shift Left
mov CRC_H,a ; Store Back
jnc l_p2 ; Skip If Bit 15 Wasn't Set
xrl CRC_H,#10h ; XOR In Polynomial High
xrl CRC_L,#21h ; XOR In Polynomial Low
l_p2:
djnz b,l_p1 ; Repeat R0 More Times
ret ; Return To Caller
;========================================================================
; работа с SPI
;========================================================================
spi_init:
setb _cs
setb mosi
setb miso
setb sck
mov spcr, #01010001b
call spi_sync_byte
call spi_sync_byte
call spi_sync_byte
call spi_sync_byte
call spi_sync_byte
call spi_sync_byte
call spi_sync_byte
call spi_sync_byte
call spi_sync_byte
call spi_sync_byte
clr a
mov param1, a
mov param2, a
mov param3, a
mov param4, a
call spi_send_command
call spi_read_response
cjne a, #001h, _no_response
_wait_for_init:
mov a, #CMD1
call spi_send_command
call spi_read_response
jnz _wait_for_init
setb cy
ret
_no_response:
mov last_error, #err_sd_init
clr cy
ret
;--- чтение регистров CID и CSD SD-карты в ОЗУ ---
spi_read_csd:
mov b, #CMD9 ;номер команды чтения CSD в "В"
sjmp spi_read_reg ;переход на обработку команды
spi_read_cid:
mov b, #CMD10 ;номер команды чтения CID в "В"
spi_read_reg:
mov dptr, #temp_buffer
clr a ;обнулим аккумулятор
mov param1, a ;обнулим параметр 1
mov param2, a ;обнулим параметр 2
mov param3, a ;обнулим параметр 3
mov param4, a ;обнулим параметр 4
mov crc_h, a
mov crc_l, a
mov a, b ;получим команду из "В"
call spi_send_command ;отправить команду
call spi_wait_data
jnc _spi_read_cid_err
mov b, #spi_reg_len ;длина буфера
_spi_read_cid_l1:
push b
call spi_read_byte ;получить данные
call mem_write_rep ;запишем полученный байт в ОЗУ
call ccitt_crc
inc dptr ;увеличим указатель ОЗУ на 1
pop b
djnz b, _spi_read_cid_l1 ;если не все получено, то повторить
mov last_error, #err_crc
call spi_read_byte ;получим байт из SD-карты MSB_CRC16
cjne a, crc_h, _spi_read_cid_err
call spi_read_byte ;получим байт из SD-карты LSB_CRC16
cjne a, crc_l, _spi_read_cid_err
setb cy ;данные получены
ret ;вернемся
;--- чтение статуса SD-карты (ACMD13) ---
; всегда читаем во внешнее ОЗУ с адреса 0!!!
spi_read_status:
mov dptr, #00000h
clr a ;обнулим аккумулятор
mov param1, a ;обнулим параметр 1
mov param2, a ;обнулим параметр 2
mov param3, a ;обнулим параметр 3
mov param4, a ;обнулим параметр 4
mov a, #CMD55 ;получим команду расширения
call spi_send_command ;отправить команду
call spi_read_response ;ждем ответа
jnz _spi_read_cid_err ;ошибка выполнения команды
mov a, #ACMD13 ;чтение статуса
call spi_send_command ;отправить команду
call spi_wait_data_r2
jnc _spi_read_cid_err
mov b, #042h ;для получения 66 байт
sjmp _spi_read_data_l1 ;если все нормально, то читаем данные
;--- была ошибка ---
_spi_read_cid_err:
clr cy ;ошибка выполнения команды
ret ;вернемся
;--- чтение блока данных из SD карты ---
; на входе должен быть установлен DPTR
spi_read_data:
mov last_error, #err_read
mov a, #CMD17 ;команда чтения одного блока данных
call spi_send_command ;отправить команду
mov crc_h, #000h
mov crc_l, #000h
call spi_wait_data
jnc _spi_read_cid_err
call _spi_read_data_d1 ;выполним прием первых 256 байт
call _spi_read_data_d1 ;а после еще 256 байт
call spi_read_byte ;получим байт из SD-карты MSB_CRC16
cjne a, crc_h, _spi_read_cid_err
call spi_read_byte ;получим байт из SD-карты LSB_CRC16
cjne a, crc_l, _spi_read_cid_err
setb cy
ret ;вернемся
;---
_spi_read_data_d1:
mov b, #000h ;счетчик 256 байт
_spi_read_data_l1:
push b ;сохраним, т.к. точно потеряем
call spi_read_byte ;получим байт из SD-карты
call mem_write_rep ;запишем полученный байт в ОЗУ
call ccitt_crc
inc dptr ;увеличим указатель ОЗУ на 1
pop b ;восстановим счетчик принятых байт
djnz b, _spi_read_data_l1 ;если не все считано, то повторим
ret
;--- запись блока данных в SD карту ---
spi_write_data:
mov last_error, #err_write
mov a, #CMD24
call spi_send_command
call spi_read_response
jnz _spi_write_err
mov crc_h, a
mov crc_l, a
call spi_sync_byte
call spi_sync_byte
call spi_sync_byte
mov a, #0FEh
call spi_send_byte
call _spi_write_data
call _spi_write_data
mov a, crc_h
call spi_send_byte
mov a, crc_l
call spi_send_byte
call spi_read_response
anl a, #01Fh
cjne a, #005h, _spi_write_err
_spi_write_busy:
call spi_read_byte
jz _spi_write_busy ;wait for busy
setb cy
ret
_spi_write_err:
clr cy
ret
;--- send 256 bytes block from dptr and accumulate ccitt crc16 ---
_spi_write_data:
mov b, #000h
_spi_write_data_l1:
push b
movx a, @dptr
call spi_send_byte
movx a, @dptr
call ccitt_crc
pop b
inc dptr
djnz b, _spi_write_data_l1
ret
;--- wait for data block start ---
spi_wait_data_r2:
call spi_read_response ;ждем ответа R1
jnz _spi_wait_data ;ошибка выполнения команды
spi_wait_data:
call spi_read_response ;ждем ответа R1 или R2
jnz _spi_wait_data ;ошибка выполнения команды
call spi_read_response ;ждем готовности карты
cjne a, #0FEh, _spi_wait_data ;получен не заголовок блока данных
setb cy
ret
_spi_wait_data:
clr cy
ret
;--- read the response from sd-card ---
spi_read_response:
call spi_read_byte ;читаем шину
cpl a ;инверсия для проверки на #0FFh
jz spi_read_response ;если #000h (а фактически #0FFh), то ждем
cpl a ;инверсия, для восстановления фактического значения
ret ;вернемся
;--- in acc command to be send, and param[1..4] - parameters to be send ---
spi_send_command:
setb _cs ;отключимся от карты
push acc ;запомним номер команды
call spi_sync_byte ;межоперационное пространство
clr _cs ;подключимся к карте
call spi_sync_byte ;синхронизируемся
pop acc ;восстановим номер команды
orl a, #040h ;установим префикс
call spi_send_byte ;отошлем команду
mov a, param1 ;получим параметр #1
call spi_send_byte ;отошлем параметр #1
mov a, param2 ;получим параметр #2
call spi_send_byte ;отошлем параметр #2
mov a, param3 ;получим параметр #3
call spi_send_byte ;отошлем параметр #3
mov a, param4 ;получим параметр #4
call spi_send_byte ;отошлем параметр #4
mov a, #095h ;CRC7 для команды "0" (idle), остальные команды без проверки
sjmp spi_send_byte ;отошлем CRC7
;--- прием и синхронизация ведется при высоком уровне MOSI
spi_sync_byte:
spi_read_byte:
mov a, #0FFh ;во время приема идет передача "1"
;--- прием и передача идут одновременно, в аккумуляторе данные для передачи и результат приема ---
spi_send_byte:
mov spdr, a ;загрузим передатчик
_spi_send_byte:
mov a, spsr ;регистр флагов SPI
anl a, #spif ;проверим флаг готовности
jz _spi_send_byte ;если передача не завершена, то проверяем снова
mov a, spdr ;отсылка прошла успешно, в регистре данных результат приема
ret ;вернемся
В общем, перед CMD0 надо 10 штук FF кинуть(!!!), по документации это что-то типа синхронизации. Потом CMD0 и ждем response. Потом CMD1 и опять response. Теперь карта должна войти в нормальный режим работы по SPI, т.е. можно читать, писать, идентифицировать и т.п.
И еще, на МК надо корректно настроить фазы синхронизации относительно данных, в доке это описано. Для S8252 это через SPCR (SPI control register) делается.