Compact Flash I/O test

Dependencies:   mbed

FAT_FS/diskio.c

Committer:
emh203
Date:
2011-12-27
Revision:
0:6b1e6c9e48ba
Child:
1:dc171f34db9b

File content as of revision 0:6b1e6c9e48ba:

#include "mbed.h"
#include "LPC17xx.h"
#include "integer.h"
#include "diskio.h"
#include "stdarg.h"

#define _CF_DEBUG_MESSAGES
//#define _CF_DEBUG_READ_ATA

#ifdef _CF_DEBUG_MESSAGES 
   #define  CF_DEBUG(...) printf(__VA_ARGS__)    
#else
   #define  CF_DEBUG     
#endif
            

void disk_timerproc (void);


#define LED1_MASK  (uint32_t )(1<<18)
#define LED2_MASK  (uint32_t )(1<<20)
#define LED3_MASK  (uint32_t )(1<<21)
#define LED4_MASK  (uint32_t )(1<<23)


#define LED1_ON             LPC_GPIO1->FIOSET=LED1_MASK
#define LED1_OFF            LPC_GPIO1->FIOCLR=LED1_MASK
#define LED1_TOGGLE         LPC_GPIO1->FIOPIN^=LED1_MASK

#define LED2_ON             LPC_GPIO1->FIOSET=LED2_MASK
#define LED2_OFF            LPC_GPIO1->FIOCLR=LED2_MASK
#define LED2_TOGGLE         LPC_GPIO1->FIOPIN^=LED2_MASK

#define LED3_ON             LPC_GPIO1->FIOSET=LED3_MASK
#define LED3_OFF            LPC_GPIO1->FIOCLR=LED3_MASK
#define LED3_TOGGLE         LPC_GPIO1->FIOPIN^=LED3_MASK

#define LED4_ON             LPC_GPIO1->FIOSET=LED4_MASK
#define LED4_OFF            LPC_GPIO1->FIOCLR=LED4_MASK
#define LED4_TOGGLE         LPC_GPIO1->FIOPIN^=LED4_MASK



//8-bit Data Bus is on Pins P0.4 - P0.11
#define DATA_BUS_MASK      (uint32_t )((0xFF)<<4)

//3 bit address is on Port 2.0 - 2.2
#define ADDRESS_BUS_MASK   (uint32_t )(0x7)

//ChipSelects are on port 2
#define CS0_MASK    (uint32_t )(1<<3)
#define CS1_MASK    (uint32_t )(1<<4)

//IORD and IOWR are on port 0
#define IORD_MASK   (uint32_t )(1<<24)
#define IOWR_MASK   (uint32_t )(1<<23)


//Reset and power enable are on port 1
#define COMPACT_FLASH_RESET_MASK           (uint32_t )(1<<30)
#define COMPACT_FLASH_POWER_ENABLE_MASK    (uint32_t )(0x80000000)
//Card Detect is on Port 2
#define COMPACT_FLASH_CARD_DETECT_MASK     (uint32_t )(1<<5)

//Low Level Bus Operation Macros
//Note: LPC 176x have dedicate set and clear registers

#define SET_DATA_BUS_TO_INPUTS                LPC_GPIO0->FIODIR &= ~(DATA_BUS_MASK);
#define SET_DATA_BUS_TO_OUTPUT                LPC_GPIO0->FIODIR |=  (DATA_BUS_MASK);

#define CS0_ACTIVE                            LPC_GPIO2->FIOCLR = CS0_MASK
#define CS0_INACTIVE                          LPC_GPIO2->FIOSET   = CS0_MASK
#define CS1_ACTIVE                            LPC_GPIO2->FIOCLR = CS1_MASK
#define CS1_INACTIVE                          LPC_GPIO2->FIOSET   = CS1_MASK

#define IORD_ACTIVE                           LPC_GPIO0->FIOCLR = IORD_MASK
#define IORD_INACTIVE                         LPC_GPIO0->FIOSET   = IORD_MASK

#define IOWR_ACTIVE                           LPC_GPIO0->FIOCLR   = IOWR_MASK
#define IOWR_INACTIVE                         LPC_GPIO0->FIOSET   = IOWR_MASK

