please dont rip this site

Scenix Contest Mmc SX52MMC.SRC

; ******************************************************************************
;	(C) Copyright 1997-2000 Scenix Semiconductor
;       Length: 476 bytes (total)
;       Authors: Craig Webb, Andrian Kouznetsov
;       Written: 97/03/10 to 00/10/30
;       This program implements six virtual peripherals on Scenix's SX28/52
;       DEMO board for demonstrating MultiMedia Card (MMC) external memory
;	storage playing its contents as a .WAV file through the audio plug-in PCB.
;	The MMC is accessed through a high level command interpreter subroutine
;	peripheral that controls the MMC by way of an SPI subroutine interface
;	peripheral. Data coming in from the MMC is stored in a circular buffer
;	as it awaits output through the audio harware. The program uses timer
;	clock virtual peripheral in conjunction with a pulse width modulation
;	(PWM) virtual peripheral for output of the stored MMC data as streamed
;	audio samples, and also uses a sigma-delta A/D virtual peripheral to
;	sample external signals that can then be written to the MMC.
;	1) MMC command interpreter interface
;	2) SPI interface subroutine(s)
;	3) Circular storage buffer
;	4) Timer routine
;       5) Pulse-Width Modulated (PWM) outputs (2)
;       6) Adjustable 8-15-bit Analog-to-Digital Converter (ADC) (1)
;       These latter four of these virtual peripherals take advantage
;       of the SX's internal RTCC-driven interrupt and operate
;       in the background while the main program loop is executing.
; Program Notes:
;	-Be careful that the SX SPI port pins are initialized when inserting or
;	  removing the MMC, oherwise peculiar card states can result (in which
;	  case the MMC should be completely removed and powered off, and then
;	  the SYNCHRONIZATION and INITIALIZATION routines must be run again).
;	-if the program does encounter any MMC communication error, it is best
;	  to repeat the INITIALIZATION routine, and occasionally the
;	  SYNCHRONIZATION routine also.
;******************************* Program Variables ***************************
;****** Assembly Options
pcb_revision	=	0	;0 or 1		;PCB revision 1.x
compiler	=	0	;0 or 1		;0=SX-Key, 1=SXIDE
stereo		=	0	;0 or 1		;set =1 for stereo PCM data
chip_type	=	1	;0 or 1		;0=SX18/28, 1=SX52
include_adc	=	0	;0 or 1		;0=adc routine not included
;****** Adjustable Paramaters
sample_freq	=	11025	;6512-44100	;sampling rate of .wav file
MMC_size	=	4	;1-128		;storage size of MMC card
resolution	=	8	;8-15		;adc resolution (8-15 bits)
pwm0_init	=	80h	;0-FFh		;initial pwm0 voltage
pwm1_init	=	80h	;0-FFh		;initial pwm1 voltage
;****** Program Variables
int_period	=	40+(include_adc*20)+6	;minimum + main routine allowance	
sx_freq_factor	=	1			;this is a time delay scaling factor,
						; for SX running at 50MHz(=1), 100MHz (=2)
;****** Program Constants & Dependent Variables
;sample_freq= $380 @ 11,025Hz mono, with xtal=50MHz, with adc on
samp_freq	=	$380*int_period/66*sample_freq/11025 ;sets the audio PCM sample rate
		IF stereo=1
samp_freq	=	samp_freq*2		;stereo needs twice the sample output rate
		IF MMC_size<16
end_of_mem	=	MMC_size<<4-3		;last bank of mmc memory blocks
		ELSE					; for MMC cards >16Meg
end_of_mem	=	MMC_size>>4-3		;last bank of mmc memory blocks
end_of_mem	=	$37			; or end of audio sample (comment this if needed)			

		IF compiler=0				;assembler directive (for SX-Key)
		 freq	50_000_000*sx_freq_factor	;default clock rate: 50 MHz
;***SPIX timing delays:
;spix_rate	=	(1 + (3 * ( sx_freq_factor - 1)))
spix_rate	=	sx_freq_factor
					;rate factor for SPIX.MMC
					; (may need to be trimmed for different SX clock rates)
sync_duration	=	10 			;?-255	;MMC SYNCHRONIZATION function duration
cmd1_resp_wait	=	$80 * sx_freq_factor	;?-128	;initialization (CMD1) response wait duration
cmd1_delay1	=	$8  ;* sx_freq_factor	;?-128	;initialization (CMD1) internal loop delay
cmd1_delay2	=	$10 ;* sx_freq_factor	;?-128	;initialization (CMD1) internal loop delay
cmd1_delay3	=	$80 * sx_freq_factor	;?-128	;initialization (CMD1) internal loop delay
resp_r1_delay	=	32 * sx_freq_factor	;2-255	;delay for a RESPONSE from MMC
blk_rd_delay1	=	1 * sx_freq_factor	;?-255	;delay for MMC initial block read preparation		"	   written
blk_rd_wait	=	20 * sx_freq_factor	;?-255	;read wait time MMC incoming data bytes
blk_wr_delay	=	20 * sx_freq_factor	;?-255	;delay for MMC initial block write preparation
option_init	=	%10001000	;bits: 1=RTCC@01h|0=int_en|0=RTCC_internal|x|psa(1=WDT)|ps(2-0)	
resolution	=	resolution-8		;8-15	;adc resolution (8-15 bits)
mmc_blk_size	=	512		;block size for reading/writing MMC (in SPI mode)
mmc_buffsize	=	32		;MMC read/write buffer size: must be =(2^n)*16, n=0,1,2(,3 for SX52)
					; (only 16 and 32 have been tested)

