使用 Pico 的硬件启动选择开关
预先选择要启动的操作系统,甚至在通过切换开关打开计算机之前。现在您不必等待选择操作系统。
图>故事
在 Hackaday.io 的项目中徘徊,我偶然发现了 Stephen Holdaway 的这个项目(点击)。在这个项目中,他解决了每一个双启动用户面临的一个令人沮丧的任务,当我们想切换到windows时,他总是坐着等待从GRUB菜单中选择os(Windows)。他能够添加一个硬件开关来确定每次打开计算机时要启动的操作系统。
他通过将 STM32 微控制器配置为 USB 大容量存储设备来实现这一点。他在 hackaday 帖子(点击)中记录了他整个项目研究和实施的过程。请阅读他的帖子以更好地了解实现。
在这个项目中,我将展示我如何设法将更改移植到 Raspberry Pi Pico。你可以在这个 GitHub 存储库中找到我的版本(点击)。
概念
GNU GRUB 是一个在加载任何操作系统之前运行的程序。通过这个菜单,我们可以选择加载哪个操作系统。 GRUB 提供的模块非常有限。这意味着它无法从通过 USB 连接的微控制器读取数据。但它可以从存储磁盘读取数据。
因此,我们可以通过将我们的微控制器枚举为大容量存储设备来欺骗 GRUB 从微控制器读取数据。
因此,我们将我们的 raspberry pi pico 枚举为一个大容量存储设备,通过 TinyUSB 库,它将有一个文件 switch.cfg 文件,pico 会将开关位置写入该文件,即 1 表示 ON 0 表示 OFF .
我们必须在 GRUB 中添加一个脚本,即读取 switch.cfg 文件并设置 default 的函数 0(Ubuntu)/2(Windows)。
GRUB 加载时,运行我们的自定义脚本,它依次通过 UUID 标识符搜索我们的设备,如果退出,则读取 switch.cfg 文件。获取开关位置后分别设置默认的os选择。
总之,
- pico 会将自身配置为大容量存储设备。
- grub 菜单调用我们的脚本并请求特定文件。
- Pico 通过在 switch.cfg 文件中添加开关位置来响应读取请求。
- grub 中的脚本从文件中提取信息并从提取的数据中设置默认选项。
将 Pico 配置为大容量存储设备
我使用了 cdc_msc tinyUSB 的示例来实现这一点。该示例将 pico 配置为大容量存储设备并创建 FAT12 文件系统并枚举 README.txt 文件。
我将 README.txt 更改为 switch.cfg 并在文件中添加了“set os_hw_switch=0\n”行。
#define SWITCH_CFG_CONTENTS \
"set os_hw_switch=0\n"
...
//----- -------- Block3:自述内容-------------//
SWITCH_CFG_CONTENTS
现在我们已将 pico 配置为大容量存储设备。将uf2文件复制到pico后,枚举为存储设备。我们将需要 GRUB 脚本的设备的 UUID id,即 UUID="0000-1234"。
$ sudo blkid
...
/dev/sda:SEC_TYPE="msdos" LABEL_FATBOOT="TinyUSB MSC" LABEL="TinyUSB MSC" UUID="0000-1234" BLOCK_SIZE="512" TYPE="vfat"
电路
图> 图>读取开关位置并写入文件
现在我们需要读取开关位置并相应地更改 switch.cfg 文件的内容,即
- 如果开关打开:设置 os_hw_switch=1\n
- 如果开关关闭:设置 os_hw_switch=0\n
我已经使用GPIO_PIN 28作为开关管脚,设置为下拉。
read_switch_value 返回开关位置,即“1”打开(拉高)和“0”关闭(拉低)。
//-------------------------main.c-------- -------------
#define SWITCH_PIN 28
// 读取开关值
uint8_t read_switch_value()
{
返回 gpio_get(SWITCH_PIN) ? '1' :'0';
}
int main(void)
{
gpio_init(SWITCH_PIN);
//配置引脚为输入
gpio_set_dir(SWITCH_PIN, false);
//配置引脚为PULL_DOWN
gpio_set_pulls (SWITCH_PIN,false,true);
要将开关位置写入 switch.cfg,我使用了 readGRUBConfig() 它调用 read_switch_value 功能,并用开关位置设置输出缓冲区。
我发现在阅读第三个block3时lba 设置为 3,因此拦截调用并调用 readGrubConfig 并传递文件内容将被复制的缓冲区。
//-------------------------msc_disk.c-------- -------------
static char grubConfigStr[] ="set os_hw_switch=0\n";
static void readGrubConfig(uint8_t* output)
{
// 用当前开关值修改配置字符串
grubConfigStr[sizeof(grubConfigStr)-3] =read_switch_value();
memcpy(output, &grubConfigStr, sizeof(grubConfigStr));
}
// 收到 READ10 命令时调用回调。
// 将磁盘数据复制到缓冲区(最多 bufsize)并返回复制的字节数。
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
{
(void) lun;
//读取文件时
if(lba ==3){
readGrubConfig(buffer);
return bufsize;
}
...
...
}
编译 Pico 代码
我们需要添加 pico stdlib 到我们的代码以获取 gpio 针访问。
//-------------------------main.c-------- ---------------------------
#include
#include
#include
#include "bsp/board.h"
#include "tusb.h"
...
#include "pico/ stdlib.h"
制作项目:
$ mkdir build
$ cd build
$ cmake ..
$ make
配置 GRUB 读取文件内容
我已经在我的 Ubuntu 20.10 中添加了这些更改。
$ sudo vim /etc/grub.d/40_custom
....
# 通过硬编码查找硬件开关设备filesystem ID
search --no-floppy --fs-uuid --set hdswitch 0000-1234
#如果找到,读取动态配置文件并为每个位置选择合适的条目
if [ "${hdswitch}" ]; then
source ($hdswitch)/switch.cfg
if [ "${os_hw_switch}" ==0 ]; then
# 启动 Linux
set default="0"
elif [ "${os_hw_switch}" ==1 ]; then
# 启动 Windows
set default="2"
else
# 回退到默认值
set default="${GRUB_DEFAULT}"
fi
else
set default="${GRUB_DEFAULT}"
fi
首先,我们搜索我们的文件系统。 GRUB 有一个子命令 <em>search 仅此而已。
- -无软盘 选项阻止搜索软盘设备
- -fs–uuid 0000-1234 搜索 UUID 为 0000-1234 的文件系统。
如果找到任何设备,则将找到的第一个设备设置为环境变量的值。
–设置 hdswitch hdswitch 是我们的环境变量,如果找到则设置为磁盘名称。
接下来,如果 hdswitch 设置变量,这会创建另一个环境变量 os_hw_switch
开关位置,即 0/1。
我们读取 os_hw_switch 的值
并将默认值分别设置为 0 或 2。 0 因为在 GRUB 菜单中,Ubuntu 位于第 0 位,而 windows 位于第 2 位。
最后,如果hdswitch 未设置,我们将默认设置为 GRUB_DEFAULT。
现在我们需要更新我们的 grub:
$ sudo update-grub
来源: 使用 Pico 的硬件启动选择开关
制造工艺