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