|
| 1 | +/* |
| 2 | + Copyright (C) 2012 Andy Karpov <andy.karpov@gmail.com> |
| 3 | +
|
| 4 | + This program is free software; you can redistribute it and/or |
| 5 | + modify it under the terms of the GNU General Public License |
| 6 | + version 2 as published by the Free Software Foundation. |
| 7 | + |
| 8 | + Ported to STM32F103 by Vassilis Serasidis on 21 May 2015 |
| 9 | + Home: http://www.serasidis.gr |
| 10 | + email: avrsite@yahoo.gr |
| 11 | + |
| 12 | + */ |
| 13 | + |
| 14 | +// STL headers |
| 15 | +// C headers |
| 16 | +#include <avr/pgmspace.h> |
| 17 | +// Framework headers |
| 18 | +// Library headers |
| 19 | +#include <avr/pgmspace.h> |
| 20 | +#include <limits.h> |
| 21 | +#include <libmaple/dma.h> |
| 22 | +#include "pins_arduino.h" |
| 23 | +#include "wiring_private.h" |
| 24 | +#include <SPI.h> |
| 25 | +// Project headers |
| 26 | +// This component's header |
| 27 | +#include <VS1003_STM.h> |
| 28 | + |
| 29 | +const uint8_t vs1003_chunk_size = 32; |
| 30 | + |
| 31 | +#undef PROGMEM |
| 32 | +#define PROGMEM __attribute__ ((section (".progmem.data"))) |
| 33 | +#undef PSTR |
| 34 | +#define PSTR(s) (__extension__({static char __c[] PROGMEM = (s); &__c[0];})) |
| 35 | + |
| 36 | +/****************************************************************************/ |
| 37 | + |
| 38 | +// VS1003 SCI Write Command byte is 0x02 |
| 39 | +#define VS_WRITE_COMMAND 0x02 |
| 40 | + |
| 41 | +// VS1003 SCI Read COmmand byte is 0x03 |
| 42 | +#define VS_READ_COMMAND 0x03 |
| 43 | + |
| 44 | +// SCI Registers |
| 45 | + |
| 46 | +const uint8_t SCI_MODE = 0x0; |
| 47 | +const uint8_t SCI_STATUS = 0x1; |
| 48 | +const uint8_t SCI_BASS = 0x2; |
| 49 | +const uint8_t SCI_CLOCKF = 0x3; |
| 50 | +const uint8_t SCI_DECODE_TIME = 0x4; |
| 51 | +const uint8_t SCI_AUDATA = 0x5; |
| 52 | +const uint8_t SCI_WRAM = 0x6; |
| 53 | +const uint8_t SCI_WRAMADDR = 0x7; |
| 54 | +const uint8_t SCI_HDAT0 = 0x8; |
| 55 | +const uint8_t SCI_HDAT1 = 0x9; |
| 56 | +const uint8_t SCI_AIADDR = 0xa; |
| 57 | +const uint8_t SCI_VOL = 0xb; |
| 58 | +const uint8_t SCI_AICTRL0 = 0xc; |
| 59 | +const uint8_t SCI_AICTRL1 = 0xd; |
| 60 | +const uint8_t SCI_AICTRL2 = 0xe; |
| 61 | +const uint8_t SCI_AICTRL3 = 0xf; |
| 62 | +const uint8_t SCI_num_registers = 0xf; |
| 63 | + |
| 64 | +// SCI_MODE bits |
| 65 | + |
| 66 | +const uint8_t SM_DIFF = 0; |
| 67 | +const uint8_t SM_LAYER12 = 1; |
| 68 | +const uint8_t SM_RESET = 2; |
| 69 | +const uint8_t SM_OUTOFWAV = 3; |
| 70 | +const uint8_t SM_EARSPEAKER_LO = 4; |
| 71 | +const uint8_t SM_TESTS = 5; |
| 72 | +const uint8_t SM_STREAM = 6; |
| 73 | +const uint8_t SM_EARSPEAKER_HI = 7; |
| 74 | +const uint8_t SM_DACT = 8; |
| 75 | +const uint8_t SM_SDIORD = 9; |
| 76 | +const uint8_t SM_SDISHARE = 10; |
| 77 | +const uint8_t SM_SDINEW = 11; |
| 78 | +const uint8_t SM_ADPCM = 12; |
| 79 | +const uint8_t SM_ADCPM_HP = 13; |
| 80 | +const uint8_t SM_LINE_IN = 14; |
| 81 | + |
| 82 | +// Register names |
| 83 | + |
| 84 | +char reg_name_MODE[] PROGMEM = "MODE"; |
| 85 | +char reg_name_STATUS[] PROGMEM = "STATUS"; |
| 86 | +char reg_name_BASS[] PROGMEM = "BASS"; |
| 87 | +char reg_name_CLOCKF[] PROGMEM = "CLOCKF"; |
| 88 | +char reg_name_DECODE_TIME[] PROGMEM = "DECODE_TIME"; |
| 89 | +char reg_name_AUDATA[] PROGMEM = "AUDATA"; |
| 90 | +char reg_name_WRAM[] PROGMEM = "WRAM"; |
| 91 | +char reg_name_WRAMADDR[] PROGMEM = "WRAMADDR"; |
| 92 | +char reg_name_HDAT0[] PROGMEM = "HDAT0"; |
| 93 | +char reg_name_HDAT1[] PROGMEM = "HDAT1"; |
| 94 | +char reg_name_AIADDR[] PROGMEM = "AIADDR"; |
| 95 | +char reg_name_VOL[] PROGMEM = "VOL"; |
| 96 | +char reg_name_AICTRL0[] PROGMEM = "AICTRL0"; |
| 97 | +char reg_name_AICTRL1[] PROGMEM = "AICTRL1"; |
| 98 | +char reg_name_AICTRL2[] PROGMEM = "AICTRL2"; |
| 99 | +char reg_name_AICTRL3[] PROGMEM = "AICTRL3"; |
| 100 | + |
| 101 | +static PGM_P register_names[] PROGMEM = |
| 102 | +{ |
| 103 | + reg_name_MODE, |
| 104 | + reg_name_STATUS, |
| 105 | + reg_name_BASS, |
| 106 | + reg_name_CLOCKF, |
| 107 | + reg_name_DECODE_TIME, |
| 108 | + reg_name_AUDATA, |
| 109 | + reg_name_WRAM, |
| 110 | + reg_name_WRAMADDR, |
| 111 | + reg_name_HDAT0, |
| 112 | + reg_name_HDAT1, |
| 113 | + reg_name_AIADDR, |
| 114 | + reg_name_VOL, |
| 115 | + reg_name_AICTRL0, |
| 116 | + reg_name_AICTRL1, |
| 117 | + reg_name_AICTRL2, |
| 118 | + reg_name_AICTRL3, |
| 119 | +}; |
| 120 | + |
| 121 | +/****************************************************************************/ |
| 122 | + |
| 123 | +inline void DMA1_CH3_Event() { |
| 124 | + dma1_ch3_Active = 0; |
| 125 | + dma_disable(DMA1, DMA_CH3); |
| 126 | +} |
| 127 | + |
| 128 | +/****************************************************************************/ |
| 129 | + |
| 130 | +uint16_t VS1003_STM::read_register(uint8_t _reg) const |
| 131 | +{ |
| 132 | + uint16_t result; |
| 133 | + control_mode_on(); |
| 134 | + delayMicroseconds(1); // tXCSS |
| 135 | + SPI.transfer(VS_READ_COMMAND); // Read operation |
| 136 | + SPI.transfer(_reg); // Which register |
| 137 | + result = SPI.transfer(0xff) << 8; // read high byte |
| 138 | + result |= SPI.transfer(0xff); // read low byte |
| 139 | + delayMicroseconds(1); // tXCSH |
| 140 | + await_data_request(); |
| 141 | + control_mode_off(); |
| 142 | + return result; |
| 143 | +} |
| 144 | + |
| 145 | +/****************************************************************************/ |
| 146 | + |
| 147 | +void VS1003_STM::write_register(uint8_t _reg,uint16_t _value) const |
| 148 | +{ |
| 149 | + control_mode_on(); |
| 150 | + delayMicroseconds(1); // tXCSS |
| 151 | + SPI.transfer(VS_WRITE_COMMAND); // Write operation |
| 152 | + SPI.transfer(_reg); // Which register |
| 153 | + SPI.transfer(_value >> 8); // Send hi byte |
| 154 | + SPI.transfer(_value & 0xff); // Send lo byte |
| 155 | + delayMicroseconds(1); // tXCSH |
| 156 | + await_data_request(); |
| 157 | + control_mode_off(); |
| 158 | +} |
| 159 | + |
| 160 | +/****************************************************************************/ |
| 161 | + |
| 162 | +void VS1003_STM::sdi_send_buffer(const uint8_t* data, size_t len) |
| 163 | +{ |
| 164 | + data_mode_on(); |
| 165 | + while ( len ) |
| 166 | + { |
| 167 | + await_data_request(); |
| 168 | + delayMicroseconds(3); |
| 169 | + |
| 170 | + size_t chunk_length = min(len,vs1003_chunk_size); |
| 171 | + len -= chunk_length; |
| 172 | + while ( chunk_length-- ) |
| 173 | + SPI.transfer(*data++); |
| 174 | + } |
| 175 | + data_mode_off(); |
| 176 | +} |
| 177 | + |
| 178 | +/****************************************************************************/ |
| 179 | + |
| 180 | +void VS1003_STM::sdi_send_zeroes(size_t len) |
| 181 | +{ |
| 182 | + data_mode_on(); |
| 183 | + while ( len ) |
| 184 | + { |
| 185 | + await_data_request(); |
| 186 | + |
| 187 | + size_t chunk_length = min(len,vs1003_chunk_size); |
| 188 | + len -= chunk_length; |
| 189 | + while ( chunk_length-- ) |
| 190 | + SPI.transfer(0); |
| 191 | + } |
| 192 | + data_mode_off(); |
| 193 | +} |
| 194 | + |
| 195 | +/****************************************************************************/ |
| 196 | + |
| 197 | +VS1003_STM::VS1003_STM( uint8_t _cs_pin, uint8_t _dcs_pin, uint8_t _dreq_pin, uint8_t _reset_pin): |
| 198 | + cs_pin(_cs_pin), dcs_pin(_dcs_pin), dreq_pin(_dreq_pin), reset_pin(_reset_pin) |
| 199 | +{ |
| 200 | +} |
| 201 | + |
| 202 | +/****************************************************************************/ |
| 203 | + |
| 204 | +void VS1003_STM::begin(void) |
| 205 | +{ |
| 206 | + |
| 207 | + // Keep the chip in reset until we are ready |
| 208 | + pinMode(reset_pin,OUTPUT); |
| 209 | + digitalWrite(reset_pin,LOW); |
| 210 | + |
| 211 | + // The SCI and SDI will start deselected |
| 212 | + pinMode(cs_pin,OUTPUT); |
| 213 | + digitalWrite(cs_pin,HIGH); |
| 214 | + pinMode(dcs_pin,OUTPUT); |
| 215 | + digitalWrite(dcs_pin,HIGH); |
| 216 | + |
| 217 | + // DREQ is an input |
| 218 | + pinMode(dreq_pin,INPUT); |
| 219 | + |
| 220 | + // Boot VS1003 |
| 221 | + //Serial.println(PSTR("Booting VS1003...\r\n")); |
| 222 | + |
| 223 | + delay(1); |
| 224 | + |
| 225 | + SPI.begin(); |
| 226 | + SPI.setBitOrder(MSBFIRST); |
| 227 | + SPI.setDataMode(SPI_MODE0); |
| 228 | + // init SPI slow mode |
| 229 | + SPI.setClockDivider(SPI_CLOCK_DIV64); // Slow! |
| 230 | + |
| 231 | + // release from reset |
| 232 | + digitalWrite(reset_pin,HIGH); |
| 233 | + |
| 234 | + // Declick: Immediately switch analog off |
| 235 | + write_register(SCI_VOL,0xffff); // VOL |
| 236 | + |
| 237 | + /* Declick: Slow sample rate for slow analog part startup */ |
| 238 | + write_register(SCI_AUDATA,10); |
| 239 | + |
| 240 | + delay(100); |
| 241 | + |
| 242 | + /* Switch on the analog parts */ |
| 243 | + write_register(SCI_VOL,0xfefe); // VOL |
| 244 | + |
| 245 | + //printf_P(PSTR("VS1003 still booting\r\n")); |
| 246 | + |
| 247 | + write_register(SCI_AUDATA,44101); // 44.1kHz stereo |
| 248 | + |
| 249 | + write_register(SCI_VOL,0x2020); // VOL |
| 250 | + |
| 251 | + // soft reset |
| 252 | + write_register(SCI_MODE, (1<<SM_SDINEW) | (1<<SM_RESET)); |
| 253 | + delay(1); |
| 254 | + await_data_request(); |
| 255 | + //write_register(SCI_CLOCKF,0xB800); // Experimenting with higher clock settings |
| 256 | + write_register(SCI_CLOCKF,0x6000); |
| 257 | + delay(1); |
| 258 | + await_data_request(); |
| 259 | + |
| 260 | + // Now you can set high speed SPI clock |
| 261 | + // 72 MHz / 16 = 4.5 MHz max is practically allowed by VS1003 SPI interface. |
| 262 | + SPI.setClockDivider(SPI_CLOCK_DIV16); |
| 263 | + |
| 264 | + //printf_P(PSTR("VS1003 Set\r\n")); |
| 265 | + //printDetails(); |
| 266 | + //printf_P(PSTR("VS1003 OK\r\n")); |
| 267 | + |
| 268 | +} |
| 269 | + |
| 270 | +/****************************************************************************/ |
| 271 | + |
| 272 | +void VS1003_STM::setVolume(uint8_t vol) const |
| 273 | +{ |
| 274 | + uint16_t value = vol; |
| 275 | + value <<= 8; |
| 276 | + value |= vol; |
| 277 | + |
| 278 | + write_register(SCI_VOL,value); // VOL |
| 279 | +} |
| 280 | + |
| 281 | +/****************************************************************************/ |
| 282 | + |
| 283 | +void VS1003_STM::startSong(void) |
| 284 | +{ |
| 285 | + sdi_send_zeroes(10); |
| 286 | +} |
| 287 | + |
| 288 | +/****************************************************************************/ |
| 289 | + |
| 290 | +void VS1003_STM::playChunk(const uint8_t* data, size_t len) |
| 291 | +{ |
| 292 | + sdi_send_buffer(data,len); |
| 293 | +} |
| 294 | + |
| 295 | +/****************************************************************************/ |
| 296 | + |
| 297 | +void VS1003_STM::stopSong(void) |
| 298 | +{ |
| 299 | + sdi_send_zeroes(2048); |
| 300 | +} |
| 301 | + |
| 302 | +/****************************************************************************/ |
| 303 | + |
| 304 | +void VS1003_STM::print_byte_register(uint8_t reg) const |
| 305 | +{ |
| 306 | + const char *name = reinterpret_cast<const char*>(pgm_read_word( register_names + reg )); |
| 307 | + char extra_tab = strlen_P(name) < 5 ? '\t' : 0; |
| 308 | + //printf_P(PSTR("%02x %S\t%c = 0x%02x\r\n"),reg,name,extra_tab,read_register(reg)); |
| 309 | +} |
| 310 | + |
| 311 | +/****************************************************************************/ |
| 312 | + |
| 313 | +void VS1003_STM::printDetails(void) const |
| 314 | +{ |
| 315 | + //printf_P(PSTR("VS1003 Configuration:\r\n")); |
| 316 | + int i = 0; |
| 317 | + while ( i <= SCI_num_registers ) |
| 318 | + print_byte_register(i++); |
| 319 | +} |
| 320 | + |
| 321 | +/****************************************************************************/ |
| 322 | + |
| 323 | +void VS1003_STM::loadUserCode(const uint16_t* buf, size_t len) const |
| 324 | +{ |
| 325 | + while (len) |
| 326 | + { |
| 327 | + uint16_t addr = pgm_read_word(buf++); len--; |
| 328 | + uint16_t n = pgm_read_word(buf++); len--; |
| 329 | + if (n & 0x8000U) { /* RLE run, replicate n samples */ |
| 330 | + n &= 0x7FFF; |
| 331 | + uint16_t val = pgm_read_word(buf++); len--; |
| 332 | + while (n--) { |
| 333 | + //printf_P(PSTR("W %02x: %04x\r\n"),addr,val); |
| 334 | + write_register(addr, val); |
| 335 | + } |
| 336 | + } else { /* Copy run, copy n samples */ |
| 337 | + while (n--) { |
| 338 | + uint16_t val = pgm_read_word(buf++); len--; |
| 339 | + //printf_P(PSTR("W %02x: %04x\r\n"),addr,val); |
| 340 | + write_register(addr, val); |
| 341 | + } |
| 342 | + } |
| 343 | + } |
| 344 | +} |
| 345 | + |
| 346 | +/****************************************************************************/ |
0 commit comments