BBC micro:bit SN76489N VGM chiptunes player
main.cpp@1:09a3520030b4, 2018-02-11 (annotated)
- 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?
User | Revision | Line number | New 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 |