' Program: MMCIO.BAS written using PicBasicPro ver 2.40
' PicBasic Pro program to interface with a Sandisk
' MMC module using the SPI protocol.
'
'=================================================
' FOR USE WITH THE WINDOWS APPLICATION MMC_IO.EXE
'=================================================
'                    BETA CODE
' ************* NOT FOR COMMERCIAL USE *************************
'               Ver. 0.1.55  Jul, 2002
'
' Proprietory Owner: COMPsys, Copyright 2001
'
' Project Reference: MMC3V-F876
' MCU: Microchip PIC16F876
' Software: Compiler PBP V.2.40, IDE MBLAB Ver 5
' PIC Programmer: EPIC used to load the bootloader, 
'                 Custom PCB with a serial interface
'                 for ISP and terminal output
'
' Initial Date: June 28, 2002
' Projected Completion Date: Unknown
'
' Author: Ranjit Diol
'         rsdiol@compsys1.com
'         http://www.compsys1.com/workbench
'
'---------------------------------------------------------------
'                      (c) COMPSys, 2001
'                     All Rights Reserved
'***************************************************************
'   DISCLAIMER: This file is being released as non-commericial
'   freeware. It is being provided "AS IS", neither the author, 
'   nor COMPSys shall be held liable for any damages caused directly
'   or indirectly by its use. 
'***************************************************************
'Brief: The MMC is a 3volt part therefore all data lines
'       must be conditioned if interfacing with a 5v mcu.
'
'       MMC pins in SPI mode:
'            Pin1:ChipSelect(SS),Pin2:MMC input(MOSI),Pin3:GND,Pin4:3V+,
'            Pin5:Clock(SCK), Pin6:GND, Pin7:MMC output(MISO)
'       PIC pins:
'            Portd.7 SS,Portd.6 SCK,Portd.5 MOSI, Portd.4 MISO
'
'       For voltage translation:  
'       An LM317 or LM2937-3.3 can be used to provide 3.3 volts
'       for the MMC and four 2N3904 transistors with resistors
'       can be used forfor MMC power and data line conditioning
'       See the SanDisk website for more details
'                  http://www.sandisk.com
'==============================================================
'
'                    I M P O R T A  N T
'                       PLEASE NOTES
' ************** This code is written for a PIC16F876 operating on 3.3volts **************
@ device pic16F876, xt_osc, wdt_on, pwrt_off, bod_off, lvp_off, cpd_off, wrt_off, protect_off
' ****************************************************************************************
'
' This application uses a external 3.3v FRAM FM24CL64 for temporary storage
' which does not require a wait state after a write and it does not have
' any limit on the number of lifetime writes!
'
' IF YOU USE a standard I2C EEprom you will have to insert a 5 or 10 ms pause
' after each write statement depending on the eeprom used.
'
'******************************************************************************************


DEFINE	ONINT_USED	1	'If using ISP



'*****************************

include "modedefs.bas"


DEFINE OSC 10			'Adjust to suit your design
ADCON1	= 7		      	'Adjust ADC for your needs
' Set receive register to receiver enabled
DEFINE HSER_RCSTA 90h 
DEFINE HSER_BAUD 19200  'Baud = 19200
'DEFINE HSER_SPBRG 15    'for 20mhz Must be changed depending on Osc. used, see PIC datasheet
DEFINE HSER_SPBRG 7      'for 10Mhz
' Set transmit register to transmitter enabled
DEFINE HSER_TXSTA 20h 
DEFINE HSER_CLROERR 1 'Hser clear overflow automatically 

XON     con $11
XOFF    con $13

'I2C Variables
ctl      con    $A0		'EEPROM control code
edata	 var	BYTE		'Data byte to be written

'***************** PIC PIN ASSIGNMENTS *****************

'I2C Eeprom Pins
scl	 var 	PORTC.3		'I2C SCL  for ext eeprom 
sda	 var	PORTC.4		'I2C SDA for ext eeprom

'Serial Pins
'out_pin	 var	PORTC.6		'Serial OUT pin
'in_pin	 var    PORTC.7		'Serial IN pin
'ser_baud con	32		'19200 Baud


        TRISC.0 = 1                     'SO Set pin directions
	TRISC.1 = 0
	TRISC.2 = 0
	TRISC.5 = 0

'MMC Connections
SCK     var     PORTC.1                 ' Clock pin MMC pin 5
SO      var     PORTC.0                 ' Data from MMC pin 7
SS      var     PORTC.5                 ' Slave select pin 1
SI      var     PORTC.2                 ' Data to MMC pin 2


'***************** General MMC Variables used **************************

'Variables used (may be some that are no longer in use -- haven't cleaned up as yet!)

mmc_status   var     byte
mmc_status  =  $0000

addr    var     word
addr_H  var     word                    ' MMC Address is a 32 bit address, addr_h is bits 31-16
addr_L  var     word			' and addr_l is is bits 0-15
arg0    var     byte			' args used
arg1    var     byte
arg2    var     byte
arg3    var     byte
arg4    var     byte
cmd     var     byte			'command var
crc     var     byte			'crc var
dat     var     byte                    'data var
i	    var     byte			'counter var
x       var     word			'counter var
y	    var   	byte			'counter var
j       var     byte
c       var     byte
res	    var     byte			'response var
reswr   var     byte			'response w/r
res2    var     word			'response var
hex_nibble var  byte
erase_flag var  byte
erase_flag = 0
idx    var byte
eaddr1  var     word			'ext eeprom addr vars
eaddr2  var	word
e_addr  var     word


in_dat  var     byte                    'Serial vars
chk     var     byte
cntr    var     byte
in_array var    BYTE[64]		'In coming array
in_ctl	 var    BYTE[10]			'In coming control array
buff_count	var	BYTE		'General count
carry_flag   var     STATUS.0
addr      =      $0000			'Used for eeprom etc			

pause 500				'Let things settle a bit

'For debugging
'serout2 out_pin,ser_baud,["Start",10,13] 'Display on terminal
hserout ["Start",10,13]
' ***** Jump over the subroutines **************
goto main

'Initialize MMC
init_mmc:
	SS = 1
	dat =$ff
        cntr = 10
        gosub shiftout_dat
	ss = 0 
 	pause 50
	cmd = $40					'MMC Command 0
	crc = $95
cmd0:

 	dat = cmd
        Shiftout SI, SCK, MSBFIRST,[dat]
	dat = 0
        cntr = 4
        gosub shiftout_dat
	dat = crc
        Shiftout SI, SCK, MSBFIRST,[dat]
        chk = 1
        gosub chk_res
	ss = 1
	pause 50
	ss = 0
CMD1:
'TO DO: Need to insert a 'time-out' after 255 attempts and branch to error routine
	while res <> 0
	ss = 1
	dat = $ff
        Shiftout SI, SCK, MSBFIRST,[dat]
        gosub shiftin_res
	ss = 0
	dat = $41					 'MMC Command 1
        Shiftout SI, SCK, MSBFIRST,[dat]
        'arg4 = $41
        'gosub shiftout1
	dat = 0
        cntr = 4
        gosub shiftout_dat

	dat = $ff
        gosub shiftout2
        gosub shiftin_res
	wend
	dat = $ff
       'MMC now successfully initialized in SPI mode
'For debugging
'serout2 out_pin,ser_baud,["INIT OK!",10,13] 'Display on terminal
hserout ["INIT OK!",10,13]
return

'MMC Writing subroutine write 512 bytes stored in the I2C eeprom
'This routine will write whatever is in I2C eeprom locations eaddr1 to eaddr2

'Check response
chk_res:
'TO DO: Need to insert a 'time-out' after 255 attempts and branch to error routine
	while res <> chk			
        Shiftin SO, SCK, MSBPRE, [res]		
	wend
return

mwrite:
	ss = 1
	arg4 = $ff
        gosub shiftout1
        gosub shiftin_res
	ss = 0
 	arg0 = $58
        arg1 = addr_H.BYTE1
        arg2 = addr_H.BYTE0
        arg3 = addr_L.BYTE1
        arg4 = addr_L.BYTE0
        gosub shiftout5
	arg4 = $ff
        gosub shiftout1
        gosub shiftin_res
        chk = 0
        gosub chk_res

'Write command was a success
	'File token tells the MMC data is to follow
	arg4 = $FE
        gosub shiftout1


'MMC now ready to write data in blocks of 512 bytes assumes eeprom address in eaddr1, eaddr2

	for addr = eaddr1 to eaddr2
		'READ addr,dat		'Read from internal eeprom if it has 512 byte capacity

        	I2CREAD sda,scl,ctl,addr,[dat]	'Read from I2C ext eeprom
		Shiftout SI, SCK, MSBFIRST,[dat]
	next addr


	dat=$ff
        cntr = 2
        gosub shiftout_dat
        gosub shiftin_res
	y=res & $0f
        gosub shiftin_res

