Set Decimal Point on PCF8566 LCD controller

Merry Christmas everyone. I am repurposing a front panel and its electronics for a small ham radio transceiver. I am using an Arduino to drive the existing displays and read the knobs and switches. Everything is going fine with the hardware and my Arduino software except one small detail.

The display uses a PCF8566 LCD controller to drive an 8 character 7 segment display. I have all the code working for the display except for setting the decimal point on any of the digits.

The PCF8566 is running in 1:4 mode, and in this particular hardware the DP is mapped to the LSB of the byte for a given digit. The problem is, in 1:4 mode, the LSB is ignored when you write each digit/byte. So setting the DP involves writing to the chip digits register in a way that I cannot figure out.

Thanks in advance
Dudley

Please supply some clickable links so we don't have to google all that stuff... you told us nothing really, about the panel. Do you have links to that also, or specs that you can post? Images?

Thanks for the quick response. I didn't supply any more details becauses the problem is unique to the 8566 chip itself. I was hoping to find someone who has had experience with it.

I don't find much at all on Google except for the chip's spec sheet, and one simple Arduino sketch for testing it. The test sketch just writes to each segment of the digits with loops. But it does not address the extra bit for each digit that I need to write to.

I would be glad to supply all kinds of detail. A link to the specs/app notes for the PCF8566 and my C++ class that works well except for this one problem.

Referring to Fig. 11 in the Specs, they show what happens to a given digit byte that is sent to the chip. My chip is being used in the 1:3 mode, so notice that a byte is broken up into 3 bit pieces and stored into successive memory locations in the chip, The hardware mapping on my the board I am using maps the DP bit to a down arrow symbol under the digit. The spec shows that the second MSB is DP, but in my hardware, that is mapped to that down arrow. As seen in the stock photo below of the original receiver. Notice that in Fig. 11, the third bit in the location n+2 is "x" which means "don't care". Since those three locations come from a given byte that is sent, that x position is the 9th bit.

My question is, how do I send the 9th bit properly without messing up the other bits in the PCF8655 memory. In the code below, my method setDecimalPointRaw() is my attempt at doing it. It doesn't work.

image

And here is my code. Except for the decimal point, this code works fine for displaying a string on the 8 digit 7 segment display. With this and other code, I can simulate the original operation of the front panel, except for the DP.

From my .h file, here is the mapping for the existing hardware:

/* Display Ram
          -----------------
          | n | n+1 | n+2 |
          |----------------
     BP 0 | f |  a  |  b  |
        1 | e |  g  |  c  |
        2 | ^ |  d  |  dp |
        3 | x |  x  |  x  |
          -----------------

   Transmit MSB     LSB
             fe^agdbc

*/
/*  LED segments
     aaa
    f   b
    f   b
     ggg
    e   c
    e   c
     ddd  dp

      v
     v v
    v   v

*/
#include <Wire.h>
#include "K2PCF8566.h"

K2PCF8566::K2PCF8566(int address) {
  i2cSlave = address;
  _carets = 0x00;
  _decimalPoint = 0;

  for (int i = 0; i < 8; i++) {
    _textMessage[i] = 0;
  }
}

void K2PCF8566::init() {
  _startMsg();
  _write(PCF_CMD_SET_MODE | PCF_MODE_ENABLE | PCF_MODE_MULTIPLEX_3);
  _endMsg();
  SetTextMessage(_textMessage);
  Serial.println("init PCF8566");
}

void K2PCF8566::SetTextMessage(const char* textMessage) {
  for (int i = 0; i < 8; i++) {
    if (textMessage[i] != 0) {
      _textMessage[i] = textMessage[i];
    }
    else {
      _textMessage[i] = ' ';
    }
  }

  textToLedBytes();
  ledBytesToSegments();
  setMemory();
  setDecimalPointRaw();
}

void K2PCF8566::SetCaret(uint8_t caret, bool onOff) {
  if (onOff == true) _carets |= (1 << caret);
  else _carets &= ~(1 << caret);
}

void K2PCF8566::SetDecimalPoint(uint8_t pos) {
  _decimalPoint = (pos > 8) ? 8 : pos;
}

