Parallel bus (address 8 bit, data 8 bit) access sample using GPIO pins.

Dependents:   mini_board_PCU9669_demo mini_board_PCU9669

Parallel bus emulation sample code

mbed (LPC1768) does not have parallel bus but it can be emulated by GPIO.
Please find a notebook page for more infomation.

/media/uploads/nxp_ip/parallel-block-diagram.png

/media/uploads/nxp_ip/parallel_access_opt.png

hardware_abs.c

Committer:
nxp_ip
Date:
2012-07-12
Revision:
2:8f5c7901d52a
Parent:
0:d6ec2b9171cb
Child:
3:a3e71822a7a3

File content as of revision 2:8f5c7901d52a:


/** A sample code for "mini board PCU9669/PCA9665"
 *
 *  @author  Akifumi (Tedd) OKANO, NXP Semiconductors
 *  @version 1.0
 *  @date    26-Mar-2012
 *
 *    ** Parallel bus accessing library version 2.0 (12-Jul-2012)
 *    ** version 2.0 has been made for speed optimization by register level control
 *    ** And it makes parallel bus independent from mbed-library (PortOut, PortInOut)
 *
 *  Released under the MIT License: http://mbed.org/license/mit
 *
 *  An operation sample of PCU9669/PCA9665 I2C bus controller.
 *  The mbed accesses the bus controller's parallel port (8/2 bit address and 8 bit data) by bit-banging.
 *  The bit-banging is poerformed by PortInOut function of mbed library.
 *
 *    To make the code porting easier, all codes are partitioned into layers to abstract other parts.
 *    The mbed specific parts are concentrated in lowest layer: "hardware_abs.*".
 *    This module may need to be modified for the porting.
 *
 *    All other upper layers are writen in standard-C.
 *
 *    base code is written from 05-Sep-2011 to 09-Sep-2011.
 *    And demo code has been build on 11-Sep-2011.
 *    Debug and code adjustment has been done on 08-Sep-2011.
 *    Small sanitization for main.cpp. All mbed related codes are moved in to "hardware_abs.*". 13-Oct-2011
 *    hardware_abs are moved into parallel_bus library folder, 3 LED driver operation sample 13-Feb.-2012
 *    PCU9669 and PCA9665 codes are packed in a project 14-Feb-2012.
 *
 *    Before builidng the code, please edit the file mini_board_PCU9669/config.h
 *    Un-comment the target name what you want to target.
 */

/*
 *  "hardware_abs" module has been made to abstract hardware: Hardware abstract layer
 *  This is file which will be modified when the porting done for other MCUs.
 */

/*
 *  This sample code has been made for mbed. The code emulates parallel SRAM bus using the mbed's GPIO ports.
 *  To maximize the port access speed, PortOut and PortInOut libraly used.
 */

#include    "mbed.h"
#include    "hardware_abs.h"

void register_check( LPC_GPIO_TypeDef *p ) {
    printf( "%p\r\n", p );
    printf( "  %p->FIODIR  0x%08X\r\n", p, p->FIODIR );
    printf( "  %p->FIOMASK 0x%08X\r\n", p, p->FIOMASK );
    printf( "  %p->FIOPIN  0x%08X\r\n", p, p->FIOPIN );
    printf( "  %p->FIOSET  0x%08X\r\n", p, p->FIOSET );
    printf( "  %p->FIOCLR  0x%08X\r\n", p, p->FIOCLR );
}

//  GPIO port setting

#define     ADDR_MASK       0x07878000  //  8 bit address mask on PORT0: Address value will be set into this bit position
#define     DATA_MASK       0x00000FF0  //  8 bit data mask on PORT0: Data value will be appeared on this bit position
#define     CONTROL_MASK    0x00000038  //  Control signals CS=bit5(pin21), WR=bit4(pin22), RD=bit3(pin23)

PortOut     addr_port( Port0, ADDR_MASK );
PortInOut   data_port( Port0, DATA_MASK );
PortOut     ctrl_port( Port2, CONTROL_MASK );
InterruptIn int_port( p26 );

//  The very early version of PCU9669 mini board has different configuration.
//  Following part switches the port configuration for those board versions.

