view main/packages/avr/lcd/ili9340/ili9340.cpp @ 220:74381e5dd204 main

Demo for the Adafruit LCD product number 1480.
author Bob Cook <bob@bobcookdev.com>
date Mon, 07 Jul 2014 18:42:15 -0700
parents 8245c6f136b5
children
line wrap: on
line source

// ----------------------------------------------------------------------------------------
//
//  avr/lcd/ili9340/ili9340.cpp
//    
//  Bob Cook Development, Robotics Library
//  http://www.bobcookdev.com/rl/
//    
//  This file implements support for the ILI9340 240x320 RGB LCD driver.
//
//  Copyright (C) 2014 Bob Cook
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//  
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//  
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//
//  Portions copied from "Adafruit_ILI9340" written by Limor Fried, Adafruit Industries.
//  See https://github.com/adafruit/Adafruit_ILI9340
//
//  Portions copied from "Nokia 6100 LCD Display Driver" written by James P. Lynch.
//  See http://www.sparkfun.com/tutorial/Nokia%206100%20LCD%20Display%20Driver.pdf
//
// ----------------------------------------------------------------------------------------

#include "ili9340.h"

#include <avr/io.h>

#include "packages/avr/device/spi.h"
#include "packages/avr/device/spinwait.h"

#include "packages/common/font/font_defs.h"

#include "project_defs.h"

// always after other includes
#include "packages/avr/device/workaround34734.h"


#if defined( PRJ_ILI9340_ENABLE )

// ----------------------------------------------------------------------------------------

#if !defined( PRJ_SPI_ENABLE_MASTER )
#error PRJ_SPI_ENABLE_MASTER is required for SPI communications!
#endif

#if !defined( PRJ_SPI_DATA_DIRECTION_MSB )
#error PRJ_SPI_DATA_DIRECTION_MSB is required!
#endif

// ----------------------------------------------------------------------------------------

#if !defined( PRJ_ILI9340_SELECT_PORT )
#error PRJ_ILI9340_SELECT_PORT is required!
#endif

#if !defined( PRJ_ILI9340_SELECT_PIN )
#error PRJ_ILI9340_SELECT_PIN is required!
#endif

#if !defined( PRJ_ILI9340_SELECT_DDR )
#error PRJ_ILI9340_SELECT_DDR is required!
#endif

#if !defined( PRJ_ILI9340_COMMAND_PORT )
#error PRJ_ILI9340_COMMAND_PORT is required!
#endif

#if !defined( PRJ_ILI9340_COMMAND_PIN )
#error PRJ_ILI9340_COMMAND_PIN is required!
#endif

#if !defined( PRJ_ILI9340_COMMAND_DDR )
#error PRJ_ILI9340_COMMAND_DDR is required!
#endif

#if defined( PRJ_ILI9340_USE_HW_RESET )

#if !defined( PRJ_ILI9340_RESET_PORT )
#error PRJ_ILI9340_RESET_PORT is required!
#endif

#if !defined( PRJ_ILI9340_RESET_PIN )
#error PRJ_ILI9340_RESET_PIN is required!
#endif

#if !defined( PRJ_ILI9340_RESET_DDR )
#error PRJ_ILI9340_RESET_DDR is required!
#endif

#endif // #if defined( PRJ_ILI9340_USE_HW_RESET )

#if defined( PRJ_ILI9340_INC_WRITE_CSTRINGS ) || defined( PRJ_ILI9340_INC_WRITE_PGM_CSTRINGS )
#if !defined( PRJ_ILI9340_INC_WRITE_CHAR )
#define PRJ_ILI9340_INC_WRITE_CHAR
#endif
#endif

#if !defined( PRJ_FONT_8X12_ENABLE )
#error PRJ_FONT_8X12_ENABLE is required!
#endif

// ----------------------------------------------------------------------------------------

#if defined( PRJ_ILI9340_DISPLAY_ORIENTATION_0 ) \
    || defined( PRJ_ILI9340_DISPLAY_ORIENTATION_180 )

