背景:

最近买了几块ESP32模块,看了下mircopython支持还不错,所以买了个SPI触摸屏试试水,记录一下使用过程。

硬件相关:

SPI触摸屏

使用2.4寸屏幕,常见淘宝均可买到,驱动为ILI9341,具体参数如下图:

引脚描述:

ESP32模块

依旧轻松购买于淘宝,由于ESP32被很多家封装,因此模块各有不同,我的如下:

接口描述,网上找了个,差异点在于图中使用GIPOxx,而开发板印刷使用Dxx,例如,开发板上,D12,对应下图的GPIO12,其他的不需要关注:

接线

由于ESP32模块有两个可用SPI,而触摸屏显示和触摸都是使用SPI协议,刚好用完资源,

SPI

SCK

MOSI

MISO

CS

用途

SPI1(HSPI)

GPIO14(D14)

GPIO13(D13)

GPIO12(D12)

GPIO15(D15)

分配给触摸接口

SPI2(VSPI)

GPIO18(D18)

GPIO23(D23)

GPIO19(D19)

GPIO5(D5)

分配给显示接口

图上还会看到VSPI 和 HSPI

具体接线如下:

触摸屏

分配(ESP32引脚名)

归属

显示设备

VCC

3V3

ESP-IO

GND

GND

ESP-IO

CS

D5

SPI2

RESET

D2

ESP-IO

DC/RS

D21

ESP-IO

SDI

D23

SPI2

SCK

D18

SPI2

LED

D4

ESP-IO

SDO

D19

SPI2

触摸设备

T_CLK

D14

SPI1

T_CS

D15

SPI1

T_DIN

D13

SPI1

T_DO

D12

SPI1

T_IRQ

D33

ESP-IO

注意(不建议使用的引脚):

不建议使用或限制使用的引脚
不建议使用 Strapping引脚 ,SPI flash 引脚 以及 仅输入的引脚
Strapping 引脚
GPIO 0
GPIO 2
GPIO 4
GPIO 5 (启动时必须为高电平)
GPIO 12 (启动时必须为低电平)
GPIO 15 (启动时必须为高电平)
注意:
在硬件上要注意使用外接模块时不能将GPIO12拉高,否则将导致ESP32启动异常。还有一些GPIO在启动或重置时其状态更改为高或者输出PWM信号,在使用时需要注意。
集成在ESP-WROOM-32 的 SPI flash 引脚
GPIO 6 到 GPIO 11 在一些 ESP32 开发板中公开。但是,这些引脚连接到 ESP-WROOM-32 芯片上的集成 SPI 闪存,不推荐用于其他用途。所以,不要在你的项目中使用这些引脚:
GPIO 6 (SCK/CLK)
GPIO 7 (SDO/SD0)
GPIO 8 (SDI/SD1)
GPIO 9 (SHD/SD2)
GPIO 10 (SWP/SD3)
GPIO 11 (CSC/CMD)
仅输入引脚
GPIO 34 到 39 是 GPI – 仅输入引脚。这些引脚没有内部上拉或下拉电阻。它们不能用作输出,因此只能将这些引脚用作输入:
GPIO 34
GPIO 35
GPIO 36
GPIO 39
这些引脚都是ESP32用于引导加载程序或者烧录模式/在大多数内置USB/Serial的开发板上,不需要担心这些引脚的状态,开发板会把这些引脚设置为正确的状态,以便使用烧录或启动模式。
但是,如果你有外设连接到这些引脚上,当你在尝试上传新代码、用新固件烧写ESP32或重置电路板时可能会遇到麻烦,例如不明原因的错误和失败。可能是因为这些外设阻止ESP32进入正确的模式。
所以以上的引脚 不建议在项目中使用。

软件相关

目录结构

其中core/screen 文件夹,lib/ili9341.py 和 lib/xpt2046.py 以及main.py是我们本次测试文件.

效果

显示测试:

触摸测试:

代码

lib/ili9341.py

"""ILI9341 LCD/Touch module."""
from time import sleep
from math import cos, sin, pi, radians
from sys import implementation
from framebuf import FrameBuffer, RGB565  # type: ignore
import ustruct  # type: ignoredef color565(r, g, b):"""Return RGB565 color value.Args:r (int): Red value.g (int): Green value.b (int): Blue value."""return (r & 0xf8) << 8 | (g & 0xfc) << 3 | b >> 3class Display(object):"""Serial interface for 16-bit color (5-6-5 RGB) IL9341 display.Note:  All coordinates are zero based."""# Command constants from ILI9341 datasheetNOP = const(0x00)  # No-opSWRESET = const(0x01)  # Software resetRDDID = const(0x04)  # Read display ID infoRDDST = const(0x09)  # Read display statusSLPIN = const(0x10)  # Enter sleep modeSLPOUT = const(0x11)  # Exit sleep modePTLON = const(0x12)  # Partial mode onNORON = const(0x13)  # Normal display mode onRDMODE = const(0x0A)  # Read display power modeRDMADCTL = const(0x0B)  # Read display MADCTLRDPIXFMT = const(0x0C)  # Read display pixel formatRDIMGFMT = const(0x0D)  # Read display image formatRDSELFDIAG = const(0x0F)  # Read display self-diagnosticINVOFF = const(0x20)  # Display inversion offINVON = const(0x21)  # Display inversion onGAMMASET = const(0x26)  # Gamma setDISPLAY_OFF = const(0x28)  # Display offDISPLAY_ON = const(0x29)  # Display onSET_COLUMN = const(0x2A)  # Column address setSET_PAGE = const(0x2B)  # Page address setWRITE_RAM = const(0x2C)  # Memory writeREAD_RAM = const(0x2E)  # Memory readPTLAR = const(0x30)  # Partial areaVSCRDEF = const(0x33)  # Vertical scrolling definitionMADCTL = const(0x36)  # Memory access controlVSCRSADD = const(0x37)  # Vertical scrolling start addressPIXFMT = const(0x3A)  # COLMOD: Pixel format setWRITE_DISPLAY_BRIGHTNESS = const(0x51)  # Brightness hardware dependent!READ_DISPLAY_BRIGHTNESS = const(0x52)WRITE_CTRL_DISPLAY = const(0x53)READ_CTRL_DISPLAY = const(0x54)WRITE_CABC = const(0x55)  # Write Content Adaptive Brightness ControlREAD_CABC = const(0x56)  # Read Content Adaptive Brightness ControlWRITE_CABC_MINIMUM = const(0x5E)  # Write CABC Minimum BrightnessREAD_CABC_MINIMUM = const(0x5F)  # Read CABC Minimum BrightnessFRMCTR1 = const(0xB1)  # Frame rate control (In normal mode/full colors)FRMCTR2 = const(0xB2)  # Frame rate control (In idle mode/8 colors)FRMCTR3 = const(0xB3)  # Frame rate control (In partial mode/full colors)INVCTR = const(0xB4)  # Display inversion controlDFUNCTR = const(0xB6)  # Display function controlPWCTR1 = const(0xC0)  # Power control 1PWCTR2 = const(0xC1)  # Power control 2PWCTRA = const(0xCB)  # Power control APWCTRB = const(0xCF)  # Power control BVMCTR1 = const(0xC5)  # VCOM control 1VMCTR2 = const(0xC7)  # VCOM control 2RDID1 = const(0xDA)  # Read ID 1RDID2 = const(0xDB)  # Read ID 2RDID3 = const(0xDC)  # Read ID 3RDID4 = const(0xDD)  # Read ID 4GMCTRP1 = const(0xE0)  # Positive gamma correctionGMCTRN1 = const(0xE1)  # Negative gamma correctionDTCA = const(0xE8)  # Driver timing control ADTCB = const(0xEA)  # Driver timing control BPOSC = const(0xED)  # Power on sequence controlENABLE3G = const(0xF2)  # Enable 3 gamma controlPUMPRC = const(0xF7)  # Pump ratio controlROTATE = {0: 0x88,90: 0xE8,180: 0x48,270: 0x28}def __init__(self, spi, cs, dc, rst,width=240, height=320, rotation=0):"""Initialize OLED.Args:spi (Class Spi):  SPI interface for OLEDcs (Class Pin):  Chip select pindc (Class Pin):  Data/Command pinrst (Class Pin):  Reset pinwidth (Optional int): Screen width (default 240)height (Optional int): Screen height (default 320)rotation (Optional int): Rotation must be 0 default, 90. 180 or 270"""self.spi = spiself.cs = csself.dc = dcself.rst = rstself.width = widthself.height = heightif rotation not in self.ROTATE.keys():raise RuntimeError('Rotation must be 0, 90, 180 or 270.')else:self.rotation = self.ROTATE[rotation]# Initialize GPIO pins and set implementation specific methodsif implementation.name == 'circuitpython':self.cs.switch_to_output(value=True)self.dc.switch_to_output(value=False)self.rst.switch_to_output(value=True)self.reset = self.reset_cpyself.write_cmd = self.write_cmd_cpyself.write_data = self.write_data_cpyelse:self.cs.init(self.cs.OUT, value=1)self.dc.init(self.dc.OUT, value=0)self.rst.init(self.rst.OUT, value=1)self.reset = self.reset_mpyself.write_cmd = self.write_cmd_mpyself.write_data = self.write_data_mpyself.reset()# Send initialization commandsself.write_cmd(self.SWRESET)  # Software resetsleep(.1)self.write_cmd(self.PWCTRB, 0x00, 0xC1, 0x30)  # Pwr ctrl Bself.write_cmd(self.POSC, 0x64, 0x03, 0x12, 0x81)  # Pwr on seq. ctrlself.write_cmd(self.DTCA, 0x85, 0x00, 0x78)  # Driver timing ctrl Aself.write_cmd(self.PWCTRA, 0x39, 0x2C, 0x00, 0x34, 0x02)  # Pwr ctrl Aself.write_cmd(self.PUMPRC, 0x20)  # Pump ratio controlself.write_cmd(self.DTCB, 0x00, 0x00)  # Driver timing ctrl Bself.write_cmd(self.PWCTR1, 0x23)  # Pwr ctrl 1self.write_cmd(self.PWCTR2, 0x10)  # Pwr ctrl 2self.write_cmd(self.VMCTR1, 0x3E, 0x28)  # VCOM ctrl 1self.write_cmd(self.VMCTR2, 0x86)  # VCOM ctrl 2self.write_cmd(self.MADCTL, self.rotation)  # Memory access ctrlself.write_cmd(self.VSCRSADD, 0x00)  # Vertical scrolling start addressself.write_cmd(self.PIXFMT, 0x55)  # COLMOD: Pixel formatself.write_cmd(self.FRMCTR1, 0x00, 0x18)  # Frame rate ctrlself.write_cmd(self.DFUNCTR, 0x08, 0x82, 0x27)self.write_cmd(self.ENABLE3G, 0x00)  # Enable 3 gamma ctrlself.write_cmd(self.GAMMASET, 0x01)  # Gamma curve selectedself.write_cmd(self.GMCTRP1, 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E,0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00)self.write_cmd(self.GMCTRN1, 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31,0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F)self.write_cmd(self.SLPOUT)  # Exit sleepsleep(.1)self.write_cmd(self.DISPLAY_ON)  # Display onsleep(.1)self.clear()def block(self, x0, y0, x1, y1, data):"""Write a block of data to display.Args:x0 (int):  Starting X position.y0 (int):  Starting Y position.x1 (int):  Ending X position.y1 (int):  Ending Y position.data (bytes): Data buffer to write."""self.write_cmd(self.SET_COLUMN, *ustruct.pack(">HH", x0, x1))self.write_cmd(self.SET_PAGE, *ustruct.pack(">HH", y0, y1))self.write_cmd(self.WRITE_RAM)self.write_data(data)def cleanup(self):"""Clean up resources."""self.clear()self.display_off()self.spi.deinit()print('display off')def clear(self, color=0):"""Clear display.Args:color (Optional int): RGB565 color value (Default: 0 = Black)."""w = self.widthh = self.height# Clear display in 1024 byte blocksif color:line = color.to_bytes(2, 'big') * (w * 8)else:line = bytearray(w * 16)for y in range(0, h, 8):self.block(0, y, w - 1, y + 7, line)def display_off(self):"""Turn display off."""self.write_cmd(self.DISPLAY_OFF)def display_on(self):"""Turn display on."""self.write_cmd(self.DISPLAY_ON)def draw_circle(self, x0, y0, r, color):"""Draw a circle.Args:x0 (int): X coordinate of center point.y0 (int): Y coordinate of center point.r (int): Radius.color (int): RGB565 color value."""f = 1 - rdx = 1dy = -r - rx = 0y = rself.draw_pixel(x0, y0 + r, color)self.draw_pixel(x0, y0 - r, color)self.draw_pixel(x0 + r, y0, color)self.draw_pixel(x0 - r, y0, color)while x < y:if f >= 0:y -= 1dy += 2f += dyx += 1dx += 2f += dxself.draw_pixel(x0 + x, y0 + y, color)self.draw_pixel(x0 - x, y0 + y, color)self.draw_pixel(x0 + x, y0 - y, color)self.draw_pixel(x0 - x, y0 - y, color)self.draw_pixel(x0 + y, y0 + x, color)self.draw_pixel(x0 - y, y0 + x, color)self.draw_pixel(x0 + y, y0 - x, color)self.draw_pixel(x0 - y, y0 - x, color)def draw_ellipse(self, x0, y0, a, b, color):"""Draw an ellipse.Args:x0, y0 (int): Coordinates of center point.a (int): Semi axis horizontal.b (int): Semi axis vertical.color (int): RGB565 color value.Note:The center point is the center of the x0,y0 pixel.Since pixels are not divisible, the axes are integer roundedup to complete on a full pixel.  Therefore the major andminor axes are increased by 1."""a2 = a * ab2 = b * btwoa2 = a2 + a2twob2 = b2 + b2x = 0y = bpx = 0py = twoa2 * y# Plot initial pointsself.draw_pixel(x0 + x, y0 + y, color)self.draw_pixel(x0 - x, y0 + y, color)self.draw_pixel(x0 + x, y0 - y, color)self.draw_pixel(x0 - x, y0 - y, color)# Region 1p = round(b2 - (a2 * b) + (0.25 * a2))while px < py:x += 1px += twob2if p < 0:p += b2 + pxelse:y -= 1py -= twoa2p += b2 + px - pyself.draw_pixel(x0 + x, y0 + y, color)self.draw_pixel(x0 - x, y0 + y, color)self.draw_pixel(x0 + x, y0 - y, color)self.draw_pixel(x0 - x, y0 - y, color)# Region 2p = round(b2 * (x + 0.5) * (x + 0.5) +a2 * (y - 1) * (y - 1) - a2 * b2)while y > 0:y -= 1py -= twoa2if p > 0:p += a2 - pyelse:x += 1px += twob2p += a2 - py + pxself.draw_pixel(x0 + x, y0 + y, color)self.draw_pixel(x0 - x, y0 + y, color)self.draw_pixel(x0 + x, y0 - y, color)self.draw_pixel(x0 - x, y0 - y, color)def draw_hline(self, x, y, w, color):"""Draw a horizontal line.Args:x (int): Starting X position.y (int): Starting Y position.w (int): Width of line.color (int): RGB565 color value."""if self.is_off_grid(x, y, x + w - 1, y):returnline = color.to_bytes(2, 'big') * wself.block(x, y, x + w - 1, y, line)def draw_image(self, path, x=0, y=0, w=320, h=240):"""Draw image from flash.Args:path (string): Image file path.x (int): X coordinate of image left.  Default is 0.y (int): Y coordinate of image top.  Default is 0.w (int): Width of image.  Default is 320.h (int): Height of image.  Default is 240."""x2 = x + w - 1y2 = y + h - 1if self.is_off_grid(x, y, x2, y2):returnwith open(path, "rb") as f:chunk_height = 1024 // wchunk_count, remainder = divmod(h, chunk_height)chunk_size = chunk_height * w * 2chunk_y = yif chunk_count:for c in range(0, chunk_count):buf = f.read(chunk_size)self.block(x, chunk_y,x2, chunk_y + chunk_height - 1,buf)chunk_y += chunk_heightif remainder:buf = f.read(remainder * w * 2)self.block(x, chunk_y,x2, chunk_y + remainder - 1,buf)def draw_letter(self, x, y, letter, font, color, background=0,landscape=False):"""Draw a letter.Args:x (int): Starting X position.y (int): Starting Y position.letter (string): Letter to draw.font (XglcdFont object): Font.color (int): RGB565 color value.background (int): RGB565 background color (default: black).landscape (bool): Orientation (default: False = portrait)"""buf, w, h = font.get_letter(letter, color, background, landscape)# Check for errors (Font could be missing specified letter)if w == 0:return w, hif landscape:y -= wif self.is_off_grid(x, y, x + h - 1, y + w - 1):return 0, 0self.block(x, y,x + h - 1, y + w - 1,buf)else:if self.is_off_grid(x, y, x + w - 1, y + h - 1):return 0, 0self.block(x, y,x + w - 1, y + h - 1,buf)return w, hdef draw_line(self, x1, y1, x2, y2, color):"""Draw a line using Bresenham's algorithm.Args:x1, y1 (int): Starting coordinates of the linex2, y2 (int): Ending coordinates of the linecolor (int): RGB565 color value."""# Check for horizontal lineif y1 == y2:if x1 > x2:x1, x2 = x2, x1self.draw_hline(x1, y1, x2 - x1 + 1, color)return# Check for vertical lineif x1 == x2:if y1 > y2:y1, y2 = y2, y1self.draw_vline(x1, y1, y2 - y1 + 1, color)return# Confirm coordinates in boundaryif self.is_off_grid(min(x1, x2), min(y1, y2),max(x1, x2), max(y1, y2)):return# Changes in x, ydx = x2 - x1dy = y2 - y1# Determine how steep the line isis_steep = abs(dy) > abs(dx)# Rotate lineif is_steep:x1, y1 = y1, x1x2, y2 = y2, x2# Swap start and end points if necessaryif x1 > x2:x1, x2 = x2, x1y1, y2 = y2, y1# Recalculate differentialsdx = x2 - x1dy = y2 - y1# Calculate errorerror = dx >> 1ystep = 1 if y1 < y2 else -1y = y1for x in range(x1, x2 + 1):# Had to reverse HW ????if not is_steep:self.draw_pixel(x, y, color)else:self.draw_pixel(y, x, color)error -= abs(dy)if error < 0:y += ysteperror += dxdef draw_lines(self, coords, color):"""Draw multiple lines.Args:coords ([[int, int],...]): Line coordinate X, Y pairscolor (int): RGB565 color value."""# Starting pointx1, y1 = coords[0]# Iterate through coordinatesfor i in range(1, len(coords)):x2, y2 = coords[i]self.draw_line(x1, y1, x2, y2, color)x1, y1 = x2, y2def draw_pixel(self, x, y, color):"""Draw a single pixel.Args:x (int): X position.y (int): Y position.color (int): RGB565 color value."""if self.is_off_grid(x, y, x, y):returnself.block(x, y, x, y, color.to_bytes(2, 'big'))def draw_polygon(self, sides, x0, y0, r, color, rotate=0):"""Draw an n-sided regular polygon.Args:sides (int): Number of polygon sides.x0, y0 (int): Coordinates of center point.r (int): Radius.color (int): RGB565 color value.rotate (Optional float): Rotation in degrees relative to origin.Note:The center point is the center of the x0,y0 pixel.Since pixels are not divisible, the radius is integer roundedup to complete on a full pixel.  Therefore diameter = 2 x r + 1."""coords = []theta = radians(rotate)n = sides + 1for s in range(n):t = 2.0 * pi * s / sides + thetacoords.append([int(r * cos(t) + x0), int(r * sin(t) + y0)])# Cast to python float first to fix rounding errorsself.draw_lines(coords, color=color)def draw_rectangle(self, x, y, w, h, color):"""Draw a rectangle.Args:x (int): Starting X position.y (int): Starting Y position.w (int): Width of rectangle.h (int): Height of rectangle.color (int): RGB565 color value."""x2 = x + w - 1y2 = y + h - 1self.draw_hline(x, y, w, color)self.draw_hline(x, y2, w, color)self.draw_vline(x, y, h, color)self.draw_vline(x2, y, h, color)def draw_sprite(self, buf, x, y, w, h):"""Draw a sprite (optimized for horizontal drawing).Args:buf (bytearray): Buffer to draw.x (int): Starting X position.y (int): Starting Y position.w (int): Width of drawing.h (int): Height of drawing."""x2 = x + w - 1y2 = y + h - 1if self.is_off_grid(x, y, x2, y2):returnself.block(x, y, x2, y2, buf)def draw_text(self, x, y, text, font, color,  background=0,landscape=False, spacing=1):"""Draw text.Args:x (int): Starting X position.y (int): Starting Y position.text (string): Text to draw.font (XglcdFont object): Font.color (int): RGB565 color value.background (int): RGB565 background color (default: black).landscape (bool): Orientation (default: False = portrait)spacing (int): Pixels between letters (default: 1)"""for letter in text:# Get letter array and letter dimensionsw, h = self.draw_letter(x, y, letter, font, color, background,landscape)# Stop on errorif w == 0 or h == 0:print('Invalid width {0} or height {1}'.format(w, h))returnif landscape:# Fill in spacingif spacing:self.fill_hrect(x, y - w - spacing, h, spacing, background)# Position y for next lettery -= (w + spacing)else:# Fill in spacingif spacing:self.fill_hrect(x + w, y, spacing, h, background)# Position x for next letterx += (w + spacing)# # Fill in spacing# if spacing:#     self.fill_vrect(x + w, y, spacing, h, background)# # Position x for next letter# x += w + spacingdef draw_text8x8(self, x, y, text, color,  background=0,rotate=0):"""Draw text using built-in MicroPython 8x8 bit font.Args:x (int): Starting X position.y (int): Starting Y position.text (string): Text to draw.color (int): RGB565 color value.background (int): RGB565 background color (default: black).rotate(int): 0, 90, 180, 270"""w = len(text) * 8h = 8# Confirm coordinates in boundaryif self.is_off_grid(x, y, x + 7, y + 7):return# Rearrange colorr = (color & 0xF800) >> 8g = (color & 0x07E0) >> 3b = (color & 0x1F) << 3buf = bytearray(w * 16)fbuf = FrameBuffer(buf, w, h, RGB565)if background != 0:bg_r = (background & 0xF800) >> 8bg_g = (background & 0x07E0) >> 3bg_b = (background & 0x1F) << 3fbuf.fill(color565(bg_b, bg_r, bg_g))fbuf.text(text, 0, 0, color565(b, r, g))if rotate == 0:self.block(x, y, x + w - 1, y + (h - 1), buf)elif rotate == 90:buf2 = bytearray(w * 16)fbuf2 = FrameBuffer(buf2, h, w, RGB565)for y1 in range(h):for x1 in range(w):fbuf2.pixel(y1, x1,fbuf.pixel(x1, (h - 1) - y1))self.block(x, y, x + (h - 1), y + w - 1, buf2)elif rotate == 180:buf2 = bytearray(w * 16)fbuf2 = FrameBuffer(buf2, w, h, RGB565)for y1 in range(h):for x1 in range(w):fbuf2.pixel(x1, y1,fbuf.pixel((w - 1) - x1, (h - 1) - y1))self.block(x, y, x + w - 1, y + (h - 1), buf2)elif rotate == 270:buf2 = bytearray(w * 16)fbuf2 = FrameBuffer(buf2, h, w, RGB565)for y1 in range(h):for x1 in range(w):fbuf2.pixel(y1, x1,fbuf.pixel((w - 1) - x1, y1))self.block(x, y, x + (h - 1), y + w - 1, buf2)def draw_vline(self, x, y, h, color):"""Draw a vertical line.Args:x (int): Starting X position.y (int): Starting Y position.h (int): Height of line.color (int): RGB565 color value."""# Confirm coordinates in boundaryif self.is_off_grid(x, y, x, y + h - 1):returnline = color.to_bytes(2, 'big') * hself.block(x, y, x, y + h - 1, line)def fill_circle(self, x0, y0, r, color):"""Draw a filled circle.Args:x0 (int): X coordinate of center point.y0 (int): Y coordinate of center point.r (int): Radius.color (int): RGB565 color value."""f = 1 - rdx = 1dy = -r - rx = 0y = rself.draw_vline(x0, y0 - r, 2 * r + 1, color)while x < y:if f >= 0:y -= 1dy += 2f += dyx += 1dx += 2f += dxself.draw_vline(x0 + x, y0 - y, 2 * y + 1, color)self.draw_vline(x0 - x, y0 - y, 2 * y + 1, color)self.draw_vline(x0 - y, y0 - x, 2 * x + 1, color)self.draw_vline(x0 + y, y0 - x, 2 * x + 1, color)def fill_ellipse(self, x0, y0, a, b, color):"""Draw a filled ellipse.Args:x0, y0 (int): Coordinates of center point.a (int): Semi axis horizontal.b (int): Semi axis vertical.color (int): RGB565 color value.Note:The center point is the center of the x0,y0 pixel.Since pixels are not divisible, the axes are integer roundedup to complete on a full pixel.  Therefore the major andminor axes are increased by 1."""a2 = a * ab2 = b * btwoa2 = a2 + a2twob2 = b2 + b2x = 0y = bpx = 0py = twoa2 * y# Plot initial pointsself.draw_line(x0, y0 - y, x0, y0 + y, color)# Region 1p = round(b2 - (a2 * b) + (0.25 * a2))while px < py:x += 1px += twob2if p < 0:p += b2 + pxelse:y -= 1py -= twoa2p += b2 + px - pyself.draw_line(x0 + x, y0 - y, x0 + x, y0 + y, color)self.draw_line(x0 - x, y0 - y, x0 - x, y0 + y, color)# Region 2p = round(b2 * (x + 0.5) * (x + 0.5) +a2 * (y - 1) * (y - 1) - a2 * b2)while y > 0:y -= 1py -= twoa2if p > 0:p += a2 - pyelse:x += 1px += twob2p += a2 - py + pxself.draw_line(x0 + x, y0 - y, x0 + x, y0 + y, color)self.draw_line(x0 - x, y0 - y, x0 - x, y0 + y, color)def fill_hrect(self, x, y, w, h, color):"""Draw a filled rectangle (optimized for horizontal drawing).Args:x (int): Starting X position.y (int): Starting Y position.w (int): Width of rectangle.h (int): Height of rectangle.color (int): RGB565 color value."""if self.is_off_grid(x, y, x + w - 1, y + h - 1):returnchunk_height = 1024 // wchunk_count, remainder = divmod(h, chunk_height)chunk_size = chunk_height * wchunk_y = yif chunk_count:buf = color.to_bytes(2, 'big') * chunk_sizefor c in range(0, chunk_count):self.block(x, chunk_y,x + w - 1, chunk_y + chunk_height - 1,buf)chunk_y += chunk_heightif remainder:buf = color.to_bytes(2, 'big') * remainder * wself.block(x, chunk_y,x + w - 1, chunk_y + remainder - 1,buf)def fill_rectangle(self, x, y, w, h, color):"""Draw a filled rectangle.Args:x (int): Starting X position.y (int): Starting Y position.w (int): Width of rectangle.h (int): Height of rectangle.color (int): RGB565 color value."""if self.is_off_grid(x, y, x + w - 1, y + h - 1):returnif w > h:self.fill_hrect(x, y, w, h, color)else:self.fill_vrect(x, y, w, h, color)def fill_polygon(self, sides, x0, y0, r, color, rotate=0):"""Draw a filled n-sided regular polygon.Args:sides (int): Number of polygon sides.x0, y0 (int): Coordinates of center point.r (int): Radius.color (int): RGB565 color value.rotate (Optional float): Rotation in degrees relative to origin.Note:The center point is the center of the x0,y0 pixel.Since pixels are not divisible, the radius is integer roundedup to complete on a full pixel.  Therefore diameter = 2 x r + 1."""# Determine side coordinatescoords = []theta = radians(rotate)n = sides + 1for s in range(n):t = 2.0 * pi * s / sides + thetacoords.append([int(r * cos(t) + x0), int(r * sin(t) + y0)])# Starting pointx1, y1 = coords[0]# Minimum Maximum X dictxdict = {y1: [x1, x1]}# Iterate through coordinatesfor row in coords[1:]:x2, y2 = rowxprev, yprev = x2, y2# Calculate perimeter# Check for horizontal sideif y1 == y2:if x1 > x2:x1, x2 = x2, x1if y1 in xdict:xdict[y1] = [min(x1, xdict[y1][0]), max(x2, xdict[y1][1])]else:xdict[y1] = [x1, x2]x1, y1 = xprev, yprevcontinue# Non horizontal side# Changes in x, ydx = x2 - x1dy = y2 - y1# Determine how steep the line isis_steep = abs(dy) > abs(dx)# Rotate lineif is_steep:x1, y1 = y1, x1x2, y2 = y2, x2# Swap start and end points if necessaryif x1 > x2:x1, x2 = x2, x1y1, y2 = y2, y1# Recalculate differentialsdx = x2 - x1dy = y2 - y1# Calculate errorerror = dx >> 1ystep = 1 if y1 < y2 else -1y = y1# Calcualte minimum and maximum x valuesfor x in range(x1, x2 + 1):if is_steep:if x in xdict:xdict[x] = [min(y, xdict[x][0]), max(y, xdict[x][1])]else:xdict[x] = [y, y]else:if y in xdict:xdict[y] = [min(x, xdict[y][0]), max(x, xdict[y][1])]else:xdict[y] = [x, x]error -= abs(dy)if error < 0:y += ysteperror += dxx1, y1 = xprev, yprev# Fill polygonfor y, x in xdict.items():self.draw_hline(x[0], y, x[1] - x[0] + 2, color)def fill_vrect(self, x, y, w, h, color):"""Draw a filled rectangle (optimized for vertical drawing).Args:x (int): Starting X position.y (int): Starting Y position.w (int): Width of rectangle.h (int): Height of rectangle.color (int): RGB565 color value."""if self.is_off_grid(x, y, x + w - 1, y + h - 1):returnchunk_width = 1024 // hchunk_count, remainder = divmod(w, chunk_width)chunk_size = chunk_width * hchunk_x = xif chunk_count:buf = color.to_bytes(2, 'big') * chunk_sizefor c in range(0, chunk_count):self.block(chunk_x, y,chunk_x + chunk_width - 1, y + h - 1,buf)chunk_x += chunk_widthif remainder:buf = color.to_bytes(2, 'big') * remainder * hself.block(chunk_x, y,chunk_x + remainder - 1, y + h - 1,buf)def is_off_grid(self, xmin, ymin, xmax, ymax):"""Check if coordinates extend past display boundaries.Args:xmin (int): Minimum horizontal pixel.ymin (int): Minimum vertical pixel.xmax (int): Maximum horizontal pixel.ymax (int): Maximum vertical pixel.Returns:boolean: False = Coordinates OK, True = Error."""if xmin < 0:print('x-coordinate: {0} below minimum of 0.'.format(xmin))return Trueif ymin < 0:print('y-coordinate: {0} below minimum of 0.'.format(ymin))return Trueif xmax >= self.width:print('x-coordinate: {0} above maximum of {1}.'.format(xmax, self.width - 1))return Trueif ymax >= self.height:print('y-coordinate: {0} above maximum of {1}.'.format(ymax, self.height - 1))return Truereturn Falsedef load_sprite(self, path, w, h):"""Load sprite image.Args:path (string): Image file path.w (int): Width of image.h (int): Height of image.Notes:w x h cannot exceed 2048"""buf_size = w * h * 2with open(path, "rb") as f:return f.read(buf_size)def reset_cpy(self):"""Perform reset: Low=initialization, High=normal operation.Notes: CircuitPython implemntation"""self.rst.value = Falsesleep(.05)self.rst.value = Truesleep(.05)def reset_mpy(self):"""Perform reset: Low=initialization, High=normal operation.Notes: MicroPython implemntation"""self.rst(0)sleep(.05)self.rst(1)sleep(.05)def scroll(self, y):"""Scroll display vertically.Args:y (int): Number of pixels to scroll display."""self.write_cmd(self.VSCRSADD, y >> 8, y & 0xFF)def set_scroll(self, top, bottom):"""Set the height of the top and bottom scroll margins.Args:top (int): Height of top scroll marginbottom (int): Height of bottom scroll margin"""if top + bottom <= self.height:middle = self.height - (top + bottom)print(top, middle, bottom)self.write_cmd(self.VSCRDEF,top >> 8,top & 0xFF,middle >> 8,middle & 0xFF,bottom >> 8,bottom & 0xFF)def sleep(self, enable=True):"""Enters or exits sleep mode.Args:enable (bool): True (default)=Enter sleep mode, False=Exit sleep"""if enable:self.write_cmd(self.SLPIN)else:self.write_cmd(self.SLPOUT)def write_cmd_mpy(self, command, *args):"""Write command to OLED (MicroPython).Args:command (byte): ILI9341 command code.*args (optional bytes): Data to transmit."""self.dc(0)self.cs(0)self.spi.write(bytearray([command]))self.cs(1)# Handle any passed dataif len(args) > 0:self.write_data(bytearray(args))def write_cmd_cpy(self, command, *args):"""Write command to OLED (CircuitPython).Args:command (byte): ILI9341 command code.*args (optional bytes): Data to transmit."""self.dc.value = Falseself.cs.value = False# Confirm SPI locked before writingwhile not self.spi.try_lock():passself.spi.write(bytearray([command]))self.spi.unlock()self.cs.value = True# Handle any passed dataif len(args) > 0:self.write_data(bytearray(args))def write_data_mpy(self, data):"""Write data to OLED (MicroPython).Args:data (bytes): Data to transmit."""self.dc(1)self.cs(0)self.spi.write(data)self.cs(1)def write_data_cpy(self, data):"""Write data to OLED (CircuitPython).Args:data (bytes): Data to transmit."""self.dc.value = Trueself.cs.value = False# Confirm SPI locked before writingwhile not self.spi.try_lock():passself.spi.write(data)self.spi.unlock()self.cs.value = True

