Shop OBEX P1 Docs P2 Docs Learn Events
Parallax Font on ST7735 TFT — Parallax Forums

Parallax Font on ST7735 TFT

Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
edited 2022-02-21 23:18 in Propeller 1

I recently purchased some 128x128 ST7735 color TFT displays from Adafruit:

https://www.adafruit.com/product/2088

I got these because there is Spin code available to drive them:

https://github.com/avsa242/st7735-spin

The code is rife with pre-processor stuff, and I tried it on both FlexProp and PropellerIDE, since both have compilers that support pre-processors. But I wasn't too happy with either option. As a consequence, I did the necessary pre-processing manually, stripping out all of the directives, so I could work with it on the good ol' Propeller Tool. Ahhh!

The only font that came with the software driver was a tiny 8x8 font that one would have to squint to see. For my app (more on that later), I needed a bigger font that could be read at a distance. The Parallax font built into the Propeller's ROM fit the bill perfectly. Plus, it can be scaled up and down quite readily.

So I wrote a driver in Spin that uses the Parallax font. Here's a photo of some sample output:

There's one more size (32x64) that takes up half the screen, but you get the idea.

Here's the front-end code that produces the above display:

CON

    _clkmode    = xtal1 + pll16x
    _xinfreq    = 5_000_000

' I/O pins
    CS_PIN      = 0
    SCK_PIN     = 1
    SDA_PIN     = 2
    DC_PIN      = 3
    RESET_PIN   = 4

' display dimensions
    WIDTH       = 128
    HEIGHT      = 128

OBJ

    dsp: "st7735"