static const uint16_t ILI9340_MAX_HORIZONTAL_PIXELS = 240;
static const uint16_t ILI9340_MAX_VERTICAL_PIXELS   = 320;

#elif defined( PRJ_ILI9340_DISPLAY_ORIENTATION_90 ) \
    || defined( PRJ_ILI9340_DISPLAY_ORIENTATION_270 )

static const uint16_t ILI9340_MAX_HORIZONTAL_PIXELS = 320;
static const uint16_t ILI9340_MAX_VERTICAL_PIXELS   = 240;

#else
#error PRJ_ILI9340_DISPLAY_ORIENTATION_* must be defined!
#endif

// ----------------------------------------------------------------------------------------

const uint8_t ILI9340_COMMAND_SWRESET = 0x01;

const uint8_t ILI9340_COMMAND_MADCTL = 0x36;
const uint8_t ILI9340_MADCTL_MH      = ( 1 << 2 );
const uint8_t ILI9340_MADCTL_BGR     = ( 1 << 3 );
const uint8_t ILI9340_MADCTL_ML      = ( 1 << 4 );
const uint8_t ILI9340_MADCTL_MV      = ( 1 << 5 );
const uint8_t ILI9340_MADCTL_MX      = ( 1 << 6 );
const uint8_t ILI9340_MADCTL_MY      = ( 1 << 7 );

const uint8_t ILI9340_COMMAND_PWCTRL1  = 0xc0;
const uint8_t ILI9340_PWCTRL1_VRH_MASK = 0x3f;
const uint8_t ILI9340_PWCTRL1_VC_MASK  = 0x0f;

const uint8_t ILI9340_COMMAND_PWCTRL2 = 0xc1;
const uint8_t ILI9340_PWCTRL2_BT_MASK = 0x0f;

const uint8_t ILI9340_COMMAND_VMCTR1  = 0xc5;
const uint8_t ILI9340_VMCTR1_VMH_MASK = 0x7f;
const uint8_t ILI9340_VMCTR1_VML_MASK = 0x7f;

const uint8_t ILI9340_COMMAND_VMCTR2  = 0xc7;
const uint8_t ILI9340_VMCTR2_nVM      = ( 1 << 7 );
const uint8_t ILI9340_VMCTR2_VMF_MASK = 0x7f;

const uint8_t ILI9340_COMMAND_PIXSET = 0x3a;
const uint8_t ILI9340_PIXSET_16BPP   = 0x55;
const uint8_t ILI9340_PIXSET_18BPP   = 0x66;

const uint8_t ILI9340_COMMAND_FRMCTR1   = 0xb1;
const uint8_t ILI9340_FRMCTR1_DIVA_MASK = 0x03;
const uint8_t ILI9340_FRMCTR1_RTNA_MASK = 0x1f;

const uint8_t ILI9340_COMMAND_DISCTRL    = 0xb6;
const uint8_t ILI9340_DISCTRL_PTG_00     = ( 0x00 << 2 );
const uint8_t ILI9340_DISCTRL_PTG_01     = ( 0x01 << 2 );
const uint8_t ILI9340_DISCTRL_PTG_10     = ( 0x10 << 2 );
const uint8_t ILI9340_DISCTRL_PTG_11     = ( 0x11 << 2 );
const uint8_t ILI9340_DISCTRL_PT_00      = 0x00;
const uint8_t ILI9340_DISCTRL_PT_01      = 0x01;
const uint8_t ILI9340_DISCTRL_PT_10      = 0x10;
const uint8_t ILI9340_DISCTRL_PT_11      = 0x11;
const uint8_t ILI9340_DISCTRL_ISC_MASK   = 0x0f;
const uint8_t ILI9340_DISCTRL_SM         = ( 1 << 4 );
const uint8_t ILI9340_DISCTRL_SS         = ( 1 << 5 );
const uint8_t ILI9340_DISCTRL_GS         = ( 1 << 6 );
const uint8_t ILI9340_DISCTRL_REV        = ( 1 << 7 );
const uint8_t ILI9340_DISCTRL_NL_MASK    = 0x1f;
const uint8_t ILI9340_DISCTRL_PCDIV_MASK = 0x1f;

