本次参加的是2023寒假一起练平台(1)- 基于STEP Pico的嵌入式系统学习平台
1、完成功能描述:
使用到的硬件设备:
1、OLED屏:显示信息,显示当前电压,显示电压增减波形。
2、电位器:调整电压变化。
3、核心板:数据处理,控制。
软件实现的功能要求:
首先是利用PICO(RP2040)通过 SPI点亮OLED128,再通过ADC采集(ADC2)电位器的电压,通过调节电位器0-3.3V之间变化的电压,树莓派Pico内部的ADC对该电压进行采集,经过计算以后对应到相应的电压值,并显示在OLED上。
注释:先将写好的程序,编译通过后,上传到PCIO,这个时候PCIO的OLED屏就会显示,就会被点亮,左边就会显示电压值,显示字符有0V 1V 2V 3V 4V,然后中间的位置就会出现当前电位器调节的电压值在什么位置;通过手动旋转电位器,这个时候电位器的阻值会随意转动而变化,而ADC读取到的值也会发生改变,从而打印出来的值也会随之显示在OLED屏上。
2、功能要求:
项目4 - 制作一个电压表
具体要求:利用板上的电位计调节电压从0-3.3V之间变化,在OLED显示屏上显示电压值,可以以数字的方式,也可以以图形的方式来显示
实现方式:调节电位计产生0-3.3V之间变化的电压,树莓派Pico内部的ADC对该电压进行采集,得到0-4095之间的数值,经过计算以后对应到相应的电压值,再通过OLED显示屏显示出来。
拿到此开发板,我首先是对核心器件进行了解,熟悉,第一步那就是主控芯片,先看引脚再看框图。
3、硬件资源:
ADC框图:
ADC主要用于对模拟信号进行数字采集,以进行数据处理,是指将连续变量的模拟信号转换为离散的数字信号。
典型的模拟数字转换器将模拟信号转换为表示一定比例电压值的数字信号。
SPI功能分析:
SPI是串行外设接口是由 Motorola 公司提出的一种高速的,全双工,同步的通信总线。SPI接口一般使用4条线:串行时钟线(SCK)、主机输入/从机输出数据线MISO、主机输出/从机输入数据线MOST和低电平有效的从机选择线C/S(有的SPI接口芯片带有中断信号线INT或INT、有的SPI接口芯片没有主机输出/从机输入数据线MOSI)。
SPI 其中一种通讯模式,SPI 一共有四种通讯模式,它们的主要区别是总线空闲时 SCK 的时钟状态以及数据采样时刻。为方便说明,在此引入“时钟极性 CPOL”和“时钟相位 CPHA”的概念。
时钟极性 CPOL 是指 SPI 通讯设备处于空闲状态时,SCK 信号线的电平信号(即 SPI 通讯开始前、 NSS 线为高电平时 SCK 的状态)。CPOL=0 时, SCK 在空闲状态时为低电平,CPOL=1 时,则相反。
时钟相位 CPHA 是指数据的采样的时刻,当 CPHA=0 时,MOSI 或 MISO 数据线上的信号将会在 SCK 时钟线的“奇数边沿”被采样。当 CPHA=1 时,数据线在 SCK 的“偶数边沿”采样。
核心板GPIO定义与设备连接流程图:
软件工作流程图:
4、代码:
SS1306
class SSD1306(framebuf.FrameBuffer):
def __init__(self, width, height, external_vcc):
self.width = width
self.height = height
self.external_vcc = external_vcc
self.pages = self.height // 8
self.buffer = bytearray(self.pages * self.width)
super().__init__(self.buffer, self.width, self.height, framebuf.MONO_VLSB)
self.init_display()
def init_display(self):
for cmd in (
SET_DISP, # display off
# address setting
SET_MEM_ADDR,
0x00, # horizontal
# resolution and layout
SET_DISP_START_LINE, # start at line 0
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.width > 2 * self.height 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
SET_IREF_SELECT,
0x30, # enable internal IREF during display on
# charge pump
SET_CHARGE_PUMP,
0x10 if self.external_vcc else 0x14,
SET_DISP | 0x01, # display on
): # on
self.write_cmd(cmd)
self.fill(0)
self.show()
def poweroff(self):
self.write_cmd(SET_DISP)
def poweron(self):
self.write_cmd(SET_DISP | 0x01)
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 rotate(self, rotate):
self.write_cmd(SET_COM_OUT_DIR | ((rotate & 1) << 3))
self.write_cmd(SET_SEG_REMAP | (rotate & 1))
def show(self):
x0 = 0
x1 = self.width - 1
if self.width != 128:
# narrow displays use centred columns
col_offset = (128 - self.width) // 2
x0 += col_offset
x1 += col_offset
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_data(self.buffer)
class SSD1306_I2C(SSD1306):
def __init__(self, width, height, i2c, addr=0x3C, external_vcc=False):
self.i2c = i2c
self.addr = addr
self.temp = bytearray(2)
self.write_list = [b"\x40", None] # Co=0, D/C#=1
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_data(self, buf):
self.write_list[1] = buf
self.i2c.writevto(self.addr, self.write_list)
class SSD1306_SPI(SSD1306):
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
import time
self.res(1)
time.sleep_ms(1)
self.res(0)
time.sleep_ms(10)
self.res(1)
super().__init__(width, height, external_vcc)
def write_cmd(self, cmd):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(0)
self.cs(0)
self.spi.write(bytearray([cmd]))
self.cs(1)
def write_data(self, buf):
self.spi.init(baudrate=self.rate, polarity=0, phase=0)
self.cs(1)
self.dc(1)
self.cs(0)
self.spi.write(buf)
self.cs(1)
电压采集:
Pot_pin = ADC(Pin(pin_cfg.pot))
CoVoltage = 3.3/(65535) #电压最高V3.3V
History = [52 for i in range(128)]
while True:
Voltage64 = int(Pot_pin. read_u16()* 52.0/65535.0)
Voltage = round(Pot_pin.read_u16() * CoVoltage,1) #ADC采集
for i in range(127):
History[i] = History[i + 1]
History[127] = Voltage64
oled.fill(0)
oled.line(25,64,25,0,1) #纵坐标
oled.text("0v",1,0) #横坐标
oled.hline(20,0,6,1)
oled.text("1v",1,10)
oled.hline(20,15,6,1)
oled.text("2v",1,26)
oled.hline(20,31,6,1)
oled.text("3v",1,43)
oled.hline(20,47,6,1)
oled.text("4v",1,57)
oled.hline(20,63,6,1)
for i in range(128):
if i>=26: #超了
oled.line(i - 1, History[i - 1], i, History[i], 1)
oled.text(str(Voltage)+ 'V',95,55)
oled.show()
sleep(0.5) #0.5s采集
实现效果:
总结:
通过本次实现电压表项目,从中学习到了如何使用树莓派Pico驱动OLED,并在屏上显示操作时的相关参数,从中需要对SPI的功能,SSD1306完全熟悉,还有电压是如何通过ADC采集的;非常的喜欢这个板子,后续我会继续探索此开发板把每个功能都熟悉,学会运用。