lib/xpt2046.py

"""XPT2046 Touch module."""
from time import sleepclass Touch(object):"""Serial interface for XPT2046 Touch Screen Controller."""# Command constants from ILI9341 datasheetGET_X = const(0b11010000)  # X positionGET_Y = const(0b10010000)  # Y positionGET_Z1 = const(0b10110000)  # Z1 positionGET_Z2 = const(0b11000000)  # Z2 positionGET_TEMP0 = const(0b10000000)  # Temperature 0GET_TEMP1 = const(0b11110000)  # Temperature 1GET_BATTERY = const(0b10100000)  # Battery monitorGET_AUX = const(0b11100000)  # Auxiliary input to ADCdef __init__(self, spi, cs, int_pin=None, int_handler=None,width=240, height=320,x_min=100, x_max=1962, y_min=100, y_max=1900):"""Initialize touch screen controller.Args:spi (Class Spi):  SPI interface for OLEDcs (Class Pin):  Chip select pinint_pin (Class Pin):  Touch controller interrupt pinint_handler (function): Handler for screen interruptwidth (int): Width of LCD screenheight (int): Height of LCD screenx_min (int): Minimum x coordinatex_max (int): Maximum x coordinatey_min (int): Minimum Y coordinatey_max (int): Maximum Y coordinate"""self.spi = spiself.cs = csself.cs.init(self.cs.OUT, value=1)self.rx_buf = bytearray(3)  # Receive bufferself.tx_buf = bytearray(3)  # Transmit bufferself.width = widthself.height = height# Set calibrationself.x_min = x_minself.x_max = x_maxself.y_min = y_minself.y_max = y_maxself.x_multiplier = width / (x_max - x_min)self.x_add = x_min * -self.x_multiplierself.y_multiplier = height / (y_max - y_min)self.y_add = y_min * -self.y_multiplierif int_pin is not None:self.int_pin = int_pinself.int_pin.init(int_pin.IN)self.int_handler = int_handlerself.int_locked = Falseint_pin.irq(trigger=int_pin.IRQ_FALLING | int_pin.IRQ_RISING,handler=self.int_press)def get_touch(self):"""Take multiple samples to get accurate touch reading."""timeout = 2  # set timeout to 2 secondsconfidence = 5buff = [[0, 0] for x in range(confidence)]buf_length = confidence  # Require a confidence of 5 good samplesbuffptr = 0  # Track current buffer positionnsamples = 0  # Count sampleswhile timeout > 0:if nsamples == buf_length:meanx = sum([c[0] for c in buff]) // buf_lengthmeany = sum([c[1] for c in buff]) // buf_lengthdev = sum([(c[0] - meanx)**2 +(c[1] - meany)**2 for c in buff]) / buf_lengthif dev <= 50:  # Deviation should be under margin of 50return self.normalize(meanx, meany)# get a new valuesample = self.raw_touch()  # get a touchif sample is None:nsamples = 0    # Invalidate buffelse:buff[buffptr] = sample  # put in buffbuffptr = (buffptr + 1) % buf_length  # Incr, until rollovernsamples = min(nsamples + 1, buf_length)  # Incr. until maxsleep(.05)timeout -= .05return Nonedef int_press(self, pin):"""Send X,Y values to passed interrupt handler."""if not pin.value() and not self.int_locked:self.int_locked = True  # Lock Interruptbuff = self.raw_touch()if buff is not None:x, y = self.normalize(*buff)self.int_handler(x, y)sleep(.1)  # Debounce falling edgeelif pin.value() and self.int_locked:sleep(.1)  # Debounce rising edgeself.int_locked = False  # Unlock interruptdef normalize(self, x, y):"""Normalize mean X,Y values to match LCD screen."""x = int(self.x_multiplier * x + self.x_add)y = int(self.y_multiplier * y + self.y_add)return x, ydef raw_touch(self):"""Read raw X,Y touch values.Returns:tuple(int, int): X, Y"""x = self.send_command(self.GET_X)y = self.send_command(self.GET_Y)if self.x_min <= x <= self.x_max and self.y_min <= y <= self.y_max:return (x, y)else:return Nonedef send_command(self, command):"""Write command to XT2046 (MicroPython).Args:command (byte): XT2046 command code.Returns:int: 12 bit response"""self.tx_buf[0] = commandself.cs(0)self.spi.write_readinto(self.tx_buf, self.rx_buf)self.cs(1)return (self.rx_buf[1] << 4) | (self.rx_buf[2] >> 4)