;****** Assembler Directives
;		DEVICE  turbo
		ID      'MMCAudio'		;program ID label
		RESET   reset_entry             ;set reset/boot address
;******************* Configuration DATA and I/O pins *************************
;******	Port definitions
RA_IO		=	%0010			;port A I/O directions	
RA_init		=	%1111			;port A initial output states	
;Pin assingment for SPI_X interface
spix_clk_pin	EQU	RA.3			;SPI clock output
spix_out_pin	EQU	RA.2			;SPI-Master-Out-Slave-In
spix_in_pin	EQU	RA.1			;SPI-Master-In-Slave-Out
spix_cs_pin	EQU	RA.0			;SPI device select
RB_IO		EQU	%10111111		;port B I/O directions	
RB_init		EQU	%11111111		;port B initial output states	
analog_port1	EQU	RB	
LEDs		EQU	RB			;individual LED outputs
await_button	EQU	LEDs.3			;await button signal
init_err	EQU	LEDs.2			;signals initialize MMC error
read_err	EQU	LEDs.1			;signals read from MMC error
write_err	EQU	LEDs.0			;signals write to MMC error
	  IF pcb_revision=0
RC_IO		EQU	%11110110		;port C I/O directions	
RC_init		EQU	%11111111		;port C initial output states	
analog_port2	EQU	RC	
;note: make duplicate changes in "analog_buff"
pwm0_pin        EQU     RB.6                    ;Pulse width mod. PWM0 output
pwm1_pin        EQU     RC.3                    ;Pulse width mod. PWM1 output
adc0_in_pin	EQU     RC.2                    ;ADC0 input pin
adc0_out_pin	EQU     RC.0                    ;ADC0 output/calibrate pin
	  ELSE	;pcb_revision=1.1
RC_IO		EQU	%11111010		;port C I/O directions	
RC_init		EQU	%11111111		;port C initial output states	
analog_port2	EQU	RC	
;note: make duplicate changes in "analog_buff"
pwm0_pin        EQU     RB.6                    ;Pulse width mod. PWM0 output
pwm1_pin        EQU     RC.2                    ;Pulse width mod. PWM1 output
adc0_in_pin	EQU     RC.1                    ;ADC0 input pin
adc0_out_pin	EQU     RC.0                    ;ADC0 output/calibrate pin
	  ENDIF	;revision
;******************************** MMC_SPIX RAM ****************************

		ORG	$0E			;global ram
next_samp_ptr	DS	1			;points to next sample in buffer	
mmc_temp_data	DS	1			;temporary storage to move the data
						; between the SX RAM banks
;SPIX and MMC make use of the same RAM bank			
mmc_spix_ram	ORG	$10			;MMC/SPI control register bank
mmc_status	ds	1			;MMC driver status byte
spix_busy	equ	mmc_status.7		;Busy bit set by SPIX VP to HIGH when byte
						;transfer (SPI) is in progress
mmc_busy	equ	mmc_status.6		;set when the MMC is in BUSY state. Used in WRITE MODE

mmc_read_write_data equ	mmc_status.5		;indicates that BLOCK READ/WRITE cycle is in progress

mmc_error	equ	mmc_status.4		;any error in data transfer
						;MMC_R1 and MMC_R2 contain the error message
mmc_no_response	equ	mmc_status.3		;no response R1 was received
						;within required time
mmc_data_error	equ	mmc_status.2		;data error token received
						;during Block Read/Write command
mmc_no_data	equ	mmc_status.1		;no data block received from MMC

mmc_wrong_command equ	mmc_status.0		;Set by the commnds decoder, indicates not supported command		
mmc_cmd			ds	1		;mmc command - 6 bytes
mmc_addr_b3		ds	1		
mmc_addr_b2		ds	1
mmc_addr_b1		ds	1
mmc_addr_b0		ds	1
mmc_cmd_crc		ds	1
mmc_r1			ds	1		;mmc response - 2bytes
mmc_r2			ds	1
mmc_temp		ds	1		;temporary storage for MMC driver
mmc_temp1		ds	1
mmc_temp2		ds	1
mmc_data_pointer	ds	1		;pointer to the current address
						;of the SX data RAM
;SPIX data
spix_data_io		ds	1		;one-byte I/O data buffer/shift register
spix_temp		ds	1		;temporary storage
spix_shift_counter	ds	1		;I/O shift counter

		org     30h                     ;bank4 variables
analog          =       $                       ;pwm and ADC bank & timer	
analog_buff1	DS	1			;analog output buffer
analog_buff2	DS	1			;analog output buffer
	IF pcb_revision=0
pwm0_out	EQU	analog_buff1.6		;pwm0 buffer
pwm1_out	EQU	analog_buff2.3		;pwm1 buffer
adc0_out	EQU	analog_buff2.0		;adc0 out buffer
pwm0_out	EQU	analog_buff1.6		;pwm0 buffer
pwm1_out	EQU	analog_buff2.2		;pwm1 buffer
adc0_out	EQU	analog_buff2.0		;adc0 out buffer
pwm0            DS      1                       ;pwm0 - value
pwm0_acc        DS      1                       ;     - accumulator
pwm1            DS      1                       ;pwm1 - value
pwm1_acc        DS      1                       ;     - accumulator
adc0_lo		DS      1                       ;adc0 - value low byte
adc0_hi		DS	1			;         "   high byte
adc0_count_lo	DS      1                       ;     - real-time count (lo)
adc0_count_hi	DS	1			;     -  "    "     "   (hi)
adc0_acc_lo	DS      1                       ;     - accumulator (lo)
adc0_acc_hi	DS	1			;     -     "	    (hi)
freq_low        ds      1                       ;frequency value low byte
freq_high       ds      1                       ;frequency value high byte
freq_accl       ds      1                       ;frequency accumulator low byte
freq_acch       ds      1                       ;frequency accumulator high byte
mmc_data_ram	ORG	$70		;MMC read/write data buffer(length of buffer=mmc_buffsize)
					; (e.g. in the case of 32 bytes it will occupy banks $50 & $70)
