如何 运行 一个在 Raspberry Pi 上没有 OS 的 C 程序?
How to run a C program with no OS on the Raspberry Pi?
我想尝试将 Raspberry Pi 用于一些不同的低级嵌入式应用程序。唯一的问题是,与可用的 AVR 和 PIC 微控制器板不同,Raspberry Pi 通常运行一个 OS(如 Raspbian),将 CPU 时间分配给所有 运行 程序并使其对于某些实时应用程序不切实际。
我最近了解到,假设您安装了像 GRUB 这样的引导加载程序,运行 x86 上的 C 程序 (in the form of a kernel) 只需要很少的实际设置,只需一个汇编程序来调用 main函数和实际的 C 代码。
有没有办法用Raspberry Pi实现这个?
这将是了解低级 ARM 编程的好方法,并且它已经有一些复杂的外围设备可供使用(USB、以太网等)
虽然 Pi 上可以使用裸机,但我会避免使用它,因为 Linux 变得如此轻便,可以为您处理一大堆东西。
如果您仍想学习裸机知识,这里有一个入门教程:http://www.valvers.com/open-software/raspberry-pi/step01-bare-metal-programming-in-cpt1/
综上所述,我会加载您最喜欢的嵌入式 linux 发行版(根据您的要求,可能首选已打补丁的 RT)并称其为好。
https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ 是一个很好的教程,因为他们会告诉你在裸机上 运行 代码的最好的快速和肮脏的方法是劫持 linux 发行版,到这样做,只需编译为 kernel.img(使用适当的体系结构选项)并使用它来替换 linux 发行版中的现有版本
对于本教程的这一部分,您可以转到:
https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok01.html#pitime
Pi 对于您想要做的事情可能有点次优,因为 SoC 设计使得 ARM CPU 是第二个-class 公民 - 这意味着有一些箍跳过去获取裸机程序运行。
但是,您可以稍微作弊并使用 U-Boot API 让您访问 U-Boot 提供的一些功能,但能够在旁边添加您自己的功能。
全自动最小裸机信号灯示例
在 Ubuntu 16.04 主机上测试,Raspberry Pi 2.
https://github.com/dwelch67/raspberrypi is the most comprehensive example set I've seen to date (previously mentioned on this now deleted answer),但这是一个易于设置的 hello world,可帮助您快速入门。
用法:
在主机上插入 SD 卡
制作图片:
./make.sh /dev/mmblck0 p1
其中:
/dev/mmblck0
是SD卡的设备
p1
是设备的第一个分区(/dev/mmblck0p1
)
在 PI 上插入 SD 卡
关闭电源再打开
start.S
.global _start
_start:
mov sp, #0x8000
bl main
hang:
b hang
main.c
#include <stdint.h>
/* This is bad. Anything remotely serious should use timers
* provided by the board. But this makes the code simpler. */
#define BUSY_WAIT __asm__ __volatile__("")
#define BUSY_WAIT_N 0x100000
int main( void ) {
uint32_t i;
/* At the low level, everything is done by writing to magic memory addresses.
The device tree files (dtb / dts), which are provided by hardware vendors,
tell the Linux kernel about those magic values. */
volatile uint32_t * const GPFSEL4 = (uint32_t *)0x3F200010;
volatile uint32_t * const GPFSEL3 = (uint32_t *)0x3F20000C;
volatile uint32_t * const GPSET1 = (uint32_t *)0x3F200020;
volatile uint32_t * const GPCLR1 = (uint32_t *)0x3F20002C;
*GPFSEL4 = (*GPFSEL4 & ~(7 << 21)) | (1 << 21);
*GPFSEL3 = (*GPFSEL3 & ~(7 << 15)) | (1 << 15);
while (1) {
*GPSET1 = 1 << (47 - 32);
*GPCLR1 = 1 << (35 - 32);
for (i = 0; i < BUSY_WAIT_N; ++i) { BUSY_WAIT; }
*GPCLR1 = 1 << (47 - 32);
*GPSET1 = 1 << (35 - 32);
for (i = 0; i < BUSY_WAIT_N; ++i) { BUSY_WAIT; }
}
}
ldscript
MEMORY
{
ram : ORIGIN = 0x8000, LENGTH = 0x10000
}
SECTIONS
{
.text : { *(.text*) } > ram
.bss : { *(.bss*) } > ram
}
make.sh
#!/usr/bin/env bash
set -e
dev="${1:-/dev/mmcblk0}"
part="${2:-p1}"
part_dev="${dev}${part}"
mnt='/mnt/rpi'
sudo apt-get install binutils-arm-none-eabi gcc-arm-none-eabi
# Generate kernel7.img
arm-none-eabi-as start.S -o start.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c main.c -o main.o
arm-none-eabi-ld start.o main.o -T ldscript -o main.elf
# Get the raw assembly out of the generated elf file.
arm-none-eabi-objcopy main.elf -O binary kernel7.img
# Get the firmware. Those are just magic blobs, likely compiled
# from some Broadcom proprietary C code which we cannot access.
wget -O bootcode.bin https://github.com/raspberrypi/firmware/blob/597c662a613df1144a6bc43e5f4505d83bd748ca/boot/bootcode.bin?raw=true
wget -O start.elf https://github.com/raspberrypi/firmware/blob/597c662a613df1144a6bc43e5f4505d83bd748ca/boot/start.elf?raw=true
# Prepare the filesystem.
sudo umount "$part_dev"
echo 'start=2048, type=c' | sudo sfdisk "$dev"
sudo mkfs.vfat "$part_dev"
sudo mkdir -p "$mnt"
sudo mount "${part_dev}" "$mnt"
sudo cp kernel7.img bootcode.bin start.elf "$mnt"
# Cleanup.
sync
sudo umount "$mnt"
QEMU 友好的裸机示例
闪光灯的问题是在 QEMU 中很难观察到 LED:https://raspberrypi.stackexchange.com/questions/56373/is-it-possible-to-get-the-state-of-the-leds-and-gpios-in-a-qemu-emulation-like-t
在这里,我描述了一些可能感兴趣的裸机 QEMU 设置:写入 UART 是从 QEMU 获取输出的最简单方法。
QEMU 模拟 Raspberry Pi 的效果可以从以下部分推断出来: 因为即使 Linux 终端出现了,很可能你的裸机也能工作。
奖金
这里有一个 x86 示例供好奇者参考:How to run a program without an operating system?
我想尝试将 Raspberry Pi 用于一些不同的低级嵌入式应用程序。唯一的问题是,与可用的 AVR 和 PIC 微控制器板不同,Raspberry Pi 通常运行一个 OS(如 Raspbian),将 CPU 时间分配给所有 运行 程序并使其对于某些实时应用程序不切实际。
我最近了解到,假设您安装了像 GRUB 这样的引导加载程序,运行 x86 上的 C 程序 (in the form of a kernel) 只需要很少的实际设置,只需一个汇编程序来调用 main函数和实际的 C 代码。
有没有办法用Raspberry Pi实现这个? 这将是了解低级 ARM 编程的好方法,并且它已经有一些复杂的外围设备可供使用(USB、以太网等)
虽然 Pi 上可以使用裸机,但我会避免使用它,因为 Linux 变得如此轻便,可以为您处理一大堆东西。
如果您仍想学习裸机知识,这里有一个入门教程:http://www.valvers.com/open-software/raspberry-pi/step01-bare-metal-programming-in-cpt1/
综上所述,我会加载您最喜欢的嵌入式 linux 发行版(根据您的要求,可能首选已打补丁的 RT)并称其为好。
https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ 是一个很好的教程,因为他们会告诉你在裸机上 运行 代码的最好的快速和肮脏的方法是劫持 linux 发行版,到这样做,只需编译为 kernel.img(使用适当的体系结构选项)并使用它来替换 linux 发行版中的现有版本 对于本教程的这一部分,您可以转到: https://www.cl.cam.ac.uk/projects/raspberrypi/tutorials/os/ok01.html#pitime
Pi 对于您想要做的事情可能有点次优,因为 SoC 设计使得 ARM CPU 是第二个-class 公民 - 这意味着有一些箍跳过去获取裸机程序运行。
但是,您可以稍微作弊并使用 U-Boot API 让您访问 U-Boot 提供的一些功能,但能够在旁边添加您自己的功能。
全自动最小裸机信号灯示例
在 Ubuntu 16.04 主机上测试,Raspberry Pi 2.
https://github.com/dwelch67/raspberrypi is the most comprehensive example set I've seen to date (previously mentioned on this now deleted answer),但这是一个易于设置的 hello world,可帮助您快速入门。
用法:
在主机上插入 SD 卡
制作图片:
./make.sh /dev/mmblck0 p1
其中:
/dev/mmblck0
是SD卡的设备p1
是设备的第一个分区(/dev/mmblck0p1
)
在 PI 上插入 SD 卡
关闭电源再打开
start.S
.global _start
_start:
mov sp, #0x8000
bl main
hang:
b hang
main.c
#include <stdint.h>
/* This is bad. Anything remotely serious should use timers
* provided by the board. But this makes the code simpler. */
#define BUSY_WAIT __asm__ __volatile__("")
#define BUSY_WAIT_N 0x100000
int main( void ) {
uint32_t i;
/* At the low level, everything is done by writing to magic memory addresses.
The device tree files (dtb / dts), which are provided by hardware vendors,
tell the Linux kernel about those magic values. */
volatile uint32_t * const GPFSEL4 = (uint32_t *)0x3F200010;
volatile uint32_t * const GPFSEL3 = (uint32_t *)0x3F20000C;
volatile uint32_t * const GPSET1 = (uint32_t *)0x3F200020;
volatile uint32_t * const GPCLR1 = (uint32_t *)0x3F20002C;
*GPFSEL4 = (*GPFSEL4 & ~(7 << 21)) | (1 << 21);
*GPFSEL3 = (*GPFSEL3 & ~(7 << 15)) | (1 << 15);
while (1) {
*GPSET1 = 1 << (47 - 32);
*GPCLR1 = 1 << (35 - 32);
for (i = 0; i < BUSY_WAIT_N; ++i) { BUSY_WAIT; }
*GPCLR1 = 1 << (47 - 32);
*GPSET1 = 1 << (35 - 32);
for (i = 0; i < BUSY_WAIT_N; ++i) { BUSY_WAIT; }
}
}
ldscript
MEMORY
{
ram : ORIGIN = 0x8000, LENGTH = 0x10000
}
SECTIONS
{
.text : { *(.text*) } > ram
.bss : { *(.bss*) } > ram
}
make.sh
#!/usr/bin/env bash
set -e
dev="${1:-/dev/mmcblk0}"
part="${2:-p1}"
part_dev="${dev}${part}"
mnt='/mnt/rpi'
sudo apt-get install binutils-arm-none-eabi gcc-arm-none-eabi
# Generate kernel7.img
arm-none-eabi-as start.S -o start.o
arm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c main.c -o main.o
arm-none-eabi-ld start.o main.o -T ldscript -o main.elf
# Get the raw assembly out of the generated elf file.
arm-none-eabi-objcopy main.elf -O binary kernel7.img
# Get the firmware. Those are just magic blobs, likely compiled
# from some Broadcom proprietary C code which we cannot access.
wget -O bootcode.bin https://github.com/raspberrypi/firmware/blob/597c662a613df1144a6bc43e5f4505d83bd748ca/boot/bootcode.bin?raw=true
wget -O start.elf https://github.com/raspberrypi/firmware/blob/597c662a613df1144a6bc43e5f4505d83bd748ca/boot/start.elf?raw=true
# Prepare the filesystem.
sudo umount "$part_dev"
echo 'start=2048, type=c' | sudo sfdisk "$dev"
sudo mkfs.vfat "$part_dev"
sudo mkdir -p "$mnt"
sudo mount "${part_dev}" "$mnt"
sudo cp kernel7.img bootcode.bin start.elf "$mnt"
# Cleanup.
sync
sudo umount "$mnt"
QEMU 友好的裸机示例
闪光灯的问题是在 QEMU 中很难观察到 LED:https://raspberrypi.stackexchange.com/questions/56373/is-it-possible-to-get-the-state-of-the-leds-and-gpios-in-a-qemu-emulation-like-t
在这里,我描述了一些可能感兴趣的裸机 QEMU 设置:
QEMU 模拟 Raspberry Pi 的效果可以从以下部分推断出来:
奖金
这里有一个 x86 示例供好奇者参考:How to run a program without an operating system?