' Program: MMC3V.BAS written using PicBasicPro ver 2.40
' PicBasic Pro program to interface with a Sandisk
' MMC module using the SPI protocol.
'
' BETA CODE
' ************* NOT FOR COMMERCIAL USE *************************
' Ver. 0.1.31 Nov, 2001
'
' 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: OCT 28, 2001
' 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
DEFINE OSC 10 'Adjust to suit your design
ADCON1 = 7 'Adjust ADC for your needs
'*****************************
include "modedefs.bas"
'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
res var byte 'response var
reswr var byte 'response w/r
res2 var word 'response var
erase_flag var byte
erase_flag = 0
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
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
' ***** 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
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:
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
chk = 0
gosub chk_res
chk = $FE
gosub chk_res
'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
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
'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
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
'Load the eeprom with some data to simulate this we
'write 512 bytes to the eeprom
Dat = "A"
for e_addr = eaddr1 to eaddr2
I2CWRITE sda,scl,ctl,e_addr,[dat]
next e_addr
'Set some MMC address 0 - to the end. (However, it must remain within sector boundaries)
'Since we are reading/writing 512 bytes at a time just make sure that the address is a integer
'multiple of 512 bytes. Such as: 0, $0200,$0400...$0A00...$00010A00 etc
addr_H = $0001
addr_L = $0000 'Multiple of 512 bytes
'Now transfer the data from the eeprom to the MMC
gosub mwrite
'For debugging
serout2 out_pin,ser_baud,["Data Written to MMC",10,13] 'Display on terminal
'Next, we read the data back from the MMC and load it into the eeprom
'using the same addresses as above
gosub mread
'Now, read it back from the eeprom and display it
gosub disp
'If you want to erase the data you need to set the start and end addresses
'But, they must not cross block boundaries
'*** Note*****
'If the program hangs at the erase command
'Sandisk documentation states that the erase command in SPI mode can
'sometimes not work correctly. Depends on the version of the card.
'However,it works fine on the Viking MMC's
'For debuggin
serout2 out_pin,ser_baud,[10,13,"Erasing...",10,13] 'Finished!
'Set the start erase TAG
gosub tag_start
'Set the end erase TAG
addr_H = $0001
addr_L = $01FF
gosub tag_end
'Issue the erase command
gosub merase
'Verify it by reading the MMC again
addr_L = $0000
gosub mread
'And finally display the results
'Display data to terminal, assumes eaddr1,eaddr2 are given
'Debugging
serout2 out_pin,ser_baud,["Data from MMC after erase",10,13] 'Display on terminal
for e_addr = eaddr1 to eaddr2
I2CREAD sda,scl,ctl,e_addr,[dat]
serout2 out_pin,ser_baud,[hex2 dat] 'Display on terminal
next e_addr
'For debugging
serout2 out_pin,ser_baud,[10,13,"All Done!",10,13] 'Finished!
end
'******************* END OF CODE ************************