写字机器人——pca9685控制芯片

"""
esp32是与电脑用数据线连接的芯片,esp32通过i2c将数据传给pca9685,pca9685控制舵机运动
因此,esp32是芯片,pca9685是舵机驱动器
"""

# 为简化可扩展性,标准Python模块的MicroPython版本通常有 u (micro)前缀。
# ustruct用于对数据按指定格式打包
import ustruct
import time

class PCA9685:
    def __init__(self, i2c, address=0x40):
        #PCA9685 是一个I2C 从设备,有个设备ID,或者叫从 地址。从地址是如下确定的:
        # A5-A0 缺省是开放的, 地址是0x40,所以address默认0x40
        self.i2c = i2c
        self.address = address
        self.reset()

    def _write(self, address, value):
        self.i2c.writeto_mem(self.address, address, bytearray([value]))

    def _read(self, address):
        return self.i2c.readfrom_mem(self.address, address, 1)[0]

    def reset(self):
        '''重置PCA9685-生成对象时默认调用'''
        self._write(0x00, 0x00) # Mode1
    def freq(self, freq=None):
        '''freq函数用于传入舵机频率后,计算pca9685内部的高低电平长短计量单位'''

    def pwm(self, index, on=None, off=None):
        """
        设置脉冲宽度,index为舵机对应的pca96856编号,on为0,off为目标值
        off = 307,舵机回中位
        off = 102,脉宽为0.5ms,普通180°舵机则转到0°
        off = 512,脉宽为2.5ms,普通180°舵机则转到180°
        """


    def duty(self, index, value=None, invert=False):
        '''对pwm函数的再一次封装,加入了反向计算转动'''

freq函数的实现

脉冲震荡时钟计算


