библиотека PCD8544 для дисплея Nokia 5110

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Здравствуйте. Имеется дисплей Nokia 5110 и arduino uno. С сайта http://www.count-zero.ru/tags/pcd8544/ взял реализацию библиотеки для таких дисплеев, подключил как расписано к библиотеке, загрузил несколько примеров с выводом чисел и символов, строк, графики, все работало. Не скажу что Adafruit меня не устраивает, но эта мне проще и понятнее. 

Возникло затруднение, когда понадобилось выводить линию функцией  void pcd8544_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);

ПС

#include "avr/io.h"
#include "util/delay.h"
#include "pcd8544_soft.h"
  uint8_t i=0;  
void setup() {
    pcd8544_init();
    pcd8544_clear();
}
void loop() {
   pcd8544_draw_line(0,24,0,24);      
}

Скетч компилируется, но на экране ничего не отображается

Графическая емкость дисплея равна 48 х 84, значит по идее должно что нибудь бы отобразиться, но не понимаю ошибку

Заголовочный файл pcd8544_soft.h

#ifndef __PCD8544_SOFT_H__
#define __PCD8544_SOFT_H__

#define PIN_SCE   PD7
#define PIN_RESET PD6
#define PIN_DC    PD5
#define PIN_SDIN  PD4
#define PIN_SCLK  PD3

#define LCD_C     0x00
#define LCD_D     0x01

#define LCD_X     84
#define LCD_Y     48

#define PORT_PCD8544 PORTD
#define DDR_PCD8544  DDRD

extern void pcd8544_init(void);
extern void pcd8544_send(uint8_t dc, uint8_t data);
extern void pcd8544_print_string(char *str);
extern void pcd8544_send_char(uint8_t ch);
extern void pcd8544_clear(void);
extern void pcd8544_set_cursor(uint8_t x, uint8_t y);
extern void pcd8544_print_at(char *str, uint8_t size, uint8_t x, uint8_t y);
extern void pcd8544_send_char_size2(uint8_t ch, uint8_t x, uint8_t y);
extern void pcd8544_send_char_size3(uint8_t ch, uint8_t x, uint8_t y);
extern void pcd8544_print_uint8_at(uint8_t num, uint8_t size, uint8_t x, uint8_t y);
extern void pcd8544_clear_fb(void);
extern void pcd8544_display_fb();
extern void pcd8544_send_char_fb(uint8_t ch);
extern void pcd8544_set_point(uint8_t x, uint8_t y);
extern void pcd8544_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);

#endif

Файл с кодом pcd8544_soft.cpp

#include "ascii.h"
#include "pcd8544_soft.h"
#include <stdio.h>
#include <stdlib.h>

#define FB_LEN  504
static uint8_t fb[FB_LEN];
uint16_t pos;

void pcd8544_init(void)
{
   // GPIO Setup
   DDR_PCD8544 |= (1<<PIN_SCE) | (1<<PIN_RESET) | (1<<PIN_DC) | (1<<PIN_SDIN) | (1<<PIN_SCLK);
   PORT_PCD8544&=~(1<<PIN_RESET);
   PORT_PCD8544|=(1<<PIN_RESET);
   pcd8544_send(LCD_C, 0x21 );  // LCD Extended Commands.
   pcd8544_send(LCD_C, 0xBA );  // Set LCD Vop (Contrast).
   pcd8544_send(LCD_C, 0x04 );  // Set Temp coefficent. //0x04
   pcd8544_send(LCD_C, 0x14 );  // LCD bias mode 1:48. //0x13
   pcd8544_send(LCD_C, 0x20 );  // LCD Basic Commands
   pcd8544_send(LCD_C, 0x0C );  // LCD in normal mode.
}

void pcd8544_send(uint8_t dc, uint8_t data)
{
   uint8_t i;
   if (dc == LCD_D)
      PORT_PCD8544 |= (1<<PIN_DC);
   else
      PORT_PCD8544 &= ~(1<<PIN_DC);

   PORT_PCD8544&=~(1<<PIN_SCE);
   for (i=0; i<8; i++)
   {
      PORT_PCD8544=(data & 0x80) ? PORT_PCD8544 | (1<<PIN_SDIN) : PORT_PCD8544 & ~(1<<PIN_SDIN);

      data=(data<<1);

      PORT_PCD8544|=(1<<PIN_SCLK);
      PORT_PCD8544&=~(1<<PIN_SCLK);
   }
   PORT_PCD8544|=(1<<PIN_SCE);
}