#define COMPACT_FLASH_RESET_ACTIVE            LPC_GPIO1->FIOCLR = COMPACT_FLASH_RESET_MASK
#define COMPACT_FLASH_RESET_INACTIVE          LPC_GPIO1->FIOSET = COMPACT_FLASH_RESET_MASK

#define  COMPACT_FLASH_POWER_ENABLE           LPC_GPIO1->FIOCLR = COMPACT_FLASH_POWER_ENABLE_MASK
#define  COMPACT_FLASH_POWER_DISABLE          LPC_GPIO1->FIOSET = COMPACT_FLASH_POWER_ENABLE_MASK

#define COMPACT_FLASH_CARD_DETECTED            (!((LPC_GPIO2->FIOPIN)&COMPACT_FLASH_CARD_DETECT_MASK))

//To set the Address and Data Bus Lines we will use the convient Mask register in the Port I/O modules
//The Hardware will mask out pins that are set to 1 in the MASK register

#define SET_CF_ADDRESS(ADDRESS)       LPC_GPIO2->FIOMASK=~(ADDRESS_BUS_MASK);   \
                                      LPC_GPIO2->FIOPIN=ADDRESS;                \
                                      LPC_GPIO2->FIOMASK=0                      //Always remember to reset the mask for other operations to complete correctly


#define SET_CF_DATA(DATA)             LPC_GPIO0->FIOMASK=~(DATA_BUS_MASK);      \
                                      LPC_GPIO0->FIOPIN=(((uint32_t)DATA)<<4);           \
                                      LPC_GPIO0->FIOMASK=0                  //Always remember to reset the mask for other operations to complete correctly

#define GET_CF_DATA(DATA)             LPC_GPIO0->FIOMASK=~(DATA_BUS_MASK);        \
                                      (DATA) = (LPC_GPIO0->FIOPIN)>>4;           \
                                      LPC_GPIO0->FIOMASK=0                  //Always remember to reset the mask for other operations to complete correctly

#define SET_DATA_BUS_AS_OUTPUTS       LPC_GPIO0->FIODIR|=DATA_BUS_MASK
#define SET_DATA_BUS_AS_INPUTS        LPC_GPIO0->FIODIR&=~DATA_BUS_MASK



/* ATA command */
#define CMD_RESET        0x08    /* DEVICE RESET */
#define CMD_READ        0x20    /* READ SECTOR(S) */
#define CMD_WRITE        0x30    /* WRITE SECTOR(S) */
#define CMD_IDENTIFY    0xEC    /* DEVICE IDENTIFY */
#define CMD_SETFEATURES    0xEF    /* SET FEATURES */

/* ATA register bit definitions */
#define    LBA             0xE0
#define    BUSY            0x80
#define    DRDY            0x40
#define    DF                0x20
#define    DRQ                0x08
#define    ERR                0x01
#define    SRST            0x40
#define    nIEN            0x20

/* Bit definitions for Control Port */
#define    CTL_READ        0x20
#define    CTL_WRITE        0x40
#define    CTL_RESET        0x80
#define    REG_DATA        0x0
#define    REG_ERROR        0x1
#define    REG_FEATURES    0x1
#define    REG_COUNT        0x2
#define    REG_SECTOR        0x3
#define    REG_CYLL        0x4
#define    REG_CYLH        0x5
#define    REG_DEV            0x6
#define    REG_COMMAND        0x7
#define    REG_STATUS        0x7
#define    REG_DEVCTRL        0xE
#define    REG_ALTSTAT        0xE



/*--------------------------------------------------------------------------

   Module Private Functions

---------------------------------------------------------------------------*/

static volatile
DSTATUS Stat = STA_NOINIT;    /* Disk status */

static volatile
BYTE DiskProcTimer;            /* 100Hz decrement timer */