const uint8_t ILI9340_COMMAND_SPLIN  = 0x10;
const uint8_t ILI9340_COMMAND_SLPOUT = 0x11;

const uint8_t ILI9340_COMMAND_DISPON = 0x29;

const uint8_t ILI9340_COMMAND_CASET = 0x2a;
const uint8_t ILI9340_COMMAND_PASET = 0x2b;
const uint8_t ILI9340_COMMAND_RAMWR = 0x2c;

// ----------------------------------------------------------------------------------------

#if defined( PRJ_ILI9340_INC_WRITE_CHAR )

const uint8_t ILI9340_MAX_HORIZONTAL_CHARS
    = ( ILI9340_MAX_HORIZONTAL_PIXELS / FONT_8X12_GLYPH_WIDTH );

const uint8_t ILI9340_MAX_VERTICAL_CHARS
    = ( ILI9340_MAX_VERTICAL_PIXELS / FONT_8X12_GLYPH_HEIGHT );

#endif // #if defined( PRJ_ILI9340_INC_WRITE_CHAR )

// ----------------------------------------------------------------------------------------

inline void select_controller()
{
    PRJ_ILI9340_SELECT_PORT &= ~( 1 << PRJ_ILI9340_SELECT_PIN );
}

inline void deselect_controller()
{
    PRJ_ILI9340_SELECT_PORT |= ( 1 << PRJ_ILI9340_SELECT_PIN );
}

// ----------------------------------------------------------------------------------------

inline void set_controller_command_mode()
{
    PRJ_ILI9340_COMMAND_PORT &= ~( 1 << PRJ_ILI9340_COMMAND_PIN );
}

inline void set_controller_data_mode()
{
    PRJ_ILI9340_COMMAND_PORT |= ( 1 << PRJ_ILI9340_COMMAND_PIN );
}

// ----------------------------------------------------------------------------------------

static void send_command( uint8_t cmd )
{
    set_controller_command_mode();
    select_controller();
    PORTB &= ~( 1 << PB7 );
    spi_write( cmd );
    deselect_controller();
}

static void send_data( uint8_t data )
{
    set_controller_data_mode();
    PORTB &= ~( 1 << PB7 );
    select_controller();
    spi_write( data );
    deselect_controller();
}

// ----------------------------------------------------------------------------------------

