This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

GFX library rotation issue

Hi There,

I'm working with E-Ink display connected with nRF52 chip. I have a base library to display images and works very well.

Now I'm trying to use GFX library to support text drawing. Using the default rotation (0 degrees) seems to work fine. When I try with 90 or 270 degrees, the text rotates but doesn't fill the whole screen (only few characters), instead the text jumps to next line.

I have tried different font sizes and the result is the same.

I attach the images for reference, any help will be appreciated. Thanks in advance!

Jhon

=======

Details:

- MCU: nRF52832

- SDK: v15.30

Text rotation 0 degrees:

Text rotation 90 degrees:

My code to talk with GFX library.

#include <stdio.h>
#include <string.h>
#include "app_error.h"
#include "app_util_platform.h"
#include "app_error.h"
#include "nrf_lcd.h"
#include "nrf_gfx.h"
#include "nrf_log.h"
#include "nrf_delay.h"
#include "epaper_screen.h"


#define PAPER_PIXEL_WIDTH  176
#define PAPER_PIXEL_HEIGHT 264

ret_code_t paper_init(void);
void paper_uninit(void);
void paper_pixel_draw(uint16_t x, uint16_t y, uint32_t color);
void paper_rect_draw(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color);
void paper_display(void);
void paper_rotation_set(nrf_lcd_rotation_t rotation);
void paper_display_invert(bool invert);


static lcd_cb_t lcd_cb = {
    .state    = NRFX_DRV_STATE_UNINITIALIZED,
    .height   = PAPER_PIXEL_HEIGHT,
    .width    = PAPER_PIXEL_WIDTH,
    .rotation = NRF_LCD_ROTATE_90
};

static const nrf_lcd_t lcd = {
    .lcd_init = paper_init,
    .lcd_uninit = paper_uninit,
    .lcd_pixel_draw = paper_pixel_draw,
    .lcd_rect_draw = paper_rect_draw,
    .lcd_display = paper_display,
    .lcd_rotation_set = paper_rotation_set,
    .lcd_display_invert = paper_display_invert,
    .p_lcd_cb = &lcd_cb
};


//extern const nrf_gfx_font_desc_t orkney_8ptFontInfo;
//static const nrf_gfx_font_desc_t * p_font = &orkney_8ptFontInfo;

extern const nrf_gfx_font_desc_t orkney_24ptFontInfo;
static const nrf_gfx_font_desc_t * p_font = &orkney_24ptFontInfo;

//extern const nrf_gfx_font_desc_t HelveticaNB_120ptFontInfo;
//static const nrf_gfx_font_desc_t * p_font = &HelveticaNB_120ptFontInfo;


#define PAPER_BYTE_WIDTH ((PAPER_PIXEL_WIDTH % 8 == 0) ? PAPER_PIXEL_WIDTH / 8 : PAPER_PIXEL_WIDTH / 8 + 1)
#define PAPER_BYTE_HEIGHT PAPER_PIXEL_HEIGHT
#define PAPER_BUFLEN PAPER_BYTE_WIDTH * PAPER_BYTE_HEIGHT

//static uint8_t paper_buffer[PAPER_BUFLEN];
static uint8_t* paper_buffer = NULL;

ret_code_t paper_init(void)
{
    paper_buffer = (uint8_t*)malloc(PAPER_BUFLEN*sizeof(uint8_t));
    memset(paper_buffer, 0x00, PAPER_BUFLEN*sizeof(uint8_t));
    
    epaper_Display_Init();
    
    return NRF_SUCCESS;
}

void paper_uninit(void) 
{
    epaper_Display_DeInit();
}

#define SWAP(a,b) { int16_t t = a; a = b; b = t;}

void paper_pixel_draw(uint16_t x, uint16_t y, uint32_t color) 
{
    ASSERT(x < PAPER_PIXEL_WIDTH);
    ASSERT(y < PAPER_PIXEL_HEIGHT);
    
    switch (lcd.p_lcd_cb->rotation) {
      case 1:
        SWAP(x, y);
        x = PAPER_PIXEL_WIDTH - x - 1;
        break;
      case 2:
        x = PAPER_PIXEL_WIDTH - x - 1;
        y = PAPER_PIXEL_HEIGHT - y - 1;
        break;
      case 3:
        SWAP(x, y);
        y = PAPER_PIXEL_HEIGHT - y - 1;
        break;
    }

    int bitn = y * PAPER_PIXEL_WIDTH + x;
    int byten = bitn / 8;
    bitn = 8 - bitn % 8 - 1;

    if (color == 0) paper_buffer[byten] |= 1UL << (bitn);
    else paper_buffer[byten] &= ~(1UL << (bitn));
}


