树莓派控制舵机_/Servo_Driver_HAT
·
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操作,具有丰富的接口,方便快速开发。
更多推荐
所有评论(0)