;**************************** INTERRUPT CODE *******************************
		ORG	0
;****** Virtual Peripheral: TIMER/FREQUENCY OUTPUT
; This routine adds a programmable value to a 16-bit accumulator (a pair of
;  two 8-bit registers) during each pass through the interrupt. It then
;  copies the value from the high bit of the accumulator to the
;  appropriate output port pin (LED, speaker, etc.)
;       Input variable(s) : timer_low,timer_high,timer_accl,timer_acch
;       		    freq_low,freq_high,freq_accl,freq_acch
;       Output variable(s) : LED port pin, speaker port pin
;       Variable(s) affected : timer_accl, timer_acch, freq_accl, freq_acch
;       Flag(s) affected : none
;	Size : 1 byte + 10 bytes (per timer)
;       Timing (turbo) : 1 cycle + 10 cycles (per timer)
		bank    analog                  ;switch to timer (& adc/pwm) bank
;               clc                             ;only needed if CARRYX=ON
		add     freq_accl,freq_low      ;adjust freq's accumulator
		CLR	W			;zero W reg.
		SNC				;did we get an overflow in low byte?
		MOV	W,#1			;if so, prepare to adjust high byte
		ADD	W,freq_high		; (freq = 16 bits long)
		SZ				;if overflow for adjustment, skip ahead
		ADD	freq_acch,W		;adjust high byte of accumulator 
		SC				;skip ahead if timer triggered
		JMP	:done			;if not, jump ahead to end

:circ_buffer	MOV	W,next_samp_ptr		;load sample pointer
		MOV	FSR,W			; for indirect addressing
		MOV	W,ind			;load next sample
		BANK	analog			;reset bank to analog
		CLRB	FSR.7			; and make sure we're in lower 128reg. block
		IF stereo=0
		 MOV	pwm0,W			;convert it to audio	
		 MOV	pwm1,W			;   "	
		 SB	next_samp_ptr.0		;is this left channel?
		 MOV	pwm0,W			;yes, send out out left
		 SNB	next_samp_ptr.0		;otherwise, if it's right channel
		 MOV	pwm1,W			; send it out right
		INC	next_samp_ptr		;advance pointer
		IF chip_type=0				;SX18/28
		 MOV	W,#buff_size*2+mmc_data_ram	;last address
		ELSE					;SX52
	 	 MOV	W,#mmc_data_ram+mmc_buffsize	;last address
		MOV	W,next_samp_ptr-W		;test for end of buffer
		MOV	W,#mmc_data_ram			;pre-load new offset in case
		SNZ					;if not at end, skip ahead
		MOV	next_samp_ptr,W			;reset to start of buffer
		IF	chip_type=0
		 MOV	W,#$10			;keep to odd banks for SX18/28
		 OR	next_sample_ptr,W	;  "
;***** Virtual Peripheral: Pulse Width Modulators
; These routines create an 8-bit programmable duty cycle output at the
; respective pwm port output pins whose duty cycle is directly proportional
; to the value in the corresponding pwm register. This value is added to an
; accumulator on each interrupt pass interrupt. When the addition causes a
; carry overflow, the ouput is set to the high part of its duty cycle.
; These routines are timing critical and must be placed before any
; variable-execution-rate code (like the UART, for example).
;       Input variable(s) : pwm0,pwm0_acc,pwm1,pwm1_acc
;       Output variable(s) : pwm port pins
;       Variable(s) affected : port_buff, pwm0_acc, pwm1_acc
;       Flag(s) affected : none
;	Size : 3 bytes + 4 bytes (per pwm)
;		+ 4 bytes shared with adc code (see below)
;       Timing (turbo) : 3 cycles + 4 cycles (per pwm)
;			 + 4 cycles shared with adc code (see below)
;:set_analog	bank    analog                  ;switch to adc/pwm bank
		clr	analog_buff1		;zero pwm output buffer
		clr	analog_buff2		;zero pwm output buffer
:pwm0           add     pwm0_acc,pwm0           ;adjust pwm0 accumulator
		snc                             ;did it trigger?
		setb    pwm0_out		;yes, toggle pwm0 high
:pwm1		add     pwm1_acc,pwm1           ;adjust pwm1 accumulator
		snc                             ;did it trigger?
		setb	pwm1_out		;yes, toggle pwm1 high
;:update_analog	MOV	analog_port1,analog_buff1 ;update cap. charge/discharge pins
;		MOV	analog_port2,analog_buff2 ;update cap. charge/discharge pins
		IF include_adc=1		;is this to be assembled in?

;***** Virtual Peripheral: Bitstream Analog to Digital Converters
; These routines allow an 8-bit value to be calculated which corresponds
; directly (within noise variation limits) with the voltage (0-5V) present
; at the respective adc port input pins. These routines are timing critical
; and must be placed before any variable-execution-rate code (like the UART,
; for example). The currently enabled routine (version A) has been optimized
; for size and speed, and RAM register usage, however a fixed execution rate,
; yet slightly larger/slower routine (version B) is provided in commented
; (disabled) form to simplify building other timing-critical virtual
; peripheral combinations (i.e. that require fixed rate preceeding code).
;    Note: if version B is selected, version A must be disabled (commented)
;       Input variable(s) : adc0_in_pin,adc0_acc_lo/hi,adc0_count_lo/hi
;       Output variable(s) : analog_port1,analog_port2,analog_buff1,analog_buff2
;       Variable(s) affected : adc0_out,adc0_acc_lo/hi,adc0_count_lo/hi
;       Flag(s) affected : none
;	Size :	20 + 4 bytes shared with pwm code (see above)
;       Timing : 5 cycle shared with pwm code (see above) +
;		   (a) [>99.5% of time] 15 cycles
;		   (b) [<0.5% of time] 20 cycles
;*** If the PWM routines are removed, the following 2 instructions must
;*** be enabled (uncommented) for the ADC routine to function properly:
;		bank    analog                  ;switch to adc/pwm bank

