This is a sample program to drive a 128x128 LCD with t6963 controller through SPI by means of an MCP23S17 16-Bit I/O Expander with Serial Interface

Dependencies:   mbed

main.cpp

Committer:
gertk
Date:
2011-10-17
Revision:
4:fd7c6559a56d
Parent:
3:fc101c00b5be

File content as of revision 4:fd7c6559a56d:

// Demo program displaying an analog clock
// on a T6963 based LCD connected to the mbed through 
// SPI with a MCP23S17 thereby using only 4 pins I/O
// by Gert van der Knokke 2010
#include "mbed.h"
#include "mcp_lcd.h"

// for 21 characters on a row (6x8 font)
#define LCDFONTSEL  0xFF
// for 16 characters on a row (8x8 font)
// #define LCDFONTSEL 0xDF

// lcd dimensions in pixels
#define LCD_XWIDTH     128
#define LCD_YHEIGHT    128

#if LCDFONTSEL == 0xFF
// lcd dimensions in characters
#define LCD_WIDTH   22
#define LCD_HEIGHT  16
#define PIXELWIDTH  6
#else
#define LCD_WIDTH   16
#define LCD_HEIGHT  16
#define PIXELWIDTH  8
#endif

#define TEXT_STARTADDRESS       0x0000
#define GRAPHIC_STARTADDRESS    0x1000

   
#define CENTERX 64
#define CENTERY 64
#define INNER_RADIUS    45
#define OUTER_RADIUS    50
#define CENTER_CIRCLE   5

DigitalOut myled(LED1);
SPI spi(p5, p6, p7); // mosi, miso, sclk
DigitalOut cs(p20);

Serial pc(USBTX, USBRX); // tx, rx


// write 8 bits lcd data
void lcd_data(unsigned char d)
{
    cs=0;
    spi.write(0x40);
    spi.write(GPIOB);  // select GPIOB
    spi.write(d);      // set data byte
    cs=1;

    cs=0;
    spi.write(0x40);
    spi.write(GPIOA);  // select GPIOA
    spi.write(LCDFONTSEL-LCD_CE-LCD_CD);   
    cs=1;

    cs=0;
    spi.write(0x40);
    spi.write(GPIOA);  // select GPIOA
    spi.write(LCDFONTSEL - LCD_WR - LCD_CE - LCD_CD);   
    cs=1;

    cs=0;
    spi.write(0x40);
    spi.write(GPIOA);  // select GPIOA
    spi.write(LCDFONTSEL - LCD_CD);   
    cs=1;
    
}

// write 8 bits lcd command
void lcd_command(unsigned char c)
{
    cs=0;
    spi.write(0x40);
    spi.write(GPIOB);  // select GPIOB
    spi.write(c);      // set data byte
    cs=1;

    cs=0;
    spi.write(0x40);
    spi.write(GPIOA);  // select GPIOA
    spi.write(LCDFONTSEL-LCD_CE);   
    cs=1;

    cs=0;
    spi.write(0x40);
    spi.write(GPIOA);  // select GPIOA
    spi.write(LCDFONTSEL - LCD_WR - LCD_CE);   
    cs=1;

    cs=0;
    spi.write(0x40);
    spi.write(GPIOA);  // select GPIOA
    spi.write(LCDFONTSEL);   
    cs=1;
}

void lcd_init()
{
    cs=0;
    spi.write(0x40);
    spi.write(IODIRA);  // select IODIRA at start
    spi.write(0x00);    // IODIRA all outputs
    spi.write(0x00);    // IODIRB all outputs
    cs=1;
    wait(0.1);
    
    cs=0;
    spi.write(0x40);
    spi.write(GPIOA);  // select GPIOA at start
    spi.write(LCDFONTSEL-LCD_RST);    // activate reset
    spi.write(0x00);    // all B outputs 0
    cs=1;
    wait(0.1);

    cs=0;
    spi.write(0x40);
    spi.write(GPIOA);  // select GPIOA at start
    spi.write(LCDFONTSEL);    // deactivate reset
    cs=1;
    wait(0.1);

    // set text home address at 0x0000
    lcd_data(TEXT_STARTADDRESS%0x100);
    lcd_data(TEXT_STARTADDRESS/0x100);
    lcd_command(TXHOME);
    
    // set graphic home address at 0x1000
    lcd_data(GRAPHIC_STARTADDRESS%0x100);
    lcd_data(GRAPHIC_STARTADDRESS/0x100);
    lcd_command(GRHOME);
    
    // set text area 
    lcd_data(LCD_WIDTH);
    lcd_data(0x00);
    lcd_command(TXAREA);

    // set graphic area
    lcd_data(LCD_WIDTH);
    lcd_data(0x00);
    lcd_command(GRAREA);
    
    // mode set (internal character generation mode)
    lcd_command(0x80);
    
    // set offset register
    lcd_data(0x02);
    lcd_data(0x00);
    lcd_command(OFFSET);
    
    // display mode (text on graphics on cursor off)
    lcd_command(0x90+0x08+0x04);
        
}