//#define     PROTOTYPE_BREADBOARD

DigitalInOut    reset_port( p20 );
//DigitalOut      trig_port( p19 );

//  Next two macros defines interface for temporaly interrupt disable/enable functions.
//  These macros are used to blocking interrupt until a bus access cycle completed.
//  Because if the interrupt happened in a cycle, the emulated parallel port state will be disturbed.
//  For the mbed, library routine: __disable_irq() / __enable_irq() can be used.

#define     interrupt_enable()    __enable_irq()
#define     interrupt_disable()   __disable_irq()

//  "prev_access_was_write" is a variable to keep the previous data directions.
//  Using this flag, successive write and read addess overhead can be reduced.
//  This mechanism will not be required if the MCU has parallel port

char        prev_access_was_write;

//  ISR routine installer

void install_ISR( void (*fptr)(void) ) {
    int_port.fall( fptr );
}

//  Hardware initialize: it defines initial state of the (emulated) parallel bus

void hardware_initialize( void ) {
    prev_access_was_write   = 0;
    ctrl_port               = 0x38;     //  CS, WR and RD are deaserted (HIGH)
    data_port.input();                  //  data bus set to Hi-Z
    reset_port.output();
    reset_port.mode( PullUp );
}

//  Hardware reset for PCU9669: It asserts RESET signal for 4us and waits 650us after deassert (reset recovery time)

void reset( int reset_pulse_width_us, int reset_recovery_us ) {
    hardware_reset( ASSERT );            //  Minimum pulse width is 4us for PCU9669
    hw_wait_us( reset_pulse_width_us );
    hardware_reset( DEASSERT );          //  deassert hardware /RESET sgnal
    hw_wait_us( reset_recovery_us );
}

//  Interface to Control hardware RESET signal

void hardware_reset( char signal ) {
    reset_port  = signal;
}

//  Interface to Control hardware TRIGGER signal

void hardware_trigger( char signal ) {
//    trig_port  = signal;
}

//  Single write cycle on (emulated) parallel bus

void write_data( char addr, char data ) {
    unsigned long   addr_data;

    interrupt_disable();    //  disable interrupt first

    addr_data   = (addr << 19) | (addr << 15) | (data << 4);

    LPC_GPIO0->FIOMASK  = ~(ADDR_MASK | DATA_MASK);
    LPC_GPIO0->FIODIR   = ADDR_MASK;
    LPC_GPIO0->FIOSET   = addr_data;
    LPC_GPIO0->FIOCLR   = ~addr_data;

    LPC_GPIO2->FIOCLR   = 0x20; //  Assert CS signal

    LPC_GPIO0->FIODIR   = ADDR_MASK | DATA_MASK;

    LPC_GPIO2->FIOCLR   = 0x30; //  repeating register write to keep pulse width wide :)

    LPC_GPIO0->FIOSET   = addr_data;
    LPC_GPIO0->FIOCLR   = ~addr_data;

    LPC_GPIO2->FIOCLR   = 0x30; //  to keep timing
    LPC_GPIO2->FIOCLR   = 0x30; //  to keep timing
    LPC_GPIO2->FIOCLR   = 0x30; //  to keep timing
    LPC_GPIO2->FIOCLR   = 0x30; //  to keep timing
    LPC_GPIO2->FIOSET   = 0x38;

    LPC_GPIO0->FIODIR   = ADDR_MASK;

    interrupt_enable();    //  enable interrupt again
}


char read_data( char addr ) {
    unsigned long   addr_data;
    volatile char   tmp;

    interrupt_disable();    //  disable interrupt first

    LPC_GPIO0->FIODIR   = ADDR_MASK;

    LPC_GPIO0->FIOMASK   = ~ADDR_MASK;
    addr_data   = (addr << 19) | (addr << 15);
    LPC_GPIO0->FIOSET   = addr_data;
    LPC_GPIO0->FIOCLR   = ~addr_data;

    LPC_GPIO2->FIOCLR   = 0x28; //  Assert CS and RD signals
    LPC_GPIO2->FIOCLR   = 0x28; //  to keep timing
    LPC_GPIO2->FIOCLR   = 0x28; //  to keep timing
    LPC_GPIO2->FIOCLR   = 0x28; //  to keep timing

    LPC_GPIO0->FIOMASK  = ~DATA_MASK;
    tmp                 = (LPC_GPIO0->FIOPIN >> 4) & 0xFF;      //  Read data bus into var

    LPC_GPIO2->FIOSET   = 0x38;

    interrupt_enable();    //  enable interrupt again

    return ( tmp );
}

