Attempting to read a uint32_t over two registers in an audio chip

Hello gurus,
My lack of C knowledge is hitting hard today. I could use a nudge in the right direction. :slightly_smiling_face:

I'm playing with an Adafruit Music Maker FeatherWing, which includes a nifty VS1053b chip that plays back several different audio formats (and even records which blows my mind!). While Adafruit has a library to read/write to most features of the chip, it's missing a read of the millisecond-accurate playback position supported if playing specific audio types. If I could pull that value, I could make an animated robot gizmo timed to audio and that would super fun.

The positionMsec value I'm trying to pull is a uint32_t value over a range of two "registers" at the address range of 0x1E27 - 0x1E28. The Adafruit library has an included method to read uint16_t values at an address that gets me close to what I need, maybe:

uint16_t Adafruit_VS1053::sciRead(uint8_t addr) {
  uint16_t data;
  
#ifdef SPI_HAS_TRANSACTION
  if (useHardwareSPI)
    SPI.beginTransaction(VS1053_CONTROL_SPI_SETTING);
#endif
  digitalWrite(_cs, LOW);
  spiwrite(VS1053_SCI_READ);
  spiwrite(addr);
  delayMicroseconds(10);
  data = spiread();
  data <<= 8;
  data |= spiread();
  digitalWrite(_cs, HIGH);
#ifdef SPI_HAS_TRANSACTION
  if (useHardwareSPI)
    SPI.endTransaction();
#endif

  return data;
}

However, I'm puzzled on getting a uint32_t over two registers. My initial attempt was to read a uint16_t value at 0x1E27 and 0x1E28 separately and printing out the values that way to see what could be done:

// Specifically for use with the Adafruit Feather, the pins are pre-set here!

// include SPI, MP3 and SD libraries
#include <SPI.h>
#include <SD.h>
#include <Adafruit_VS1053.h>

// These are the pins used
#define VS1053_RESET   -1     // VS1053 reset pin (not used!)

// Feather ESP8266
#if defined(ESP8266)
  #define VS1053_CS      16     // VS1053 chip select pin (output)
  #define VS1053_DCS     15     // VS1053 Data/command select pin (output)
  #define CARDCS          2     // Card chip select pin
  #define VS1053_DREQ     0     // VS1053 Data request, ideally an Interrupt pin

// Feather ESP32
#elif defined(ESP32) && !defined(ARDUINO_ADAFRUIT_FEATHER_ESP32S2)
  #define VS1053_CS      32     // VS1053 chip select pin (output)
  #define VS1053_DCS     33     // VS1053 Data/command select pin (output)
  #define CARDCS         14     // Card chip select pin
  #define VS1053_DREQ    15     // VS1053 Data request, ideally an Interrupt pin

// Feather Teensy3
#elif defined(TEENSYDUINO)
  #define VS1053_CS       3     // VS1053 chip select pin (output)
  #define VS1053_DCS     10     // VS1053 Data/command select pin (output)
  #define CARDCS          8     // Card chip select pin
  #define VS1053_DREQ     4     // VS1053 Data request, ideally an Interrupt pin

// WICED feather
#elif defined(ARDUINO_STM32_FEATHER)
  #define VS1053_CS       PC7     // VS1053 chip select pin (output)
  #define VS1053_DCS      PB4     // VS1053 Data/command select pin (output)
  #define CARDCS          PC5     // Card chip select pin
  #define VS1053_DREQ     PA15    // VS1053 Data request, ideally an Interrupt pin

#elif defined(ARDUINO_NRF52832_FEATHER )
  #define VS1053_CS       30     // VS1053 chip select pin (output)
  #define VS1053_DCS      11     // VS1053 Data/command select pin (output)
  #define CARDCS          27     // Card chip select pin
  #define VS1053_DREQ     31     // VS1053 Data request, ideally an Interrupt pin

// Feather M4, M0, 328, ESP32S2, nRF52840 or 32u4
#else
  #define VS1053_CS       6     // VS1053 chip select pin (output)
  #define VS1053_DCS     10     // VS1053 Data/command select pin (output)
  #define CARDCS          5     // Card chip select pin
  // DREQ should be an Int pin *if possible* (not possible on 32u4)
  #define VS1053_DREQ     9     // VS1053 Data request, ideally an Interrupt pin

#endif


Adafruit_VS1053_FilePlayer musicPlayer = 
  Adafruit_VS1053_FilePlayer(VS1053_RESET, VS1053_CS, VS1053_DCS, VS1053_DREQ, CARDCS);