core/screen/TLedCfg.py


class TLedConfig:def __init__(self) -> None:self.CS = 15    # 片选, 低电平使能self.RESET = 2  # 低电平复位self.DC = 21    # 液晶屏寄存器/数据选择信号,0:寄存器,1:数据self.SDI = 23   # MOSI 写self.SCK = 18   # 时钟self.LED = 4    # 背光控制,高电平点亮self.SDO = 19   # MISO 读self.T_CLK = 14 # 触摸时钟self.T_CS =  27 # 片选,低电平使能self.T_DIN = 13 # 总线输入,接MOSIself.T_DO  = 12 # 总线输出,接MISOself.T_IRQ = 33 # 中断,检测到触摸时为低电平

core/screen/test.py

from lib.ili9341 import Display, color565
from lib.xpt2046 import Touch
from machine import Pin, SPI, idle
from core.screen.TLedCfg import TLedConfig
from time import sleepdef ScreenTest():ledCfg = TLedConfig()power = Pin(ledCfg.LED, Pin.OUT)power.value(1)spi = SPI(2, baudrate=40000000, sck=Pin(ledCfg.SCK), mosi=Pin(ledCfg.SDI))display = Display(spi, dc=Pin(ledCfg.DC), cs=Pin(ledCfg.CS), rst=Pin(ledCfg.RESET))display.clear(color565(64, 0, 255))sleep(1)display.clear()display.draw_hline(10, 319, 229, color565(255, 0, 255))sleep(1)display.draw_vline(10, 0, 319, color565(0, 255, 255))sleep(1)display.fill_hrect(23, 50, 30, 75, color565(255, 255, 255))sleep(1)display.draw_hline(0, 0, 222, color565(255, 0, 0))sleep(1)display.draw_line(127, 0, 64, 127, color565(255, 255, 0))sleep(2)display.clear()coords = [[0, 63], [78, 80], [122, 92], [50, 50], [78, 15], [0, 63]]display.draw_lines(coords, color565(0, 255, 255))sleep(1)display.clear()display.fill_polygon(7, 120, 120, 100, color565(0, 255, 0))sleep(1)display.fill_rectangle(0, 0, 15, 227, color565(255, 0, 0))sleep(1)display.clear()display.fill_rectangle(0, 0, 163, 163, color565(128, 128, 255))sleep(1)display.draw_rectangle(0, 64, 163, 163, color565(255, 0, 255))sleep(1)display.fill_rectangle(64, 0, 163, 163, color565(128, 0, 255))sleep(1)display.draw_polygon(3, 120, 286, 30, color565(0, 64, 255), rotate=15)sleep(3)display.clear()display.fill_circle(132, 132, 70, color565(0, 255, 0))sleep(1)display.draw_circle(132, 96, 70, color565(0, 0, 255))sleep(1)display.fill_ellipse(96, 96, 30, 16, color565(255, 0, 0))sleep(1)display.draw_ellipse(96, 256, 16, 30, color565(255, 255, 0))sleep(5)display.cleanup()class Demo(object):"""Touchscreen simple demo."""CYAN = color565(0, 255, 255)PURPLE = color565(255, 0, 255)WHITE = color565(255, 255, 255)def __init__(self, display, spi2):"""Initialize box.Args:display (ILI9341): display objectspi2 (SPI): SPI bus"""ledCfg = TLedConfig()self.display = displayself.touch = Touch(spi2, cs=Pin(ledCfg.T_CS), int_pin=Pin(ledCfg.T_IRQ),int_handler=self.touchscreen_press)# Display initial messageself.display.draw_text8x8(self.display.width // 2 - 32,self.display.height - 9,"TOUCH ME",self.WHITE,background=self.PURPLE)# A small 5x5 sprite for the dotself.dot = bytearray(b'\x00\x00\x07\xE0\xF8\x00\x07\xE0\x00\x00\x07\xE0\xF8\x00\xF8\x00\xF8\x00\x07\xE0\xF8\x00\xF8\x00\xF8\x00\xF8\x00\xF8\x00\x07\xE0\xF8\x00\xF8\x00\xF8\x00\x07\xE0\x00\x00\x07\xE0\xF8\x00\x07\xE0\x00\x00')def touchscreen_press(self, x, y):"""Process touchscreen press events."""print('touch once ... ...')# Y needs to be flipped# y = (self.display.height - 1) - y# Display coordinatesself.display.draw_text8x8(self.display.width // 2 - 32,self.display.height - 9,"{0:03d}, {1:03d}".format(x, y),self.CYAN)# Draw dotself.display.draw_sprite(self.dot, x ,y, 5, 5)def TouchTest():"""Test code."""ledCfg = TLedConfig()power = Pin(ledCfg.LED, Pin.OUT)power.value(1)spi1 = SPI(2, baudrate=32000000, sck=Pin(ledCfg.SCK), mosi=Pin(ledCfg.SDI))spi2 = SPI(1, baudrate=1000000, sck=Pin(ledCfg.T_CLK), mosi=Pin(ledCfg.T_DIN))display = Display(spi1, dc=Pin(ledCfg.DC), cs=Pin(ledCfg.CS), rst=Pin(ledCfg.RESET))# display = Display(spi1, dc=Pin(4), cs=Pin(16), rst=Pin(17))# spi2 = SPI(2, baudrate=1000000, sck=Pin(18), mosi=Pin(23), miso=Pin(19))Demo(display, spi2)try:while True:idle()except KeyboardInterrupt:print("\nCtrl-C pressed.  Cleaning up and exiting...")finally:display.cleanup()

