ascii image


0010101000011111101001101010000010011000
1110101110110101011011111010010010001011
0001011100011101111001010011010010111110
0000010111101001100000110011101100001000
0011000000111010001111111000100110000001
1010110110000000000001011001000110001010
0101100010101100000100000010100100010101
0001011001011101100011000101110111101110
0110010100110100111101110100110011111101
0010111100110011010010110010101111011011
0100000000001001001011000010110100101001
1101000111100000110111011100110111000010
1111110001111111101101001010000111101100
0010110000100000111011000000101100010110
0101111000011100111010000000011111101111
0010010011110010011101001000110101000101
0000000001000100001111111100111010001111

Friday 31 December 2010

Homemade USB LCD

I used a FT245RL to power a LCD i ripped from a dead UPS.




I was surprised on how easy it was to do, though this is all to do with the FT chip :)


Nicely 'hacked' into the back with rubber bands, hehe

And the circuit if anyone wants it...

And the C code to run it...  (pity blogspot don't syntax color the code)

*EDIT: actually they do see http://lukabloga.blogspot.com/2008/10/to-test-new-highlighting.html
/*        
    To build use the following gcc statement 
    gcc -o LCD LCD.c -L. -lftd2xx -Wl,-rpath /usr/local/lib
*/

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <ftd2xx.h>

//#include <ctype.h>

#include <unistd.h>
#include <string.h>

// Globals

#define BANNER1 "LCD VERISON:1.0  mohclips kiwi-hacker.net\n"

#define TOPLINE 1    // we have test to write on the top line
#define BOTTOMLINE 2  // we have text to write on the bottom line

FT_HANDLE ftHandle = NULL;

FT_STATUS    ftStatus;
unsigned char ucMode = 0x00;

unsigned char backlight = 0; // turn on 1 /off 0 lcd backlight

//LCD Registers addresses
// ---data----  RS   En   BL   NC
//  1  2  4  8  0x10 0x20 0x40 0x80
// d0 d1 d2 d3  d4   d5   d6   d7

#define LCD_RS      0x10  //20 = d5 on FT245RL pin 9 = LCD pin 4
#define LCD_EN      0x20  //80 = d7 on FT245RL pin 6 = LCD pin 6

#define BACKLIGHT 0x40

#define LCD_MASK    0x0F

/*
AN232R-01 Bit Bang Modes for the FT232R and FT245R

The clock for the (a)Synchronous Bit Bang mode is actually 16 times the Baud rate. 
A value of 9600 Baud would transfer the data at (9600x16) = 153600 bytes per second, 
or 1 every 6.5us.

For the data to change there, has to be new data written and the Baud rate clock has to tick. 
If no new data is written to the device, the pins will hold the last value written.

19200 baud = 38400*16 = 614400 bytes/sec = 0.000001627604167 = 1 every 1.6 us 
19200 baud = 19200*16 = 307200 bytes/sec = 0.000003255208333 = 1 every 3.3 us 
9600 baud = 9600*16 = 153600 bytes/sec =   0.000006510416667 = 1 every 6.5 us 
2400 baud = 16*2400 = 38400 bytes/sec =    0.000026041666667 = 1 every 26 us
1200 baud = 16*1200 = 19200 bytes/sec =    0.000052083333333 = 1 every 52 us
*/
#define BAUD_RATE    9600
#define ASYNC_BITBANG 0x01
#define SYNC_BITBANG 0x00    // you don't want this

#define delay_multiplier 1;

/*
http://www.microcontrollerboard.com/lcd.html

Register Selector
RS     R/W     
0     0     Sends a command to LCD
1     0     Sends information to LCD

R/W is always set low (0) by grounding it

E == 1 => process data on bus
*/

void bin_prnt_byte(int x)
{
   int n;
   for(n=0; n<8; n++)
   {
      if((x & 0x80) !=0)
      {
         printf("1");
      }
      else
      {
         printf("0");
      }
      if (n==3)
      {
         printf(" "); /* insert a space between nybbles */
      }
      x = x<<1;
   }
   printf("\t");
}


void delayus(int microsecs)
{
//http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man3/usleep.3.html
//suspend thread execution for an interval measured in microseconds

    unsigned int us_sleep;
    us_sleep = microsecs * delay_multiplier;
    
    //printf("sleeping %d us\n",us_sleep);
    
    usleep( us_sleep );
}

void delayms(int millisecs)
{
//http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man3/usleep.3.html
//suspend thread execution for an interval measured in microseconds

    unsigned int us_sleep;
    us_sleep = millisecs * 1000 * delay_multiplier;
    
    //printf("sleeping %d us\n",us_sleep);
    
    usleep( us_sleep );
}

void lcd_read() {
    ftStatus = FT_GetBitMode(ftHandle, &ucMode); // get values on the D0-7 pins
    if(ftStatus != FT_OK) {
        printf("Failed to get bit mode\n");
    } else {
        printf("Ports Show: %x\n",ucMode);
    };
};

void lcd_port(unsigned char out)
{

    #define BUF_SIZE 1 // send 1 char out to driver

    DWORD     dwBytesWritten;
    unsigned char rs;
    unsigned char en;
    
    rs = out & LCD_RS;
    en = out & LCD_EN;
    
    // send 1 char out to driver

    if (backlight) {
        out |= BACKLIGHT; //turn on the backlight
    };
    
    //bin_prnt_byte(out);
    //printf("FT_Write: 0x%02x, RS 0x%02x, EN 0x%02x\n", out, rs, en);

    
    if((ftStatus = FT_Write(ftHandle, &out, BUF_SIZE, &dwBytesWritten)) != FT_OK) {
        printf("Error writing to FTDI driver %ld", ftStatus);
    };
    
    if (dwBytesWritten != 1) {
        printf("Error data not written to ports : bytes written = %ld\n",dwBytesWritten);
    };
    
    
    
    //printf("Bytes Written: %ld\n",dwBytesWritten);
    
    //ftStatus = FT_GetBitMode(ftHandle, &ucMode); // get values on the D0-7 pins

    //printf("Wrote: %02x\n",ucMode);

}

void lcd_4bit_data (unsigned char dat)
{
        unsigned char hi, lo;
        
        hi = (dat >> 4) & LCD_MASK;
        lo = dat & LCD_MASK;

        //printf("4-bit data %x hi, lo  %x, %x\n",dat, hi, lo);

            
        lcd_port ( hi | LCD_RS | LCD_EN ); // data mode - pulse En Hi->Lo
        // 450ns strobe
        lcd_port ( hi | LCD_RS );         // data mode
  
          delayms(1);                    // 200us for data writes
  
        lcd_port ( lo | LCD_RS | LCD_EN );  // data mode - pulse En Hi->Lo
        lcd_port ( lo | LCD_RS );           // data mode

        delayms(1);                    // 200us for data writes

}


void lcd_8bit_cmd (char cmd) 
{
        //printf("8-bit cmd %x\n",cmd);

       lcd_port ( cmd | LCD_EN );          // pulse En Hi->Lo
       lcd_port ( cmd );

        // we don't delay here, must be done elsewhere
}

//http://www.8051projects.net/lcd-interfacing/lcd-4-bit-programming.php
// timing from http://joshuagalloway.com/lcd.html - waits between nibbles
void lcd_4bit_cmd (char cmd)
{
        unsigned char hi, lo;
        
        
        hi = (cmd >> 4) & LCD_MASK;
        lo = cmd & LCD_MASK;

        //printf("4-bit cmd %x hi, lo  %x, %x\n",cmd, hi, lo);

        lcd_port ( hi | LCD_EN );          // pulse En Hi->Lo
        lcd_port ( hi );

        delayms(5);                     // Wait 5ms for command writes

        lcd_port ( lo | LCD_EN );          // pulse En Hi->Lo
        lcd_port ( lo );

        delayms(5);                     // Wait 5ms for command writes
}


void lcd_reset()
{
/* 
 The reset process is extremely important
 otherwise you just get garbage on the LCD

 http://www.doc.ic.ac.uk/~ih/doc/lcd/initiali.html

 http://www.repairfaq.org/filipg/LINK/F_Tech_LCD4.html
The module powers up in 8-bit mode. The initial startup instructions are sent in 8-bit mode, 
with the lower four bits (which are not connected) of each instruction as don't cares.

After the fourth instruction, which switches the module to 4-bit operation, 
the control bytes are sent on consecutive enable cycles (no delay is required between nybbles). 
Most significant is sent first, followed immediately by least significant nybble. 
** which is opposite to other sites say :(



*/
        delayms(100);                // Wait 20ms for LCD to power up
        
        //lcd_8bit_cmd( Set_Function + Data_Length_8);
        lcd_8bit_cmd( 0x3 );
        delayms(5);
                           // delay should be 4.1ms
        
        //lcd_8bit_cmd ( Set_Function + Data_Length_8);
        lcd_8bit_cmd( 0x3 );
        delayms(5);
                        // delay should be 100us
        
        //lcd_8bit_cmd ( Set_Function + Data_Length_8);
        lcd_8bit_cmd( 0x3 );
        delayms(5);
                        // delay should be 4.1ms
        
        //lcd_8bit_cmd ( Set_Function + Data_Length_4);
        lcd_8bit_cmd( 0x2 );
        delayms(5);
                        // delay should be 40us
}


void lcd_init ()
{
        // http://www.8051projects.net/lcd-interfacing/commands.php
        
        // nearly all LCDs always start in 8-bit mode
        lcd_reset();         // Call LCD reset

        // from here we are in 4-bit mode
               
        lcd_4bit_cmd(0x28);
        lcd_4bit_cmd(0x08);
        lcd_4bit_cmd(0x01);
        lcd_4bit_cmd(0x06);
        lcd_4bit_cmd(0x0C);
 }







void quit()
{  
    if(ftHandle != NULL) {
        FT_Close(ftHandle);
        ftHandle = NULL;
        printf("Closed device\n");
    }

    exit(1);
}

void usage()
{

    printf(BANNER1);
    printf("\nUsage: LCD [-1 -2 -h -?]\n");
    printf("\t-1 \"string\" = write up to 16 chars to the top line of the LCD\n");
    printf("\t-2 \"string\" = write up to 16 chars to the bottom line of the LCD\n");
    printf("\t-? -h = this help\n\n");

}

int main(int argc, char *argv[])
{
    int iport;
    int c; // getopt
    int j;
    unsigned char topline[16];
    unsigned char bottomline[16];
    int opts = 0;

    iport = 0; // only one FT245 allowed at present

    signal(SIGINT, quit);        // trap ctrl-c call quit fn 

  while( (c = getopt(argc, argv, "h?1:2:")) != -1 )
  {
    /* Process the command line arguments */
    switch( c )
    {
      case '1': if( optarg )            /* Configuration file    */
        {
          strncpy( topline, optarg, sizeof( topline ) );
          opts |= TOPLINE;
        }
        break;

      case '2': if( optarg )            /* Configuration file    */
        {
          strncpy( bottomline, optarg, sizeof( bottomline ) );
          opts |= BOTTOMLINE;
        }
        break;
    
      case 'h':
      case '?': usage();
              exit(-1);
              break;
              
      default: break;
        
    } //switch
  } // get opt

    
    ftStatus = FT_Open(iport, &ftHandle);
    if(ftStatus != FT_OK) {
        /* 
            This can fail if the ftdi_sio driver is loaded
             use lsmod to check this and rmmod ftdi_sio to remove
            also rmmod usbserial
         */
        printf("FT_Open(%d) failed = %ld\n", iport, ftStatus);
        return 1;
    }
        
    ftStatus = FT_SetBitMode(ftHandle, 0xFF, 0x01);  // ASYNC BIT BANG across all ports
    if(ftStatus != FT_OK) {
        printf("Failed to set bit mode\n");    
    }
    
    
    ftStatus = FT_SetBaudRate(ftHandle, 9600);//BAUD_RATE
    if(ftStatus != FT_OK) {
        printf("Failed to set baud rate\n");
    }

    // turn on backlight        
//    backlight=1;
//    lcd_port(0);
    
    // init the LCD - very important
    lcd_init();
    
    if ( opts & TOPLINE ) {
        // we always write to the top line as default
        for(j = 0; j < strlen(topline); j++) {
            lcd_4bit_data(topline[j]);
        }
    }

    if ( opts & BOTTOMLINE ) {
        lcd_4bit_cmd(0x80 + 0x40); // 0x40 = address of bottom line
        
        for(j = 0; j < strlen(bottomline); j++) {
            lcd_4bit_data(bottomline[j]);
        }
    }


    // turn off backlight    
//    backlight=0;
//    lcd_port(0);

    
    ftStatus = FT_ResetDevice(ftHandle);
    if(ftStatus != FT_OK) {
        printf("Failed reset device\n");
    };
    if(ftHandle != NULL) {
        ftStatus = FT_Close(ftHandle);
        if(ftStatus != FT_OK) {
        printf("Failed close device\n");
    };
        ftHandle = NULL;
    }
    
    //return 0;
}

No comments:

Post a Comment