×

使用Pico的硬件引导选择开关

消耗积分:0 | 格式:zip | 大小:0.00 MB | 2023-06-16

刘埃生

分享资料个

描述

在 Hackaday.io 上寻找项目时,我偶然发现了 Stephen Holdaway 的这个项目(点击)。在这个项目中,他解决了每个双启动用户面临的一个令人沮丧的任务,即在我们想要切换到 Windows 的任何时候,等待从 GRUB 菜单中选择 os (Windows)。他能够添加一个硬件开关,以确定每次打开计算机时要启动的操作系统。

他通过将 STM32 微控制器配置为 USB 大容量存储设备来实现这一点。他在hackaday 帖子(点击)中记录了他通过项目研究和实施的整个旅程。请阅读他的帖子以更好地了解实现。

在这个项目中,我将展示如何将更改移植到 Raspberry Pi Pico。你可以在这个 GitHub Repo (点击)中找到我的版本。

概念

GNU GRUB 是一个在加载任何操作系统之前运行的程序。通过这个菜单,我们可以选择要加载的操作系统。GRUB 提供的模块非常有限。这意味着它无法从通过 USB 连接的微控制器读取数据。但它可以从存储磁盘中读取数据。

因此,我们可以通过将我们的微控制器枚举为大容量存储设备来欺骗 GRUB 从微控制器读取数据。

因此,我们通过tinyUSB库将我们的树莓派 pico 枚举为大容量存储设备,该库将具有文件switch.cfg文件,pico 将向其中写入开关位置,即1 表示 ON 0 表示 OFF

我们必须在 GRUB 中添加一个脚本,它是读取switch.cfg文件并将默认设置0(Ubuntu )/2(Windows) 的函数。

GRUB 在加载时运行我们的自定义脚本,然后通过其 UUID 标识符搜索我们的设备,如果退出则读取switch.cfg文件。获得开关位置后,它分别设置默认的操作系统选择。

总之,

  • pico 会将自己配置为大容量存储设备。
  • grub 菜单调用我们的脚本并询问特定的文件。
  • Pico 通过在switch.cfg文件中添加开关位置来响应读取请求。
  • grub 中的脚本从文件中提取信息并从提取的数据中设置默认选项。

将 Pico 配置为大容量存储设备

我使用 tinyUSB 的cdc_msc示例来实现这一点。该示例将 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: Readme Content -------------//
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"

电路

poYBAGOIKrqASFJGAAAVOmGHIzE439.png
开关连接
 

 

pYYBAGOIOlOAQ1ZHAAoU_Ub3zR4889.jpg
 

读取开关位置并写入文件

现在我们需要读取开关位置并相应地更改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

// read switch value 
uint8_t read_switch_value()
{
    return gpio_get(SWITCH_PIN) ? '1' : '0';
}

int main(void)
{
    gpio_init(SWITCH_PIN);

    //configure pin as INPUT
    gpio_set_dir(SWITCH_PIN, false); 

    //configure pin as PULL_DOWN
    gpio_set_pulls (SWITCH_PIN,false,true);

要将开关位置写入switch.cfg ,我使用了调用read_switch_value函数的readGRUBConfig() ,并使用开关位置设置输出缓冲区。

我发现在读取第三个 block3 时lba设置为 3,因此拦截调用并调用readGrubConfig并传递将复制文件内容的缓冲区。

//-------------------------msc_disk.c---------------------

static char grubConfigStr[] = "set os_hw_switch=0\n";
static void readGrubConfig(uint8_t* output)
{
    // Modify config string with current switch value
    grubConfigStr[sizeof(grubConfigStr)-3] = read_switch_value();
    memcpy(output, &grubConfigStr, sizeof(grubConfigStr));
}

// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void* buffer, uint32_t bufsize)
{
    (void) lun;
    // when reading the file
    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

....
# Look for hardware switch device by its hard-coded filesystem ID
search --no-floppy --fs-uuid --set hdswitch 0000-1234

# If found, read dynamic config file and select appropriate entry for each position
if [ "${hdswitch}" ] ; then
  source ($hdswitch)/switch.cfg

  if [ "${os_hw_switch}" == 0 ] ; then
    # Boot Linux
    set default="0"
  elif [ "${os_hw_switch}" == 1 ] ; then
    # Boot Windows
    set default="2"
  else
    # Fallback to default
    set default="${GRUB_DEFAULT}"
  fi

else
  set default="${GRUB_DEFAULT}"
fi

首先,我们搜索我们的文件系统。GRUB 有一个子命令search就是为了这个。

  • -no-floppy选项防止搜索软盘设备
  • -fs--uuid 0000-1234搜索 UUID 为 0000-1234 的文件系统。

如果找到任何设备,则将找到的第一个设备设置为环境变量的值。

--set hdswitch hdswitch是我们的环境变量,如果找到,则使用磁盘名称进行设置。

接下来,如果设置了hdswitch变量 ,我们将获取 switch.cfg文件,这将创建另一个具有开关位置的环境变量,即 0/1。os_hw_switch

我们读取 的值 os_hw_switch并将默认值分别设置为 0 或 2。0 因为 Ubuntu 在 GRUB 菜单中位于第 0 位,而 windows 位于第 2 位。

最后,如果 没有设置hdswitch ,我们将默认设置为GRUB_DEFAULT。

现在我们需要更新我们的 grub:

$ sudo update-grub

我们完成了。

测试

poYBAGOIOnuAFjStAAoQsrAlGo8584.jpg
 

在连接设备的情况下重新启动系统。

如果一切正常,打开按钮将选择 windows 或 ubuntu(如果关闭)。

pYYBAGOIOn2AZbgqAABpnnVPDEg185.jpg
当开关位置为 1 即 ON
 
pYYBAGOIOoCAGLslAABuKgmMA4w296.jpg
当开关位置为 0 即 OFF
 

 


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

评论(0)
发评论

下载排行榜

全部0条评论

快来发表一下你的评论吧 !