main.py

from core.WifiManager import WifiManager
from core.screen.TLedCfg import TLedConfig
from core.screen.test import ScreenTest,TouchTestdef main():print("Welcome to MicroPython!")# wifi = WifiManager()# wifi.start_ap("esp32","12345678")if __name__ == '__main__':main()ScreenTest()# TouchTest()

main函数里,测试哪个就把另一个注释就好。

附录:

  • 驱动和参考主要来自如下:https://github.com/rdagger/micropython-ili9341

  • LVGL:https://github.com/lvgl/lvgl/tree/b66512183ab0ba3b36e8175504fa6fe9cd6e5312

  • ESP32引脚参考大全ESP32 – GPIO 引脚参考大全 – 凌顺实验室 (lingshunlab.com)

【micropython】SPI触摸屏开发相关推荐

  1. 触摸屏开发_Microchip推出新型电容触摸式控制器,加速汽车触摸屏EMI认证

    为解决汽车触摸屏开发人员面临的电磁干扰(EMI)和电磁兼容性(EMC)问题,Microchip Technology Inc.(美国微芯科技公司)宣布推出三款全新的maXTouch® 触摸屏控制器和附 ...

  2. MicroPython支持的开发板:高性能、低成本创客首选

    Python的开放.简洁.黏合正符合了现发展阶段对大数据分析.可视化.各种平台程序协作产生了快速的促进作用.自Python3的发布到现在已有五六年的时间,从刚发布的反对声音到慢慢被接受与喜欢经过了太漫 ...

  3. 探索MicroPython(二)--搭建开发环境

    搭建开发环境 1. 简述 2. 安装Python3 3. 安装Mu 4. 安装驱动 5. 更新固件 6. 运行程序 1. 简述 pyBoard上使用了STM32自身flash的一定空间作为U盘与电脑进 ...

  4. 咸鱼Micropython—SPI

    咸鱼Micropython-SPI SPI是Serial peripheral interface(串行外设接口)的简称,它也是一个通用的串行通信接口.无论从硬件还是软件上看,SPI和I2C都很类 似 ...

  5. 成都工控开发:太简单了!串口触摸屏开发HMI的全流程介绍

    成都工控开发:太简单了!串口触摸屏开发HMI的全流程介绍 有很多事,把它说清楚了,就简单了,比如用触摸屏开发HMI. 触摸屏是很常见的,比如说你用的智能手机,用手指在上面划划划就能显示出我们想要的页面 ...

  6. 太简单了!串口触摸屏开发HMI的全流程介绍

    有很多事,把它说清楚了,就简单了,比如用触摸屏开发HMI. 触摸屏是很常见的,比如说你用的智能手机,用手指在上面划划划就能显示出我们想要的页面,比如说早些年很流行的画画板,用触摸笔在上面画一画就能画出 ...

  7. 太简单了!串口触摸屏开发HMI的全流程介绍成都工控开发

    有很多事,把它说清楚了,就简单了,比如用触摸屏开发HMI. 触摸屏是很常见的,比如说你用的智能手机,用手指在上面划划划就能显示出我们想要的页面,比如说早些年很流行的画画板,用触摸笔在上面画一画就能画出 ...

  8. MTK SPI驱动开发

    MTK SPI驱动开发 1.由于SPI驱动 MTK封装的比较好,所以比较好处理. 唯一需要注意的是spi使用的全局Buffer必须要放在内存地址为"NONCACHEDZI"的区域直 ...

  9. MicroPython做嵌入式开发的优缺点

    关注+星标公众号,不错过精彩内容 编排 | strongerHuang 微信公众号 | 嵌入式专栏 Python的火热程度相信大家都看到了,自然,MCU和嵌入式也紧跟"潮流",随之 ...

