Y seguimos con el proyecto.
La comunicación entre el PIC y la MMC Card se realiza mediante el protocolo Síncrono
SPI. Como el PIC 18F2550 dispone de un modo de funcionamiento de la USART específico para el SPI parecería lógico usarlo y en paz, pero el hardware de la placa
iACV1 tiene la USART dedicada a sus comunicaciones con el módulo Ethernet
Tibbo EM202 así que lo que he decidido es emular la comunicación
SPI por software. No ocupa mucho y funciona correctamente.
Todo el protocolo está construido alrededor del
Driver de CCS C "mmc_spi.c" que podéis encontrar en el directorio
Drivers de la instalación de dicho compilador.
Lo único importante a tener en cuenta al usar este driver es configurar los defines que dicen qué pines vamos a utilizar para cada una de las funciones SPI (DO, DI, CK y CS) "
antes" del include del driver <mmc_spi.c> ya que en caso contrario estos defines no tendran efecto al ser compilados "
después" que el código del driver.
En mi caso concreto he optado por usar los primeros pines del
PORTB (que además son los que tengo disponibles en las clemas de la iACV1):
#define MMC_CS
PIN_B3#define MMC_CLK
PIN_B2#define MMC_DI
PIN_B0#define MMC_DO
PIN_B1Os pongo la cabecera con las funciones disponibles en el driver CCS C:
////////////////////// Driver for Multimedia Card ///////////////////////
//// ////
//// mmc_init() - Reset and Initialize the MMC. Returns zero if OK ////
//// ////
//// mmc_modify_byte(address, val) - Modify the byte at address to ////
//// change it's value to val. Will read/write the ////
//// entire 512 byte block but only change this ////
//// specific byte. Returns zero if OK. ////
//// ////
//// mmc_modify_block(address, size, *ptr) - Modifies the bytes ////
//// to change their value to whats stored at *ptr. ////
//// Will read/write the entire 512 byte block(s) but ////
//// only change the values defined by ptr and size. ////
//// Returns zero if OK. ////
//// ////
//// mmc_write_block(address, size, *ptr) - Writes a 512 byte ////
//// block to the MMC. If size is less than 512 then ////
//// unspecified data will be written as 0. Returns ////
//// zero if OK. ////
//// ////
//// mmc_read_byte(address,*ptr) - Reads the byte specified at ////
//// address. Result is saved to ptr. Returns zero ////
//// if OK. ////
//// ////
//// mmc_read_block(address, size, *ptr) - Reads the bytes ////
//// specified at address. Result is saved to ptr. ////
//// Returns zero if OK. ////
//// NOTE: You might get an address error if you try ////
//// to read over a page size. For example, trying ////
//// to read a block size of 512 starting at address ////
//// 0x100 may cause an error because you are reading ////
//// two blocks. ////
//// ////
//// mmc_erase(address, blocks) - Erases the block specified at ////
//// address. Will erase the entire 512 byte block. ////
//// If you wish to erase more blocks after specified ////
//// block use the blocks parameter to specifiy how ////
//// many extra blocks to erase. Returns zero if OK. ////
//// ////
//// ~~~~~~~ MULTI-READ FUNCTIONS ~~~~~~~~ ////
//// ////
//// mmc_read_enable(address, size) - Start multi-reads at ////
//// specified address. Size is the size of each ////
//// individual read. Returns zero if OK. ////
//// ////
//// mmc_read_mult_block(*ptr) - Reads data from the MMC, and saves ////
//// to ptr. The number of bytes read is defined ////
//// by mmc_read_enable(). You must call ////
//// mmc_read_enable() before you can call this. ////
//// Returns zero if OK. ////
//// ////
//// mmc_read_disable(void) - Stop a multi-read. ////
//// Returns zero if OK. ////
//// MAY BE BROKEN. ////
//// ////
/////////////////////////////////////////////////////////////////////////
//// (C) Copyright 1996,2001 Custom Computer Services ////
//// This source code may only be used by licensed users of the CCS ////
//// C compiler. This source code may only be distributed to other ////
//// licensed users of the CCS C compiler. No other use, ////
//// reproduction or distribution is permitted without written ////
//// permission. Derivative programs created using this software ////
//// in object code form are not restricted in any way. ////
/////////////////////////////////////////////////////////////////////////
Con este driver en uso he construido un
Firmware inicial que solo inicializa la MMC Card y que lee bloques de información sobre un
buffer de
512 bytes de largo.
Las lecturas se realizan secuencialmente desde la address 0 en adelante en saltos de 512 bytes. Tened en cuenta que estoy usando:
1º.- Un
driver directo, sin utilizar información sobre formateo lógico como puede ser un
FAT16 o un
FAT32.
2º.- La
MMC Card que estoy usando estaba ya
en uso en una cámara digital o un móvil por lo que la información que contiene está formateada y no sé cuál puede ser.
El firmware versión
v.0.0.0 es (Le he quitado lo relativo a hacer funcionar el Led, el Buzzer y el Rele del que dispone el hardware iACV1) :
////////////////////////////////////////////////////////////////////////////////////
//
// MMC Card Test ...
// by RedPic
////////////////////////////////////////////////////////////////////////////////////
#include <18f2550.h>
#fuses HS,MCLR,NOWDT,NOPROTECT,NOPUT,NOBROWNOUT,NOPBADEN,NOLVP,NOCPD,NODEBUG,NOWRT,NOVREGEN
#use delay(clock=20000000)
#define EM202_MD PIN_A0
#define EM202_RST PIN_A1
#define EM202_CTS PIN_A2
#define EM202_RTS PIN_A3
#define EM202_DTR PIN_A4
#define EM202_DSR PIN_A5
#define EM202_RX PIN_C6
#define EM202_TX PIN_C7
#define LED PIN_C0
#define BUZZER PIN_C1
#define RELE PIN_C2
#use rs232(baud=19200, xmit=EM202_RX, rcv=EM202_TX)
char cRec=0x00; // Último caracter recibido via serie
char Command=0x00; // Comando a procesar
int8 MMCBuffer[512];
int32 address=0;
#define MMC_CS PIN_B3
#define MMC_CLK PIN_B2
#define MMC_DI PIN_B0
#define MMC_DO PIN_B1
#include "rr_mmc_spi.c"
////////////////////////////////////////////////////////////////////////////////////
//
// Funciones ...
//
////////////////////////////////////////////////////////////////////////////////////
void Cursor(char c){
printf("%c\r\n>",c);
}
void Presenta_Hardware(void){
printf("\r\n\n");
printf("MMC MultiMedia Card Driver Test\r\n");
printf("Hardware iACCESS CONTROL V1 v.0.0.0\r\n\n");
printf("Commands when available:\r\n\n");
printf("[X] Reset EM202 Ethernet Module\r\n");
printf("[M] Init MMC Card\r\n");
printf("[F] Read MMC Card Block First\r\n");
printf("[R] Read MMC Card Block Actual\r\n");
printf("[ ] Read MMC Card Block Next\r\n\n");
Cursor(0x00);
}
void Reset_EM202(void){
OUTPUT_HIGH(EM202_MD);
OUTPUT_HIGH(EM202_RST);
delay_ms(110);
OUTPUT_LOW(EM202_RST);
delay_ms(110);
}
////////////////////////////////////////////////////////////////////////////////////
//
// Interrupción por Recepción Serie RS232
//
////////////////////////////////////////////////////////////////////////////////////
#int_rda
void handle_rda_int(){
if(kbhit()){ // Si hay algo pdte de recibir ...
cRec=getc(); // lo recibo sobre cRec ...
if(cRec!=0x00){ // Si es distinto de \0 ...
Command=ToUpper(cRec); // cargo cRec sobre Command para procesarlo
} // pasándolo a Mayúsculas para no confundir.
}
}
////////////////////////////////////////////////////////////////////////////////////
//
// Main
//
////////////////////////////////////////////////////////////////////////////////////
void main() {
int16 f=0;
int16 size=512;
int8 col=0;
disable_interrupts(global);
disable_interrupts(int_timer1);
disable_interrupts(int_rda);
disable_interrupts(int_ext);
disable_interrupts(int_ext1);
disable_interrupts(int_ext2);
setup_adc_ports(NO_ANALOGS);
setup_adc(ADC_OFF);
setup_spi(FALSE);
setup_counters(RTCC_INTERNAL,RTCC_DIV_2);
setup_timer_0(RTCC_OFF);
setup_timer_1(T1_INTERNAL | T1_DIV_BY_1);
setup_timer_2(T2_DISABLED,0,1);
setup_timer_3(T3_DISABLED);
setup_comparator(NC_NC_NC_NC);
setup_vref(FALSE);
port_b_pullups(false);
set_tris_a(0b00001000);
set_tris_c(0b10000000);
enable_interrupts(global);
enable_interrupts(int_rda);
Reset_EM202();
address=0;
do {
if(Command!=0x00){ // Si he recibido un comando vía Serie ...
if(Command=='?'){ // Si el comando es '?' ...
Presenta_Hardware();
}
if(Command=='X'){ // Reset EM202
Cursor(Command);
Reset_EM202();
}
if(Command=='M'){ // Inicializa MMC Card
Cursor(Command);
if(mmc_init()==0)
printf("MMC init ok");
else
printf("MMC init fault");
Cursor(0x00);
}
if(Command=='F'){ // Read First Block MMC Card
address=0;
Command='R';
}
if(Command==' '){ // Read Next Block MMC Card
address+=512;
Command='R';
}
if(Command=='R'){ // Read Block MMC Card
Cursor(Command);
for(f=0;f<=511;f++) MMCBuffer[f]=0;
size=511;
col=0;
if(mmc_read_block(address, size,&MMCBuffer[0])==0){
printf("\r\n>MMC read ok from Address %LX to %LX\r\n",address,address+size);
for(f=0;f<=511;f++){
printf("%X ",MMCBuffer[f]);
if(++col==16){
col=0;
printf("\r\n");
}
}
}
else
printf("\n\rread fault");
Cursor(0x00);
}
Command=0x00; // Indico que ya he procesado el comando.
}
} while (TRUE);
}
Y como ejemplo de su funcionamiento os pego de nuevo la imagen en la que encontré una referencia a la
FAT16 con que parece ser está formateada la
MMC Card que estoy usando:
Ea, y ya está bien por hoy.
Mañana más.