PUB Start

    setup

    dsp.mirrorh(TRUE)                          ' change these to suit the
    dsp.mirrorv(TRUE)                          ' orientation of your display

    dsp.bg_color(dsp#BLACK)
    dsp.clear

    dsp.set_font(dsp#FONT8x16)
    dsp.fg_color(dsp#CYAN)
    dsp.pix_position(64, 0)
    dsp.ctr_str(string("FONT8x16"))

    dsp.set_font(dsp#FONT16x16)
    dsp.fg_color(dsp#YELLOW)
    dsp.pix_position(64, 16)
    dsp.ctr_str(string("16x16"))

    dsp.set_font(dsp#FONT8x32)
    dsp.fg_color(dsp#GREEN)
    dsp.pix_position(64, 32)
    dsp.ctr_str(string("FONT8x32"))

    dsp.set_font(dsp#FONT16x32)
    dsp.fg_color(dsp#MAGENTA)
    dsp.pix_position(64, 64)
    dsp.ctr_str(string("16x32"))

    dsp.set_font(dsp#FONT32x32)
    dsp.fg_color(dsp#ORANGE)
    dsp.pix_position(64, 96)
    dsp.ctr_str(string("3232"))
    repeat

PUB setup

    dsp.startx(CS_PIN, SCK_PIN, SDA_PIN, DC_PIN, RESET_PIN, WIDTH, HEIGHT)

The Spin code that draws characters to the screen is painfully sloooow! So my next task is to convert my character generator to PASM. This will be done using a small hub-RAM buffer to hold the pixels for a character at a time, which can then be blasted to the ST7735 all at once.

The object, with the PASM character driver, and its sub_objects are attached here.

-Phil

Comments

  • Hi Phil,

    That looks great with the bigger fonts...I hadn't yet attempted to use the P1 ROM font. The main reason I went with the small font I found was so more could be displayed at once, but obviously that's dependent on the application.

    Yeah, graphics definitely are not my strong suit, and agreed on the Char() lack of speed...it is all in SPIN, so that's the one excuse I'll throw out there :smiley:
    I originally wrote it with buffered displays in mind, so it handles things like scrolling if trying to draw past the last row.
    It definitely wakes up (well, some) when built using FlexSpin's native code output, though.
    The preprocessed/conditionally built code is there so I could reuse the same terminal I/O library as any other driver (or serial), as well as use the same graphics code, rather than re-implementing it (and thus maintaining it) for every single driver.

    Cheers

  • Hi Jesse,

    I didn't find your 8x8 code to be slow at all. But with bigger characters and all the conditional checking my Char() routine has to do to deal with different font sizes, things really get bogged down.

    BTW, thanks for providing such a thorough driver for this display! It would've taken me months to start from scratch, and I sorta have a Christmas deadline. :)

    -Phil

  • Really? I thought it was pretty slow - lol! Thanks all the same, though. I know what you mean about the conditional code. Every time I look at it, I think maybe there's something I can forego checking, or something I can take out of the innermost loop.
    You're welcome, and good luck on the project. :)

    Cheers

  • Hi Phil,

    I have a similar but 128x160 pixels LCD on a robot, and I really want to have bigger fonts on it. Please share your spin1 code for the driver ( I'm using propeller tool) and I will be very grateful.

    Cheers

  • Will do. I've got some tidying up to do first, and hopefully I can make it obvious how to expand to your display size. This might take awhile, unfortunately. I was able to transfer the character display to PASM, however, and it's very fast.

    -Phil

  • No hurry. Good luck!

    -Stefan

  • I finally got around to cleaning up and commenting my modifications of Jesse's code. It's attached in the first post.

    -Phil

  • avsa242avsa242 Posts: 452
    edited 2022-02-21 13:13

    @"Phil Pilgrim (PhiPi)"

    Thanks for sharing, this looks nice (and definitely fast!)
    I had to change a few calls in the demo code in the 1st post to get it to build (looks like the names of some methods changed). Below incorporates those changes, plus the other setup code to complete it:

    CON
    
        _clkmode    = xtal1 + pll16x
        _xinfreq    = 5_000_000
    
    ' I/O pins
        CS_PIN      = 0
        SCK_PIN     = 1
        SDA_PIN     = 2
        DC_PIN      = 3
        RESET_PIN   = 4
    
    ' display dimensions
        WIDTH       = 128
        HEIGHT      = 128
    
    OBJ
    
        dsp: "st7735"
    
    PUB Start
    
        setup
    
        dsp.mirrorh(TRUE)                          ' change these to suit the
        dsp.mirrorv(TRUE)                          ' orientation of your display
    
        dsp.bg_color(dsp#BLACK)
        dsp.clear
    
        dsp.set_font(dsp#FONT8x16)
        dsp.fg_color(dsp#CYAN)
        dsp.pix_position(64, 0)
        dsp.ctr_str(string("FONT8x16"))
    
        dsp.set_font(dsp#FONT16x16)
        dsp.fg_color(dsp#YELLOW)
        dsp.pix_position(64, 16)
        dsp.ctr_str(string("16x16"))
    
        dsp.set_font(dsp#FONT8x32)
        dsp.fg_color(dsp#GREEN)
        dsp.pix_position(64, 32)
        dsp.ctr_str(string("FONT8x32"))
    
        dsp.set_font(dsp#FONT16x32)
        dsp.fg_color(dsp#MAGENTA)
        dsp.pix_position(64, 64)
        dsp.ctr_str(string("16x32"))
    
        dsp.set_font(dsp#FONT32x32)
        dsp.fg_color(dsp#ORANGE)
        dsp.pix_position(64, 96)
        dsp.ctr_str(string("3232"))
        repeat
    
    pub setup
    
        dsp.startx(CS_PIN, SCK_PIN, SDA_PIN, DC_PIN, RESET_PIN, WIDTH, HEIGHT)
    

    Cheers

    EDIT: This also uncovered a potential bug I have in Reset(); my intent there was to only touch the I/O pin if the driver was started with RESET set to 0..31, but I must've left old code in there from when I started writing the driver, because it tries to bring the pin high first anyway, and then checks it, and wiggles it again (curiously, this mistake didn't make it into my spin2 driver). I've made the change to the driver, but here's the changes:

    old Reset()

    PUB Reset | i
    
    ' Reset the display controller
    
        outa[_RESET] := 1
        dira[_RESET] := 1
        if lookdown(_RESET: 0..31)                  ' I/O pin defined - hard reset
            outa[_RESET] := 1
            'time.usleep(10)
            outa[_RESET] := 0
            'time.usleep(10)
            outa[_RESET] := 1
            waitcnt(cnt + clkfreq / 1000 * 5)
        else                                        ' no I/O pin defined - do
            write_reg(core#SOFT_RESET, 0, 0)         '   soft reset instead
    

    new Reset()

    PUB Reset{}
    ' Reset the display controller
        if lookdown(_RESET: 0..31)                  ' I/O pin defined - hard reset
            outa[_RESET] := 1
            time.usleep(10)
            outa[_RESET] := 0
            time.usleep(10)
            outa[_RESET] := 1
            time.msleep(5)
        else                                        ' no I/O pin defined - do
            writereg(core#SOFT_RESET, 0, 0)         '   soft reset instead
    
  • Jesse,

    I'm not using the time object, so I changed your revised Reset method as follows:

    PUB Reset
    
    ' Reset the display controller
    
        if lookdown(_RESET: 0..31)                  ' I/O pin defined - hard reset
            outa[_RESET] := 1
            outa[_RESET] := 0 
            outa[_RESET] := 0                      ' Extend low time to 14 usec at 80 MHz.
            outa[_RESET] := 1
            waitcnt(cnt + clkfreq / 1000 * 5)
        else                                        ' no I/O pin defined - do
            write_reg(core#SOFT_RESET, 0, 0)         '   soft reset instead
    
    

    That, along with a correction in my revised line method will be included in the first post in a few minutes.

    -Phil

  • Just curious if anyone has tried this code with the P2? I tried it with my Edge board and pins on the low 32 ports, however I haven't been able to get it to do anything beyond reset the display. I'm using FlexProp BTW. As others have noted, using 8x8 or 6x8 fonts are incredibly small, so having more options would be amazing.

  • The driver uses the font that's in the P1's ROM. So, at the very least, you'll have to include that font in your program, since it's not native to the P2. Then the driver will have to modified to use the font at the font's RAM address, since it won't be at the same address as in the P1's ROM.

    -Phil

  • iseriesiseries Posts: 1,492
    edited 2022-03-27 10:35

    I copied those fonts off the P1 a while back and include them in my driver for these devices so that I have all the fonts. Maybe we need to copy this font into high flash and then have some small code to just copy it into high memory for use.

    In any case my drivers have a bulge in them.

    void ST7789_writeXChar(short x, short y, char c)
    {
      long *base;
      int offset;
      long v;
    
      if ((x < 0) || (y < 0) || (x >= _width) || (y >= _height))
        return;
    
      pinl(st7789.cs);
      ST7789_window(x, y, 32, 64);
    
      offset = c & 0xfe; // Two characters per location
      base = &PropellerFont[offset*16]; // jump to character position
    
      offset = 0;
      for (int i=0;i<64;i++)
      {
        if ((i & 0x01) == 1)
          v = base[offset++];
        else
          v = base[offset];
        if (c & 0x01)
          v = v >> 1;
    
        for (int j=0;j<16;j++)
        {
          if ((v & 0x01) == 1)
            Spi_Out(spi, 16, _fgc);
          else
            Spi_Out(spi, 16, _bgc);
          if ((v & 0x01) == 1)
            Spi_Out(spi, 16, _fgc);
          else
            Spi_Out(spi, 16, _bgc);
          v = v >> 2;
        }
      }
      pinh(st7789.cs);
    }
    

    I noticed when I was writing an ST7789 driver that the code and registers was very similar to the HX8357. They use the same registers and the startup code is similar.

    Mike

  • Hi Phil,
    Thank you very much for the code. My display is 160x128 1.8inch with ILI9163c. With some minor changes in the 'Preset_GreenTab128x128' section it worked. It looks very nice.
    -Stefan

  • Thanks, Stefan! 'Glad you got it working on the larger display. I have, too, but I'm a bit confused about the rotation settings, so haven't posted any code for it yet.

    -Phil

Sign In or Register to comment.