void InitCompactFlashInterface()
{
    SET_DATA_BUS_AS_INPUTS;

    LPC_GPIO2->FIODIR  |= ADDRESS_BUS_MASK | CS0_MASK | CS1_MASK;
    LPC_GPIO2->FIODIR  &=~(COMPACT_FLASH_CARD_DETECT_MASK);
    LPC_GPIO1->FIODIR  |= COMPACT_FLASH_RESET_MASK | COMPACT_FLASH_POWER_ENABLE_MASK | LED1_MASK | LED2_MASK | LED3_MASK | LED4_MASK;
    LPC_GPIO0->FIODIR  |= IORD_MASK  | IOWR_MASK ;

    COMPACT_FLASH_RESET_ACTIVE;
    COMPACT_FLASH_POWER_DISABLE;
    CS0_INACTIVE;
    CS1_INACTIVE;

    SysTick_Config(SystemCoreClock/100);
    NVIC_SetVector(SysTick_IRQn, (uint32_t)(&disk_timerproc));
}


void  CompactFlashIO_Test()
{

    SET_DATA_BUS_AS_OUTPUTS;
    while(1)
    {
         
        wait(0.1);
        COMPACT_FLASH_RESET_ACTIVE;
        COMPACT_FLASH_POWER_ENABLE;
        IOWR_ACTIVE;  
        IORD_ACTIVE;
        CS0_ACTIVE; 
        CS1_ACTIVE;
        SET_CF_ADDRESS(0x04);
        SET_CF_DATA(0x80); 
     
        wait(0.1);
        COMPACT_FLASH_RESET_INACTIVE;
        COMPACT_FLASH_POWER_DISABLE;
        IOWR_INACTIVE;  
        IORD_INACTIVE;
        CS0_INACTIVE;  
        CS1_ACTIVE;
        SET_CF_ADDRESS(0x00);
        SET_CF_DATA(0x00);   
    }
}



/*-----------------------------------------------------------------------*/
/* Read an ATA register                                                  */
/*-----------------------------------------------------------------------*/

static
BYTE read_ata (
    BYTE reg            /* Register to be read */
)
{
    BYTE rd;
    DWORD i;
     
    CS0_ACTIVE; 
    SET_DATA_BUS_AS_INPUTS;
    SET_CF_ADDRESS(reg); 
    IORD_ACTIVE;  
    __nop();
    __nop();
    __nop();
    __nop();
  
    GET_CF_DATA(rd); 
    __nop();
    __nop();
    __nop();
    __nop();
    IORD_INACTIVE;  
    CS0_INACTIVE; 
    #ifdef _CF_DEBUG_READ_ATA
      CF_DEBUG("rd 0x%2x\r\n",rd);
    #endif
    return rd;
}



/*-----------------------------------------------------------------------*/
/* Write a byte to an ATA register                                       */
/*-----------------------------------------------------------------------*/

static
void write_ata (
    BYTE reg,        /* Register to be written */
    BYTE dat        /* Data to be written */
)
{
    DWORD i;
    __nop();
    CS0_ACTIVE; 
    SET_DATA_BUS_AS_OUTPUTS;
    SET_CF_ADDRESS(reg);
    SET_CF_DATA(dat); 
    IOWR_ACTIVE;  
    __nop();
    __nop();
    __nop();
    __nop();
      IOWR_INACTIVE; 
      __nop();
      __nop();
      __nop();
      __nop();
    CS0_INACTIVE; 
    SET_DATA_BUS_AS_INPUTS;
}



/*-----------------------------------------------------------------------*/
/* Read a part of data block                                             */
/*-----------------------------------------------------------------------*/

static
void read_part (
    BYTE *buff,     /* Data buffer to store read data */
    BYTE ofs,        /* Offset of the part of data in unit of word */
    BYTE count        /* Number of word to pick up */
)
{
    BYTE c = 0, dl, dh;
    DWORD i;
    
    SET_CF_ADDRESS(REG_DATA);        /* Select Data register */
    __nop();
    __nop();
  
     SET_DATA_BUS_AS_INPUTS;   
    CS0_ACTIVE; 
    __nop();
    __nop();
  do {
        IORD_ACTIVE;        /* IORD = L */
        __nop();
        __nop();
        __nop();
        __nop();
         GET_CF_DATA(dl);                /* Read Even data */
        IORD_INACTIVE;        /* IORD = H */
        __nop();
        __nop();
        __nop();
        __nop();
         IORD_ACTIVE;        /* IORD = L */
        __nop();
        __nop();
        __nop();
        __nop();
        GET_CF_DATA(dh);                /* Read Odd data */
        IORD_INACTIVE;    /* IORD = H */
        __nop();
        __nop();
        __nop();
        __nop();
        if (count && (c >= ofs)) {    /* Pick up a part of block */
            *buff++ = dl;
            *buff++ = dh;
            count--;
        }
    } while (++c);
    CS0_INACTIVE; 
    
    read_ata(REG_ALTSTAT);
    read_ata(REG_STATUS);
}


