blob: 8b391bd9d0a4b9966b761041d4ef29f3ec7c44ef [file] [log] [blame]
# Lint as: python2, python3
# Copyright 2016 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""LCM display module controller.
The NT7534 (128x64 digital LCM) on SocKit is controlled by HPS SPI interface
from SPIM1.
Before using, please make sure the corresponent GPIO pins are enabled in SocKit
IO pinmux setting. Such as LCM_RST_n(GPIO48), LCM_D_C(GPIO62), and
LCM_BK(GPIO40).
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import logging
import os
import time
from . import chameleon_common # pylint: disable=W0611
from chameleond.utils import lcm_canvas
from chameleond.utils import lcm_font
from chameleond.utils import mem
from chameleond.utils import spim
from six.moves import range
class LcmDisplayError(Exception):
"""Exception raise when any unexpected behavior happened on LCM display."""
pass
class LcmDisplay(object):
"""A Class for LCM display controller."""
_GPIO1_ADDRESS = 0xff709000
_GPIO2_ADDRESS = 0xff70a000
_DR_OFFSET = 0x00
_DDR_OFFSET = 0x04
# Locate in GPIO1 bank
_RST_N_BITMASK = 0x00080000
_BK_BITMASK = 0x00000800
# Locate in GPIO2 bank
_D_C_BITMASK = 0x00000010
_WIDTH = 128
_HEIGHT = 64
_RESET_PULSE = 1.0 / 16
_BOOTSCREEN_BITMAP = os.path.join(os.path.dirname(os.path.realpath(__file__)),
'..', 'data', 'lcm_chameleon.bitmap')
# LCM instructions
_INST_SET_DISPLAY_ADDR = 0xae
_INST_SET_LINE_ADDR = 0x40
_INST_SET_PAGE_ADDR = 0xb0
_INST_SET_COL_L_ADDR = 0x00
_INST_SET_COL_H_ADDR = 0x10
_INST_SET_COM_SEL_ADDR = 0xc0
_INST_SET_POWER_CON_ADDR = 0x28
def __init__(self):
"""Constructs a LCM display object."""
self._memory = mem.MemoryForHPS
self._data_in = False
self.HardwareReset()
self._spim = spim.Spim(1, tx_only=True)
# LcmCanvas is an intermediate bitmap buffer of LcmDisplay, modify LcmCanvas
# will not change the display screen until you call RefreshDisplay().
self._canvas = lcm_canvas.LcmCanvas(
self._HEIGHT, self._WIDTH, self._BOOTSCREEN_BITMAP)
self.SetBacklight(True)
self.Initialize()
def HardwareReset(self):
"""Resets LCM display hardware module."""
logging.info('HW Reset LCM display...')
# Trigger LCM reset signal
self._memory.SetMask(self._GPIO1_ADDRESS + self._DDR_OFFSET,
self._RST_N_BITMASK)
self._memory.ClearAndSetMask(self._GPIO1_ADDRESS + self._DR_OFFSET,
self._RST_N_BITMASK,
self._RESET_PULSE)
time.sleep(self._RESET_PULSE)
# Turn on LCM backlight
self.SetBacklight(False)
# Set LCM-A0 (data in) pin as default
self._SetDataIn(is_data=False)
time.sleep(self._RESET_PULSE)
def Initialize(self):
"""Initiates LCM display settings."""
logging.info('Initializing LCM display...')
self._SetCommonOutputModeSelect(is_normal=True)
self._SetPowerControl(0x7)
self._SetStartLineAddress(0)
self._SetPageAddress(0)
self._SetColumnAddress(0)
self._SetDisplay(enable=True)
# Show up boot screen
self.RefreshDisplay()
logging.info('LCM display Init Done...')
def SetBacklight(self, enable=True):
"""Sets backlight on or off.
Args:
enable: True for setting backlight on; otherwise False.
"""
self._memory.SetMask(self._GPIO1_ADDRESS + self._DDR_OFFSET,
self._BK_BITMASK)
if enable:
self._memory.SetMask(self._GPIO1_ADDRESS + self._DR_OFFSET,
self._BK_BITMASK)
else:
self._memory.ClearMask(self._GPIO1_ADDRESS + self._DR_OFFSET,
self._BK_BITMASK)
def CanvasClear(self):
"""Clears the canvas."""
self._canvas.Clear()
def CanvasPrintLine(self, string, line, highlight=False, underline=False):
"""Prints string on a line on the canvas.
String will be cropped to fit the maximum length of display line.
Args:
string: The input string.
line: The line index.
highlight: Whether line is shown with highlight.
underline: Whether line is shown with underline.
"""
for i in range(self._canvas.max_char_length):
if i < len(string):
self._canvas.DrawChar(
string[i], line, i * lcm_font.FONT_WIDTH, highlight, underline)
else:
self._canvas.DrawChar(
' ', line, i * lcm_font.FONT_WIDTH, False, False)
def CanvasPrintMenuItem(self, string, is_leaf, line):
"""Prints menu item on a line on the canvas.
Args:
string: The input string of menu item.
is_leaf: Whether the item is a leaf node of menu.
line: The line index.
"""
# The format of menu item: first 2 spaces for the room of putting cursor. If
# item is not a leaf node, print a right arrow in the tail.
# Ex: string = 'Video' -> format_string = ' Video >'
format_string = ' ' * 2 + string + ' ' * (self._canvas.max_char_length - 2)
if not is_leaf:
format_string = (format_string[:self._canvas.max_char_length - 2] +
lcm_font.ARROW_RIGHT)
self.CanvasPrintLine(format_string, line)
def CanvasPrintCursor(self, line, column):
"""Prints cursor on the canvas.
Args:
line: The line index.
column: The column index.
"""
for i in range(len(lcm_font.CURSOR)):
char = lcm_font.CURSOR[i]
self._canvas.DrawChar(
char, line, column + i * lcm_font.FONT_WIDTH, False, False)
def GetMaxCharLines(self):
"""Get the maximum lines for printing characters."""
return self._canvas.max_char_lines
def GetMaxCharLength(self):
"""Get the maximum lengths of printing characters in a line."""
return self._canvas.max_char_length
def RefreshDisplay(self):
"""Refreshes and outputs canvas image to LCM display.
To be noted, calling canvas-related functions will only draw on the canvas
object, after then you need to call RefreshDisplay to output them on LCM
display.
"""
# Sometimes common output mode is changed accidentally and display will be
# upside down, set mode to normal while display refresh every time.
self._SetCommonOutputModeSelect(is_normal=True)
bitmap = self._canvas.bitmap
for page in range(self._canvas.pages):
self._SetPageAddress(page)
self._SetColumnAddress(0)
for data in iter(bitmap[page]):
self._WriteData(data)
def _SetDataIn(self, is_data=True):
"""Sets LCM_A0 (data in) pin to indicate input data type.
Args:
is_data: True for data input; False for command input.
"""
self._memory.SetMask(self._GPIO2_ADDRESS + self._DDR_OFFSET,
self._D_C_BITMASK)
if is_data:
self._memory.SetMask(self._GPIO2_ADDRESS + self._DR_OFFSET,
self._D_C_BITMASK)
else: # is command
self._memory.ClearMask(self._GPIO2_ADDRESS + self._DR_OFFSET,
self._D_C_BITMASK)
# Save the status of LCM-A0 (data in) to reduce redundant GPIO settings.
self._data_in = is_data
def _SetDisplay(self, enable):
"""Sets display on/off by SPI instruction command.
Args:
enable: True for display on; otherwise False.
"""
command = self._INST_SET_DISPLAY_ADDR | enable
self._WriteCommand(command)
def _SetStartLineAddress(self, line):
"""Sets start line address by SPI instrucion command.
Args:
line: The start line index.
"""
command = self._INST_SET_LINE_ADDR | (line & 0x3f)
self._WriteCommand(command)
def _SetPageAddress(self, page):
"""Sets page address for data by SPI instrucion command.
Args:
page: The start page index.
"""
command = self._INST_SET_PAGE_ADDR | (page & 0x0f)
self._WriteCommand(command)
def _SetColumnAddress(self, column):
"""Sets column address for data by SPI instrucion command.
Args:
column: The start column index.
"""
command = self._INST_SET_COL_L_ADDR | (column & 0x0f) # lower 4-bit
self._WriteCommand(command)
command = self._INST_SET_COL_H_ADDR | ((column >> 4) & 0x0f) # upper 4-bit
self._WriteCommand(command)
def _SetCommonOutputModeSelect(self, is_normal):
"""Sets LCM scan direction by SPI instrucion command.
Args:
is_normal: True for pixel data arranges from top-left to bottom-right;
False for the reversed direction.
"""
command = self._INST_SET_COM_SEL_ADDR | (is_normal << 3)
self._WriteCommand(command)
def _SetPowerControl(self, power):
"""Sets power control mask by SPI instruction command.
Args:
power: The 3-bit long power mask.
"""
command = self._INST_SET_POWER_CON_ADDR | (power & 0x07)
self._WriteCommand(command)
def _WriteCommand(self, command):
"""Writes a command byte through SPI interface.
Args:
command: The command byte pattern.
"""
self._WriteData(command, is_data=False)
def _WriteData(self, data, is_data=True):
"""Writes a data byte through SPI interface.
Args:
data: The data byte pattern.
is_data: True for writing data; False for writing command.
"""
if isinstance(data, str):
data = ord(data)
try:
if self._data_in != is_data:
self._SetDataIn(is_data)
self._spim.WriteData(data)
except Exception as e:
raise LcmDisplayError('Write %s 0x%2x failed: %s' %
('data' if is_data else 'command', data, e))