```python
def freq(self, freq=None):
    '''设置脉冲频率,一般是50Hz'''
    if freq is None:
        return int(25000000.0 / 4096 / (self._read(0xfe) - 0.5))
    prescale = int(25000000.0 / 4096.0 / freq + 0.5)
    old_mode = self._read(0x00) # 读取Mode 1
    self._write(0x00, (old_mode & 0x7F) | 0x10) # Mode 1, sleep
    self._write(0xfe, prescale) # Prescale
    self._write(0x00, old_mode) # Mode 1
    time.sleep_us(5)  # 规定要求500us的时间,才能完成重置调整
    self._write(0x00, old_mode | 0xa1) # Mode 1, autoincrement on
     # 由于12bit是由8bit + 8bit组合成的,
     # 当第1个8bit满了之后,需要对mode1进行自增位的改变,进行第2个bit写入
  1. 一般舵机频率是50HZ,相当于1秒发送了50次脉冲
    而4096是pca9685的每次脉冲长度单位
  2. (12bit=>2**12=4096),相当于1次脉冲可以分为4096份,每份是1/4096
  3. 25MHZ是pca9685内部的时钟振荡器,相当于震荡25M次,表示1秒。那么50HZ则是50次脉冲,那么1次脉冲发生,时钟振荡器就会震荡 25000000HZ/50HZ次,
  4. 而1次脉冲是4096份组成,则每次震荡25000000/50次,那每份震荡25000000/50/4096次,四舍五入,可以是round(2500000/50/4096)【减1不知道是为啥】
  5. prescale的作用,就是计量脉宽的单位,例如on-off脉宽(亦称为pwm)
注:当舵机频率越高,意味着每次发出的脉冲时间更短,
则prescale会更小,不同的prescale计量出的脉宽是不同的

写字机器人——pca9685控制芯片_第1张图片

写字机器人——pca9685控制芯片_第2张图片
最后形成要设置频率prescale
在这里插入图片描述

写入寄存器的mode操作

写字机器人——pca9685控制芯片_第3张图片

1、而要将数据写入寄存器之间,需要先将第0位(0x00)寄存器的MODE1设置为0
写字机器人——pca9685控制芯片_第4张图片

首先0x7f相当于0111 1111,而与mode1的读取出来的8个二进制位bit,进行&与的位运算,即,只要同位为1,即为1,否则为0。因此&与运算结束后,只对mode1的8bit中的第0位进行修改为0,其余后7位&111 1111是不会发生改变的。

mode1的8bit:  xxxx  xxxx
0x7f的8bit:   0111  1111
最后mode1&0xf7f = 0xxx xxxx,mode1的第0个bit改为0

2、将第4位(0x10)寄存器的MODE1进行设置为1
mode1的8bit: xxxx xxxx
0x7f的8bit: 0111 1111
mode1&0xf7f = 0xxx xxxx,mode1的第0个bit改为0

0xxx xxxx|0x10 => 0xxx xxxx|0001 0000,这|位或运算,是两个都是0,才为0
0xxx xxxx
0001 0000
最后位或运算是对mode的第4位,设置为1,即最终mode为 0xx1 xxxx

其实就是将mode设置了第0位为0(all call),第4位为1(sleep),对应的mode1的含义如下图
写字机器人——pca9685控制芯片_第5张图片

pwm函数的实现

def pwm(self, index, on=None, off=None):
    '''设置脉冲宽度,index为舵机对应的pca96856编号,on为0,off为目标值'''
    if on is None or off is None:
        data = self.i2c.readfrom_mem(self.address, 0x06 + 4 * index, 4)
        return ustruct.unpack(', data)
    data = ustruct.pack(', on, off)
    self.i2c.writeto_mem(self.address, 0x06 + 4 * index,  data)

当起始寄存器位置on为空或是停止寄存器off为空,则读取第index个舵机的寄存器起始位置,并读取4个寄存器的数据,作为第index个舵机的所有数据
写字机器人——pca9685控制芯片_第6张图片

这里需要明确,pca9685总共有256个寄存器,每个寄存器是8bit,除了前6个寄存器是用来控制模式的,后边的寄存器大多用于控制LED灯(pca9685本来是设计用于控制LED等的,这里用来控制舵机,是个人做法)
而更需要注意的是,假设我需要pca9685控制4个舵机,则第1个舵机的位置,从0x06+40开始,到0x06+41结束,相当于1个舵机占用4个寄存器
第2个舵机的位置,从0x06+41的位置开始,到0x06+42结束,占用4个寄存器。

每个舵机为什么占用四个寄存器
由于存储是12bit的数据,因此需要2个8bit的寄存器分别作为ON的低位寄存器和高位寄存器(即LED0_ON_L和LED0_ON_H),并且要注意,是L在前,H在后

1. 如果数据是 0x7f=>0111 1111,其中0111是高位 1111是低位
2. 那么在寄存器中的存储顺序,应该是0x06存1111,0x07存0111
3. 映射到ustruct.unpack中的格式是"

写字机器人——pca9685控制芯片_第7张图片

duty函数的实现

def duty(self, index, value=None, invert=False):
    '''
    对pwm函数的再一次封装,加入了反向计算转动
    '''
    if value is None:
        pwm = self.pwm(index)
        if pwm == (0, 4096):
            value = 0
        elif pwm == (4096, 0):
            value = 4095
        value = pwm[1]
        if invert:
            value = 4095 - value
        return value
    if not 0 <= value <= 4095:
        raise ValueError("Out of range")
    if invert:
        value = 4095 - value
    if value == 0:
        self.pwm(index, 0, 4096)
    elif value == 4095:
        self.pwm(index, 4096, 0)
    else:
        self.pwm(index, 0, value)

value为None

当没给value传值时,会获取pca9685对应index舵机的原数据,而获取出来的pwm是以元组的方式,表示起始位置on和截止位置off的表示值。

*注意:在pca9685中,12bit的数据由8bit+8bit组合而成。*
当on数据为0,off的数据为4096时=>0000 0000 0010,
表示LED全灭,即数据值为空
当off的数据为0,on的数据为4096时=>0000 0000 0010,
表示LED灯全亮,即数据值4095

invert用于当舵机倒装时,角度值相反

value不为None

当传入的value不为None时,判断范围之外,依然要进行value为0及value为4095的全灭全亮操作

on为0,off为4096=>为全灭=>value为0
on为4096,off为0=>为全亮=>value为4095

写字机器人——pca9685控制芯片_第8张图片

你可能感兴趣的:(硬件机器人,单片机,python,嵌入式硬件)