/*-----------------------------------------------------------------------*/
/* Wait for Data Ready                                                   */
/*-----------------------------------------------------------------------*/

static
int wait_data (void)
{
    BYTE s;

    DiskProcTimer = 100;    /* Time out = 1 sec */
    do {
        if (!DiskProcTimer) return 0;            /* Abort when timeout occured */
        s = read_ata(REG_STATUS);        /* Get status */
    } while ((s & (BUSY|DRQ)) != DRQ);    /* Wait for BUSY goes low and DRQ goes high */

    read_ata(REG_ALTSTAT);
    return 1;
}





/*-----------------------------------------------------------------------*/
/* Initialize Disk Drive                                                 */
/*-----------------------------------------------------------------------*/

DSTATUS disk_initialize (
    BYTE drv        /* Physical drive nmuber (0) */
)
{
    if (drv) return STA_NOINIT;                /* Supports only single drive */

    Stat |= STA_NOINIT;

    for (DiskProcTimer = 10; DiskProcTimer; );                /* 100ms */

    if (Stat & STA_NODISK) return Stat;        /* Exit when socket is empty */

    /* Initialize CFC control port */
    COMPACT_FLASH_POWER_ENABLE;
    
    for (DiskProcTimer = 1;DiskProcTimer; );                /* 10ms */
    
    SET_DATA_BUS_AS_INPUTS;
    for (DiskProcTimer = 5; DiskProcTimer; );                /* 50ms */
    COMPACT_FLASH_RESET_INACTIVE;
    for (DiskProcTimer = 5; DiskProcTimer; );                /* 50ms */
    write_ata(REG_DEV, LBA);                /* Select Device 0 */
    DiskProcTimer = 200;
    do {                                    /* Wait for card goes ready */
        if (!DiskProcTimer) 
        {
            CF_DEBUG("Timeout waiting for card BUSY to go inactive\r\n");
            return Stat;
        }
    } while (read_ata(REG_STATUS) & BUSY);

    write_ata(REG_DEVCTRL, SRST | nIEN);    /* Software reset */
    for (DiskProcTimer = 2; DiskProcTimer; );                /* 20ms */
    write_ata(REG_DEVCTRL, nIEN);            /* Release software reset */
    for (DiskProcTimer = 2; DiskProcTimer; );                /* 20ms */
    DiskProcTimer = 200;
    do {                                    /* Wait for card goes ready */
        if (!DiskProcTimer)
        {
            CF_DEBUG("Timeout waiting for card DRDY\r\n");
            return Stat;
         }
    } while ((read_ata(REG_STATUS) & (DRDY|BUSY)) != DRDY);

    
    
    
    CF_DEBUG("Setting to 8-bit PIO MOD\r\n");
    write_ata(REG_FEATURES, 0x01);            /* Select 8-bit PIO transfer mode */
    write_ata(REG_COMMAND, CMD_SETFEATURES);
    DiskProcTimer = 100;
    do {
        wait(.25);
        if (!DiskProcTimer)
        {
            CF_DEBUG("Timeout waiting after trying to call the SETFEATURES command\r\n");
            return Stat;
        }
         
    } while (read_ata(REG_STATUS) & (BUSY | ERR));

    Stat &= ~STA_NOINIT;                    /* When device goes ready, clear STA_NOINIT */

    return Stat;
}



/*-----------------------------------------------------------------------*/
/* Return Disk Status                                                    */
/*-----------------------------------------------------------------------*/

DSTATUS disk_status (
    BYTE drv        /* Physical drive nmuber (0) */
)
{
    if (drv) return STA_NOINIT;        /* Supports only single drive */
    return Stat;
}



/*-----------------------------------------------------------------------*/
/* Read Sector(s)                                                        */
/*-----------------------------------------------------------------------*/

