Raspberry Pi Pico Tips and Tricks

Saturday, 31 December 2022

Inter-Integrated Circuit (I2C) on the Raspberry Pi Pico

 The Raspberry Pi Pico includes support to the I2C communications protocol through two I2C controllers which combined service a total of 12 separate connection pair options.

General Purpose Input / Output

Right from the outset, this does not mean that we can hook up multiple devices (or string of devices) to multiple connection points that use the same controller. One device or string of devices per controller only. That means that we are limited to a total of 127 daisy chained devices per controller. That should be enough for a start :-).

I've written this short explanation as part of the much larger book 'Raspberry Pi Pico Tips and Tricks'. You can download it for free from here.

I2C (Inter-Integrated Circuit) is a serial communication protocol that allows devices to communicate with each other over a shared bus. It was developed in the 1980s by Philips Semiconductors as a way to reduce the number of wires needed to connect devices, and has become widely used in a variety of applications.

I2C uses a two-wire interface, consisting of a serial data line (SDA) and a serial clock line (SCL). Devices that use I2C are called ‘slaves’, and they are connected to a ‘master’ device (usually a microcontroller or processor) through these two wires. The master device controls the communication by generating the clock signal and sending/receiving data on the SDA line.

I2C is a very useful protocol because it allows multiple devices to communicate with a single microcontroller or processor using just two wires, making it well suited for use in embedded systems and other applications where space and resources are limited. It is also relatively simple to implement, making it a popular choice for many applications.

How does I2C work?

The master device controls the communication by generating the clock signal and sending/receiving data on the SDA line. The slave devices are connected to the master device through the SDA and SCL lines, and they respond to the commands and requests of the master device.

Here is a brief overview of how I2C works:

  • The master device starts the communication by pulling the SDA line low while the SCL line is high and then pulling the SCL line low as well, indicating the start of a new data transfer.
Listen up everyone!
  • The master device then sends a 7-bit slave address followed by a read/write bit, indicating whether it wants to read data from or write data to the slave device.
1101100 step forward!
  • If the slave device recognizes its own address, it sends an acknowledgement (ACK) by pulling the SDA line low. If the slave device does not recognize its own address, it does not send an ACK and the communication ends.
I’m here!
  • If the master device wants to write data to the slave device, it sends the data byte by byte, followed by an ACK from the slave device after each byte. If the master device wants to read data from the slave device, it sends a request for data and the slave device responds by sending a byte of data followed by an ACK from the master device.
Send me the data
Here you go!
  • The communication ends when the master device sends a stop condition by pulling the SDA line high while the SCL line is high.
The conversation is over

Finding and I2C devices connected to the Raspberry Pi Pico

On the Raspberry Pi Pico, I2C is implemented using a hardware peripheral called the I2C controller, which is part of the Pico’s RP2040 microcontroller. The I2C controller is responsible for generating the clock signal and managing the communication between the Pico and I2C slave devices.

To use I2C on the Raspberry Pi Pico, you will need to import the machine module and use its I2C class.

The following code that demonstrates how to scan for I2C slave devices on the Raspberry Pi Pico and print the addresses of the detected devices.

import machine

# Create an I2C object with the specified SDA and SCL pins
i2c = machine.I2C(sda=machine.Pin(22), scl=machine.Pin(23))

# Scan for slave devices on the I2C bus
devices = i2c.scan()

# Print the addresses of the detected devices
print(devices)

This code creates an I2C object using the SDA and SCL pins 22 and 23, respectively, and uses the scan() method to detect slave devices on the I2C bus. It then prints the addresses of the detected devices.

Don't forget, if you're looking for the book 'Raspberry Pi Pico Tips and Tricks'. You can download it for free (or donate if you wish) from here.

Thursday, 29 December 2022

Using a Stepper Motor with a Raspberry Pi Pico

 

The Stepper Motor

A stepper motor is a type of electric motor that rotates in precise increments or ‘steps’ when electrical commands are applied. Stepper motors are commonly used in applications where precise positioning is required, such as in printers, scanners, and 3D printers.

I've written this short explanation as part of the much larger book 'Raspberry Pi Pico Tips and Tricks'. You can download it for free from here.

Stepper Motor Connection

Stepper motors can be classified as either unipolar or bipolar. Unipolar stepper motors have a single winding per phase and require a special type of drive circuit, while bipolar stepper motors have two windings per phase and can be driven with a more common H-bridge drive circuit.

Stepper motors operate by energizing the windings in a specific sequence, causing the rotor to rotate a precise number of degrees per step. The number of steps per revolution and the step angle (the angle of rotation per step) are typically specified by the manufacturer. Stepper motors can be controlled with a microcontroller or other type of controller, such as a motor driver or stepper motor driver.

There are several key differences between a stepper motor and a normal (also known as a brushed or brushless DC) motor:

  • Precise positioning: Stepper motors are capable of more accurate positioning because they rotate in precise increments or “steps” when electrical commands are applied. Normal motors, on the other hand, are not capable of precise positioning because they rotate continuously when powered.
  • Torque: Stepper motors typically have less continuous torque than normal motors, but they can deliver high torque in short bursts. Normal motors, on the other hand, can deliver a more consistent level of torque over a longer period of time.
  • Speed: Stepper motors have a limited speed range, typically up to several hundred RPM, depending on the model. Normal motors, on the other hand, can reach higher speeds and have a wider speed range.
  • Control: Stepper motors can be controlled with a microcontroller or other type of controller, such as a motor driver or stepper motor driver. Normal motors typically require a more complex drive circuit, such as an H-bridge, to control the speed and direction of rotation.
  • Cost: Stepper motors tend to be more expensive than normal motors of similar size and power due to their precision and control capabilities.

