Raspberr Pi Pico 笔记

上学期末做项目,要用到下位机实现底层的硬件控制。然而STM32板子价格疯涨。看着本来十几块的小板子一下子涨到近40块,这个功能相似的树莓派pico板子倒被我看上了。虽然最后为了求稳,我还是用了STM32板子,不过借着这个机会我也成功搞来了几块pico玩。

网上关于pico的教程十分少且碎,我只能看官方文档慢慢学。目前笔记也不很全、甚至可以说有些乱,但说不定可以帮后来人尽快地上手。

20250107,师弟最近玩树莓派,我给他推了这帖,结果几年没管此帖喜提VIP自动锁。CSDN你干的好啊,蒸蒸日上,再也不见。


上电刷机

挺简单的,按住BOOTSET键用microUSB连电脑,之后就可以用uf2程序以文件复制的方式刷机了。

MicroPython的uf2文件可以从这里下载:microPython环境

而对于C/C++而言,需要编译出uf2文件,然后如上烧进板子中。

在这里插入图片描述

管脚图

在这里插入图片描述

MicroPython控制

machine模块

machine.Pin

GPIO类。通过类似下面的代码可以实现对GPIO的操作。

switch = Pin(0, Pin.IN, Pin.PULL_DOWN)  # 设置下拉输入
trigger = Pin(1, Pin.IN, Pin.PULL_UP)  # 设置上拉输入
led = Pin(25, Pin.OUT)  # 板载LED为25号GP

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

def setLED(pin):  # 中断处理函数,接收一个参数为触发者的Pin对象
    led.value(state)
    # 无参数时,value()返回引脚的值
    # 有参数时,value(i)设置引脚值为i
    
trigger.irq(setLED, Pin.IRQ_FALLING)  # 设置下降沿触发中断

while True:
    if switch.value():  # 如果为高电平
        change()
        while switch.value():
            pass  # 在降回低电平之前,等待

引脚设置相当方便~

初始化:

初始化需要至少指定GP引脚,GP对应的物理引脚见上图。同时要指定I/O,若未指定,则引脚默认置0。

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

指定为OUT时,引脚初始为低电平,可以使用high()函数或on()函数来置位、用low()函数或off()函数来复位、用value(v)函数来直接指定值、用toggle()函数来反转高低电平。此时,还可以用value()函数来查询引脚的电平高低。具体可以看下面两节。

指定为IN时,引脚初始电平为初始化时所处电平,但可以指定上拉输入和下拉输入:

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

此时,high()low()on()off()toggle()value(v)都可以顺利调用但不会生效。使用value()函数可以正常查询引脚电平。

除了默认的初始化,还可以使用重初始化函数init(),除了无需设置引脚,其他同上。

设置引脚电平:

仅能对OUT GP设置。高电平为3V3。

使用high()on()value(1)来置位,即将引脚电平置高;使用low()off()value(0)来复位,即将引脚电平置低;使用toggle()来反转高低。

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

读取引脚电平:

对各类GP,皆可读取。

使用value()来读取,高电平返回1,低电平返回0。

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

设置/查询中断:

对各类GP皆可设置中断。

使用irq(func[, flag])函数来设置。第一个参数指定中断触发调用的函数,函数须有一个参数,用来接收触发中断的Pin对象。第二个参数可选,用来指定上升沿/下降沿触发,默认为IRQ_FALLING | IRQ_RISING,即电平任意变化触发。

函数会返回GP的irq对象。它的flags()方法可以查询当前触发状态,未触发则为0,否则为IRQ_FALLINGIRQ_RISING之一。它的trigger()方法返回中断种类,未设置中断则为0,否则为IRQ_FALLINGIRQ_RISINGIRQ_FALLING | IRQ_RISING之一。

无参数调用irq()则只会返回GP的irq对象。

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

直接上官方例程吧,因为挺简单的。

官方例程:

# 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)

初始化:

使用一个Pin对象来初始化:

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

设置/查询频率:

使用freq(f)来设置频率。频率至少为10Hz(8Hz也可以),最高为125MHz,范围外的设置会导致报错,某些对精度要求高的设置会被自动舍入到附近的分频允许值。

使用无参数的freq()来查询频率,返回值为频率。

设置/查询占空比:

有两种模式:计数模式和计时模式。

duty_u16(d)为计数模式设置,占空比为
d 65535 × 100 % {d\over 65535}\times100 \% 65535d×100%
由于内部设置,某些设置值会被改为附近的、更贴合硬件的值。如果超出了65535的限制,则会得到奇怪的结果。

无参数调用duty_u16()会得到上述的d值。

duty_ns(t)为计时模式设置,用来设置每个周期高电平持续的时间(单位:纳秒)。若已设置频率为f,则占空比为
t f × 1 0 − 9 × 100 % tf\times10^{-9} \times100\% tf×109×100%
同样由于内部设置,实际值会与指定的值有点出入。

无参数调用duty_ns()会得到上述的t值。

初始的频率为1907Hz(我试了几遍,好像的确是这个数),占空比为0。

停止:

使用deinit()函数以暂停PWM波的生成,设置占空比即可恢复。暂停会维持暂停瞬间的电平高低。设置频率不会恢复PWM波。

machine.UART

官方例程:

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'))

(讲真,一目了然。等之后整理时再细写吧)

machine.SPI

初始化:

spi = SPI(0
          baudrate=1000000,  # 波特率
          polarity=0,        # 时钟默认电平
          phase=0,           # 上升沿/下降沿读取
          bits=8,            # 位
          firstbit=SPI.MSB,  # 倒序/顺序
          sck=Pin(2),        # 指定时钟端口
          mosi=Pin(3),       # 指定输出端口
          miso=Pin(4)        # 指定输入端口
)