:adc0           SB	adc0_in_pin		;get current status of adc0
		SETB	adc0_out		;complement input to output
		SNB	adc0_in_pin		; "
		CLRB	adc0_out		; "
		SB	adc0_in_pin		;check if adc0 triggered?
		INCSZ   adc0_acc_lo		;if so, incr. 16-bit accumulator
		DEC     adc0_acc_hi             ; adjusting high byte as necessary
		INC	adc0_acc_hi		; but making sure not to

:adj_count	INCSZ	adc0_count_lo		;adjust 16-bit counter
		DEC	adc0_count_hi		; by skipping this instr.
		INC	adc0_count_hi		; when low byte overflows

:check_trig	SB	adc0_count_hi.resolution ;sample ready?
		JMP	:done_adcs		;if not, exit adc routine

:adc_ready	MOV	adc0_lo,adc0_acc_lo	;yes, copy new value
		MOV	adc0_hi,adc0_acc_hi	; into adc0 registers
		CLR	adc0_count_hi		;clear count (low already=0)		
		CLR	adc0_acc_lo		;clear accumulator (low)		
		CLR	adc0_acc_hi		;clear accumulator (high)		

		ENDIF				;whether to include adc or not

:update_analog	MOV	analog_port1,analog_buff1 ;update cap. charge/discharge pins
		MOV	analog_port2,analog_buff2 ;update cap. charge/discharge pins

		MOV     W,#-int_period          ;interrupt every 'int_period' clocks
		RETIW				;exit interrupt
;******	End of interrupt sequence
;***************************** PROGRAM DATA ********************************
;****************************** SUBROUTINES ********************************
Sine_Table	JMP	PC+W			;Lookup sine value (32 bytes)
		RETW	128,153,174,199,219,234,246,254,255,254,246,234,219,199,174,153
		RETW	128,103,79,57,37,22,10,2,0,2,10,22,37,57,79,103
;Sine_Table_16	RETW	0,9,37,79,128,176,218,246,255,246,218,176,128,79,37,9
;*** 8-BIT SPI Master VP - SPIX

;***Send the byte
spix_send		bank	mmc_spix_ram			;select SPIX RAM bank			
			setb	spix_busy			;set the bit indicating that 
								;SPIX started data transfer
			mov	spix_shift_counter,#$08		;set number of shifts
spix_send_loop							;CLK_HIGH
			clrb	spix_clk_pin			;set CLK_LOW

			movb	spix_out_pin,spix_data_io.7	;shift data out. CLK_LOW
			rl	spix_data_io			;CLK_LOW
			mov	spix_temp,#spix_rate		;transfer rate delay
spix_loop2		djnz	spix_temp,spix_loop2		;CLK_LOW

			setb	spix_clk_pin			;set CLK - HIGH
			mov	spix_temp,#spix_rate		;transfer rate delay
spix_loop3		djnz	spix_temp,spix_loop3		;CLK- HIGH

			djnz	spix_shift_counter,spix_send_loop	;check the bit counter
			clrb	spix_busy			;exit if done
			setb	spix_out_pin		
;get the byte
spix_get		bank	mmc_spix_ram			;select SPIX RAM bank			
			setb	spix_busy			;prepare to start
			mov	spix_data_io,#$ff		;preset all bits high
			STC					;including carry bit
			mov	spix_shift_counter,#$08		;set number of shifts
spix_get_loop							;CLK_High
			clrb	spix_clk_pin			;set CLK_LOW
			rl	spix_data_io			;CLK_LOW
			IF sx_freq_factor>1			;100 MHZ?
			 mov	spix_temp,#spix_rate		;transfer rate delay
spix_loop6		 djnz	spix_temp,spix_loop6		; while CLK_LOW
			SB	spix_in_pin			;if new bit=1, then do nothing
			CLRB	spix_data_io.0			; otherwise, clear it accordingly
			setb	spix_clk_pin			;set CLK_HIGH

			IF sx_freq_factor>1			;100 MHZ?
			mov	spix_temp,#spix_rate		;transfer rate delay
spix_loop8		djnz	spix_temp,spix_loop8		;while CLK_High
			djnz	spix_shift_counter,spix_get_loop;check the bit counter
			setb	spix_out_pin			;exit if done
			clrb	spix_busy
;End of SPIX VP
;*** SPIX port initialization
spix_init	setb	spix_clk_pin
		setb	spix_out_pin
		setb	spix_cs_pin

;*** Set MMC data address = 0
zero_MMC_addr	CLR	mmc_addr_b3			;set the block address to read (hi byte first)
		CLR	mmc_addr_b2			; "
		CLR	mmc_addr_b1			; "
		CLR	mmc_addr_b0			; (lo byte last: always = 0 since 512 bytes/block)
