如何在树莓派上运行没有操作系统的 C 程序? [英] How to run a C program with no OS on the Raspberry Pi?
问题描述
我想尝试将 Raspberry Pi 用于一些不同的低级嵌入式应用程序.唯一的问题是,与可用的 AVR 和 PIC 微控制器板不同,Raspberry Pi 通常运行一个操作系统(如 Raspbian),该操作系统将 CPU 时间分配给所有正在运行的程序,这使得它对某些实时应用程序不切实际.
我最近了解到,假设您安装了像 GRUB 这样的引导程序,在 x86 上运行 C 程序
开始.S
.global _start_开始:mov sp, #0x8000主线悬挂:b挂
main.c
#include /* 这不好.任何严重的事情都应该使用计时器* 由董事会提供.但这使代码更简单.*/#define BUSY_WAIT __asm__ __volatile__("")#define BUSY_WAIT_N 0x100000int 主(无效){uint32_t i;/* 在底层,一切都是通过写入魔术内存地址来完成的.硬件供应商提供的设备树文件(dtb/dts),告诉 Linux 内核这些魔法值.*/易失性 uint32_t * const GPFSEL4 = (uint32_t *)0x3F200010;易失性 uint32_t * const GPFSEL3 = (uint32_t *)0x3F20000C;易失性 uint32_t * const GPSET1 = (uint32_t *)0x3F200020;易失性 uint32_t * const GPCLR1 = (uint32_t *)0x3F20002C;*GPFSEL4 = (*GPFSEL4 & ~(7 << 21)) |(1<<21);*GPFSEL3 = (*GPFSEL3 & ~(7 << 15)) |(1<<15);而 (1) {*GPSSET1 = 1 <<(47 - 32);*GPCLR1 = 1<<(35 - 32);for (i = 0; i
ldscript
内存{内存:原点 = 0x8000,长度 = 0x10000}部分{.text : { *(.text*) } >内存.bss : { *(.bss*) } >内存}
make.sh
#!/usr/bin/env bash设置 -edev="${1:-/dev/mmcblk0}"部分="${2:-p1}"part_dev="${dev}${part}"mnt='/mnt/rpi'sudo apt-get install binutils-arm-none-eabi gcc-arm-none-eabi# 生成kernel7.imgarm-none-eabi-as start.S -o start.oarm-none-eabi-gcc -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -c main.c -o main.oarm-none-eabi-ld start.o main.o -T ldscript -o main.elf# 从生成的 elf 文件中获取原始程序集.arm-none-eabi-objcopy main.elf -O binary kernel7.img# 获取固件.那些只是魔法斑点,可能是编译过的# 来自一些我们无法访问的 Broadcom 专有 C 代码.wget -O bootcode.bin https://github.com/raspberrypi/firmware/blob/597c662a613df1144a6bc43e5f4505d83bd748ca/boot/bootcode.bin?raw=truewget -O start.elf https://github.com/raspberrypi/firmware/blob/597c662a613df1144a6bc43e5f4505d83bd748ca/boot/start.elf?raw=true# 准备文件系统.须藤卸载$part_dev"echo 'start=2048, type=c' |须藤 sfdisk "$dev"须藤 mkfs.vfat "$part_dev"须藤 mkdir -p "$mnt"须藤挂载${part_dev}"$mnt"须藤cp kernel7.img bootcode.bin start.elf "$mnt"# 清理.同步须藤 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 设置:如何制作裸机 ARM 程序并在 QEMU 上运行它们? 写入 UART 是从 QEMU 中获取输出的最简单方法.
QEMU 模拟 Raspberry Pi 的效果可以部分推断出:如何使用 QEMU 模拟 Raspberry Pi Raspbian? 因为即使是 Linux 终端也会出现,所以您的裸机很可能也可以工作.
奖金
这是一个 x86 示例:如何在没有操作系统的情况下运行程序?
I'd like to experiment using the Raspberry Pi for some different low level embedded applications. The only problem is that, unlike the AVR and PIC microcontroller boards available, Raspberry Pi typically runs an OS (like Raspbian) that distributes CPU time across all running programs and makes it impractical for certain real time applications.
I've recently learned that, assuming you have a bootloader like GRUB installed, running a C program on x86 (in the form of a kernel) takes very little actual setup, just an assembly program to call the main function and the actual C code.
Is there a way to achieve this with a Raspberry Pi? It'd be a great way to learn about low level ARM programming, and it already has a few complex peripherals to mess around with (USB, Ethernet, etc.)
Fully automated minimal bare metal blinker example
Tested on Ubuntu 16.04 host, 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), but this is a minimal easy to setup hello world to get you started quickly.
Usage:
Insert SD card on host
Make the image:
./make.sh /dev/mmblck0 p1
Where:
/dev/mmblck0
is the device of the SD cardp1
is the first partition of the device (/dev/mmblck0p1
)
Inset SD card on PI
Turn power off and on
GitHub upstream: https://github.com/cirosantilli/raspberry-pi-bare-metal-blinker/tree/d20f0337189641824b3ad5e4a688aa91e13fd764
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 friendly bare metal examples
The problem with the blinker is that it is hard to observe LEDs in QEMU: https://raspberrypi.stackexchange.com/questions/56373/is-it-possible-to-get-the-state-of-the-leds-and-gpios-in-a-qemu-emulation-like-t
Here I describe some bare metal QEMU setups that may be of interest: How to make bare metal ARM programs and run them on QEMU? Writing to the UART is the easiest way to get output out from QEMU.
How well QEMU simulates the Raspberry Pi can be partially inferred from: How to emulate Raspberry Pi Raspbian with QEMU? Since even the Linux terminal shows up, it is likely that your baremetal stuff will also work.
Bonus
Here is an x86 example for the curious: How to run a program without an operating system?
这篇关于如何在树莓派上运行没有操作系统的 C 程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!