📄 mmc.asm
字号:
/* ***********************************************************************
**
** Copyright (C) 2002 Jesper Hansen <jesperh@telia.com> and
** Romuald Bialy (MIS) <romek_b@o2.pl>.
**
**
** Yampp-7/USB - low level support library
** File: mmc.asm - MultiMedia Card Support
**
*************************************************************************
**
** This file is part of the yampp system.
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License
** as published by the Free Software Foundation; either version 2
** of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program; if not, write to the Free Software Foundation,
** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*************************************************************************
**
** Revision History
**
** when what who why
**
** 2003-01-22 1.0 MIS initial public release
** 2003-07-14 1.1 MIS added watchdog resets to USB<->CARD functions
** 2003-09-13 1.2 MIS fixed card busy checking - speed up write about 60%
**
*********************************************************************** */
#define __ASSEMBLER__ 1
#define __SFR_OFFSET 0
#include <avr/io.h>
#define MMC_ASM
#include "mmc.h"
#define __tmp_reg__ r0
#define __zero_reg__ r1
#define USB_PORT PORTD
#define USB_TXRDY PD3
#define USB_RXRDY PD2
#define USB_ADDR 0x8000
.section .text
.extern vs1001_init_io
.extern usb_writeblock
;*******************************************************************************
;* MMC Interface stuff *
;*******************************************************************************
; 512 byte sector buffer in internal RAM
.global mmc_sbuf
.comm mmc_sbuf,512
.comm lba,3
.global mmc_response
.comm mmc_response,10
get_spi_byte:
ser r24
spi_tx_rx:
out SPDR, r24 ; send over SPI
vspi1: sbis SPSR,SPIF ; while flag is clear
rjmp vspi1 ; wait for SPI
in r24, SPDR ; and get input data
ret ; done
;-------------------------------------------------------------------------------
IncLBA: ; LBA[0..24]++;
lds r22,(lba+0)
lds r23,(lba+1)
lds r24,(lba+2)
sec
adc r22,__zero_reg__
adc r23,__zero_reg__
adc r24,__zero_reg__
ret
;-------------------------------------------------------------------------------
StoreLBA:
sts (lba+0),r22
sts (lba+1),r23
sts (lba+2),r24
ret
;-------------------------------------------------------------------------------
SendMmcCmd_0: ; r20 - cmd
clr r25 ; all parameters = 0
clr r23
clr r22
rjmp SendMmcCmd
;-------------------------------------------------------------------------------
DecodeLBA: ; LBA[0..24] << 9
lds r22,(lba+0)
lds r23,(lba+1)
lds r24,(lba+2)
lsl r22
rol r23
rol r24
mov r25,r24
SendMmcCmd: ; r22,r23,r25 - param, r20 - cmd
cbi MMC_PORT,MMC_CS ; activate chip select
mov r24,r20
ori r24,0x40 ; mmc comand
rcall spi_tx_rx
mov r24,r25 ; arg [24..31]
rcall spi_tx_rx
mov r24,r23 ; arg [16..23]
rcall spi_tx_rx
mov r24,r22 ; arg [8..15]
rcall spi_tx_rx
ldi r24,0 ; arg [0..7]
rcall spi_tx_rx
ldi r24,0x95 ; good checksum for cmd 64 0 0 0 0
rcall spi_tx_rx ; for others it's ignored
ldi r25,10
get_resp:
rcall get_spi_byte ; read card response
sbrs r24,7
rjmp cmdok
dec r25
brne get_resp
ser r24
cmdok: ret
;-------------------------------------------------------------------------------
;
; clear MMC cycle
;
FlushMMC_END: rcall FlushMMC
rjmp mmcFinish
FlushMMC: push r24 ; count in r20
FlushMMCloop: rcall get_spi_byte ; read byte from MMC
dec r20
brne FlushMMCloop
pop r24
ret
;-------------------------------------------------------------------------------
;
; wait for MMC 0xFE data token or error token
;
; ogranicznie do 255 bajtow
; bylo za male
; karta Kingston 128MB potrzebowala ok 270 bajtow i dopiero odpowiadala
Read_Tag: push r26
ldi r26, 64 ;; by ao
ldi r25, 0 ;; by ao
readTag1: dec r25
brne skip
dec r26 ;; by ao
breq rt1 ;; by ao
skip:
rcall get_spi_byte ; read byte from MMC
cpi r24,0xFE ; check for data token
breq rt1
andi r24,0xf1 ; check for data error token
cpi r24,0x01
brne readTag1 ;; by ao
;;brne Read_Tag ; wait for data or error
rt1:
pop r26
ret
;-------------------------------------------------------------------------------
;u08 MMC_Reset(void);
.global MMC_Reset
MMC_Reset:
sbi MMC_PORT, MMC_CS ; set CS high
sbi MMC_PORT-1, MMC_CS ; set CS as output
;; call vs1001_init_io
ldi r20,10
rcall FlushMMC ; 80 empty spi clocks
ldi r20,MMC_GO_IDLE_STATE
rcall SendMmcCmd_0 ; set card in idle state
rcall mmcFinish
andi r24,0x85
cpi r24,1
breq rstchk
RST_Error: clr r25
clr r24 ; return with NO CARD status
ret
rstchk: ldi r20,MMC_SEND_OP_COND
rcall SendMmcCmd_0 ; initialize operating conditions
rcall mmcFinish
tst r24
brne rstchk
ldi r20,MMC_SET_BLOCKLEN ; Set MMC block len
ldi r22,2 ; to 512 bytes
;;ldi r23,255
clr r23
clr r25
;;ldi r25,16
rcall SendMmcCmd ; execute command
rcall mmcFinish
tst r24
brne RST_Error
ser r24 ; Reset OK
clr r25
ret
;-------------------------------------------------------------------------------
;void MMC_Off(void);
.global MMC_Off
MMC_Off:
ldi r20,MMC_GO_IDLE_STATE
rcall SendMmcCmd_0 ; set card in idle state
rjmp mmcFinish
;-------------------------------------------------------------------------------
.global MMC_Read
; r25..r22
;u08 MMC_Read(u32 lba)
MMC_Read:
rcall StoreLBA
ldi r20,MMC_READ_SINGLE_BLOCK
rcall DecodeLBA ; decode LBA and execute command
tst r24
brne mmcFinish
rcall Read_Tag
ldi r31,hi8(mmc_sbuf)
ldi r30,lo8(mmc_sbuf)
clr r20
read_loop:
rcall get_spi_byte ; read byte from MMC
st Z+,r24
rcall get_spi_byte ; read byte from MMC
st Z+,r24
dec r20
brne read_loop
clr r24
ldi r20,2 ; drop CRC
rjmp FlushMMC_END
;-------------------------------------------------------------------------------
mmcFinish: ; deactivates MMC
push r24
sbi MMC_PORT,MMC_CS ; release chip select
rcall get_spi_byte ; one empty SPI cycle
pop r24
ret
;-------------------------------------------------------------------------------
.global MMC_Write
; r25..r22
;u08 MMC_Write(u32 lba)
MMC_Write:
rcall StoreLBA
MMC_Write1:
ldi r20,MMC_WRITE_BLOCK
rcall DecodeLBA ; decode LBA and execute command
tst r24
brne mmcFinish
rcall get_spi_byte ; one empty cycle
ldi r24,0xFE ; send tag
rcall spi_tx_rx
ldi r31,hi8(mmc_sbuf)
ldi r30,lo8(mmc_sbuf)
clr r20
write_loop:
ld r24,Z+
rcall spi_tx_rx ; write byte to MMC
ld r24,Z+
rcall spi_tx_rx ; write byte to MMC
dec r20
brne write_loop
USB_WR_HLP:
clr r24
rcall spi_tx_rx ; write CRC
clr r24
rcall spi_tx_rx ; write CRC
write_reply: rcall get_spi_byte
andi r24,0x1F
cpi r24,0x05
brne write_reply
wbusy: rcall delay100
rcall delay100 ; delay 200us
rcall get_spi_byte ; check for busy
inc r24
brne wbusy ; card is busy
clr r24
rjmp mmcFinish
;-------------------------------------------------------------------------------
; This procedure is maximally optimized for best speed, but is little longer.
; Sorry, speed is needed.
;
; r25..r22 r20
;u08 MMC_Write_USB( u32 lba, u08 numsectors);
;
.global MMC_Write_USB
MMC_Write_USB:
mov r21,r20 ; save numsectors
.UBW0:
rcall StoreLBA
wdr
ldi r20,MMC_WRITE_BLOCK
rcall DecodeLBA ; decode LBA and execute command
tst r24
brne mmcFinish ; return with error
rcall get_spi_byte ; one empty cycle
ldi r24,0xFE ; tag
out SPDR, r24
ldi r31,hi8(USB_ADDR)
ldi r30,lo8(USB_ADDR) ; Z points to USB
clr r20 ; loop count = 256 words
.getusb0: sbic USB_PORT-2,USB_RXRDY ; while flag is set
rjmp .getusb0 ; wait for bit to be cleared
ld r24,Z ; get byte from USB
.waitspi0: sbis SPSR,SPIF ; while flag is clear
rjmp .waitspi0 ; wait for SPI
; in r0,SPDR ; clear SPIF flag
out SPDR, r24 ; send over SPI
.getusb1: sbic USB_PORT-2,USB_RXRDY ; while flag is set
rjmp .getusb1 ; wait for bit to be cleared
ld r24,Z ; get byte from USB
.waitspi1: sbis SPSR,SPIF ; while flag is clear
rjmp .waitspi1 ; wait for SPI
; in r0,SPDR ; clear SPIF flag
out SPDR, r24 ; send over SPI
dec r20
brne .getusb0
.waitspi2: sbis SPSR,SPIF ; while flag is clear
rjmp .waitspi2 ; wait for SPI
; in r0,SPDR ; clear SPIF flag
rcall USB_WR_HLP ; write CRC and read ACK
dec r21 ; dec sector counter
breq .RTU1
rcall IncLBA
rjmp .UBW0
;-------------------------------------------------------------------------------
.global MMC_Read_USB
; r25..r22 r20
;u08 MMC_Read_USB( u32 lba, u08 numsectors);
;
MMC_Read_USB:
mov r21,r20 ; save numsectors
.RTU0:
wdr
rcall MMC_Read ; read from MMC to RAM
ldi r25,hi8(mmc_sbuf)
ldi r24,lo8(mmc_sbuf) ; Z points to buffer
ldi r23,2 ; 512 bytes length
clr r22
;; call usb_writeblock ; send buffer to USB
dec r21 ; dec sector counter
breq .RTU1
rcall IncLBA
rjmp .RTU0
.RTU1: clr r24 ; TODO! Return error values
clr r25
.RTU2: ret
;-------------------------------------------------------------------------------
.global MMC_Name
MMC_Name:
ldi r20,MMC_SEND_CID
rcall SendMmcCmd_0
rcall Read_Tag ; wait for data token
ldi r20,3 ; skip first 3 bytes of CID
rcall FlushMMC
ldi r31,hi8(mmc_sbuf)
ldi r30,lo8(mmc_sbuf) ; Z points to buffer
ldi r20,7 ; 7 bytes of name
.IN3:
rcall get_spi_byte
st Z+,r24
dec r20
brne .IN3
clr r24
ldi r20,8 ; flush last 6 bytes of CID
rjmp FlushMMC_END ; + 2 byte of CRC
;-------------------------------------------------------------------------------
;
;u32 MMC_Capacity(void)
.global MMC_Capacity
MMC_Capacity:
ldi r20,MMC_SEND_CSD
rcall SendMmcCmd_0
rcall Read_Tag ; wait for data token
ldi r20,6
rcall FlushMMC ; skip first 6 bytes of CSD
; get size into reg
;
; xxxx xxxx xxxx xxxx xxxx xxxx
; ^^ ^^^^ ^^^^ ^^
rcall get_spi_byte
andi r24,0x03
mov r23,r24 ; save two highest bits
rcall get_spi_byte
mov r22,r24 ; save middle bits
rcall get_spi_byte ; get lowest two bits
lsl r24
rol r22
rol r23
lsl r24
rol r22
rol r23 ; shift the result by two bits left
; now size is in r23:r22
; get multiplier
;
; xxxx xxxx xxxx xxxx
; ^^ ^
rcall get_spi_byte ; get byte
andi r24,0x03
mov r20,r24 ; save it
rcall get_spi_byte ; get byte
lsl r24
rol r20 ; multiplier in r20
clr r24
sec
adc r22,__zero_reg__ ; sectors = (size+1)<<(multiplier+2)
adc r23,__zero_reg__
adc r24,__zero_reg__
clr r25
inc r20
inc r20 ; multiplier+2
sh_loop:
lsl r22
rol r23
rol r24
rol r25
dec r20
brne sh_loop
; size in r25..r22
ldi r20,7 ; flush last 5 bytes of CSD
rjmp FlushMMC_END ; + 2 byte of CRC
;-------------------------------------------------------------------------------
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -