' Program: MMSDIO_HS2.BAS written using PicBasicPro ver 2.45 ' PicBasic Pro program to interface with a Sandisk ' MMC module using the SPI protocol. ' Revision: Added CTS handshake using Portb.0 '================================================= ' FOR USE WITH THE WINDOWS APPLICATION MMC_IO.EXE '================================================= ' BETA CODE ' ************* NOT FOR COMMERCIAL USE ************************* ' Ver. 1.0.1 Apr, 2007 ' ' Proprietory Owner: COMPsys, Copyright 2005 ' ' Project Reference: MMC3V-F876 ' MCU: Microchip PIC16F876 or equiv mcu ' Software: Compiler PBP V.2.47 ' PIC Programmer: EPIC used to load the bootloader, ' Custom PCB with a serial interface ' Author: Ranjit Diol ' rsdiol@compsys1.com ' http://www.compsys1.com/workbench ' '--------------------------------------------------------------- ' (c) COMPSys, 2007 ' 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.2 SS,Portc.3 SCK,Portc.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 ' ************Remove the above line if compiling for an 18Fxxx PIC *********** ' ' ' 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 LOADER_USED 1 'If using ISP '***************************** include "modedefs.bas" DEFINE OSC 10 'Adjust to suit your design ADCON1 = 7 'Adjust ADC for your needs OPTION_REG.7 = 1 'PORTB internal pull-ups = off (0=on) ' Set receive register to receiver enabled DEFINE HSER_BAUD 19200 'Baud = 19200 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 'For the SDMM3F and MMSD3F Boards '***************** PIC PIN ASSIGNMENTS ***************** TRISC = %10010000 ' Set data-direction on PortC for SPI,UART and I2C 'I2C Eeprom Pins scl var PORTC.0 'I2C SCL for ext eeprom sda var PORTC.1 'I2C SDA for ext eeprom 'SD/MMC Connections SCK var PORTC.3 ' Clock pin SD/MMC pin 5 SO var PORTC.4 ' Data from SD/MMC pin 7 SS var PORTC.2 ' Slave select pin 1 SI var PORTC.5 ' Data to SD/MMC pin 2 'Added Jan 2005 trisA.0=0 Cts var portA.0 low cts 'Test CTS pin 'tmpy: 'low cts 'pause 1000 'high cts 'pause 1000 'goto tmpy '***************** 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 chk_flag var byte w_tmp1 var word w_tmp2 var word w_addr var word file_info var byte[12] cnt var byte 'Diagnostic LEDs wr_led var PORTB.7 err_led var PORTB.6 ok_led var PORTB.5 TRISB.7 = 0 TRISB.6 = 0 TRISB.5 = 0 pause 500 'Let things settle a bit 'For debugging 'serout2 out_pin,ser_baud,["Start",10,13] 'Display on terminal hserout ["Trying....",10,13] ' ***** Jump over the subroutines ************** goto main 'Initialize MMC 'Initialize MMC init_mmc: pause 500 chk_flag = 0 : w_tmp2 = 0 hserout ["Trying...",10,13] SS = 1 dat =$ff cnt = 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 cnt = 4 gosub shiftout_dat dat = crc Shiftout SI, SCK, MSBFIRST,[dat] chk = 1 gosub chk_res if chk_flag = 1 then hserout ["No response",10,13] ss = 1 pause 50 ss = 0 if chk_flag = 1 then init_mmc HSerout ["Cmd0 OK",10,13] CMD1: 'TO DO: Need to insert a 'time-out' after 255 attempts and branch to error routine w_tmp2 = 0 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 cnt = 4 gosub shiftout_dat dat = $ff gosub shiftout2 gosub shiftin_res w_tmp2 = w_tmp2 + 1 if w_tmp2 > $0FFF then init_mmc wend dat = $ff 'MMC now successfully initialized in SPI mode 'For debugging chk_ok2: hserout ["MMC INIT OK!",10,13] 'Display on terminal high ok_led 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: 'time-out after several attempts and branch to error routine w_tmp2 =0 : chk_flag = 0 while res <> chk Shiftin SO, SCK, MSBPRE, [res] w_tmp2 = w_tmp2 + 1 if w_tmp2 > $1000 then no_go1 wend goto chk_ok1 no_go1: chk_flag = 1 chk_ok1: return mwrite: high wr_led 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 'if chk_flag = 1 then hserout ["Write Error Resp not 0",10,13] '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] 'Send it to the MMC next addr dat=$ff cnt = 2 gosub shiftout_dat gosub shiftin_res y=res & $0f gosub shiftin_res 'time-out after several attempts and branch out while res = 0 'MMC is busy writing until response is not = 0 Shiftin SO, SCK, MSBPRE, [res] wend gosub mstatus 'Get write status low wr_led 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 if chk_flag = 1 then hserout ["Read Error Resp not 0",10,13] chk = $FE gosub chk_res if chk_flag = 1 then hserout ["Read Error Resp not $FE",10,13] '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 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: 'ERASE BLOCK at addr_h,addr_L 'Cmd38 cmd = $66 gosub send_cmd 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 cnt = 2 gosub shiftout_dat gosub shiftin_res chk = 0 gosub chk_res if chk_flag = 1 then hserout ["Cmd error Resp not 0",10,13] return 'MMC status mstatus: ss = 1 dat = $ff Shiftout SI, SCK, MSBFIRST,[dat] gosub shiftin_res ss = 0 arg4 = $4D gosub shiftout1 dat = 0 cnt = 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 CNT shiftout_dat: for y = 1 to cnt Shiftout SI, SCK, MSBFIRST,[dat] next y return 'Shift out 5 (ARG0-ARG4) shiftout5: Shiftout SI, SCK, MSBFIRST,[arg0,arg1,arg2,arg3,arg4] : return shiftout4: Shiftout SI, SCK, MSBFIRST,[arg1,arg2,arg3,arg4] : return shiftout3: Shiftout SI, SCK, MSBFIRST,[arg2,arg3,arg4]: return shiftout2: Shiftout SI, SCK, MSBFIRST,[arg3, arg4]: return 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: low cts hserout ["Waiting..",10,13] get_param: pause 100 high cts hserin [wait ("@"),STR in_ctl\11] low cts 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 ["Addr:",hex4 addr_H,hex4 addr_L,10,13] for c = 1 to buff_count gosub mread for e_addr = eaddr1 to eaddr2 I2CREAD sda,scl,ctl,e_addr,[dat] hserout [hex2 dat] 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 ["Addr:",hex4 addr_H,hex4 addr_L,10,13] 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 [dat] 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 high cts hserin 5000,WrErr,[STR in_array\64] low cts I2CWRITE sda,scl,ctl,e_addr,[STR in_array\64] 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 goto cont2 WrErr: low cts hserout ["Write Error!",13,10] goto get_param Cont2: hserout ["Data Written",13,10] goto get_param mmc_erase: goto get_param mmc_tag: goto get_param goto get_param 'Safety net! end '******************* END OF CODE ************************