(用法类似UART)

C/C++控制

这里的操作均在Linux系统上执行。Windows也可以安装相关SDK,但基本全程手动配环境,注意各文件位置(仅附一个Windows下的环境配置教程链接吧,写的相当详细,我就不狗尾续貂了)。

这一大节是我看官方文档时记的笔记,由于文档有在更新,实际开发时还请以文档为准。
个人觉得,在初步入门上手后,看前人的笔记反而不如直接看文档来得高效。

SDK配置

想偷懒的话,直接从官方渠道拿到脚本,然后运行即可。

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

运行前可以读一读脚本代码确认一下它的操作。

也可以自己一步一步安装:

  • 选择一处风水宝地。尽量是不包含其他内容的文件夹。

  • 安装依赖包:

    sudo apt update
    sudo apt install cmake gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential
    
  • 克隆SDK,也可以选择克隆样例程序:

    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
    

    注意创建所需的文件夹。

  • 在各自文件夹下创新文件夹并编译。

    cmake ../
    make -j4
    
  • 另外,记得时不时检查一下更新:

    cd pico-sdk
    git pull
    git submodule update
    

程序

stdio

需要包含这个头文件:

#include <stdio.h>

输入输出流可以指定为USB或普通串口,默认通过串口。在CMakeLists.txt中设置

pico_enable_stdio_usb(mylowvel 1)
pico_enable_stdio_uart(mylowvel 0)

来使能USB的IO。使用USB时,可以利用tusb.h内的函数帮助判断USB是否连接成功,从而避免上位机检测不到的问题:

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

程序中使用库函数前,需要先初始化:

stdio_init_all();

其他可用函数同标准C语言中的函数,比如printfgetchar之类的。

GPIO

需要包含这两个头文件:

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

初始化某个GPIO:

gpio_init(GP_id);

设置/查询某个GPIO的输入/输出方向:

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

设置/查询某个GPIO为上拉/下拉输入:

gpio_pull_up(GP_id);
gpio_is_pulled_up(GP_id);  // 返回true/false
gpio_pull_down(GP_id);
gpio_is_pulled_down(GP_id);  // 返回true/false

gpio_set_pulls(GP_id, true, false);  // 第二个参数指定上拉,第三个参数指定下拉

设置/查询某个GPIO的电平:

gpio_put(GP_id, true);  // 或false
gpio_get(GP_id);  // 返回true/false
IRQ中断

需要包含这三个头文件:

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

设置中断处理:

gpio_set_irq_enabled_with_callback(GP_id, 
                                   GPIO_IRQ_EDGE_FALL,  // 或者其他的中断事件,比如GPIO_IRQ_EDGE_RISE等
                                   true,                // true则立即启用
                                   &handlingFunction);

中断处理函数类型:

void handlingFunction(uint gpio, uint32_t events);
PWM

需要包含这些头文件:

#include "pico/stdlib.h"
#include "hardware/gpio.h"
#include "hardware/pwm.h"
#include "hardware/clocks.h"  // 如果未用到系统时钟频率,可忽略

设置指定GPIO为PWM输出:

gpio_set_function(GP_id, GPIO_FUNC_PWM);

设置GPIO频率:

uint wrap;
uint slice_num = pwm_gpio_to_slice_num(GP_id);
pwm_set_wrap(slice_num, wrap);  // 设置频率为 (125M/wrap)Hz

注意:

上面方法设置wrap,最大为65535,如果想设置更小的频率,需要手动设置PWM分频:

uint freq;  // 要设置的频率
uint wrap;  // 顶值
uint times; // 分频系数
uint slice_num = pwm_gpio_to_slice_num(GP_id);
pwm_set_enabled(slice_num, true);  // 使能。好像没这一行也行。
pwm_config config = pwm_get_default_config();  // 利用一个设置结构体来准备接下来的初始化
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);

需要注意,wrap是相对于分频后的时钟而言的。程序中的表达式可以是其他形势,但最终应满足:
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

设置占空比:

pwm_set_gpio_level(GP_id, level);  // 设置占空比为 (level/wrap) × 100%
UART

需要包含这些头文件:

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

初始化:

uart_init(UART_id, 115200);  // UART_id为uart0或uart1,标识符均已定义
gpio_set_function(GP_id, GPIO_FUNC_UART);  // 将符合的TX注册为UART口,需要参照上图
gpio_set_function(GP_id, GPIO_FUNC_UART);  // 将符合的RX注册为UART口,需要参照上图
uart_set_baudrate(UART_id, 115200);  // 设置波特率,默认115200
uart_set_format(UART_id, 8, 1, 0);  // 设置数据位、停止位、校验位,默认8/1/0

查询串口是否可读/可写:

uart_is_readable(UART_id);  // 返回true/false
uart_is_writable(UART_id);  // 返回true/false

读/写串口:

uart_getc(UART_id);  // 返回读到的字符
uart_putc(UART_id, 'c');
uart_puts(UART_id, "string");

编译

在完成上面的SDK配置和程序编写后,就只需注意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
        )

其中,pico_sdk_import.cmake文件可以直接从<PICO_SDK_PATH>/external/pico_sdk_import.cmake复制过去。

之后就是老一套的:

mkdir build
cd build
cmake ..
make -j4

注意:

编译时,所在目录的完整路径名请尽量只含半角英文。已知在路径名存在中文时,make有大概率会报错!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值