void pcd8544_print_string(char *str)
{
   while (*str)
   {
      pcd8544_send_char(*str++);
   }
}

void pcd8544_send_char(uint8_t ch)
{
   int i;
   char * ptr= (char *)(&ASCII);
   if (ch >= 0x20 && ch <= 0x80)
   {
      pcd8544_send(LCD_D, 0x00);
      for (i = 0; i < 5; i++)
      {
       		uint8_t c=ch - 0x20;
            c=pgm_read_byte(ptr+c*5+i);
        	pcd8544_send(LCD_D,  c);
      }
      pcd8544_send(LCD_D, 0x00);
   }
}
void pcd8544_clear(void)
{
   int i;
   for (i=0; i < LCD_X * LCD_Y / 8; i++)
   {
      pcd8544_send(LCD_D, 0x00);
   }
}

void pcd8544_set_cursor(uint8_t x, uint8_t y) {
    x=x%12; y=y%6;
    pcd8544_send(LCD_C, 0x40+y);
    pcd8544_send(LCD_C, 0x80+x*7);
}

void pcd8544_print_at(char *str, uint8_t size,  uint8_t x, uint8_t y)
{
    uint8_t i=0;
    pcd8544_set_cursor(x,y);
    switch (size) {
      case 3:
      while (*str)
      {
         pcd8544_send_char_size3(*str++,x+i,y);
         i+=3;
      }
      break;
      case 2:
      while (*str)
      {
        pcd8544_send_char_size2(*str++,x+i,y);
        i+=2;
      }
      break;
      default:
      while (*str)
      {
        pcd8544_send_char(*str++);
      }
      break;
   }
}

void pcd8544_send_char_size2(uint8_t ch, uint8_t x, uint8_t y) {
    uint8_t s[5]; // source
    uint8_t r[20]; // result
    uint8_t i,j;
   // get littera
    char * ptr= (char *)(&ASCII);
    if (ch >= 0x20 && ch <= 0x80)
    {
        for (i=0; i < 5; i++)
        {
              uint8_t c=ch - 0x20;
              s[i]=pgm_read_byte(ptr+c*5+i);
        }
    }
    // scale
    for(i=0;i<5;i++)
    {
        uint8_t b=0;
        uint8_t a=0;
        for(j=0;j<4;j++)
        {
            b=(s[i]>>j) & 0x01;
            a|=(b<<(j<<1)) | (b<<((j<<1)+1));
        }
        r[(i<<1)]=a;
        r[(i<<1)+1]=a;
    }

    for(i=0;i<5;i++)
    {
        uint8_t b=0;
        uint8_t a=0;
        for(j=0;j<4;j++)
        {
            b=(s[i]>>(j+4)) & 0x01;
            a|=(b<<(j<<1)) | (b<<((j<<1)+1));
        }
        r[(i<<1)+10]=a;
        r[(i<<1)+11]=a;
    }
    // print
    pcd8544_set_cursor(x,y);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);
    for(i=0;i<10;i++)
        pcd8544_send(LCD_D, r[i]);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);

    pcd8544_set_cursor(x,y+1);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);
    for(i=10;i<20;i++)
        pcd8544_send(LCD_D, r[i]);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);
}
void pcd8544_send_char_size3(uint8_t ch, uint8_t x, uint8_t y) {
    uint8_t s[5]; // source
    uint8_t r[45]; // result
    uint8_t i;
    // get littera
    char * ptr= (char *)(&ASCII);
    if (ch >= 0x20 && ch <= 0x80)
    {
        for (i=0; i < 5; i++)
        {
              uint8_t c=ch - 0x20;
              s[i]=pgm_read_byte(ptr+c*5+i);
        }
    }
    // scale
    for(i=0;i<5;i++)
    {
        uint8_t b,a;
        b=(s[i] & 0x01);
        a=(b) ? 0x7 : 0;
        b=(s[i]>>1) & 0x01;
        if (b) a|=0x38;
        b=(s[i]>>2) & 0x01;
        a|=(b<<6)|(b<<7);

        r[i*3]=a;
        r[i*3+1]=a;
        r[i*3+2]=a;

        r[i*3+15]=b;
        r[i*3+16]=b;
        r[i*3+17]=b;
    }

    for(i=0;i<5;i++)
    {
        uint8_t b,a;
        b=(s[i]>>3) & 0x01;
        a=(b) ? 0x0e : 0;
        b=(s[i]>>4) & 0x01;
        if (b) a|=0x70;
        b=(s[i]>>5) & 0x01;
        a|=(b<<7);

        r[i*3+15]|=a;
        r[i*3+16]|=a;
        r[i*3+17]|=a;
     }

    for(i=0;i<5;i++)
    {
        uint8_t b,a;
        b=(s[i]>>5) & 0x01;
        a=(b) ? 0x3 : 0;
        b=(s[i]>>6) & 0x01;
        if (b) a|=0x1c;
        b=(s[i]>>7) & 0x01;
        if (b) a|=0xe0;

        r[i*3+30]=a;
        r[i*3+31]=a;
        r[i*3+32]=a;
     }

    // print
    pcd8544_set_cursor(x,y);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);
    for(i=0;i<15;i++)
        pcd8544_send(LCD_D, r[i]);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);

    pcd8544_set_cursor(x,y+1);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);
    for(i=15;i<30;i++)
        pcd8544_send(LCD_D, r[i]);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);

    pcd8544_set_cursor(x,y+2);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);
    for(i=30;i<45;i++)
        pcd8544_send(LCD_D, r[i]);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);
    pcd8544_send(LCD_D, 0x00);
}