;***********************  MMC COMMAND INTERPRETER **********************
;FUNCTIONS are not MMC card commands, but sequences of the commands. 
;The MMC VP currently implements four separate FUNCTIONS
; - synchronize
; - initialize
; - read the data block
; - write the data block
mmc_synchronize		equ	$FF		;send 80 SPI_clk signals for initial synchronization
mmc_initialize		equ	$FE		;card initialization procedure
						; flow chart (exept for synchronization)
mmc_block_read_command	equ	$FD		;Complete implementation of the block read flow chart
						;Send the Read Block Command and set the
						;mmc_read_write_data flag in the MMC_STATUS byte
						;The MMC /CS line is left active (LOW) until the data 
						;are read. Get the data block from MMC and 
						;finishes the read block cycle by setting MMC /CS 
mmc_block_write_command	equ	$FA		;complete implementation of the block writing
;Implemented MMC commands are used as FUNCTION calls
;Other commands listed here (but currently unimplemented) are presented
; mainly for reference. These commands are described in detail in the MMC manual.

mmc_go_idle_state	equ	$40		;CMD0	;these three command are used as a part
mmc_send_op_cond	equ	$41		;CMD1	;of INITIALIZE function
mmc_set_blocklen	equ	$50		;CMD16

mmc_read_single_block	equ	$51		;CMD17
mmc_write_block		equ	$58		;CMD24 

;MMC commands currently NOT implemented:
;mmc_send_status	equ	$4d		;CMD13 assuming that status data come in the
						;R2 bytes
;mmc_send_csd		equ	$49		;CMD9 
;mmc_send_cid		equ	$4a		;CMD10 

;mmc_program_csd	equ	$5b		;CMD27 
;mmc_set_write_prot	equ	$5c		;CMD28 
;mmc_clr_write_prot	equ	$5d		;CMD29 
;mmc_send_write_prot	equ	$5e		;CMD30 
;mmc_tag_sector_start	equ	$60		;CMD32 
;mmc_tag_sector_end	equ	$61		;CMD33 
;mmc_untag_sector	equ	$62		;CMD34 
;mmc_tag_erase_group_start equ	$63		;CMD35 
;mmc_tag_erase_group_end equ	$64		;CMD36 
;mmc_untag_erase_group	equ	$65		;CMD37 
;mmc_erase		eque	$66		;CMD38 
;mmc_crc_on		equ	$67		;CMD59 

;******************** MMC Subroutines *********************************
;*** MMC Wait
;Sometimes a delay is needed for internal MMC processes
mmc_long_wait	MOV	W,#$FF			;longest delay
mmc_wait	MOV	mmc_temp1,W		;selectable delay (loaded in W prior to call)
mloop49		mov	mmc_temp,#$FF		;set wait cycles counter			
mloop69		djnz	mmc_temp,mloop69	;inner delay loop
		djnz	mmc_temp1,mloop49	;outer delay loop
		ret				;exit

;*** MMC Send "DUMMY" Bytes
;This subroutine generates a number of DUMMY byte transfer cycles on the SPI bus.
;The number of cycles should be specified in MMC_TEMP prior to the call.
;These dummy cycles are required by the MMC card data transfer protocol.
mmc_delay		bank	mmc_spix_ram
			call	spix_get
			djnz	mmc_temp,mmc_delay

;*** MMC Send Command
;This Subroutine sends a 6 byte MMC command string starting with the MMC_CMD byte
;and finishing by CRC byte
mmc_cmd_send		bank	mmc_spix_ram	;select SPIX RAM bank
			mov	mmc_temp,#mmc_cmd
mmc_loop2		mov	fsr,mmc_temp
			mov	w,ind		;load next COMMAND byte to
						;the SPIX I/O buffer using index addressing mode
			mov	spix_data_io,w
			call	spix_send	;send the byte
			inc	mmc_temp
			cjne	mmc_temp,#(mmc_cmd + 6),mmc_loop2
			setb	spix_out_pin	;loop until all six bytes sent
;*** MMC Get Response R1
;Get the response byte from the MMC. Wait for the response R1 for x=mmc_r1_wait_cycles
;byte cycles. If the R1 is not recieved or R1 is not $00, set the error flag.
;Number of wait cycles is recommended to be 2, but here we use 32 for safety.
;The routine can return two errors: (1) no response at all, (2) non zero response 
mmc_r1_get		bank	mmc_spix_ram			;select the RAM bank
			mov	mmc_r1,#$ff			;initialize the byte to FF
			mov	mmc_temp,#resp_r1_delay		;set delay counter			
mmc_loop3		call 	spix_get			;read the byte
			cjne	spix_data_io,#$FF,mmc_loop4	;if the byte is "FF" then repeate
								;mmc_r1_wait_cycles times
			djnz	mmc_temp,mmc_loop3
			setb	mmc_no_response			;no response

mmc_loop4		test	spix_data_io			;test if not zero
			setb	mmc_error			;set an error flag	
			mov	mmc_r1,spix_data_io

;******************** MMC Command Function Interpreter *******************
;This is the MMC command decoder subroutine. This is an optional structure
;The actual commands can be executed separately. The use of this structure
;is to reduce the number of subroutines (i.e. CALLs) and replace them by JMPs
mmc_execute		bank	mmc_spix_ram
			cje	mmc_cmd,#mmc_block_read_command,mmc_cmd_block_read
			cje	mmc_cmd,#mmc_block_write_command,mmc_cmd_block_write
			cje	mmc_cmd,#mmc_synchronize,mmc_cmd_synchronize
			cje	mmc_cmd,#mmc_initialize,mmc_cmd_initialize
			setb	mmc_wrong_command
mmc_exit		ret

;****************************** MMC Functions ****************************