void ili9340_init()
{
    //--    Configure the SPI hardware; the caller might have done it but it doesn't
    //      hurt to do it again.

    spi_init();

    //--    Configure our select line and mode line.

    PRJ_ILI9340_SELECT_DDR  |= ( 1 << PRJ_ILI9340_SELECT_PIN );
    PRJ_ILI9340_SELECT_PORT |= ( 1 << PRJ_ILI9340_SELECT_PIN );

    PRJ_ILI9340_COMMAND_DDR  |=  ( 1 << PRJ_ILI9340_COMMAND_PIN );
    PRJ_ILI9340_COMMAND_PORT &= ~( 1 << PRJ_ILI9340_COMMAND_PIN );

    //--    Configure our reset line (if supplied); it should be pulled high normally.

#if defined( PRJ_ILI9340_USE_HW_RESET )

    PRJ_ILI9340_RESET_DDR  |= ( 1 << PRJ_ILI9340_RESET_PIN );
    PRJ_ILI9340_RESET_PORT |= ( 1 << PRJ_ILI9340_RESET_PIN );

#endif

    //--    Reset the display.

    ili9340_reset();

    //--    Configure the display controller. This is mostly copied from example code
    //      and reading the ILITEK datasheet.

    //select_controller();

#if 0
    send_command(0xEF);
    send_data(0x03);
    send_data(0x80);
    send_data(0x02);

    send_command(0xCF);
    send_data(0x00);
    send_data(0XC1);
    send_data(0X30);

    send_command(0xED);
    send_data(0x64);
    send_data(0x03);
    send_data(0X12);
    send_data(0X81);

    send_command(0xE8);
    send_data(0x85);
    send_data(0x00);
    send_data(0x78);

    send_command(0xCB);
    send_data(0x39);
    send_data(0x2C);
    send_data(0x00);
    send_data(0x34);
    send_data(0x02);

    send_command(0xF7);
    send_data(0x20);

    send_command(0xEA);
    send_data(0x00);
    send_data(0x00);
#endif

#if 1
#define ILI9340_PWCTR1  0xC0
#define ILI9340_PWCTR2  0xC1
#define ILI9340_PWCTR3  0xC2
    send_command(ILI9340_PWCTR1);    //Power control 
    send_data(0x23);   //VRH[5:0] 

    send_command(ILI9340_PWCTR2);    //Power control 
    send_data(0x10);   //SAP[2:0];BT[3:0] 

#define ILI9340_VMCTR1  0xC5
    send_command(ILI9340_VMCTR1);    //VCM control 
    send_data(0x3e); //�Աȶȵ���
    send_data(0x28);

#define ILI9340_VMCTR2  0xC7
    send_command(ILI9340_VMCTR2);    //VCM control2 
    send_data(0x86);  //--

#define ILI9340_MADCTL  0x36
#define ILI9340_MADCTL_MX  0x40
#define ILI9340_MADCTL_BGR 0x08
    send_command(ILI9340_MADCTL);    // Memory Access Control 
    send_data(ILI9340_MADCTL_MX | ILI9340_MADCTL_BGR);

#define ILI9340_PIXFMT  0x3A
    send_command(ILI9340_PIXFMT);
    send_data(0x55);

#define ILI9340_FRMCTR1 0xB1
    send_command(ILI9340_FRMCTR1);
    send_data(0x00);
    send_data(0x18);

#define ILI9340_DFUNCTR 0xB6
    send_command(ILI9340_DFUNCTR);    // Display Function Control 
    send_data(0x08);
    send_data(0x82);
    send_data(0x27);
#endif

#if 0
    send_command( ILI9340_COMMAND_PWCTRL1 );
    send_data( 0x23 & ILI9340_PWCTRL1_VRH_MASK );   // GVDD = 4.60 V
    send_data( 0x00 & ILI9340_PWCTRL1_VC_MASK );    // VCI1 = 2.30 V

    send_command( ILI9340_COMMAND_PWCTRL2 );
    send_data( 0x00 & ILI9340_PWCTRL2_BT_MASK );    // BT = 0b0000

    send_command( ILI9340_COMMAND_VMCTR1 );
    send_data( 0x3e & ILI9340_VMCTR1_VMH_MASK );    // VCOMH =  4.250 V
    send_data( 0x28 & ILI9340_VMCTR1_VML_MASK );    // VCOML = -1.500 V

    send_command( ILI9340_COMMAND_VMCTR2 );
    send_data( ILI9340_VMCTR2_nVM | ( 0x06 & ILI9340_VMCTR2_VMF_MASK ) );

#if defined( PRJ_ILI9340_DISPLAY_ORIENTATION_0 )

    send_command( ILI9340_COMMAND_MADCTL );
    send_data( ILI9340_MADCTL_MX | ILI9340_MADCTL_BGR );

#elif defined( PRJ_ILI9340_DISPLAY_ORIENTATION_90 )

    send_command( ILI9340_COMMAND_MADCTL );
    send_data( ILI9340_MADCTL_MV | ILI9340_MADCTL_BGR );

#elif defined( PRJ_ILI9340_DISPLAY_ORIENTATION_180 )

    send_command( ILI9340_COMMAND_MADCTL );
    send_data( ILI9340_MADCTL_MY | ILI9340_MADCTL_BGR );

#elif defined( PRJ_ILI9340_DISPLAY_ORIENTATION_270 )

    send_command( ILI9340_COMMAND_MADCTL );
    send_data( ILI9340_MADCTL_MV | ILI9340_MADCTL_MY | ILI9340_MADCTL_MX | ILI9340_MADCTL_BGR );

#endif

    send_command( ILI9340_COMMAND_PIXSET );
    send_data( ILI9340_PIXSET_16BPP );

    send_command( ILI9340_COMMAND_FRMCTR1 );
    send_data( 0x00 & ILI9340_FRMCTR1_DIVA_MASK );
    send_data( 0x18 & ILI9340_FRMCTR1_RTNA_MASK );      // 79 Hz refresh rate

    send_command( ILI9340_COMMAND_DISCTRL );
    send_data( ILI9340_DISCTRL_PTG_10 | ILI9340_DISCTRL_PT_00 );
    send_data( ILI9340_DISCTRL_REV | ( 0x02 & ILI9340_DISCTRL_ISC_MASK ) );
    send_data( 0x27 & ILI9340_DISCTRL_NL_MASK );
    send_data( 0x00 & ILI9340_DISCTRL_PCDIV_MASK );

    send_command( ILI9340_COMMAND_SLPOUT );
    spinwait_delay_ms( 120 );

    send_command( ILI9340_COMMAND_DISPON );
#endif

#if 0
    send_command(0xF2);    // 3Gamma Function Disable 
    send_data(0x00);

#define ILI9340_GAMMASET 0x26
    send_command(ILI9340_GAMMASET);    //Gamma curve selected 
    send_data(0x01);

#define ILI9340_GMCTRP1 0xE0
    send_command(ILI9340_GMCTRP1);    //Set Gamma 
    send_data(0x0F);
    send_data(0x31);
    send_data(0x2B);
    send_data(0x0C);
    send_data(0x0E);
    send_data(0x08);
    send_data(0x4E);
    send_data(0xF1);
    send_data(0x37);
    send_data(0x07);
    send_data(0x10);
    send_data(0x03);
    send_data(0x0E);
    send_data(0x09);
    send_data(0x00);

#define ILI9340_GMCTRN1 0xE1
    send_command(ILI9340_GMCTRN1);    //Set Gamma 
    send_data(0x00);
    send_data(0x0E);
    send_data(0x14);
    send_data(0x03);
    send_data(0x11);
    send_data(0x07);
    send_data(0x31);
    send_data(0xC1);
    send_data(0x48);
    send_data(0x08);
    send_data(0x0F);
    send_data(0x0C);
    send_data(0x31);
    send_data(0x36);
    send_data(0x0F);
#endif 

#define ILI9340_SLPOUT  0x11
    send_command(ILI9340_SLPOUT);    //Exit Sleep 
    spinwait_delay_ms(120);
#define ILI9340_DISPON  0x29
    send_command(ILI9340_DISPON);    //Display on

    deselect_controller();
}