'TO DO: Need to insert a 'time-out' after 255 attempts and branch to error routine
	while res = 0		'MMC is busy writing until response is not = 0
          Shiftin SO, SCK, MSBPRE, [res]
	wend
	gosub mstatus		'Get write status	
return

'MMC Reading routine, assumes addr_H and addr_L have the MMC 32 bit address
mread:
'hserout [XON,"Reading",XOFF]   
	ss = 1
	arg4 = $ff
        gosub shiftout1
        gosub shiftin_res
	ss = 0
	arg0 = $51
        arg1 = addr_H.BYTE1
        arg2 = addr_H.BYTE0
        arg3 = addr_L.BYTE1
        arg4 = addr_L.BYTE0
        gosub shiftout5
	arg4 = $ff
        gosub shiftout1
        gosub shiftin_res
'hserout [XON,"Chk",XOFF]
	chk = 0
	gosub chk_res
	chk = $FE
	gosub chk_res
'hserout [XON,"Read CMD",10,13,XOFF]
'Read cmd successful

'Read the MMC data and save in eeprom assumes eaddr1 and eaddr2 is given
    for e_addr = eaddr1 to eaddr2		'We will read 512 bytes can be any number
    
         Shiftin SO, SCK, MSBPRE, [dat]
        'We now have a byte do something with it!
         I2CWRITE sda,scl,ctl,e_addr,[dat]		'Write to eeprom from eaddr1 to eaddr2
         pause 5
 
    next e_addr

'Required after a MMCread
        gosub shiftin_res
        gosub shiftin_res

 	 gosub mstatus		'Get read status

return

'Set block length (must lie with sector boundaries minimum default is 512)
mset_blk:
'Cmd16:
	cmd = $50
        crc = $ff
        gosub send_cmd
return

'MMC tag beginning and end of an earse block
tag_start:
'MMC tag block
'Block START TAG
'Cmd32
	cmd = $60
        gosub send_cmd
return

tag_end:
'Cmd33
	cmd = $61
        gosub send_cmd
return

'MMC erase tagged block
merase:

'For debugging
'ERASE BLOCK at addr_h,addr_L
'Cmd38
	erase_flag = 1
	cmd = $66
        gosub send_cmd
	erase_flag = 0     
return

'=============================

'Send a command to the mmc assumes CMD, addr_H and addr_L
send_cmd:
        ss = 1
	arg4 = $ff
        gosub shiftout1
        gosub shiftin_res
        ss = 0
        arg0  = cmd
        arg1 = addr_H.BYTE1
        arg2 = addr_H.BYTE0
        arg3 = addr_L.BYTE1
        arg4 = addr_L.BYTE0
        gosub shiftout5
	Dat = $FF
        cntr = 2
        gosub shiftout_dat
        gosub shiftin_res
	if erase_flag = 1 then return
	chk = 0
	gosub chk_res
return



'MMC status
mstatus:
	ss = 1
	dat = $ff
        Shiftout SI, SCK, MSBFIRST,[dat]
        gosub shiftin_res

	ss = 0
	arg4 = $4D
        gosub shiftout1
	dat = 0
        cntr = 4
        gosub shiftout_dat
	arg4 = $ff
        gosub shiftout1
        Shiftin SO, SCK, MSBPRE, [res2\16]
        mmc_status = res2
return

'Shift out the same data. Assumes DAT and cntr
shiftout_dat:
            for y = 1 to cntr
        	Shiftout SI, SCK, MSBFIRST,[dat]
            next y
return

'Shift out 5 (ARG0-ARG4)
shiftout5:
        Shiftout SI, SCK, MSBFIRST,[arg0]
shiftout4:
        Shiftout SI, SCK, MSBFIRST,[arg1]
shiftout3:
        Shiftout SI, SCK, MSBFIRST,[arg2]
shiftout2:
        Shiftout SI, SCK, MSBFIRST,[arg3]
shiftout1:
        Shiftout SI, SCK, MSBFIRST,[arg4]
return

'Shiftin RES
shiftin_res:
         Shiftin SO, SCK, MSBPRE, [res]
return

'====================== END OF MMC GENERAL ROUTINES =========================

