Trouble displaying data to NHD-C12832A1Z-FSW-FBW-3V3
I am able to display data on the NHD-C12832A1Z-FSW-FBW-3V3 via raspberry pi and RPi.GPIO. However, I am looking to using other libraries to interface with the SPI and GPIO pins because I need this to run on a non-raspberry pi.
I have tried replacing all instances of the RPi.GPIO usage with periphery.GPIO. When using a logic analyzer, I can see that the program does produce what looks like the correct signals. However, nothing shows on the display when using the periphery library
This is the RPi.GPIO code that works on the Raspberry Pi
import RPi.GPIO as GPIO from ascii_values import ASCII168 import icon_manager # Mapping based on BCM (Broadcom) pinout from https://pinout.xyz/ LCD_CS = 8 LCD_RST = 22 LCD_A0 = 23 LCD_CLK = 11 LCD_SI = 10 class LCD: _instance = None def __new__(cls, *args, **kwargs): """Ensure only one instance of the LCD class exists.""" if cls._instance is None: cls._instance = super(LCD, cls).__new__(cls) cls._instance._initialize_lcd() return cls._instance def __del__(self): GPIO.cleanup() def _initialize_lcd(self): self.io_init() self.lcd_init() GPIO.setwarnings(False) def cleanup(self): GPIO.cleanup() def io_init(self): GPIO.setmode(GPIO.BCM) GPIO.setup(LCD_CS, GPIO.OUT) GPIO.setup(LCD_RST, GPIO.OUT) GPIO.setup(LCD_A0, GPIO.OUT) GPIO.setup(LCD_CLK, GPIO.OUT) GPIO.setup(LCD_SI, GPIO.OUT) def lcd_init(self): GPIO.output(LCD_CS, True) GPIO.output(LCD_RST, False) GPIO.output(LCD_RST, True) self.lcd_transfer_data(0xe2, 0) # Internal reset self.lcd_transfer_data(0xa2, 0) # Sets the LCD drive voltage bias ratio ##A2: 1/9 bias ##A3: 1/7 bias (ST7565V) self.lcd_transfer_data(0xa0, 0) # Sets the display RAM address SEG output correspondence ##A0: normal ##A1: reverse self.lcd_transfer_data(0xc8, 0) # Select COM output scan direction ##C0~C7: normal direction ##C8~CF: reverse direction self.lcd_transfer_data(0xa4, 0) # Display all points ON/OFF ##A4: normal display ##A5: all points ON self.lcd_transfer_data(0xa6, 0) # Sets the LCD display normal/inverted ##A6: normal ##A7: inverted self.lcd_transfer_data(0x2F, 0) # Select internal power supply operating mode ##28~2F: Operating mode self.lcd_transfer_data(0x40, 0) # Display start line set ##40~7F: Display start address self.lcd_transfer_data(0x20, 0) # V5 voltage regulator internal resistor ratio set (contrast) ##20~27: small~large self.lcd_transfer_data(0x81, 0) # Electronic volume mode set ##81: Set the V5 output voltage self.lcd_transfer_data(0x30, 0) # Electronic volume register set ##00~3F: electronic volume register self.lcd_transfer_data(0b10101111, 0) # Display ON/OFF ## 0b10101111: ON ## 0b10101110: OFF self.lcd_clear() def display_icon(self, x_pos, y_pos, icon, negative=False): icon_matrix = icon_manager.format_icon(icon, negative) if not isinstance(icon_matrix, list) or not all(isinstance(i, int) for i in icon_matrix): raise TypeError("icon_matrix must be a list of integers") quadrant_offsets = [ (0, 0, 0, 8), # Quadrant 1: Top-left (1, 0, 8, 16), # Quadrant 2: Bottom-left (0, 8, 16, 24), # Quadrant 3: Top-right (1, 8, 24, 32), # Quadrant 4: Bottom-right ] for y_offset, x_offset, start_idx, end_idx in quadrant_offsets: self.lcd_set_page(y_pos + y_offset, x_pos + x_offset) # Transfer the entire quadrant data at once data = icon_matrix[start_idx:end_idx] self.lcd_transfer_data_bulk(data) def lcd_ascii168_string(self, x_pos, y_pos, string): string_len = len(string) for i in range(0, string_len): self.lcd_ascii168(x_pos + i * 8, y_pos, ord(string[i]) - 32) def lcd_ascii168(self, x_pos, y_pos, char): self.lcd_set_page(y_pos, x_pos) for i in range(0, 8): self.lcd_transfer_data(ASCII168[char][i], 1) self.lcd_set_page(y_pos + 1, x_pos) for i in range(8, 16): self.lcd_transfer_data(ASCII168[char][i], 1) def lcd_clear(self): GPIO.output(LCD_CS, False) for i in range(0, 8): self.lcd_set_page(i, 0) for j in range(0, 128): self.lcd_transfer_data(0x00, 1) GPIO.output(LCD_CS, True) def lcd_set_page(self, page, column): lsb = column & 0x0f msb = column & 0xf0 msb >>= 4 msb |= 0x10 page |= 0xb0 self.lcd_transfer_data(page, 0) self.lcd_transfer_data(msb, 0) self.lcd_transfer_data(lsb, 0) def lcd_transfer_data(self, value, A0): GPIO.output(LCD_CS, False) GPIO.output(LCD_CLK, True) if (A0): GPIO.output(LCD_A0, True) else: GPIO.output(LCD_A0, False) self.lcd_byte(value) GPIO.output(LCD_CS, True) def lcd_transfer_data_bulk(self, data_list): """Transfers a list of data in bulk, minimizing GPIO operations.""" GPIO.output(LCD_CS, False) GPIO.output(LCD_CLK, True) for data in data_list: GPIO.output(LCD_A0, True) # Data mode self.lcd_byte(data) GPIO.output(LCD_CS, True) def lcd_byte(self, bits): tmp = bits for i in range(0, 8): GPIO.output(LCD_CLK, False) if (tmp & 0x80): GPIO.output(LCD_SI, True) else: GPIO.output(LCD_SI, False) tmp = (tmp << 1) GPIO.output(LCD_CLK, True)
-
Hi Jacob,
I can't say with certainty why the periphery.GPIO implementation is not working, but I will try to offer guidance. Because RPi.GPIO is slower than periphery.GPIO, you may need to introduce delays in your code. Please try adding a 100ms delay after GPIO.output(LCD_RST, False) and GPIO.output(LCD_RST, True) to ensure a proper reset. It may also help to add a 100us delay after asserting the CS pin. Let me know if this helps.
0 -
I have tried adding the delays with no luck.
- I have tried with the spidev library having that handle all spi operations with periphery.GPIO accessing the Reset and A0 pins.
- I have tried using the same implementation as above by only accessing the pins via periphery.GPIOhere is code that does all gpio access with the periphery library
from periphery import GPIO
from ascii_values import ASCII168
import icon_manager
import time
# Mapping based on BCM (Broadcom) pinout from https://pinout.xyz/
LCD_CS = 8
LCD_RST = 22
LCD_A0 = 23
LCD_CLK = 11
LCD_SI = 10
class LcdGpio:
def __init__(self):
self.RST = GPIO("/dev/gpiochip0", LCD_RST, "out")
self.A0 = GPIO("/dev/gpiochip0", LCD_A0, "out")
self.CS = GPIO("/dev/gpiochip0", LCD_CS, "out")
self.CLK = GPIO("/dev/gpiochip0", LCD_CLK, "out")
self.SI = GPIO("/dev/gpiochip0", LCD_SI, "out")
self._lcd_init()
def lcd_byte(self, bits):
tmp = bits
for i in range(0, 8):
self.CLK.write(False)
if (tmp & 0x80):
self.SI.write(True)
else:
self.SI.write(False)
tmp = (tmp << 1)
self.CLK.write(True)
def cleanup(self):
self.RST.close()
self.A0.close()
self.CS.close()
self.CLK.close()
self.SI.close()
#######################################################################################################
# SEND INSTRUCTIONS
#######################################################################################################
def _send_instruction(self, instruction):
self.CS.write(False)
self.CLK.write(True)
self.A0.write(False)
self.lcd_byte(instruction)
self.CS.write(True)
def _send_instructions_bulk(self, data):
self.CS.write(False)
self.CLK.write(True)
for d in data:
self.A0.write(False)
self.lcd_byte(d)
self.CS.write(True)
def _lcd_init(self):
self.CS.write(True)
self.RST.write(False)
self.RST.write(True)
init_instructions = [0xE2, 0xA2, 0xA0, 0xC8, 0xA4, 0xA6, 0x2F, 0x40, 0x20, 0x81, 0x30, 0b10101111]
self._send_instructions_bulk(init_instructions)
self.lcd_clear()
def lcd_set_page(self, page, column):
lsb = column & 0x0f
msb = column & 0xf0
msb >>= 4
msb |= 0x10
page |= 0xb0
self._send_instruction(page)
self._send_instruction(msb)
self._send_instruction(lsb)
#######################################################################################################
# SEND DATA
#######################################################################################################
def _send_data(self, data):
self.CS.write(False)
self.CLK.write(True)
self.A0.write(True)
self.lcd_byte(data)
self.CS.write(True)
def _send_data_bulk(self, instructions):
self.CS.write(False)
self.CLK.write(True)
for instr in instructions:
self.A0.write(True)
self.lcd_byte(instr)
self.CS.write(True)
def display_icon(self, x_pos, y_pos, icon, negative=False):
icon_matrix = icon_manager.format_icon(icon, negative)
if not isinstance(icon_matrix, list) or not all(isinstance(i, int) for i in icon_matrix):
raise TypeError("icon_matrix must be a list of integers")
quadrant_offsets = [
(0, 0, 0, 8), # Quadrant 1: Top-left
(1, 0, 8, 16), # Quadrant 2: Bottom-left
(0, 8, 16, 24), # Quadrant 3: Top-right
(1, 8, 24, 32), # Quadrant 4: Bottom-right
]
for y_offset, x_offset, start_idx, end_idx in quadrant_offsets:
self.lcd_set_page(y_pos + y_offset, x_pos + x_offset)
# Transfer the entire quadrant data at once
data = icon_matrix[start_idx:end_idx]
self._send_data_bulk(data)
def lcd_ascii168_string(self, x_pos, y_pos, string):
string_len = len(string)
for i in range(0, string_len):
self.lcd_ascii168(x_pos + i * 8, y_pos, ord(string[i]) - 32)
def lcd_ascii168(self, x_pos, y_pos, char):
self.lcd_set_page(y_pos, x_pos)
for i in range(0, 8):
self._send_data(ASCII168[char][i])
self.lcd_set_page(y_pos + 1, x_pos)
for i in range(8, 16):
self._send_data(ASCII168[char][i])
def lcd_clear(self):
empty_row = [0x00] * 128
self.CS.write(False)
for i in range(0, 8):
self.lcd_set_page(i, 0)
self._send_data_bulk(empty_row)
self.CS.write(True)
def main():
print("Initializing LCD for testing...")
lcd = LcdGpio()
try:
# Display some text
print("trying to display text")
lcd.lcd_ascii168_string(0, 0, "Hello!")
time.sleep(2)
except Exception as e:
print(f"Error displaying text: {e}")
finally:
lcd.lcd_clear()
lcd.cleanup()
print("was it successful?")
if __name__ == "__main__":
main()0 -
Update: I was able to finally get it working on the target board Up Squared 7100. Using the spidev library combined with periphery.GPIO to set the RST and A0 worked. Seems to have been a timing issue when sending the data serially via just GPIO
0 -
Thank you for the update and I am glad you got the display working. Please let us know if you need further assistance.
0
Please sign in to leave a comment.
Comments
4 comments