void K2PCF8566::textToLedBytes() {
  for (int i = 0; i < 8; i++) {
    switch (_textMessage[i]) {
      case '0': _ledBytes[i] = LED_0; break;
      case '1': _ledBytes[i] = LED_1; break;
      case '2': _ledBytes[i] = LED_2; break;
      case '3': _ledBytes[i] = LED_3; break;
      case '4': _ledBytes[i] = LED_4; break;
      case '5': _ledBytes[i] = LED_5; break;
      case '6': _ledBytes[i] = LED_6; break;
      case '7': _ledBytes[i] = LED_7; break;
      case '8': _ledBytes[i] = LED_8; break;
      case '9': _ledBytes[i] = LED_9; break;

      case 'u': _ledBytes[i] = LED_u; break;
      case 'U': _ledBytes[i] = LED_U; break;
      case 'l': _ledBytes[i] = LED_l; break;
      case 'L': _ledBytes[i] = LED_L; break;
      case 'c': _ledBytes[i] = LED_c; break;
      case 'C': _ledBytes[i] = LED_C; break;
      case 'h': _ledBytes[i] = LED_h; break;
      case 'H': _ledBytes[i] = LED_H; break;
      case 'b': _ledBytes[i] = LED_b; break;
      case ' ': _ledBytes[i] = LED_Space; break;
      default: _ledBytes[i] = LED_Space;
    }
    bool caretOn = (((1 << i) & _carets) > 0);
    _ledBytes[i] = (caretOn) ? _ledBytes[i] | CARET_BIT : _ledBytes[i] & ~CARET_BIT;
  }
  // Serial.println(_ledBytes[0], BIN);
}

void K2PCF8566::ledBytesToSegments() {
  for (int i = 0; i < 8; i++) {
    int j = i * 3;
    _segments[j] = _ledBytes[i] & 0xe0;
    _segments[j + 1] = (_ledBytes[i] << 3) & 0xe0;
    _segments[j + 2] = (_ledBytes[i] << 6) & 0xe0;
  }
}

void K2PCF8566::setMemory() {
  // unsigned char bits = 0b00100000;
  _startMsg();
  delayMicroseconds(10);
  _write(PCF_CMD_SELECT_DEVICE(0) | PCF_CONTINUE);
  _write(PCF_CMD_LOAD_DATA_POINTER(0));
  for (int i = 0; i < 8; i++) {
    _write(_ledBytes[i]);
   }
  _endMsg();
}

void K2PCF8566::setDecimalPointRaw() {
  Serial.println("dp ");
   Serial.println(_decimalPoint);

 if (_decimalPoint == 0) return;
  
  uint8_t memOffset = ((_decimalPoint - 1) * 3) + 2;
  uint8_t msg = ((_segments[memOffset] & 0xe0) | 0x20)
               | ((_segments[memOffset + 1] & 0xe0) >> 3)
                   | ((_segments[memOffset + 2] & 0xe0) >> 6);
  _startMsg();
  delay(1);
  _write(PCF_CMD_SELECT_DEVICE(0) | PCF_CONTINUE);
  delay(1);
  _write(PCF_CMD_LOAD_DATA_POINTER(0));
  delay(1);
  _write(msg);

  delay(1);
  _endMsg();
  delay(1);
}

void K2PCF8566::_write(char msg) {
  Wire.write(msg);
}

void K2PCF8566::_startMsg() {
  Wire.beginTransmission(i2cSlave);
}

void K2PCF8566::_endMsg() {
  int error = Wire.endTransmission();
  if (error != 0) {
    Serial.print("Error endTransmission() == ");
    Serial.println(error);
  }
}

//© 2021 GitHub, Inc.

But it could help shed some light. Please post it. Also please post "your C++ class that works well".

It's unlikely that you will find anyone versed in the chip, before the thread falls off the page. But very often, people can diagnose from scratch.

I edited my last comment and put in all the detail. I didn't expect anyone to go down to bare copper for me, but you are probably right that it is unlikely I will find anyone versed in the chip.

I would be using a nice new OLED display if this were a green field project. But this front panel is just what I need for a small low power transceiver project. The rest of it is all digital signal processing.