Putting the ‘Step’ into stepper motors

Stepper motors operate by energizing their windings in a specific sequence, causing the rotor to rotate a precise number of degrees per step. The number of steps per revolution and the step angle (the angle of rotation per step) are typically specified by the manufacturer.

  • The controller sends electrical commands to energize the windings in a specific sequence. For example, the sequence may be to energize winding A, then winding B, then winding C, and finally winding D.
  • When the windings are energized, the magnetic fields generated by the windings interact with the magnetic field of the permanent magnets in the rotor, causing the rotor to rotate a certain number of degrees.
  • The controller sends the next set of electrical commands to energize the windings in the next sequence. For example, the sequence may be to energize winding C, then winding D, then winding A, and finally winding B.
  • The process repeats, with the controller sending electrical commands to energize the windings in the specified sequence, causing the rotor to rotate a certain number of degrees each time. This results in the stepper motor turning in a precise, step-by-step manner.
Stepper Motor Sequence

The specific sequence of the windings being energized depends on the type of stepper motor (unipolar or bipolar) and the specific drive circuit being used.

Torque

Rated torque is a measure of the maximum torque that a stepper motor can produce under specified conditions. It is typically expressed in units of force times distance, such as Newton-meters (Nm) or ounce-inches (oz-in).

Rated torque determines the amount of force that the motor can generate to move a load. In general, a stepper motor with a higher rated torque will be able to move a larger or heavier load than a motor with a lower rated torque.

It is important to note that the rated torque of a stepper motor is not constant and can vary depending on factors such as operating voltage, current, speed, and temperature. In general, the rated torque of a stepper motor decreases as the speed increases, and it may also be affected by the type and size of the load being driven.

The 28BYJ-48

The 28BYJ-48 is a small, low-cost stepper motor commonly used in hobbyist and educational projects. It has a 5V operating voltage and is driven by a ULN2003A driver board (or similar stepper motor driver).

The 28BYJ-48 has a step angle of 5.625 degrees and a step resolution of 64 steps per revolution, resulting in a total of 4096 steps per revolution. It has a rated torque of 44.4 g-cm (0.6 oz-in) and a maximum no-load speed of approximately 15 RPM.

The 28BYJ-48 is a unipolar stepper motor, meaning it has a single winding per phase and requires a special type of drive circuit to operate (hence the use of the ULN2003A driver board). It is commonly used in applications such as small robotics, educational models, and DIY projects.

Connecting the Pico to the controller to the GY-521

The connection from the ULN2003A driver board to the motor is via a pre-made molex connector. Power is applied to the ULN2003A via the + and - connectors which should be connected to a 5V supply. In the connection diagram below that power is being sourced from the VBUS connector of the Raspberry Pi Pico. We need to be a little bit careful here. The 28BYJ-48 is quite power hungry (around 240mA) and typically this should come from a separate power source. However, in this case, the VBUS connector on the Pico is connected directly to the 5V supply from the USB connector and so long as that has sufficient power we should be alright. Having said that, be aware that this is a very simple motor and it will consume power even when it is standing still. If we leave it for a few minutes, we can feel it getting warm.

The power connections are as follows;

  • VBUS (Pin 40) -> +
  • GND (Pin 38) -> -

The 28BYJ-48 rotates by sending signals to its four coils in a specific sequence that switches the coils on and off in a pattern that creates a magnetic field that rotates the motor. Those four coils are controlled from four GPIO pins on the Pico. In our case we will use GPIO18, 19,20 and 21. For no reason other than they make drawing the circuit diagram below slightly prettier.

The connections are as follows;

  • GPIO18 (Pin 24) -> IN1
  • GPIO19 (Pin 25) -> IN2
  • GPIO20 (Pin 26) -> IN3
  • GPIO21 (Pin 27) -> IN4
Stepper Motor Connection

Code

The following code demonstrates the stepper motor turning in one direction;

from machine import Pin
from time import sleep

IN1 = Pin(18,Pin.OUT)
IN2 = Pin(19,Pin.OUT)
IN3 = Pin(20,Pin.OUT)
IN4 = Pin(21,Pin.OUT)

pins = [IN1, IN2, IN3, IN4]

sequence = [[1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]

while True:
    for step in sequence:
        for i in range(len(pins)):
            pins[i].value(step[i])
            sleep(0.001)

The following code allows the motor to turn in different directions through a function call;

import time
from machine import Pin

# Constants for the stepper motor pins
IN1 = Pin(18, Pin.OUT)
IN2 = Pin(19, Pin.OUT)
IN3 = Pin(20, Pin.OUT)
IN4 = Pin(21, Pin.OUT)

# Sequence for moving the stepper motor
SEQUENCE = [[1,0,0,0], [0,1,0,0], [0,0,1,0], [0,0,0,1]]

# Function to move the stepper motor
def move_stepper(direction, steps):
    # Set the input pins
    pins = [IN1, IN2, IN3, IN4]

    # Set the direction of the sequence
    if direction == 'forward':
        sequence = SEQUENCE
    elif direction == 'backward':
        sequence = list(reversed(SEQUENCE))

    # Loop through the specified number of steps
    for i in range(steps):

        # Set the input pins based on the current step
        for j in range(len(pins)):
            pins[j].value(sequence[i%4][j])

        # Delay between steps
        time.sleep(0.005)

# Main loop
while True:
    # Move the stepper motor forward
    move_stepper('forward', 400)

    # Move the stepper motor backward
    move_stepper('backward', 400)

Don't forget, if you're looking for the book 'Raspberry Pi Pico Tips and Tricks'. You can download it for free (or donate if you wish) from here.