成果
材料和设备
orangepi zero2 硬件设备
- ubuntu server 20系统
屏幕
- 1.8英寸
- 驱动芯片:ST7735S
- 分辨率:128x160
- 淘宝仅售13元(好便宜!太心动了才买的
)
[quote color="info"]这块板子采用的是RGB565
的编码方式,每一个像素点占两个字节。[/quote]
杜邦线
- 八根杜邦线,双母头
教程
1、接线。关于屏幕和zero2开发板的接线看下面的表
ST7735S | OrangePi zero2 | 物理引脚编号 |
---|---|---|
BLK | PC6 | 11 |
CS | CE.1 | 24 |
DC | PC8 | 15 |
RST | PC5 | 13 |
SDA | MOSI.1 | 19 |
SCL | SCLK.1 | 23 |
VCC | 3.3V | 1 |
GND | GND | 9 |
开发版接口图
开发板引脚图
按照文中所说接好引脚。
2、找到屏幕的spi设备,代码在下面可以看到存在两个spi设备。看一下spi设备名,为什么要看设备名呢?因为下面的代码里,可以看到需要打开屏幕的spi设备。经过测试,spidev1.1为这块屏幕的spi设备名。
hsm@orangepizero2 ~> ls /dev/ |grep spi
spidev0.0
spidev1.1
3、编写代码,保存文件为screen_print.py,命令:sudo vim /home/hsm/screen_print.py
# coding : UTF-8
import time #用于计算spi刷新整个屏幕所用时长
import OPi.GPIO as GPIO #用于操作引脚
#树莓派与屏幕的交互协议为SPI,说明见:https://github.com/doceme/py-spidev
import spidev
#用于创建画布,或者读取具体路径下的图片。给图片添加文字。
from PIL import Image, ImageFont, ImageDraw
import socket
import platform
def hardReset(): #重置电平时序
GPIO.output(PinReset, 0)
time.sleep(.2)
GPIO.output(PinReset, 1)
time.sleep(.5)
def sendCommand(command, *bytes): #发送指令(DC为低电平)和数据(DC为高电平)
GPIO.output(PinDC, 0)
spi.writebytes([command])
if len(bytes) > 0:
GPIO.output(PinDC, 1)
spi.writebytes(list(bytes))
def reset(): #屏幕初始化
sendCommand(0x11);
sendCommand(0x26, 0x04); # Set Default Gamma
sendCommand(0xB1, 0x0e, 0x10); # Set Frame Rate
sendCommand(0xC0, 0x08, 0x00); # Set VRH1[4:0] & VC[2:0] for VCI1 & GVDD
sendCommand(0xC1, 0x05); # Set BT[2:0] for AVDD & VCL & VGH & VGL
sendCommand(0xC5, 0x38, 0x40); # Set VMH[6:0] & VML[6:0] for VOMH & VCOML
sendCommand(0x3a, 0x05); # Set Color Format
sendCommand(0x36, 0xc8); # RGB
sendCommand(0x2A, 0x00, 0x00, 0x00, 0x7F); # Set Column Address
sendCommand(0x2B, 0x00, 0x00, 0x00, 0x9F); # Set Page Address
sendCommand(0xB4, 0x00);
sendCommand(0xf2, 0x01); # Enable Gamma bit
sendCommand(0xE0, 0x3f, 0x22, 0x20, 0x30, 0x29, 0x0c, 0x4e, 0xb7, 0x3c, 0x19, 0x22, 0x1e, 0x02, 0x01, 0x00);
sendCommand(0xE1, 0x00, 0x1b, 0x1f, 0x0f, 0x16, 0x13, 0x31, 0x84, 0x43, 0x06, 0x1d, 0x21, 0x3d, 0x3e, 0x3f);
sendCommand(0x29); # Display On
sendCommand(0x2C);
def sendManyBytes(bytes): #发送屏幕数据
GPIO.output(PinDC, 1)
spi.writebytes(bytes)
def drawImg_old(img160x128): #入参为160x128像素的image对象
picReadStartTime = time.time()
bytes = []
i = 0
for x in range(0, screenWidth):
for y in range(screenHeight - 1, -1, -1):
colorValue = img160x128.getpixel((x, y))
red = colorValue[0]
green = colorValue[1]
blue = colorValue[2]
red = red >> 3; # st7735s的红色占5位
green = green >> 2; # st7735s的绿色占6位
blue = blue >> 3; # st7735s的蓝色占5位
highBit = 0 | (blue << 3) | (green >> 3); # 每个像素写入个字节,highBit高字节,lowBit低字节
lowBit = 0 | (green << 5) | red;
bytes.append(highBit)
bytes.append(lowBit)
picReadTimeConsuming = time.time() - picReadStartTime
startTime = time.time()
# screenWidth*screenHeight*2 每个像素写入个字节。以下for循环是为了控制每次传入的数组长度,防止这个报错,:OverflowError: Argument list size exceeds 4096 bytes.
for j in range(2000, screenWidth * screenHeight * 2, 2000):
sendManyBytes(bytes[i:j])
i = i + 2000
# showPic()
sendManyBytes(bytes[i:screenWidth * screenHeight * 2])
SpiTimeConsuming = time.time() - startTime
print("picReadTimeConsuming = %.3fs , SpiTimeConsuming = %.3fs" % (picReadTimeConsuming, SpiTimeConsuming))
def print_screen(bytes1):
i = 0
# screenWidth*screenHeight*2 每个像素写入个字节。以下for循环是为了控制每次传入的数组长度,防止这个报错,:OverflowError: Argument list size exceeds 4096 bytes.
for j in range(2000, screenWidth * screenHeight * 2, 2000):
sendManyBytes(bytes1[i:j])
i = i + 2000
sendManyBytes(bytes1[i:screenWidth * screenHeight * 2])
def extract_ip():
st = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
try:
st.connect(('10.255.255.255', 1))
IP = st.getsockname()[0]
except Exception:
IP = '127.0.0.1'
finally:
st.close()
return IP
print(extract_ip())
if __name__ == '__main__':
screenWidth = 160 #屏幕长度
screenHeight = 128 #屏幕宽度
PinDC = 15 #G5IO.BOARD引脚模式,第15号引脚
PinReset = 13#GPIO.BOARD引脚模式,第13号引脚
BLK = 11
#GPIO.setwarnings(False)
GPIO.setboard(GPIO.H616)
GPIO.setmode(GPIO.BOARD)
GPIO.setup(PinDC, GPIO.OUT)
GPIO.setup(PinReset, GPIO.OUT)
local_ip = extract_ip()
# set blink
GPIO.setup(BLK,GPIO.OUT)
spi = spidev.SpiDev() #https://github.com/doceme/py-spidev
spi.open(1, 1)
spi.max_speed_hz = 24000000 #通信时钟最大频率
spi.mode = 0x00 #SPI的模式,ST7735S为模式0,可以参看我这篇内容:
hardReset()
reset()
image = Image.new('RGB', (160, 128)) #可以使用代码新建画布
draw = ImageDraw.Draw(image)
setFont = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/yahei.ttf", 15)
draw.text((5, 0), "IP:"+local_ip, font=setFont, fill="#FFFFFF", direction=None)
setFont2 = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/yahei.ttf", 20)
import datetime
now_time = datetime.datetime.now()
draw.text((5, 40),datetime.datetime.strftime(now_time,'%m月%d日 %H:%M'), font=setFont2, fill="#ffd966", direction=None)
draw.text((70, 95), "Author:HSM", font=setFont, fill="#7fc9d8", direction=None)
drawImg_old(image)
GPIO.cleanup() #退出的时候将本程序运行时修改的端口都重置成之前的样子
4、在Linux上设置设置定时任务:在linux终端输入:vim /var/spool/cron/crontabs/root
,编辑文件,加入一行配置,如下:
# 每隔一分钟刷新一次
*/1 * * * * python3 /home/hsm/screen_print.py
5、测试
- 看屏幕是否正确显示
- 如果屏幕显示不正常,检查流程是否操作正确。检查接线,步骤1、2、3。
原理
学习一下原理,看不明白就放弃吧。
了解ST7735S
ST7735S是一款用于262K彩色、图形型TFT-LCD的单芯片控制器/驱动器。 它由396条源极线和162条栅极线驱动电路组成。 该芯片能够直接与外部微处理器连接,并接受串行外设接口(SPI)、8bit/9bit/16bit/18bit并行接口。 显示数据可以存储在132×162×18位的片上显示数据RAM中。 它可以在没有外部操作时钟的情况下执行显示数据RAM读/写操作,以最大限度地减少功耗。 此外,由于驱动液晶所需的集成电源电路,可以用较少的元件制造显示系统。
驱动IC数据手册下载:
驱动IC数据手册.pdf
颜色模式
ST7735S支持多种可编程像素颜色格式(颜色深度),用于各种显示数据输入格式
- 12位/像素:RGB=(444)使用384K位帧存储器和LUT
- 16位/像素:RGB=(565)使用384K位帧存储器和LUT
- 18位/像素:RGB=(666)使用384K位帧存储器和LUT
[quote color="info"]我这次采用RGB565的颜色格式[/quote]
RGB565
RGB565的详细文档可以查看驱动IC数据手册45页
数据帧的格式为:
颜色格式初始化
sendCommand(0x3a, 0x05); # Set Color Format
spidev
spidev
模块主要用于通过spidev
linux内核驱动程序从用户空间与SPI设备
连接。
使用
import spidev
spi = spidev.Spidev()
#方法--open
bus = 0 #supporyed values:0,1
device = 0 #supported values:0,1 default: 0
spi.open(bus,device) #连接到指定的spi设备 /dev/spidev<bus>.<device>
#方法--readbytes/writebytes
spi.readbytes(n) #从spi设备读取n个字节
spi.writebytes(list of values) #将数据列表写入spi设备
spi.writebytes2(list of values) #接受大型列表,支持numpy字节数组
spi.xfer(send_list) #传输数据
#方法--close
spi.close() #关闭连接
#配置
max_speed_hz #通信时钟最大频率
mode #spi mode 0b00~0b11
no_cs #设置 SPI_NO_CS标志是否使用CS
threewire #共享SI/SO信号
OPI.GPIO
关于官方库
RPi的一个替换库。用于Orange Pi Zero和其他sbc的GPIO。使用sysfs只复制基本的GPIO功能:这允许从用户空间访问GPIO引脚。
https://github.com/eutim/OPI.GPIO
[quote color="warning"]我们不能直接使用OrangePi官方提供的OPI.GPIO库,因为这个库年久失修并未提供对zero2这款型号的支持,我在github上找到了一款对Zero2提供支持的Python库[/quote]
编译修改过的OPI.GPIO库
经过我的尝试,最终在github上发现了别人写好对zero2提供支持的库。
修改版本的OrangePi。GPIO带来对Orange Pi Zero2, Orange Pi 3和Orange Pi Lite2的支持
sudo apt-get update
sudo apt-get install python-dev git
git clone https://github.com/eutim/OPI.GPIO
cd OPI.GPIO
sudo python setup.py install
现在,就可以正常使用这个库啦。
关于显示图片的代码函数
def produceImage(file_in, width, height, file_out):
image = Image.open(file_in)
resized_image = image.resize((width, height), Image.ANTIALIAS)
resized_image.save(file_out)
def png2rgb565(png_path:str):
try:
im=Image.open(png_path)
except:
print ("Fail to open png file ", png_path)
return False
image_height = 128
image_width = 160
screen_data = []
pix = im.load() #load pixel array
for h in range(image_width):
# 高
for w in range(image_height-1,-1,-1):
if w < im.size[0]:
R=pix[w,h][0]>>3
G=pix[w,h][1]>>2
B=pix[w,h][2]>>3
# print("red5:",red)
highBit = 0 | (B << 3) | (G >> 3); # 每个像素写入个字节,highBit高字节,lowBit低字节
lowBit = 0 | (G << 5) | R;
screen_data.append(highBit)
screen_data.append(lowBit)
# print(screen_data)
return screen_data
def drawImg(png_path:str): #入参为160x128像素的image对象
im=Image.open(png_path)
bytes1 = []
i = 0
# print(img160x128.getpixel((0, 0)))
for x in range(0, screenWidth):
# 循环160
for y in range(screenHeight - 1, -1, -1):
# 循环128次
colorValue = im.getpixel((x, y))
# print(img160x128.getpixel((x, y)))
# print(i,colorValue)
red = colorValue[0]
green = colorValue[1]
blue = colorValue[2]
# print("red:",red)
# 本来8位的,改成6位
red = red >> 3; # st7735s的红色占5位
green = green >> 2; # st7735s的绿色占6位
blue = blue >> 3; # st7735s的蓝色占5位
# print("red5:",red)
highBit = 0 | (blue << 3) | (green >> 3); # 每个像素写入个字节,highBit高字节,lowBit低字节
lowBit = 0 | (green << 5) | red;
bytes1.append(highBit)
bytes1.append(lowBit)
return bytes1
def drawImg1():
produceImage("6.png",160,128,"5.png")
rgb565 = png2rgb565("5.png")
i = 0
for j in range(2000, screenWidth * screenHeight * 2, 2000):
sendManyBytes(rgb565[i:j])
i = i + 2000
sendManyBytes(rgb565[i:screenWidth * screenHeight * 2])
更多
- 树莓派 python 驱动 lcd tft spi 1.8寸 ST7735S
- [python中的spidev模块](
有香橙派3LTS驱动这个屏幕的代码嘛,大佬,求一份
这个代码应该也可以用的