- Ask/Aim:
- Principle:
- Practice:
- Linkage:
- Exercise/Experiment:
01 无线电轨
一、问题/目标
这个ESP32控制板是无线电轨小车的核心, 在其接入电脑之后, 可以通过几个小实验来练习 MicroPython 编程, 这样可以为后面小车调试打下基础, 通过这个过程, 可以逐步熟悉控制板上的各个端口。 下面看几个基本小程序的开发测试。
二、基本原理
利用ESP32控制电路板上的 LED, 按键以及 OLED屏幕, 需要知道这些外设对应的端口。 这是ESP32 外部端口的定义, LED1 接入 IO2,。 两个按键分级别介入 IO25, IO26。 这里给出了 OLED 屏幕的五个控制信号对应的端口。 此外, 再找到电路板上它们各自的位置。 从电路板正面来看, LED1 对应的 丝网面为 D1, 两个按键在电路板的右侧, 这个 7 针的插座用于固定 OLED 屏幕的。
通过原理图来看, LED1 端口如果是低电平, 对应的 LED 点亮, 高电平时, LED 熄灭。 两个按键端口, 被两个上拉电阻置成高电位, 当被按下式, 对应的 IO 口电平为 低电平。 右侧的 OLED 端口用于连接外部的 128 ×64 的液晶屏幕。
三、应用举例
1、LED闪烁
在给定的资料中, 包含有一个 Test LED 的 Python 程序,
这是程序的代码。 程序前两行 import 后面所需要用到的模块, 一个是用于管脚控制的 Pin 模块, 另外一个是用于定时的 time 模块。 使用 Pin 类 声明一个 LED0 对象, 带有两个参数。 第一个参数表示 IO 管脚的序号, 刚才从原理图中可以知道 LED1 连接到 IO2。 第二个管脚定义该 管脚输入输出性质。 这里定义成输出管脚, 驱动 LED。 下面利用对象函数 value 设置该管脚的状态。 1 表示高电平, 0 表示低电平。 程序后一半是 while 循环结构, 由于条件始终为 TRUE, 所以这个循环是一直在执行, 直到遇见 break 指令, 或者程序出现错误。 下面使用 if else 语句更改 LED的状态。 前面应用 value 函数读取 LED 的状态, 当它为 0 时, 就将它设置为 1,。 否则设置为 0,。 因此这个操作就是改变 LED 的状态。 每一次循环暂停 0.5 秒钟, 所以 LED 就每秒钟更变一次点亮状态。
将程序拷贝到 Thonny 上面的程序编辑窗口, 通过菜单 Run 命令, 启动程序运行。
程序运行后, LED 便开始闪烁。 如果改变这个延时时间, 比如改为 0.25 秒, 重新装载程序运行, 可以观察到 LED 闪烁频率增加一倍。
2、按钮测试
电路板上的按键可以用于输入临时的命令, 比如开启或者停止程序的执行。 这两个按钮位于电路板的右侧。
程序和非常简单。 前面两句仍然是输入 Pin 和 time 软件模块。 接下来利用 Pin 声明量个管脚, 前面的数字分别表示两个按键接入 ESP32 的IO端口序号, 后面的参数表示 该管脚的属性, 输入输入端口。 后面是一个 while 循环体, 通过 value 读取两个按键的状态, 使用 print 显示在 Thonny 开发环境中 的 Shell 窗口。 按键电平为高电平, 返回 1, 否则返回 0。 根据原理图我们知道, 当按键按下之后, 对应的管脚为低电平。 通过 time 的 sleep 函数, 控制循环周期为 1 秒钟, 所以该程序运行之后, 每个一秒钟显示按键状态。
将程序拷贝到 Thonny 环境内运行, 可以看到 Shell 窗口每个一秒钟输出一行数字, 按键没有按动式, 数字为 1, 当按键按下时, 对应的数字为 0。 因此通过判断管脚的电平高低, 便可以知道对应的按键是否按下, 由此可以控制程序的执行流程。
3、测试OLED
如果需要显示更多的信息, 使用OLED会更加方便。 开发套件中的 OLED 是 128 乘 64 点阵的 OLED显示屏, 可以显示字符、图形等。 OLED屏具有七个管脚, 可以安插在电路板上的插座内。 接入之后, 便可以通过程序进行测试了。
由于 OLED 程序比较复杂, 下面就简单介绍一下测试的步骤。 程序涉及到两个文件, 一个是前面的 OLED096 , 另一个是 test oled 文件。 OLED096文件包含有 屏幕驱动函数, 它会被 test oled 程序调用。 下面需要将这两个文件都载入到 ESP32 设备中。 首先在 Thonny 信件新建立一个文件, 吧 OLED096 文件内容拷贝到程序编辑界面。 然后存储。 存储的时候选择 MicroPython 设备, 存储的文件名为 LQ_OLED096。 注意这个名字不能够改变。 如果存储过程显示设备忙, 可以按动 “STOP” 停止 ESP32 程序运行。 接下来在新建一个文件, 把 TEST OLED 程序内容拷贝到编辑框内, 然后执行该软件。 在Shell 窗口内没有出错提示, 程序便是正常运行了。
程序运行后, 可以观察到 OLED 屏幕上显示的测试信息, 下面有一个递增的数字。 OLED 屏幕如果是双色, 可以看到对应的黄色和蓝色字符。 至此, OLED程序测试完毕。 如果大家对于相应的程序感兴趣, ·可以自行研究一下两个文件中的代码。
四、总结
本文给出了基于 ESP32 的控制板的三个基本测试实验, 通过这些实验, 大家熟悉开发板上 MicroPython 程序的开发。 为后面开发修改智能车程序打下基础。 建议大家在熟悉开发板原理图的基础上自行完成更多的测试。
02 讲座资料
一、讲座视频
在 ESP32控制板上进行初步实验
测试LED程序
from machine import Pin
import time
LED0 = Pin(2,Pin.OUT)
LED0.value(1)
while True:
if LED0.value() is 0:
LED0.value(1)
else:
LED0.value(0)
time.sleep(0.5)
测试按键程序
from machine import Pin
import time
KEY0 = Pin(25, Pin.IN) # create input pin on GPIO25
KEY1 = Pin(26, Pin.IN) # create input pin on GPIO26
while True:
print(KEY0.value()," ",KEY1.value())
time.sleep(1)
测试OLED
Test OLED 程序
import time
from machine import Pin,SoftSPI
from LQ_OLED096 import OLED096_SPI
LED0 = Pin(2,Pin.OUT) #创建LED对象
spi=SoftSPI(baudrate=100000,polarity=1,phase=0,sck=Pin(18),mosi=Pin(23),miso=Pin(19)) #创建软件模拟spi
dc=Pin(15,Pin.OUT)
res=Pin(13,Pin.OUT)
cs=Pin(19,Pin.OUT)
oled=OLED096_SPI(128,64,spi,dc,res,cs) #创建oled对象
value = 0
def Show(): #显示
txt = ""
oled.fill(0) #清屏
txt = (" OLED_Test ")
oled.text(txt, 6*0, 8*0 ,1)
txt = ("HELLO ESP32 ")
oled.text(txt, 6*0, 8*2 ,1)
txt = (" ")
oled.text(txt, 6*0, 8*3 ,1)
txt = ("value: %d ") % (value)
oled.text(txt, 6*2, 8*5 ,1)
oled.show()
while True:
value+=1
Show()
if LED0.value() is 0:
LED0.value(1)
else:
LED0.value(0)
time.sleep(0.5)
OLED096 程序
# ssd1306.py
#MicroPython SSD1306 OLED driver, I2C and SPI interfaces created by Adafruit
import time
import framebuf
# register definitions
SET_CONTRAST = const(0x81)
SET_ENTIRE_ON = const(0xa4)
SET_NORM_INV = const(0xa6)
SET_DISP = const(0xae)
SET_MEM_ADDR = const(0x20)
SET_COL_ADDR = const(0x21)
SET_PAGE_ADDR = const(0x22)
SET_DISP_START_LINE = const(0x40)
SET_SEG_REMAP = const(0xa0)
SET_MUX_RATIO = const(0xa8)
SET_COM_OUT_DIR = const(0xc0)
SET_DISP_OFFSET = const(0xd3)
SET_COM_PIN_CFG = const(0xda)
SET_DISP_CLK_DIV = const(0xd5)
SET_PRECHARGE = const(0xd9)
SET_VCOM_DESEL = const(0xdb)
SET_CHARGE_PUMP = const(0x8d)
class OLED096:
def __init__(self, width, height, external_vcc):
self.width = width
self.height = height
self.external_vcc = external_vcc
self.pages = self.height // 8
# Note the subclass must initialize self.framebuf to a framebuffer.
# This is necessary because the underlying data buffer is different
# between I2C and SPI implementations (I2C needs an extra byte).
self.poweron()
self.init_display()
def init_display(self):
for cmd in (
SET_DISP | 0x00, # off
# address setting
SET_MEM_ADDR, 0x00, # horizontal
# resolution and layout
SET_DISP_START_LINE | 0x00,
SET_SEG_REMAP | 0x01, # column addr 127 mapped to SEG0
SET_MUX_RATIO, self.height - 1,
SET_COM_OUT_DIR | 0x08, # scan from COM[N] to COM0
SET_DISP_OFFSET, 0x00,
SET_COM_PIN_CFG, 0x02 if self.height == 32 else 0x12,
# timing and driving scheme
SET_DISP_CLK_DIV, 0x80,
SET_PRECHARGE, 0x22 if self.external_vcc else 0xf1,
SET_VCOM_DESEL, 0x30, # 0.83*Vcc
# display
SET_CONTRAST, 0xff, # maximum
SET_ENTIRE_ON, # output follows RAM contents
SET_NORM_INV, # not inverted
# charge pump
SET_CHARGE_PUMP, 0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01): # on
self.write_cmd(cmd)
self.fill(0)
self.show()
def poweroff(self):
self.write_cmd(SET_DISP | 0x00)
def contrast(self, contrast):
self.write_cmd(SET_CONTRAST)
self.write_cmd(contrast)
def invert(self, invert):
self.write_cmd(SET_NORM_INV | (invert & 1))
def show(self):
x0 = 0
x1 = self.width - 1
if self.width == 64:
# displays with width of 64 pixels are shifted by 32
x0 += 32
x1 += 32
self.write_cmd(SET_COL_ADDR)
self.write_cmd(x0)
self.write_cmd(x1)
self.write_cmd(SET_PAGE_ADDR)
self.write_cmd(0)
self.write_cmd(self.pages - 1)
self.write_framebuf()
def fill(self, col):
self.framebuf.fill(col)
def pixel(self, x, y, col):
self.framebuf.pixel(x, y, col)
def scroll(self, dx, dy):
self.framebuf.scroll(dx, dy)
def text(self, string, x, y, col=1):
self.framebuf.text(string, x, y, col)
class OLED096_I2C(OLED096):
def __init__(self, width, height, i2c, addr=0x3c, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
# Add an extra byte to the data buffer to hold an I2C data/command byte
# to use hardware-compatible I2C transactions. A memoryview of the
# buffer is used to mask this byte from the framebuffer operations
# (without a major memory hit as memoryview doesn't copy to a separate
# buffer).
self.buffer = bytearray(((height // 8) * width) + 1)
self.buffer[0] = 0x40 # Set first byte of data buffer to Co=0, D/C=1
self.framebuf = framebuf.FrameBuffer1(memoryview(self.buffer)[1:], width, height)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.temp[0] = 0x80 # Co=1, D/C#=0
self.temp[1] = cmd
self.i2c.writeto(self.addr, self.temp)
def write_framebuf(self):
# Blast out the frame buffer using a single I2C transaction to support
# hardware I2C interfaces.
self.i2c.writeto(self.addr, self.buffer)
def poweron(self):
pass
class OLED096_SPI(OLED096):
def __init__(self, width, height, spi, dc, res, cs, external_vcc=False):
self.rate = 10 * 1024 * 1024
dc.init(dc.OUT, value=0)
res.init(res.OUT, value=0)
cs.init(cs.OUT, value=1)
self.spi = spi
self.dc = dc
self.res = res
self.cs = cs
self.buffer = bytearray((height // 8) * width)
self.framebuf = framebuf.FrameBuffer1(self.buffer, width, height)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs.on()
self.dc.off()
self.cs.off()
self.spi.write(bytearray([cmd]))
self.cs.on()
def write_framebuf(self):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs.on()
self.dc.on()
self.cs.off()
self.spi.write(self.buffer)
self.cs.on()
def poweron(self):
self.res.on()
time.sleep_ms(1)
self.res.off()
time.sleep_ms(10)
self.res.on()
二、讲座资料
▲ 图2.2.1 初步实验
- 清华云盘资料下载 : https://cloud.tsinghua.edu.cn/d/7471180bdb0f4d1ebbc7/
- 无线电轨智能车校内赛 | 如何学习无线电轨车模?
- 基础Python学习网站 Learn Python : https://www.learnpython.org/
■ 相关文献链接: