MCPcopy Index your code
hub / github.com/Makerfabs/PICO_Merchanical_Hand_Driver

github.com/Makerfabs/PICO_Merchanical_Hand_Driver @main

Chat with this repo
repository ↗ · DeepWiki ↗ · + Follow
33 symbols 93 edges 4 files 0 documented · 0%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

PICO Merchanical Hand Driver

/*
Version:        V2.0
Author:         Vincent
Create Date:    2021/7/21
Note:
            2021/7/23 V2.0:Add PIO document.
*/

main

[toc]

Makerfabs

Makerfabs home page

Makerfabs Wiki

PICO Merchanical Hand Driver

Introduce

Pico mechanical hand driver is a platform based on raspberry pi pico, which is designed for driving and controlling servos. The board comes with an 8-bit level translator and 8 channels GPIO headers that can connect the servo directly. Now you can build your new project about servo by trying out the Raspberry PI Pico.

Wiki Link:PICO_Servo_Driver

Feature

  • Standard Raspberry Pi Pico header.
  • Onboard 8-bit level translator TXB0108PWR for driving servos.
  • 8-channel expansion GPIOs for servos.
  • Onboard Active buzzer.
  • One button.
  • Power supply: 5V.

Front:

front

Control multiple Servos with RP2040 PIO

[toc]

Overview

Most of the MCU have some kind of PWM outputs, for example the analogWrite() in Arduino, it output PWM to related pins, with UNO, these pins can be 3/5/6/9/10 and 11, to control the connected LED brightness , or motor speed.

But if these hardware pins already used by some other functions, such as the D11 in UNO(MOSI in SPI communication), most users simulate the PWM in firmware, which need hardware times/interrupts, which take big burden to the MCU. and seriously, when the PWM frequency get high, or multiple PWM are needed, the frequency get unstable, as the MCU maybe too busy to deal with them all, on time.

And more, for servo control, it may needs signal with 20ms frequency, with 0.1%~99.9% logic high duty, hardware PWM can not output such a signal, while with firmware simulating, it will take a lot of timer/interrupt and MCU ability, especially when it comes to multiple servo simultaneously. Here we introduce a simple way , based on the hardware PIO in RP2040(Raspberry Pi PICO), to control multiple servo without the MCU engaging in much.

PIO

The programmable input/output block (PIO) is a versatile hardware interface. It can support a variety of IO standards, including: - 8080 and 6800 parallel bus - I2C - 3-pin I2S - SDIO - SPI, DSPI, QSPI - UART - DPI or VGA (via resistor DAC)

There are 2 identical PIO blocks in RP2040. Each PIO block has dedicated connections to the bus fabric, GPIO and interrupt controller.

1

PIO is programmable in the same sense as a processor. There are two PIO blocks with four state machines each, that can independently execute sequential programs to manipulate GPIOs and transfer data. Unlike a general purpose processor, PIO state machines are highly specialised for IO, with a focus on determinism, precise timing, and close integration with fixed-function hardware. Each state machine is equipped with: - Two 32-bit shift registers – either direction, any shift count - Two 32-bit scratch registers - 4×32-bit bus FIFO in each direction (TX/RX), reconfigurable as 8×32 in a single direction - Fractional clock divider (16 integer, 8 fractional bits) - Flexible GPIO mapping - DMA interface, sustained throughput up to 1 word per clock from system DMA - IRQ flag set/clear/status

Each state machine, along with its supporting hardware, occupies approximately the same silicon area as a standard serial interface block, such as an SPI or I2C controller. However, PIO state machines can be configured and reconfigured dynamically to implement numerous different interfaces.

Making state machines programmable in a software-like manner allows more hardware interfaces to be offered in the same cost and power envelope and also presents a more familiar programming model, and simpler tool flow, to those who wish to exploit PIO’s full flexibility by programming it directly, rather than using a premade interface from the PIO library.

State machines' inputs and outputs are mapped to up to 32 GPIOs (limited to 30 GPIOs for RP2040), and all state machines have independent, simultaneous access to any GPIO. For example, the standard UART code allows TX, RX, CTS and RTS to be any four arbitrary GPIOs, and I2C permits the same for SDA and SCL. The amount of freedom available depends on how exactly a given PIO program chooses to use PIO’s pin mapping resources, but at the minimum, an interface can be freely shifted up or down by some number of GPIOs.

The four state machines execute from a shared instruction memory. System software loads programs into this memory, configures the state machines and IO mapping, and then sets the state machines running. PIO programs come from various sources:

  • Assembled directly by the user
  • Drawn from the PIO library
  • Generated programmatically by user software.

From this point on, state machines are generally autonomous, and system software interacts through DMA, interrupts and control registers, as with other peripherals on RP2040. For more complex interfaces, PIO provides a small but flexible set of primitives which allow system software to be more hands-on with state machine control flow.

2

PIO state machines execute short, binary programs.