//  Wait for micro-seconds

void hw_wait_us( int v ) {
    wait_us( v );
}

//  Wait for seconds

void wait_sec( float f ) {
    wait( f );
}

//  Following part is an optionto accerelate bus access.
//  If such trick is not required, undefine the "BURST_DATA_ACCESS" and don't touch it.
//
//  Next two functions access single address with repeating read/write.
//  The repeating read/write are used often for PCU9669 like SLATABLE, TRANCONFIG and DATA (buffer accesses).
//  So this accerelation contributes saving MCU time.
//
//  For the porting, it may be good idea to modify those routines to DMA access.

#ifdef  BURST_DATA_ACCESS

void write_data_burst( char addr, char *data, char length ) {
    unsigned long   addr_data;
    int             i;

    interrupt_disable();    //  disable interrupt first

    addr_data   = (addr << 19) | (addr << 15);

    LPC_GPIO0->FIOMASK  = ~ADDR_MASK;
    LPC_GPIO0->FIODIR   = ADDR_MASK;
    LPC_GPIO0->FIOSET   = addr_data;
    LPC_GPIO0->FIOCLR   = ~addr_data;

    LPC_GPIO2->FIOCLR   = 0x20; //  Assert CS signal

    LPC_GPIO0->FIODIR   = ADDR_MASK | DATA_MASK;
    LPC_GPIO0->FIOMASK  = ~DATA_MASK;

    for ( i = 0; i < length; i++ ) {    //  repeat data read access

        addr_data   = *(data + i) << 4;

        LPC_GPIO2->FIOCLR   = 0x30; //  repeating register write to keep pulse width wide :)

        LPC_GPIO0->FIOCLR   = ~addr_data;
        LPC_GPIO0->FIOSET   = addr_data;

        LPC_GPIO2->FIOCLR   = 0x30; //  to keep timing
        LPC_GPIO2->FIOCLR   = 0x30; //  to keep timing
        LPC_GPIO2->FIOCLR   = 0x30; //  to keep timing
        LPC_GPIO2->FIOSET   = 0x10;
    }
        LPC_GPIO2->FIOSET   = 0x38;

    LPC_GPIO0->FIODIR   = ADDR_MASK;
    interrupt_enable();    //  enable interrupt again
}

void read_data_burst( char addr, char *data, char length ) {
    unsigned long   addr_data;
    int             i;

    interrupt_disable();    //  disable interrupt first

    addr_data   = (addr << 19) | (addr << 15);

    LPC_GPIO0->FIOMASK  = ~ADDR_MASK;
    LPC_GPIO0->FIODIR   = ADDR_MASK;
    LPC_GPIO0->FIOSET   = addr_data;
    LPC_GPIO0->FIOCLR   = ~addr_data;

    LPC_GPIO0->FIOMASK  = ~DATA_MASK;

    for ( i = 0; i < length; i++ ) {    //  repeat data read access
        LPC_GPIO2->FIOCLR   = 0x28; //  Assert CS and RD signals
        LPC_GPIO2->FIOCLR   = 0x28; //  to keep timing
        LPC_GPIO2->FIOCLR   = 0x28; //  to keep timing
        LPC_GPIO2->FIOCLR   = 0x28; //  to keep timing

        *(data + i)         = (LPC_GPIO0->FIOPIN >> 4) & 0xFF;      //  Read data bus into var
        LPC_GPIO2->FIOSET   = 0x08;
    }
        LPC_GPIO2->FIOSET   = 0x38;

    LPC_GPIO0->FIODIR   = ADDR_MASK;
    interrupt_enable();    //  enable interrupt again
}

#endif