void setup() {
  Serial.begin(115200);

  // if you're using Bluefruit or LoRa/RFM Feather, disable the radio module
  //pinMode(8, INPUT_PULLUP);

  // Wait for serial port to be opened, remove this line for 'standalone' operation
  while (!Serial) { delay(1); }
  delay(500);
  Serial.println("\n\nAdafruit VS1053 Feather Test");
  
  if (! musicPlayer.begin()) { // initialise the music player
     Serial.println(F("Couldn't find VS1053, do you have the right pins defined?"));
     while (1);
  }

  Serial.println(F("VS1053 found"));
  
  if (!SD.begin(CARDCS)) {
    Serial.println(F("SD failed, or not present"));
    while (1);  // don't do anything more
  }
  Serial.println("SD OK!");
  
  // Set volume for left, right channels. lower numbers == louder volume!
  musicPlayer.setVolume(200,200);
  
#if defined(__AVR_ATmega32U4__) 
  // Timer interrupts are not suggested, better to use DREQ interrupt!
  // but we don't have them on the 32u4 feather...
  musicPlayer.useInterrupt(VS1053_FILEPLAYER_TIMER0_INT); // timer int
#else
  // If DREQ is on an interrupt pin we can do background
  // audio playing
  musicPlayer.useInterrupt(VS1053_FILEPLAYER_PIN_INT);  // DREQ int
#endif

  Serial.println(F("playing track..."));
  musicPlayer.startPlayingFile("/track002.ogg");
}

void loop() {
  uint16_t decodeTime;
  uint16_t playMsec0;
  uint16_t playMsec1;
  // File is playing in the background
  if (musicPlayer.stopped()) {
    Serial.println("done playing track");
    while (1) {
      delay(10);  // we're done! do nothing...
    }
  } else {
    decodeTime = musicPlayer.decodeTime();
    playMsec0 = musicPlayer.sciRead(0x1E27);
    playMsec1 = musicPlayer.sciRead(0x1E28);
    Serial.print("decodeTime(): ");
    Serial.print(decodeTime);
    Serial.print(" playMsec0: ");
    Serial.print(playMsec0);
    Serial.print(" playMsec1: ");
    Serial.print(playMsec1);
    Serial.print("\n");
  }
  delay(100);
}

If I read from those values though while playing back a .ogg file, 0x1E27 always reads as "7721", while 0x1E28 reads as other crazy numbers that change by the second, sometimes (see below). These values are not populated when playing a .mp3 file as expected. I can read other nearby values properly such as 0x1E05 (also not used in the Adafruit library) which is the average playback rate, quickly ramping up to "48001" in the case of my .ogg file.

Output:

Adafruit VS1053 Feather Test
VS1053 found
SD OK!
playing track...
decodeTime(): 0 playMsec0: 7721 playMsec1: 0
decodeTime(): 0 playMsec0: 7721 playMsec1: 0
decodeTime(): 0 playMsec0: 7721 playMsec1: 0
decodeTime(): 0 playMsec0: 7721 playMsec1: 0
decodeTime(): 0 playMsec0: 7721 playMsec1: 0
decodeTime(): 0 playMsec0: 7721 playMsec1: 0
decodeTime(): 0 playMsec0: 7721 playMsec1: 0
decodeTime(): 0 playMsec0: 7721 playMsec1: 0
decodeTime(): 0 playMsec0: 7721 playMsec1: 0
decodeTime(): 1 playMsec0: 7721 playMsec1: 33805
decodeTime(): 1 playMsec0: 7721 playMsec1: 33805
decodeTime(): 1 playMsec0: 7721 playMsec1: 33805
decodeTime(): 1 playMsec0: 7721 playMsec1: 33805
decodeTime(): 1 playMsec0: 7721 playMsec1: 33805
decodeTime(): 1 playMsec0: 7721 playMsec1: 33805
decodeTime(): 1 playMsec0: 7721 playMsec1: 33805
decodeTime(): 1 playMsec0: 7721 playMsec1: 33805
decodeTime(): 1 playMsec0: 7721 playMsec1: 33805
decodeTime(): 2 playMsec0: 7721 playMsec1: 26549
decodeTime(): 2 playMsec0: 7721 playMsec1: 26549
decodeTime(): 2 playMsec0: 7721 playMsec1: 26549
decodeTime(): 2 playMsec0: 7721 playMsec1: 26549
decodeTime(): 2 playMsec0: 7721 playMsec1: 26549
decodeTime(): 2 playMsec0: 7721 playMsec1: 26549
decodeTime(): 2 playMsec0: 7721 playMsec1: 26549
decodeTime(): 2 playMsec0: 7721 playMsec1: 26549
decodeTime(): 3 playMsec0: 7721 playMsec1: 27018
decodeTime(): 3 playMsec0: 7721 playMsec1: 27018
decodeTime(): 3 playMsec0: 7721 playMsec1: 27018
decodeTime(): 3 playMsec0: 7721 playMsec1: 27018
decodeTime(): 3 playMsec0: 7721 playMsec1: 27018
decodeTime(): 3 playMsec0: 7721 playMsec1: 27018
decodeTime(): 3 playMsec0: 7721 playMsec1: 27018
decodeTime(): 3 playMsec0: 7721 playMsec1: 27018
decodeTime(): 4 playMsec0: 7721 playMsec1: 27689
decodeTime(): 4 playMsec0: 7721 playMsec1: 27689
decodeTime(): 4 playMsec0: 7721 playMsec1: 27689
decodeTime(): 4 playMsec0: 7721 playMsec1: 27689
decodeTime(): 4 playMsec0: 7721 playMsec1: 27689
decodeTime(): 4 playMsec0: 7721 playMsec1: 27689
decodeTime(): 4 playMsec0: 7721 playMsec1: 27689
decodeTime(): 4 playMsec0: 7721 playMsec1: 27689
decodeTime(): 4 playMsec0: 7721 playMsec1: 27689
decodeTime(): 5 playMsec0: 7721 playMsec1: 27689
decodeTime(): 5 playMsec0: 7721 playMsec1: 27689
decodeTime(): 5 playMsec0: 7721 playMsec1: 27689
decodeTime(): 5 playMsec0: 7721 playMsec1: 27689
decodeTime(): 5 playMsec0: 7721 playMsec1: 27689
decodeTime(): 5 playMsec0: 7721 playMsec1: 27689
decodeTime(): 5 playMsec0: 7721 playMsec1: 27689
decodeTime(): 5 playMsec0: 7721 playMsec1: 27689
decodeTime(): 5 playMsec0: 7721 playMsec1: 27689
decodeTime(): 6 playMsec0: 7721 playMsec1: 27689
decodeTime(): 6 playMsec0: 7721 playMsec1: 27689
decodeTime(): 6 playMsec0: 7721 playMsec1: 27689
decodeTime(): 6 playMsec0: 7721 playMsec1: 27689
decodeTime(): 6 playMsec0: 7721 playMsec1: 27689
decodeTime(): 6 playMsec0: 7721 playMsec1: 27689
decodeTime(): 6 playMsec0: 7721 playMsec1: 27689
decodeTime(): 6 playMsec0: 7721 playMsec1: 27689
decodeTime(): 7 playMsec0: 7721 playMsec1: 27689
decodeTime(): 7 playMsec0: 7721 playMsec1: 27689
done playing track

