Please note as of Wednesday, August 15th, 2018 this wiki has been set to read only. If you are a TI Employee and require Edit ability please contact x0211426 from the company directory.

RemoTI-1.4.0-AdvancedRemote-Voice

From Texas Instruments Wiki
Jump to: navigation, search


CC2533-RF4CE-Kit-Pairing.jpg

This page describes the Voice feature of the Advanced Remote sample application that comes as part of the RemoTI-1.4.0 installer. It runs on a special version of the Advanced Remote hardware which is part of the CC2533 RF4CE Development Kit. You can request a sample of the Voice remote hardware here. For more information about the regular features of the Advanced Remote see this page.

Quick Start

Get hardware

You can request a sample of the Voice remote hardware here

Flash firmware

For details about how to flash the remote and dongle, please see RemoTI-1.4.0-AdvancedRemote. Instead of using the images proposed in those steps, use the images generated after adding the Voice feature.

Operate

Plug the USB dongle in a PC or MAC, or any other device, which supports USB Audio interface. The dongle will enumerate as a USB microphone. In Windows you should be able to find it in the list of recording devices, see figure on the right. Select USB CC2531 ZRC AUD as your default device. Now, after pairing with the dongle you can not only control the volume on your device, but you can talk to it too! Open google.com in Chrome and click on the microphone. While holding down the green button, talk into the hole on the remote. You may for example search for maps this way.

Figure 1: Find USB ZRC Audio Dongle in Recording Devices
Figure 2: Voice enabled Advanced Remote
Figure 3: Use Voice to Search Google

Installation

The Voice feature for RemoTI is delivered as a patch to RemoTI-1.4.0 installer.

Download

First, please download the standard RemoTI-1.4.0 installer. Then please download the patch using this link.

Install

Install RemoTI-1.4.0 as usual. If already installed, and you have made changes you want to keep, then make sure you have a copy of those changes before you proceed. When you have installed RemoTI-1.4.0 without modifications, then you are ready to unzip the archive you just installed with RemoTI-1.4.0-Voice-Installer-1.4.0. It might be a very good idea to keep a copy of the unmodified version of RemoTI-1.4.0 for reference, although you always have access to this by reinstalling RemoTI-1.4.0.

Accept all overwrites when you unzip the archive over the unmodified installation.

Project Path

The newly installed projects can be found here:

  • <installation path>\Projects\RemoTI\AdvancedVoiceRemote (see details)
  • <installation path>\Projects\RemoTI\ZrcAudioDongle (see details)

<installation path> is typically C:\Texas Instruments\RemoTI-CC253xDK-1.4.0.

Sample Application

This section describes the voice feature in details.

Main Features

  • 16kHz sampling rate, 16 bits resolution
  • 4:1 compression perform on-chip
  • Required throughput -- 64kbps
  • RF4CE application throughput up to 90kbps

Details

Block Diagram

The figure below shows the components used in the Voice enabled Advanced Remote. The external codec is connected to an analog microphone. It is configured via I2C and audio is retrieved via I2S. The master clock is provided by CC2533 which also acts as I2S slave. The I2S slave is implemented using the SPI module in slave operation, with word clock connected to the chip select pin.

Audio Flow Diagram

The figure below shows how the audio flows from the microphone --> the codec --> the SPI module on CC2533 --> via compression --> to RF4CE.

AdvancedRemoteVoiceFlowDiagram ZRCAudioDongleSide.png
AdvancedRemoteVoiceFlowDiagram RNPside.png.png

Audio Protocol

In order to transmit audio data over any RF link it is necessary to have a protocol in place. A protocol defines rules on how to interpret data. To save power we don't transmit audio continuously. As such an audio stream has a finite beginning and a finite end. So, in addition to indicate audio data, we can identify the beginning of transmission as well as the end. This has the added benefit of allowing the recipient to prepare itself for an audio stream. In case of RF4CE this involves locking the RF channel. To improve rate we can remove the need for re-transmission on different channels, which is generally needed to reacquire the channel in case the recipient changes channel during stream. Locking the RF channel removes this need.

For RF4CE there is a very easy way to transmit your own vendor specific data, which is simply called vendor specific data transmission. Since there is no official protocol to transmit audio data we use vendor specific data. Texas Instruments already defines several vendor specific data protocols, such as an Over-the-Air-Download protocol and a latency test protocol.


#define RTI_PROTOCOL_RAS                              0x50

Data Format

Start Frame
Number of bytes 1 1 1 1 1
Field Protocol ID CMD ID Sample Freq. Sample Width Mode
0x50 0x00 8: 8kHz
16: 16kHz
8, 12 or 16 bits 0: RAW
1: IMA-ADPCM
IMA-ADPCM Data Frame
Number of bytes 1 1 1 1 1 var
Field Protocol ID Seq # ID Header 1 Header 2 Header 3 Audio Samples
0x50 [7:3] [2:0]
0-31 0x01
Stop Frame
Number of bytes 1 1
Field Protocol ID CMD ID
0x50 0x02
Raw PCM Data Frame
Number of bytes 1 1 1 var
Field Protocol ID CMD ID Seq # Audio Samples
0x50 0x03 0-255 16 bits LSB PCM

Sequence Diagram

LPRF AudioProtocol RF4CE.png

Key Code to Command Mapping

Two buttons are remapped to support the voice feature.

Row/Column Code --> Key ID Look Up Table Key ID --> Command and Action Look Up Table
Index Row/Col Code Macro Name Macro Value
0 0b00 00 0000 NONE
28 0b00 01 1100 RSA_KEY_ID_GREEN 23
46 0b00 10 1110 RSA_KEY_ID_6 39
Index Action CERC Code HID Code Button Name
--
23 RSA_ACT_PUSH_TO_TALK N/A N/A GREEN (K2)
39 RSA_ACT_XMIT NUM_6 KEYBOARD_6 6

Special Function Actions

Macro Value Macro Name Function
0xB9 RSA_ACT_AUDIO rsaAudioKeyAction()
0xBA RSA_ACT_PUSH_TO_TALK rsaPushToTalkKeyAction()


Detailed guide to add Voice support for the Advanced Remote project

This section describes all steps required to add the Voice remote functionality to RemoTI-1.4.0. There are several aspects and gotchas to consider. Each code snippet that is added comes with a brief comment of why it is added.

Project options update

  1. Add $PROJ_DIR$\..\..\..\..\COMPONENTS\audio\include to the include paths
  2. Add to the pre-compiler options the following two defines
  3.   
      CC2533F96_VRC_REVB
      RAS_LIB
      
    
    IncludePath-Define-ForVoiceRemote.png
  4. We need to link with the audio library, in addition to the network library. Instead of adding the path to the compile options, we add a folder in the project, and then place both libraries there.
  5. AudioLibraryFolderInIARproject.png
    
  6. Uncheck the box which includes command line arguments for the linker. This is required since we add the library to the project in a new folder.
  7. UncheckIncludeCommandLineArgumentsForLinker.png
    
  8. We also need to add hal_codec.c to drive the codec
  9. AddAudioCodecDriverInIARproject.png
    
  10. To improve throughput and processing time we need to use compiler optimization settings for speed
  11. AudioIARprojectOptimizationForSpeed.png
    

Source files update

Application

