1、进入树莓派环境

cd /home/jerry/Desktop/water/
source venv/bin/activate

2.安装库


sudo apt install python-pip 
sudo pip install RPi.GPIO
sudo apt install python-smbus2

3、进入示例

sudo apt-get install p7zip-full
wget http://www.waveshare.net/w/upload/6/6c/Servo_Driver_HAT.7z
7zr x Servo_Driver_HAT.7z -r -o./Servo_Driver_HAT
sudo chmod 777 -R Servo_Driver_HAT
cd Servo_Driver_HAT/Raspberry\ Pi/
cd python3/
sudo python3 PCA9685.py

0号舵机转动

#!/usr/bin/python

import time
import math
import smbus2 as smbus

# ============================================================================
# Raspi PCA9685 16-Channel PWM Servo Driver
# ============================================================================

# 定义一个类,用于控制PCA9685 PWM控制器
class PCA9685:

  # 定义各个寄存器地址(常量,以方便程序操作)
  __SUBADR1            = 0x02
  __SUBADR2            = 0x03
  __SUBADR3            = 0x04
  __MODE1              = 0x00
  __PRESCALE           = 0xFE
  __LED0_ON_L          = 0x06
  __LED0_ON_H          = 0x07
  __LED0_OFF_L         = 0x08
  __LED0_OFF_H         = 0x09
  __ALLLED_ON_L        = 0xFA
  __ALLLED_ON_H        = 0xFB
  __ALLLED_OFF_L       = 0xFC
  __ALLLED_OFF_H       = 0xFD

  def __init__(self, address=0x40, debug=False):
    # 初始化I2C总线(采用总线1)
    self.bus = smbus.SMBus(1)
    self.address = address  # 设置设备地址
    self.debug = debug      # 调试模式开关
    if (self.debug):
      print("Reseting PCA9685")
    # 将MODE1寄存器写入0,重置设备
    self.write(self.__MODE1, 0x00)
	
  def write(self, reg, value):
    """向指定寄存器写入一个8位值"""
    self.bus.write_byte_data(self.address, reg, value)
    if (self.debug):
      print("I2C: Write 0x%02X to register 0x%02X" % (value, reg))
	  
  def read(self, reg):
    """读取指定寄存器的值"""
    result = self.bus.read_byte_data(self.address, reg)
    if (self.debug):
      print("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" % (self.address, result & 0xFF, reg))
    return result
	
  def setPWMFreq(self, freq):
    """设置PWM频率"""
    # 计算预缩放值(预分频器)
    prescaleval = 25000000.0    # 25MHz的内部振荡频率
    prescaleval /= 4096.0       # 12-bit计数(4096)
    prescaleval /= float(freq)  # 除以目标频率
    prescaleval -= 1.0          # 计算预分频值
    if (self.debug):
      print("Setting PWM frequency to %d Hz" % freq)
      print("Estimated pre-scale: %d" % prescaleval)
    prescale = math.floor(prescaleval + 0.5)  # 四舍五入取整
    if (self.debug):
      print("Final pre-scale: %d" % prescale)

    # 读取MODE1寄存器的当前值
    oldmode = self.read(self.__MODE1)
    # 设置睡眠模式,避免更改预缩放寄存器时出现问题
    newmode = (oldmode & 0x7F) | 0x10  # 使睡眠位置位
    self.write(self.__MODE1, newmode)
    # 设定预缩放寄存器
    self.write(self.__PRESCALE, int(math.floor(prescale)))
    # 恢复之前的MODE1状态
    self.write(self.__MODE1, oldmode)
    time.sleep(0.005)  # 等待设备稳定
    # 使能自动刷新(重启)
    self.write(self.__MODE1, oldmode | 0x80)

  def setPWM(self, channel, on, off):
    """设置某个通道的PWM值(开启/关闭时间)"""
    # 设置该通道的开启(on)和关闭(off)时间点
    self.write(self.__LED0_ON_L + 4*channel, on & 0xFF)
    self.write(self.__LED0_ON_H + 4*channel, on >> 8)
    self.write(self.__LED0_OFF_L + 4*channel, off & 0xFF)
    self.write(self.__LED0_OFF_H + 4*channel, off >> 8)
    if (self.debug):
      print("channel: %d  LED_ON: %d LED_OFF: %d" % (channel, on, off))

  def setServoPulse(self, channel, pulse):
    """设置舵机的脉冲宽度,注意频率必须为50Hz"""
    # 将脉冲宽度转换为对应的PWM值(在4096的范围内)
    pulse = pulse * 4096 / 20000  # 20ms周期对应脉冲宽度
    self.setPWM(channel, 0, int(pulse))

# 主程序部分
if __name__=='__main__':
 
  # 创建PCA9685控制对象(地址0x40,调试关闭)
  pwm = PCA9685(0x40, debug=False)
  # 设置PWM频率为50Hz(常用于舵机)
  pwm.setPWMFreq(50)
  while True:
    # 逐步增加舵机脉冲宽度,从500微秒到2500微秒
    for i in range(500, 2500, 10):  
      pwm.setServoPulse(0, i)   # 通道0输出脉冲宽度i
      time.sleep(0.02)          # 等待20毫秒

    # 逐步减小脉冲宽度,从2500微秒到500微秒
    for i in range(2500, 500, -10):
      pwm.setServoPulse(0, i)   # 通道0输出脉冲宽度i
      time.sleep(0.02)          # 等待20毫秒

# -*- coding: utf-8 -*-

#!/usr/bin/python

import time
import math
import smbus2 as smbus

# ====================================================================
# Raspi PCA9685 16-Channel PWM Servo Driver
# ====================================================================

class PCA9685:

    # Register and address definitions
    __SUBADR1 = 0x02
    __SUBADR2 = 0x03
    __SUBADR3 = 0x04
    __MODE1 = 0x00
    __PRESCALE = 0xFE
    __LED0_ON_L = 0x06
    __LED0_ON_H = 0x07
    __LED0_OFF_L = 0x08
    __LED0_OFF_H = 0x09
    __ALLLED_ON_L = 0xFA
    __ALLLED_ON_H = 0xFB
    __ALLLED_OFF_L = 0xFC
    __ALLLED_OFF_H = 0xFD

    def __init__(self, address=0x40, debug=False):
        self.bus = smbus.SMBus(1)
        self.address = address
        self.debug = debug
        if self.debug:
            print("Resetting PCA9685")
        self.write(self.__MODE1, 0x00)

    def write(self, reg, value):
        """Write 8-bit value to register."""
        self.bus.write_byte_data(self.address, reg, value)
        if self.debug:
            print("I2C: Write 0x%02X to register 0x%02X" % (value, reg))

    def read(self, reg):
        """Read byte from register."""
        result = self.bus.read_byte_data(self.address, reg)
        if self.debug:
            print("I2C: Device 0x%02X returned 0x%02X from reg 0x%02X" % (self.address, result & 0xFF, reg))
        return result

    def setPWMFreq(self, freq):
        """Set PWM frequency."""
        prescaleval = 25000000.0
        prescaleval /= 4096.0
        prescaleval /= float(freq)
        prescaleval -= 1.0
        prescale = math.floor(prescaleval + 0.5)
        if self.debug:
            print("Setting PWM frequency to %d Hz" % freq)
            print("Estimated pre-scale: %d" % prescale)
        oldmode = self.read(self.__MODE1)
        newmode = (oldmode & 0x7F) | 0x10  # sleep
        self.write(self.__MODE1, newmode)
        self.write(self.__PRESCALE, int(prescale))
        self.write(self.__MODE1, oldmode)
        time.sleep(0.005)
        self.write(self.__MODE1, oldmode | 0x80)

    def setPWM(self, channel, on, off):
        """Set PWM for a channel."""
        self.write(self.__LED0_ON_L + 4 * channel, on & 0xFF)
        self.write(self.__LED0_ON_H + 4 * channel, on >> 8)
        self.write(self.__LED0_OFF_L + 4 * channel, off & 0xFF)
        self.write(self.__LED0_OFF_H + 4 * channel, off >> 8)
        if self.debug:
            print("Channel: %d  ON: %d  OFF: %d" % (channel, on, off))

    def setServoPulse(self, channel, pulse):
        """Set servo pulse width in microseconds."""
        pulse = pulse * 4096 / 20000  # 20 ms period
        self.setPWM(channel, 0, int(pulse))

if __name__ == '__main__':
    pwm = PCA9685(0x40, debug=False)
    pwm.setPWMFreq(50)  # Set frequency to 50Hz

    # Run for 30 seconds
    duration = 30  # seconds
    start_time = time.time()

    try:
        while True:
            elapsed = time.time() - start_time
            if elapsed > duration:
                break  # Stop after 30 seconds

            # Compute a smooth oscillation using sine wave
            t = elapsed / duration * math.pi
            pulse_width = 500 + (2000 / 2) * (1 + math.sin(t - math.pi/2))

            # Set pulse for channels 0 to 3 simultaneously
            for ch in range(4):
                pwm.setServoPulse(ch, pulse_width)

            time.sleep(0.02)
    except KeyboardInterrupt:
        pass  # Handle Ctrl+C gracefully

    # Reset all channels to middle position
    for ch in range(4):
        pwm.setServoPulse(ch, 1500)

    print("Motion complete!")



smbus2 库概述

  • 用途: 简化在Python中与I2C设备通信,例如传感器、舵机模块(如PCA9685)、温度传感器等。
  • 底层: 通过Linux的/dev/i2c-*设备文件(如/dev/i2c-1)实现硬件访问。
  • 支持: 支持基本的读写功能,还能实现块读写、I2C命令组合等高级功能。
  • 兼容性: 支持Python 2和Python 3。

主要功能和接口

1. 创建I2C连接

import smbus2

bus = smbus2.SMBus(1)  # 参数是I2C总线编号,常用的是0或1(一般树莓派使用1)

2. 读写基本操作

操作方法描述
写一个字节到设备寄存器write_byte_data(addr, reg, val)向设备addr的寄存器reg写入值val
从设备寄存器读取一个字节read_byte_data(addr, reg)从设备addr寄存器reg读取一个字节,返回值为整型
写一个字节数据到设备write_byte(val)只写一个字节到I2C设备,没有针对寄存器,适用于只需要一次性写入数据的设备
读取一个字节数据(无寄存器地址)read_byte()只读取设备中的一个字节,不指定寄存器

3. 块读写操作(连续数据)

  • 写块:
write_i2c_block_data(addr, reg, data_list)
  • 读块:
read_i2c_block_data(addr, reg, length)

这些允许一次传输连续的字节序列。

4. 特殊控制

  • 组合: i2c_msg 类允许复杂的I2C读写操作,支持多条消息的组合。
from smbus2 import SMBus, i2c_msg

# 例子:写入和读取复杂的操作

smbus2 相比py-smbus的优势

  • 更好的Python 3支持
  • 支持多条消息同时进行(i2c_msg)
  • 支持重试机制和高级控制
  • 兼容py-smbus但提供更强扩展性

简单示例

import smbus2

bus = smbus2.SMBus(1)  # 打开I2C总线1
address = 0x40        # 设备地址

# 写一个字节到寄存器
bus.write_byte_data(address, 0x00, 0xFF)

# 读一个字节
value = bus.read_byte_data(address, 0x00)

# 关闭总线(可选,自动处理)
bus.close()

结论

smub2是一个非常实用的Python库,用于在树莓派或Linux支持的硬件平台上与I2C设备进行通信。它封装了底层的I2C操作,具有丰富的接口,方便快速开发。

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