Perhaps reading two values like that separately is not the way to go. Would you have an idea on how to get that uint32_t value over a range registers 0x1E27 to 0x1E28? Once I get this going, I'll be sure to post a crude demo video as thanks.

Thanks for taking a look in advance! :heart:

A unit32 in 2 unit16's and want to combine to make 1 unit32.

from

I take it that E28 is the LSB bits as its rapidly changing and it supposed to be counting milliseconds. Actually, not sure of milli or micro seconds.

Anyways, make a variable of int32 out of 2 16 bit numbers, put the E27 into the high bits, and the E28 into the low bits to make a 32 bit number.

something like but not tested

unit32_t thingy32;

thingy32 = (uint32_t)E7<<16 | (unit32_t)E8;

Bit Shift Operators (<<, >>) — librambutan prerelease documentation.

Thanks @Idahowalker ! I'll give it a shot in about 3 hours when I'm back on my computer.

thingy32 = (uint32_t)E7<<16 | (unit32_t)E8;

Should any of those be uint16_t? Example:

thingy32 = (uint16_t)E7<<16 | (unit16_t)E8;

You could try it and see if it works. Would not E7E8 already be units16's? Frankly, you might not need them. To me, it makes sense.

A generic way but, endianess might matter (although not for this I guess):

uint32_t val = 0;
*((unsigned char *)&val + 0) = *((unsigned char *)&playMsec0 + 0);
*((unsigned char *)&val + 1) = *((unsigned char *)&playMsec0 + 1);
*((unsigned char *)&val + 2) = *((unsigned char *)&playMsec1 + 0);
*((unsigned char *)&val + 3) = *((unsigned char *)&playMsec1 + 1);

Alright, so here's what I have so far @Idahowalker (abbreviated from original example):

uint16_t playMsec0;
uint16_t playMsec1;
uint32_t thingy32;

...

    thingy32 = playMsec0<<16 | playMsec1;
    Serial.println(thingy32);

That outputs this repeatedly:

...
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
...

That might* be correct (need to check other factors shortly)? Not ideal, yet maybe an actual read.

Following the generic example @spokes:

  uint16_t playMsec0;
  uint16_t playMsec1;
  uint32_t val = 0;

...

    *((unsigned char *)&val + 0) = *((unsigned char *)&playMsec0 + 0);
    *((unsigned char *)&val + 1) = *((unsigned char *)&playMsec0 + 1);
    *((unsigned char *)&val + 2) = *((unsigned char *)&playMsec1 + 0);
    *((unsigned char *)&val + 3) = *((unsigned char *)&playMsec1 + 1);
    Serial.println(val);

output (same!):

...
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
506011177
...

Thanks for the examples. I'll need to play around a bit more to make sure I'm using a correct audio file and such. I'll respond once I find something. I'm also open to additional ideas if you think of anything. :slight_smile:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.