// ----------------------------------------------------------------------------------------

void ili9340_reset()
{

#if defined( PRJ_ILI9340_USE_HW_RESET )

    spinwait_delay_ms( 5 );
    PRJ_ILI9340_RESET_PORT &= ~( 1 << PRJ_ILI9340_RESET_PIN );
    spinwait_delay_ms( 20 );
    PRJ_ILI9340_RESET_PORT |= ( 1 << PRJ_ILI9340_RESET_PIN );
    spinwait_delay_ms( 150 );

#else

    //select_controller();

    spinwait_delay_ms( 5 );
    send_command( ILI9340_COMMAND_SWRESET );
    spinwait_delay_ms( 150 );

    //deselect_controller();

#endif

}

// ----------------------------------------------------------------------------------------
//  Prepare to write to the display memory to a given rectangle.
//

static void prep_page_col_address( uint16_t x, uint16_t dx, uint16_t y, uint16_t dy )
{
    send_command( ILI9340_COMMAND_CASET );
    send_data( static_cast< uint8_t >( x >> 8 ) );
    send_data( static_cast< uint8_t >( x ) );
    send_data( static_cast< uint8_t >( ( x + dx ) >> 8 ) );
    send_data( static_cast< uint8_t >( x + dx ) );

    send_command( ILI9340_COMMAND_PASET );
    send_data( static_cast< uint8_t >( y >> 8 ) );
    send_data( static_cast< uint8_t >( y ) );
    send_data( static_cast< uint8_t >( ( y + dy ) >> 8 ) );
    send_data( static_cast< uint8_t >( y + dy ) );

    send_command( ILI9340_COMMAND_RAMWR );
}