最新文章

  1. Struts2 的stream result用法
  2. 安装和使用花生壳(linux)
  3. 制作FAT12软盘以查看软盘的根目录条目+文件属性+文件内容
  4. git学习(8):windows系统下VI编辑器的基本使用
  5. LeetCode 2212. 射箭比赛中的最大得分(状态枚举)
  6. 多个无线 AP 怎么实现无缝漫游?
  7. 微信小程序——点赞和取消点赞功能的实现
  8. Excel公式向导,详细演艺多条件求平均值的操作
  9. 【视频分享】尚硅谷Java视频教程_Spring Boot视频教程(下)整合篇
  10. python刷题大杂烩
  11. 小红帽系统搭建dns服务器,红帽7(centos 7 )配置DNS服务器
  12. 如何快速生成2000万行数据
  13. 1198 今天星期几
  14. 【星辰傀儡线·命运环·卷一 血鸦】 6 四大长老
  15. MATLAB做回归分析
  16. 中信银行柜员岗面试总结
  17. TCP/IP五层模型简介
  18. RAW格式照片编辑工具分享,拥有无可替代的重要性
  19. 一开机鼠标锁定计算机图标,电脑鼠标可以移动无法点击桌面图标怎么回事
  20. onUnload事件

热门文章

  1. 在 Solaris 系统上安装 PHP
  2. 将文件大小数值转换成B、KB、MB、GB
  3. 数据库概念 第六版 中英文 带目录+习题答案
  4. 人脸识别Face Recognition综述
  5. 学了 C 语言到底能做什么, 能从事什么工作?
  6. android适配手机与平板,关于平板适配问题
  7. MyIE2变Maxthon就来了(转)
  8. java计算机毕业设计学生日常事务管理系统源码+mysql数据库+lw文档+系统+调试部署
  9. matlab norm函数使用_matlab中norm函数的用法
  10. pcm系统设计及matlab仿真实现,PCM系统设计及MATLAB仿真实现