Programs for common interfaces, such as UART, SPI, or I2C, are available in the PIO library, so in many cases, it is not necessary to write PIO programs. However, the PIO is much more flexible when programmed directly, supporting a wide variety of interfaces which may not have been foreseen by its designers.

Basic usage with PIO RP2040

Output 2 PWM with diff frequency, with Thonny IDE MicroPyhon

Full code is in : PICO_Merchanical_Hand_Driver/Arm6axis/

Outputs PWM with PIO

Open Thonny, Create a new Python file, inputs the follows code and excute:

from rp2 import PIO, StateMachine, asm_pio
from machine import Pin
import time


@asm_pio(set_init=PIO.OUT_LOW)
def pwm_1():
    set(pins, 1)
    set(pins, 0)


def test():
    sm1 = StateMachine(1, pwm_1, freq=1000000, set_base=Pin(0))
    sm1.active(1)
    while 1:
        print("Run....")
        time.sleep(5)


test()

Test the GPIO0 with Oscilloscope:

pico1

PWM with frequency 500K Hz:

pico1

Assembly Parts

@asm_pio(set_init=PIO.OUT_LOW)
def pwm_1():
    set(pins, 1)
    set(pins, 0)

@asm_pio defines this function a PIO assembly function

set(pins, 1) is not a python funtion, but a PIO assembly function(we will explain it some later,together with other PIO assembly functions),it sets the logitic 1 and 0. each PIO assebly function take 1 PIO cycle, so there 2 assembly funtions here , wit 2 PIO cycles.

MicroPython Parts

sm1 = StateMachine(1, pwm_1, freq=1000000, set_base=Pin(0))
sm1.active(1)

initiate a state machine object , and set the machine state code, assembly function, frequency and the related pins. There totally 2 PIO in RP2040, and 4 state machine each, so the machine state code 0~7

freq sets the PIO frequency,1MHz,so the cycle 1us。

set_base sets PIO GPIO to 0

sm1.active(1) to open the state machine.

The PIO outputs PWM with 2 us cycle(500 KHz), as the Oscilloscope, the advantagtes are:

a. Much precisly than simulation with software

b. Easy to change the frequency

c. Do not take hardware ablity , timers/ interrupts/ MCU.

Output 2 PWM with PIO

Open Thonny, Create a new Python file, inputs the follows code and excute:

from rp2 import PIO, StateMachine, asm_pio
from machine import Pin
import time


@asm_pio(set_init=PIO.OUT_LOW)
def pwm_1():
    set(pins, 1)
    set(pins, 0)

@asm_pio(set_init=PIO.OUT_LOW)
def pwm_2():
    set(pins, 1)[2]
    set(pins, 0)[6]


def test():
    sm1 = StateMachine(1, pwm_1, freq=1000000, set_base=Pin(0))
    sm1.active(1)
    sm2 = StateMachine(2, pwm_2, freq=5000000, set_base=Pin(15))
    sm2.active(1)
    while 1:
        print("Run....")
        time.sleep(5)


test()

The outputs:

pico3

2 PWM with same frequency, GPIO1 duty 50% and GPIO15 duty 30%

Assembly Parts

@asm_pio(set_init=PIO.OUT_LOW)
def pwm_2():
    set(pins, 1)[2]
    set(pins, 0)[6]

In the set(pins, 1)[2], it means 2 cycle delays after the commaand set, equivalent to NOP:

@asm_pio(set_init=PIO.OUT_LOW)
def pwm_2():
    set(pins, 1)
    nop()
    nop()
    set(pins, 0)
    nop()
    nop()
    nop()
    nop()
    nop()
    nop()

10 commands totally.

PIO MicroPython

sm2 = StateMachine(2, pwm_2, freq=5000000, set_base=Pin(15))
sm2.active(1)

Here the state machine #2 was used, and frequency set to 5MHz. As simple caculation, the logic 10.6us, logic 0 1.4us, that is , PWM with 2 us cyle, 30% duty, same as the outputs from the oscilloscope.

PIO assembly commands

PIO assembly commands

There 9 commands in PIO:

3

JMP

Set program counter to Address if Condition is true, otherwise no operation.

Condition:

  • 000: (no condition): Always
  • 001: !X: scratch X zero
  • 010: X--: scratch X non-zero, post-decrement
  • 011: !Y: scratch Y zero
  • 100: Y--: scratch Y non-zero, post-decrement
  • 101: X!=Y: scratch X not equal scratch Y
  • 110: PIN: branch on input pin
  • 111: !OSRE: output shift register not empty

WAIT

Stall until some condition is met.

Polarity:

  • 1: wait for a 1.
  • 0: wait for a 0.

Source: what to wait on. Values are:

  • 00: GPIO: System GPIO input selected by Index. This is an absolute GPIO index, and is not affected by the state machine’s input IO mapping.
  • 01: PIN: Input pin selected by Index. This state machine’s input IO mapping is applied first, and then Index selects which of the mapped bits to wait on. In other words, the pin is selected by adding Index to the PINCTRL_IN_BASE configuration, modulo 32.
  • 10: IRQ: PIO IRQ flag selected by Index
  • 11: Reserved