DRESULT disk_read (
    BYTE drv,        /* Physical drive nmuber (0) */
    BYTE *buff,        /* Data buffer to store read data */
    DWORD sector,    /* Sector number (LBA) */
    BYTE count        /* Sector count (1..255) */
)
{
    BYTE c;
    DWORD i;
    
    
    if (drv || !count) return RES_PARERR;
    if (Stat & STA_NOINIT) return RES_NOTRDY;

    /* Issue Read Setor(s) command */
    write_ata(REG_COUNT, count);
    write_ata(REG_SECTOR, (BYTE)sector);
    write_ata(REG_CYLL, (BYTE)(sector >> 8));
    write_ata(REG_CYLH, (BYTE)(sector >> 16));
    write_ata(REG_DEV, ((BYTE)(sector >> 24) & 0x0F) | LBA);
    write_ata(REG_COMMAND, CMD_READ);


    do {
        if (!wait_data()) return RES_ERROR;    /* Wait data ready */
       
    
        SET_CF_ADDRESS(REG_DATA);
         __nop();
        __nop();
        __nop();
        __nop();
          CS0_ACTIVE;
         c = 0;
       
          __nop();
         __nop();
        __nop();
    
        SET_DATA_BUS_AS_INPUTS;
        do {
            IORD_ACTIVE;        /* IORD = L */
             __nop();
            __nop();
            __nop();
                GET_CF_DATA(*buff++);        /* Get even data */
          __nop();
        __nop();
        __nop();
    
          __nop();
         __nop();
        __nop();

            IORD_INACTIVE;        /* IORD = H */
        __nop();
        __nop();
        __nop();
 
            IORD_ACTIVE;        /* IORD = L */
           __nop();
        __nop();
        __nop();

            GET_CF_DATA(*buff++);    /* Get Odd data */
          __nop();
        __nop();
        __nop();

        __nop();
            IORD_INACTIVE;            /* IORD = H */
     __nop();
        __nop();
        __nop();
  
           
        } while (--c);
    } while (--count);

    CS0_INACTIVE;
    read_ata(REG_ALTSTAT);
    read_ata(REG_STATUS);


    return RES_OK;
}


/*-----------------------------------------------------------------------*/
/* Write Sector(s)                                                       */
/*-----------------------------------------------------------------------*/

DRESULT disk_write (
    BYTE drv,            /* Physical drive number (0) */
    const BYTE *buff,    /* Data to be written */
    DWORD sector,        /* Sector number (LBA) */
    BYTE count            /* Sector count (1..255) */
)
{
    BYTE s, c;
    DWORD i;
    
    if (drv || !count) return RES_PARERR;
    if (Stat & STA_NOINIT) return RES_NOTRDY;

    /* Issue Write Setor(s) command */
    write_ata(REG_COUNT, count);
    write_ata(REG_SECTOR, (BYTE)sector);
    write_ata(REG_CYLL, (BYTE)(sector >> 8));
    write_ata(REG_CYLH, (BYTE)(sector >> 16));
    write_ata(REG_DEV, ((BYTE)(sector >> 24) & 0x0F) | LBA);
    write_ata(REG_COMMAND, CMD_WRITE);



    do {
        if (!wait_data()) return RES_ERROR;
      
        SET_CF_ADDRESS(REG_DATA);
         __nop();
         __nop();
         __nop();
        CS0_ACTIVE;
        __nop();
         __nop();
         __nop();
    
        SET_DATA_BUS_AS_OUTPUTS;
        c = 0;
        do {
            SET_CF_DATA(*buff++);    /* Set even data */
            __nop();
            __nop();
            __nop();

            IOWR_ACTIVE;        /* IOWR = L */
            __nop();
            __nop();
            __nop();

            IOWR_INACTIVE;        /* IOWR = H */
            __nop();
            __nop();
            __nop();
    
            SET_CF_DATA(*buff++);    /* Set odd data */
            __nop();
            __nop();
            __nop();
 
            IOWR_ACTIVE;        /* IOWR = L */
            __nop();
            __nop();
            __nop();

            IOWR_INACTIVE;        /* IOWR = H */
            __nop();
            __nop();
            __nop();

        } while (--c);
 
    } while (--count);
       SET_DATA_BUS_AS_INPUTS;
        CS0_INACTIVE;

    DiskProcTimer = 100;
    do {
        if (!DiskProcTimer) return RES_ERROR;
        s = read_ata(REG_STATUS);
    } while (s & BUSY);
    if (s & ERR) return RES_ERROR;

    read_ata(REG_ALTSTAT);
    read_ata(REG_STATUS);

    return RES_OK;
}