// ----------------------------------------------------------------------------------------

void ili9340_fill_screen( ili9340_color_t color )
{
    //--    Set the page and column addressing to the full screen.

    prep_page_col_address( 0, ILI9340_MAX_HORIZONTAL_PIXELS - 1,
                           0, ILI9340_MAX_VERTICAL_PIXELS - 1 );

    set_controller_data_mode();
    select_controller();

    uint8_t color_hi = static_cast< uint8_t >( color >> 8 );
    uint8_t color_lo = static_cast< uint8_t >( color );

    for ( uint16_t i = 0; i < ILI9340_MAX_HORIZONTAL_PIXELS; ++i )
    {
        for ( uint16_t j = 0; j < ILI9340_MAX_VERTICAL_PIXELS; ++j )
        {
    PORTB &= ~( 1 << PB7 );
            spi_write( color_hi );
    PORTB &= ~( 1 << PB7 );
            spi_write( color_lo );
        }
    }

    deselect_controller();
}


// ----------------------------------------------------------------------------------------

#if defined( PRJ_ILI9340_INC_WRITE_CHAR )

void ili9340_write( char            c, 
                    uint8_t         x, 
                    uint8_t         y, 
                    ili9340_color_t fg_color,
                    ili9340_color_t bg_color )
{
    //--    Convert the glyph coordinates to pixel coordinates.

    uint16_t pixel_x = static_cast< uint16_t >( x ); //% ILI9340_MAX_HORIZONTAL_CHARS );
    pixel_x *= FONT_8X12_GLYPH_WIDTH;

    uint16_t pixel_y = static_cast< uint16_t >( y ); //% ILI9340_MAX_VERTICAL_CHARS );
    pixel_y *= FONT_8X12_GLYPH_HEIGHT;

    //--    Fetch the PROGMEM ptr to the desired glyph data.

    const prog_uint8_t* this_glyph = font_8x12_glyph_ptr( c );

    //--    Write the glyph pixels at the specified location.

    uint8_t fg_color_hi = static_cast< uint8_t >( fg_color >> 8 );
    uint8_t fg_color_lo = static_cast< uint8_t >( fg_color );

    uint8_t bg_color_hi = static_cast< uint8_t >( bg_color >> 8 );
    uint8_t bg_color_lo = static_cast< uint8_t >( bg_color );

    prep_page_col_address( pixel_x,
                           static_cast< uint16_t >( FONT_8X12_GLYPH_WIDTH - 1 ),
                           pixel_y, 
                           static_cast< uint16_t >( FONT_8X12_GLYPH_HEIGHT - 1 ) );

    set_controller_data_mode();
    select_controller();

    for ( uint8_t i = 0; i < FONT_8X12_GLYPH_HEIGHT; i++ )
    {
        uint8_t data = pgm_read_byte_near( this_glyph++ );

        for ( uint8_t j = 0; j < FONT_8X12_GLYPH_WIDTH; j++ )
        {
            if ( data & 0x80 )
            {
                spi_write( fg_color_hi );
                spi_write( fg_color_lo );
            }
            else
            {
                spi_write( bg_color_hi );
                spi_write( bg_color_lo );
            }
            data <<= 1;
        }
    }

    deselect_controller();
}

#endif

// ----------------------------------------------------------------------------------------

#if defined( PRJ_ILI9340_INC_WRITE_CSTRINGS )

void ili9340_write
    ( 
        const char*    str, 
        uint8_t        x, 
        uint8_t        y, 
        ili9340_color_t fg_color,
        ili9340_color_t bg_color 
    )
{
    while ( *str )
    {
        ili9340_write( *str++, x++, y, fg_color, bg_color );
    }
}

#endif

// ----------------------------------------------------------------------------------------

#if defined( PRJ_ILI9340_INC_WRITE_PGM_CSTRINGS )

