Hi to all and welcome back!
the finish line is getting closer and closer, so It's about time to finalize things and edit some documentation for this project. I am completing my project with the last steps: Audio and Capsense joypad.
This post is about an audio generation for my emulator through the Pioneer Kit, but before the boring theory and the details, here is a short clip:
There are different ways to generate digital audio on embedded systems depending on the resources offered by the system. The PSoC62s4 offers a good number of choices for the analog part. What I decided to use has been conditioned by the emulation technique that the emulator of the Programmable Sound Generator of the SEGA SG-1000 uses.
I won't go too deep into the details of the emulation, but the useful thing to know is that the emulator renders 60 frames of graphics and sounds per second in buffers. If you are familiar with SDL library you know that the system uses a double buffer, and feeds the hardware with a front buffer while the application generates new data in a back buffer. At the end of a video frame, the two buffers are swapped, and so on.
The important thing is to feed graphics and sound data at a constant rate. For this purpose, you should set a timer or counter that sends data maybe through an interrupt. The problem is that the more interrupts we have and the more noise we will see on the video image. Actually, for an emulator like this, we could use the existing ISR of the VGA driver to send audio data, for each scanline. That means for the VGA standard, 525 samples * 60 fps, or 31500 samples per second, which is not bad for an 8-bit audio emulation.
The SEGA SG-1000 makes sounds through a TI SN7689 Programmable Sound Generator, so the code that I wrote mimics this chip and renders the audio data of 3 tones channels and a noise channel directly into a buffer, scanline by scanline.
If you want to go deeper into this chip you could take a look at the datasheet:
Concerning the PSoC62s4, I decided to use the 12-bit DAC, directly driven by the software. This is the simplest and easiest way. ModusToolbox is great and lets you configure what you need, and thanks to the Device Configurator you can design this analog peripheral easily.
And the audio initialization code is reduced to a few lines. Nice and clean.
/*////////////////////////////////////////////////////////////////////////////// * File Name: audio_dac.c * * Description: Element14 - At The Core Design Challenge * Digimorf, SEGA SG-1000 Emulator lite * Audio output driver for PSoC62S4 Pioneer kit * * Related Document: See audio.txt * * Created on: 17 Apr 2023 * Author: Francesco De Simone - Digimorf * * //////////////////////////////////////////////////////////////////////////////*/ // ///////////////////////////////////////////////////////////////////////////// // Includes // ///////////////////////////////////////////////////////////////////////////// #include "cy_pdl.h" #include "ipc_shared_memory.h" #include "audio_dac.h" // ///////////////////////////////////////////////////////////////////////////// // Private variables // ///////////////////////////////////////////////////////////////////////////// uint16_t gAudioBuffer[526*2]; volatile uint16_t *gAudioBufferFront,*gAudioBufferBack; // ///////////////////////////////////////////////////////////////////////////// // Function prototypes // ///////////////////////////////////////////////////////////////////////////// // ///////////////////////////////////////////////////////////////////////////// // Functions // ///////////////////////////////////////////////////////////////////////////// void Audio_Init(void) { /* * DESCRIPTION: Initialise the audio driver by configuring and starting * basic components such as CTDAC. * PARAMETERS: See above. * RETURNS: Nothing */ gAudioBufferFront = &gAudioBuffer[0]; gAudioBufferBack = &gAudioBuffer[525]; *((uint32_t*)gAudioBufferAddr) = (uint32_t)gAudioBufferBack; // Shared location Cy_CTDAC_Init(audio_dac_HW, &audio_dac_config); Cy_CTDAC_Enable(audio_dac_HW); } // /////////////////////////////////////////////////////////////////////////////
So, inside my VGA ISR code, I only need to add this line to send a sample to the DAC, which immediately converts to a voltage signal.
Cy_CTDAC_SetValue(audio_dac_HW, gAudioBufferFront[gVGAScanline]);
As I said in previous posts this is the first time that I put my hands on this PSoC by Infineon, so I could obtain better results for both video and audio generation, but for now I am very happy for two months of self-learning with this hardware. In the end, I hope that this project can be useful for other users also.