Raspberr Pi Pico Notes

Keywords: Single-Chip Microcomputer

At the end of last semester, the lower computer is used to control the underlying hardware. However, the price of STM32 boards has gone up dramatically. Seeing that more than a dozen boards have gone up to nearly 40 at a time, the raspberry pie pico boards with similar functions have been shown to me. Although in the end, I used STM32 boards for stability, I also managed to get some Picos to play with.

The online tutorials on pico are small and fragmented, and I can only read Official Documents Learn slowly. At present, the notes are not complete or even a little confused, but they may help later people get started as soon as possible.

Up brusher

Very simple, press and hold the BOOTSET key to connect the computer with microUSB, then you can use uf2 program to brush the machine in the way of file copy.

The uf2 file for MicroPython can be downloaded from here: microPython environment

For C/C++, uf2 files need to be compiled and then burned into the board.

Pipe Chart

MicroPython Control

machine module

machine.Pin

GPIO class. The operation of GPIO can be accomplished by code like the following.

switch = Pin(0, Pin.IN, Pin.PULL_DOWN)  # Set drop-down input
trigger = Pin(1, Pin.IN, Pin.PULL_UP)  # Set up pull-up input
led = Pin(25, Pin.OUT)  # On-board LED is GP 25

state = 0
def change():
    global state
    state = 1-state

def setLED(pin):  # Interrupt handler to receive a Pin object whose argument is the trigger
    led.value(state)
    # value() returns the pin value when no parameter is present
    # With parameters, value(i) sets the pin value to I
    
trigger.irq(setLED, Pin.IRQ_FALLING)  # Set drop edge to trigger interrupt

while True:
    if switch.value():  # If high level
        change()
        while switch.value():
            pass  # Wait until you fall back to low level

Pin setting is quite convenient~

Initialization:

Initialization requires at least a GP pin. The physical pin corresponding to the GP is shown in the figure above. An I/O is also specified. If not, the pin is set at 0 by default.

p1 = Pin(0)
p2 = Pin(1, Pin.IN)
p3 = Pin(2, Pin.OUT)

When specified as OUT, pins are initially low level. Pins can be positioned with high() or on() functions, reset with low() or off() functions, directly specified with value(v) functions, and inverted with toggle() functions. At this time, pin levels can also be queried with value() functions. Specifically, the following two sections can be seen.

When specified as IN, the pin initial level is the level at which it was initialized, but the pull-up and pull-down inputs can be specified:

p4 = Pin(3, Pin.IN, Pin.PULL_UP)
p5 = Pin(4, Pin.IN, Pin.PULL_DOWN)

At this time, high(), low(), on(), off(), toggle() and value(v) can be called successfully but will not take effect. Use the value() function to query pin level normally.

In addition to the default initialization, you can use the reinitialization function init(), which is the same as above, except that pins are not required.

Set pin level:

Only set for OUT GP. High level is 3V3.

Use high(), on(), value(1) to position, i.e. the pin level is high; use low(), off(), value(0) to reset, i.e., the pin level is low; use toggle() to reverse the height.

led = Pin(25, Pin.OUT)
led.high()
sleep(2)
led.low()
sleep(3)
while True:
    led.toggle()
    sleep(0.5)

Read pin level:

Readable for all kinds of GP s.

Use value() to read, high returns 1, low returns 0.

led = Pin(25, Pin.OUT)
button = Pin(0, Pin.IN, Pin.PULL_DOWN)
while True:
    led.value(button.value())

Setup/Query Interrupt:

Interrupts can be set for all types of GP s.

Set using the irq(func[, flag]) function. The first parameter specifies the function that triggers the interrupt call. The function must have one parameter to receive the Pin object that triggers the interrupt. The second parameter is optional to specify the rise/fall edge trigger. The default is IRQ_FALLING | IRQ_RISING, which is triggered by any change in the level.

The function returns the irq object of the GP. Its flags() method queries the current trigger state, or 0 if it is not triggered, or it is one of IRQ_FALLING or IRQ_RISING. Its trigger() method returns the type of interrupt, or 0 if no interrupt is set, or one of IRQ_FALLING, IRQ_RISING, or IRQ_FALLING | IRQ_RISING.

A parameter-free call to irq() returns only the IRQ object of the GP.

button = Pin(0, Pin.IN, Pin.PULL_UP)

def handler(pin):
    print('I am pulled down.' if pin.irq().flags() == Pin.IRQ_FALLING else 'I am pulled up.')

button.irq(handler)

machine.PWM

Go straight to the official routine, because it's easy.

Official routines:

# Example using PWM to fade an LED.
import time
from machine import Pin, PWM

# Construct PWM object, with LED on Pin(25).
pwm = PWM(Pin(25))