void ili9340_write_pgm
    ( 
        const prog_char* str, 
        uint8_t          x, 
        uint8_t          y, 
        ili9340_color_t  fg_color,
        ili9340_color_t  bg_color 
    )
{
    for ( ;; )
    {
        char c = static_cast< char >( pgm_read_byte_near( str++ ) );

        if ( c == '\0' )
        {
            return;
        }

        ili9340_write( c, x++, y, fg_color, bg_color );
    }
}

#endif

// ----------------------------------------------------------------------------------------

#if defined( PRJ_ILI9340_INC_SET_PIXEL )

void ili9340_set_pixel( uint8_t x, uint8_t y, ili9340_color_t color )
{
    x %= ILI9340_MAX_HORIZONTAL_PIXELS;
    y %= ILI9340_MAX_VERTICAL_PIXELS;

    select_controller();
    
    prep_page_col_address( x, 0, y, 0 );
    send_data( color );
    
    deselect_controller();
}

#endif

// ----------------------------------------------------------------------------------------

#if defined( PRJ_ILI9340_INC_DRAWING )

void ili9340_line_h( uint16_t x, uint16_t y, uint16_t len, ili9340_color_t color )
{
    x %= ILI9340_MAX_HORIZONTAL_PIXELS;
    y %= ILI9340_MAX_VERTICAL_PIXELS;
    
    if ( len + x > ILI9340_MAX_HORIZONTAL_PIXELS )
    {
        len = ILI9340_MAX_HORIZONTAL_PIXELS - x;
    }

    select_controller();
    
    prep_page_col_address( x, len - 1, y, 0 );
    
    while ( len-- )
    {
        send_data( color );
    }

    deselect_controller();
}

#endif

// ----------------------------------------------------------------------------------------

#if defined( PRJ_ILI9340_INC_DRAWING )

void ili9340_line_v( uint16_t x, uint16_t y, uint16_t len, ili9340_color_t color )
{
    x %= ILI9340_MAX_HORIZONTAL_PIXELS;
    y %= ILI9340_MAX_VERTICAL_PIXELS;
    
    if ( len + y > ILI9340_MAX_VERTICAL_PIXELS )
    {
        len = ILI9340_MAX_VERTICAL_PIXELS - y;
    }

    prep_page_col_address( x, 0, y, len - 1 );
    
    set_controller_data_mode();
    select_controller();

    uint8_t color_hi = static_cast< uint8_t >( color >> 8 );
    uint8_t color_lo = static_cast< uint8_t >( color );

    while ( len-- )
    {
        spi_write( color_hi );
        spi_write( color_lo );
    }

    deselect_controller();
}

#endif

// ----------------------------------------------------------------------------------------

#if defined( PRJ_ILI9340_INC_DRAWING )

void ili9340_rect( uint16_t x, uint16_t y, uint16_t w, uint16_t h, ili9340_color_t color )
{
    ili9340_line_h( x, y, w, color );
    ili9340_line_h( x, y + h - 1, w, color );
    ili9340_line_v( x, y, h - 1, color );
    ili9340_line_v( x + w - 1, y, h - 1, color );
}

#endif

// ----------------------------------------------------------------------------------------

#if defined( PRJ_ILI9340_INC_DRAWING )

void ili9340_fill( uint16_t x, uint16_t y, uint16_t w, uint16_t h, ili9340_color_t color )
{
    if ( x > ILI9340_MAX_HORIZONTAL_PIXELS ) { x = ILI9340_MAX_HORIZONTAL_PIXELS; }
    if ( y > ILI9340_MAX_VERTICAL_PIXELS )   { y = ILI9340_MAX_VERTICAL_PIXELS; }
    
    if ( w + x > ILI9340_MAX_HORIZONTAL_PIXELS )
    {
        w = ILI9340_MAX_HORIZONTAL_PIXELS - x;
    }

    if ( h + y > ILI9340_MAX_VERTICAL_PIXELS )
    {
        h = ILI9340_MAX_VERTICAL_PIXELS - y;
    }

    prep_page_col_address( x, w - 1, y, h - 1 );

    set_controller_data_mode();
    select_controller();

    uint8_t color_hi = static_cast< uint8_t >( color >> 8 );
    uint8_t color_lo = static_cast< uint8_t >( color );

    for ( uint16_t i = 0; i < w; ++i )
    {
        for ( uint16_t j = 0; j < h; ++j )
        {
    PORTB &= ~( 1 << PB7 );
            spi_write( color_hi );
    PORTB &= ~( 1 << PB7 );
            spi_write( color_lo );
        }
    }

    deselect_controller();
}