void pcd8544_print_uint8_at(uint8_t num, uint8_t size, uint8_t x, uint8_t y){
    uint8_t sym[3];
    int8_t i=2;
    do  {
      if (num == 0 && i<2)
        sym[i]=0x20; // space
      else
        sym[i]=0x30+num%10;

      num=num/10;
      i--;

    } while (i>=0);

    uint8_t j=0;
    for (i=0;i<3;i++)
    {
        if (!(i<2 && sym[i] == 0x20))
        {
            switch(size) {
            case 3:
                pcd8544_send_char_size3(sym[i],x+j*size,y);
                break;
            case 2:
                pcd8544_send_char_size2(sym[i],x+j*size,y);
                break;
            default:
                pcd8544_send_char(sym[i]);
                break;
            }
            j++;
        }
    }
}

void pcd8544_clear_fb(void)
{
    for(pos=0;pos<FB_LEN; pos++)
        fb[pos]=0;
    pos=0;
}

void pcd8544_display_fb() {
    int i;
    for(i=0;i<FB_LEN; i++)
        pcd8544_send(LCD_D,fb[i]);
}

void pcd8544_send_char_fb(uint8_t ch)
{
    int i;
    char * ptr= (char *)(&ASCII);

    if (ch >= 0x20 && ch <= 0xf0 && pos <= (FB_LEN-7))
    {
        for (i=0; i < 5; i++)
        {
            uint8_t c=(ch<0xe0) ? ch - 0x20 : ch - 0x50;
            c=pgm_read_byte(ptr+c*5+i);
            fb[pos+i]|=c;
        }
        pos+=7;
    }
}

void pcd8544_set_point(uint8_t x, uint8_t y) {
    if (x < LCD_X && y < LCD_Y)
    {
        uint16_t index = ((y>>3)*LCD_X)+x;
        fb[index]|=(1<<(y&0x07));
    }
}

void pcd8544_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2) {
    const int deltaX = abs(x2 - x1);
    const int deltaY = abs(y2 - y1);
    const int signX = x1 < x2 ? 1 : -1;
    const int signY = y1 < y2 ? 1 : -1;

    int error = deltaX - deltaY;

    pcd8544_set_point(x2,y2);
    while(x1 != x2 || y1 != y2)
    {
        pcd8544_set_point(x1,y1);
        const int error2 = error * 2;

        if(error2 > -deltaY)
        {
            error -= deltaY;
            x1 += signX;
        }
        if(error2 < deltaX)
        {
            error += deltaX;
            y1 += signY;
        }
    }
}

 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Не уточнил, это функция рисования прямой линии. Построена на основе функции рисования точки pcd8544_set_point по несложному алгоритму.

На сайте об этой функции так и сказано

"Какого-то впечатляющего примера для проверки этих функций я не нашел, поэтому предлагаю проверить их сразу в деле."

 

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

BuonanotteMasha пишет:

Не уточнил, это функция рисования прямой линии. Построена на основе функции рисования точки pcd8544_set_point по несложному алгоритму.

На сайте об этой функции так и сказано

"Какого-то впечатляющего примера для проверки этих функций я не нашел, поэтому предлагаю проверить их сразу в деле."

Пару-тройку вопросов:

1. А какие-либо другие функции отображения Вы пробовали?

2. Есть ли к библиотеке примеры, и пробовали ли Вы их?