/*-----------------------------------------------------------------------*/
/* Miscellaneous Functions                                               */
/*-----------------------------------------------------------------------*/

DRESULT disk_ioctl (
    BYTE drv,        /* Physical drive nmuber (0) */
    BYTE ctrl,        /* Control code */
    void *buff        /* Buffer to send/receive data block */
)
{
    BYTE n, w, ofs, dl, dh, *ptr = (BYTE *)buff;


    if (drv) return RES_PARERR;
    if (Stat & STA_NOINIT) return RES_NOTRDY;

    switch (ctrl) {
        case GET_SECTOR_COUNT :    /* Get number of sectors on the disk (DWORD) */
            ofs = 60; w = 2; n = 0;
            break;

        case GET_BLOCK_SIZE :    /* Get erase block size in sectors (DWORD) */
            *(DWORD*)buff = 32;
            return RES_OK;

        case CTRL_SYNC :        /* Nothing to do */
            return RES_OK;

        case ATA_GET_REV :        /* Get firmware revision (8 chars) */
            ofs = 23; w = 4; n = 4;
            break;

        case ATA_GET_MODEL :    /* Get model name (40 chars) */
            ofs = 27; w = 20; n = 20;
            break;

        case ATA_GET_SN :        /* Get serial number (20 chars) */
            ofs = 10; w = 10; n = 10;
            break;

        default:
            return RES_PARERR;
    }

    write_ata(REG_COMMAND, CMD_IDENTIFY);
    if (!wait_data()) return RES_ERROR;
    read_part(ptr, ofs, w);
    while (n--) {
        dl = *ptr; dh = *(ptr+1);
        *ptr++ = dh; *ptr++ = dl; 
    }

    return RES_OK;
}


/*-----------------------------------------------------------------------*/
/* Device timer interrupt procedure                                      */
/*-----------------------------------------------------------------------*/
/* This function must be called in period of 10ms */

void disk_timerproc (void)
{
    static BYTE pv;
    BYTE n;

    LED2_TOGGLE;

    n = DiskProcTimer;                    /* 100Hz decrement timer */
    if (n) DiskProcTimer = --n;

    n = pv;
    pv = COMPACT_FLASH_CARD_DETECTED ;    /* Sapmle socket switch */

    if (n == pv) {                    /* Have contacts stabled? */
        if (!COMPACT_FLASH_CARD_DETECTED )
        {            /* CD1 or CD2 is high (Socket empty) */
            Stat |= (STA_NODISK | STA_NOINIT);
            SET_DATA_BUS_TO_INPUTS;        /* Float D0-D7 */
            COMPACT_FLASH_RESET_ACTIVE;    /* Assert RESET# */
            COMPACT_FLASH_POWER_DISABLE;   /* Power OFF */
            LED1_OFF; 
            
        } else {                    /* CD1 and CD2 are low (Card inserted) */
            Stat &= ~STA_NODISK;
            LED1_ON;
        }
    }
}


DWORD get_fattime(void)
{
    time_t CurrentTimeStamp;
    tm *CurrentLocalTime;
    DWORD FATFSTimeCode;
        
    CurrentTimeStamp = time(NULL);
    CurrentLocalTime = localtime(&CurrentTimeStamp);
        
        //Map the tm struct time into the FatFs time code    
    FATFSTimeCode =  ((CurrentLocalTime->tm_year-80)<<25) | 
                     ((CurrentLocalTime->tm_mon+1)<<21)   | 
                     ((CurrentLocalTime->tm_mday)<<16)    | 
                     ((CurrentLocalTime->tm_hour)<<11)    |
                     ((CurrentLocalTime->tm_min)<<5)     | 
                     ((CurrentLocalTime->tm_sec));

   return FATFSTimeCode;
}