BBC micro:bit SN76489N VGM chiptunes player

Dependencies:   microbit

Committer:
linker3000
Date:
Sun Feb 11 11:10:58 2018 +0000
Revision:
1:09a3520030b4
Parent:
0:e4dc860c8472
Child:
2:74ecf50e4424
First Commit; ; This code plays BBC micro (SN76489) VGM sound files on a BBC micro:bit.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
linker3000 0:e4dc860c8472 1 /*
linker3000 1:09a3520030b4 2 BBC micro:bit VGM player
linker3000 0:e4dc860c8472 3
linker3000 1:09a3520030b4 4 Version: 0.1 NK 10-Feb-2018
linker3000 1:09a3520030b4 5
linker3000 1:09a3520030b4 6 Copyright 2018 N Kendrick (nigel-dot-kendrick-at-gmail-dotcom)
linker3000 0:e4dc860c8472 7
linker3000 0:e4dc860c8472 8 Permission is hereby granted, free of charge, to any person obtaining a
linker3000 0:e4dc860c8472 9 copy of this software and associated documentation files (the "Software"),
linker3000 0:e4dc860c8472 10 to deal in the Software without restriction, including without limitation
linker3000 0:e4dc860c8472 11 the rights to use, copy, modify, merge, publish, distribute, sublicense,
linker3000 1:09a3520030b4 12 and/or sell copies of the Software, and to permit persons to whom the Software
linker3000 1:09a3520030b4 13 is furnished to do so, subject to the following conditions:
linker3000 0:e4dc860c8472 14
linker3000 0:e4dc860c8472 15 The above copyright notice and this permission notice shall be included in
linker3000 0:e4dc860c8472 16 all copies or substantial portions of the Software.
linker3000 0:e4dc860c8472 17
linker3000 0:e4dc860c8472 18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
linker3000 0:e4dc860c8472 19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
linker3000 1:09a3520030b4 20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
linker3000 1:09a3520030b4 21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
linker3000 1:09a3520030b4 22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
linker3000 1:09a3520030b4 23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
linker3000 1:09a3520030b4 24 THE SOFTWARE.
linker3000 1:09a3520030b4 25
linker3000 1:09a3520030b4 26 Inspired by Arduino code produced by Artkasser (https://github.com/artkasser)
linker3000 1:09a3520030b4 27
linker3000 1:09a3520030b4 28 This code plays BBC micro (SN76489) VGM sound files on a BBC micro:bit.
linker3000 1:09a3520030b4 29
linker3000 1:09a3520030b4 30 Note that in this code version, the VGM data is stored as a byte array file
linker3000 1:09a3520030b4 31 (minus the header info), and not read-in from anywhere. Maybe later!?
linker3000 1:09a3520030b4 32
linker3000 1:09a3520030b4 33 External circuitry is needed to make this work:
linker3000 0:e4dc860c8472 34
linker3000 1:09a3520030b4 35 1 x BBC micro:bit breakout board (eg: Kitronik) to get to the required pins
linker3000 1:09a3520030b4 36 1 x SN74HC595 shift register IC - easy to obtain
linker3000 1:09a3520030b4 37 1 x SN76489 programmable sound generator (PSG) IC (From a reputable[!]
linker3000 1:09a3520030b4 38 supplier. You can get them on Ebay and elsewhere but beware of fakes.
linker3000 1:09a3520030b4 39 2 x 0.1uF ceramic capacitors
linker3000 1:09a3520030b4 40 1 x 4MHz oscillator module - the connections list below assumes a '14-pin'
linker3000 1:09a3520030b4 41 unit - such as something similar to QX14T50B4.000000B50TT.
linker3000 1:09a3520030b4 42 Note, you want a 4MHz crystal MODULE, not a 4MHz crystal.
linker3000 1:09a3520030b4 43 1 x Breadboard to build the circuit, unless there is room on the breakout board
linker3000 1:09a3520030b4 44 or you use perfboard/stripboard or make your own PCB etc.
linker3000 1:09a3520030b4 45 2 x 16-pin IC sockets (optional, and not needed for a breadboard)
linker3000 1:09a3520030b4 46 1 x audio amplifier or lead to connect PSG audio out to a set of active
linker3000 1:09a3520030b4 47 speakers, you could instead connect a pieze element (not a buzzer) to the
linker3000 1:09a3520030b4 48 audio out pin of the PSG. If connecting to a piezo sounder, you'll need a
linker3000 1:09a3520030b4 49 1K resistor.
linker3000 1:09a3520030b4 50 1 x Assorted wiring
linker3000 0:e4dc860c8472 51
linker3000 0:e4dc860c8472 52 Connections:
linker3000 0:e4dc860c8472 53
linker3000 1:09a3520030b4 54 The micro:bit takes power as usual from the connected micro USB cable.
linker3000 1:09a3520030b4 55
linker3000 1:09a3520030b4 56 Everything else i powered from a separate 5V supply, such as a USB phone
linker3000 1:09a3520030b4 57 charger. You *could* try powering these parts from the 3V connection on
linker3000 1:09a3520030b4 58 the micro:bit - it might work, but the PSG and the oscillator module are
linker3000 1:09a3520030b4 59 officially 5V parts so YMMV.
linker3000 1:09a3520030b4 60
linker3000 1:09a3520030b4 61 Remember to ensure that the micro:bit's 0V/GND line is connected to the
linker3000 1:09a3520030b4 62 GND line of all the other parts to ensure proper circuit operation. DO NOT
linker3000 1:09a3520030b4 63 connect the 5V power line to the micro:bit's 3V line as you'll break
linker3000 1:09a3520030b4 64 something!
linker3000 1:09a3520030b4 65
linker3000 1:09a3520030b4 66 Hookup overview:
linker3000 1:09a3520030b4 67
linker3000 1:09a3520030b4 68 From Function Connect to
linker3000 1:09a3520030b4 69 micro:bit P15 MOSI 74HC595 pin 14 (SER)
linker3000 1:09a3520030b4 70 micro:bit P13 CE 74HC595 pin 12 (RCLK)
linker3000 1:09a3520030b4 71 micro:bit P2 SER 74HC595 pin 11 (SRCLK)
linker3000 1:09a3520030b4 72 micro:bit P8 PSG ~WE SN76489 pin 5 (~WE)
linker3000 1:09a3520030b4 73 74HC595 Data out Data bus PSG Data lines - Note that the PSG data
linker3000 1:09a3520030b4 74 lines are labelled in REVERSE, so the
linker3000 1:09a3520030b4 75 wiring between the 595 and the 76489 should
linker3000 1:09a3520030b4 76 be 74HC595 D0 --- D7 SN76489
linker3000 1:09a3520030b4 77 74HC595 D1 --- D6 SN76489 etc
linker3000 1:09a3520030b4 78 Oscillator P7 GND To common ground with all other GND pins
linker3000 1:09a3520030b4 79 Oscillator P8 CLK SN76489 pin 16
linker3000 1:09a3520030b4 80 Audio out AUDIO SN76489 pin 7 to an external amplifier,
linker3000 1:09a3520030b4 81 or a piezo sounder through a 1K resistor
linker3000 1:09a3520030b4 82 +5V VCC Both ICs, pin 16, Oscillator pin 14; all
linker3000 1:09a3520030b4 83 active components EXCEPT the micro:bit
linker3000 1:09a3520030b4 84 micro:bit GND GND connect to the common GND on the external
linker3000 1:09a3520030b4 85 parts
linker3000 1:09a3520030b4 86
linker3000 1:09a3520030b4 87 For stability, connect 0.1uF capacitors between VCC and GND near the both the
linker3000 1:09a3520030b4 88 74HC595 and SN76489 chips.
linker3000 1:09a3520030b4 89
linker3000 1:09a3520030b4 90 Double-check all wiring before powering up anything!
linker3000 0:e4dc860c8472 91
linker3000 0:e4dc860c8472 92 */
linker3000 0:e4dc860c8472 93
linker3000 0:e4dc860c8472 94 #include "MicroBit.h"
linker3000 0:e4dc860c8472 95 #include "nkpins.h"
linker3000 0:e4dc860c8472 96
linker3000 1:09a3520030b4 97 //VGM file as a byte array with header info removed...
linker3000 1:09a3520030b4 98 #include "sonic.h"
linker3000 1:09a3520030b4 99
linker3000 0:e4dc860c8472 100 MicroBit uBit;
linker3000 1:09a3520030b4 101 MicroBitDisplay display;
linker3000 1:09a3520030b4 102
linker3000 1:09a3520030b4 103 const uint16_t SampleTime = 23; //VGM format = 44,100Hz sampling = 23uS/sample
linker3000 1:09a3520030b4 104
linker3000 1:09a3520030b4 105 // General program variables
linker3000 1:09a3520030b4 106
linker3000 1:09a3520030b4 107 uint16_t Samples = 0;
linker3000 1:09a3520030b4 108 uint16_t vgmpos = 0;
linker3000 1:09a3520030b4 109
linker3000 1:09a3520030b4 110 // Microbit SPI port setup
linker3000 1:09a3520030b4 111 // Using the default SPI pins defined in the micro:bit docs...
linker3000 1:09a3520030b4 112 SPI spi(mbit_p15, mbit_p14, mbit_p13); // mosi, miso (not used), sclk
linker3000 1:09a3520030b4 113 DigitalOut cs(mbit_p1); //Chip select pin for the shift register
linker3000 1:09a3520030b4 114 DigitalOut PSG_WE(mbit_p8); //~WE pin for PSG
linker3000 1:09a3520030b4 115
linker3000 1:09a3520030b4 116 void PutByte (uint8_t b)
linker3000 1:09a3520030b4 117 // Write data to the shift register via SPI interface
linker3000 1:09a3520030b4 118 {
linker3000 1:09a3520030b4 119 cs = 1;
linker3000 1:09a3520030b4 120 spi.write(b);
linker3000 1:09a3520030b4 121 cs = 0;
linker3000 1:09a3520030b4 122 }
linker3000 1:09a3520030b4 123
linker3000 1:09a3520030b4 124 void SendByte(uint8_t b)
linker3000 1:09a3520030b4 125 {
linker3000 1:09a3520030b4 126 PSG_WE = 1;
linker3000 1:09a3520030b4 127 PutByte(b);
linker3000 1:09a3520030b4 128 PSG_WE = 0;
linker3000 1:09a3520030b4 129 wait_us(SampleTime);
linker3000 1:09a3520030b4 130 PSG_WE = 1;
linker3000 1:09a3520030b4 131 }
linker3000 1:09a3520030b4 132
linker3000 1:09a3520030b4 133 void SilenceAllChannels()
linker3000 1:09a3520030b4 134 {
linker3000 1:09a3520030b4 135 SendByte(0x9f);
linker3000 1:09a3520030b4 136 SendByte(0xbf);
linker3000 1:09a3520030b4 137 SendByte(0xdf);
linker3000 1:09a3520030b4 138 SendByte(0xff);
linker3000 1:09a3520030b4 139 }
linker3000 1:09a3520030b4 140
linker3000 1:09a3520030b4 141 void Playloop()
linker3000 1:09a3520030b4 142 //Process the VGM data
linker3000 1:09a3520030b4 143 {
linker3000 1:09a3520030b4 144
linker3000 1:09a3520030b4 145 bool runstop = false;
linker3000 1:09a3520030b4 146 // True when we get to the 'end of data' value in the array.
linker3000 1:09a3520030b4 147 // There's no current trap for if the last byte in the array isn't 0x66
linker3000 1:09a3520030b4 148
linker3000 1:09a3520030b4 149 do {
linker3000 1:09a3520030b4 150 uint8_t vgmdata = (*(VGMDataArray + vgmpos));
linker3000 1:09a3520030b4 151
linker3000 1:09a3520030b4 152 // Process VGM codes.
linker3000 1:09a3520030b4 153
linker3000 1:09a3520030b4 154 /* Some ranges are reserved for future use, with different numbers of
linker3000 1:09a3520030b4 155 operands:
linker3000 0:e4dc860c8472 156
linker3000 1:09a3520030b4 157 0x30..0x3F dd : one operand, reserved for future use
linker3000 1:09a3520030b4 158 Note: used for dual-chip support
linker3000 1:09a3520030b4 159 0x40..0x4E dd dd : two operands, reserved for future use
linker3000 1:09a3520030b4 160 Note: was one operand only til v1.60
linker3000 1:09a3520030b4 161 0xA1..0xAF dd dd : two operands, reserved for future use
linker3000 1:09a3520030b4 162 Note: used for dual-chip support
linker3000 1:09a3520030b4 163 0xBC..0xBF dd dd : two operands, reserved for future use
linker3000 1:09a3520030b4 164 0xC5..0xCF dd dd dd : three operands, reserved for future use
linker3000 1:09a3520030b4 165 0xD5..0xDF dd dd dd : three operands, reserved for future use
linker3000 1:09a3520030b4 166 0xE1..0xFF dd dd dd dd : four operands, reserved for future use
linker3000 1:09a3520030b4 167
linker3000 1:09a3520030b4 168 This programming does NOT currently cater for those codes
linker3000 1:09a3520030b4 169 */
linker3000 1:09a3520030b4 170
linker3000 1:09a3520030b4 171 if ((vgmdata & 0xF0) == 0x70) {
linker3000 1:09a3520030b4 172 // 0x7n : wait n+1 samples, n can range from 0 to 15
linker3000 1:09a3520030b4 173 //Samples = 1;
linker3000 1:09a3520030b4 174 vgmpos++;
linker3000 1:09a3520030b4 175 wait_us(((vgmdata & 0x0F)+1) * SampleTime);
linker3000 1:09a3520030b4 176 } else {
linker3000 1:09a3520030b4 177 switch (vgmdata) {
linker3000 1:09a3520030b4 178 case 0x50: // 0x50 dd : PSG (SN76489/SN76496) write value dd
linker3000 1:09a3520030b4 179 vgmpos++;
linker3000 1:09a3520030b4 180 vgmdata = (*(VGMDataArray + vgmpos));
linker3000 1:09a3520030b4 181 SendByte(vgmdata);
linker3000 1:09a3520030b4 182 vgmpos++;
linker3000 1:09a3520030b4 183 break;
linker3000 0:e4dc860c8472 184
linker3000 1:09a3520030b4 185 case 0x61: // 0x61 nn nn : Wait n samples, n can range from 0 to 65535
linker3000 1:09a3520030b4 186 vgmpos++;
linker3000 1:09a3520030b4 187 Samples = (uint16_t)( (*(VGMDataArray + vgmpos)) & 0x00FF );
linker3000 1:09a3520030b4 188 vgmpos++;
linker3000 1:09a3520030b4 189 Samples |= (uint16_t)(( (*(VGMDataArray + vgmpos)) << 8) & 0xFF00 );
linker3000 1:09a3520030b4 190 vgmpos++;
linker3000 1:09a3520030b4 191 wait_us(Samples * SampleTime);
linker3000 1:09a3520030b4 192 break;
linker3000 1:09a3520030b4 193
linker3000 1:09a3520030b4 194 case 0x62: // wait 735 samples (60th of a second)
linker3000 1:09a3520030b4 195 vgmpos++;
linker3000 1:09a3520030b4 196 wait_ms(17);
linker3000 1:09a3520030b4 197 break;
linker3000 1:09a3520030b4 198
linker3000 1:09a3520030b4 199 case 0x63: // wait 882 samples (50th of a second)
linker3000 1:09a3520030b4 200 vgmpos++;
linker3000 1:09a3520030b4 201 wait_ms(20);
linker3000 1:09a3520030b4 202 break;
linker3000 1:09a3520030b4 203
linker3000 1:09a3520030b4 204 case 0x66: // 0x66 : end of sound data
linker3000 1:09a3520030b4 205 vgmpos = 0;
linker3000 1:09a3520030b4 206 SilenceAllChannels();
linker3000 1:09a3520030b4 207 wait_ms(2000);
linker3000 1:09a3520030b4 208 runstop = true;
linker3000 1:09a3520030b4 209 break;
linker3000 1:09a3520030b4 210
linker3000 1:09a3520030b4 211 default:
linker3000 1:09a3520030b4 212 break;
linker3000 1:09a3520030b4 213 } //end switch
linker3000 1:09a3520030b4 214
linker3000 1:09a3520030b4 215 } // end else
linker3000 1:09a3520030b4 216
linker3000 1:09a3520030b4 217
linker3000 1:09a3520030b4 218 } while (!runstop);
linker3000 1:09a3520030b4 219 }
linker3000 0:e4dc860c8472 220 int main()
linker3000 0:e4dc860c8472 221 {
linker3000 0:e4dc860c8472 222 // Initialise the micro:bit runtime.
linker3000 0:e4dc860c8472 223 uBit.init();
linker3000 0:e4dc860c8472 224
linker3000 0:e4dc860c8472 225 //SPI setup
linker3000 0:e4dc860c8472 226 // Chip must be deselected
linker3000 0:e4dc860c8472 227 cs = 1;
linker3000 0:e4dc860c8472 228
linker3000 0:e4dc860c8472 229 // Setup the spi for 8 bit data, high steady state clock,
linker3000 1:09a3520030b4 230 // second edge capture, with a 1MHz clock rate (microbit and shift
linker3000 1:09a3520030b4 231 // register can handle it - tested).
linker3000 0:e4dc860c8472 232 spi.format(8,3);
linker3000 0:e4dc860c8472 233 spi.frequency(1000000);
linker3000 1:09a3520030b4 234
linker3000 1:09a3520030b4 235 // Silence the PSG
linker3000 0:e4dc860c8472 236
linker3000 1:09a3520030b4 237 PSG_WE = 1;
linker3000 1:09a3520030b4 238 SilenceAllChannels();
linker3000 1:09a3520030b4 239 wait_ms(500);
linker3000 0:e4dc860c8472 240
linker3000 1:09a3520030b4 241 //Play music
linker3000 1:09a3520030b4 242 Playloop();
linker3000 1:09a3520030b4 243 //Mute the last tone
linker3000 1:09a3520030b4 244 SilenceAllChannels();
linker3000 1:09a3520030b4 245
linker3000 1:09a3520030b4 246 display.scroll("** DONE ** :)");
linker3000 1:09a3520030b4 247
linker3000 1:09a3520030b4 248 //Tidy up and we're done
linker3000 1:09a3520030b4 249 release_fiber();
linker3000 0:e4dc860c8472 250
linker3000 0:e4dc860c8472 251 }
linker3000 0:e4dc860c8472 252