diff --git a/part6-breakout/io.h b/part6-breakout/io.h index 2a412e6..ba84877 100644 --- a/part6-breakout/io.h +++ b/part6-breakout/io.h @@ -1,4 +1,5 @@ #define PERIPHERAL_BASE 0xFE000000 +#define SAFE_ADDRESS 0x00210000 // Somewhere safe to store a lot of data void uart_init(); void uart_writeText(char *buffer); diff --git a/part6-breakout/kernel.c b/part6-breakout/kernel.c index 6c972e9..4635de5 100644 --- a/part6-breakout/kernel.c +++ b/part6-breakout/kernel.c @@ -35,10 +35,8 @@ enum { OBJ_BALL = 3 }; -#define OBJS_ADDRESS 0x02100000 // Somewhere safe to store a lot of data - unsigned int numobjs = 0; -struct Object *objects = (struct Object *)OBJS_ADDRESS; +struct Object *objects = (struct Object *)SAFE_ADDRESS; struct Object *ball; struct Object *paddle; diff --git a/part9-sound/README.md b/part9-sound/README.md index 21c0afb..1546961 100644 --- a/part9-sound/README.md +++ b/part9-sound/README.md @@ -28,15 +28,29 @@ Here are a few things I learned on this journey, which helped me along significa And always, always have a browser tab open on the [BCM2711 ARM Peripherals document](https://www.raspberrypi.org/documentation/hardware/raspberrypi/bcm2711/rpi_DATA_2711_1p0.pdf). It's a very handy reference for register addresses and bitmaps etc. +Audio sample format +------------------- +_audio.bin_ is the audio file we'll be playing. Technically speaking, it's 8-bit, unsigned PCM data at 44.1Khz. This is an unusual format in this modern day and age, but it's easily converted using a tool like [ffmpeg](https://ffmpeg.org/). + +To convert from our _.bin_ file to a _.wav_ file that any laptop can play natively, do this: + +`ffmpeg -f u8 -ar 44.1k -ac 2 -i audio.bin audio.wav` + +To convert back to our binary format, do this: + +`ffmpeg -i audio.wav -f u8 -ar 44.1k -ac 2 audio.bin` + +This should help you try the code with your own audio samples! + Testing playback using the CPU and PWM module --------------------------------------------- I knew DMA transfers might be a tricky beast, so I began by just proving I could play audio to the jack output of the Raspberry Pi 4. Looking at `main()`, you'll see that we first call `audio_init()`. This function ensures that PWM1 is correctly mapped. PWM is a technique used to control analogue devices using digital signals. Whilst digital signals are either on (1 - full power) or off (0 - no power), analogue signals may be an infinite number of values between 1 and 0. PWM fakes an analogue signal by applying power in quick pulses/bursts of regulated voltage. The resultant average voltage will end up looking roughly like an analogue signal, despite not being one. Clever, eh? -These pulses/bursts do need to be highly accurate for this trick to work, and so we need a reliable clock source. Just like your kitchen clock ticks every second, so the oscillator on the Raspberry Pi 4 has a regular 'tick' - in this case, 54,000,000 times per second (54Mhz)! Our audio sample is at 44.1Khz though, so we need to 'slow it down'. We do this by first stopping the clock, then setting a clock divisor, setting the PWM range, and enabling the clock again. In my code, I use 2 as the clock divisor (so we're down to 27Mhz) and set the range to 612 (0x264). Essentially, this means that our PWM will move to a new sample every 27,000,000/612 times per second - roughly equivalent to 44.1Khz, which just happens to be the sample rate of the included audio sample _audio.bin_ (I've included _audio.wav_ so you can listen normally on your laptop too!). +These pulses/bursts do need to be highly accurate for this trick to work, and so we need a reliable clock source. Just like your kitchen clock ticks every second, so the oscillator on the Raspberry Pi 4 has a regular 'tick' - in this case, 54,000,000 times per second (54Mhz)! Our audio sample is at 44.1Khz though, so we need to 'slow it down'. We do this by first stopping the clock, then setting a clock divisor, setting the PWM range, and enabling the clock again. In my code, I use 2 as the clock divisor (so we're down to 27Mhz) and set the range to 612 (0x264). Essentially, this means that our PWM module will move to a new sample every 27,000,000/612 times per second - roughly equivalent to 44.1Khz, which just happens to be the sample rate of the included audio sample _audio.bin_ (I've included _audio.wav_ so you can listen normally on your laptop too!). -We then enable the PWM, telling it to wait for sample data on its FIFO input, and we're good to go. Until we start filling the buffer, no audio will play. +We then enable the PWM module, telling it to wait for sample data on its FIFO input, and we're good to go. Until we start filling the buffer, no audio will play. Hopefully you'll notice that we set both channel 0 and channel 1 up similarly. This is because we're working with a stereo sample. @@ -46,7 +60,7 @@ In `playaudio_cpu()` we use the CPU to drive our sample data into the FIFO buffe The code is fairly self-documenting. We essentially check the FIFO buffer isn't full before we send the left channel byte and the right channel byte (stereo, remember!). If we see errors, we clear them as we go. -And that's it... The PWM will pick up the digital data in the buffer and send it, PWM-style (faking an analogue signal), at the right speed (thanks to our clock divisor/PWM range mastery), to the audio jack! +And that's it... The PWM will pick up the digital data in the buffer and send it, PWM-style (faking an analogue signal), at the right speed (thanks to our clock divisor/PWM range mastery), to the audio jack. Todo ----