[树莓派pico][PIO]编程小白继续调教AI
本帖最后由 量子隧道 于 2025-2-15 17:51 编辑周末,继续学习PIO编程。PIO这一想法实在太强了。虽然PIO其实指令不复杂,“指令集”只有9条指令,“指令内存”只有32条,但用好了了的话可以在简单场合代替CPLD,系统可以当作“MCU+PLD”的组合,想象空间很大。
问:请举一两个示例,在arduino下给树莓派pico的PIO编程的
deepseek给了我两个程序,其中一个是这样的:
#include <Arduino.h>
// PIO程序,用于生成PWM信号
const uint pio_program_instructions[] = {
0xE081, // set pindirs, 1
0xE101, // set pins, 1
0x0001, // jmp 1
};
void setup() {
// 初始化PIO
PIO pio = pio0;
uint offset = pio_add_program(pio, &pio_program_instructions);
uint sm = pio_claim_unused_sm(pio, true);
pio_sm_config c = pio_get_default_sm_config();
sm_config_set_wrap(&c, offset, offset + 2);
sm_config_set_set_pins(&c, 25, 1); // 使用GPIO 25
pio_sm_init(pio, sm, offset, &c);
pio_sm_set_enabled(pio, sm, true);
}
void loop() {
// 主循环不需要做任何事情
}
看不出啥毛病,然而编译时不通过,说pio_add_program(pio, &pio_program_instructions)函数参量不对。想起以前坐地铁时隐约看到过,第二个变量是个结构体,里边不止包含代码,还包含汇编程序参数。于是继续追问:
问:pio_add_program()装载的是PIO程序结构体,不仅仅是代码结构体。请重新修改一下程序。
AI的认错态度很好,也很厉害,知道我在说啥,立刻更改:
你是对的,pio_add_program() 需要装载的是一个完整的 PIO 程序结构体,而不仅仅是代码指令。PIO 程序结构体包含了指令数组以及相关的元数据(如指令长度等)。以下是修正后的示例程序,确保正确使用 PIO 程序结构体。
。。。
const struct pio_program pio_program = {
.instructions = pio_program_instructions,
.length = 3, // 指令数量
.origin = -1, // 自动分配起始地址
};
。。。
uint offset = pio_add_program(pio, &pio_program); // 加载 PIO 程序
。。。
厉害了,立刻改正。编译通过。
但是程序运行后,PWM对应的灯不亮。这一定是缺了什么配置。继续问:
问:请仔细看看对状态机的配置还缺了什么?
问的不好,不具体,AI于是也跟我胡诌了一通,结果当然是徒劳无功。
于是干脆让它给我查代码(查它自己生成的代码有没有问题):
问:下面这段程序不工作,可能是什么原因?
#include <Arduino.h>
#include "pico/stdlib.h"
#include "hardware/gpio.h"
// PIO 程序指令
const uint16_t pio_program_instructions[] = {
0xE081, // set pindirs, 1
0xE101, // set pins, 1
0x0001, // jmp 1
};
// 定义 PIO 程序
const struct pio_program pio_program = {
.instructions = pio_program_instructions,
.length = 3, // 指令数量
.origin = -1, // 自动分配起始地址
};
void setup() {
// 初始化 PIO
PIO pio = pio0; // 使用 PIO 0
uint offset = pio_add_program(pio, &pio_program); // 加载 PIO 程序
uint sm = pio_claim_unused_sm(pio, true); // 分配一个状态机
pio_sm_config c = pio_get_default_sm_config(); // 获取默认状态机配置
// 配置状态机
sm_config_set_wrap(&c, offset, offset + pio_program.length - 1); // 设置循环范围
sm_config_set_set_pins(&c, 2, 1); // 设置 GPIO 2 为输出
sm_config_set_out_pins(&c, 2, 1);
pio_sm_init(pio, sm, offset, &c); // 初始化状态机
pio_sm_set_enabled(pio, sm, true); // 启用状态机
}
void loop() {
// 主循环不需要做任何事情
}
这回AI终于不蒙圈了,直接指出我缺了这个配置:
gpio_init(2); // 初始化 GPIO 2
gpio_set_function(2, GPIO_FUNC_PIO0); // 将 GPIO 2 配置为 PIO 功能
又把我的PIO汇编程序复杂化为如下代码:
const uint16_t pio_program_instructions[] = {
0xE081, // set pindirs, 1 (设置引脚方向为输出)
0xE101, // set pins, 1 (设置引脚为高电平)
0x80A0, // pull block (从 FIFO 中读取延迟时间)
0x6020, // out x, 32 (将延迟时间加载到 X 寄存器)
0x0042, // jmp x--, 2 (根据 X 寄存器的值循环)
0xE100, // set pins, 0 (设置引脚为低电平)
0x80A0, // pull block (从 FIFO 中读取延迟时间)
0x6020, // out x, 32 (将延迟时间加载到 X 寄存器)
0x0047, // jmp x--, 7 (根据 X 寄存器的值循环)
0x0001, // jmp 1 (跳转到指令 1,重新开始)
};
并且告诉我,我的MCU程序也不能啥事都不做,要在setup()里喂PIO,给PIO的FIFO写脉宽参数,来驱动PIO的PWM时间。
pio_sm_put_blocking(pio, sm, 100000); // 高电平持续时间
pio_sm_put_blocking(pio, sm, 100000); // 低电平持续时间
这回灯亮了。早知如此何必当初,那个破程序也是deepseek给我的,现在它自己把自己给的程序改好了。为啥当初就不给我一个好的呢?
当然,这个亮,一看就不对,用示波器一看,是恒高,不是PWM。意思是,它work是work了,程序逻辑还是错的。
本小白自己能看出来的一个明显错误是:从汇编循环看,PIO需要不断读FIFO。这意味着需要MCU程序要不停地喂FIFO,不可能只在setup里喂一次。
另外,一看机器码后边跟着的汇编注释,那两个jmp的位置就不对,不可能跳到第2行和第7行。于是阅读RP2040手册,把PIO汇编码手动改为如下:
set pindirs, 1
set pins, 1
pull block
out x, 32
high:
jmp x--, high
set pins, 0
pull block
out x, 32
low:
jmp x--, low
jmp 1
并且在主程序loop()中加入不断更改PWM脉宽和喂PIO状态机的代码:
void loop() {
// 向 FIFO 写入延迟时间
pio_sm_put_blocking(pio, sm, ontime); // 高电平持续时间
pio_sm_put_blocking(pio, sm, (10000-ontime)); // 低电平持续时间
ontime = (ontime+500)%10000;
Serial.println(ontime);
}
主程序和PIO汇编程序的运转和交互,终于如我所愿了。
看来,AI还是没逻辑,它给我的代码,就是网上抄来的人云亦云的玩意儿,自己不细细检查,根据底层逻辑重新改一遍,是不能运转,并且埋下很多雷的。
全文完。 PIO 本来就是相对小众的东西,网上的资源有限,AI 训练素材自然也很少、出错的概率必然更高。
俺之前一直在说,目前的 AI,至少在编程方面,还只能是效率工具的定位。
那些惊呼 AI 取代码农的,基本都是没写过几行代码的 ...... :lol scoopydoo 发表于 2025-2-15 17:59
PIO 本来就是相对小众的东西,网上的资源有限,AI 训练素材自然也很少、出错的概率必然更高。
俺之前一 ...
不过呢,它其实对我有帮助。我今天这一堆提问,不是向它学习编程的,是想向它学习PIO的驱动结构,也即“需要做哪些必须的事情,以让PIO转起来”。虽然即使在这一目标下,它给我的东西也是残缺不全的,但是经过一阵追问它最终给出了能跑起来的版本。
只是,指望不学编程,让它给你编出能转的程序,是不太现实的。 本帖最后由 scoopydoo 于 2025-2-15 20:22 编辑
量子隧道 发表于 2025-2-15 19:22
不过呢,它其实对我有帮助。我今天这一堆提问,不是向它学习编程的,是想向它学习PIO的驱动结构,也即“ ...
确实帮助非常大,极大地提高了学习效率,俺已经离不开 AI 编程小助手了,花了点儿小钱订阅了 Github Copilot :lol 回过头来再看上次坐地铁时DeepSeek给我的PIO例程,简直就是儿戏。虽然程序看起来像模像样的,但是离功能正常还差了十万八千里。
http://www.crystalradio.cn/forum.php?mod=viewthread&tid=2179564&extra=page%3D1 scoopydoo 发表于 2025-2-15 20:20
确实帮助非常大,极大地提高了学习效率,俺已经离不开 AI 编程小助手了,花了点儿小钱订阅了 Github Co ...
不知道老兄的Copilot在编程辅助方面表现如何。今早试了试同样的PIO编程问题提给文心一言和Chatgpt,这俩AI的回答都远不及Deepseek。 量子隧道 发表于 2025-2-17 08:58
不知道老兄的Copilot在编程辅助方面表现如何。今早试了试同样的PIO编程问题提给文心一言和Chatgpt,这俩A ...
俺并没有感受到 Copilot 有多出色,甚至俺切换了它可以调用的几个 AI 后端,也没感受到什么区别。
俺觉得 AI 的回答质量主要取决于两个方面,一是越热门的话题准确率越高,二是提问的精确性对结果的影响非常大。
Copilot 的主要优势是它和开发环境紧密结合的时候,其方便程度远远高于之前的“面向搜索引擎编程”和“面向Stackoverflow编程”,虽然都属于广义上的“复制/粘贴”。
据说 Deepseek 也已经有了在各种开发环境下的插件,但是俺没试过,不敢妄加评论。 刚刚让 Copilot 给我写一个用 PIO 功能输出 1MHz 方波的小程序,他居然给出了如下的代码:
.program pwm
.wrap_target
set pins, 1 // Set pin high and delay for 62 cycles (1 + 61)
set pins, 0 // Set pin low and delay for 62 cycles (1 + 61)
.wrap
这特么神马虎狼之辞啊,PIO 指令的延迟时间必须小于等于 31,最基本的东西都能搞错!;P
scoopydoo 发表于 2025-2-17 16:49
刚刚让 Copilot 给我写一个用 PIO 功能输出 1MHz 方波的小程序,他居然给出了如下的代码:
是的,delay域最多给了5bit。
另外以它的无逻辑,不知是否会在给出的例程里,包含PIO的SM的时钟分频配置代码。 量子隧道 发表于 2025-2-17 18:52
是的,delay域最多给了5bit。
另外以它的无逻辑,不知是否会在给出的例程里,包含PIO的SM的时钟分频配置 ...
好像并没有设置 SM 的时钟,用的默认设置吧
页:
[1]