Merge pull request #1994 from openmv/display_backlight

modules/display: Add support for Python backlight controllers.
This commit is contained in:
Ibrahim Abdelkader 2023-10-27 15:24:39 +02:00 committed by GitHub
commit b65f639f82
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 217 additions and 321 deletions

View File

@ -14,7 +14,13 @@ import display
sensor.reset() # Initialize the camera sensor.
sensor.set_pixformat(sensor.RGB565) # or sensor.GRAYSCALE
sensor.set_framesize(sensor.QQVGA2) # Special 128x160 framesize for LCD Shield.
lcd = display.SPIDisplay() # Initialize the lcd screen.
# Initialize the lcd screen.
# Note: A DAC or a PWM backlight controller can be used to control the
# backlight intensity if supported:
# lcd = display.SPIDisplay(backlight=display.DACBacklight(channel=2))
# lcd.backlight(25) # 25% intensity
# Otherwise the default GPIO (on/off) controller is used.
lcd = display.SPIDisplay()
while True:
lcd.write(sensor.snapshot()) # Take a picture and display the image.

View File

@ -5,125 +5,44 @@
#
# This work is licensed under the MIT license, see the file LICENSE for details.
#
# This is an extension to the display C user-module. Add any display-related
# drivers here, and freeze this module in the board's manifest, and those
# drivers will be importable from display.
# This is an extension to the display C user-module. Add or import any display-related
# drivers here, and freeze this module in the board's manifest, and those drivers will
# be importable from display.
from udisplay import *
import time
from udisplay import * # noqa
try:
from st7701 import * # noqa
except (ImportError, AttributeError):
pass
class ST7701:
DSI_CMD2_BKX_SEL = const(0xFF)
DSI_CMD2_BK0_SEL = const(0x10)
DSI_CMD2_BK1_SEL = const(0x11)
DSI_CMD2_BKX_SEL_NONE = const(0x00)
class DACBacklight:
def __init__(self, channel, bits=8):
from pyb import DAC
DSI_CMD2_BK0_PVGAMCTRL = const(0xB0)
DSI_CMD2_BK0_NVGAMCTRL = const(0xB1)
DSI_CMD2_BK0_LNESET = const(0xC0)
DSI_CMD2_BK0_PORCTRL = const(0xC1)
DSI_CMD2_BK0_INVSEL = const(0xC2)
self.bits = bits
self.dac = DAC(channel, bits=bits, buffering=True)
self.backlight(100)
DSI_CMD2_BK1_SECTRL = const(0xE0)
DSI_CMD2_BK1_NRCTRL = const(0xE1)
DSI_CMD2_BK1_SRPCTRL = const(0xE2)
DSI_CMD2_BK1_CCCTRL = const(0xE3)
DSI_CMD2_BK1_SKCTRL = const(0xE4)
def deinit(self):
self.dac.deinit()
DSI_CMD2_BK1_VRHS = const(0xB0)
DSI_CMD2_BK1_VCOM = const(0xB1)
DSI_CMD2_BK1_VGHSS = const(0xB2)
DSI_CMD2_BK1_TESTCMD = const(0xB3)
DSI_CMD2_BK1_VGLS = const(0xB5)
DSI_CMD2_BK1_PWCTLR1 = const(0xB7)
DSI_CMD2_BK1_PWCTLR2 = const(0xB8)
DSI_CMD2_BK1_DGMLUTR = const(0xB9)
DSI_CMD2_BK1_SPD1 = const(0xC1)
DSI_CMD2_BK1_SPD2 = const(0xC2)
DSI_CMD2_BK1_MIPISET1 = const(0xD0)
def backlight(self, value):
self.dac.write(int(value / 100 * (2**self.bits - 1)))
DCS_SOFT_RESET = const(0x01)
DCS_EXIT_SLEEP_MODE = const(0x11)
DCS_SET_DISPLAY_ON = const(0x29)
def __init__(self):
pass
class PWMBacklight:
def __init__(self, pin, timer=3, channel=3, frequency=200):
from machine import Pin
from pyb import Timer
def init(self, dc):
self.dc = dc
self.timer = Timer(timer, freq=frequency)
self.channel = self.timer.channel(channel, Timer.PWM, pin=Pin(pin))
self.backlight(100)
# Soft-reset
dc.dsi_write(DCS_SOFT_RESET)
time.sleep_ms(200)
def deinit(self):
self.timer.deinit()
# Exit sleep mode
dc.dsi_write(DCS_EXIT_SLEEP_MODE)
time.sleep_ms(800)
# Select bank 0
dc.dsi_write(DSI_CMD2_BKX_SEL, b"\x77\x01\x00\x00\x10")
# Display Control setting
dc.dsi_write(DSI_CMD2_BK0_LNESET, b"\x63\x00")
dc.dsi_write(DSI_CMD2_BK0_PORCTRL, b"\x11\x02")
dc.dsi_write(DSI_CMD2_BK0_INVSEL, b"\x01\x08")
# dc.dsi_write(0xCC, b"\x18")
# Gamma cluster settings
dc.dsi_write(
0xB0, b"\x40\xc9\x91\x0d\x12\x07\x02\x09\x09\x1f\x04\x50\x0f\xe4\x29\xdf"
)
dc.dsi_write(
0xB1, b"\x40\xcb\xd0\x11\x92\x07\x00\x08\x07\x1c\x06\x53\x12\x63\xeb\xdf"
)
# Select bank 1
dc.dsi_write(DSI_CMD2_BKX_SEL, b"\x77\x01\x00\x00\x11")
# Power control settings
dc.dsi_write(DSI_CMD2_BK1_VRHS, 0x65)
dc.dsi_write(DSI_CMD2_BK1_VCOM, 0x34)
dc.dsi_write(DSI_CMD2_BK1_VGHSS, 0x87)
dc.dsi_write(DSI_CMD2_BK1_TESTCMD, 0x80)
dc.dsi_write(DSI_CMD2_BK1_VGLS, 0x49)
dc.dsi_write(DSI_CMD2_BK1_PWCTLR1, 0x85)
dc.dsi_write(DSI_CMD2_BK1_PWCTLR2, 0x20)
dc.dsi_write(DSI_CMD2_BK1_DGMLUTR, 0x10)
dc.dsi_write(DSI_CMD2_BK1_SPD1, 0x78)
dc.dsi_write(DSI_CMD2_BK1_SPD2, 0x78)
dc.dsi_write(DSI_CMD2_BK1_MIPISET1, 0x88)
time.sleep_ms(100)
# GIP settings
dc.dsi_write(DSI_CMD2_BK1_SECTRL, b"\x00\x00\x02")
dc.dsi_write(
DSI_CMD2_BK1_NRCTRL, b"\x08\x00\x0A\x00\x07\x00\x09\x00\x00\x33\x33"
)
dc.dsi_write(
DSI_CMD2_BK1_SRPCTRL,
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
)
dc.dsi_write(DSI_CMD2_BK1_CCCTRL, b"\x00\x00\x33\x33")
dc.dsi_write(DSI_CMD2_BK1_SKCTRL, b"\x44\x44")
dc.dsi_write(
0xE5, b"\x0E\x60\xA0\xa0\x10\x60\xA0\xA0\x0A\x60\xA0\xA0\x0C\x60\xA0\xA0"
)
dc.dsi_write(0xE6, b"\x00\x00\x33\x33")
dc.dsi_write(0xE7, b"\x44\x44")
dc.dsi_write(
0xE8, b"\x0D\x60\xA0\xA0\x0F\x60\xA0\xA0\x09\x60\xA0\xA0\x0B\x60\xA0\xA0"
)
dc.dsi_write(0xEB, b"\x02\x01\xE4\xE4\x44\x00\x40")
dc.dsi_write(0xEC, b"\x02\x01")
dc.dsi_write(
0xED, b"\xAB\x89\x76\x54\x01\xFF\xFF\xFF\xFF\xFF\xFF\x10\x45\x67\x98\xBA"
)
dc.dsi_write(DSI_CMD2_BKX_SEL, b"\x77\x01\x00\x00\x00")
time.sleep_ms(10)
dc.dsi_write(DCS_SET_DISPLAY_ON, dcs=True)
time.sleep_ms(200)
def read_id(self):
return self.dc.dsi_read(0x04, 3)
def backlight(self, value):
self.channel.pulse_width_percent(value * 100 // 100)

126
scripts/libraries/st7701.py Normal file
View File

@ -0,0 +1,126 @@
# This file is part of the OpenMV project.
#
# Copyright (c) 2023 Ibrahim Abdelkader <iabdalkader@openmv.io>
# Copyright (c) 2023 Kwabena W. Agyeman <kwagyeman@openmv.io>
#
# This work is licensed under the MIT license, see the file LICENSE for details.
#
# ST7701 LCD controller driver.
import time
class ST7701:
DSI_CMD2_BKX_SEL = const(0xFF)
DSI_CMD2_BK0_SEL = const(0x10)
DSI_CMD2_BK1_SEL = const(0x11)
DSI_CMD2_BKX_SEL_NONE = const(0x00)
DSI_CMD2_BK0_PVGAMCTRL = const(0xB0)
DSI_CMD2_BK0_NVGAMCTRL = const(0xB1)
DSI_CMD2_BK0_LNESET = const(0xC0)
DSI_CMD2_BK0_PORCTRL = const(0xC1)
DSI_CMD2_BK0_INVSEL = const(0xC2)
DSI_CMD2_BK1_SECTRL = const(0xE0)
DSI_CMD2_BK1_NRCTRL = const(0xE1)
DSI_CMD2_BK1_SRPCTRL = const(0xE2)
DSI_CMD2_BK1_CCCTRL = const(0xE3)
DSI_CMD2_BK1_SKCTRL = const(0xE4)
DSI_CMD2_BK1_VRHS = const(0xB0)
DSI_CMD2_BK1_VCOM = const(0xB1)
DSI_CMD2_BK1_VGHSS = const(0xB2)
DSI_CMD2_BK1_TESTCMD = const(0xB3)
DSI_CMD2_BK1_VGLS = const(0xB5)
DSI_CMD2_BK1_PWCTLR1 = const(0xB7)
DSI_CMD2_BK1_PWCTLR2 = const(0xB8)
DSI_CMD2_BK1_DGMLUTR = const(0xB9)
DSI_CMD2_BK1_SPD1 = const(0xC1)
DSI_CMD2_BK1_SPD2 = const(0xC2)
DSI_CMD2_BK1_MIPISET1 = const(0xD0)
DCS_SOFT_RESET = const(0x01)
DCS_EXIT_SLEEP_MODE = const(0x11)
DCS_SET_DISPLAY_ON = const(0x29)
def __init__(self):
pass
def init(self, dc):
self.dc = dc
# Soft-reset
dc.dsi_write(DCS_SOFT_RESET)
time.sleep_ms(200)
# Exit sleep mode
dc.dsi_write(DCS_EXIT_SLEEP_MODE)
time.sleep_ms(800)
# Select bank 0
dc.dsi_write(DSI_CMD2_BKX_SEL, b"\x77\x01\x00\x00\x10")
# Display Control setting
dc.dsi_write(DSI_CMD2_BK0_LNESET, b"\x63\x00")
dc.dsi_write(DSI_CMD2_BK0_PORCTRL, b"\x11\x02")
dc.dsi_write(DSI_CMD2_BK0_INVSEL, b"\x01\x08")
# dc.dsi_write(0xCC, b"\x18")
# Gamma cluster settings
dc.dsi_write(
0xB0, b"\x40\xc9\x91\x0d\x12\x07\x02\x09\x09\x1f\x04\x50\x0f\xe4\x29\xdf"
)
dc.dsi_write(
0xB1, b"\x40\xcb\xd0\x11\x92\x07\x00\x08\x07\x1c\x06\x53\x12\x63\xeb\xdf"
)
# Select bank 1
dc.dsi_write(DSI_CMD2_BKX_SEL, b"\x77\x01\x00\x00\x11")
# Power control settings
dc.dsi_write(DSI_CMD2_BK1_VRHS, 0x65)
dc.dsi_write(DSI_CMD2_BK1_VCOM, 0x34)
dc.dsi_write(DSI_CMD2_BK1_VGHSS, 0x87)
dc.dsi_write(DSI_CMD2_BK1_TESTCMD, 0x80)
dc.dsi_write(DSI_CMD2_BK1_VGLS, 0x49)
dc.dsi_write(DSI_CMD2_BK1_PWCTLR1, 0x85)
dc.dsi_write(DSI_CMD2_BK1_PWCTLR2, 0x20)
dc.dsi_write(DSI_CMD2_BK1_DGMLUTR, 0x10)
dc.dsi_write(DSI_CMD2_BK1_SPD1, 0x78)
dc.dsi_write(DSI_CMD2_BK1_SPD2, 0x78)
dc.dsi_write(DSI_CMD2_BK1_MIPISET1, 0x88)
time.sleep_ms(100)
# GIP settings
dc.dsi_write(DSI_CMD2_BK1_SECTRL, b"\x00\x00\x02")
dc.dsi_write(
DSI_CMD2_BK1_NRCTRL, b"\x08\x00\x0A\x00\x07\x00\x09\x00\x00\x33\x33"
)
dc.dsi_write(
DSI_CMD2_BK1_SRPCTRL,
b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
)
dc.dsi_write(DSI_CMD2_BK1_CCCTRL, b"\x00\x00\x33\x33")
dc.dsi_write(DSI_CMD2_BK1_SKCTRL, b"\x44\x44")
dc.dsi_write(
0xE5, b"\x0E\x60\xA0\xa0\x10\x60\xA0\xA0\x0A\x60\xA0\xA0\x0C\x60\xA0\xA0"
)
dc.dsi_write(0xE6, b"\x00\x00\x33\x33")
dc.dsi_write(0xE7, b"\x44\x44")
dc.dsi_write(
0xE8, b"\x0D\x60\xA0\xA0\x0F\x60\xA0\xA0\x09\x60\xA0\xA0\x0B\x60\xA0\xA0"
)
dc.dsi_write(0xEB, b"\x02\x01\xE4\xE4\x44\x00\x40")
dc.dsi_write(0xEC, b"\x02\x01")
dc.dsi_write(
0xED, b"\xAB\x89\x76\x54\x01\xFF\xFF\xFF\xFF\xFF\xFF\x10\x45\x67\x98\xBA"
)
dc.dsi_write(DSI_CMD2_BKX_SEL, b"\x77\x01\x00\x00\x00")
time.sleep_ms(10)
dc.dsi_write(DCS_SET_DISPLAY_ON, dcs=True)
time.sleep_ms(200)
def read_id(self):
return self.dc.dsi_read(0x04, 3)

View File

@ -7,6 +7,7 @@ require("neopixel")
freeze ("$(OMV_LIB_DIR)/", "modbus.py")
freeze ("$(OMV_LIB_DIR)/", "pid.py")
freeze ("$(OMV_LIB_DIR)/", "gt911.py")
freeze ("$(OMV_LIB_DIR)/", "st7701.py")
freeze ("$(OMV_LIB_DIR)/", "machine.py")
freeze ("$(OMV_LIB_DIR)/", "display.py")

View File

@ -13,6 +13,7 @@ freeze ("$(OMV_LIB_DIR)/", "tb6612.py")
freeze ("$(OMV_LIB_DIR)/", "vl53l1x.py")
freeze ("$(OMV_LIB_DIR)/", "bno055.py")
freeze ("$(OMV_LIB_DIR)/", "machine.py")
freeze ("$(OMV_LIB_DIR)/", "display.py")
# Networking
require("ntptime")

View File

@ -10,3 +10,4 @@ freeze ("$(OMV_LIB_DIR)/", "ssd1306.py")
freeze ("$(OMV_LIB_DIR)/", "tb6612.py")
freeze ("$(OMV_LIB_DIR)/", "vl53l1x.py")
freeze ("$(OMV_LIB_DIR)/", "machine.py")
freeze ("$(OMV_LIB_DIR)/", "display.py")

View File

@ -209,13 +209,6 @@
#define OMV_SPI_DISPLAY_RST_PIN (&omv_pin_D12_GPIO)
#define OMV_SPI_DISPLAY_BL_PIN (&omv_pin_A5_GPIO)
#define OMV_DISPLAY_BL_DAC (DAC)
#define OMV_DISPLAY_BL_DAC_CHANNEL (DAC_CHANNEL_2)
#define OMV_DISPLAY_BL_DAC_CLK_ENABLE() __HAL_RCC_DAC_CLK_ENABLE()
#define OMV_DISPLAY_BL_DAC_CLK_DISABLE() __HAL_RCC_DAC_CLK_DISABLE()
#define OMV_DISPLAY_BL_DAC_FORCE_RESET() __HAL_RCC_DAC_FORCE_RESET()
#define OMV_DISPLAY_BL_DAC_RELEASE_RESET() __HAL_RCC_DAC_RELEASE_RESET()
// FIR Lepton
#define OMV_FIR_LEPTON_I2C_BUS (FIR_I2C_ID)
#define OMV_FIR_LEPTON_I2C_BUS_SPEED (FIR_I2C_SPEED)

View File

@ -10,3 +10,4 @@ freeze ("$(OMV_LIB_DIR)/", "ssd1306.py")
freeze ("$(OMV_LIB_DIR)/", "tb6612.py")
freeze ("$(OMV_LIB_DIR)/", "vl53l1x.py")
freeze ("$(OMV_LIB_DIR)/", "machine.py")
freeze ("$(OMV_LIB_DIR)/", "display.py")

View File

@ -209,13 +209,6 @@
#define OMV_SPI_DISPLAY_RST_PIN (&omv_pin_D12_GPIO)
#define OMV_SPI_DISPLAY_BL_PIN (&omv_pin_A5_GPIO)
#define OMV_DISPLAY_BL_DAC (DAC)
#define OMV_DISPLAY_BL_DAC_CHANNEL (DAC_CHANNEL_2)
#define OMV_DISPLAY_BL_DAC_CLK_ENABLE() __HAL_RCC_DAC_CLK_ENABLE()
#define OMV_DISPLAY_BL_DAC_CLK_DISABLE() __HAL_RCC_DAC_CLK_DISABLE()
#define OMV_DISPLAY_BL_DAC_FORCE_RESET() __HAL_RCC_DAC_FORCE_RESET()
#define OMV_DISPLAY_BL_DAC_RELEASE_RESET() __HAL_RCC_DAC_RELEASE_RESET()
// FIR Lepton
#define OMV_FIR_LEPTON_I2C_BUS (FIR_I2C_ID)
#define OMV_FIR_LEPTON_I2C_BUS_SPEED (FIR_I2C_SPEED)

View File

@ -12,6 +12,7 @@ freeze ("$(OMV_LIB_DIR)/", "ssd1306.py")
freeze ("$(OMV_LIB_DIR)/", "tb6612.py")
freeze ("$(OMV_LIB_DIR)/", "vl53l1x.py")
freeze ("$(OMV_LIB_DIR)/", "machine.py")
freeze ("$(OMV_LIB_DIR)/", "display.py")
# Networking
require("ntptime")

View File

@ -310,13 +310,6 @@
#define OMV_SPI_DISPLAY_RST_PIN (&omv_pin_D12_GPIO)
#define OMV_SPI_DISPLAY_BL_PIN (&omv_pin_A5_GPIO)
#define OMV_DISPLAY_BL_DAC (DAC1)
#define OMV_DISPLAY_BL_DAC_CHANNEL (DAC_CHANNEL_2)
#define OMV_DISPLAY_BL_DAC_CLK_ENABLE() __HAL_RCC_DAC12_CLK_ENABLE()
#define OMV_DISPLAY_BL_DAC_CLK_DISABLE() __HAL_RCC_DAC12_CLK_DISABLE()
#define OMV_DISPLAY_BL_DAC_FORCE_RESET() __HAL_RCC_DAC12_FORCE_RESET()
#define OMV_DISPLAY_BL_DAC_RELEASE_RESET() __HAL_RCC_DAC12_RELEASE_RESET()
// FIR Lepton
#define OMV_FIR_LEPTON_I2C_BUS (FIR_I2C_ID)
#define OMV_FIR_LEPTON_I2C_BUS_SPEED (FIR_I2C_SPEED)

View File

@ -12,6 +12,7 @@ freeze ("$(OMV_LIB_DIR)/", "ssd1306.py")
freeze ("$(OMV_LIB_DIR)/", "tb6612.py")
freeze ("$(OMV_LIB_DIR)/", "vl53l1x.py")
freeze ("$(OMV_LIB_DIR)/", "machine.py")
freeze ("$(OMV_LIB_DIR)/", "display.py")
# Networking
require("ntptime")

View File

@ -314,13 +314,6 @@
#define OMV_SPI_DISPLAY_BL_PIN (&omv_pin_A5_GPIO)
#define OMV_SPI_DISPLAY_TRIPLE_BUFFER (1)
#define OMV_DISPLAY_BL_DAC (DAC1)
#define OMV_DISPLAY_BL_DAC_CHANNEL (DAC_CHANNEL_2)
#define OMV_DISPLAY_BL_DAC_CLK_ENABLE() __HAL_RCC_DAC12_CLK_ENABLE()
#define OMV_DISPLAY_BL_DAC_CLK_DISABLE() __HAL_RCC_DAC12_CLK_DISABLE()
#define OMV_DISPLAY_BL_DAC_FORCE_RESET() __HAL_RCC_DAC12_FORCE_RESET()
#define OMV_DISPLAY_BL_DAC_RELEASE_RESET() __HAL_RCC_DAC12_RELEASE_RESET()
// FIR Lepton
#define OMV_FIR_LEPTON_I2C_BUS (FIR_I2C_ID)
#define OMV_FIR_LEPTON_I2C_BUS_SPEED (FIR_I2C_SPEED)

View File

@ -12,6 +12,7 @@ freeze ("$(OMV_LIB_DIR)/", "ssd1306.py")
freeze ("$(OMV_LIB_DIR)/", "tb6612.py")
freeze ("$(OMV_LIB_DIR)/", "vl53l1x.py")
freeze ("$(OMV_LIB_DIR)/", "machine.py")
freeze ("$(OMV_LIB_DIR)/", "display.py")
# Networking
require("ntptime")

View File

@ -12,6 +12,7 @@ freeze ("$(OMV_LIB_DIR)/", "ssd1306.py")
freeze ("$(OMV_LIB_DIR)/", "tb6612.py")
freeze ("$(OMV_LIB_DIR)/", "vl53l1x.py")
freeze ("$(OMV_LIB_DIR)/", "machine.py")
freeze ("$(OMV_LIB_DIR)/", "display.py")
# Networking
require("ntptime")

View File

@ -355,15 +355,6 @@
#define OMV_RGB_DISPLAY_DISP_PIN (&omv_pin_G9_GPIO)
#define OMV_RGB_DISPLAY_BL_PIN (&omv_pin_B0_TIM3)
#define OMV_DISPLAY_BL_TIM (TIM3)
#define OMV_DISPLAY_BL_TIM_FREQ (100000)
#define OMV_DISPLAY_BL_TIM_CHANNEL (TIM_CHANNEL_3)
#define OMV_DISPLAY_BL_TIM_CLK_ENABLE() __HAL_RCC_TIM3_CLK_ENABLE()
#define OMV_DISPLAY_BL_TIM_CLK_DISABLE() __HAL_RCC_TIM3_CLK_DISABLE()
#define OMV_DISPLAY_BL_TIM_FORCE_RESET() __HAL_RCC_TIM3_FORCE_RESET()
#define OMV_DISPLAY_BL_TIM_RELEASE_RESET() __HAL_RCC_TIM3_RELEASE_RESET()
#define OMV_DISPLAY_BL_TIM_PCLK_FREQ() HAL_RCC_GetPCLK1Freq()
// SPI LCD Interface
#define OMV_SPI_DISPLAY_CONTROLLER (SPI2_ID)
#define OMV_SPI_DISPLAY_MOSI_PIN (&omv_pin_B15_SPI2)
@ -376,13 +367,6 @@
#define OMV_SPI_DISPLAY_BL_PIN (&omv_pin_A5_GPIO)
#define OMV_SPI_DISPLAY_TRIPLE_BUFFER (1)
#define OMV_DISPLAY_BL_DAC (DAC1)
#define OMV_DISPLAY_BL_DAC_CHANNEL (DAC_CHANNEL_2)
#define OMV_DISPLAY_BL_DAC_CLK_ENABLE() __HAL_RCC_DAC12_CLK_ENABLE()
#define OMV_DISPLAY_BL_DAC_CLK_DISABLE() __HAL_RCC_DAC12_CLK_DISABLE()
#define OMV_DISPLAY_BL_DAC_FORCE_RESET() __HAL_RCC_DAC12_FORCE_RESET()
#define OMV_DISPLAY_BL_DAC_RELEASE_RESET() __HAL_RCC_DAC12_RELEASE_RESET()
// HDMI CEC/DDC I/O
#define OMV_DISPLAY_CEC_ENABLE (1)
#define OMV_DISPLAY_DDC_ENABLE (1)

View File

@ -12,6 +12,7 @@ freeze ("$(OMV_LIB_DIR)/", "ssd1306.py")
freeze ("$(OMV_LIB_DIR)/", "tb6612.py")
freeze ("$(OMV_LIB_DIR)/", "vl53l1x.py")
freeze ("$(OMV_LIB_DIR)/", "machine.py")
freeze ("$(OMV_LIB_DIR)/", "display.py")
# Networking
require("ntptime")

View File

@ -69,6 +69,13 @@ STATIC mp_obj_t py_display_deinit(mp_obj_t self_in) {
if (display_p->deinit != NULL) {
display_p->deinit(self);
}
if (self->bl_controller != mp_const_none) {
mp_obj_t dest[2];
mp_load_method_maybe(self->bl_controller, MP_QSTR_deinit, dest);
if (dest[0] != MP_OBJ_NULL) {
mp_call_method_n_kw(0, 0, dest);
}
}
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(py_display_deinit_obj, py_display_deinit);
@ -86,17 +93,27 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(py_display_clear_obj, 1, 2, py_displa
STATIC mp_obj_t py_display_backlight(uint n_args, const mp_obj_t *args) {
py_display_obj_t *self = MP_OBJ_TO_PTR(args[0]);
if (n_args > 1) {
uint32_t intensity = mp_obj_get_int(args[1]);
if ((intensity < 0) || (255 < intensity)) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("0 <= intensity <= 255!"));
if (intensity > 100) {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Intensity ranges between 0 (off) and 100 (full on)"));
}
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
if (display_p->set_backlight != NULL) {
display_p->set_backlight(self, intensity);
if (self->bl_controller != mp_const_none) {
// If the display has a backlight controller set, call it first.
mp_obj_t dest[3];
mp_load_method(self->bl_controller, MP_QSTR_backlight, dest);
dest[2] = args[1];
mp_call_method_n_kw(1, 0, dest);
} else {
// Otherwise, if the display protocol's set_backlight is set, call it.
py_display_p_t *display_p = (py_display_p_t *) MP_OBJ_TYPE_GET_SLOT(self->base.type, protocol);
if (display_p->set_backlight != NULL) {
display_p->set_backlight(self, intensity);
} else {
mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Display does not support backlight control."));
}
}
self->intensity = intensity;
} else {
return mp_obj_new_int(self->intensity);
}

View File

@ -52,6 +52,7 @@ typedef struct _py_display_obj_t {
bool display_on;
bool portrait;
mp_obj_t controller;
mp_obj_t bl_controller;
#if defined(OMV_SPI_DISPLAY_CONTROLLER)
omv_spi_t spi_bus;
bool spi_tx_running;

View File

@ -288,53 +288,12 @@ static void spi_display_clear(py_display_obj_t *self, bool display_off) {
}
}
#ifdef OMV_DISPLAY_BL_DAC
static DAC_HandleTypeDef lcd_dac_handle;
#endif
#ifdef OMV_SPI_DISPLAY_BL_PIN
static void spi_display_set_backlight(py_display_obj_t *self, uint32_t intensity) {
#ifdef OMV_DISPLAY_BL_DAC
if ((self->intensity < 255) && (255 <= intensity)) {
#else
if ((self->intensity < 1) && (1 <= intensity)) {
#endif
omv_gpio_write(OMV_SPI_DISPLAY_BL_PIN, 1);
omv_gpio_deinit(OMV_SPI_DISPLAY_BL_PIN);
} else if ((0 < self->intensity) && (intensity <= 0)) {
omv_gpio_config(OMV_SPI_DISPLAY_BL_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
omv_gpio_write(OMV_SPI_DISPLAY_BL_PIN, 0);
}
#ifdef OMV_DISPLAY_BL_DAC
if (((self->intensity <= 0) || (255 <= self->intensity)) && (0 < intensity) && (intensity < 255)) {
omv_gpio_config(OMV_SPI_DISPLAY_BL_PIN, OMV_GPIO_MODE_ANALOG, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
DAC_ChannelConfTypeDef lcd_dac_channel_handle;
lcd_dac_handle.Instance = OMV_DISPLAY_BL_DAC;
lcd_dac_channel_handle.DAC_Trigger = DAC_TRIGGER_NONE;
lcd_dac_channel_handle.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
#if defined(MCU_SERIES_H7)
lcd_dac_channel_handle.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
lcd_dac_channel_handle.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;
lcd_dac_channel_handle.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
#endif
HAL_DAC_Init(&lcd_dac_handle);
HAL_DAC_ConfigChannel(&lcd_dac_handle, &lcd_dac_channel_handle, OMV_DISPLAY_BL_DAC_CHANNEL);
HAL_DAC_Start(&lcd_dac_handle, OMV_DISPLAY_BL_DAC_CHANNEL);
HAL_DAC_SetValue(&lcd_dac_handle, OMV_DISPLAY_BL_DAC_CHANNEL, DAC_ALIGN_8B_R, intensity);
} else if ((0 < self->intensity) && (self->intensity < 255) && ((intensity <= 0) || (255 <= intensity))) {
HAL_DAC_Stop(&lcd_dac_handle, OMV_DISPLAY_BL_DAC_CHANNEL);
HAL_DAC_DeInit(&lcd_dac_handle);
} else if ((0 < self->intensity) && (self->intensity < 255) && (0 < intensity) && (intensity < 255)) {
HAL_DAC_SetValue(&lcd_dac_handle, OMV_DISPLAY_BL_DAC_CHANNEL, DAC_ALIGN_8B_R, intensity);
}
#endif
self->intensity = intensity;
omv_gpio_config(OMV_SPI_DISPLAY_BL_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
omv_gpio_write(OMV_SPI_DISPLAY_BL_PIN, !!intensity);
}
#endif // OMV_SPI_DISPLAY_BL_PIN
#endif
static void spi_display_deinit(py_display_obj_t *self) {
if (self->triple_buffer) {
@ -345,14 +304,13 @@ static void spi_display_deinit(py_display_obj_t *self) {
omv_spi_deinit(&self->spi_bus);
omv_gpio_deinit(OMV_SPI_DISPLAY_RS_PIN);
omv_gpio_deinit(OMV_SPI_DISPLAY_RST_PIN);
#ifdef OMV_SPI_DISPLAY_BL_PIN
spi_display_set_backlight(self, 255);
omv_gpio_deinit(OMV_SPI_DISPLAY_BL_PIN);
#endif
}
mp_obj_t spi_display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_width, ARG_height, ARG_refresh, ARG_bgr, ARG_byte_swap, ARG_triple_buffer };
enum { ARG_width, ARG_height, ARG_refresh, ARG_bgr, ARG_byte_swap, ARG_triple_buffer, ARG_backlight };
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_width, MP_ARG_INT, {.u_int = 128 } },
{ MP_QSTR_height, MP_ARG_INT, {.u_int = 160 } },
@ -360,6 +318,7 @@ mp_obj_t spi_display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
{ MP_QSTR_bgr, MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_byte_swap, MP_ARG_BOOL, {.u_bool = false} },
{ MP_QSTR_triple_buffer, MP_ARG_BOOL, {.u_bool = LCD_TRIPLE_BUFFER_DEFAULT} },
{ MP_QSTR_backlight, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
};
// Parse args.
@ -386,6 +345,7 @@ mp_obj_t spi_display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
self->triple_buffer = args[ARG_triple_buffer].u_bool;
self->bgr = args[ARG_bgr].u_bool;
self->byte_swap = args[ARG_byte_swap].u_bool;
self->bl_controller = args[ARG_backlight].u_obj;
omv_spi_config_t spi_config;
omv_spi_default_config(&spi_config, OMV_SPI_DISPLAY_CONTROLLER);
@ -426,10 +386,6 @@ mp_obj_t spi_display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n
fb_alloc_mark_permanent();
}
#ifdef OMV_SPI_DISPLAY_BL_PIN
spi_display_set_backlight(self, 255);
#endif
return MP_OBJ_FROM_PTR(self);
}

View File

@ -55,9 +55,6 @@ typedef struct _display_state {
#ifdef OMV_DSI_DISPLAY_CONTROLLER
DSI_HandleTypeDef hdsi;
#endif
#ifdef OMV_DISPLAY_BL_TIM
TIM_HandleTypeDef htim;
#endif
LTDC_LayerCfgTypeDef framebuffer_layers[FRAMEBUFFER_COUNT];
} display_state_t;
@ -485,56 +482,10 @@ static void display_clear(py_display_obj_t *self, bool off) {
#ifdef OMV_DISPLAY_BL_PIN
static void display_set_backlight(py_display_obj_t *self, uint32_t intensity) {
#ifdef OMV_DISPLAY_BL_TIM
if ((self->intensity < 255) && (255 <= intensity)) {
#else
if ((self->intensity < 1) && (1 <= intensity)) {
#endif
omv_gpio_config(OMV_DISPLAY_BL_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
omv_gpio_write(OMV_DISPLAY_BL_PIN, 1);
} else if ((0 < self->intensity) && (intensity <= 0)) {
omv_gpio_write(OMV_DISPLAY_BL_PIN, 0);
omv_gpio_deinit(OMV_DISPLAY_BL_PIN);
}
#ifdef OMV_DISPLAY_BL_TIM
int tclk = OMV_DISPLAY_BL_TIM_PCLK_FREQ() * 2;
int period = (tclk / OMV_DISPLAY_BL_TIM_FREQ) - 1;
if (((self->intensity <= 0) || (255 <= self->intensity)) && (0 < intensity) && (intensity < 255)) {
omv_gpio_config(OMV_DISPLAY_BL_PIN, OMV_GPIO_MODE_ALT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
display.htim.Instance = OMV_DISPLAY_BL_TIM;
display.htim.Init.Prescaler = 0;
display.htim.Init.CounterMode = TIM_COUNTERMODE_UP;
display.htim.Init.Period = period;
display.htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
display.htim.Init.RepetitionCounter = 0;
display.htim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
TIM_OC_InitTypeDef lcd_tim_oc_handle;
lcd_tim_oc_handle.Pulse = (period * intensity) / 255;
lcd_tim_oc_handle.OCMode = TIM_OCMODE_PWM1;
lcd_tim_oc_handle.OCPolarity = TIM_OCPOLARITY_HIGH;
lcd_tim_oc_handle.OCNPolarity = TIM_OCNPOLARITY_HIGH;
lcd_tim_oc_handle.OCFastMode = TIM_OCFAST_DISABLE;
lcd_tim_oc_handle.OCIdleState = TIM_OCIDLESTATE_RESET;
lcd_tim_oc_handle.OCNIdleState = TIM_OCNIDLESTATE_RESET;
HAL_TIM_PWM_Init(&display.htim);
HAL_TIM_PWM_ConfigChannel(&display.htim, &lcd_tim_oc_handle, OMV_DISPLAY_BL_TIM_CHANNEL);
HAL_TIM_PWM_Start(&display.htim, OMV_DISPLAY_BL_TIM_CHANNEL);
} else if ((0 < self->intensity) && (self->intensity < 255) && ((intensity <= 0) || (255 <= intensity))) {
HAL_TIM_PWM_Stop(&display.htim, OMV_DISPLAY_BL_TIM_CHANNEL);
HAL_TIM_PWM_DeInit(&display.htim);
} else if ((0 < self->intensity) && (self->intensity < 255) && (0 < intensity) && (intensity < 255)) {
__HAL_TIM_SET_COMPARE(&display.htim, OMV_DISPLAY_BL_TIM_CHANNEL, (period * intensity) / 255);
}
#endif
self->intensity = intensity;
omv_gpio_config(OMV_DISPLAY_BL_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
omv_gpio_write(OMV_DISPLAY_BL_PIN, !!intensity);
}
#endif // OMV_DISPLAY_BL_PIN
#endif
#ifdef OMV_DSI_DISPLAY_CONTROLLER
int display_dsi_write(py_display_obj_t *self, uint8_t cmd, uint8_t *args, size_t n_args, bool dcs) {
@ -591,18 +542,18 @@ static void display_deinit(py_display_obj_t *self) {
}
}
fb_alloc_free_till_mark_past_mark_permanent();
#ifdef OMV_DISPLAY_BL_PIN
if (self->display_on) {
// back to default state
display_set_backlight(self, 0);
}
#endif // OMV_DISPLAY_BL_PIN
omv_gpio_deinit(OMV_DISPLAY_BL_PIN);
#endif
fb_alloc_free_till_mark_past_mark_permanent();
}
mp_obj_t display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
enum { ARG_framesize, ARG_refresh, ARG_display_on, ARG_triple_buffer, ARG_portrait, ARG_channel, ARG_controller };
enum {
ARG_framesize, ARG_refresh, ARG_display_on, ARG_triple_buffer,
ARG_portrait, ARG_channel, ARG_controller, ARG_backlight
};
static const mp_arg_t allowed_args[] = {
{ MP_QSTR_framesize, MP_ARG_INT, {.u_int = DISPLAY_RESOLUTION_FWVGA } },
{ MP_QSTR_refresh, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 60 } },
@ -611,6 +562,7 @@ mp_obj_t display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
{ MP_QSTR_portrait, MP_ARG_BOOL | MP_ARG_KW_ONLY, {.u_bool = false} },
{ MP_QSTR_channel, MP_ARG_INT | MP_ARG_KW_ONLY, {.u_int = 0 } },
{ MP_QSTR_controller, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_backlight, MP_ARG_OBJ | MP_ARG_KW_ONLY, {.u_rom_obj = MP_ROM_NONE} },
};
// Parse args.
@ -647,6 +599,7 @@ mp_obj_t display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
self->height = display_modes[self->framesize].vactive;
}
self->controller = args[ARG_controller].u_obj;
self->bl_controller = args[ARG_backlight].u_obj;
// Store state to access it from IRQ handlers or callbacks
display.self = self;
@ -672,12 +625,6 @@ mp_obj_t display_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw,
}
#endif
#ifdef OMV_DISPLAY_BL_PIN
if (self->display_on) {
display_set_backlight(self, 255); // to on state
}
#endif // OMV_DISPLAY_BL_PIN
return MP_OBJ_FROM_PTR(self);
}

View File

@ -289,12 +289,6 @@ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) {
}
#endif // (OMV_XCLK_SOURCE == OMV_XCLK_TIM)
#if defined(OMV_DISPLAY_BL_TIM)
if (htim->Instance == OMV_DISPLAY_BL_TIM) {
OMV_DISPLAY_BL_TIM_CLK_ENABLE();
}
#endif
#if defined(OMV_BUZZER_TIM)
if (htim->Instance == OMV_BUZZER_TIM) {
OMV_BUZZER_TIM_CLK_ENABLE();
@ -303,14 +297,6 @@ void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim) {
}
void HAL_TIM_PWM_MspDeInit(TIM_HandleTypeDef *htim) {
#if defined(OMV_DISPLAY_BL_TIM)
if (htim->Instance == OMV_DISPLAY_BL_TIM) {
OMV_DISPLAY_BL_TIM_FORCE_RESET();
OMV_DISPLAY_BL_TIM_RELEASE_RESET();
OMV_DISPLAY_BL_TIM_CLK_DISABLE();
}
#endif
#if defined(OMV_BUZZER_TIM)
if (htim->Instance == OMV_BUZZER_TIM) {
OMV_BUZZER_TIM_FORCE_RESET();
@ -660,11 +646,6 @@ void HAL_LTDC_MspInit(LTDC_HandleTypeDef *hltdc) {
omv_gpio_config(OMV_RGB_DISPLAY_DISP_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
omv_gpio_write(OMV_RGB_DISPLAY_DISP_PIN, 0);
#endif
#if defined(OMV_RGB_DISPLAY_BL_PIN)
omv_gpio_config(OMV_RGB_DISPLAY_BL_PIN, OMV_GPIO_MODE_OUTPUT, OMV_GPIO_PULL_NONE, OMV_GPIO_SPEED_LOW, -1);
omv_gpio_write(OMV_RGB_DISPLAY_BL_PIN, 0);
#endif
}
void HAL_LTDC_MspDeInit(LTDC_HandleTypeDef *hltdc) {
@ -714,31 +695,9 @@ void HAL_LTDC_MspDeInit(LTDC_HandleTypeDef *hltdc) {
#if defined(OMV_RGB_DISPLAY_DISP_PIN)
omv_gpio_deinit(OMV_RGB_DISPLAY_DISP_PIN);
#endif
#if defined(OMV_RGB_DISPLAY_BL_PIN)
omv_gpio_deinit(OMV_RGB_DISPLAY_BL_PIN);
#endif
}
#endif
void HAL_DAC_MspInit(DAC_HandleTypeDef *hdac) {
#if defined(OMV_DISPLAY_BL_DAC)
if (hdac->Instance == OMV_DISPLAY_BL_DAC) {
OMV_DISPLAY_BL_DAC_CLK_ENABLE();
}
#endif
}
void HAL_DAC_MspDeInit(DAC_HandleTypeDef *hdac) {
#if defined(OMV_DISPLAY_BL_DAC)
if (hdac->Instance == OMV_DISPLAY_BL_DAC) {
OMV_DISPLAY_BL_DAC_FORCE_RESET();
OMV_DISPLAY_BL_DAC_RELEASE_RESET();
OMV_DISPLAY_BL_DAC_CLK_DISABLE();
}
#endif
}
void HAL_MspDeInit(void) {
}