3. Среди заголовков есть что-то, оканчивающееся на _fb, а в тексте библиотеки массив fb равный по размеру экрану, который по смыслу может быть фреймбуфером. Вы не пытались понять, как это либо использовать, либо отключить?

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

andriano, спасибо, отвечу вначале на первый вопрос. Да пробывал к примеру отображение символа и целого числа

#include "avr/io.h"
#include "util/delay.h"
#include "pcd8544_soft.h"
#include <avr/pgmspace.h>

uint8_t i=0;

void setup() 
{
   pcd8544_init();
}
   
void loop() {
      pcd8544_clear();
      pcd8544_print_at(">",3,0,2);
      pcd8544_print_uint8_at(i++,3,3,2);
      _delay_ms(1000);
}

Добавление фреймбуфера. Программа рисования фрактала.

#include "avr/io.h"
#include "util/delay.h"
#include "pcd8544_soft.h"

int8_t x[17];
int8_t y[17];
    
void setup()
{  
    pcd8544_init();
    pcd8544_clear();
}
    
void loop ()
{
   pcd8544_clear_fb();

   uint8_t i,j,k;
   for(i=1;i<=4;i++){
       for(j=1;j<=4;j++) {
            k=4*i+j-4;
            x[k]=j-3;
            y[k]=i-3;
       }
   }

   x[2]=0;   y[2]=-3;
   x[8]=2;   y[8]=0;
   x[9]=-3;  y[9]=-1;
   x[15]=-1; y[15]=2;

   for(i=1;i<=16;i++){
        for(j=1;j<=16;j++) {
            for(k=1;k<=16;k++)
            {
                int8_t xx=(((x[i]<<4)+(x[j]<<2)+x[k])>>1);
                int8_t yy=(((y[i]<<4)+(y[j]<<2)+y[k])>>1);
                pcd8544_set_point(50+xx,28+yy);
            }
        }
    }

    pcd8544_display_fb();

    _delay_ms(1000);
}

2 Примеры показаны на сайте http://www.count-zero.ru/2017/pcd8544/. Там же сказано  и про фреймбуфер дисплей, который равен 504 байта. 

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Если я правильно вас понимаю, то фреймбуфер это область видеопамяти дисплея, куда складываются значения в виде набора байт. То есть обращаясь к определенному байту мы покажем выделенную ячейку на дисплее.

Алгоритм понял

1 Очистить фреймбуфер

2 Записать туда значения

3 Вывод содержимого на дисплей

4 Возврат к п. 1

Тогда разберусь

--------------------------

Спасибо все работает. Отображаются точки на дисплее, теперь и с линией разберусь

#include "avr/io.h"
#include "util/delay.h"
#include "pcd8544_soft.h"
    
void setup()
{  
    pcd8544_init();
    pcd8544_clear();
}
    
void loop ()
{
  pcd8544_clear_fb();
  
  pcd8544_set_point(68,27);
        pcd8544_set_point(60,27);
        pcd8544_set_point(70,27);
        pcd8544_set_point(68,28);
        pcd8544_set_point(69,28);
        pcd8544_set_point(70,28);
        pcd8544_set_point(68,29);
        pcd8544_set_point(69,29);
        pcd8544_set_point(70,29);

   pcd8544_display_fb();
   _delay_ms(1000);
}

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

BuonanotteMasha пишет:

Если я правильно вас понимаю, то фреймбуфер это область видеопамяти дисплея, куда складываются значения в виде набора байт. То есть обращаясь к определенному байту мы покажем выделенную ячейку на дисплее.

Я не совсем понял, какое отношение лично я имею к фреймбуферу вообще, и к тому, как Вы понрмаете его работу, но понимаете ее Вы, похоже не совсем правильно.

Одна точка на экране соответствует одному биту. Минимально адресуемая единица информации - один байт. На экране он соответствует восьми стоящим рядом точкам. Просто так невозможно изменить один бит в байте, т.к. у него нет адреса. Приходится читать байт целиком, править в нем нужный бит и записывать байт целиком обратно. Если просто записать байт с установленным нужным битом, то попрртятся остальные 7 битов, т.е. на экране в 7 смежных пикселях будет мусор.

Но проблема в том, что записать на экран байт можно всегда, а вот прочесть из него - нет. Поэтому приходится хранить в оперативной памяфти копию экрана (тот самый фреймбуфер), время от времени переправляя ее на экран либо целиком, либо по частям.

BuonanotteMasha
BuonanotteMasha аватар
Offline
Зарегистрирован: 02.01.2018

Благодарю, понятно разъяснили)