In the main application code, rsa_point.c

  1. Include headers
  2. 
    #include "hal_codec.h"
    #include "ras_lib.h"
    
    
  3. Add stream event to the event list
  4. 
    // RSA Events
    #define RSA_EVT_INIT                    0x0001
    ...
    #ifdef RAS_LIB
    #define RSA_EVT_STOP_STREAM             0x0100
    #endif 
    
    
  5. Add stream state to the state enum
  6. 
    // RSA States
    enum
    {
    ...
     RSA_STATE_POLLING,
    #ifdef RAS_LIB
     RSA_STATE_STREAM,
    #endif
    };
    
    
  7. Add action define for audio and "push-to-talk". These defines serve as function pointer table index in the array rsaAppKeyActions. We simply subtract 0xB0 to use them as index.
  8. 
    #define RSA_ACT_TOGGLE_POLL        0xB8
    #if (defined RAS_LIB)
    #define RSA_ACT_AUDIO              0xB9
    #define RSA_ACT_PUSH_TO_TALK       0xBA
    #endif //RAS_LIB
    #define RSA_ACT_APP_END            0xBA
    
    
  9. Add action in key map, so that an action is associated with a button on the remote.
  10. 
    ...
     { RSA_ACT_XMIT, RTI_CERC_RECORD, HID_NONE },                   // 0b00 00 1000   - REC
     { RSA_ACT_XMIT, RTI_CERC_NUM_1, HID_KEYBOARD_1 },              // 0b00 00 1001   - 1
    #if (defined RAS_LIB)
     { RSA_ACT_AUDIO, RTI_CERC_RESERVED_1, HID_NONE },              // 0b00 00 1010   - AV
    #else
     { RSA_ACT_KEY_EX, RTI_CERC_RESERVED_1, HID_NONE },             // 0b00 00 1010   - AV
    #endif //RAS_LIB
     { RSA_ACT_PAIR, RTI_CERC_RESERVED_1, HID_NONE },               // 0b00 00 1100   - RED (K1)
    ...
     { RSA_ACT_UNPAIR, RTI_CERC_RESERVED_1, HID_NONE },             // 0b00 01 1011   - YELLOW (K3)
    #if (defined RAS_LIB)
     { RSA_PUSH_TO_TALK, RTI_CERC_RESERVED_1, HID_NONE },           // 0b00 01 1100   - GREEN (K2)
    #else
     { RSA_ACT_TEST_MODE, RTI_CERC_RESERVED_1, HID_NONE },          // 0b00 01 1100   - GREEN (K2)
    #endif
     { RSA_ACT_XMIT, RTI_CERC_NUM_8, HID_KEYBOARD_8 },              // 0b00 01 1101   - 8
    ...
    
    
  11. Add functions to handle key press, and callback from audio library
  12. 
    #if (defined RAS_LIB)
    static void rsaAudioKeyAction( rsaKeyState_t keyState );
    static void rsaPushToTalkKeyAction( rsaKeyState_t keyState );
    static void rsaAudioToggle(void);
    void RSA_StreamStopped( uint8 status);
    #endif //RAS_LIB
    
    
  13. Add audio key action functions to function array.
  14. 
    const rsaAppActionFn_t rsaAppKeyActions[] =
    {
    ...
      rsaTogglePollAction,
    #if (defined RAS_LIB)
      rsaAudioKeyAction,
      rsaPushToTalkKeyAction
    #endif //RAS_LIB
    
    
  15. Add state variable to track audio state
  16. 
    #ifdef RAS_LIB
    static uint8 rsaAudioState;
    #endif //RAS_LIB
    
    
  17. Initialize audio state when application initializes
  18. 
    void RSA_Init( uint8 taskId )
    {
    ...
    #ifdef RAS_LIB
      rsaAudioState = RSA_STATE_READY;
    #endif //RAS_LIB
    
    
  19. Add STOP_STREAM event handling
  20. 
    uint16 RSA_ProcessEvent( uint8 taskId, uint16 events )
    {
    ...
    #ifdef RAS_LIB
      if (events & RSA_EVT_STOP_STREAM)
      {
        if ( (rsaState == RSA_STATE_STREAM) )
        {
          //Rest the codec to put it in Power down mode
          Hal_codecReset();
          //Stop the MCLK
          Hal_codecMclockStop();
          RAS_Stop(); //==> Lib will call the stop confirmation callback
          
          //Technically, it s not ready, a packet might be pending in the queue...
          //Wait for the callback before chaning state
          //rsaState = RSA_STATE_READY;
        }
        return events ^ RSA_EVT_STOP_STREAM;
      }
    #endif 
    
    
  21. We need all processing that has to do with audio to happen fast. So we use local compile optimization pragmas. Note that this is redundant when we can afford to use speed optimization globally, however it is required when we have to select parts of the code to optimize for speed. When streaming audio we route the send data confirmation to the audio library. Following are the updates required in the RTI_SendDataCnf()
  22. 
    #ifdef RAS_LIB
    #pragma optimize=speed
    #endif
    void RTI_SendDataCnf( rStatus_t status )
    {
    #ifdef RAS_LIB    
      if( (rsaState == RSA_STATE_STREAM))
      {
        RAS_SendDataCnf(status);
      }
    #endif
    
    
  23. Add function to process callback from audio library
  24. 
    #if (defined RAS_LIB)
    void RSA_StreamStopped( uint8 status)
    { 
      rsaState = RSA_STATE_READY;
    
      if(status == 0)
      {
    #if (defined HAL_BUZZER) && (HAL_BUZZER == TRUE)
        /* Tell OSAL to not go to sleep because buzzer uses T3 */
        osal_pwrmgr_task_state( RSA_TaskId, PWRMGR_HOLD );
        
        RSA_PlayTune( RSA_TUNE_END  ); // ring buzzer to signal end
    #endif    
      }
      else
      {
        //Status fail, stop the clock...
        Hal_codecReset();
        //Stop the MCLK
        Hal_codecMclockStop();
        
    #if (defined HAL_BUZZER) && (HAL_BUZZER == TRUE)
        /* Tell OSAL to not go to sleep because buzzer uses T3 */
        osal_pwrmgr_task_state( RSA_TaskId, PWRMGR_HOLD );
    
        RSA_PlayTune( RSA_TUNE_ERROR  ); // ring buzzer to signal end error
    #endif    
      }
      
      rsaAudioState = RSA_STATE_READY;
    }
    #endif // RAS_LIB
    
    
  25. Add function to start/stop audio stream
  26. 
    #if (defined RAS_LIB)
    uint8 globalRsaCalledRAS_Start = FALSE;
    static void rsaAudioToggle(void)
    {
      if(  rsaDestIndex != RTI_INVALID_PAIRING_REF)
      {
        {
          if(rsaState == RSA_STATE_STREAM)          
          {
            if (rsaAudioState == RSA_STATE_STREAM)
            {
              //Rest the codec to put it in Power down mode
              Hal_codecReset();
              //Stop the MCLK
              Hal_codecMclockStop();
              RAS_Stop(); //==> Lib will call the stop confirmation callback
              
              //Technically, it s not ready, a packet might be pending in the queue...
              //Wait for the callback before changing state
              //rsaState = RSA_STATE_READY;
            }
          }
          else if (rsaState == RSA_STATE_READY)
          {
            if (rsaAudioState == RSA_STATE_READY)
            {
              rasCfg_t rasAudioCfg;
              rsaState = RSA_STATE_STREAM;
              
              //First start the MCLK and reset the codec.
              Hal_codecMclockStart(4000);
              Hal_codecReset();
              
              //Configure the codec
    #ifdef CC2533F96_VRC_REVB
              Hal_codecInit16kHz_ADC3101_mic_vrc_diff();    
    #else
              Hal_codecInit16kHz_ADC3101_mic_vrc();    
    #endif            
              //Hal_codecInit16kHz_ADC3101_lineIn_vrc();
              
              //Initialize the Audio Configuration Structure.
              rasAudioCfg.fs       = 16;
              rasAudioCfg.nbBits  =  16;
              rasAudioCfg.format   = 1;
              rasAudioCfg.ras_StreamStopped   = RSA_StreamStopped;
              rsaAudioState = RSA_STATE_STREAM;
              
              globalRsaCalledRAS_Start = TRUE;
              RAS_Start( rsaDestIndex , rasAudioCfg);
              globalRsaCalledRAS_Start = FALSE;
              
              //If the audio needs to run for a specific duration, set a timeout now.
              //if (rasCfg.duration)
              //  osal_start_timerEx(RSA_TaskId, RSA_EVT_STOP_AUDIO_CMD,rasCfg.duration*10 );
            }
          }
        }    
      }  
    }
    #endif // RAS_LIB
    
    

OSAL

In the OSAL setup code, rsa_osal.c perform the following steps

  1. Include header
  2. 
    #include "ras_lib.h"
    
    
  3. Add RAS_ProcessEvent function to the task processing functions array. Position in array reflects the priority given to the audio task
  4. 
    const pTaskEventHandlerFn tasksArr[] =
    {
      macEventLoop,
      RCN_ProcessEvent,
      RTI_ProcessEvent,
    #ifdef RAS_LIB
      RAS_ProcessEvent,
    #endif //RAS_LIB  
      gdpOriginator_ProcessEvent,
      RSA_ProcessEvent,
      Hal_ProcessEvent
    };
    
    
  5. Add OSAL init function for audio task. Again, order is not random, but reflects task priority
  6. 
    void osalInitTasks( void )
    {
    ...
      RTI_Init( taskID++ );
    #ifdef RAS_LIB
      RAS_Init( taskID++ );
    #endif //RAS_LIB  
      gdpOriginator_Init( taskID++ );
    
    

Driver

In the main file, rsa_main.c, add hardware initialization steps required for the codec.

  1. Include header
  2. 
    #include "hal_codec.h"
    
    
  3. Call the codec reset functions
  4. 
    #if (defined RAS_LIB)
      Hal_codecResetConfig();
      Hal_codecReset();
    #endif
    
    

In the CODEC driver file hal_codec.c, set global ticks to 0. The buzzer driver has been updated to use a divided global tick source (8MHz). Before starting the codec we must set it back to an undivided clock.

  1. While setting up the master clock, set the global tick speed back to undivided.
  2. 
    void Hal_codecMclockStart( uint16 freq )
    {
    ...
      /* Configure output pin as peripheral since we're using T4 to generate */
      MCLK_SEL |= (uint8)HAL_MCLK_GPIO_PINS;
    
      CLKCONCMD &= (TICKSPD_MASK ^ 0xFF);
      
      T4CTL = divider |
              HAL_T4_TIMER_CTL_CLEAR |
              opmode;
    
    

In the DMA driver file hal_dma.c, add hooks for the audio library.

  1. Include header
  2. 
    #include "ras_lib.h"
    
    
  3. Add RAS_LIB to precompiler clause to enable DMA interrupt, and to implement interrupt handler
  4. 
    void HalDmaInit( void )
    {
    

    ...

    #if (HAL_UART_DMA || \
      ((defined HAL_SPI) && (HAL_SPI == TRUE))  || \
      (defined RAS_LIB)  || \
      ((defined HAL_IRGEN) && (HAL_IRGEN == TRUE)))
     DMAIE = 1;
    #endif
    }
    
    #if (HAL_UART_DMA || \
       ((defined HAL_SPI) && (HAL_SPI == TRUE))  || \
       (defined RAS_LIB)   || \
       ((defined HAL_IRGEN) && (HAL_IRGEN == TRUE)))
    /******************************************************************************
     * @fn      HalDMAInit
    
    
  5. Add call to audio library's DMA interrupt handler
  6. 
    #if (defined RAS_LIB) 
      if ( HAL_DMA_CHECK_IRQ( HAL_DMA_CH_RX ) )
      {
        HAL_DMA_CLEAR_IRQ( HAL_DMA_CH_RX );
        RAS_DmaIsr();
      }
    #endif 
    
    

In the key scanner driver hal_key.c update to support voice remote layout

  1. Voice remote uses P0.7 instead of P1.4 for key scan, so update relevant macros.
  2. 
    #ifdef CC2533F96_VRC_REVB
      //VRC-RevB use P0.7 for key scan.
      #define HAL_KEY_P0_GPIO_PINS  ( HAL_KEY_BIT0 | HAL_KEY_BIT1 | HAL_KEY_BIT2 | HAL_KEY_BIT3 | HAL_KEY_BIT4 | HAL_KEY_BIT7)
    #else
      #define HAL_KEY_P0_GPIO_PINS  ( HAL_KEY_BIT0 | HAL_KEY_BIT1 | HAL_KEY_BIT2 | HAL_KEY_BIT3 | HAL_KEY_BIT4)
    #endif
    ...
    #ifndef CC2533F96_VRC_REVB
      //adv. remote use P1.4 for key scan.
      #define HAL_KEY_P1_GPIO_PINS  ( HAL_KEY_BIT4 )
    #endif
    ...
    #ifdef CC2533F96_VRC_REVB
      //VRC-RevB use P0.7 for key scan.
      #define HAL_KEY_P0_OUTPUT_PINS  ( HAL_KEY_BIT3 | HAL_KEY_BIT4 | HAL_KEY_BIT7)
    #else
      #define HAL_KEY_P0_OUTPUT_PINS  ( HAL_KEY_BIT3 | HAL_KEY_BIT4 )
    #endif
    ...
    #ifndef CC2533F96_VRC_REVB
      #define HAL_KEY_P1_OUTPUT_PINS  ( HAL_KEY_BIT4 )
    #endif
    ...
    #ifdef CC2533F96_VRC_REVB
      #define HAL_KEY_SHIFT_REGISTER_DATA_PIN   P0_7
    #else
      #define HAL_KEY_SHIFT_REGISTER_DATA_PIN   P1_4
    #endif
    
    
  3. In the initialization routine we no longer have to setup Port 1 since no pins are connected anymore
  4. 
    #ifndef CC2533F96_VRC_REVB
      P1SEL &= (uint8) ~HAL_KEY_P1_GPIO_PINS;
    #endif  
    ...
    #ifndef CC2533F96_VRC_REVB
      P1DIR |= (uint8) HAL_KEY_P1_OUTPUT_PINS;
    #endif
    
    

In the accelerometer driver hal_accel.c update to support voice remote layout

  1. Voice remote uses P1.3 instead of P1.2 for accelerometer interrupt and no pin to control power to the accelerometer, so update relevant macros.
  2. 
    #ifdef CC2533F96_VRC_REVB
    //VRC-Rev B: P1.3 used for Accel interrupt
    #define HAL_ACCEL_P1_GPIO_PINS  ( BV(3) )
    /* These defines indicate the direction of each pin */
    #define HAL_ACCEL_P1_INPUT_PINS  ( BV(3) )
    /* Which pins are used for key interrupts */
    #define HAL_ACCEL_P1_INTERRUPT_PINS   ( BV(3) )
    #else
    ...
    /* Which pins are used for key interrupts */
    #define HAL_ACCEL_P1_INTERRUPT_PINS   ( BV(2) )
    ...
    #endif
    
    
  3. Then update initialization code
  4. 
     /* Initialize outputs */
    #ifdef CC2533F96_VRC_REVB
      P1SEL &= (uint8) ~HAL_ACCEL_P1_GPIO_PINS;
      P1DIR &= (uint8) ~HAL_ACCEL_P1_INPUT_PINS;
    #else
    ...
     P1DIR &= (uint8) ~HAL_ACCEL_P1_INPUT_PINS;
    #endif
    
    

In the gyro driver hal_gyro.c update to support voice remote layout

  1. Voice remote uses P1.2 instead of P1.3 for gyro interrupt, and no pin to control power to the gyro, so update relevant macros.
  2. 
    
    1. ifdef CC2533F96_VRC_REVB
    /* The following define which port pins are being used by the gyro */
    #define HAL_GYRO_P0_GPIO_PINS  ( BV( 5 ) )
    #define HAL_GYRO_P1_GPIO_PINS  ( BV( 2 ) )
    /* These defines indicate the direction of each pin */
    #define HAL_GYRO_P0_OUTPUT_PINS  ( BV( 5 ) )
    #define HAL_GYRO_P1_OUTPUT_PINS  ( BV( 2 ) )
    /* Defines for each output pin assignment */
    #define HAL_GYRO_CLKIN_PIN  P0_5
    #define HAL_GYRO_INT_PIN    P1_2
    #else
    ...
    #define HAL_GYRO_INT_PIN    P1_3
    #endif
    
    
  3. Update initialization code to not control power pin, which no longer exist
  4. 
    #ifndef CC2533F96_VRC_REVB
      HAL_GYRO_POWER_PIN = 1;
    #endif  
    
    

Detailed guide to add Voice support for the ZRC Dongle project

This section describes all steps required to add the Voice remote functionality to RemoTI-1.4.0. There are several aspects and gotchas to consider. Each code snippet that is added comes with a brief comment of why it is added.

Project options update

  1. Add $PROJ_DIR$\..\..\..\..\COMPONENTS\audio\include to the include paths
  2. Add to the pre-compiler options the following two defines
  3.   
      VOICE_STREAM
      HAL_USB_DMA=TRUE
      
    
    IncludePath-Define-ForVoiceDongle.png
  4. We need to link with the audio library, in addition to the network library. Instead of adding the path to the compile options, we add a folder in the project, and then place both libraries there.
  5. AudioLibraryFolderInIARprojectTarget.png
    
  6. Uncheck the box which includes command line arguments for the linker. This is required since we add the library to the project in a new folder.
  7. UncheckIncludeCommandLineArgumentsForLinkerTarget.png
    
  8. To improve throughput and processing time we need to use compiler optimization settings for speed
  9. AudioIARprojectOptimizationForSpeed.png
    
  10. Add USB audio files usb_aud.c/h
  11. AddAudioUSBinIARproject.png
    

Source files update

Application

In the main application code, zrc_dongle.c

  1. Include headers
  2. 
    #ifdef VOICE_STREAM
    #include "usb_aud.h"
    #include "ratl_lib.h"
    #endif
    
    
  3. Add defines for audio buffer management, and audio decoding configuration structure
  4. 
    #ifdef VOICE_STREAM
    #define USB_AUDIO_NB_FRAME_TO_BUF               3
    #define USB_MAX_AUDIO_RX_QUEUE_SIZE            13
    #define AUDIO_DEAD_LINK_TIMER                1000   //if no packet is received during this time, link is dead, so stop everything.
    ratl_Config_t UsbDecodeconfig;
    #endif
    
    
  5. Add function prototype to handle stop event. Also add a global counter, which is used to count audio frames to toggle LED in a pattern based on received data
  6. 
    #ifdef VOICE_STREAM
    static void audio_EventStop( void );
    uint8 kk;
    #endif
    
    
  7. In the function Dongle_Init() add code to initialize and configure Audio Target library
  8. 
    #ifdef VOICE_STREAM
     // Want Audio buffer initialized as early as possible, so do it here
     extern void RATL_DecodeInit( void );
     extern CBAudioBuf_t pUsbAudioTxBuffer;
     RATL_DecodeInit();
     pUsbAudioTxBuffer.usbSamples = usbDataBuf;  
     pUsbAudioTxBuffer.size = MAX_USB_AUDIO_BUFFER_SIZE;
     
     
     UsbDecodeconfig.CircBuf            = &pUsbAudioTxBuffer;
     UsbDecodeconfig.MaxNbByteToDecode  = USB_NB_BYTES_TO_DECODE;
     UsbDecodeconfig.MaxRxQueueSize     = USB_MAX_AUDIO_RX_QUEUE_SIZE;
     UsbDecodeconfig.BufferingSize      = USB_AUDIO_NB_FRAME_TO_BUF;
     
     RATL_decodeCfg( &UsbDecodeconfig);
    #endif
    
    
  9. Add processing of Audio events to Dongle_ProcessEvent()
  10. 
    #ifdef VOICE_STREAM
      if (events & ZRC_DONGLE_EVT_DEAD_LINK )
      {    
        audio_EventStop();
        return (events ^ ZRC_DONGLE_EVT_DEAD_LINK );
      }
      if ( events & ZRC_DONGLE_EVT_AUD_DECODE )
      {
        if (RATL_DecodeAudioSamples()) 
        {
          return events;
        }
        else
        {
          return (events ^ ZRC_DONGLE_EVT_AUD_DECODE );
        }
      }
    #endif
    
    
  11. Add received data processing in RTI_ReceiveDataInd(). This includes detecting whether or not we are already receiving audio data, and if not already receiving disable Frequency Agility.
  12. 
    #if defined VOICE_STREAM
     else if (len > 0 && profileId == RTI_PROFILE_ZRC && (rxFlags & RTI_RX_FLAGS_VENDOR_SPECIFIC) )
     {
       if (pData[0] == RTI_PROTOCOL_RAS)
       {
         uint8 res;
         halIntState_t intState;
         audioDataEvent_t audData;
         audData.len = len-1;
         audData.pData = (pData+1);
         HAL_ENTER_CRITICAL_SECTION(intState);
         uint8 temp = usbStatus;
         HAL_EXIT_CRITICAL_SECTION(intState);
         osal_start_timerEx(zrcDongleTaskId, ZRC_DONGLE_EVT_DEAD_LINK, AUDIO_DEAD_LINK_TIMER);       
         if ( RATL_COPY_ALLOWED == (res = RATL_ProcessDataPacket( &audData, temp)))
         {
           streamStatus = STREAM_START_COPY;
         }
         else if (RATL_STOPPED == res)
         {
           //Stop Cmd Receive
            audio_EventStop();
         }
         else if (RATL_DECODING_ALLOWED == res)
         {
           //Start Decoding (needed for buffering...)
           osal_set_event(zrcDongleTaskId, ZRC_DONGLE_EVT_AUD_DECODE);
         }
         //Play LED:
         if (temp == USB_START)
         {
            if (kk++ == 50)
             HalLedSet(HAL_LED_1, HAL_LED_MODE_ON);
           if (kk >= 100)
           {
             HalLedSet(HAL_LED_1, HAL_LED_MODE_OFF);
             HalLedSet(HAL_LED_2, HAL_LED_MODE_OFF);
             kk = 0;
           }
         }
         else
         {
           if (kk++ == 50)
             HalLedSet(HAL_LED_2, HAL_LED_MODE_ON);
           if (kk >= 100)
           {
             HalLedSet(HAL_LED_1, HAL_LED_MODE_OFF);
             HalLedSet(HAL_LED_2, HAL_LED_MODE_OFF);
             kk = 0;
           }
         }                
       }
     }
    #endif
    
    
  13. Implement function to handle stop stream event
  14. 
    #ifdef VOICE_STREAM
    static void audio_EventStop( void )
    {
      streamStatus = STREAM_STOP;
     
      RATL_audioStop();
     
      osal_clear_event(zrcDongleTaskId, ZRC_DONGLE_EVT_AUD_DECODE);
      osal_stop_timerEx(zrcDongleTaskId, ZRC_DONGLE_EVT_DEAD_LINK);
      osal_clear_event(zrcDongleTaskId, ZRC_DONGLE_EVT_DEAD_LINK);
    }
    #endif
    
    

In zrc_dongle.h

  1. Add audio stream events to the list
  2. 
    #ifdef VOICE_STREAM
    #define ZRC_DONGLE_EVT_AUD_DECODE         0x0010 // event to decode audio frame
    #define ZRC_DONGLE_EVT_DEAD_LINK          0x0020 // timed event to detect dead link
    #endif
    
    

Profile

Audio data are sent as Texas Instruments vendor specific ZRC data. TI use the first byte of the payload to indicate the specific TI protocol. For Audio, add:

#define RTI_PROTOCOL_RAS            0x50

Driver

USB

In zrc_usb.h

  1. Add audio interface enumeration macros
  2. 
    #ifdef VOICE_STREAM
    #define INTERFACE_NUMBER_AUDIO_CONTROL      0x02
    #define INTERFACE_NUMBER_AUDIO_STREAM       0x03
    #define INTERFACE_NUMBER_ZRC                0x04
    #else
    #define INTERFACE_NUMBER_ZRC                0x02
    #endif
    
    
  3. Modify endpoint macros to better reflect endpoint direction. Also update code to use these new macros.
  4. 
    //#define ZRC_PROXY_RNP_EP_OUT_ADDR        0x03
    //#define ZRC_PROXY_RNP_EP_IN_ADDR         ZRC_PROXY_RNP_EP_OUT_ADDR
    //#define ZRC_PROXY_ZRC_EP_OUT_ADDR        0x04
    //#define ZRC_PROXY_ZRC_EP_IN_ADDR         0x05
    
    #define CE_ENPOINT_ADDR                 0x02  
    #define RNP_ENPOINT_ADDR                0x03  
    #define ZRC_ENPOINT_ADDR                0x04  
    #define AUD_ENPOINT_ADDR                0x05  
    
    #define IN_ENPOINT_ADDR                 0x80
    #define OUT_ENPOINT_ADDR                0x00
    
    #define CE_EP_IN_ADDR                   IN_ENPOINT_ADDR  | CE_ENPOINT_ADDR 
    #define CE_EP_OUT_ADDR                  OUT_ENPOINT_ADDR | CE_ENPOINT_ADDR
    #define RNP_EP_IN_ADDR                  IN_ENPOINT_ADDR  | RNP_ENPOINT_ADDR
    #define RNP_EP_OUT_ADDR                 OUT_ENPOINT_ADDR | RNP_ENPOINT_ADDR
    #define ZRC_EP_IN_ADDR                  IN_ENPOINT_ADDR  | ZRC_ENPOINT_ADDR
    #define ZRC_EP_OUT_ADDR                 OUT_ENPOINT_ADDR | ZRC_ENPOINT_ADDR
    #define AUDIO_EP_IN_ADDR                IN_ENPOINT_ADDR  | AUD_ENPOINT_ADDR
    #define AUDIO_EP_OUT_ADDR               OUT_ENPOINT_ADDR | AUD_ENPOINT_ADDR
    #define RNP_EP_OUT_PACKET_LEN           ZRC_DONGLE_OUTBUF_SIZE
    
    

In zrc_usb.c

  1. Update macros to account for the extended USB descriptor, now that audio is added
  2. 
    #ifdef VOICE_STREAM
    #define ZRC_USB_NUM_INTERFACES                5
    #else
    #define ZRC_USB_NUM_INTERFACES                3
    #endif
    ...
    #ifdef VOICE_STREAM
    #define DBLBUF_LUT_INFO_SIZE                 (sizeof(DBLBUF_LUT_INFO) * 6)
    #else //VOICE_STREAM
    #define DBLBUF_LUT_INFO_SIZE                 (sizeof(DBLBUF_LUT_INFO) * 3)
    #endif //VOICE_STREAM
    
    
  3. Add audio descriptors
  4. 
    #ifdef VOICE_STREAM
    // Interface Descriptor for audio control 
    const uint8 usb_iface_desc_audio_control[] = {
      DESC_SIZE_INTERFACE,            // bLength
      DESC_TYPE_INTERFACE,            // bDescriptorType
      INTERFACE_NUMBER_AUDIO_CONTROL, // bInterfaceNumber
      0x00,                           // bAlternateSetting (none)
      0x00,                           // bNumEndpoints (none)
      0x01,                           // bInterfaceClass (AUDIO)
      0x01,                           // bInterfaceSubClass (AUDIO CONTROL)
      0x00,                           // bInterfaceProcotol (N/A)
      0x00                            // iInterface
    };
    
    //Audio Descriptor: HEADER
    const uint8 usb_audio_desc_header[] = {
      DESC_SIZE_AUDIO_HEADER,         // bLength
      DESC_TYPE_CS_INTERFACE,         // bDescriptorType
      DESC_AUDIO_TYPE_HEADER,         // bDescriptorSubType
      0x01, 0x00,                     // bcdADC (1.0)
                                      // wTotalLength   //SIZE EQUAL TO SUM OF DESCRIPTOR of the audio control descriptor, including tis header: 
                                      //  == size of (usb_audio_desc_input_term) + 
                                      //  == size of (usb_audio_desc_feat_unit)  +
      0x27, 0x00,                     //  == size of (usb_audio_desc_out_term) 
      0x01,                           // bInCollection (1 stream interface)
      0x03,                           // bInterfaceNr (interface 4 is stream)
    };
    
    //Audio Descriptor: INPUT TERMINAL
    const uint8 usb_audio_desc_input_term[] = {
      DESC_SIZE_AUDIO_INPUT_TERM,     // bLength
      DESC_TYPE_CS_INTERFACE,         // bDescriptorType
      DESC_AUDIO_TYPE_INPUT_TERM,     // bDescriptorSubType
      0x01,                           // bTerminalID
      0x01, 0x02,                     // wTerminalType
      0x00,                           // bAssocTerminal (none)
      0x01,                           // bNrChannel
      0x00, 0x00,                     // wChannelConfig //No predifined spatial position
      0x00,                           //iChannelNames
      0x00,                           //iTerminal (none)
    };
    
    //Audio Descriptor: FEATURE UNIT
    const uint8 usb_audio_desc_feat_unit[] = {
      DESC_SIZE_AUDIO_FEAT_UNIT,  // bLength (9)
      DESC_TYPE_CS_INTERFACE,     // bDescriptorType (CS_INTERFACE)
      DESC_AUDIO_TYPE_FEAT_UNIT,  // bDescriptorSubtype (FEATURE_UNIT)
      0x02,                       // bUnitID (2)
      0x01,                       // bSourceID (input terminal 1)
      0x01,                       // bControlSize (2 bytes)
      0x03, 0x00,                 // Master controls (volume and mute supported)
      0x00                        // iFeature (none)
    };
    
    //Audio Descriptor: OUTPUT TERMINAL
    const uint8 usb_audio_desc_out_term[] = {
      DESC_SIZE_AUDIO_OUTPUT_TERM,    // bLength
      DESC_TYPE_CS_INTERFACE,         // bDescriptorType
      DESC_AUDIO_TYPE_OUTPUT_TERM,    // bDescriptorSubType
      0x03,                           // bTerminalID
      0x01, 0x01,                     // wTerminalType
      0x00,                           // bAssocTerminal (none)
      0x02,                           // bSourceID (feature unit 2)
      0x00                            // iTerminal (none)
    };
    
    // Interface Descriptor for audio stream 
    const uint8 usb_iface_desc_audio_stream0[] = {
      DESC_SIZE_INTERFACE,            // bLength
      DESC_TYPE_INTERFACE,            // bDescriptorType
      INTERFACE_NUMBER_AUDIO_STREAM , // bInterfaceNumber
      0x00,                           // bAlternateSetting (0)
      0x00,                           // bNumEndpoints (none)
      0x01,                           // bInterfaceClass (AUDIO)
      0x02,                           // bInterfaceSubClass (AUDIO STREAM)
      0x00,                           // bInterfaceProcotol (N/A)
      0x00                            // iInterface
    };
    
    // Interface Descriptor for audio stream, ALTERNATE SETTING 
    const uint8 usb_iface_desc_audio_stream1[] = {
      DESC_SIZE_INTERFACE,            // bLength
      DESC_TYPE_INTERFACE,            // bDescriptorType
      INTERFACE_NUMBER_AUDIO_STREAM , // bInterfaceNumber
      0x01,                           // bAlternateSetting (1)
      0x01,                           // bNumEndpoints (1)
      0x01,                           // bInterfaceClass (AUDIO)
      0x02,                           // bInterfaceSubClass (AUDIO STREAM)
      0x00,                           // bInterfaceProcotol (N/A)
      0x00                            // iInterface
    };
    
    //Audio Descriptor: aduio Stream, general
    const uint8 usb_audio_desc_AS_general[] = {
      DESC_SIZE_AUDIO_STREAM,         // bLength
      DESC_TYPE_CS_INTERFACE,         // bDescriptorType
      DESC_AUDIO_TYPE_GENERAL,        // bDescriptorSubType
      0x03,                           // bTerminalLink (terminal 3)
      0x00,                           // bDelay (none)
      0x01, 0x00                      // wFormatTag (PCM format)
    };
    
    //Audio Descriptor: FORMAT TYPE
    const uint8 usb_audio_desc_AS_format_type[] = {
      DESC_SIZE_AUDIO_FORMAT_TYPEI,       // bLength
      DESC_TYPE_CS_INTERFACE,             // bDescriptorType
      DESC_AUDIO_TYPE_FORMAT_TYPE,        // bDescriptorSubType
      0x01,                               // bFormatType (TYPE_I)
      0x01,                               // bNrChannels (1)
      0x02,                               // bSubFrameSize (2)
      0x10,                               // bBitResolution (16)
      0x01,                               // bSamFreqType (1 sampling frequency)
      0x80,                               // 16,000 Hz (byte 0)
      0x3E,                               // 16,000 Hz (byte 1)
      0x00                                // 16,000 Hz (byte 2)
    };
    
    //Audio Descriptor: Audio Stream endpoint
    const uint8 usb_audio_desc_AS_endpoint[] = {
      DESC_SIZE_AUDIO_ENDPOINT,       // bLength
      DESC_TYPE_ENDPOINT,             // bDescriptorType
      AUDIO_EP_IN_ADDR,               // bEndpointAddress (EP1 in)
      0x05,                           // bmAttributes (isochronous + asynchronous)
      0x20, 0x00,                     // wMaxPacketSize (256)
      0x01,                           // bInterval (1 millisecond)
      0x00,                           // bRefresh (0)
      0x00                            // bSynchAddress (no synchronization) 
    };
    
    //Audio Descriptor: Isochronous endpoint
    const uint8 usb_audio_desc_AS_isoendpoint[] = {
      DESC_SIZE_AUDIO_ISO_ENDPOINT,   // bLength
      DESC_TYPE_CS_ENDPOINT,          // bDescriptorType
      DESC_AUDIO_TYPE_EP_GENERAL,     // bDescriptorSubType
      0x00,                           // bmAttributes (none)
      0x02,                           // bLockDelayUnits (PCM samples)
      0x00, 0x00                      // wLockDelay (0)
     };
    #endif 
    
    
  5. Update Product String
  6. 
    const uint8 string2Product[] = {
    #ifdef VOICE_STREAM
      38,                          // bLength
    #else
      30,						   // bLength
    #endif
      DESC_TYPE_STRING,            // bDescriptorType
      'U', 0,                      // unicode string
    ...
      'C', 0,
    #ifdef VOICE_STREAM
      ' ', 0,
      'A', 0,
      'U', 0,
      'D', 0
    #endif
    };
    
    
  7. Update all macros to define endpoint enumeration, and also update descriptors to use it
  8.    ZRC_PROXY_RNP_EP_OUT_ADDR
    
    -->
       RNP_EP_OUT_ADDR
    
       ZRC_PROXY_RNP_EP_IN_ADDR
    
    -->
       RNP_EP_IN_ADDR
    
       ZRC_PROXY_ZRC_EP_OUT_ADDR
    
    -->
       ZRC_EP_OUT_ADDR
    
       ZRC_PROXY_ZRC_EP_IN_ADDR
    
    -->
       ZRC_EP_IN_ADDR
    
    
    -  0x85,                        // bEndpointAddress
    +  ZRC_EP_IN_ADDR,              // bEndpointAddress
    ...
    -  0x04,                        // bEndpointAddress
    +  ZRC_EP_OUT_ADDR,             // bEndpointAddress
    ...
    -  0x83,                        // bEndpointAddress
    +  RNP_EP_IN_ADDR,              // bEndpointAddress
    ...
    -  0x82,                        // bEndpointAddress
    +  CE_EP_IN_ADDR,               // bEndpointAddress
    
    
  9. Add USB audio descriptor components offset defines
  10. 
    #ifdef VOICE_STREAM
    #define ZRC_AUD_CTRL_IFACE_DESC       (ZRC_CE_IN_EP_DESC                  +  DESC_SIZE_ENDPOINT)
    #define ZRC_AUD_CTRL_HDR_DESC         (ZRC_AUD_CTRL_IFACE_DESC          +  DESC_SIZE_INTERFACE)
    #define ZRC_AUD_CTRL_IN_TERM_DESC     (ZRC_AUD_CTRL_HDR_DESC            +  DESC_SIZE_AUDIO_HEADER)
    #define ZRC_AUD_CTRL_FEAT_DESC        (ZRC_AUD_CTRL_IN_TERM_DESC        +  DESC_SIZE_AUDIO_INPUT_TERM)
    #define ZRC_AUD_CTRL_OUT_TERM_DESC    (ZRC_AUD_CTRL_FEAT_DESC           +  DESC_SIZE_AUDIO_FEAT_UNIT)
    //#define ZRC_AUD_CTRL_OUT_TERM_DESC    (ZRC_AUD_CTRL_IN_TERM_DESC           +  DESC_SIZE_AUDIO_INPUT_TERM)
    #define ZRC_AUD_STREAM_IFACE0_DESC    (ZRC_AUD_CTRL_OUT_TERM_DESC       +  DESC_SIZE_AUDIO_OUTPUT_TERM)
    #define ZRC_AUD_STREAM_IFACE1_DESC    (ZRC_AUD_STREAM_IFACE0_DESC       +  DESC_SIZE_INTERFACE)
    #define ZRC_AUD_STREAM_GENERAL_DESC   (ZRC_AUD_STREAM_IFACE1_DESC       +  DESC_SIZE_INTERFACE)
    #define ZRC_AUD_STREAM_FORMAT_DESC    (ZRC_AUD_STREAM_GENERAL_DESC      +  DESC_SIZE_AUDIO_STREAM)
    #define ZRC_AUD_STREAM_EP_DESC        (ZRC_AUD_STREAM_FORMAT_DESC       +  DESC_SIZE_AUDIO_FORMAT_TYPEI)
    #define ZRC_AUD_STREAM_ISO_EP_DESC    (ZRC_AUD_STREAM_EP_DESC           +  DESC_SIZE_AUDIO_ENDPOINT)
    #define ZRC_IFACE_DESC                (ZRC_AUD_STREAM_ISO_EP_DESC       +  DESC_SIZE_AUDIO_ISO_ENDPOINT)
    #else //VOICE STREAM
    #define ZRC_IFACE_DESC                (ZRC_CE_IN_EP_DESC       +  DESC_SIZE_AUDIO_ISO_ENDPOINT)
    #endif //VOICE STREAM
    
    
  11. Add to total length calculation
  12. 
    #ifdef VOICE_STREAM  
      // Add Audio class descriptor to the total length.
      wTotalLength += DESC_SIZE_INTERFACE; // Audio Control interface
      wTotalLength += DESC_SIZE_AUDIO_HEADER; // Audio Control Header descriptor
      wTotalLength += DESC_SIZE_AUDIO_INPUT_TERM; // Audio Control input terminal descriptor
      wTotalLength += DESC_SIZE_AUDIO_FEAT_UNIT; // Audio Control feature unit descriptor
      wTotalLength += DESC_SIZE_AUDIO_OUTPUT_TERM; // Audio Control output terminal descriptor
      wTotalLength += DESC_SIZE_INTERFACE; // Audio Stream interface, alternate 0
      wTotalLength += DESC_SIZE_INTERFACE; // Audio Stream interface, alternate 1
      wTotalLength += DESC_SIZE_AUDIO_STREAM; // Audio Stream, general descriptor
      wTotalLength += DESC_SIZE_AUDIO_FORMAT_TYPEI; // Audio Stream, format type  descriptor
      wTotalLength += DESC_SIZE_AUDIO_ENDPOINT; // Audio Stream, endpoint  descriptor
      wTotalLength += DESC_SIZE_AUDIO_ISO_ENDPOINT; // Audio Stream, isochronous endpoint  descriptor
    #endif  
    
    
  13. Add interfaces to the dynamically built descriptor
  14. 
    #ifdef VOICE_STREAM  
    //Add  Voice descriptor and interface
       ptr = osal_memcpy(ptr, usb_iface_desc_audio_control,    sizeof(usb_iface_desc_audio_control));
       ptr = osal_memcpy(ptr, usb_audio_desc_header,           sizeof(usb_audio_desc_header));
       ptr = osal_memcpy(ptr, usb_audio_desc_input_term,       sizeof(usb_audio_desc_input_term));
       ptr = osal_memcpy(ptr, usb_audio_desc_feat_unit,        sizeof(usb_audio_desc_feat_unit));
       ptr = osal_memcpy(ptr, usb_audio_desc_out_term,         sizeof(usb_audio_desc_out_term));
       ptr = osal_memcpy(ptr, usb_iface_desc_audio_stream0,    sizeof(usb_iface_desc_audio_stream0));
       ptr = osal_memcpy(ptr, usb_iface_desc_audio_stream1,    sizeof(usb_iface_desc_audio_stream1));
       ptr = osal_memcpy(ptr, usb_audio_desc_AS_general,       sizeof(usb_audio_desc_AS_general));
       ptr = osal_memcpy(ptr, usb_audio_desc_AS_format_type,   sizeof(usb_audio_desc_AS_format_type));
       ptr = osal_memcpy(ptr, usb_audio_desc_AS_endpoint,      sizeof(usb_audio_desc_AS_endpoint));
       ptr = osal_memcpy(ptr, usb_audio_desc_AS_isoendpoint,   sizeof(usb_audio_desc_AS_isoendpoint));
    #endif
    
    
  15. There are two additional USB interfaces if VOICE_STREAM is defined
  16. 
    #ifdef VOICE_STREAM  
       *ptr = 4; // bNumInterfaces
    #else
       *ptr = 2; // bNumInterfaces
    #endif
    
    
  17. Add endpoint configuration for audio interfaces
  18. 
    #ifdef VOICE_STREAM
      usbDescriptorMarker.pUsbDblbufLut[2].pInterface = (USB_INTERFACE_DESCRIPTOR *)ZRC_AUD_CTRL_IFACE_DESC;
      usbDescriptorMarker.pUsbDblbufLut[2].inMask = 0;
      usbDescriptorMarker.pUsbDblbufLut[2].outMask = 0;
    
      usbDescriptorMarker.pUsbDblbufLut[3].pInterface = (USB_INTERFACE_DESCRIPTOR *)ZRC_AUD_STREAM_IFACE0_DESC;
      usbDescriptorMarker.pUsbDblbufLut[3].inMask = 0;
      usbDescriptorMarker.pUsbDblbufLut[3].outMask = 0;
    
      usbDescriptorMarker.pUsbDblbufLut[4].pInterface = (USB_INTERFACE_DESCRIPTOR *)ZRC_AUD_STREAM_IFACE1_DESC;
      usbDescriptorMarker.pUsbDblbufLut[4].inMask = (1<<AUD_ENPOINT_ADDR ); //0x02; // Set EP1 to double buffering on IN
      usbDescriptorMarker.pUsbDblbufLut[4].outMask = 0;
    
      usbDescriptorMarker.pUsbDblbufLut[5].pInterface = (USB_INTERFACE_DESCRIPTOR *)ZRC_IFACE_DESC;
      usbDescriptorMarker.pUsbDblbufLut[5].inMask =  0; 
      usbDescriptorMarker.pUsbDblbufLut[5].outMask = 0;
    #else
      usbDescriptorMarker.pUsbDblbufLut[2].pInterface = (USB_INTERFACE_DESCRIPTOR *)ZRC_IFACE_DESC;
      usbDescriptorMarker.pUsbDblbufLut[2].inMask =  0; 
      usbDescriptorMarker.pUsbDblbufLut[2].outMask = 0;
    #endif
    
    

In usb_zrc.h

  1. Add audio events
  2. 
    #ifdef VOICE_STREAM
    #define AUDIOD_CMD_START        0x04
    #define AUDIOD_CMD_STOP         0x08
    #define AUDIOD_STREAM_STARTED   0x10
    #define AUDIOD_STREAM_REQUESTED 0x20
    #endif
    
    

In the usb_zrc_class_request.c

  1. Add USB audio class request handles
  2. 
    #if defined VOICE_STREAM
    uint8 audioCVolumeMin[2]={0x00,0x80};
    uint8 audioCVolumeMax[2]={0x00,0x06};
    uint8 audioCVolumeRes[2]={0x00,0x01};
    uint8 audioCVolumeCur[2]={0x00,0x00};
    
    uint8 audioMuteValue = 0;
    
    void usbcrGetMin(void) 
    {
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        // Is the length stall correct?
        if ( (usbSetupHeader.length == 0) || (HI_UINT16(usbSetupHeader.index) > 2)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } 
        else 
        {
          switch (HI_UINT16(usbSetupHeader.value)) 
          {
            
          case 2: //Volume
            usbSetupData.pBuffer = (uint8 *)&audioCVolumeMin;
            usbSetupData.bytesLeft = 2;
            usbfwData.ep0Status = EP_TX;
            break;
          default:
            usbfwData.ep0Status = EP_STALL;
            break;
          }
        }
      }
    }// usbcrGetReport
    
    void usbcrGetMax(void) 
    {
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        // Is the length stall correct?
        if ( (usbSetupHeader.length == 0) || (HI_UINT16(usbSetupHeader.index) > 2)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } 
        else 
        {
          switch (HI_UINT16(usbSetupHeader.value)) 
          {  
          case 2: //Volume
            usbSetupData.pBuffer = (uint8 *)&audioCVolumeMax;
            usbSetupData.bytesLeft = 2;
            usbfwData.ep0Status = EP_TX;
            break;
          default:
            usbfwData.ep0Status = EP_STALL;
            break;  
          }
        }
      }
    }// usbcrGetReport
    
    void usbcrGetRes(void) 
    {
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        // Is the length stall correct?
        if ( (usbSetupHeader.length == 0) || (HI_UINT16(usbSetupHeader.index) > 2)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } else 
        {
          switch (HI_UINT16(usbSetupHeader.value)) 
          {
          case 2:
            usbSetupData.pBuffer = (uint8 *)&audioCVolumeRes;
            usbSetupData.bytesLeft = 2;
            usbfwData.ep0Status = EP_TX;
            break;
          default:
            usbfwData.ep0Status = EP_STALL;
            break;  
          }
        }
      }
    }// usbcrGetReport
    
    
    void usbcrGetCur(void) 
    {
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        // Is the length stall correct?
        if ( (usbSetupHeader.length == 0) || (HI_UINT16(usbSetupHeader.index) > 2)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } 
        else 
        {
          switch (HI_UINT16(usbSetupHeader.value)) 
          {  
          case 1:
            usbSetupData.pBuffer = (uint8 *)&audioMuteValue;
            usbSetupData.bytesLeft = 1;
            usbfwData.ep0Status = EP_TX;
            break;
          case 2:
            usbSetupData.pBuffer = (uint8 *)&audioCVolumeCur;
            usbSetupData.bytesLeft = 2;
            usbfwData.ep0Status = EP_TX;
            break;
          default:
            usbfwData.ep0Status = EP_STALL;
            break;  
          }
        }
      }
    }// usbcrGetReport
    
    uint8 *pVolumeCur;
    uint8 *pMuteCur;
    
    void usbcrSetCur(void) 
    {
      // Received header
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        // Is the length stall correct?
        if ( (usbSetupHeader.length == 0) || (HI_UINT16(usbSetupHeader.index) > 2)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } 
        else 
        {
          switch (HI_UINT16(usbSetupHeader.value)) 
          {  
          case 1: //Mute
            pMuteCur = (uint8 *)osal_mem_alloc(usbSetupHeader.length);
            
            usbfwData.ep0Status = EP_RX;
            
            usbSetupData.bytesLeft = usbSetupHeader.length;
            usbSetupData.pBuffer = pMuteCur;
            break;
          case 2: //Volume
            pVolumeCur = (uint8 *)osal_mem_alloc(usbSetupHeader.length);
            
            usbfwData.ep0Status = EP_RX;
            
            usbSetupData.bytesLeft = usbSetupHeader.length;
            usbSetupData.pBuffer = pVolumeCur;
            break;
          default:
            usbfwData.ep0Status = EP_STALL;
            break;  
          }
        }
      }
      else if (usbfwData.ep0Status == EP_RX) 
      {
        switch (HI_UINT16(usbSetupHeader.value)) 
        {
        case 1: //Mute
          // Send OUT report up to Application
          //zrcPxyServeHIDClassRequests(usbSetupHeader.request, zrcClassRequestDataOut);
          audioMuteValue = *pMuteCur;
          osal_mem_free(pMuteCur);
          break;
        case 2: //Volume
          // Send OUT report up to Application
          //zrcPxyServeHIDClassRequests(usbSetupHeader.request, zrcClassRequestDataOut);
          audioCVolumeCur[0] = pVolumeCur[0];
          audioCVolumeCur[1] = pVolumeCur[1];
          osal_mem_free(pVolumeCur);
          break;
        default:
          break;
        }
      }
    }// usbcrSetReport
    
    uint8 *pVolumeRes;
    void usbcrSetRes(void)
    {
      // Received header
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        // Is the length stall correct?
        if ( (usbSetupHeader.length == 0) || (HI_UINT16(usbSetupHeader.index) > 2)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } 
        else 
        {
          switch (HI_UINT16(usbSetupHeader.value)) 
          {  
            //              case 2: //Volume
            //#ifdef IO_BENCHMARK
            //              HAL_BM_TURN_ON_IO1 (); //RF4CE task on
            //#endif  
            //                pVolumeRes = (uint8 *)osal_mem_alloc(usbSetupHeader.length);
            //                
            //                usbfwData.ep0Status = EP_RX;
            //
            //                usbSetupData.bytesLeft = usbSetupHeader.length;
            //                usbSetupData.pBuffer = pVolumeRes;
            //                break;
          default:
            usbfwData.ep0Status = EP_STALL;
            break;  
          }
        }
      }
      else if (usbfwData.ep0Status == EP_RX) 
      {
        // Send OUT report up to Application
        //zrcPxyServeHIDClassRequests(usbSetupHeader.request, zrcClassRequestDataOut);
        audioCVolumeRes[0] = pVolumeRes[0];
        audioCVolumeRes[1] = pVolumeRes[1];
        osal_mem_free(pVolumeRes);
      }
    }
    
    void usbcrSetMin(void) 
    {
      if (usbfwData.ep0Status == EP_IDLE) 
      {
        if ( (usbSetupHeader.value & 0xFFFE) || 
            (usbSetupHeader.length != 0) || 
              (usbSetupHeader.index > INTERFACE_NUMBER_AUDIO_STREAM)) 
        {
          usbfwData.ep0Status = EP_STALL;
        } 
        else 
        {
        }
      }
    }// usbcrSetProtocol
    #endif
    
    

