1

I want to know what's the default or recommended arithmetic to draw a circle on the LCD128x64 using ST7920 driver.

I've done the following functions:

  1. drawing a pixel at any point on the LCD
  2. sending an image
  3. drawing a vertical, diagonal and horizontal lines
  4. send CGRAM chars

But I can't draw a circle.

Here's my code:

/*  Project name:       lcd128x64
    Author:             r1s8k
    Date:               22/12/2019, 12:25 AM

    Project specs:      This is the source file of the lcd128x64 driver that uses 
                        MCP23017 as the lcd driver using SPI interface

*/

#include <avr/io.h>
#include <Arduino.h>
#include <SPI.h>
#include <avr/pgmspace.h>
#include <math.h>
#include "lcd128x64_spi.h"

////////////////////////////////////////////////////////////////////////////////////////////
// global variables
static uint8_t graphics, horizontal,y_pre,p_dr_act,block_pre;
//static uint16_t p_buf;
static uint16_t p_pre;
uint8_t p_hi,p_lo,p_block,p_pos,p_bit;

////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// control functions

// init
void lcd128x64_init(void){
    pinMode(CS_PIN, OUTPUT);
    pinMode(CLK_PIN, OUTPUT);
    pinMode(MOSI_PIN, OUTPUT);
    digitalWrite(CS_PIN, LOW);                          // disable CS_PIN
    SPI.begin();
    _delay_ms(100);                                     // trying initial delay
    digitalWrite(CS_PIN, HIGH);                         // enable CS_PIN

    _delay_ms(50);                                      // datasheet says > 40ms
    lcd128x64_cmd(FUNCTION_SET_BASIC);  //_delay_us(28);// datasheet says > 100us - 72 = 28us
    lcd128x64_cmd(FUNCTION_SET_BASIC);  //_delay_us(28);// important for going to / coming back from graph mode
    lcd128x64_cmd(DISABLE_V_SCROLL);                    // if you have extended list to scroll
    lcd128x64_cmd(DISPLAY_CONTROL);     //_delay_us(28);// display = 1, cursor = 0, blink = 0
    lcd128x64_cmd(ENTERY_MODE);                         // cursor = right, display shift = 0

    lcd128x64_cmd(DISPLAY_CLEAR);   
    lcd128x64_cmd(RETURN_HOME);
    graphics = 0;
    horizontal = 0;
}
// command tx
void lcd128x64_cmd(uint8_t cmd){
    SPI.transfer(CMD_MSK);
    SPI.transfer(cmd & 0xf0);
    SPI.transfer(cmd << 4);
    if(cmd == 0x01)_delay_us(1600); else _delay_us(72);
}
// data tx
void lcd128x64_data(uint8_t data){
    SPI.transfer(DATA_MSK);
    SPI.transfer(data & 0xf0);
    SPI.transfer(data << 4);_delay_us(72);
}
// move cursor
void lcd128x64_move_cursor(uint8_t row, uint8_t col){
    if      (row == 1)lcd128x64_cmd(0x80 + (col - 1));
    else if (row == 2)lcd128x64_cmd(0x90 + (col - 1));
    else if (row == 3)lcd128x64_cmd(0x88 + (col - 1));
    else if (row == 4)lcd128x64_cmd(0x98 + (col - 1));  
}
// clear lcd
void lcd128x64_clr(void){
    uint8_t y,x;   
    lcd128x64_graphics_set_mode(1);
    for(y=0;y<32;y++){
        for(x=0;x<8;x++){   
            lcd128x64_cmd(0x80+y);lcd128x64_cmd(0x80+x); 
            lcd128x64_data(0x00);lcd128x64_data(0x00);
        }
    }
    for(y=32;y<64;y++){   
        for(x=0;x<8;x++){   
            lcd128x64_cmd(0x80+y-32);lcd128x64_cmd(0x88+x);   
            lcd128x64_data(0x00);lcd128x64_data(0x00);
        }   
    }
}
// graphics mode
void lcd128x64_graphics_set_mode(uint8_t mode){
    lcd128x64_cmd(FUNCTION_SET_EXTENDED);
    if(mode){
        lcd128x64_cmd(GRAPHICS_ON1);
        lcd128x64_cmd(GRAPHICS_ON2);
        graphics = GRAPH;
    }
    else{
        lcd128x64_cmd(GRAPHICS_OFF1);
        lcd128x64_cmd(GRAPHICS_OFF2);
        graphics = TEXT;
    }
}
// write chars
void lcd128x64_write_chars(uint8_t row, uint8_t col, uint8_t *str, uint8_t w){
// row1: 80H~8FH // row2: 90H~9FH // row3: 88H~AFH // row4: 98H~BFH
    if(graphics){lcd128x64_graphics_set_mode(0);}

    if      (row == 1)lcd128x64_cmd(0x80 + (col - 1));
    else if (row == 2)lcd128x64_cmd(0x90 + (col - 1));
    else if (row == 3)lcd128x64_cmd(0x88 + (col - 1));
    else if (row == 4)lcd128x64_cmd(0x98 + (col - 1));

    uint8_t i;
    for(i=0; i<w; i++){lcd128x64_data(str[i]);}
}

////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
// draw functions