// put a text string at position x,y (character row,column)
void lcd_string(char x,char y,char *s)
{
    int adr;
    adr=TEXT_STARTADDRESS+x+y*LCD_WIDTH;
    lcd_data(adr%0x100);
    lcd_data(adr/0x100);
    lcd_command(ADPSET);
    lcd_command(AWRON);
    
    while (s[0])
    {
        // convert from ascii to t6963
        lcd_data(s[0]-32);
        s++;
    }
    lcd_command(AWROFF);
}

// clear lcd display memory (8k)        
void lcd_cls()
{
    int a;
    lcd_data(0x00);
    lcd_data(0x00);
    lcd_command(ADPSET);
    lcd_command(AWRON);
    for (a=0; a<8192; a++) lcd_data(0);
    lcd_command(AWROFF);
}

// set or reset a pixel on the display on position x,y with color 0 or 1
void lcd_plot(char x,char y,char color)
{
    int adr;                         
    adr = GRAPHIC_STARTADDRESS + ((LCD_WIDTH) * y) + (x/PIXELWIDTH);   // calculate offset
    lcd_data(adr%0x100);       // set low byte
    lcd_data(adr/0x100);       // set high byte
    lcd_command(ADPSET);           // set address pointer
    if (color) lcd_command(BS + ((PIXELWIDTH-1)-(x%PIXELWIDTH)));   // use bit set mode
        else  lcd_command(BR + ((PIXELWIDTH-1)-(x%PIXELWIDTH)));  // use bit reset mode
}

// Bresenham line routine
void lcd_line(int x0, int y0, int x1, int y1,char color)
{
    char steep=1;
    int i,dx,dy,e;
    signed char sx,sy;
    
    dx = abs(x1-x0);
    sx = ((x1 - x0) >0) ? 1 : -1;
    dy=abs(y1-y0);
    sy = ((y1 - y0) >0) ? 1 : -1;
    
    if (dy > dx)
    {
        steep=0;
        // swap X0 and Y0
        x0=x0 ^ y0;
        y0=x0 ^ y0;
        x0=x0 ^ y0;

        // swap DX and DY
        dx=dx ^ dy;
        dy=dx ^ dy;
        dx=dx ^ dy;

        // swap SX and SY
        sx=sx ^ sy;
        sy=sx ^ sy;
        sx=sx ^ sy;
    }

    e = (dy << 1) - dx;

    for (i=0; i<=dx; i++)
    {
        if (steep)
        {
            lcd_plot(x0,y0,color);
        }
        else
        {
            lcd_plot(y0,x0,color);
        }
        while (e >= 0)
        {
            y0 += sy;
            e -= (dx << 1);
        }
        x0 += sx;
        e += (dy << 1);
    }
 }

// Bresenham circle routine
void lcd_circle(int x0,int y0, int radius, char color)
{

    int f = 1 - radius;
    int dx = 1;
    int dy = -2 * radius;
    int x = 0;
    int y = radius;
 
    lcd_plot(x0, y0 + radius,color);
    lcd_plot(x0, y0 - radius,color);
    lcd_plot(x0 + radius, y0,color);
    lcd_plot(x0 - radius, y0,color);
 
    while(x < y)
    {
        if(f >= 0) 
        {
            y--;
            dy += 2;
            f += dy;
        }
        x++;
        dx += 2;
        f += dx;    
        lcd_plot(x0 + x, y0 + y,color);
        lcd_plot(x0 - x, y0 + y,color);
        lcd_plot(x0 + x, y0 - y,color);
        lcd_plot(x0 - x, y0 - y,color);
        lcd_plot(x0 + y, y0 + x,color);
        lcd_plot(x0 - y, y0 + x,color);
        lcd_plot(x0 + y, y0 - x,color);
        lcd_plot(x0 - y, y0 - x,color);
    }
}