void paper_rect_draw(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint32_t color) 
{
    ASSERT(x + width < PAPER_PIXEL_WIDTH);
    ASSERT(y + height < PAPER_PIXEL_HEIGHT);

    for (uint16_t _x = x; _x < x + width; _x++) {
        for (uint16_t _y = y; _y < y + height; _y++) {
            paper_pixel_draw(_x, _y, color);
        }
    }
}

void paper_display(void)
{
    epaper_PartialScreen_Display(0, 0, 176, 264, paper_buffer, 5808);
}

void paper_rotation_set(nrf_lcd_rotation_t rotation)
{
    UNUSED_PARAMETER(rotation);
}

void paper_display_invert(bool invert)
{
    UNUSED_PARAMETER(invert);
}


/////////////////////////
/* Aplication functions*/
/////////////////////////

void gui_interface_init(void)
{
    APP_ERROR_CHECK(nrf_gfx_init(&lcd));
}

void gui_interface_print_text(uint16_t x, uint16_t y, const uint8_t* text)
{
    nrf_gfx_point_t text_start = NRF_GFX_POINT(x, y);
    APP_ERROR_CHECK(nrf_gfx_print(&lcd, &text_start, 0, text, p_font, true));
}

void gui_interface_display(void)
{
    nrf_gfx_display(&lcd);
}

  • Hello,

    Actually, I am surprised that the text is rotated, because your paper_rotation_set() function isn't even populated:

    void paper_rotation_set(nrf_lcd_rotation_t rotation)
    {
        UNUSED_PARAMETER(rotation);
    }

    Also, I don't see where you print your text. perhaps you can share it?

    Are you using the text_print() funciton? Does the APP_ERROR_CHECK() inside that function trigger? 

    I think you need to show a bit more what you have done. At least since you have written this driver yourself (?) and it doesn't seem complete.

    BR,

    Edvin

  • Hi Edvin,

    Thanks for your response. The initial rotation works because is by default set on variable "lcd_cb".

    My test works as follows:

    void main() {

    ...

    gui_interface_init();

    gui_interface_print_text(0, 0, "0123456789");

    gui_interface_display();

    ...

    }

     I'm using nrf_gfx_print() function and no error appears there.

    Yes, I wrote this driver, what more information do you need?

    Best regards,

    Jhon

  • Ok, 

    I have been looking at your driver for while now. Just to be clear, this is not nordic specific, and it is not something we have provided, so I believe you will need to take it from here, but here is what I see:

    In the drivers that are included in the SDK, we have functions implemented in lcd_rotation_set:

    static void ili9341_rotation_set(nrf_lcd_rotation_t rotation)
    {
        write_command(ILI9341_MADCTL);
        switch (rotation) {
            case NRF_LCD_ROTATE_0:
                write_data(ILI9341_MADCTL_MX | ILI9341_MADCTL_BGR);
                break;
            case NRF_LCD_ROTATE_90:
                write_data(ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR);
                break;
            case NRF_LCD_ROTATE_180:
                write_data(ILI9341_MADCTL_MY | ILI9341_MADCTL_BGR);
                break;
            case NRF_LCD_ROTATE_270:
                write_data(ILI9341_MADCTL_MX | ILI9341_MADCTL_MY | ILI9341_MADCTL_MV | ILI9341_MADCTL_BGR);
                break;
            default:
                break;
        }
    }

    Which is a way to tell the screen that everything from now on should be following this rotation. That is implemented in the screen, not the gfx driver.

    I see you have taken a different path to the rotation setting. You implement it in every pixel: 

    void paper_pixel_draw(uint16_t x, uint16_t y, uint32_t color) 
    {
        ASSERT(x < PAPER_PIXEL_WIDTH);
        ASSERT(y < PAPER_PIXEL_HEIGHT);
        
        switch (lcd.p_lcd_cb->rotation) {
          case 1:
            SWAP(x, y);
            x = PAPER_PIXEL_WIDTH - x - 1;
            break;
          case 2:
            x = PAPER_PIXEL_WIDTH - x - 1;
            y = PAPER_PIXEL_HEIGHT - y - 1;
            break;
          case 3:
            SWAP(x, y);
            y = PAPER_PIXEL_HEIGHT - y - 1;
            break;
        }
    
        int bitn = y * PAPER_PIXEL_WIDTH + x;
        int byten = bitn / 8;
        bitn = 8 - bitn % 8 - 1;
    
        if (color == 0) paper_buffer[byten] |= 1UL << (bitn);
        else paper_buffer[byten] &= ~(1UL << (bitn));
    }
    

    which is fine, but that means that we can't tell you why it isn't working. Also, remember that this check is running for every single pixel you are drawing. Just make sure that it isn't taking too long, and drawing to much current for your use case.

    Now, since you do this at a pixel level, this may not be handled in every layer. You are saying that the text is not writing to the entire width. Have you looked at the nrf_gfx_print() implementation?

    ret_code_t nrf_gfx_print(nrf_lcd_t const * p_instance,
                             nrf_gfx_point_t const * p_point,
                             uint16_t font_color,
                             const char * string,
                             const nrf_gfx_font_desc_t * p_font,
                             bool wrap)
    {
        ASSERT(p_instance != NULL);
        ASSERT(p_instance->p_lcd_cb->state != NRFX_DRV_STATE_UNINITIALIZED);
        ASSERT(p_point != NULL);
        ASSERT(string != NULL);
        ASSERT(p_font != NULL);
    
        uint16_t x = p_point->x;
        uint16_t y = p_point->y;
    
        if (y > (nrf_gfx_height_get(p_instance) - p_font->height))
        {
            // Not enough space to write even single char.
            return NRF_ERROR_INVALID_PARAM;
        }
    
        for (size_t i = 0; string[i] != '\0' ; i++)
        {
            if (string[i] == '\n')
            {
                x = p_point->x;
                y += p_font->height + p_font->height / 10;
            }
            else
            {
                write_character(p_instance, p_font, (uint8_t)string[i], &x, y, font_color);
            }
    
            uint8_t char_idx = string[i] - p_font->startChar;
            uint16_t char_width = string[i] == ' ' ? (p_font->height / 2) :
                                                    p_font->charInfo[char_idx].widthBits;
    
            if (x > (nrf_gfx_width_get(p_instance) - char_width))
            {
                if (wrap)
                {
                    x = p_point->x;
                    y += p_font->height + p_font->height / 10;
                }
                else
                {
                    break;
                }
    
                if (y > (nrf_gfx_height_get(p_instance) - p_font->height))
                {
                    break;
                }
            }
        }
    
        return NRF_SUCCESS;
    }

    Particularly the line:

    if (x > (nrf_gfx_width_get(p_instance) - char_width))

    checks whether there is room for the next character on the line. nrf_gfx_width_get() isn't aware of the rotation flipping that you do in your pixel_draw() function. 

    To be fair, the default gfx driver in the example doesn't seem to be aware of it either. I suggest that you at least look into a proper driver for your epaper online. It may not have the exact same functions as the ones in our gfx example, but it will probably be a lot easier to use.

    I did this for an LED screen a while ago, to use with the nRF, since it used TWI instead of SPI. 

    If you are almost there with what you need, and you only need to fix the width when the screen is oriented, you can try to modify these (from nrf_gfx.c)

    uint16_t nrf_gfx_height_get(nrf_lcd_t const * p_instance)
    {
        ASSERT(p_instance != NULL);
        if (p_instance->p_lcd_cb->rotation == NRF_LCD_ROTATE_90 || p_instance->p_lcd_cb->rotation == NRF_LCD_ROTATE_270)
        {
            return p_instance->p_lcd_cb->width;
        }
    
        return p_instance->p_lcd_cb->height;
    }
    
    uint16_t nrf_gfx_width_get(nrf_lcd_t const * p_instance)
    {
        ASSERT(p_instance != NULL);
        if (p_instance->p_lcd_cb->rotation == NRF_LCD_ROTATE_90 || p_instance->p_lcd_cb->rotation == NRF_LCD_ROTATE_270)
        {
            return p_instance->p_lcd_cb->height;
        }
        return p_instance->p_lcd_cb->width;
    }

    Best regards,

    Edvin

  • Hi Edvin,

    Thanks for your advice. I will try with that modification or change the driver.

    Best regards,

    Jhon

Related