# Set the PWM frequency.
pwm.freq(1000)

# Fade the LED in and out a few times.
duty = 0
direction = 1
for _ in range(8 * 256):
	duty += direction
	if duty > 255:
		duty = 255
		direction = -1
	elif duty < 0:
		duty = 0
		direction = 1
	pwm.duty_u16(duty * duty)
	time.sleep(0.001)

Initialization:

Initialize with a Pin object:

led = Pin(25, Pin.OUT)
pwm = PWM(led)
wave = PWM(Pin(0))

Set/Query Frequency:

freq(f) is used to set the frequency. Frequency is at least 10 Hz (8 Hz is also possible), up to 125 MHz. Settings outside the range can cause errors, and some high accuracy settings are automatically rounded to nearby crossover allowable values.

freq() without parameters is used to query the frequency, and the return value is frequency.

Set/Query Duty Cycle:

There are two modes: counting mode and timing mode.

duty_u16(d) is set for count mode, duty ratio
d 65535 × 100 % {d\over 65535}\times100 \% 65535d​×100%
Due to internal settings, some settings will be changed to nearby, more hardware-friendly values. If the limit of 65535 is exceeded, strange results will be obtained.

A parameterless call to duty_u16() yields the above d-value.

duty_ns(t) is set for the timing mode to set the duration (in nanoseconds) of the high level for each cycle. If the frequency is set to f, the duty cycle is
t f × 1 0 − 9 × 100 % tf\times10^{-9} \times100\% tf×10−9×100%
Also because of the internal settings, the actual value will be a little different from the specified value.

A parameterless call to duty_ns() yields the above t-value.

The initial frequency was 1907Hz (I tried it several times, as if it were), and the duty cycle was 0.

Stop it:

Use the deinit() function to pause the generation of PWM waves, and set the duty cycle to resume. The pause will maintain the level of the pause instant. Setting the frequency will not resume the PWM waves.

machine.UART

Official routines:

from machine import UART, Pin
import time

uart1 = UART(1, baudrate=9600, tx=Pin(8), rx=Pin(9))

uart0 = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))

txData = b'hello world\n\r'
uart1.write(txData)
time.sleep(0.1)
rxData = bytes()
while uart0.any() > 0:
    rxData += uart0.read(1)

print(rxData.decode('utf-8'))