;*** Read Data Block (Function $51)
;upon the reception of this command the MMC will start to access the data block.
;the data will be ready within 1.5 msec
			bank	mmc_spix_ram		;Select the RAM Bank
			clrb	spix_cs_pin		;set MMC_CS active (LOW)
;			mov	mmc_temp,#blk_rd_delay1	;do a DUMMY read cycle delay
;			call	mmc_delay		;
			clr	mmc_status
:do_read		mov	mmc_cmd,#$51		;send the BLOCK READ Command
			call	mmc_cmd_send		
			call	mmc_r1_get
			test	mmc_r1			;MMC response is not Zero, exit	
			jnz	mmc_read_exit	

;******************* Continue Read Cycle, Read The Data *********************
;Wait for the Start Byte.
;Wait time is set to (mmc_read_wait_cycles * $FF).  Total wait time sould be 1.5ms+.

mmc_read_data_block	bank	mmc_spix_ram		;select the RAM bank
			setb	mmc_read_write_data

;await data start byte = $FE
			mov	mmc_temp1,#blk_rd_wait	 	;load delay value	
mmc_label40		mov	mmc_temp,#$FF			;set wait counter			
mmc_label41		call 	spix_get			;wait for data start byte = $FE
			cje	spix_data_io,#$fe,mmc_label42	;End waiting if Start Byte
			cjne	spix_data_io,#$ff,mmc_label44	;End waiting if Error Message
			djnz	mmc_temp,mmc_label41		;inner wait loop
			djnz	mmc_temp1,mmc_label40		;outer wait loop

			setb	mmc_no_data			;waiting time expiered - error
			jmp	mmc_read_exit			;exit

mmc_label44		mov	mmc_r2,spix_data_io		;Error message is in R2
			setb	mmc_data_error			;MMC sent the Data Error Token
			jmp	mmc_read_exit			;the token will be placed in MMC_R2
;Read MMC Data (if no errors occurred): 
;This routine should be used as an example only. It transfers a block of data from MMC card
;placing the data bytes in the data storage buffer. Since the buffer is smaller than the MMC
;block size (i.e. data not being streamed), the 512 bytes coming from the MMC will
;repeatedly overwrite the buffer locations with the data steaming from MMC card.
;At the end of the cycle the last 16 (32) bytes of 512 MMC data block will be located
;in the SX data buffer

mmc_label42		mov	mmc_temp2,#mmc_blk_size/mmc_buffsize
								;set number of cycles 
mmc_label43		mov	mmc_temp,#mmc_buffsize		;set the byte counter and 
			IF chip_type=0				;SX18/28
			 MOV	W,#buff_size*2+mmc_data_ram-1	;last address
			ELSE					;SX52
			 MOV	W,#mmc_data_ram+mmc_buffsize-1	;last address
			mov	mmc_data_pointer,W		;data pointer for incoming data

:mmc_read_byte		call	spix_get			;data read loop

;store incoming bytes into the data (circular) buffer
			INC	mmc_data_pointer
			IF chip_type=0				;SX18/28
			 MOV	W,#buff_size*2+mmc_data_ram	;last address
			ELSE					;SX52
		 	 MOV	W,#mmc_data_ram+mmc_buffsize	;last address
			MOV	W,mmc_data_pointer-W		;test for end of buffer
			MOV	W,#mmc_data_ram			;pre-load new offset in case
			SNZ					;if not at end, skip ahead
			MOV	mmc_data_pointer,W			;reset to start of buffer
			IF	chip_type=0
			 MOV	W,#$10				;keep to odd banks for SX18/28
			 OR	mmc_data_pointer,W		;  "
:wait_buff_space	MOV	W,next_samp_ptr			;load output pointer
			MOV	W,mmc_data_pointer-W		;and check if buffer full
			SNZ					;if not, skip ahead
			JMP	:wait_buff_space		;wait till there's room

			mov	mmc_temp_data,spix_data_io	;move data to global storage	
			mov	fsr,mmc_data_pointer		;use index register and W
			mov	w,mmc_temp_data			;to move the date from SPIX temp data reg.
			mov	ind,w				; to MMC data buffer

			BANK	mmc_spix_ram			;reset to variables bank
			CLRB	FSR.7				; and make sure we're in lower 128reg. block
			djnz	mmc_temp,:mmc_read_byte		;decrement byte counter
			djnz	mmc_temp2,mmc_label43		;decrement block counter
			call	spix_get			;"read" two bytes CRC
			call	spix_get			;the CRC is not implemented and thus the data are not used

mmc_read_exit		call 	spix_get			;DUMMY read cycle
;			call 	spix_get			;DUMMY read cycle
			clrb	mmc_read_write_data		;end the transfer and exit
			setb	spix_cs_pin			;set MMC to inactive state
			jmp	mmc_exit			;done
;******************* Send CMD24 ($58) (WRITE BLOCK) to MMC  *********************
;this is the first part of the BLOCK WRITE sequence - the command itself and the R1
;response from the MMC
mmc_cmd_block_write	bank	mmc_spix_ram		;select the RAM bank
			clr	mmc_status		;clear the Status byte
			clrb	spix_cs_pin		;set MMC_CS active (LOW)

;execute CMD16
:set_block_length	mov	mmc_temp,#$10		;fixed delay
			call	mmc_delay
			call	spix_get		;"DUMMY" byte
			mov	mmc_cmd,#$58		;send the command
			call	mmc_cmd_send
			call	mmc_r1_get		;get the response
			call	spix_get		;"DUMMY" byte
			test	mmc_r1			;if response was 00 then start
			jz	mmc_write_data_block
			jmp	mmc_write_exit		;end the transfer and exit

;*** WRITE CYCLE: Write the data block to MMC
;Send the Start Byte=$FE, wait for response (R1 type) 
;Wait time= (mmc_write_wait_cycles * $FF).  Total wait time should be =>1.5ms
mmc_write_data_block	call	spix_get
			mov	spix_data_io,#$fe	;send the start byte $FE
			call	spix_send

;send the 512 data bytes the from the SX write data buffer. The buffer can be 16 or 32 bytes
;In a real wold when the buffer is empty, the program should wait untill the MAIN fills the buffer 
;and returns to the black transfer again
;This program just sends the incrementing counter to the the MMC card
;It is intended to demonstrate the timing and the logic of the data transfer
;This part of the program heavily depends on the higher level program artichecture 

;			clr	mmc_temp_data			;This is temp location for the Demo
			mov	mmc_temp1,#mmc_blk_size/mmc_buffsize
mmc_label38		mov	mmc_temp,#mmc_buffsize		;set the byte counter and 
			mov	mmc_data_pointer,#mmc_data_ram	;data pointer for the data
;this part of the program was tested and it works, but is not used for this demo
;			mov	fsr,mmc_data_pointer		;move the byte from MMC_DATA_RAM 
;			mov	w,ind				;to MMC_SPIX_RAM using global
;			mov	mmc_temp_data,w			;location at $08 - MMC_TEMP_DATA
;			bank	mmc_spix_ram			
;			mov	spix_data_io,mmc_temp_data		

;this is just for the Demo - write incrementing values to MMC offset addresses
			bank	mmc_spix_ram			;switch to spix control ram bank
			mov	spix_data_io,mmc_temp_data	;Increment the counter and sent it to MMC card		
			INC	mmc_temp_data			;write increading values: DEMO only
			call	spix_send			;send the byte			
			inc	mmc_data_pointer		;point to next data address
			or	mmc_data_pointer,#$10		;keep bank address in %xxx1000 range
			djnz	mmc_temp,mmc_label39		;decrement byte counter
			djnz	mmc_temp1,mmc_label38		;decrement SX RAM blocks counter
			call	spix_get			;read the two byte "CRC"
			call	spix_get			; "											
			call	mmc_r1_get			;get the response on the data block
			call	spix_get			;the R1 in this case is not a typical one
			clrb	mmc_error			;error detection in MMC_GET_R1 is not
			and	mmc_r1,#$0f			;applicable
			cje	mmc_r1,#$05,mmc_label37		
			jmp	mmc_write_error			;error in the data - exit

;continue BLOCK WRITE dummy read cycles until MMC's BUSY signal is clear
mmc_label37		setb	mmc_busy			;set wait cycles counter
			mov	mmc_temp1,#blk_wr_delay	
mmc_label36		mov	mmc_temp,#$FF
mmc_label35		call	spix_get					
			jb	spix_in_pin,mmc_write_done	;wait for the BUSY signal to clear
			djnz	mmc_temp,mmc_label35
			djnz	mmc_temp1,mmc_label36
			jmp	mmc_write_error			;error exit - time expired
mmc_write_done		clr	mmc_status			;no-error exit
			jmp	mmc_write_exit
mmc_write_error		setb	mmc_error			;exit on error
			setb	mmc_data_error
mmc_write_exit		call	spix_get			;DUMMY read cycle
			clrb	mmc_read_write_data
			setb	spix_cs_pin
			jmp	mmc_exit
;*** MMC Synchronization
;send $FF data to the MMC 10 times. Only called after power-up
mmc_cmd_synchronize	bank 	mmc_spix_ram			;select the RAM bank		
			CALL	spix_init			;initialize SPI	I/O pins	
			clr	mmc_status			;initialize the MMC driver
			clr	mmc_r1
			clr	mmc_r2
			mov	mmc_temp,#sync_duration		;get n bytes as a delay
mmc_lable1		setb	spix_out_pin			;in case of non-zero response from MMC	
			call	spix_get
			cjne	spix_data_io,#$ff,mmc_lable2
			djnz	mmc_temp,mmc_lable1
mmc_lable2		mov 	mmc_r1,spix_data_io
			call 	spix_get			;DUMMY read cycle
			jmp	mmc_exit
;*** MMC Initialization
;This function executes CMD0 and CMD1 with corresponding R1 MMC response detection
mmc_cmd_initialize	clrb	spix_cs_pin		;set MCC_CS active (low)
			bank	mmc_spix_ram		;init all the variables
			clr	mmc_status		;clear the status byte
			CALL	zero_MMC_addr		;set MMC data address to 0
			mov	mmc_cmd_crc,#$FF

							;execute the CMDO command, exit if R1 is not equal to $01
			mov	mmc_temp,#$08
			call	mmc_delay		;execute 8 DUMMY cycles

			mov	mmc_cmd,#$40		;execute CMD0			
			call	mmc_cmd_send
			call	mmc_r1_get		;get a response byte back

			mov	mmc_temp,#$10
			call	mmc_delay		;execute 16 DUMMY cycles
			cjne	mmc_r1,#$01,mmc_label14	;correct R1 is equal to #$01
;The above code segment seems to go against MMC specs. The MMC isn't supposed to give
; a $00 response on this command but it seems to if we try to repeat it.
;The correct (from the manual point of view) sequence should look like following, 
; (but it does not work):
;			cje	mmc_r1,#$01,mmc_label15	;correct R1 is equal to #$01
;			setb	mmc_error		;if not - set the error flag
;			jmp	mmc_label14		;and exit
;execute the CMD1 command until R1==00 or MMC_CMD1_WAIT times

mmc_label15		mov	mmc_temp2,#cmd1_resp_wait ;load wait duration for response
mmc_label13		clr	mmc_status		;reset error status
			mov	mmc_temp,#cmd1_delay1	;send n (=8?) DUMMY bytes
			call	mmc_delay		
			mov	mmc_cmd,#$41		;send CMD1 code		
			call	mmc_cmd_send
			call	mmc_r1_get		;get the response
			mov	mmc_temp,#cmd1_delay2	;load delay
			call	mmc_delay		;send n (=16?) DUMMY bytes
			MOV	W,#cmd1_delay3		;load delay value
			call 	mmc_wait		;wait for some time
			test	mmc_r1			;check for response
			jz	mmc_label14		;correct R1 is equal to #$00
			djnz	mmc_temp2,mmc_label13	;if not - repeat mmc_cmd1_wait times
mmc_label14		setb	spix_cs_pin		;set MMC_CS to inactive (HIGH)
			jmp	mmc_exit		;exit
;*************************** Main Program Code *****************************
;* Main *
;		ORG     100h
		IF chip_type=1			;SX52
		 MOV	FSR,#$0A		;load first offset
		ELSE				;SX52
		 CLR	FSR			;point to beginning of RAM
		IF chip_type=0
		 SB	FSR.4			;are we on low half of bank?
		 SETB	FSR.3			;If so, don't touch regs 0-7
		CLR     IND                     ;clear using indirect addressing
		IJNZ    FSR,:zero_ram           ;repeat until done

:setup_regs	MOV	RA,#RA_init		;initialize port RA
		MOV     !RA,#RA_IO		;Set RA in/out directions
		MOV	RB,#RB_init		;initialize port RB
		MOV     !RB,#RB_io		;Set RB in/out directions
		MOV	RC,#RC_init		;initialize port RC
		MOV     !RC,#RC_io		;Set RC in/out directions
		MOV     !OPTION,#option_init	;initialize OPTION reg.

		BANK	analog                  ;switch to adc/pwm/timer bank
		MOV	W,#pwm0_init		;get initial pwm0 voltage
		MOV	pwm0,W			; and store it
		MOV	W,#pwm1_init		;get initial pwm0 voltage
		MOV	pwm1,W			; and store it
		MOV	W,#samp_freq&$FF	;keep low byte of sample rate
		MOV	freq_low,W		;store it
		MOV	W,#samp_freq>>8		;keep high byte of sample rate
		MOV	freq_high,W		;store it
		IF chip_type=0				;SX18/28
		 MOV	W,#buff_size*2+mmc_data_ram-2	;last address (-2 to keep stereo syncing)
		ELSE					;SX52
		 MOV	W,#mmc_data_ram+mmc_buffsize-2	;last address (-2 to keep stereo syncing)
		MOV	next_samp_ptr,W			;set sample pointer to start

		CALL	spix_init		;SPIX port pin default states
		CALL	zero_MMC_addr		;reset MMC data address pointers
;************** Main program loop

:sync		bank	mmc_spix_ram
		mov	mmc_cmd,#$ff		;execute the SYNCHRONIZE function
		call	mmc_execute
:init		mov	mmc_cmd,#$fe		;execute the INITIALIZE function
		call	mmc_execute
		test	mmc_status	
		jnz	:sync			;repeat in case of syncronization/initialization error

:write		mov 	mmc_cmd,#$FA		;execute the 512 byte block write command
;		call	mmc_execute		;uncomment this to write the block
		test	mmc_status		;check how write procedure went
		JMP	:error			;exit if we got an error writing data block

:read_init	CALL	zero_MMC_addr		;reset MMC data address pointer => 0
:read_loop	mov	mmc_cmd,#$FD		;execute the block read command
		call	mmc_execute
		test	mmc_status		;check how read procedure went
		SZ				;
		JMP	:error			;exit if we got an error reading data block

		INC	mmc_addr_b1		;count through multiple blocks
		INCSZ	mmc_addr_b1		; (b1=b1+2 => advance 512 bytes at a time)
		JMP	:read_loop 		;keep reading

	IF MMC_size<16
		INC	mmc_addr_b2		;continue through multiple blocks
		MOV	W,#end_of_mem		;load end of storage memory (or sample size)
		MOV	W,mmc_addr_b2-W		; and see if we're there yet
	ELSE					; for MMC cards >16Meg
		INCSZ	mmc_addr_b2		;continue through multiple blocks
		JMP	:read_loop 		;keep reading
		INC	mmc_addr_b3		;continue through multiple blocks
		MOV	W,#end_of_mem		;load end of storage memory (or sample size)
		MOV	W,mmc_addr_b3-W		; and see if we're there yet
		SZ				;if so, skip ahead
		JMP	:read_loop 		;otherwise, keep looping

:done_read	JMP	:read_init		;run through the whole read again
:error		JMP	MainLoop		;in case of read/write error, re-synchronize

file: /Techref/scenix/contest/mmc/sx52mmc.src, 38KB, , updated: 2000/11/10 16:24, local time: 2023/3/21 13:06,

 ©2023 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions?
Please DO link to this page! Digg it! / MAKE!

<A HREF=""> scenix contest mmc sx52mmc</A>

Did you find what you needed?

  PICList 2023 contributors:
o List host: MIT, Site host, Top posters @none found
- Page Editors: James Newton, David Cary, and YOU!
* Roman Black of Black Robotics donates from sales of Linistep stepper controller kits.
* Ashley Roll of Digital Nemesis donates from sales of RCL-1 RS232 to TTL converters.
* Monthly Subscribers: Gregg Rew. on-going support is MOST appreciated!
* Contributors: Richard Seriani, Sr.

Welcome to!