Python控制树莓派硬件
Python控制树莓派硬件的方式比较多,也比较杂乱,这里会混合使用其中一部分。
平台: raspbian-bookworm-arm64。
1. 安装工具
$ sudo apt install python3-rpi.gpio python3-gpiozero python3-smbus python3-spidev python3-serial python3-picamera2 python3-libgpiod
使用之前,先打开相应外设,如i2c:
$ sudo raspi-config
打开i2c:
3 Interface Options --> I5 I2C --> Yes
打开即可,启用有些接口无需重启(如i2c,spi),有些需要重启(1-wire),如果需要重启一般也会有提示,如果没有提示,需注意自行甄别。
2. Blink
接线: LED --> GPIO 3.
控制GPIO 3闪烁LED:
from time import sleep from gpiozero import LED # GPIO3 引脚. led = LED(3) while True: led.on() sleep(1) led.off() sleep(1)
3. Button
接线: Button --> GPIO 17, LED --> GPIO 18.
按键控制LED:
from gpiozero import LED, Button from signal import pause def toggle_led(): led.value = not led.value print('button pressed.') led = LED(18) # GPIO18 控制 LED. button = Button(17, pull_up=True) # GPIO17 接按钮. button.when_pressed = toggle_led # 保持程序运行. pause()
4. Interrupt
接线: Button --> GPIO 17.
按键触发中断,打印字符串:
from gpiozero import Button from signal import pause def rising_edge(): print('Detected rising.') button = Button(17) button.when_activated = rising_edge pause()
5. PWM
接线: LED --> GPIO 12.
呼吸灯:
from time import sleep from gpiozero import PWMLED # 使用 GPIO12 引脚. led = PWMLED(12) # 渐亮渐灭效果. while True: for i in range(0, 101): led.value = i / 100 # 设置亮度(0.0 到 1.0). sleep(0.01) for i in range(100, -1, -1): led.value = i / 100 sleep(0.01)
接线: PWM --> GPIO 12.
控制风扇转速:
from time import sleep from gpiozero import PWMOutputDevice # GPIO12 支持硬件PWM. fan = PWMOutputDevice(12) # 模拟风扇转速从低到高. for speed in [0.0, 0.2, 0.4, 0.6, 0.8, 1.0]: fan.value = speed print(f'Fan speed: {speed * 100:.0f}%') sleep(2)
6. I2C
接线: SDA --> GPIO 2, SCL --> GPIO 3.
需在'raspi-config'中启用i2c。
读写AT24C02:
import time from smbus2 import SMBus, i2c_msg class AT24C02: def __init__(self, address=0x50, bus=1): self.bus = SMBus(bus) self.address = address def write_byte(self, mem_addr, data): self.bus.write_byte_data(self.address, mem_addr, data) time.sleep(0.01) def read_byte(self, mem_addr): return self.bus.read_byte_data(self.address, mem_addr) eeprom = AT24C02() eeprom.write_byte(0x00, 0xAB) print(f'Address 0x00 value: {hex(eeprom.read_byte(0x00))}')
7. SPI
接线: MOSI --> GPIO 10, MISO --> GPIO 9, CLK --> GPIO11, CS --> GPIO 8.
需在'raspi-config'中启用spi。
读写W25Q64 Flash(8 MiB):
import spidev import time class W25Q64: def __init__(self, spi_bus=0, spi_device=0, max_speed_hz=1000000): self.spi = spidev.SpiDev() self.spi.open(spi_bus, spi_device) self.spi.max_speed_hz = max_speed_hz self.spi.mode = 0b00 # SPI mode 0. def read_id(self): """ 读取设备ID """ cmd = [0x9F] # JEDEC ID命令 id_data = self.spi.xfer2(cmd + [0x00, 0x00, 0x00]) return (id_data[1] << 16) | (id_data[2] << 8) | id_data[3] def write_enable(self): """ 写使能 """ cmd = [0x06] # WREN self.spi.xfer2(cmd) def wait_busy(self): """ 等待设备就绪 """ while True: cmd = [0x05, 0x00] # 读状态寄存器1. status = self.spi.xfer2(cmd)[1] if not (status & 0x01): # 检查BUSY位. break time.sleep(0.01) def sector_erase(self, addr): """ 擦除4KB扇区 """ self.write_enable() cmd = [0x20, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF] self.spi.xfer2(cmd) self.wait_busy() def page_program(self, addr, data): """ 写入一页数据 (最多256字节) """ self.write_enable() cmd = [0x02, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF] self.spi.xfer2(cmd + data) self.wait_busy() def read_data(self, addr, length): """ 读取数据 """ cmd = [0x03, (addr >> 16) & 0xFF, (addr >> 8) & 0xFF, addr & 0xFF] return self.spi.xfer2(cmd + [0x00]*length)[4:] def close(self): """ 关闭SPI连接 """ self.spi.close() # 使用示例. if __name__ == "__main__": flash = W25Q64() try: # 验证设备ID (W25Q64的JEDEC ID应为0xEF4017). jedec_id = flash.read_id() print(f"JEDEC ID: {hex(jedec_id)}") # 测试参数设置. test_addr = 0x000000 # 测试地址 (确保该地址已擦除). test_data = list(b'hello world!') # 测试数据 (256字节). # 擦除扇区 (4KB对齐). print("擦除扇区...") flash.sector_erase(test_addr) # 写入数据. print("写入数据...") flash.page_program(test_addr, test_data) # 读取数据. print("读取数据...") read_back = flash.read_data(test_addr, len(test_data)) print(bytes(read_back).decode()) # 验证数据. if test_data == read_back: print("数据验证成功!") else: print("数据验证失败!") finally: flash.close()
8. UART
接线: TXD --> GPIO 15, RXD --> GPIO 14 (TX接RX, RX接TX).
需在'raspi-config'中启用uart。
然后关闭BT:
$ sudo systemctl disable hciuart $ sudo vim /boot/firmware/config.txt
在'config.txt'中添加如下内容(如果不存在的话):
enable_uart=1 dtoverlay=disable-bt
再然后,关闭串口控制台:
$ sudo vim /boot/firmware/cmdline.txt
修改如下:
--- console=serial0,115200 console=tty1 root=... +++ # console=serial0,115200 +++ console=tty1 root=...
重启:
$ sudo reboot
收发测试代码(使用usb转串口连接电脑,或者短接树莓派的tx和rx):
import serial from time import sleep uart = serial.Serial( port = '/dev/ttyAMA0', baudrate = 115200, parity = serial.PARITY_NONE, stopbits = serial.STOPBITS_ONE, bytesize = serial.EIGHTBITS, timeout = 1 ) try: while True: uart.write(b'hello world.\n') print('Send data.') data = uart.readline() if data: print('Receive data: ', data.decode('utf-8').strip()) sleep(2) except KeyboardInterrupt: uart.close() print('Uart closed.')
9. 1-Wire
接线: DS18B20 --> GPIO 4.
需在'raspi-config'中启用1-wire。
读取ds18b20的温度:
import os import glob import time class DS18B20: def __init__(self): self.device_folder = '/sys/bus/w1/devices/' self.device_prefix = '28-' self._init_devices() def _init_devices(self): # 加载内核模块 (备用方案). if not glob.glob(self.device_folder + self.device_prefix + '*'): os.system('sudo modprobe w1-gpio') os.system('sudo modprobe w1-therm') def find_devices(self): devices = glob.glob(self.device_folder + self.device_prefix + '*') return [device.split('/')[-1] for device in devices] def read_temp(self, device_id=None): if not device_id: devices = self.find_devices() if not devices: raise Exception("未检测到DS18B20设备") device_id = devices[0] device_file = os.path.join(self.device_folder, device_id, 'w1_slave') with open(device_file, 'r') as f: lines = f.readlines() if lines[0].strip()[-3:] != 'YES': time.sleep(0.2) return self.read_temp(device_id) # 重试读取. temp_pos = lines[1].find('t=') if temp_pos == -1: raise ValueError("温度数据格式错误") temp_raw = lines[1][temp_pos+2:] temp_c = float(temp_raw) / 1000.0 return temp_c if __name__ == "__main__": sensor = DS18B20() print(f"检测到设备:{sensor.find_devices()}") try: while True: temperature = sensor.read_temp() print(f"当前温度:{temperature:.2f}℃ ({time.strftime('%H:%M:%S')})") time.sleep(1) except KeyboardInterrupt: print("\n监测已终止")
10. Camera
接线: OV5647 --> CSI 0.
拍摄一张照片'test.jpg',并保存到当前位置:
from picamera2 import Picamera2 camera = Picamera2() config = camera.create_still_configuration() camera.configure(config) camera.start() camera.capture_file('test.jpg') camera.stop()
11. Thread
接线: LED --> GPIO 17.
多线程示例,主线程打印字符串,子线程闪烁LED:
import RPi.GPIO as GPIO import time import threading import signal import sys # 配置参数. LED_PIN = 17 # BCM编号. BLINK_INTERVAL = 0.5 # 默认闪烁间隔(秒). STOP_EVENT = threading.Event() class LedController(threading.Thread): def __init__(self, pin, interval): super().__init__() self.pin = pin self.interval = interval self.daemon = True # 设为守护线程,主程序退出自动终止. def run(self): GPIO.setmode(GPIO.BCM) GPIO.setup(self.pin, GPIO.OUT) try: while not STOP_EVENT.is_set(): GPIO.output(self.pin, GPIO.HIGH) time.sleep(self.interval) GPIO.output(self.pin, GPIO.LOW) time.sleep(self.interval) finally: GPIO.cleanup(self.pin) def signal_handler(sig, frame): print("\n终止程序...") STOP_EVENT.set() sys.exit(0) if __name__ == "__main__": signal.signal(signal.SIGINT, signal_handler) # 初始化线程. led_thread = LedController(LED_PIN, BLINK_INTERVAL) # 启动线程. led_thread.start() print("LED闪烁已启动,按Ctrl+C停止") # 主线程可执行其他任务. try: while True: # 示例:主线程每秒输出状态. print("主线程运行中...") time.sleep(1) except KeyboardInterrupt: signal_handler(None, None)
树莓派没有内建的ADC,所以上述不包含ADC示例,但可以通过外置ADC转换器使用ADC功能。
以上示例都是混合使用的,生态太混乱了,比较底层的就是RPI.GPIO,且RPI.GPIO还从2023年开始停止更新了,上层的gpiozero就是基于这个库做的,然后C版的WiringPi也停止更新了。
另外,还可以去看看adafruit-circuitpython,也可以控制树莓派3/4/5等硬件。

浙公网安备 33010602011771号