VS1000 User Interface handlers

Writing software that controls the system and peripherals such as displays, SD cards, Buttons, LEDs, Serial Ports etc.
Post Reply
User avatar
Panu
VSDSP Expert
Posts: 2829
Joined: Tue 2010-06-22 13:43

VS1000 User Interface handlers

Post by Panu »

To help with writing a new user interface handler for VLSI's stand-alone Ogg Vorbis player IC VS1000D, here's a small explanation on how the user interface works in VS1000 rom code.

When PlayCurrentFile() is called, the Ogg Vorbis decoder software routine becomes the "master" of the system. It runs, asking for blocks of data from the logical disk mapper as needed, and outputs samples to the audio sample buffer by calling AudioOutputSamples().

When there is free CPU time, the Vorbis decoder calls the Idle Hook, which is a RAM vector, which by default points to a ROM handler UserInterfaceIdleHook:

Code: Select all

void UserInterfaceIdleHook(void) {
	if (uiTrigger) {
		uiTrigger = 0;
		KeyScan();
	
		// Update LEDs
		{
			register u_int16 leds = LED1;
			if (player.pauseOn > 0 && (uiTime & 0x06) != 0) {
				leds = 0; /* power led flashes when in pause mode */
			}
			if (player.randomOn) {
				leds |= LED2;
			}
			PERIP(GPIO1_ODATA) = (PERIP(GPIO1_ODATA) & ~(LED1 | LED2)) | leds;
		}
	}
}
...which checks for a flag uiTrigger, which is set (by AudioOutputSamples function) 16 times per second, and then calls a ROM function KeyScan:

Code: Select all

/* KeyScan handles
   1) usb attach detection -- cancels playback and pause if attached
   2) Actual reading of keys.
   3) Key event handling -- hook-up the KeyEventHandler() if you don't like it.
*/
void KeyScan(void) {
	register u_int16 check = 0;
	register u_int16 keyNew;
	  
	if (USBIsAttached()) {
		cs.cancel = 1;
		player.pauseOn = 0;
	}

	  
	keyNew = ReadGPIO() & 31;
	if (PERIP(SCI_STATUS) & SCISTF_REGU_POWERBUT) {
		keyNew |= KEY_POWER;
	}
	  	  
	if (keyNew == keyOld) {
		if (++keyOldTime >= SHORT_LIMIT) {
			/* long press */
			check = keyOld | KEY_LONG_PRESS;
			if (keyOld & KEY_POWER) {
				/* Any combination containing power key requires longer press*/
				if (keyOldTime < OFF_LIMIT) /* 2 seconds */
				check = 0;
			}
		}
	} else {
		if ((s_int16)keyOldTime >= 0 && keyOldTime < SHORT_LIMIT) {
			/* Short press: was pressed < 0.5 seconds */
			check = keyOld;
		} else {
			check = KEY_RELEASED | KEY_LONG_PRESS; /*any long press released!*/
		}
		keyOld = keyNew;
		keyOldTime = 0;
	}
	
	if (check) {
		register const struct KeyMapping *p = currentKeyMap;
		keyCheck = check; /* for polled key handling */
		while (p->key) {
			if (check == p->key &&
			(p->event > 0 || keyOldTime <= SHORT_LIMIT)) {
				KeyEventHandler(p->event & 0x7fff);
			}
			p++;
		}
	}
}

auto u_int16 ReadGPIO(void) {
  register u_int16 i;
  register u_int16 mode = PERIP(GPIO0_MODE);

  PERIP(GPIO0_MODE) = mode & 0xff00U;
  /* Wait a bit... about 200us */
  for (i=0;i<10000;i++)
    PERIP(GPIO0_IDATA);
  i = PERIP(GPIO0_IDATA) & 255;
  PERIP(GPIO0_MODE) = mode;
  return i;
}

... which reads some of the GPIO pins and compares their states to a list of key mappings, pointed by *currentKeyMap. When a match is found, it calls a RAM vector KeyEventHandler, which by default points to a ROM function RealKeyEventHandler:

Code: Select all

void RealKeyEventHandler(enum keyEvent event) {
	switch (event) {
	
		case ke_previous:
			if (cs.playTimeSeconds < 5) {
				player.nextFile = player.currentFile - 1;
			} else {
				player.nextFile = player.currentFile;
			}
			player.nextStep = -1; /* skip unsupported files backward */
			cs.cancel = 1; /* jump to the new file */
			player.pauseOn = 0;
			break;
		
		case ke_next:
			player.nextStep = 1; /* skip unsupported files forward */
			cs.cancel = 1;
			player.pauseOn = 0;
			break;
		
		case ke_rewind:
			VorbisSkip(-5);
			break;
		
		case ke_forward:
			VorbisSkip(5);
			break;
		    
		case ke_volumeUp2:
			player.volume -= 1; /* volume up 1.0dB */
		/* fall through! */
		    
		case ke_volumeUp:
			player.volume -= 1; /* volume up 0.5dB */
			goto volCheck;
		    
		case ke_volumeDown2:
			player.volume += 1; /* volume down 1.0dB */
		/* fall through! */
		    
		case ke_volumeDown:
			player.volume += 1; /* volume down 0.5dB */
			volCheck:
			if (player.volume < -24) {
				player.volume = -24;
			} else if (player.volume > 180) {
				player.volume = 180;
			}
			PlayerVolume();
			break;
		    
		case ke_earSpeaker: /* 0, 18000U, 36000U, 54000U */
			if (earSpeakerReg >= 47535U) {
				earSpeakerReg = 0U;
			} else {
				earSpeakerReg += 18000U;
			}
			break;
		
		case ke_earSpeakerToggle: /* 0, 36000U */
			if (earSpeakerReg) {
				earSpeakerReg = 0U;
			} else {
				earSpeakerReg = 36000U;
			}
			break;
		
		case ke_randomToggleNewSong:
			if (!player.randomOn) {
				cs.cancel = 1;
				player.pauseOn = 0;
			}
		/* fall through! */
		
		case ke_randomToggle:
			player.randomOn ^= 1;
			srand(PERIP(TIMER_T0CNTL));
			break;
		    
		case ke_pauseToggle:
			player.pauseOn = player.pauseOn ? 0 : 1;
			break;
		    
		case ke_powerOff:
			PowerOff(); /* Can now be powered off even when attached to USB */
			break;
		    
		case ke_ff_faster:
			player.ffCount++;
			ff_action:
			{
				register s_int16 speed = 2 + (QsortLog2(player.ffCount/8));
				if (speed > 5) {
					cs.fastForward = 1;
					VorbisSkip(speed);
				} else {
					cs.fastForward = speed;
				}
			}
			break;
		
		case ke_ff_slower:
			if (player.ffCount) {
				--player.ffCount;
				goto ff_action;
			}
		/* fall through! */
		
		case ke_ff_off:
			cs.fastForward = 1;
			player.ffCount = 0;
			break;
		    
		default:
			break;
	}
}
...which adjusts the system structures player and CodecServices cs according to the passed enum keyEvent.

Code: Select all

enum keyEvent {
	ke_null = 0,
	ke_previous,
	ke_next,
	ke_rewind,
	ke_forward,
	ke_volumeUp,   /* +0.5dB */
	ke_volumeDown, /* -0.5dB */
	ke_earSpeaker,
	ke_earSpeakerToggle,
	ke_randomToggle,
	ke_randomToggleNewSong,
	ke_pauseToggle,
	ke_powerOff,
	ke_ff_faster,  /* increase play speed */
	ke_ff_slower,  /* decrease play speed */
	ke_ff_off,     /* back to normal play speed */
	ke_volumeUp2,  /* +1.0dB */
	ke_volumeDown2,/* -1.0dB */
};
extern struct Player {
	s_int16 totalFiles;
	s_int16 currentFile;
	s_int16 nextFile;
	s_int16 nextStep; /* 1 = next file, -1 = previous file */
	s_int16 pauseOn;
	s_int16 randomOn;
	s_int16 volume;
	s_int16 volumeOffset;
	u_int16 offDelay; /* pause timeout in 5 sec increments -> power off */
	u_int16 ffCount;
	u_int16 maxClock; /* max clock used by player: 7=3.5x, 6=3.0x etc.. */
} player;


struct CodecServices {
  /** Version number. 8 MSBs contain version number, 8 LSBs size of
      the structure in words. */
  u_int16 version;
  /** Read data from a file. If \a firstOdd is set, only the LSB of the
      first word is filled. If the last word is not completely filled
      (either \a firstOdd set or \a bytes is odd, but not both),
      only the MSB is changed. */
  u_int16 (*Read)(struct CodecServices *cs, u_int16 *ptr,
		  u_int16 firstOdd, u_int16 bytes);
  /** Skip data in a file. This should also be supported for streams. */
  u_int32 (*Skip)(struct CodecServices *cs, u_int32 bytes);
  /** Seek in a file. \a offset and \a whence are equivalent with their
      fseek() counterparts.
      If \a Seek is NULL, the input is a stream and cannot be seeked. */
  s_int16 (*Seek)(struct CodecServices *cs, s_int32 offset, s_int16 whence);
  /** Tell location in a file (in bytes). */
  s_int32 (*Tell)(struct CodecServices *cs);
  /** Output to audio file.
      \a data is a pointer to the data. There are \a n \a chan channel samples
      (Example: \a n = 32, \a cs->chan = 2, there are a total of 64 samples in
      \a data). */
  s_int16 (*Output)(struct CodecServices *cs, s_int16 *data, s_int16 n);
  /** Offers comments fields one character at the time. Special code
      0x4000U is reserved for end-of-line. 0xC000U means end of comments. */
  void (*Comment)(struct CodecServices *cs, u_int16 c);
  /** Spectrum analyzer hook. */
  void (*Spectrum)(struct CodecServices *cs, s_int16 __y *data, s_int16 n,
		   s_int16 ch);
  /** File size in bytes. If set to 0xFFFFFFFFU, then the file is a
      stream and the file size is not known. */
  u_int32 fileSize;
  /** Bytes left in a file. If set to 0xFFFFFFFFU, then the file is a
      stream and never ends. */
  u_int32 fileLeft;
  /** Point to move to in an audio file in seconds. When set to
      anything other than 0xFFFFU, the codec has the responsibility
      to jump to that point.
      The codec has the freedom of deciding for itself the actual
      landing point in the file. When the codec has reached its
      destination it will clear this variable, and the actual position
      in the file can be read from \a playTimeSeconds. If the codec
      cannot jump to a given point (e.g. the file is a stream and the
      jump point would require jumping backwards), goTo is silently
      cleared to 0xFFFFU.
  */
  u_int16 goTo;
  /** Request codec to cancel playing current file.
      To request cancellation, set this to a non-zero positive value. When the
      codec has finished cancelling, it will clear this value and
      return ceCancelled.
  */
  s_int16 cancel;
  /** Playback time from beginning of file in seconds. Updated by the codec.
      If set to -1, the codec doesn't know where it is in the file. */
  s_int32 playTimeSeconds;
  /** Samples played since last full second. Updated by the codec. If set to
      -1, the codec doesn't know where it is in the file. */
  s_int32 playTimeSamples;
  /** Total playback time in seconds. Updated by the codec. May be a
      changing estimate if an exact figure isn't available. 0xFFFFFFFFU
      if there is no information available. */
  u_int32 playTimeTotal;
  /** Sample rate. Updated by the codec. 0 if unknown. */
  u_int32 sampleRate;
  /** Number of channels. Updated by the codec. */
  u_int16 channels;
  /** Channel matrix. Updated by the codec. */
  enum ChannelMatrix matrix[MAX_SOURCE_CHANNELS];
  /** Average bitrate. Updated by the codec. May change during playback. */
  u_int32 avgBitRate;
  /** Current bitrate. Updated by the codec. May change during playback. */
  u_int32 currBitRate;
  /** Peak bitrate of the file. Updated by the codec.
      May change during playback. */
  u_int32 peakBitRate;
  /** Volume gain recommended by codec in 1/2 dB steps. Updated by the codec.*/
  s_int16 gain;
  /** Request codec to fast forward current file. If set, playback is
      requested at fastForward times normal playback speed.
      To stop fast forwarding, set value to 1 or 0. */
  u_int16 fastForward;
};
That's it.

To adjust the user interface, you can
- declare a new keymap, as explained in the Programmer's Guide,
- hook your own UserInterfaceIdleHook, and/or
- hook you own KeyHandler.

You can study our example codes to see alternative versions for these.
Have fun experimenting!
-Panu
HeLiO
Senior User
Posts: 94
Joined: Thu 2011-04-14 12:47

Re: VS1000 User Interface handlers

Post by HeLiO »

Hi again!
Still need a little more explanations about buttins work "scheme". I managed to make vol up\down buttons - and now they work correctly - due to "Trigger help trcik" (it was the problem why buttons didnt want to work).
But for now, i need help in upgrading the shut down way of my VS1000B. As i mentioned before -i need to power off my device by holding down Vol Dwn button, so it will look like this:
we hold it - the volume is going down --->> we acheved -24 value of player.volume --->> if we still hold button - we turn off device after 1-2 seconds (for example)

So, i figured out we need myevent handler where we definitely should watch for player.volume value - but after this im stucked, cause i dont know how to watch if it was long PRESS or not.. So ,i will put here some kind of my-code mask and hope for your help,
tnx


Code:

Code: Select all

const struct KeyMapping myKeyMap_mine[] = {
	{KEY_1, ke_volumeUp},								// Key A: Volume step up
	{KEY_1 | KEY_LONG_PRESS, ke_volumeUp},				// Key A: Volume up continuous
	{KEY_2, ke_volumeDown},								// Key B: Volume step dn
	{KEY_2 | KEY_LONG_PRESS, ke_volumeDown},			// Key B: Volume dn continuous
	{0, ke_null}										// End of key mappings
};


void MyKeyEventHandler(enum keyEvent event) 
{
       enum KeyEvent  Power_off_after_longpress;
       if (event == ke_volumeDown)
	{
		if ( (player.volume == -24) && (myKeyMap_mine[])
		/*SOMETHING TODO - DONT NOW WHAT..
               
                
                 Power_off_after_longpress = ke_poweroff;
                */
	}
	RealKeyEventHandler(Power_off_after_longpress);
}
Peter
HeLiO
Senior User
Posts: 94
Joined: Thu 2011-04-14 12:47

Re: VS1000 User Interface handlers

Post by HeLiO »

post should be placed in BUTTONs theme actually, written here by mistake
Post Reply