int main() {

    
    float a,b,f;
    float pi=3.14159265;
    float h_pi=pi/6;
    float m_pi=pi/30;
    
        char buf[40];
    time_t seconds;
    int sec,min,hour;
    int s_sx,s_sy,s_ex,s_ey;
    int m_sx,m_sy,m_ex,m_ey;
    int h_sx,h_sy,h_ex,h_ey;
    
    // setup time structure
    struct tm t;
    t.tm_sec = 00;    // 0-59
    t.tm_min = 30;    // 0-59
    t.tm_hour = 21;   // 0-23
    t.tm_mday = 14;   // 1-31
    t.tm_mon = 11;     // 0-11
    t.tm_year = 110;  // year since 1900
    seconds = mktime(&t);
    // set_time(seconds);


    pc.printf("SPI test\n");
    
    // set SPI to full speed (10 MHz mode)
    spi.format(8,0);
    spi.frequency(10000000);
//    spi.frequency(10000);
    wait(0.1);

    pc.printf("MCP init\n");
    lcd_init();
    lcd_cls();

    // write some text 345678901234567890   
    lcd_string(0,0,"* Hello mbed World! *");
    // lcd_string(0,15,"abcdefghijklmnopqrstuvwxyz");
   
    
    // draw outer circle of analog clock
    lcd_circle(CENTERX,CENTERY,OUTER_RADIUS+1,1);    
   
   // draw hour markings  
    for (min=0; min<59; min+=5)
    {
        b=min*m_pi;
        m_sx=sin(b)*INNER_RADIUS+CENTERX;
        m_sy=-cos(b)*INNER_RADIUS+CENTERY;
        m_ex=sin(b)*OUTER_RADIUS+CENTERX;
        m_ey=-cos(b)*OUTER_RADIUS+CENTERY;
        lcd_line(m_sx,m_sy,m_ex,m_ey,1);
    }
        

    for(;;)
    {
        seconds = time(NULL);
        
        //                13:24:00   dd/mm/yyyy
        strftime(buf,40, "%H:%M:%S   %d/%m/%Y", localtime(&seconds));
        lcd_string(0,15,buf);

        strftime(buf,40, "%I %M %S", localtime(&seconds));
        sscanf(buf,"%d %d %d",&hour,&min,&sec);
        
        b=sec*m_pi;
        s_sx=CENTERX;
        s_sy=CENTERY;
        s_ex=sin(b)*(INNER_RADIUS-3)+CENTERX;
        s_ey=-cos(b)*(INNER_RADIUS-3)+CENTERY;

        
        b=min*m_pi;
        m_sx=sin(b)*(CENTER_CIRCLE)+CENTERX;
        m_sy=-cos(b)*(CENTER_CIRCLE)+CENTERY;
        m_ex=sin(b)*(INNER_RADIUS-10)+CENTERX;
        m_ey=-cos(b)*(INNER_RADIUS-10)+CENTERY;

        // advancing hour hand
        if (hour<12)
        {
            // draw hour hand with an offset
            // calculated by dividing minutes by 12 
            b=(hour*5+min/12)*m_pi;
        }
        else
        {
            // hour would be 0 offset at 12 o'clock
            // so we can leave it out of the equation...
            b=(min/12)*m_pi;
        }
        h_sx=sin(b)*(CENTER_CIRCLE)+CENTERX;
        h_sy=-cos(b)*(CENTER_CIRCLE)+CENTERY;
        h_ex=sin(b)*(INNER_RADIUS-20)+CENTERX;
        h_ey=-cos(b)*(INNER_RADIUS-20)+CENTERY;

        // draw 'new' hands
        lcd_line(s_sx,s_sy,s_ex,s_ey,1);
        lcd_line(m_sx,m_sy,m_ex,m_ey,1);
        lcd_line(h_sx,h_sy,h_ex,h_ey,1);

        lcd_circle(CENTERX,CENTERY,CENTER_CIRCLE,1);        
        lcd_circle(CENTERX,CENTERY,1,1);        
 
        myled = !myled;
        
        // now wait until the seconds change
        while (seconds==time(NULL)) wait(0.1);
        
        // erase 'old' hands
        lcd_line(s_sx,s_sy,s_ex,s_ey,0);
        lcd_line(m_sx,m_sy,m_ex,m_ey,0);
        lcd_line(h_sx,h_sy,h_ex,h_ey,0);
        
    }
}