' 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 ************************