#endif

// ----------------------------------------------------------------------------------------

#if defined( PRJ_ILI9340_INC_DRAWING_ADV )

void ili9340_line( uint16_t x0,
                   uint16_t y0,x
                   uint16_t x1, x
                   uint16_t y1, x
                   ili9340_color_t color )
{
    //--    Bresenham's line rasterization algorithm: 
    //      http://www.cs.unc.edu/~mcmillan/comp136/Lecture6/Lines.html

    int16_t dy = y1 - y0;
    int16_t dx = x1 - x0;
    int8_t  step_x = 1;
    int8_t  step_y = 1;

    if ( dy < 0 )
    {
        dy *= -1;
        step_y = -1;
    }

    if ( dx < 0 )
    {
        dx *= -1;
        step_x = -1;
    }

    dy <<= 1;
    dx <<= 1;

    ili9340_set_pixel( x0, y0, color );

    if ( dx > dy )
    {
        int16_t fraction = dy - ( dx >> 1 );

        while ( x0 != x1 )
        {
            if ( fraction >= 0 ) 
            {
                y0 += step_y;
                fraction -= dx;
            }

            x0 += step_x;
            fraction += dy;

            ili9340_set_pixel( x0, y0, color );
        }
    }
    else
    {
        int16_t fraction = dx - ( dy >> 1 );

        while ( y0 != y1 )
        {
            if ( fraction >= 0 )
            {
                x0 += step_x;
                fraction -= dy;
            }

            y0 += step_y;
            fraction += dx;

            ili9340_set_pixel( x0, y0, color );
        }
    }
}

#endif

// ----------------------------------------------------------------------------------------

#if defined( PRJ_ILI9340_INC_IMAGEMASK )

void ili9340_imagemask
    (
        uint8_t             x,
        uint8_t             y,
        uint8_t             w,
        uint8_t             h,
        ili9340_color_t      fg_color,
        ili9340_color_t      bg_color,
        const prog_uint8_t* pixels
    )
{
    x %= ILI9340_MAX_HORIZONTAL_PIXELS;
    y %= ILI9340_MAX_VERTICAL_PIXELS;

    select_controller();
    
    prep_page_col_address( x, w - 1, y, h - 1 );
    
    while ( h-- )
    {
        uint8_t pix_bit = 0x00;
        uint8_t data    = 0x00;

        for ( uint8_t i = 0; i < w; i++ )
        {
            if ( pix_bit == 0x00 )
            {
                data = pgm_read_byte_near( pixels++ );
                pix_bit = 0x80;
            }

            if ( data & pix_bit )
            {
                send_data( fg_color );
            }
            else
            {
                send_data( bg_color );
            }

            pix_bit >>= 1;
        }
    }

    deselect_controller();
}

#endif

// ----------------------------------------------------------------------------------------

#if defined( PRJ_ILI9340_INC_COLORIMAGE )

void ili9340_colorimage
    (
        uint8_t x,
        uint8_t y,
        uint8_t w,
        uint8_t h,
        const prog_uint8_t* pixels
    )
{
    x %= ILI9340_MAX_HORIZONTAL_PIXELS;
    y %= ILI9340_MAX_VERTICAL_PIXELS;

    select_controller();
    
    prep_page_col_address( x, w - 1, y, h - 1 );
    
    while ( h-- )
    {
        for ( uint8_t i = 0; i < w; i++ )
        {
            send_data( pgm_read_byte_near( pixels++ ) );
        }
    }

    deselect_controller();
}

#endif

// ----------------------------------------------------------------------------------------

#endif  // #if defined( PRJ_ILI9340_ENABLE )
// ----------------------------------------------------------------------------------------