In usb_zrc_class_requests.h

  1. Add audio class requests macros
  2. 
    #ifdef VOICE_STREAM
    // Audio Request Codes
    #define GET_CUR  0x81                  // Code for Get Current Value
    #define GET_MIN  0x82
    #define GET_MAX  0x83
    #define GET_RES  0x84
    #define SET_CUR  0x01                  // Code for Get Current Value
    #define SET_MIN  0x02
    #define SET_MAX  0x03
    #define SET_RES  0x04
    #endif
    
    
  3. And add audio class requests function prototypes
  4. 
    #ifdef VOICE_STREAM
    void usbcrSetCur(void);
    void usbcrSetRes(void);
    void usbcrGetCur(void);
    void usbcrGetMin(void);
    void usbcrGetMax(void);
    void usbcrGetRes(void);
    #endif
    
    

In the usb_zrc_hooks.c

  1. Add include files
  2. 
    #ifdef VOICE_STREAM
    #include "usb_zrc.h"
    #include "zrc_common.h"
    #include "zrc_usb.h"
    #endif
    
    
  3. In usbcrHookProcessOut()
  4. 
    #ifdef VOICE_STREAM    
         case SET_CUR:
           usbcrSetCur(); 
           break;
         case SET_RES:
           usbcrSetRes(); 
           break;
    #endif  //VOICE_STREAM   
    
    
  5. In usbcrHookProcessIn()
  6. 
    #ifdef VOICE_STREAM    
         case GET_CUR:
           usbcrGetCur(); break;
         case GET_MIN:
           usbcrGetMin(); break;
         case GET_MAX:
           usbcrGetMax(); break;
         case GET_RES:
           usbcrGetRes(); break;
    #endif   //VOICE_STREAM 
    
    
  7. Define start and stop streaming functions as extern hooks. These are defined in usb_aud.c
  8. 
    #ifdef VOICE_STREAM
    //extern void InitBuffer(void);
    extern void StartAudioStreamingCommand(void);
    extern void StopAudioStreamingCommand(void);
    #endif
    
    // ************************ USB standard request event processing *************************
    void usbsrHookProcessEvent(uint8 event, uint8 index)
    {
    ...
       // Process relevant events, one at a time.
       switch (event) {
     ...
    #ifdef VOICE_STREAM
         // interface 3 and alternate Setting 1 ==> start audio
         if ( (index == INTERFACE_NUMBER_AUDIO_STREAM) && (usbfwData.pAlternateSetting[usbSetupHeader.index] == 1) )
         {
           StartAudioStreamingCommand();
         }
         else if ( (index == INTERFACE_NUMBER_AUDIO_STREAM) && (usbfwData.pAlternateSetting[usbSetupHeader.index] == 0) )
         {
           StopAudioStreamingCommand();
         }
    #endif
    
    

In usb_descriptor.h

  1. Add audio descriptor macros
  2. 
    #define DESC_TYPE_CS_INTERFACE    0x24
    #define DESC_TYPE_CS_ENDPOINT     0x25
    #define DESC_AUDIO_TYPE_HEADER        0x01
    #define DESC_AUDIO_TYPE_INPUT_TERM    0x02
    #define DESC_AUDIO_TYPE_OUTPUT_TERM   0x03
    #define DESC_AUDIO_TYPE_FEAT_UNIT     0x06
    
    //Audio Stream
    #define DESC_AUDIO_TYPE_GENERAL       0x01
    #define DESC_AUDIO_TYPE_FORMAT_TYPE   0x02
    
    #define DESC_AUDIO_TYPE_EP_GENERAL    0x01
       
    #define DESC_SIZE_DEVICE              0x12
    #define DESC_SIZE_CONFIG              0x09
    #define DESC_SIZE_INTERFACE           0x09
    #define DESC_SIZE_ENDPOINT            0x07
    #define DESC_SIZE_AUDIO_HEADER        0x09
    #define DESC_SIZE_AUDIO_INPUT_TERM    0x0C
    #define DESC_SIZE_AUDIO_OUTPUT_TERM   0x09
    #define DESC_SIZE_AUDIO_FEAT_UNIT     0x09
    #define DESC_SIZE_AUDIO_STREAM        0x07
    #define DESC_SIZE_AUDIO_FORMAT_TYPEI  0x0B //for one discrete Fs
    #define DESC_SIZE_AUDIO_ENDPOINT      0x09
    #define DESC_SIZE_AUDIO_ISO_ENDPOINT  0x07
    
    

In usb_interrupt.c

  1. Add files to include
  2. 
    #ifdef VOICE_STREAM
    #include "usb_aud.h"
    #endif
    
    
  3. Add hooks for isochronous interrupt service routines, implemented in usb_aud.c
  4. 
    #ifdef VOICE_STREAM
    extern void usbIsoInIsr(void);
    extern void usbSofIsr(void);
    #endif
    
    
  5. In usbirqHandler() add #ifdef VOICE_STREAM uint8 usbiif; #endif ... #ifdef VOICE_STREAM usbiif = USBIIF; #endif ... #ifdef VOICE_STREAM if (usbcif & USBCIF_SOFIF) { usbSofIsr(); } if ( (usbiif & (1<<AUDIO_ENPOINT_ADDR) /*USBIIF_INEP1IF*/) ) //|| (usbcif & USBCIF_SOFIF) { // Handle Audio stream usbIsoInIsr(); if (usbiif & (1<<AUDIO_ENPOINT_ADDR)) { usbiif &= ~((1<<AUDIO_ENPOINT_ADDR)); } } #endif eventMask = usbcif; #ifdef VOICE_STREAM eventMask |= (uint16)usbiif << 4; #else eventMask |= (uint16)USBIIF << 4; #endif
DMA

In the DMA driver file hal_dma.c, add hooks for the audio library.

  1. Include header
  2. 
    #if (defined HAL_USB_DMA) && (HAL_USB_DMA == TRUE)
    #include "usb_aud.h"
    #endif
    
    
  3. Add HAL_USB_DMA to precompiler clause to enable DMA interrupt, and to implement interrupt handler
  4. 
    void HalDmaInit( void )
    {
    

    ...

    #if (HAL_UART_DMA || \
      ((defined HAL_SPI) && (HAL_SPI == TRUE))  || \
      ((defined HAL_USB_DMA) && (HAL_USB_DMA == TRUE)) || \
      ((defined HAL_IRGEN) && (HAL_IRGEN == TRUE)))
     DMAIE = 1;
    #endif
    }
    
    #if (HAL_UART_DMA || \
       ((defined HAL_SPI) && (HAL_SPI == TRUE))  || \
      ((defined HAL_USB_DMA) && (HAL_USB_DMA == TRUE)) || \
       ((defined HAL_IRGEN) && (HAL_IRGEN == TRUE)))
    /******************************************************************************
     * @fn      HalDMAInit
    
    
  5. Add call to usb audio driver usb_aud.c DMA interrupt handler
  6. 
    #if (defined HAL_USB_DMA) && (HAL_USB_DMA == TRUE)
      if ( HAL_DMA_CHECK_IRQ( HAL_DMA_CH_RX ) )
      {
        usbDmaIsr();
        HAL_DMA_CLEAR_IRQ( HAL_DMA_CH_RX );
      }
    #endif
    
    
  7. Add macro to check if DMA has started in hal_dma.h
  8. 
    #define HAL_DMA_CHECK_STARTED( ch )   (DMAREQ & ( 1 << (ch) ))