Index: which pin or bit to check.

IN

Shift Bit count bits from Source into the Input Shift Register (ISR). Shift direction is configured for each state machine by SHIFTCTRL_IN_SHIFTDIR. Additionally, increase the input shift count by Bit count, saturating at 32.

Source:

  • 000: PINS
  • 001: X (scratch register X)
  • 010: Y (scratch register Y)
  • 011: NULL (all zeroes)
  • 100: Reserved
  • 101: Reserved
  • 110: ISR
  • 111: OSR

Bit count: How many bits to shift into the ISR. 1…32 bits, 32 is encoded as 00000.

If automatic push is enabled, IN will also push the ISR contents to the RX FIFO if the push threshold is reached (SHIFTCTRL_PUSH_THRESH). IN still executes in one cycle, whether an automatic push takes place or not. The state machine will stall if the RX FIFO is full when an automatic push occurs. An automatic push clears the ISR contents to all-zeroes, and clears the input shift count.

IN always uses the least significant Bit count bits of the source data. For example, if PINCTRL_IN_BASE is set to 5, the instruction IN PINS, 3 will take the values of pins 5, 6 and 7, and shift these into the ISR. First the ISR is shifted to the left or right to make room for the new input data, then the input data is copied into the gap this leaves. The bit order of the input data is not dependent on the shift direction.

NULL can be used for shifting the ISR’s contents. For example, UARTs receive the LSB first, so must shift to the right. After 8 IN PINS, 1 instructions, the input serial data will occupy bits 31…24 of the ISR. An IN NULL, 24 instruction will shift in 24 zero bits, aligning the input data at ISR bits 7…0. Alternatively, the processor or DMA could perform a byte read from FIFO address + 3, which would take bits 31…24 of the FIFO contents.

OUT

Shift Bit count bits out of the Output Shift Register (OSR), and write those bits to Destination. Additionally, increase the output shift count by Bit count, saturating at 32.

Destination:

  • 000: PINS
  • 001: X (scratch register X)
  • 010: Y (scratch register Y)
  • 011: NULL (discard data)
  • 100: PINDIRS
  • 101: PC
  • 110: ISR (also sets ISR shift counter to Bit count)
  • 111: EXEC (Execute OSR shift data as instruction)

Bit count: how many bits to shift out of the OSR. 1…32 bits, 32 is encoded as 00000.

A 32-bit value is written to Destination: the lower Bit count bits come from the OSR, and the remainder are zeroes. This value is the least significant Bit count bits of the OSR if SHIFTCTRL_OUT_SHIFTDIR is to the right, otherwise it is the most significant bits.

PINS and PINDIRS use the OUT pin mapping.

If automatic pull is enabled, the OSR is automatically refilled from the TX FIFO if the pull threshold, SHIFTCTRL_PULL_THRESH, is reached. The output shift count is simultaneously cleared to 0. In this case, the OUT will stall if the TX FIFO is empty, but otherwise still executes in one cycle.

OUT EXEC allows instructions to be included inline in the FIFO datastream. The OUT itself executes on one cycle, and the instruction from the OSR is executed on the next cycle. There are no restrictions on the types of instructions which can be executed by this mechanism. Delay cycles on the initial OUT are ignored, but the executee may insert delay cycles as normal.

OUT PC behaves as an unconditional jump to an address shifted out from the OSR.

PUSH

Push the contents of the ISR into the RX FIFO, as a single 32-bit word. Clear ISR to all-zeroes.

  • If Full: If 1, do nothing unless the total input shift count has reached its threshold, SHIFTCTRL_PUSH_THRESH (the same as for autopush).
  • Block: If 1, stall execution if RX FIFO is full.

PUSH IFFULL helps to make programs more compact, like autopush. It is useful in cases where the IN would stall at an inappropriate time if autopush were enabled, e.g. if the state machine is asserting some external control signal at this point.

The PIO assembler sets the Block bit by default.

Core symbols most depended-on inside this repo

explain_line
called by 9
Arm6axis/arm_controller.py
flesh
called by 6
Arm6axis/arm_6axis.py
set_single_angle
called by 5
Arm6axis/arm_6axis.py
init_arm_angle
called by 4
Arm6axis/arm_controller.py
set_ms
called by 3
Arm6axis/pio_servo.py
pos_report
called by 3
Arm6axis/arm_6axis.py
get_index
called by 2
Arm6axis/arm_controller.py
alarm
called by 2
Arm6axis/arm_controller.py

Shape

Method 19
Function 10
Class 4

Languages

Python100%

Modules by API surface

Arm6axis/arm_controller.py14 symbols
Arm6axis/arm_6axis.py12 symbols
Arm6axis/pio_servo.py7 symbols

For agents

$ claude mcp add PICO_Merchanical_Hand_Driver \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact