Read/intercept 20x4 LCD characters

Hello,
I’m trying to capture/intercept/read the characters being sent to a 4x20 LCD which is set to 4-bit mode.
I need to read every time the Enable line goes high. First nibble then the second nibble to build the 8bit character.

My testing code, needless to say it’s not giving correct results.

  1. if I correlate the codes to the ascii table its incorrect
  2. I’m not getting the number of binaries as the number of characters displayed every time the lcd is refreshed.

Im wondering if what I want to do is even possible?

according to this forum it is possible by reading the PORTS directly instead of individual IO.

Read data from LCD (4-bit mode) - Using Arduino / Displays - Arduino Forum

Any ideas would be welcomed.

void Read_LCD(void);

// setup() runs once, when the device is first turned on.
bool LCD_D7_STATE; //lcd is in 4 bit mode
bool LCD_D6_STATE; //lcd is in 4 bit mode
bool LCD_D5_STATE; //lcd is in 4 bit mode
bool LCD_D4_STATE; //lcd is in 4 bit mode
bool LCD_D3_STATE; //lcd is in 4 bit mode
bool LCD_D2_STATE; //lcd is in 4 bit mode
bool LCD_D1_STATE; //lcd is in 4 bit mode
bool LCD_D0_STATE; //lcd is in 4 bit mode
bool nibble = 0; //lcd is in 4 bit mode

char data_char [9];//holds the value D7,D6,D5,D4

#define LCD_D7 A3//data read 
#define LCD_D6 A2//data read 
#define LCD_D5 A1//data read 
#define LCD_D4 A0//data read 
#define LCD_E A5//20x4 enable line

void setup() {
  // Put initialization like pinMode and begin functions here.
  pinMode(LCD_D4, INPUT_PULLDOWN);//lcd 4 bit data mode
  pinMode(LCD_D5, INPUT_PULLDOWN);
  pinMode(LCD_D6, INPUT_PULLDOWN);
  pinMode(LCD_D7, INPUT_PULLDOWN);
  pinMode(LCD_E, INPUT_PULLUP);//lcd enable

  attachInterrupt(LCD_E, Read_LCD, CHANGE);
}

// loop() runs over and over again, as quickly as it can execute.
void loop() {
  // The core of your code will likely live here.
}

void Read_LCD() {
  if (nibble == 0){
    LCD_D7_STATE = pinReadFast(LCD_D7);
    LCD_D6_STATE = pinReadFast(LCD_D6);
    LCD_D5_STATE = pinReadFast(LCD_D5);
    LCD_D4_STATE = pinReadFast(LCD_D4);
  }  
  if (nibble == 1) {
    LCD_D3_STATE = pinReadFast(LCD_D7);
    LCD_D2_STATE = pinReadFast(LCD_D6);
    LCD_D1_STATE = pinReadFast(LCD_D5);
    LCD_D0_STATE = pinReadFast(LCD_D4);
  } 
  Serial.printf ("nibble %d\n" ,nibble);
  nibble = !nibble;
  sprintf(data_char, "%d%d%d%d%d%d%d%d",LCD_D0_STATE,LCD_D1_STATE,LCD_D2_STATE,LCD_D3_STATE,LCD_D4_STATE,LCD_D5_STATE,LCD_D6_STATE,LCD_D7_STATE);//convert bin data to string
  Serial.printf("data_char %s\n",data_char);
}

You absolutely don’t want Serial.print() in your ISR.
You also wouldn’t want any other heavy function call like snprintf() in there either.

I’d select a set of pins that are located on a single port, perform a single port read and do the interpretation of the gathered data outside of the ISR.

For monitoring an asynchronous, relatively high speed data stream, I’d also use SYSTEM_MODE(MANUAL) and avoid any network traffic.

1 Like

Thanks for that Scruff, I changed the code as per your suggestion. - split the port up and remove unwanted code from ISR.
I have tested my BIN to ASCII function and thats working if I do manual BIN entries

My problem still

  1. My port read is not done correctly or not fast enough - I found the below I dont knwo if there is a better way
    uint32_t port_state = nrf_gpio_port_in_read(NRF_GPIO);
    uint8_t pins_state = (port_state >> 24) & 0xFF;
  2. I am getting ASCII results but they all incorrect.
    output: B0B► 0@►"�b↨@0@►B0B► 0 �"�"0 ►♣�

BTW what happened to the add code button on the post? I only see Blockquote "

my updated code below

void Read_LCD(void);//keeps the Binary digits received
bool LCD_D7_STATE; //lcd is in 4 bit mode
bool LCD_D6_STATE; //lcd is in 4 bit mode
bool LCD_D5_STATE; //lcd is in 4 bit mode
bool LCD_D4_STATE; //lcd is in 4 bit mode
bool LCD_D3_STATE; //lcd is in 4 bit mode
bool LCD_D2_STATE; //lcd is in 4 bit mode
bool LCD_D1_STATE; //lcd is in 4 bit mode
bool LCD_D0_STATE; //lcd is in 4 bit mode
int nibble = 0; //lcd is in 4 bit mode this show what nibble s read
bool new_LCD_char_arrived = 0;
uint8_t BIN_received[] ="00000000"; 
char lcd_char_Bin_string[9];

#define LCD_D7 A5//data read  P0.31
#define LCD_D6 A4//data read  P0.30
#define LCD_D5 A3//data read  P0.29
#define LCD_D4 A2//data read  P0.28
#define LCD_E D2//20x4 enable line P1.01

void setup() {
  pinMode(LCD_D4, INPUT_PULLDOWN);//lcd 4 bit data mode
  pinMode(LCD_D5, INPUT_PULLDOWN);
  pinMode(LCD_D6, INPUT_PULLDOWN);
  pinMode(LCD_D7, INPUT_PULLDOWN);
  pinMode(LCD_E, INPUT_PULLUP);//lcd enable
  attachInterrupt(LCD_E, Read_LCD, CHANGE);
}

void loop() {
  if (new_LCD_char_arrived == 1){
    //uint8_t buf[9] ="00110111";// "00110111" this gives the correct ascii value of 7 so function is working
    //sprintf (lcd_char_Bin_string,"%d%d%d%d%d%d%d%d",0,1,1,1,1,1,0,0,'\0');// used to test and all correct values
     sprintf (lcd_char_Bin_string,"%d%d%d%d%d%d%d%d",BIN_received[7],BIN_received[6],BIN_received[5],BIN_received[4],BIN_received[3],BIN_received[2],BIN_received[1],BIN_received[0],'\0');

    char *endptr;
    uint8_t value = strtoul((char*)lcd_char_Bin_string, &endptr, 2);

    if (endptr == (char*)lcd_char_Bin_string){
      Serial.println("No digits found");
    }
    else if (*endptr != '\0'){
      Serial.println("Non-digit found after digits");
    }
    else{
      Serial.write(value);
      Serial.print("\n");
    }
  }
  new_LCD_char_arrived = 0;
}

void Read_LCD() { 
  if (nibble == 0){
    //Serial.printf ("nibble =0\n");
    BIN_received[3] = pinReadFast(LCD_D4);
    BIN_received[2] = pinReadFast(LCD_D5);
    BIN_received[1] = pinReadFast(LCD_D6);
    BIN_received[0] = pinReadFast(LCD_D7);
    nibble = 1;// increase by one to set up 2nd nibble
  }
  if (nibble == 1){
    // Serial.printf ("nibble =1\n");
    BIN_received[7] = pinReadFast(LCD_D4);
    BIN_received[6] = pinReadFast(LCD_D5);
    BIN_received[5] = pinReadFast(LCD_D6);
    BIN_received[4] = pinReadFast(LCD_D7);
    nibble = 0;// set back up for 1st nibble
    new_LCD_char_arrived = 1;// show main loop to print the ascii char of byte received
  }
}

Thanks for any insight or suggestions

It looks like the icon changed, but it’s still there

Does not come up on my edge or chrome browser
image

1 Like

Same for me


(using Chrome and Edge)

It’s there for Firefox

@melt777 I have some observations:

  • Why are you using INPUT_PULLDOWN mode? The MCU driving the display likely is using
    push-pull outputs where the pins are always HIGH or LOW. Adding pull-downs may not be a good idea.
  • The arrangement of GPIO pins on all Particle products is not amenable to whole-port reading. In your case, you want to read 4 bits at a time and the only pins that are contiguous on the Argon are P0.28 to P0.31 (ADC2 to ADC5). Reading P0, shifting by 28 bits and masking will produce the data you need.

  • The MCU writing to the display typically will setup the data on its ports along with RS and R/W pins then toggles the ENABLE pin to write to the display. With a 4-bit interface, the MCU will write two 4-bit values to the display in quick succession. The timing between the two writes is likely too fast to catch on the Argon, even using interrupts. The time between ENABLE edges will be too fast (due to interrupt latency) and the time the data is held on the Argon port may be too short to sample.

Looking at the Arduino thread you linked to, the discussion talks about the display timing issues and the possibility of needing a hardware latch to capture the data. Understanding the timing on your display would be a good place to start but you may need an oscilloscope for that.

1 Like

Hi Peekay, Thanks for that, I have tried INPUT_PULLDOWN and INPUT_PULLUP. The fact that the LCD still operate correctly I dont think it has such a big influence on the microPIC driving the LCD. I will take your advice though and just leave them as INPUT.

your point two is what I have been trying to figure out, how do you read a PORT as a whole, I looked everywhere and can find the command. is there a read.Port(P0) that I dont know about?

Your last point, I am concerned about, is this even possible? I am well aware of setup times and hold times but thought I would give it a bash anyway :slightly_smiling_face:. The fact that the ASCII (even though incorrect) is constant and not random with the LCD display (even when only one or two characters change) give me hope.

On the updated code below I have added the RS signal which should now filter out the instruction code from actual data. latching on the LCD are as follow
DB0-DB7 H/L
Enable H,H->L:
RS register select H/L

I have changed my interrupt to Falling.

