“CrunkyOS”: sound from SD card now works on #RaspberryPi0

I’m putting together a very crude unikernel-type system for the Raspberry Pi 0. I’m really pleased with my progress: I can read a song stored on an SD card into memory and play it via PWM.

Here’s the code for the project:

#include <assert.h>
#include <stdio.h>
#include <bcm2835.h>
#include <../pico/fat32.h>

// PWM output on RPi Plug P1 pin 12 (which is GPIO pin 18)
// in alt fun 5.
// Note that this is the _only_ PWM pin available on the RPi IO headers
#define PIN RPI_GPIO_P1_12
// and it is controlled by PWM channel 0
#define PWM_CHANNEL 0
// This controls the max range of the PWM signal
#define RANGE 256

uint8_t* song;
uint32_t len;

void kernel_main(void)
	puts("\nPlaying song with PWM audio");

	file32_t file;
	char cooked_name[12];
	canfile(cooked_name, "song.raw");
	file32_init(&file, cooked_name);
	printf("Song %s\n", file32_found(&file) ? "found" : "unfound");
	len = file32_size(&file);
	song = malloc(len);


	// Set the output pin to Alt Fun 5, to allow PWM channel 0 to be output there
	bcm2835_gpio_fsel(PIN, BCM2835_GPIO_FSEL_ALT5);

	// PWM base frequency is 19.2MHz
	// freq = 19'200'000/DIVIDER/RANGE = 19'200'000/4/256 = 18750Hz
	bcm2835_pwm_set_mode(PWM_CHANNEL, 1, 1);
	bcm2835_pwm_set_range(PWM_CHANNEL, RANGE);

	uint32_t offset = 0;
	while(file32_read(&file, song + offset))
		offset += 512;
	puts("Song finished reading.");

	// Vary the PWM m/s ratio between 1/RANGE and (RANGE-1)/RANGE
	int idx = 0;
	//int data = 1;
	while (1)
		unsigned char data = song[idx++];
		if(idx == len) idx = 0;

		bcm2835_pwm_set_data(PWM_CHANNEL, data);


The song plays a little slowly because the delay is not properly calibrated. Ideally I’d use interrupts anyway, which is also available for the unikernel.

There’s a lot of cobbled-together code that makes up the kernel. The BCM2835 was adapted from the Linux version. The SD card library is by “bzt”. It seems rather slower than I would expect (by at least an order of magnitude, I would say), which is unfortunate. It prints debug messages as it loads in the file. For some bizarre reason I have to leave the debugging in, or the module won’t work. I’ve had a look at the debugging routines, and I cannot see anything at all that suggests that they cannot be dispensed with.

I have written the FAT32 driver myself. It is read-only, with some limitations on the directory size. It uses 8.3 file format only.

Crunky, so far, uses the simplest thing that could possibly work. I kid you not. malloc() just takes a hunk of heap and moves the bottom of the heap up. There is no free().

Starting from a few days ago, I decided to abandon the idea of making Crunky programmable in C++. I had it working before – except for exceptions – but it required some infrastructure that was just too fiddly to maintain when I returned to the project after some absence.

I also abandoned the idea of using a third-party C library like nano or newlib. Integration was a faff the last time I tried it. When I upgraded my Debian system I found that newlib no longer compiled in with it properly. Rather than spend time trying to reintegrate it, I decided just to throw the baby out with the bathwater. I don’t need the whole C library. Many functions aren’t that difficult to write, anyway.

I’ve used “tinyprint” to implement things like printf. It doesn’t handle the printing of floats, but I can live with that for now. Maybe I’ll have a go at implementing it if I feel the urge. There is also other code that I’ve integrated from other sites, so it’s not as though I have insisted on writing everything from scratch.

The framebuffer works, which is quite nice. You can print and draw to it. I actually communicate via the serial port, chiefly because I can’t get a keyboard working. USB is a whole complex ball of wax, and I’ve always ended up abandoning any attempts at getting it working. I’m thinking of cheating anyway. Maybe I could set up an ESP32 to log into the Pi 0 over Wifi, or maybe try getting tinyusb working on a Pico and using it to interface with the serial port on the Pi 0.

Talking of the Pico: it has been frustrating for me trying to get real-time audio on it. I had written an SD card reader using SPI. The sound works, but I noticed some fluttering on the audio. I am pretty sure that the SPI is causing some problems for the audio interrupt. I know this because I wrote a project to transfer files from an SD card to the Pico’s flash. When I play audio from the flash, the fluttering goes away.

Problems, problems, problems.

I was thinking about evaluating some real-time systems for the Pi 0. The ones that are on my radar are: ChibiOS, RT-Thread, RTEMS, and maybe ZephyrOS or RIOT. RT-Thread recently announced that they were putting out a real-time system, which seemed like Linux but real-time. I noticed that RT-Thread also included support for the Raspberry Pi 2. This is pretty exciting, because if it supports the Pi 2, it should be fairly easy to support the Pi 0. Theoretically. I had reported on a previous occasion that RT-Thread didn’t seem ready for primetime. “It compiles for me” seems to be the level of quality assurance that goes on in that project.

I do face dipping into OS projects with some trepidation. There’s usually a steep learning curve, all sorts of pitfalls and traps in setting the thing up, and there are far from any guarantees that the game is worth the candle.

But anyhoo, I’m excited about getting my song player working on the Pi. Onwards and upwards!

About mcturra2000

Computer programmer living in Scotland.
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s