(True, clear at a glance. Wait until you've finished)

machine.SPI

Initialization:

spi = SPI(0
          baudrate=1000000,  # baud rate
          polarity=0,        # Clock default level
          phase=0,           # Up/Down Edge Read
          bits=8,            # position
          firstbit=SPI.MSB,  # Reverse/Order
          sck=Pin(2),        # Specify clock port
          mosi=Pin(3),       # Specify Output Port
          miso=Pin(4)        # Specify Input Port
)

(Usage similar to UART)

C/C++ Control

All operations here are performed on Linux systems. Windows can also install related SDK s, but has not yet tried them.

I see this section Official Documents Notes taken in the diary should be based on documentation when developing.
I personally feel that, after getting started, it is not as efficient to read the notes of predecessors as to read the documents directly.

SDK Configuration

If you want to be lazy, get the script directly from the official channel and run it.

wget https://raw.githubusercontent.com/raspberrypi/pico-setup/master/pico_setup.sh
sudo bash ./pico_setup.sh

You can read the script code to confirm its operation before running.

You can also install it yourself step by step:

  • Choose a geomancy site. Try not to include any other folders.

  • Install dependent packages:

    sudo apt update
    sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential
    
  • Clone the SDK, or you can choose to clone the sample program:

    git clone -b master https://github.com/raspberrypi/pico-sdk.git
    cd pico-sdk
    git submodule update --init
    cd ..
    git clone -b master https://github.com/raspberrypi/pico-examples.git
    

    Be careful to create the folder you want.

  • Create folders under their own folders and compile them.

    cmake ../
    make -j4
    
  • Also, remember to check for updates from time to time:

    cd pico-sdk
    git pull
    git submodule update
    

program

stdio

This header file needs to be included:

#include <stdio.h>

Input and output streams can be specified as USB or normal serial ports, which are used by default. Set in CMakeLists.txt

pico_enable_stdio_usb(mylowvel 1)
pico_enable_stdio_uart(mylowvel 0)

To enable USB IO. When using USB, you can use functions within tusb.h to help determine if the USB connection is successful, thus avoiding problems that the upper computer cannot detect:

#include <tusb.h>
// ...
while(!tud_cdc_connected());

Before using library functions in your program, you need to initialize them:

stdio_init_all();

Other available functions are those in the standard C language, such as printf, getchar, and so on.

GPIO

You need to include these two headers:

#include "pico/stdlib.h"
#include "hardware/gpio.h"

Initialize a GPIO:

gpio_init(GP_id);

Set/query the input/output direction of a GPIO:

gpio_set_dir(GP_id, GPIO_OUT);  // Or GPIO_IN
gpio_get_dir(GP_id);

Set/query a GPIO for pull-up/drop-down input:

gpio_pull_up(GP_id);
gpio_is_pulled_up(GP_id);  // Return true/false
gpio_pull_down(GP_id);
gpio_is_pulled_down(GP_id);  // Return true/false

gpio_set_pulls(GP_id, true, false);  // The second parameter specifies a drop-down, and the third parameter specifies a drop-down

Set/query the level of a GPIO:

gpio_put(GP_id, true);  // Or false
gpio_get(GP_id);  // Return true/false

IRQ interrupt

You need to include these three headers:

#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/irq.h"

Set interrupt handling:

gpio_set_irq_enabled_with_callback(GP_id, 
                                   GPIO_IRQ_EDGE_FALL,  // Or other interrupt events, such as GPIO_IRQ_EDGE_RISE, etc.
                                   true,                // true is enabled immediately
                                   &handlingFunction);

Interrupt handler type:

void handlingFunction(uint gpio, uint32_t events);

PWM

These headers need to be included:

#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/pwm.h"
#include "hardware/clocks.h" //If the system clock frequency is not used, ignore

Set the specified GPIO to PWM output:

gpio_set_function(GP_id, GPIO_FUNC_PWM);

Set GPIO frequency:

uint wrap;
uint slice_num = pwm_gpio_to_slice_num(GP_id);
pwm_set_wrap(slice_num, wrap);  // Set frequency to (125M/wrap)Hz

Be careful:

The above method sets wrap up to 65535. If you want to set a smaller frequency, you need to set the PWM crossover manually:

uint freq;  // Frequency to set
uint wrap;  // Top value
uint times; // division factor
uint slice_num = pwm_gpio_to_slice_num(GP_id);
pwm_set_enabled(slice_num, true);  // Enable. It looks like there's no such thing.
pwm_config config = pwm_get_default_config();  // Use a setup structure to prepare for subsequent initialization
float div = (float)clock_get_hz(clk_sys) / (freq * times);
pwm_config_set_clkdiv(&config, div);
pwm_config_set_wrap(&config, wrap);
pwm_init(slice_num, &config, true);

It is important to note that wrap is relative to the clock after crossover. The expression in the program can be in other situations, but should ultimately be:
t i m e s × w r a p = c l k _ s y s = 125   M H z times\times wrap=clk\_sys=125\ {\rm MHz} times×wrap=clk_sys=125 MHz

Set duty cycle:

pwm_set_gpio_level(GP_id, level);  // Set duty cycle (level/wrap) *100%

UART

These headers need to be included:

#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/uart.h"

Initialization:

uart_init(UART_id, 115200);  // UART_id is uart0 or uart1, identifiers are defined
gpio_set_function(GP_id, GPIO_FUNC_UART);  // Register the matching TX as UART port, referring to the above figure
gpio_set_function(GP_id, GPIO_FUNC_UART);  // Register the matching RX as a UART port, referring to the above figure
uart_set_baudrate(UART_id, 115200);  // Set baud rate, default 115200
uart_set_format(UART_id, 8, 1, 0);  // Set data bits, stop bits, check bits, default 8/1/0

Query if the serial port is readable/writable:

uart_is_readable(UART_id);  // Return true/false
uart_is_writable(UART_id);  // Return true/false

Read/write serial port:

uart_getc(UART_id);  // Return read characters
uart_putc(UART_id, 'c');
uart_puts(UART_id, "string");

Compile

After completing the above SDK configuration and programming, you only need to be aware of the configuration of CMakeLists.txt.

cmake_minimum_required(VERSION 3.12)

include(pico_sdk_import.cmake)

project(your_project_name C CXX ASM)
set(CMAKE_C_STANDARD 11)
set(CMAKE_CXX_STANDARD 17)

pico_sdk_init()

add_executable(your_project_name
        c_or_cpp_file.c
        )

target_link_libraries(your_project_name pico_stdlib)

pico_add_extra_outputs(your_project_name)

add_compile_options(-Wall
        -Wno-format          
        -Wno-unused-function
        -Wno-maybe-uninitialized
        )

Where the pico_sdk_import.cmake file can be copied directly from <PICO_SDK_PATH>/external/pico_sdk_import.cmake.

Then there's the stereotype:

mkdir build
cd build
cmake ..
make -j4

Be careful:

When compiling, try to include only half-corner English in the full path name of the directory you are in. make has a high probability of making errors when the path name is known to exist in Chinese!

Posted by robdavies on Mon, 20 Sep 2021 11:58:02 -0700