void Read_LCD(void);
bool LCD_D7_STATE; //lcd is in 4 bit mode
bool LCD_D6_STATE; //lcd is in 4 bit mode
bool LCD_D5_STATE; //lcd is in 4 bit mode
bool LCD_D4_STATE; //lcd is in 4 bit mode
bool LCD_D3_STATE; //lcd is in 4 bit mode
bool LCD_D2_STATE; //lcd is in 4 bit mode
bool LCD_D1_STATE; //lcd is in 4 bit mode
bool LCD_D0_STATE; //lcd is in 4 bit mode
bool LCD_RS_STATE; //register selec state 1:Data 0:instruction
int nibble = 0; //lcd is in 4 bit mode this show what nibble s read
bool new_LCD_char_arrived = 0;
uint8_t BIN_received ="000000000";
char lcd_char_Bin_string[9];

#define LCD_D7 A5//data read P0.31
#define LCD_D6 A4//data read P0.30
#define LCD_D5 A3//data read P0.29
#define LCD_D4 A2//data read P0.28
#define LCD_E D2//20x4 enable line P1.01
#define LCD_RS D3//20x4 enable line P1.01

void setup() {
// Put initialization like pinMode and begin functions here.

pinMode(LCD_D4, INPUT);//lcd 4 bit data mode
pinMode(LCD_D5, INPUT);
pinMode(LCD_D6, INPUT);
pinMode(LCD_D7, INPUT);
pinMode(LCD_E, INPUT);//lcd enable
pinMode(LCD_RS, INPUT);//lcd enable
attachInterrupt(LCD_E, Read_LCD, FALLING);
}

void loop() {

if (new_LCD_char_arrived == 1){
//uint8_t buf[9] ="00110111";// "00110111" this gives the correct ascii value of 7 so function is working
//sprintf (lcd_char_Bin_string,"%d%d%d%d%d%d%d%d",0,1,1,1,1,1,0,0,'\0');// used to test and all correct values
sprintf (lcd_char_Bin_string,"%d%d%d%d%d%d%d%d",BIN_received[7],BIN_received[6],BIN_received[5],BIN_received[4],BIN_received[3],BIN_received[2],BIN_received[1],BIN_received[0],'\0');

char endptr;
uint8_t value = strtoul((char
)lcd_char_Bin_string, &endptr, 2);

if (endptr == (char*)lcd_char_Bin_string){
Serial.println("No digits found");
}
else if (*endptr != '\0'){
Serial.println("Non-digit found after digits");
}
else{
Serial.write(value);
// Serial.print("\n");
}
}
new_LCD_char_arrived = 0;
}

void Read_LCD()

{ LCD_RS_STATE = pinReadFast(LCD_RS);

if ((nibble == 0) && (LCD_RS_STATE == 1));{
//Serial.printf ("nibble =0\n");
BIN_received[3] = pinReadFast(LCD_D4);
BIN_received[2] = pinReadFast(LCD_D5);
BIN_received[1] = pinReadFast(LCD_D6);
BIN_received[0] = pinReadFast(LCD_D7);
nibble = 1;// increase by one to set up 2nd nibble
}

if ((nibble == 1) && (LCD_RS_STATE == 1)){
// Serial.printf ("nibble =1\n");
BIN_received[4] = pinReadFast(LCD_D4);
BIN_received[5] = pinReadFast(LCD_D5);
BIN_received[6] = pinReadFast(LCD_D6);
BIN_received[7] = pinReadFast(LCD_D7);
nibble = 0;// increase by one to set up 2nd nibble
new_LCD_char_arrived = 1;// show main loop to print the ascii char of byte received
}
}

Hi @melt777

You really only have 8-bits of storage available in your ISR so when the LCD is written and you are eavesdropping, you can only store one byte (two nibbles) before you start the slow string conversions and serial write in your main loop. It wouldn’t surprise me if you miss data this way.

I would use a FIFO here–an array of data from the ISR and instead of single variable new_LCD_char_arrived, I would use a counter. You increment the count as you put data in from the ISR and decrement it as you pull it out in the main loop. You probably need separate read and write pointers or array addresses for the FIFO. This will make the code more robust to the timing of the slow routines to dump the data.

I would also declare the variables that communicate from the ISR to the main loop as volatile so that the compiler know there are asynchronous writes happening.

4 Likes

Thanks BKO. I will try as you suggested as soon as im able to extract an ascii value effienctly from the port.I have hardwired the D0-D4 to dipswitches for testing.

The following works but is too time consuming, also note I have to add the terminator ‘\0’ for it to work

The output ascii corresponds to my dipswitch settings what ever they may be.

sprintf (lcd_char_Bin_string,"%d%d%d%d%d%d%d%d",BIN_received[7],BIN_received[6],BIN_received[5],BIN_received[4],BIN_received[3],BIN_received[2],BIN_received[1],BIN_received[0],'\0');    
      char *endptr;
      uint8_t value = strtoul((char*)lcd_char_Bin_string, &endptr, 2);
      Serial.write(value);
      }

image

trying to use the below in my ISR to save ASCII to buffer but its always 0

char BIN_received[9];

 char *endptr;
      uint8_t value1 = strtoul((char*)BIN_received, &endptr, 2);
          Serial.write(value1);

here my value is always 0
image
I can clearly see the difference but dont know what to do

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.