精华内容
下载资源
问答
  • 乐幻索尔总线舵机通信协议
  • 电信设备-有SPI接口的串行接口总线通信控制器.zip
  • 串口总线舵机之舵机命令

    千次阅读 2021-02-26 18:59:35
    驱动串口总线舵机 文章目录一、舵机指令包格式二、串口舵机连接1.硬件连接2.74HC126D三、关于ctypes四、串口舵机命令代码 从今天开始学习 幻尔科技总线舵机通信协议 一、舵机指令包格式 帧头: 连续收到两个 0x55 ...

    前言:
    关于一些前期配置,测试情况请观看下面这个博客
    驱动串口总线舵机


    从今天开始学习
    幻尔科技总线舵机通信协议
    关于这部分在SerialServoCmd文件里

    一、舵机指令包格式

    在这里插入图片描述

    帧头: 连续收到两个 0x55 ,表示有数据包到达。
    ID: 每个舵机都有一个 ID 号。ID 号范围 0~253,转换为十六进制 0x00~0xFD。广播 ID: ID 号 254(0xFE) 为广播 ID,若控制器发出的 ID 号为 254(0xFE),所有的舵机均接收指令,但都不返回应答信息,(读取舵机 ID 号除外,具体说明参见下面指令介绍)以防总线冲突。
    数据长度: 等于待发送的数据(包含本身一个字节)长度,即数据长度 Length加 3 等于这一包指令的长度,从帧头到校验和。
    指令: 控制舵机的各种指令,如位置、速度控制等。
    参数: 除指令外需要补充的控制信息。
    校验和: 校验和 Checksum,计算方法如下:
    Checksum = ~ (ID + Length + Cmd+ Prm1 + … Prm N)若括号内的计算和超出 255,则取最低的一个字节,“~”表示取反。

    说明:
    数据长度为 数据长度+指令+ 参数+校验和 参数个数 = 数据长度-3

    二、串口舵机连接

    1.硬件连接

    在这里插入图片描述

    2.74HC126D

    74HC126D介绍:
    功能描述:
    在这里插入图片描述
    可以看出,当OE输出高电平时 输入是高电平那么输出就是高电平,输入是低电平输出就是低电平。

    OE为低电平时不管输入状态是什么,输出都是高阻抗关断状态(抽象理解为悬空)

    高阻输出一般是指数字电路输出时不为高电平或低电平,而是相当于断开的一种状态,输出点的电位由后面的电路决定。

    这个芯片的作用就是,当需要写入的时候,拉低TX_CON,这样,串口TX发送什么,输出就是什么。拉高RX_CON,这样,串口接收RX就相当于悬空,什么也不干。接收数据也是如此.

    三、关于ctypes

    关于ctypes的介绍
    ctypes

    四、串口舵机命令代码

    # 串口舵机的命令
    #!/usr/bin/python3
    # encoding: utf-8
    import serial # 导入串口库
    import pigpio # 导入pigpio库  由c语言编写的库函数 并提供python接口
    import time # 导入时间库
    import ctypes # ctypes是Python的一个外部库,提供和C语言兼容的数据类型,可以很方便地调用C DLL中的函数。
    
    LOBOT_SERVO_FRAME_HEADER         = 0x55  # 机架头
    LOBOT_SERVO_MOVE_TIME_WRITE      = 1     # 移动时间写入
    LOBOT_SERVO_MOVE_TIME_READ       = 2     # 移动时间读
    LOBOT_SERVO_MOVE_TIME_WAIT_WRITE = 7     # 移动时间等待写
    LOBOT_SERVO_MOVE_TIME_WAIT_READ  = 8     # 移动时间等待读
    LOBOT_SERVO_MOVE_START           = 11    # 移动开始
    LOBOT_SERVO_MOVE_STOP            = 12    # 移动停止
    LOBOT_SERVO_ID_WRITE             = 13    # 舵机ID写
    LOBOT_SERVO_ID_READ              = 14    # 舵机ID读
    LOBOT_SERVO_ANGLE_OFFSET_ADJUST  = 17    # 角度偏移调整
    LOBOT_SERVO_ANGLE_OFFSET_WRITE   = 18    # 角度偏移写
    LOBOT_SERVO_ANGLE_OFFSET_READ    = 19    # 角度偏移读
    LOBOT_SERVO_ANGLE_LIMIT_WRITE    = 20    # 角度限制写
    LOBOT_SERVO_ANGLE_LIMIT_READ     = 21    # 角度限制读
    LOBOT_SERVO_VIN_LIMIT_WRITE      = 22    # VIN限制写
    LOBOT_SERVO_VIN_LIMIT_READ       = 23    # VIN限制读
    LOBOT_SERVO_TEMP_MAX_LIMIT_WRITE = 24    # TEMP最大限度写
    LOBOT_SERVO_TEMP_MAX_LIMIT_READ  = 25    # TEMP最大限度读
    LOBOT_SERVO_TEMP_READ            = 26    # TEMP读
    LOBOT_SERVO_VIN_READ             = 27    # VIN读
    LOBOT_SERVO_POS_READ             = 28    # POS读
    LOBOT_SERVO_OR_MOTOR_MODE_WRITE  = 29    # 模式写
    LOBOT_SERVO_OR_MOTOR_MODE_READ   = 30    # 模式读
    LOBOT_SERVO_LOAD_OR_UNLOAD_WRITE = 31    # 加载或卸载写
    LOBOT_SERVO_LOAD_OR_UNLOAD_READ  = 32    # 加载或卸载读
    LOBOT_SERVO_LED_CTRL_WRITE       = 33    # LED控制写
    LOBOT_SERVO_LED_CTRL_READ        = 34    # LED控制读
    LOBOT_SERVO_LED_ERROR_WRITE      = 35    # LED错误写
    LOBOT_SERVO_LED_ERROR_READ       = 36    # LED错误读
    
    pi = pigpio.pi()  # 初始化pigpio库,创建一个实例
    # ser=serial.Serial("/dev/ttyUSB0",9600,timeout=0.5) #使用USB连接串行口
    # ser=serial.Serial("/dev/ttyAMA0",9600,timeout=0.5) #使用树莓派的GPIO口连接串行口
    serialHandle = serial.Serial("/dev/ttyAMA0", 115200)  # 初始化串口, 波特率为115200
    
    
    # 使用pigpio配置驱动串口的引脚模式为输出
    def portInit():  # 配置用到的IO口
     # 说明  pigpio使用的是BCM编码  
        # RX_CON和TX_CON是使能信号  来决定TX还是RX输出servo signal
        pi.set_mode(17, pigpio.OUTPUT)  # 配置RX_CON 即 GPIO17 为输出
        pi.write(17, 0)# GPIO 17 低电平
        pi.set_mode(27, pigpio.OUTPUT)  # 配置TX_CON 即 GPIO27 为输出
        pi.write(27, 1) # GPIO 27 高电平
    
    
    portInit() # 执行端口初始化
    
    # 配置为串口写模式
    def portWrite():  # 配置单线串口为输出
        pi.write(27, 1)  # 拉高TX_CON 即 GPIO27  串口发送的是什么输出就是什么
        pi.write(17, 0)  # 拉低RX_CON 即 GPIO17  相当于悬空接受引脚,什么都不干
    
    # 配置为串口读模式
    def portRead():  # 配置单线串口为输入
        pi.write(17, 1)  # 拉高RX_CON 即 GPIO17
        pi.write(27, 0)  # 拉低TX_CON 即 GPIO27 悬空
    
    # 复位,重新打开串口
    def portRest(): # 端口复位
        time.sleep(0.1)   # 延迟100us
        serialHandle.close()# 关闭串口
        pi.write(17, 1)
        pi.write(27, 1)
        serialHandle.open() # 打开串口
        time.sleep(0.1)
    
    # 校验和 = ~(ID + Length+ Cmd + pr1+ .. + prn) 若超出255,则取最低的一个字节
    def checksum(buf):
        # 计算校验和
        sum = 0x00
        for b in buf:  # 求和
            sum += b # 累加
        sum = sum - 0x55 - 0x55  # 去掉命令开头的两个 0x55
        sum = ~sum  # 取反
        return sum & 0xff  # 取最低的一个字节
    
    # 串口舵机写命令
    # 指令包格式:0x55,0x55 ID号 数据长度,指令,参数1...参数n,校验和
    # 数据长度等于待发送的数据(包含本身1个字节)
    def serial_serro_wirte_cmd(id=None, w_cmd=None, dat1=None, dat2=None):
        portWrite()  # 端口写
            # bytearray() 方法返回一个新字节数组。这个数组里的元素是可变的,并且每个元素的值范围: 0 <= x < 256
        '''
        如果 source 为整数,则返回一个长度为 source 的初始化数组;
        如果 source 为字符串,则按照指定的 encoding 将字符串转换为字节序列;
        如果 source 为可迭代类型,则元素必须为[0 ,255] 中的整数;
        如果 source 为与 buffer 接口一致的对象,则此对象也可以被用于初始化 bytearray。
        如果没有输入任何参数,默认就是初始化数组为0个元素。
        '''
        # b'\x55\x55')    [0x55,0x55]
        buf = bytearray(b'\x55\x55')  # 帧头   buf = [0x55,0x55]
        buf.append(id) # buf = [0x55,0x55,id]
        # 指令长度
        if dat1 is None and dat2 is None:# dat1和dat2都为空
            buf.append(3) # buf = [0x55,0x55,id,3]  3个表示指令长度 指令 检验和
        elif dat1 is not None and dat2 is None: # dat1不为空dat2为空
            buf.append(4) # buf = [0x55,0x55,id,4] 4个表示指令长度,指令 dat1(低8位),校验和
        elif dat1 is not None and dat2 is not None: #dat1 和dat2都不为空
            buf.append(7)  # buf = [0x55,0x55,id,7] 7个表示指令长度,指令,dat1(高8位,低8位) dat2(高8位,低8位),校验和
    
        buf.append(w_cmd) # 把指令也添加到列表  # buf = [0x55,0x55,id,x,w_cmd]
        # 写数据
        if dat1 is None and dat2 is None: # dat1和dat2都为空
            pass # buf = [0x55,0x55,id,x,w_cmd]
        elif dat1 is not None and dat2 is None: # dat1不为空dat2为空
         # (dat1 & 0xff) 取最低位
            buf.append(dat1 & 0xff)  # buf = [0x55,0x55,id,x,w_cmd,dat1]
        elif dat1 is not None and dat2 is not None: # dat1 和 dat2 都不为空
            buf.extend([(0xff & dat1), (0xff & (dat1 >> 8))])  # 分低8位 高8位 放入缓存
            buf.extend([(0xff & dat2), (0xff & (dat2 >> 8))])  # 分低8位 高8位 放入缓存
        # 可能是buf = [0x55,0x55,id,x,w_cmd,dat1,dat2]
              
        # 校验和
        buf.append(checksum(buf)) # 到这数据有三种情况
        serialHandle.write(buf)  # 发送给串口
        
    # 串口舵机读命令 先发送读命令 再接收,不单独使用
    def serial_servo_read_cmd(id=None, r_cmd=None):
        portWrite() # 端口写
        buf = bytearray(b'\x55\x55')  # 帧头
        buf.append(id)# 添加舵机ID到列表
        buf.append(3)  # 指令长度
        buf.append(r_cmd)  # 指令
        buf.append(checksum(buf))  # 校验和
        serialHandle.write(buf)  # 发送
        time.sleep(0.00034) # 延迟 3.4us
    
    # 获取指定读取命令的数据
    def serial_servo_get_rmsg(cmd):
        serialHandle.flushInput()  # 清空接收缓存
        portRead()  # 将单线串口配置为输入
        time.sleep(0.005)  # 稍作延时,等待接收完毕
        count = serialHandle.inWaiting()    # 获取接收缓存中的字节数
        if count != 0:  # 如果接收到的数据不空
            recv_data = serialHandle.read(count) # 读取count字节个数据
            try:
            # recv_data[2] : id   recv_data[3]: 数据长度 
                if recv_data[0] == 0x55 and recv_data[1] == 0x55 and recv_data[4] == cmd: # 帧头正确  命令符合 匹配
                    dat_len = recv_data[3]  # 数据长度
                    serialHandle.flushInput()  # 清空接收缓存
                    # 数据长度为 数据长度+指令+ 参数+校验和  参数个数 = 数据长度-3
                    if dat_len == 4: # dat1为一个8位的参数dat2为空
                        # print ctypes.c_int8(ord(recv_data[5])).value    # 转换成有符号整型
                        return recv_data[5] # 返回这个8位的参数
                    elif dat_len == 5:  # dat1为一个16位的参数dat2为空
                        pos = 0xffff & (recv_data[5] | (0xff00 & (recv_data[6] << 8))) # 一个16位的数据
                        return ctypes.c_int16(pos).value
                    elif dat_len == 7: # dat1位16位的参数,dat2为16位的参数
                        pos1 = 0xffff & (recv_data[5] | (0xff00 & (recv_data[6] << 8)))
                        pos2 = 0xffff & (recv_data[7] | (0xff00 & (recv_data[8] << 8)))
                        return ctypes.c_int16(pos1).value, ctypes.c_int16(pos2).value
                else:  # 数据不正确,不符合接收数据的格式
                    return None
            except BaseException as e:
                print(e) # 打印异常
        else:  # 接收数据为空
            serialHandle.flushInput()  # 清空接收缓存
            return None
    
    
    展开全文
  • 行业分类-设备装置-可读写的串行接口总线通信控制器
  • 行业分类-设备装置-有SPI接口的可读写串行接口总线通信控制器
  • 串口总线舵机之舵机运行

    千次阅读 2021-02-26 19:20:38
    我们总是需要让舵机去转动一定的角度,执行对应的动作。 文章目录前提:1....# 串口舵机运行 动作组调用 #!/usr/bin/env python3 # encoding: utf-8 import time # 时间模块 import os # 系统OS i

    我们总是需要让舵机去转动一定的角度,执行对应的动作。

    这一部分在Serial_Servo_Running文件里

    前提:

    因为这一部分需要运行动作组,所以需要动作文件和支持文件

    1.动作文件

    在这里插入图片描述

    2.支持文件

    需要将下面的文件拷贝到当前目录
    在这里插入图片描述

    一、舵机运行代码

    # 串口舵机运行 动作组调用
    
    #!/usr/bin/env python3
    # encoding: utf-8
    import time # 时间模块
    import os  # 系统OS
    import sqlite3 as sql  # 数据库
    import SerialServoCmd as ssc # 串口舵机命令
    import config_serial_servo # 配置串口舵机
    import threading  # 线程
    from hwax import HWAX # 未知 驱动动作组的 不可缺少,文件夹有一个HWAX.so文件
    
    runningAction = False   # 运行动作组不成立
    stopRunning = False  # 停止运行不成立
    online_action_num = None   # 动作组编号为空
    online_action_times = -1 # 执行时间为空载
    update_ok = False # 更新动作组不成立
    action_group_finish = True # 动作组完成成立
    
    
    # 设置串口舵机的位置和时间  
    # 时间参数意义:舵机在参数时间内从当前角度匀速转动到参数角度
    # s_id:要设置的舵机  pos:0-1000 对应角度 0-240度 s_time:时间0-30000ms  0-30s
    def serial_setServo(s_id, pos, s_time):
        if pos > 1000:  # 最大位置是1000 对应角度0-240
            pos = 1000
        elif pos < 0:
            pos = 0
        else:
            pass
        if s_time > 30000:  # 最大时间是30000ms = 30s
            s_time = 30000
        elif s_time < 10:  # 最小时间是10ms
            s_time = 10
        ssc.serial_serro_wirte_cmd(s_id, ssc.LOBOT_SERVO_MOVE_TIME_WRITE, pos, s_time)
        
    # 设置舵机偏差值
    # servoId:要设置的舵机  d:偏差值
    def setDeviation(servoId, d):
        global runningAction  # 全局变量
        if servoId < 1 or servoId > 16:  # 舵机号的范围 1-16个舵机
            return
        if d < -200 or d > 200: # 偏差值的范围
            return
        if runningAction is False: # 如果当前不在运行状态就设置偏差值
            config_serial_servo.serial_servo_set_deviation(servoId, d)
    
    # 停止全部舵机
    def stop_servo():
        for i in range(16): 
            config_serial_servo.serial_servo_stop(i+1)  # 依次让每一个舵机都停止
            
    # 停止动作组
    def stop_action_group():
        global stopRunning, online_action_num, online_action_times, update_ok
        update_ok = False  # 更新ok不成立
        stopRunning = True  # 停止运行成立
        online_action_num = None # 动作组的编号为空
        online_action_times = -1 # 动作组的次数为空载状态
        time.sleep(0.1) # 延时100ms
        
    # 获取动作完成标志
    def action_finish():
        global action_group_finish 
        return action_group_finish #获取动作组完成标志
    
    # 运行动作组,无法发送停止信号
    # actNum:动作组的名字,字符串类型
    def runAction(actNum):
        global runningAction  # 运行动作组
        global stopRunning  # 停止运行
        global online_action_times # 运行动作组次数
        if actNum is None: # 动作名字为空
            return
        # 判断动作组的类型
        hwaxNum = "/home/pi/Desktop/Serial_SteerEngine/ActionGroups/" + actNum + ".hwax" # 获取动作组的路径
        actNum = "/home/pi/Desktop/Serial_SteerEngine/ActionGroups/" + actNum + ".d6a"
    
        if os.path.exists(hwaxNum) is True:  # hwax路径存在
            if runningAction is False: # 如果当前舵机不在运行状态 
                runningAction = True  # 设置舵机正在运行
                ssc.portWrite()  # 端口复位
                hwax = HWAX(hwaxNum, ssc.serialHandle)
                hwax.reset()
                while True:
                    if stopRunning is True: # 如果是停止运行
                        stopRunning = False
                        print('stop')    # 输入提示信息               
                        break
                    ret = hwax.next()
                    if ret is None:
                        hwax.reset()
                        break
                hwax.close() # 关闭这个文件
                runningAction = False
    
        elif os.path.exists(actNum) is True: # d6a路径存在
            if runningAction is False:
                runningAction = True
                ag = sql.connect(actNum) # 连接数据库
                # 游标就像您打开文件所得到的文件句柄一样,只要文件打开成功, 
                #该文件句柄就可代表该文件
                cu = ag.cursor() # 获取数据库可操作对象
                '''
                使用Cursor对象执行insert,update,delete语句时,执行结果由rowcount返回影响的行数,就可以拿到执行结果
                使用Cursor对象执行select语句时,通过featchall()返回select的全部数据结果集。结果集是一个list,
                每个元素都是一个tuple,对应一行记录,按建表的字段顺序排列。
                fetchone()返回一条结果,是一个tuple,每个元素是一个字段值。
                '''
                cu.execute("select * from ActionGroup") # 从ActionGroup查找全部数据
                while True:
                    act = cu.fetchone() # 获取一条查询的结果
                    if stopRunning is True: # 如果是停止运行
                        stopRunning = False 
                        print('stop')      # 输入提示信息               
                        break
                    if act is not None: # 查询到数据
                        # 设置每一个舵机的act[2 + i]位置act[1]运行时间
                        for i in range(0, len(act)-2, 1): # len(act)-2 : 第一个是 第二个是运行时间
                            serial_setServo(i+1, act[2 + i], act[1]) # ID 位置 时间
                        time.sleep(float(act[1])/1000.0) # 转换为秒
                    else:   #  没有查找到数据
                        break
                runningAction = False  # 运行状态停止
                
                cu.close() # 关闭游标
                ag.close() # 关闭数据库连接
        else: # 路径不存在
            runningAction = False
            print("未能找到动作组文件")
            
    # 线程运行动作
    def online_thread_run_acting():
        global online_action_times, online_action_num, update_ok, action_group_finish
        while True:  # 在线程里  死循环
            if update_ok: # 更新ok 就是说动作组有变化
                if online_action_times == 0: # 如果在线动作次数等于0,执行无限次
                    # 无限次运行
                    if action_group_finish: # 动作还在执行中,不允许动作发生改变
                        action_group_finish = False
                    runAction(online_action_num)   # 运行  online_action_num 动作            
                elif online_action_times > 0: # 如果在线动作次数大于0,执行online_action_times次
                    # 有次数运行
                    if action_group_finish: # 动作还在执行中,不允许动作发生改变
                        action_group_finish = False
                    runAction(online_action_num) # 运行  online_action_num 动作  
                    online_action_times -= 1     # 运行次数减一次             
                    if online_action_times == 0:
                        online_action_times = -1  # 运行完成后,进入空载 
                else:
                    # 空载
                    if not action_group_finish: # 如果动作组没有完成
                        action_group_finish = True # 动作组完成
                    time.sleep(0.001) # 延时1ms
            else: # 更新不ok 没有动作发生变化
            # 查看上一次动作是否完成
                if not action_group_finish:  # 如果动作组没有完成
                    action_group_finish = True # 动作组完成
                time.sleep(0.001) # 延时1ms
    
    # 开始动作线程            
    def start_action_thread():
        th1 = threading.Thread(target=online_thread_run_acting) # 运行动作组
        th1.setDaemon(True)  # 设置为后台线程,这里默认是True
        th1.start()
        
    # 改变动作组的值
    # actNum:动作名 actTimes:运行次数
    def change_action_value(actNum, actTimes):
        global online_action_times, online_action_num, update_ok, stopRunning, action_group_finish
        # 动作组都完成了之后才能允许动作发生改变
        if action_group_finish: # 如果动作组完成了
            online_action_times = actTimes # 动作执行的次数 
            online_action_num = actNum # 执行动作组的编号
            stopRunning = False  # 停止运行不成立
            update_ok = True # 更新ok成立
    
    # 单独执行的时候运行
    if __name__ == '__main__':
        # 开启动作组线程,update_ok =True 就运行动作
        start_action_thread() # 开启一个动作组的线程
        # 动作一改变 update_ok =True 就会运行动作
        change_action_value('1', 0) # 改变动作   运行动作1,执行无限次
        time.sleep(2) # 延时2s
        stop_action_group() # 停止动作组
    

    二、测试

    在这里插入图片描述
    舵机会执行动作,2s后停止。

    展开全文
  • SPI(Serial Peripheral Interface--串行外设接口总线系统是一种同步串行外设接口,它可以使MCU与各种外围设备以串行方式进行通信以交换信息。
  • 阐述了串口总线电路的防护设计方法,主要对防止串口总线电路吊死的逻辑设计原理进行了较详细的阐述,分析了现有的串口总线电路设计中存在的缺陷,并提出一种通过逻辑控制来实现的串口防护设计方案。该方案实现简单、...
  • 树莓派之驱动串口总线舵机

    千次阅读 2021-02-25 21:21:32
    在我们做机器人项目的时候,不可避免的要和总线舵机打交道,今天就介绍怎么去测试总线舵机。 前言 驱动舵机之前需要先去安装pigpio库和配置串口 一、安装pigpio库 二、配置串口 一、硬件连接 二、编写程序 #!/usr/...


    在我们做机器人项目的时候,不可避免的要和总线舵机打交道,今天就介绍怎么去测试总线舵机。

    前言

    驱动舵机之前需要先去安装pigpio库和配置串口
    一、安装pigpio库
    二、配置串口

    一、硬件连接

    在这里插入图片描述

    二、编写程序

    #!/usr/bin/python3
    
    import serial
    import pigpio
    import time
    
    pi = pigpio.pi()  #初始化 pigpio库
    serialHandle = serial.Serial("/dev/ttyAMA0", 115200)  #初始化串口, 波特率为115200
    
    
    ##
    ##命令发送
    ##
    def servoWriteCmd(id, cmd, par1 = None, par2 = None):
        buf = bytearray(b'\x55\x55')
        try:
            len = 3   #若命令是没有参数的话数据长度就是3
            buf1 = bytearray(b'')
    
    	## 对参数进行处理
            if par1 is not None:
                len += 2  #数据长度加2
                buf1.extend([(0xff & par1), (0xff & (par1 >> 8))])  #分低8位 高8位 放入缓存
            if par2 is not None:
                len += 2
                buf1.extend([(0xff & par2), (0xff & (par2 >> 8))])  #分低8位 高8位 放入缓存
            buf.extend([(0xff & id), (0xff & len), (0xff & cmd)])
            buf.extend(buf1) #追加参数
    
    	##计算校验和
            sum = 0x00
            for b in buf:  #求和
                sum += b
            sum = sum - 0x55 - 0x55  #去掉命令开头的两个 0x55
            sum = ~sum  #取反
            buf.append(0xff & sum)  #取低8位追加进缓存
            serialHandle.write(buf) #发送
        except Exception as e:
            print(e)
    
    def portInit(): #配置用到的IO口
        pi.set_mode(17, pigpio.OUTPUT)  #配置RX_CON 即 GPIO17 为输出
        pi.write(17, 0)
        pi.set_mode(27, pigpio.OUTPUT)  #配置TX_CON 即 GPIO27 为输出
        pi.write(27, 1)
    
    def portWrite():  #配置单线串口为输出
        pi.write(27, 1)  #拉高TX_CON 即 GPIO27
        pi.write(17, 0)  #拉低RX_CON 即 GPIO17
        
    
    def portRead():   #配置单线串口为输入
        pi.write(27, 0) #拉低TX_CON 即 GPIO27
        pi.write(17, 1) #拉高RX_CON 即 GPIO17
    
    
    portInit()
    while True:
        try:
            portWrite() #将单线串口配置为输出
            servoWriteCmd(1,1,0,1000) #发送命令 参数1 舵机id=1, 参数2 命令 = 1, 参数3 位置 = 0, 参数4 时间 = 1000ms
            time.sleep(1.1)
            servoWriteCmd(1,1,1000,2000)
            time.sleep(2.1)
        except Exception as e:
            print(e)
            break
    
    

    需要修改为对应的舵机的ID,这里使用的是1,舵机出厂默认是1

    三、测试

    直接在树莓派运行这个程序,发现舵机会转动
    在这里插入图片描述
    测试成功。

    展开全文
  • 单片机I2C串口总线扩展设计说明.doc
  • 通用串行总线接口USB

    2020-08-26 22:35:23
    由于多媒体技术的发展对外设与主机之间的数据传输率有了更高的需求,因此,USB总线技术应运而生。
  • 行业分类-物理装置-多通道HDLC同步串口总线设备数据通信装置和方法.zip
  • 摘要多个单片机通过串行总线串行接口(I2C、SPI)存储芯片可以构成简捷、高效的多主机系统。多主机系统需要解决好共用串行总线的仲裁问题,本文给出利用优先编码器74HCl48和译码器74HCl38实现硬件仲裁的解决方法。与...
  • 串行外设接口 (SPI) 总线是一个工作在全双工模式下的同步串行数据链路。它可用于在单个主控制器和一个或多个从设备之间交换数据。其简单的实施方案只使用四条支持数据与控制的信号线(图 1):  虽然表1中的引脚...
  • 串行总线技术(一)-串行总线结构(以PCIe为例)串行总线的出现在早期的计算机系统中,多数外围设备使用并行总线结构。这些总线包括PCI和PATA(并行ATA)。当通信速率较低时,并行总线结...

    3bb40fa785e836bab1494a6103cfce2b.png

    串行总线技术(一)-串行总线结构(以PCIe为例)

    串行总线的出现

    在早期的计算机系统中,多数外围设备使用并行总线结构。这些总线包括PCI和PATA(并行ATA)。当通信速率较低时,并行总线结构可以设计得非常简单和有效,可以连接大量外围设备。通过使用中央仲裁机制,可以方便地实现总线设备间的通信:然而,当速率和带宽不断增加时,并行结构的潜力不断被发掘并不再能够满足系统设计要求。

    并行总线结构的带宽可以通过增加总线宽度或者提高总线的工作频率来实现,但这种增加带宽的方式会逐渐变得困难。并行总线会占用很多引脚,而对现代数字芯片来说,单一芯片中集成了大量的功能,引脚本身就是一种非常紧张的资源,这为继续增加总线宽度带来了困难。

    另外,总线频率已经进行了多次增加(如PCI-->PCIX),继续对大量信号线提高工作频率也变得更加困难。除此之外,并行结构还有一些固有不足,如没有包的概念,没有错误检验机制等。在并行总线中,传输的是数据突发片段,不是完整的数据包,也没有与所传输数裾 相关的CRC校验结果以进行差错控制。并行总线的不足促进串行总线结构的发展,这不仅克服了原有的缺陷,还带来了其他好处。如图所示为并行总线与串行总线示例。

    447b79565efe60fd75ab5f99374ac1e2.png 538966c2d406f5b4c3040a8ae4c835df.png

    现在的数字系统中有很多种串行总线。PCI Express(代替了并行PCI总线)、SATA(代替PATA)以及USB等就是一些常用的高速总线。这些总线的速率也从MHz达到了GHz。

    串行总线的优缺点

    优点

    • 占用引脚数量少

    串行总线使用一对信号线发送数据(TX+ TX-),使用一对信号线接收数据(RX+ RX-)。而PCI和PCIX等并行总线会占用大量引脚。

    • 差分信号

    TX和RX信号线上采用的是差分信号传输方式,这种传输方式具有很高的抗噪声能力。

    • 强错误检测能力

    串行协议使用基干包的数据传输方式,对数据包采用了CRC校验。相比于PCI中采用的奇偶校验,CRC校验具有很强的检错能力。

    • 纠错与恢复

    因为采用基于包的传输方式并且带有CRC校验,接收设备能够检测出接收数据包中的错误并通知发送端出现了传输错误,发送端可以重新发送出现错误的数据包。

    • 全双工数据和控制流

    数据包可以在TX信号线上传输,同时可以在RX信号线上接收控制信息。SATA使用类似HOLD的流控原语来实时阻止数据发送以避免数据溢出。

    • 分割式数据传输

    在分割式数据传输中,request和completion可以不出现在同一个数据传输操作中。这不是串行总线所特有的必要特征。所有的串行总线都使用包和分割式数据传输协议进行数据传输。多数早期的并行总线(PCI、AHB)不支持分割式数据传输。最新的并行总线,如AXI 开始支持这一协议。

    缺点

    串行总线也存在一些不利之处。

    • 只支持点到点连接

    通过串行总线只能连接两个设备,而对于并行总线,可以在单一总线上连接多个设备,并目.很容易增减总线上的设备。对于串行总线,我们需要使用交换机和多个总线设备连接。

    • 更大的延迟

    由于串行及分层结构特点,串行结构的总线延迟更大一些。

    串行总线结构

    串行总线结构只允许点到点连接,一条串行总线只能连接两个设备,而并行总线可以同吋连接多个设备。在串行总线结构中,总线设备中需要包括两个基本电路部件:MAC控制器(通常简称为控制器)和PHY(主要实现模拟收发功能)。MAC具有分层结构,通常包括三个 层次。PHY包括两个部分:PCS和PMA。PCS(Physical Coding Sublayer,物理编码子层)主要实现编解码等数字逻辑功能。PMA主要实现时钟恢复、均衡和信号电平检测等模拟功能。

    下面我们将以PCIe和SATA为例,对串行总线结构加以分析,如图所示。

    2c62c0ff59219bbf1c1f9f34e0f37b58.png

    MAC控制器包括三个层次:PHY逻辑层 、 数据链路层和事务层。每一层都有自己特定的功能。

    PHY逻辑层

    两个相互联接设备的PHY逻辑层之间使用PHY层包进行通信,称为有序训练集合,如图所示。PHY层包被用于建立链路和确定交互的操作速度,在训练阶段结束时,双方进入连接阶段,此时它们已经做好了传输数据链路层和事务层包的准备。PHY层包产生于PHY层,终结于另一侧的PHY逻辑层,不会上交给其他层次。

    c57f1de5cd76df56330b490ed5b0ffc6.png

    数据链路层

    数据链路层使用短的、固定长度(2个dword/8字节)的包在两个设备间交互链路信息,如图所示。这些包也被称为DLLP(Data Link Layer Packet,数据链路层包)。DLLP被用于交换信用信息、ACK、NAK和功率管理协议。DLLP在本层产生,终止于对端的同一层, 不会进一步向上提交。

    28e72432b2e5353c9821d6f296acd7ff.png

    事务层

    本层在两个设备间进行实际的数据交互,如下图所示。这些包被称为TLP(Transaction Layer Packet,事务层包),TLP为变长包。TLP包括头域、数据净荷和CRC校验。一个TLP由start-of-packet符号开始,以END符号结束。

    0d05b15fd1039fc03ccd92a570a89a89.png

    串行总线时钟

    在PCIe中,平台提供100MHz参考时钟,通过PCIe插槽提供给总线设备。PCIe端点设备从PCIe连接器处获取该参考时钟并将其交给PHY PMA层。PMA内部有一个PLL,它根据输入的100MHz时钟和输入的RX数据流生成250MHz的时钟PCLK。PHY将PCLK提供给MAC发送数据(从MAC到PHY)和接收数据(从PHY到MAC)都同步于PCLK。对于PCIe,MAC的接收和发送电路工作在同一个时钟域。对于其他串行总线结构,如SATA收发电路时钟可能不同。在SATA中,发送和接收时钟是不同的,属于异步时钟。

    发送路径的微结构

    MAC和PHY PCS之间的接口是标准的,虽然这不一定必要,但标准化有利于IP核的开发,可提高不同芯片厂商IP核之间的互操作性。对于PCIe来说,该接口被称为PIPE接口。

    MAC提供的发送数据的位宽为8比特或16比特。PCLK的频率与数据总线的位宽有直接关系,数据位宽为8比特时,PCLK为250MHz数据位宽为16比特时,PCLK为125MHz,如图所示。

    00ca7a2918b58ad29b3e0755584b5048.png

    这两个频率是PCIe Genl所使用的,在Gen2中,二者都进行了翻倍。位宽变换电路模块可以将16比特的数据位宽转换成8比特的位宽,接着将其送入8b/10b编码器。编码器将每个8比特的数据转换成为10比特的编码值并将其传递到PHY的PMA层。PMA层使用一个高速时钟(Genl时为2.5GHz,Gen2时为5GHz)进一步将10比特的编码结果转换为单比特串行数据并通过TX信号线发出。

    接收路径的微结构

    PMA接收电路实现比特提取和串并变换功能,将单比特的串行接收数据变换成为10比特的并行数据,如图所示。

    88466fc5bea9f359e763c6ba2d7f96c8.png

    10比特的数据流从PMA接收电路进入PCS接收电路。此时的10比特数据流并非是字符对准的。在PCS内部,10比特数据流先后进入字符对准电路、弹性缓冲区、10b/8b解码电路并最终进入可选的8b/16b转换电路。

    字符对准

    PCIe接收的数据是以10比特的字符为组成单位的。PMA接收电路将接收数据组成10比特字符时没有按照字符边界进行。字符对准逻辑电路查找COMMA字符并以它为基础进行字符边界对准。对准后的字符流被送入弹性缓冲区,如图所示。

    19976dbf9a65f8790824289f7f5fd492.png

    弹性缓冲区

    PCIe链路两端所使用时钟的标称值均为250MHz。它们可以使用平台提供的同一个时钟,或者选择它们自带的时钟源来生成250MHz的工作时钟。当使用相互独立的时钟时,它们之间会有微小的偏差(偏差可能非常微小,但不会为0),此时,经过一段时间之后,会造成数据的上溢或下溢。总线一端的时钟频率可能比另一端略微高一些,频率低的一端会出现数据缓冲区上溢,频率高的一端会出现数据缓冲区下溢。串行总线中使用弹性缓冲区来处理时钟频率上的微小差别。我们将对PCIe和SATA中的弹性缓冲区加以介绍。

    位宽为10比特的接收符号流被写入一个FIFO。按照PCIe协议,在发送数据时,会按照一定的间隔定期发送填充包(称为SKIP集合)。这些填充包可以在不影响数据净荷、编码/解码、扰码/解扰码的情况下快速插入和去除。写入逻辑持续将10比特的字符写入FIFO,读岀逻辑持续将FIFO中的字符读出。如果写入速度比读出速度快,FIFO中的数据深度将逐渐增加。

    当FIFO中的数据深度达到了预先设定的上限时,写入逻辑会丢弃1个或多个SKIP字符。类似地,当写入速度低于读出速度时,FIFO中的数据深度会逐渐降低,当深度降至预先设定的下限时,读出逻辑不再从FIFO中读出数据,它会暂停读出数据,同时向数据通路中插入一个SKIP符号。这里的FIFO就是弹性缓冲区,其内部数据深度是变化的,可以用于调整读写时钟频率的微小偏差,如图所示。

    4a1e61ba9fb2709ff8e331b7c8e7b38a.png

    需要说明的是,这种工作机制可以用于处理微小的读写频率偏差,不适合处理较大的频率偏差(较大的时钟偏差需要深度较大的弹性缓冲区并且会引入较大的延迟)。规范中对频率偏差会提出限制,例如,PCIe中的频率偏差应小于300PPM。当时钟偏差被限定在一定PPM之内时,SKIP出现的间隔就可以计算得到。

    SATA使用了类似的机制,它会在每256个双字之间插入两个ALIGH原语。ALIGN原语根据两边的频率差可以快速地被丢弃或插入。

    10b/8b解码和8b/16b转换

    弹性缓冲区的输出进入解码器电路,它会将10比特的字符转换成为8比特的数据。如果PCS-MAC接口数据通道宽度为16比特,那么需要将两个连续的8比特数据拼接起来构成16比特的数据并送给接收MAC如果数据通路宽度为8比特,那么不需要做任何处理,直接送给接收MAC。

    后面会继续讲讲《串行总线更多结构》,敬请期待。

    032f0b24a3963054f4579f492b607e8c.gif

    NOW

    学习Xilinx FPGA最好的资料其实就是官方手册,下表总结了部分手册的主要介绍内容,关注我,持续更新中......

    文件名主标题内容简单介绍是否有中文版
    UG4767 Series FPGAs GTX/GTH  TransceiversGTX和GTH介绍,PCIe、serdes等学习必备
    UG4717 Series FPGAs SelectIO Resources描述 7 系列 FPGA 中可用的 SelectIO资源。
    UG1114PetaLinux Tools DocumentatonPetaLinux 工具文档 参考指南是,V2019.2
    UG949UltraFAST 设计方法指南(适用于 Vivado  Design Suite)赛灵思® UltraFast™  设计方法是用于为当今器件优化设计进程的一套最佳实践。这些设计的规模与复杂性需要执行特定的步骤与设计任务,从而确保设计每一个阶段的成功开展。依照这些步骤,并遵循最佳实践,将帮助您以最快的速度和最高的效率实现期望的设计目标是,V2018.1
    IP手册pg057FIFO GeneratorFIFO生成器IP使用手册
    pg104Complex Multiplier复数乘法器IP使用手册
    pg122RAM-Based Shift Register 移位寄存器IP使用手册

    45ca22f429c9dfff436af538e7189353.png

    推荐阅读

    【Vivado那些事】如何查找官网例程及如何使用官网例程

    【Vivado使用误区与进阶】总结篇

    【Vivado那些事】Vivado中常用的快捷键(二)其他常用快捷键

    SystemVerilog数字系统设计_夏宇闻 PDF

    图书推荐|ARM Cortex-M0 全可编程SoC原理及实现

    简谈:如何学习FPGA

    Github 上有哪些优秀的 VHDL/Verilog/FPGA 项目

    AD936x+ZYNQ搭建收音机(一)

    AD936x+ZYNQ搭建OpenWIFI

    无招胜有招-Vivado非工程模式下的详细设计

    面试中经常会遇到的FPGA基本概念,你会几个?

    推荐一些可以获取免费的国外的原版书籍(电子版)网站

    FPGA 的重构

    国产CPU概括

    从电子游戏历史看IC发展的助推剂

    80年代电子游戏及电脑游戏的发展历史

    PCIe总线的基础知识

    万字长文带你回顾电子游戏的七十多年历史(完整版)

    FPGA中异步复位,同步释放的理解

    OpenFPGA系列文章总结

    用Verilog设计一个16 位 RISC 处理器

    介绍一些新手入门FPGA的优秀网站(新增)

    Verilog数字系统基础设计-CR

    Verilog数字系统基础设计-奇偶校验

    建立和保持时间及时序简单理解

    (Xilinx)FPGA中LVDS差分高速传输的实现

    Xilinx Multiboot实例演示

    高速串行通信常用的编码方式-8b/10b编码/解码
    Verilog计时(微秒、毫秒和秒)脉冲的产生及同步整形电路

    图书推荐|一本图像/视频处理的强大工具书

    Verilog HDL-同步技术

    再说System Verilog 与 Verilog 的关系

    数模混合信号建模语言Verilog-AMS

    Intel/Altera 系列FPGA简介

    一块带给无数人年少欢乐的CPU,别说你没用过它

    Verilog在编写第一行代码之前

    【例说】Verilog HDL 编译器指令,你见过几个?

    穿越时空的爱恋-Z80 CPU的前世今生

    【Vivado】那些事儿-汇总篇

    古老CPU启示录-晶体管之路

    【Vivado那些事儿】约束的顺序

    童年修复系列-SNES芯片组介绍及FPGA实现

    优秀的 Verilog/FPGA开源项目介绍(二)-RISC-V

    展开全文
  • 并行接口 概括的说就是有8个车bai道同一时刻能传送8位(一个字节)...与此相对的串行接口是在一根数据线上以1位数据位为单位与I/O设备或通信设备传送信息。实际应用中,凡是在CPU与外设之间同时需要两位以上信息传送
  • STM32 串口总线空闲检测

    万次阅读 2016-10-13 23:15:21
    前两天在群里看到有人在询问有关STM32 串口总线空闲检测的事情,根据串口总线是否空闲来判断一帧数据是否发送完成,之前使用串口一直没怎么注意过这一串口特性,所以后来特意去看了下手册中有关总线空闲检测的指示,...
  • 网卡 串口驱动 pci总线设备驱动 驱动研究 及vxworks的启动过程
  • 串口总线通信DB9介绍

    千次阅读 2018-03-16 20:29:16
    一,公头和母头的区分以及接头二,各个管脚的功能三,接线方法串口串行的,全双工的,异步的
  • 这是一个基于单片机的串口转CAN总线的程序,它可以实现串口和CAN总线协议的相互转换。 这是一个基于单片机的串口转CAN总线的程序,它可以实现串口和CAN总线协议的相互转换。
  • 串口UART串行总线协议

    千次阅读 2018-09-14 20:13:30
    UART可以理解为可以用一线总线就可以完成数据的传输,连接图如下 VCC 用来给设备提供电源的,如果设备有电源,可以省略 TX 顾名思义 就是CPU发送数据给设备-对应接设备的RX RX 顾名思义 就是CPU接收设备的数据-...
  • 总线串行数据接口的Verilog实现、电子技术,开发板制作交流
  • USB(Universal Serial Bus),翻译为中文就是通用串行总线,是由Conpaq,DEC,IBM,Inter,Microsoft,NEC和Northen Telecom等公司为简化PC与外设之间的互连而共同研究开发的一种免费的标准化连接器,它支持各种PC与...
  • 串口总线的速率、连接拓扑等介绍,汇总了市面上的一些总线的产品应用等
  • SPI、I2C、UART(即串口)三种串行总线详解

    万次阅读 多人点赞 2019-01-03 09:17:06
    </tr></tbody></table></div><h1 id="%E4%B8%89%E3%80%81%E4%B8%B2%E8%A1%8C%E5%A4%96%E8%AE%BE%E6%8E%A5%E5%8F%A3%EF%BC%9ASPI"><a name="t2"></a><br>三、串行外设接口:SPI</strong></h1> 1、速览点 (1...
  • CAN(Controller Area Network)属于现场总线范畴,它是一种有效支持分布式控制或实时控制串行通信网络。较之目前许多RS-485基于R线构建分布式控制系统而言,基于 CAN总线分布式控制系统在以下方面具有明显优越性: ...
  • 工业控制中,各设备的信号采集和监控只靠串口总线难以实现扩展,要将现场控制网络和信息网络相连,就需要解决串口通信协议和因特网通信协议的转换问题,即把原有设备转换为具备网络接口的外设,这样可以将传统串行链...
  • 采用示波器侦测串口总线的通信是比较常用的一种解码技术。以Pico示波器为例,Pico示波器具有串行解码的功能,能够对CAN、FlexRay、 I2C、I2S、SPI、LIN或UART等串行总线进行解码。Pico示波器比较适合做串行解码,...
  • 描述并行通信传输中有多个数据位,同时在两个设备之间传输。发送设备将这些数据位通过 对应的数据线传送...串行数据传输时,数据是一位一位地在通信线上传输的,先由具有几位总线的计算机内的发送设备,将几位并行数...
  •  首先,外设增多,计算机接口也必须增多,但计算机的标准接口总是有限的,虽然可以通过增加功能接口插卡来扩展接口,但可增插卡的数量受计算机插槽个数的限制,并且扩展接口卡存在以下问题: (1)接口卡的插拔过程...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 65,435
精华内容 26,174
关键字:

串口总线