目录
- 前言
- 一、python环境搭建
- 1.1 用APT安装python
- 1.2 设置 python 和 pip 的默认版本
- 1.3 用 setuptools 工具安装软件包
- 1.4 Python语法基础使用
- 二、GPIO输入输出
- 2.1 安装控制接口包
- 2.2 libgpiod基本概念
- 2.2.1 实验准备
- 2.2.2 使用python3-libgpiod
- 2.3 使用python-periphery
- 2.4 使用 Adafruit Blinka
- 三、PWM 输出
- 3.1 实验准备
- 3.2 使用 python-periphery
- 3.3 使用 Adafruit Blinka
- 四、UART 通讯
- 4.1 添加UART资源
- 4.2 使用 pyserial
- 4.3 使用 python-periphery
- 五、I2C通讯
- 5.1 添加I2C资源
- 5.2 使用 python-periphery
- 5.3 使用Adafruit Blinka
- 六、SPI 通讯
- 6.1 添加 SPI 资源
- 6.2 使用 python-periphery
- 6.3 使用Adafruit Blinka
- 七、LCD 显示
- 7.1 环境搭建
- 7.1.1 添加 LCD 资源
- 7.1.2 安装Pygame库:
- 7.1.2 安装 fbset 工具
- 7.2 示例代码
- 八、CAN 总线通讯
- 8.1 python-can 库
- 8.2 python-can库安装
- 8.3 python-can 库使用
- 8.4 实验步骤
- 九、web开发
- 9.1 Web 库 - Flask
- 9.1.1 Flask 库安装
- 9.1.2 Flask库使用
- 9.1.3 运行webapp
- 9.2 Web 库 - Django
- 9.2.1 Django 库安装
- 9.2.2 Django库使用
- 9.2.3 启动 webapp
前言
主要介绍linux系统下python应用开发。本文提供一些基本的使用方法,具体可以去看Python源码,或者可到 github 或 pypi 查找。
一、python环境搭建
首先嵌入式系统得是Ubuntu
1.1 用APT安装python
sudo apt update #第一次使用apt需要更新
sudo apt -y install python3 #安装python3
sudo apt -y install python3-pip #安装pip工具
安装好可以用以下命令测试
python3 --version #查看 python 版本
pip3 --version # 查看 pip3 版本
1.2 设置 python 和 pip 的默认版本
sudo ln -s /usr/bin/python3 /usr/bin/python #设置软链接,python默认使用python3
sudo ln -s /usr/bin/pip3 /usr/bin/pip #设置软链接,pip默认使用pip3
注意:使用 pip 工具安装软件包时,通常是在本机中进行编译的,通常由于性能不够导致编译时间非常长,还可能因为缺少某些库文件而安装失败。议先直接搜索一下是否能使用 apt 工具安装,它会从软件库中下载预先编译好的软件包,安装时间基本只取决于网络速度。需要安装更新版本或者 apt 找不到的包时才使用 pip 安装。
1.3 用 setuptools 工具安装软件包
由于一些原因,我们只获取到了这些Python库包的源码。那么我们用 pip 工具或者 apt 工具就没有办法去安装了。这时我们可以使用 python 的 setuptools 工具,来通过库包源码进行 Python 库包的安装。
sudo apt -y install python3-setuptools #setuptools 工具安装
sudo python3 setup.py install #使用 setuptools 工具通过源码安装库
1.4 Python语法基础使用
代码编写好之后,在终端用如下命令运行:
python3 hello.py
二、GPIO输入输出
2.1 安装控制接口包
• python3-libgpiod:标准GPIO libgpiod 库的 python版本,只支持控制 IO 输入输出。
• python-periphery:支持GPIO、PWM、I2C、SPI、UART等多种接口的基础控制。
• Adafruit Blinka:支持GPIO、PWM、I2C、SPI、UART等,还带有一些常用传感器、OLED屏的应用示例。
2.2 libgpiod基本概念
2.2.1 实验准备
GPIO 主要用来对外输出高低电平,控制 GPIO 时,基本都会涉及到 libgpiod 的控制,我们主要需要知道板卡引脚的命名方式即可。
CPU 的 GPIO 引脚使用 (chip, line) 的方式命名,使用以下命令可以查看:
#在板卡上执行以下命令
gpioinfo
#若提示找不到命令,使用如下方式安装
sudo apt -y install gpiod libgpiod-dev
注意:
1)部分GPIO可能会被系统占用,在使用前请根据需要修改 /boot/uEnv.txt 文件,在对应资源中加上#注释,可以取消加载
2)如出现 Permission denied 或类似字样,请注意用户权限,大部分操作硬件外设的功能,几乎都需要 root 用户权限,简单的解决方案是在执行语句前加入 sudo 或以 root 用户运行程序。
2.2.2 使用python3-libgpiod
①安装python3-libgpiod
# 在板卡使用如下命令安装
sudo apt -y install python3-libgpiod
# 测试及查看帮助
python3
import gpiod
help(gpiod)
②libgpiod 输出blink.py
import time
import gpiod
# 根据具体板卡的 LED 灯连接修改使用的 Chip 和 Line
LED_LINE_OFFSET = 19
#创建了一个 chip ID 为 3 的 gpiod.Chip 对象 chip3
chip3 = gpiod.Chip("3", gpiod.Chip.OPEN_BY_NUMBER)
#设置使用 chip3 对象的 line19 作为 led
led = chip3.get_line(LED_LINE_OFFSET)
#设置 LED 的 GPIO 控制方向为输出
led.request(consumer="LED", type=gpiod.LINE_REQ_DIR_OUT, default_vals=[0])
print(led.consumer())
try:
while True:
led.set_value(1)
time.sleep(0.5)
led.set_value(0)
time.sleep(0.5)
finally:
led.set_value(1)
led.release()
2.3 使用python-periphery
①安装 python-periphery
sudo pip3 install python-periphery
②periphery 输入输出
"""Digital IO (Input/Output) using periphery"""
from periphery import GPIO
# 根据具体板卡的LED灯和按键连接修改使用的Chip和Line
LED_CHIP = "/dev/gpiochip3"
LED_LINE_OFFSET = 19
BUTTON_CHIP = "/dev/gpiochip4"
BUTTON_LINE_OFFSET = 1
#分别创建了 led 和 button 的 GPIO 输出、输入对象
led = GPIO(LED_CHIP, LED_LINE_OFFSET, "out")
button = GPIO(BUTTON_CHIP, BUTTON_LINE_OFFSET, "in")
try:
while True:
#直接使用按键的输入值控制 LED
led.write(button.read())
finally:
led.write(True)
led.close()
button.close()
2.4 使用 Adafruit Blinka
①安装 Adafruit Blinka
sudo apt -y install python3-libgpiod # 注:如在方法一中已安装,忽略此步
sudo pip3 install Adafruit-Blinka
②Adafruit-Blinka 输入输出
"""Digital IO (Input/Output) using Blinka"""
import board
import digitalio
# 根据具体板卡的LED灯和按键连接修改使用的GPIO
# LubanCat i.MX6ULL board, GPIO_PC32 = Pin 115
# 定义了 LED 使用的引脚 GPIO_PC32,并设置为输出方向
led = digitalio.DigitalInOut(board.GPIO_PC32)
led.direction = digitalio.Direction.OUTPUT
# LubanCat i.MX6ULL board, GPIO_PD17 = Pin 129
#定义了按键使用的引脚 GPIO_PD17,并设置为输入方向
button = digitalio.DigitalInOut(board.GPIO_PD17)
button.direction = digitalio.Direction.INPUT
try:
while True:
#直接使用按键的输入值控制 LED
led.value = button.value
finally:
led.value = True
led.deinit()
button.deinit()
三、PWM 输出
3.1 实验准备
在板卡上的部分资源可能默认未被开启,在使用前请根据需要修改 /boot/uEnv.txt 文件,可添加对应设备树插件的加载,重启系统,以在系统中添加对应资源。
如本节实验中,可能在系统中默认没有使能 PWM 的功能,所以要将 PWM 的功能开启。同时本例中使用 PWM 功能进行演示时,部分板卡会利用到 LED 灯占用的引脚,所以还要将对应的 LED 设备树插件取消加载,否则被占的引脚无法使用 libgpiod 等的方式进行控制。
# 在终端中输入如下命令,可以查看到 PWM 资源:
ls /sys/class/pwm/
3.2 使用 python-periphery
①为方便观察实验现象,建议先将其他两个 LDE 灯调整为常灭状态,复制如下命令
echo 0 > /sys/class/pwm/pwmchip7/export
echo 1000000 > /sys/class/pwm/pwmchip7/pwm0/period
echo "inversed" > /sys/class/pwm/pwmchip7/pwm0/polarity
echo 1 > /sys/class/pwm/pwmchip7/pwm0/enable
echo 0 > /sys/class/pwm/pwmchip7/unexport
②使用 python-periphery 库进行 PWM 输出
"""periphery库 PWM测试."""
import time
from periphery import PWM
# 打开 PWM 3, channel 0 ,对应开发板上PWM3外设
try:
pwm = PWM(2, 0)
# 设置PWM输出频率为 1 kHz
pwm.frequency = 1e3
# 设置占空比为 50%
pwm.duty_cycle = 0.50
# 开启PWM输出
pwm.enable()
while True:
for i in range(0, 9):
time.sleep(0.1)
pwm.duty_cycle += 0.05
for i in range(0, 9):
time.sleep(0.1)
pwm.duty_cycle -= 0.05
if pwm.duty_cycle == 0.0:
time.sleep(1)
finally:
pwm.close()
3.3 使用 Adafruit Blinka
import time
import board
import pulseio
#定义了 LED 使用的引脚为板上的 PWM 资源:PWM1,并设置输出的 PWM 频率为 5000,占空比为 0
led = pulseio.PWMOut(board.PWM1, frequency=5000, duty_cycle=0)
#通过 for 循环语句,调整 PWM 输出的占空比,实现 LED 灯的呼吸效果
while True:
for i in range(100):
# PWM LED up and down
if i < 50:
led.duty_cycle = int(i * 2 * 65535 / 100) # Up
else:
led.duty_cycle = 65535 - int((i - 50) * 2 * 65535 / 100) # Down
time.sleep(0.01)
四、UART 通讯
4.1 添加UART资源
在板卡上的部分资源可能默认未被开启,在使用前请根据需要修改 /boot/uEnv.txt 文件,可添加对应设备树插件的加载,重启系统,以在系统中添加对应资源。
# 在终端中输入如下命令,可以查看到 UART 资源:
ls /dev/ttymxc*
4.2 使用 pyserial
①安装 pyserial
sudo pip3 install pyserial
②示例代码
""" pyserial uart 测试 """
import serial
# 打开uart3,设置串口波特率为115200,数据位为8,无校验位,停止位为1,不使用流控制,以非阻塞模式打开串口,等待时间为1s
with serial.Serial(
"/dev/ttymxc2",
baudrate=115200,
bytesize=serial.EIGHTBITS,
stopbits=serial.STOPBITS_ONE,
parity=serial.PARITY_NONE,
timeout=1,
) as uart3:
# 使用申请的串口发送字节流数据 "Hello World!\n"
uart3.write(b"Hello World!\n")
# 以非阻塞的方式打开的串口,在读取串口接收的数据时,该函数返回条件二者满足其一,一、读取到128个字节,二、读取时间超过1秒
buf = uart3.read(128)
# 注:Python读取出来的数据类型为:bytes
# 打印原始数据
print("原始数据:\n", buf)
# 转码为gbk字符串,可以显示中文
data_strings = buf.decode("gbk")
# 打印读取的数据量及数据内容
print("读取到 {:d} 个字节 , 以字符串形式打印:\n {:s}".format(len(buf), data_strings))
4.3 使用 python-periphery
""" periphery uart 测试 """
from periphery import Serial
try:
# 申请串口资源/dev/ttymxc2,设置串口波特率为115200,数据位为8,无校验位,停止位为1,不使用流控制
serial = Serial(
"/dev/ttymxc2",
baudrate=115200,
databits=8,
parity="none",
stopbits=1,
xonxoff=False,
rtscts=False,
)
# 使用申请的串口发送字节流数据 "Hello World!\n"
serial.write(b"Hello World!\n")
# 读取串口接收的数据,该函数返回条件二者满足其一,一、读取到128个字节,二、读取时间超过1秒
buf = serial.read(128, 1)
# 注:Python读取出来的数据类型为:bytes
# 打印原始数据
print("原始数据:\n", buf)
# 转码为gbk字符串,可以显示中文
data_strings = buf.decode("gbk")
# 打印读取的数据量及数据内容
print("读取到 {:d} 个字节 , 以字符串形式打印:\n {:s}".format(len(buf), data_strings))
finally:
# 释放申请的串口资源
serial.close()
五、I2C通讯
5.1 添加I2C资源
在使用前请根据需要修改 /boot/uEnv.txt 文件,可添加对应设备树插件的加载,重启系统,以在系统中添加对应资源。
屏蔽内容如下
# 以具体板卡 I2C 设备树插件内容为例:
dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/imx-fire-i2c1.dtbo
dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/imx-fire-i2c2.dtbo
# 在终端中输入如下命令,可以查看到 I2C 资源:
ls /sys/bus/i2c/devices
5.2 使用 python-periphery
""" periphery i2c 测试 使用0.96寸OLED模块 """
import time
from periphery import I2C
# 打开 i2c-1 控制器
i2c = I2C("/dev/i2c-1")
# 设备从机地址0x3c,即OLED模块地址
I2CSLAVEADDR = 0x3C
# 使用periphery i2c库读取功能测试
def i2c_read_reg(devregaddr):
"""
使用periphery i2c库读取功能测试
"""
# 构造数据结构
# 第一个Message为要读取的设备地址
# 第二个Message用于存放读取回来的消息,注意其中添加的read=True
msgs = [I2C.Message([devregaddr]), I2C.Message([0x00], read=True)]
# 发送消息,发送到i2cSlaveAddr
i2c.transfer(I2CSLAVEADDR, msgs)
print("从寄存器 0x{:02x} 读取出: 0x{:02x}".format(devregaddr, msgs[1].data[0]))
def oled_write_cmd(cmd):
"""
使用periphery i2c库发送功能测试
"""
msgs = [I2C.Message([0x00, cmd])]
i2c.transfer(I2CSLAVEADDR, msgs)
def oled_write_data(data):
"""
使用periphery i2c库发送功能测试
"""
msgs = [I2C.Message([0x40, data])]
i2c.transfer(I2CSLAVEADDR, msgs)
def oled_init():
"""
使用OLED模块进行代码功能测试 0.96寸OLED模块初始化
"""
time.sleep(1)
oled_write_cmd(0xAE)
oled_write_cmd(0x20)
oled_write_cmd(0x10)
oled_write_cmd(0xB0)
oled_write_cmd(0xC8)
oled_write_cmd(0x00)
oled_write_cmd(0x10)
oled_write_cmd(0x40)
oled_write_cmd(0x81)
oled_write_cmd(0xFF)
oled_write_cmd(0xA1)
oled_write_cmd(0xA6)
oled_write_cmd(0xA8)
oled_write_cmd(0x3F)
oled_write_cmd(0xA4)
oled_write_cmd(0xD3)
oled_write_cmd(0x00)
oled_write_cmd(0xD5)
oled_write_cmd(0xF0)
oled_write_cmd(0xD9)
oled_write_cmd(0x22)
oled_write_cmd(0xDA)
oled_write_cmd(0x12)
oled_write_cmd(0xDB)
oled_write_cmd(0x20)
oled_write_cmd(0x8D)
oled_write_cmd(0x14)
oled_write_cmd(0xAF)
def oled_fill(filldata):
"""
清空OLED屏幕
"""
for i in range(8):
oled_write_cmd(0xB0 + i)
# page0-page1
oled_write_cmd(0x00)
# low column start address
oled_write_cmd(0x10)
# high column start address
for _ in range(128):
oled_write_data(filldata)
# 代码测试
try:
# 初始化OLED屏幕,SSD1306必要
oled_init()
# 清空OLED屏幕
oled_fill(0xFF)
# 读取寄存器测试
i2c_read_reg(0x10)
finally:
print("测试正常结束")
# 释放资源
i2c.close()
5.3 使用Adafruit Blinka
""" blinka i2c 测试 使用0.96寸OLED模块 """
# pylint: disable=W0401
from board import *
import busio
# 初始化OLED命令字节数组
OledInitBuf = bytes(
[
0xAE,
0x20,
0x10,
0xB0,
0xC8,
0x00,
0x10,
0x40,
0x81,
0xFF,
0xA1,
0xA6,
0xA8,
0x3F,
0xA4,
0xD3,
0x00,
0xD5,
0xF0,
0xD9,
0x22,
0xDA,
0x12,
0xDB,
0x20,
0x8D,
0x14,
0xAF,
]
)
# 数据发送、接受缓冲区
OutBuffer = bytearray(1)
InBuffer = bytearray(1)
try:
# 申请i2c资源
# pylint: disable=E0602
i2c = busio.I2C(SCL, SDA)
# 扫描I2C设备地址,测试
print("挂载I2C总线上的I2C设备地址有")
for i in i2c.scan():
print("0x%02x " % i)
# IIC发送命令测试(writeto),初始化OLED
# 初始化OLED,初始命令存放于字节数组oledInitBuf中
i2c.writeto(0x3C, OledInitBuf)
# IIC读取命令测试(writeto_then_readfrom),读取OLED的0x10寄存器
# 发送数据,发送数据完成后不产生停止信号,并重新生产起始信号进行读取。
# 发送数据内容存放在字节数组outBuffer中,内容为OLED寄存器地址:0x10
OutBuffer[0] = 0x10
# 发送,发送寄存器地址0x10,并将寄存器地址0x10下的内容读取到inBuffer中
i2c.writeto_then_readfrom(0x3C, OutBuffer, InBuffer)
# 打印数据信息
print("(writeto_then_readfrom)读取到的内容为:0x%02x" % InBuffer[0])
# IIC读取测试(readfrom_into),从设备内部的地址读,则不需要指定读取的地址
i2c.readfrom_into(0x3C, InBuffer)
# 打印数据信息
print("(readfrom_into)读取到的内容为:0x%02x" % InBuffer[0])
# OLED清屏
for i in range(8):
i2c.writeto(0x3C, bytearray([0x00, 0xB0 + i])) # page0-page1
i2c.writeto(0x3C, bytearray([0x00, 0x00])) # low column start address
i2c.writeto(0x3C, bytearray([0x00, 0x10])) # high column start address
for j in range(128):
i2c.writeto(0x3C, bytearray([0x40, 0xFF]))
finally:
# 释放i2c总线资源
i2c.deinit()
六、SPI 通讯
6.1 添加 SPI 资源
# 以板卡 SPI 设备树插件内容为例
dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/imx-fire-ecspi3.dtbo
# 在终端中输入如下命令,可以查看到 SPI 资源:
ls /dev/spi*
6.2 使用 python-periphery
""" periphery spi 测试 """
from periphery import SPI
# 待发送数据列表
data_out = [0xAA, 0xBB, 0xCC, 0xDD]
try:
# 申请SPI资源,打开 spidev2.0 控制器,配置SPI主机为工作模式0、工作速率为1MHz
spi = SPI("/dev/spidev2.0", 0, 1000000)
# 发送数据,同时接收数据到data_in列表中
data_in = spi.transfer(data_out)
# 打印发送的数据内容
print("发送的数据: [0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:02x}]".format(*data_out))
print("接收到的数据: [0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:02x}]".format(*data_in))
finally:
# 关闭申请的SPI资源
spi.close()
6.3 使用Adafruit Blinka
""" blinka spi 测试 """
import busio
# pylint: disable=W0401
from board import *
# 数据发送、接受缓冲区
OutBuffer = [0xAA, 0xBB, 0xCC, 0xDD]
InBuffer = bytearray(4)
try:
# 申请spi资源
# pylint: disable=E0602
spi = busio.SPI(SCLK, MOSI, MISO)
# 配置时先锁定SPI
spi.try_lock()
# 配置SPI主机工作速率为1MHz、时钟极性为0、时钟相位为0、单个数据位数为8位
spi.configure(1000000, 0, 0, 8)
# 配置操作完成解锁
spi.unlock()
# SPI通讯,发送的同时也进行读取,发送的数据存放在OutBuffer,读取的数据存放在InBuffer
spi.write_readinto(OutBuffer, InBuffer)
print(
"(write_readinto)接收到的数据: [0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:02x}]".format(
*InBuffer
)
)
# SPI通讯,只写使用方法演示
spi.write(OutBuffer)
print(
"(OutBuffer)发送的数据: [0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:02x}]".format(*OutBuffer)
)
InBuffer = [0, 0, 0, 0]
# SPI通讯,只读使用方法演示
spi.readinto(InBuffer)
print(
"(readinto)接收到的数据: [0x{:02x}, 0x{:02x}, 0x{:02x}, 0x{:02x}]".format(*InBuffer)
)
finally:
# 释放spi资源
spi.deinit()
七、LCD 显示
7.1 环境搭建
Pygame 是 Python 库中用于开发游戏的一种工具,或许这么说有点限制 Pygame 的用法。Pygame不仅可以用于游戏开发,还可以用于显示处理,多媒体设备处理等等方面,该库提供了许多多媒体、显示设备的操作功能。
7.1.1 添加 LCD 资源
屏蔽以下文件
# 以鲁班猫 i.MX6ULL MINI 板卡 LCD 设备树插件及触摸设备树插件内容为例:
dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/imx-fire-lcd.dtbo,clock-frequency_0=9000000,hactive_0=480,vactive_0=272,hfront-porch_0=8,hback-porch_0=2,hsync-len_0=41,vback-porch_0=4,vfront-porch_0=4,vsync-len_0=10,bits-per-pixel_0=24
dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/imx-fire-touch-capacitive-goodix.dtbo
# 在终端中输入如下命令,可以查看到显示设备资源:
ls /dev/fb*
7.1.2 安装Pygame库:
①使用 apt 工具安装
# 在终端中输入如下命令,安装 Pygame 库:
sudo apt -y install python3-pygame
②Pygame 库使用
可以使用以下命令不断地往 /dev/fb0写入随机数据,使得屏幕花屏
# 在终端中输入如下命令测试:
cat /dev/urandom > /dev/fb0
也可以通过 fbset 工具,在用户空间查看显示设备相关参数:
# 安装 fbset 工具
sudo apt update
sudo apt -y install fbset
7.1.2 安装 fbset 工具
sudo apt update
sudo apt -y install fbset
2)调用
import os
import time
import pygame
7.2 示例代码
""" 使用pygame进行屏幕测试 """
import os
import sys
import time
import pygame
class PyScope:
""" 定义一个PyScope类,进行屏幕测试 """
screen = None
def __init__(self):
"PyScope类的初始化方法,使用framebuffer构造pygame会使用到的图像缓冲区"
# Based on "Python GUI in Linux frame buffer"
# http://www.karoltomala.com/blog/?p=679
# 尝试获取环境目录下定义的显示设备
disp_no = os.getenv("DISPLAY")
if disp_no:
print("I'm running under X display = {0}".format(disp_no))
# 检查何种驱动方式可用
# 从fbcon开始,因为directfb会挂起复合输出
drivers = ["fbcon", "directfb", "svgalib"]
# 设置系统环境为无鼠标模式
os.environ["SDL_NOMOUSE"] = "1"
found = False
# 根据枚举的framebuffer设备类型适配驱动
for driver in drivers:
# 确保环境变量SDL_VIDEODRIVER被设置
if not os.getenv("SDL_VIDEODRIVER"):
os.putenv("SDL_VIDEODRIVER", driver)
try:
print("Driver: {0} is checking now...".format(driver))
# 尝试初始化显示设备功能
pygame.display.init()
except pygame.error:
# 出错,打印提示
print("Driver: {0} failed.".format(driver))
continue
print("Driver: {0} is suitable.".format(driver))
found = True
break
if not found:
raise Exception("No suitable video driver found!")
# 获取显示设备的大小
size = (pygame.display.Info().current_w, pygame.display.Info().current_h)
# 打印显示设备的大小
print("Framebuffer size: %d x %d" % (size[0], size[1]))
# 设置pygame使用的窗口显示为全屏幕
self.screen = pygame.display.set_mode(size, pygame.FULLSCREEN)
# 清屏
self.screen.fill((0, 0, 0))
# 初始化字体库
pygame.font.init()
# 更新屏幕,以显示写入缓冲区的内容
pygame.display.update()
def __del__(self):
"退出pygame库的时候会调用该方法,可以在此添加资源释放操作"
def test(self):
"PyScope类的测试方法,使屏幕填充为红色"
# 填充屏幕为红色,其rgb值为(255, 0, 0)
red = (255, 0, 0)
# 填充
self.screen.fill(red)
# 更新屏幕,以显示写入缓冲区的内容
pygame.display.update()
# 创建一个测试实例,开始测试
scope = PyScope()
# 调用scope类的测试方法
scope.test()
time.sleep(3)
sys.exit()
以上代码值得一提的是 init 方法中,会检查系统当前设备所处平台,并根据设备情况匹配驱动方式,最终该代码会在对应设备上全屏填充红色像素
八、CAN 总线通讯
8.1 python-can 库
python-can 是 Python 库下的一个库包,它实现了 CAN 总线通讯中许多的通讯操作,它对不同的硬件设备提供了通用的抽象接口,方便 Python 开发人员使用该库进行 CAN 通讯中的数据收发。
①添加CAN资源
# 以鲁班猫 i.MX6ULL Pro 板卡 CAN 设备树插件内容为例:
dtoverlay=/usr/lib/linux-image-4.19.35-imx6/overlays/imx-fire-can1.dtbo
②查看设备是否已经正常添加
# 在终端中输入如下命令,可以查看到 CAN 设备资源:
ifconfig -a
8.2 python-can库安装
使用 apt 工具安装
sudo apt -y install python3-can
8.3 python-can 库使用
""" python can 测试 """
import sys
import time
import threading
import can
def msg_recv(device_x):
"接收消息功能"
print("success: msg_recv Thread is running!")
# 将can_mask转换为二进制形式,can_mask中为1的位,用于过滤接收到的帧
# 举例 id: 0 0
# mask: 1 0 则接收到消息的ID中,mask为1对应id中的位,必须与id一致,为0
# 如接收到了四个id的消息 id1: 0 0 此条消息被放行
# id2: 0 1 此条消息被放行
# id3: 1 0 此条消息被过滤
# id4: 1 1 此条消息被过滤
# 过滤器配置示例如下。第一条规则,接收所有标准帧,第二条规则,接收拓展帧中id为0x300的消息。
can_filters = [
{"can_id": 1, "can_mask": 0x0, "extended": False},
{"can_id": 0x300, "can_mask": 0x1FFFFFFF, "extended": True},
]
# 应用过滤器配置
device_x.set_filters(can_filters)
# 查询退出线程是否退出,如果为真,则说明用户期望程序退出,退出本线程循环,线程结束
while tasks_quitThread.is_alive():
try:
# 接收can消息
msg = device_x.recv(1)
if msg is not None:
print("success: ", msg)
except can.CanError:
print("error: 接收消息时出错,请检查设备是否启用及状态正常")
def msg_send(device_x):
"发送消息功能"
print("success: msg_send Thread is running!")
# 构造发送的CAN消息结构,ID为0xC0FFEE,数据内容包含在data中,is_extended_id为拓展ID标识
msg = can.Message(
arbitration_id=0xC0FFEE, data=[0, 25, 0, 1, 3, 1, 4, 1], is_extended_id=True
)
# 查询退出线程是否退出,如果为真,则说明用户期望程序退出,退出本线程循环,线程结束
while tasks_quitThread.is_alive():
try:
# 发送构造的CAN消息
device_x.send(msg)
# 打印发送提示
print(f"success: 消息已发送至 {device_x.channel_info}")
except can.CanError:
print("error: 消息发送出错,请检查设备是否启用及状态正常!")
# 两秒后再次发送
time.sleep(2)
def tasks_quit():
"程序退出功能"
print("success: tasks_quit Thread is running!")
exitright = "e"
while exitright not in ["q", "Q"]:
# 获取用户输入,如果为q则退出程序
exitright = input(
"""
***********************************
**输入字母q后,按下回车以退出程序**
***********************************
"""
)
# 线程退出
# 打印运行程序前提示信息
print(
"information: 执行本程序前,请先启用can设备。命令如下:\
\nsudo ip link set can0 type can bitrate 1000000\nsudo ip link set can0 up"
)
# 打开CAN设备,CAN设备类型为socketcan,channel为can0,可使用ifconfig -a命令查看。
with can.interface.Bus(
bustype="socketcan", channel="can0", bitrate=1000000
) as device_can0:
# 创建线程:监听程序退出线程、发送can消息线程、接收can消息线程
try:
print("information: 开始创建 tasks_quitThread 线程!")
tasks_quitThread = threading.Thread(target=tasks_quit, daemon=True)
print("information: 开始创建 msg_sendThread 线程!")
msg_sendThread = threading.Thread(
target=msg_send, daemon=True, args=(device_can0,)
)
print("information: 开始创建 msg_recvThread 线程!")
msg_recvThread = threading.Thread(
target=msg_recv, daemon=True, args=(device_can0,)
)
# 开启线程
print("information: 开始启动 tasks_quitThread 线程!")
tasks_quitThread.start()
print("information: 开始启动 msg_sendThread 线程!")
msg_sendThread.start()
print("information: 开始启动 msg_recvThread 线程!")
msg_recvThread.start()
# pylint: disable=W0702
except:
print("error: 创建或启动线程中出错!")
sys.exit()
# 等待线程结束
tasks_quitThread.join()
print("information: tasks_quitThread结束")
msg_sendThread.join()
print("information: msg_sendThread结束")
msg_recvThread.join()
print("information: msg_recvThread结束")
# 所有正常线程结束,退出程序
sys.exit()
8.4 实验步骤
①输入如下命令来启用 CAN 总线设备
# 在终端中输入如下命令,可以启用 CAN 总线设备:
sudo ip link set can0 type can bitrate 1000000;sudo ip link set can0 up
启动后如下图所示:
②运行代码来查看一下现象
# 在终端中输入如下命令:
python3 canbus_test.py
九、web开发
9.1 Web 库 - Flask
Flask 是一个 Python 实现的 Web 开发微框架,我们可以在鲁班猫板卡上安装 Python-flask 库,并通过编写一些测试代码来使用该库。在我们的板卡上部署一个简单的 web 页面。
9.1.1 Flask 库安装
# 在终端中输入如下命令,安装 flask 库:
sudo apt -y install python3-flask
9.1.2 Flask库使用
①新建工程目录
# 新建一个目录,用于存放我们的 web 项目,并进入目录
mkdir webapp
cd webapp
# 创建我们 webapp 的目录
mkdir app
mkdir app/static
mkdir app/templates
mkdir tmp
②在app目录下,创建__init__.py文件,建一个简单的初始化脚本,内容如下:
from flask import Flask
app = Flask(__name__)
# pylint: disable=C0413
from app import views
③在app目录下,创建视图函数 views.py,
在 Flask 中,视图被编写成 Python 函数。每一个视图函数映射到一个或多个请求的 URL,让我们编写第一个视图函数 views.py
from app import app
@app.route("/")
@app.route("/index")
def index():
return "Hello, Lubancat!"
④在 webapp 目录下,创建run.py脚本,用于启动我们的应用程序的 Web 服务器
#!/usr/bin/python3
from app import app
app.run(debug=True, host="192.168.7.2")
⑤run.py 创建完成之后需要赋予一些权限
# 添加权限
chmod a+x run.py
9.1.3 运行webapp
①运行
# 运行 app
./run.py
②访问网址
app 已经正常启动,终端也已打印出了服务信息。信息中提示 web 已经映射到了我们在前面代码中设置的主机 192.168.7.2 端口号为 5000,故我们可以通过此网址访问 http://192.168.7.2:5000/ 我们部署好的网页
9.2 Web 库 - Django
Django 是 Python 编程语言驱动的一个开源模型-视图-控制器(MVC)风格的 Web 应用程序框架。也是 Web 开发的中主流的框架
9.2.1 Django 库安装
# 在终端中输入如下命令,安装 Django 库:
sudo apt -y install python3-django
9.2.2 Django库使用
①新建工程目录
# 新建一个目录,用于存放我们的 web 项目,并进入目录
mkdir webapp
cd webapp
②添加代码文件及配置
使用 Django 自带的管理器 django-admin,我们可以快速新建一个工程模板。
# 在终端中输入如下命令:
django-admin startproject Lubancat
# 待指令执行完毕后,当前目录下会自动生成项目文件夹:Lubancat,进入文件夹
cd ./Lubancat/
③接下来我们需要修改一下项目中的配置文件,以允许外部主机访问我们的鲁班猫板卡。进入到项目文件夹 Lubancat 中的 Lubancat 目录中,并修改 settings.py 文件。
# 在终端中输入如下命令:
cd ./Lubancat/
# 修改配置文件
nano settings.py
# 找到 ALLOWED_HOSTS 配置,并修改为如下内容,保存退出:
ALLOWED_HOSTS = ['*']
9.2.3 启动 webapp
①回到项目文件夹 Lubancat 中,运行其目录下的 manage.py 文件,启动服务器
# 在终端中输入如下命令:
python3 manage.py runserver 192.168.7.2:5001
webapp 已经正常启动,终端也已打印出了服务信息。信息中提示 web 已经映射到了我们在前面代码中设置的主机 192.168.7.2 端口号为 5001,故我们可以通过此网址访问 http://192.168.7.2:5001/我们部署好的网页。