// pixel set
void lcd128x64_pixel_set(uint8_t y_axis, uint8_t x_axis){
    uint16_t p_buf;
    if(!graphics){lcd128x64_graphics_set_mode(1);}

    p_block = x_axis / 16;              // calculate x_axis block
    x_axis ^= 127;
    p_pos = (x_axis % 16);              // calculate the position of pixel in 16-bit    
    p_buf = (1 << p_pos);               // set the bit

    if(!p_dr_act){
        p_dr_act = 1;                   // set pixel draw activate flag
        y_pre = y_axis;                 // copy y_axis current position
        block_pre = p_block;            // store the first block
    }

    if(block_pre != p_block){           // if block changed
        p_pre = 0;                      // clear global pixel buffer
        block_pre = p_block;            // store new block, 
    }                                   // to disable triggering this check

    if(y_axis == y_pre){
        p_pre |= p_buf;                 // put current pixel in global buffer
        p_buf |= p_pre;                 // put previous pixels in local buffer  
    }
    else{                               // when changing the row, 
        y_pre = y_axis;                 // copy new row position
        p_pre = 0;                      // clear global buffer
        p_pre = p_buf;                  // store the last pixel position
    }

    if(y_axis<32){
        lcd128x64_cmd(0x80+y_axis);
        lcd128x64_cmd(0x80+p_block);
        lcd128x64_data(p_buf>>8);
        lcd128x64_data(p_buf);
    }
    else{
        lcd128x64_cmd((0x80+y_axis)-32);
        lcd128x64_cmd(0x88+p_block);
        lcd128x64_data(p_buf>>8);
        lcd128x64_data(p_buf);
    }   
}
// pixel clear
void lcd128x64_pixel_clear(uint8_t y_axis, uint8_t x_axis){   
    uint16_t p_msk;
    if(!graphics){lcd128x64_graphics_set_mode(1);}  
    p_block = x_axis / 16;
    x_axis ^= 127;
    p_pos = (x_axis % 16);
    p_msk &= ~(1 << p_pos);

    if(y_axis<32){
        lcd128x64_cmd(0x80+y_axis);
        lcd128x64_cmd(0x80+p_block);
        lcd128x64_data(p_msk>>8);
        lcd128x64_data(p_msk);
    }
    else{
        lcd128x64_cmd((0x80+y_axis)-32);
        lcd128x64_cmd(0x88+p_block);
        lcd128x64_data(p_msk>>8);
        lcd128x64_data(p_msk);
    }       
}
// draw image
void lcd128x64_img(unsigned char *img){
    uint8_t y,x;   
    if(!graphics){lcd128x64_graphics_set_mode(1);}

    for(y=0;y<32;y++){
        for(x=0;x<8;x++){
            lcd128x64_cmd(0x80+y);  
            lcd128x64_cmd(0x80+x);
            lcd128x64_data(pgm_read_byte_near(&img[(y*16)+(x*2)]));  
            lcd128x64_data(pgm_read_byte_near(&img[(y*16)+((x*2)+1)]));
        }
    }
    for(y=32;y<64;y++){   
        for(x=0;x<8;x++){
            lcd128x64_cmd((0x80+y)-32);   
            lcd128x64_cmd(0x88+x);
            lcd128x64_data(pgm_read_byte_near(&img[(y*16)+(x*2)]));  
            lcd128x64_data(pgm_read_byte_near(&img[(y*16)+((x*2)+1)]));  
        }   
    }   
}

// draw line
void lcd128x64_line(uint8_t y0, uint8_t y1, uint8_t x0, uint8_t x1){
    uint8_t ih,iw,j,h,w,div;
    h = y1 - y0;
    w = x1 - x0;
    if(w>h){
        div = w/h;
        for(ih=y0,iw=x0;iw<x1;iw++){
            lcd128x64_pixel_set(ih,iw);
            if((iw%div)==(div-1)){ih++;}
        }       
    }
    else{
        div = h/w;
        for(ih=y0,iw=x0;ih<y1;ih++){
            lcd128x64_pixel_set(ih,iw);
            if((ih%div)==(div-1)){iw++;}
        }           
    }
}

// draw circle
void lcd128x64_circle(uint8_t y, uint8_t x, uint8_t r){
    uint8_t py,px,d,f;
    if(y>x){
        //d = y/x;
        f=1;
        for(px=x,py=y-r;px<x+r;px++){
            lcd128x64_pixel_set(py,px);         
            if(((px%f))==(r%f)){py++;f++;}
            //if((py%(d%f))==1){px++;f++;}
        }   
    }
    else{
        d = x/y;
        for(py=y-r,px=x;px<x+r;px++){
            lcd128x64_pixel_set(py,px);
            if(((px%d))==(d-1)){py++;}
        }
    }
}
  • 3
    This is the function I use in DisplayCore. Feel free to use it. – Majenko Jan 18 at 22:52
  • 1
    Considering you already can put a pixel at any position of the display you can try and use something like described here: softwareandfinance.com/Turbo_C/DrawCircle.html – Coder_fox Jan 19 at 11:56
  • Yep! Absolutely worked like a charm. But it's a bit slow and uses a relatively big memory .. these lines used 1.7kb of program memory! double i, angle, x1, y1; for(i = 0; i < 360; i += 0.1){ angle = i; x1 = r * cos(angle * PI / 180); y1 = r * sin(angle * PI / 180); lcd128x64_pixel_set(x + x1, y + y1); } – R1S8K Jan 19 at 21:27
  • This is what I got i.ibb.co/r4z4pxM/20200120-002911.jpg – R1S8K Jan 19 at 21:34
  • @Majenko, that is a nice and efficient algorithm! By comparison the Turbo C example, to be honest, is a waste of processing power. It's not optimized at all (for example, by using symmetry) and uses expensive floating point functions. – StarCat Jan 22 at 21:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.