'Subroutine used by this application
disp:
'serout2 out_pin,ser_baud,["Data from MMC",10,13] 'Display on terminal
hserout ["Data from MMC",10,13]
'Display data to terminal, assumes eaddr1,eaddr2 are given
	for e_addr = eaddr1 to eaddr2
           I2CREAD sda,scl,ctl,e_addr,[dat]
           'serout2 out_pin,ser_baud,[dat]   'Display on terminal
           hserout [dat]
        next e_addr
return

'*************** M A I N   P R O G R A M ****************
Main:
'Initialize the MMC
	gosub init_mmc
'Set block length (minimum 512 bytes when writing) default is 512 bytes
	addr_H = $0000
	addr_L = $0200
	gosub mset_blk
'Set an I2C eeprom address (512 bytes)
	eaddr1 = 0
	eaddr2 = $01FF
goto Term
'======== Added Jul 13th 2002 for I/O =========
'MAIN LOOP Get the initial parameters
'Params Index: 0 = Option, 1 & 2 = buffer_count, 3-6 = Addr_H, 7-10 = Addr_L
' 
Term:             
hserout ["Waiting..",10,13]
get_param:
 hserout[XON,XON,XON]
 hserin [wait ("@"),STR in_ctl\11]
 'hserin [wait ("@")]
' goto mmc_read
 hserout [XOFF,XOFF] 'Get the attention of the PC
	for idx = 0 to 10
        Lookdown in_ctl[idx],["0123456789ABCDEF"],hex_nibble
	    in_ctl[idx]=hex_nibble
	next idx
 	j=in_ctl[0]
  	buff_count   =(in_ctl[1]<<4) | in_ctl[2]   
	addr_H.byte1 =(in_ctl[3]<<4) | in_ctl[4]
	addr_H.byte0 =(in_ctl[5]<<4) | in_ctl[6]
	addr_L.byte1 =(in_ctl[7]<<4) | in_ctl[8]
	addr_L.byte0 =(in_ctl[9]<<4) | in_ctl[10]
    if buff_count = 0 then buff_count = 1
    
    'Options
	if j  = 0 then mmc_read_hex
    if j  = 1 then mmc_read_asc 		
	if j  = 2 then mmc_write 
	if j  = 3 then mmc_erase 	
	if j =  4 then mmc_tag	
	goto get_param 
 '=========== CONTINUE LOOPING ===========
 'Options
 'Read from MMC to eeprom, the MMC address is assumed to be in addr_H and addr_L
 mmc_read_hex:
  ' addr_h = $0000
  ' addr_L = $0000
   hserout [XON,"Addr:",hex4 addr_H,hex4 addr_L,10,13,XOFF]
   for c = 1 to buff_count     
   gosub mread
	for e_addr = eaddr1 to eaddr2
           I2CREAD sda,scl,ctl,e_addr,[dat]
           hserout [XON,hex2 dat,XOFF]
    next e_addr
    
    addr_L = addr_L + $0200
    if carry_flag = 1 then
    addr_H = addr_H + 1
    endif 
  next c       
goto get_param


 mmc_read_asc:
  ' addr_h = $0000
  ' addr_L = $0000
   hserout [XON,"Addr:",hex4 addr_H,hex4 addr_L,10,13,XOFF]
   for c = 1 to buff_count   
      gosub mread
	     for e_addr = eaddr1 to eaddr2
           I2CREAD sda,scl,ctl,e_addr,[dat]
           if dat = 10 then lfcr
           if dat = 13 then lfcr
           if (dat > 126) OR (dat < 32) then cont_read 'strip non-ascii 
           lfcr:
           hserout [XON,dat,XOFF]
        cont_read:       
        next e_addr
    
    addr_L = addr_L + $0200
    if carry_flag = 1 then
    addr_H = addr_H + 1
    endif 
  next c    
goto get_param
 
 
 mmc_write:
 for c = 1 to buff_count
 'Load 512 bytes into eeprom
  e_addr = $0000
  for idx = 1 to 8
     hserout [XON]
     hserin [STR in_array\64]
     hserout [XOFF,XOFF,XOFF,XOFF]
     I2CWRITE sda,scl,ctl,e_addr,[STR in_array\64]
     pause 5
     e_addr = e_addr + 64
 next idx
 gosub mwrite
     addr_L = addr_L + $0200
       if carry_flag = 1 then
         addr_H = addr_H + 1
       endif 
 next c
 
hserout [XON,"Data Written",XOFF]
goto get_param

 mmc_erase:
goto get_param

 mmc_tag:
goto get_param

goto get_param 'Safety net!
end
'******************* END OF CODE ************************
