This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

I2S + CC2650 sensortag (microphone driver)

Other Parts Discussed in Thread: CC2650, CC2541, CC2640, SYSBIOS, CC1310

Hi again e2e. 

I've taken a bit of a different approach after finding some useful info in the TRM + digging thru doxygen. 

Before I go into new code details, I have a high level question: 
Question 1a: How do the settings for WCLK and the peripheral differ when you are using a single Microphone? 1b: Does that mean that BCLK = MCLK? 

I would think you want an idle or disabled WCLK, since you are not multiplexing any audio with a single Mic. 
I am also guessing that we should be using PRCMAudioClockConfigSetOverride, to have direct access to WCLK settings,
since the driver is expecting multiple audio channels. 

In any case, section 22.9.1 of the TRM - "Start-up Sequence", gives a lot of detail on the order of operations for setting this up. 

Question 2: When setting up the PRCM clock, after calling PRCMLoadSet(), should we be waiting on PRCMLoadGet() before proceeding?

The latest code is below, now using driverlib APIs instead of direct HWREG calls, in most cases. 
I have omitted steps 5-8 because I am not yet certain steps 1-4 are properly implemented.
Once I can get the clocking set up properly, I can start actually tuning the I2S module settings to get the data into the cc2650. 

  // 1. Set up and configure required ADx and clock pins 

  // (set externally in the IOC module).
  IOCPortConfigureSet(IOID_11, IOC_PORT_MCU_I2S_BCLK,  IOC_STD_OUTPUT);
  IOCPortConfigureSet(IOID_2,  IOC_PORT_MCU_I2S_AD0,   IOC_STD_INPUT);
  // TODO : What about WCLK? We're only using a single Mic (dummy for now)
  IOCPortConfigureSet(IOID_25, IOC_PORT_MCU_I2S_WCLK,  IOC_STD_OUTPUT);
  // TODO : What about MCLK? (dummy for now)
  IOCPortConfigureSet(IOID_24, IOC_PORT_MCU_I2S_MCLK,  IOC_STD_OUTPUT);
  // 2. Enable I2S peripheral and configure WCLK and MCLK audio clocks 
  // (set externally in the PRCM module).
  PRCMPeripheralRunEnable(PRCM_PERIPH_I2S);
  PRCMPeripheralSleepDisable(PRCM_PERIPH_I2S);
  PRCMPeripheralDeepSleepDisable(PRCM_PERIPH_I2S);
  
  PRCMAudioClockConfigSet(PRCM_WCLK_SINGLE_PHASE | PRCM_WCLK_NEG_EDGE, 
                          I2S_SAMPLE_RATE_16K);
  
  // 3. Configure the serial audio interface format and the memory interface controller
  /* Set the following registers: I2S:AIFWCLKSRC, I2S:AIFDIRCFG, I2S:AIFFMTCFG,
    I2S:AIFWMSK0, I2S:AIFWMSK1, and I2S:AIFWMSK2. 
    BCLK must not be running when changing the I2S:AIFWCLKSRC register. */
  I2SControlTable g_controlTable;    // Define global
  g_pControlTable = &g_controlTable; // Assign pointer
  
  I2SEnable(I2S0_BASE); 
  
  I2SClockConfigure(I2S0_BASE,
                    I2S_INT_WCLK |    // Clock source (internal)
                    I2S_NORMAL_WCLK); // Clock polarity (not inverted)
  I2SChannelConfigure(I2S0_BASE,
                      I2S_CHAN0_ACT,  // single phase chan 0
                      0,              // null for chan 1
                      0);             // null for chan 2
  I2SAudioFormatConfigure(I2S0_BASE,
                          I2S_MEM_LENGTH_24     | // Sample size
                          I2S_NEG_EDGE          | // Clock edge sampling
                          I2S_SINGLE_PHASE_FMT  | // Phase
                          I2S_WORD_LENGTH_24,     // Word length
                          1);
  // 4. Enable BCLK (set externally in the PRCM module).
  HWREG(PRCM_BASE + PRCM_O_I2SBCLKSEL)  = 1; // use internally generated clock 
  
  printf("Loading clock settings\n");
  PRCMLoadSet();
  while(PRCMLoadGet()); // code gets stuck here
  printf("Clock settings loaded\n");
  PRCMAudioClockEnable();
  • I have managed to get an 8 MHz MCLK running on DP0.

    BCLK and WCLK are static low for some reason.

    I have mapped those to DP1 and DP2 for convenience, but they are idle.

    Something else I am noticing is that AIFWCLKSRC is getting cleared in my main loop, which does nothing except print the register values.

    AIFWCLKSRC = 2 when I go into the loop, which is what I want.

    BCLK is disabled when I set that register (as instructed in the TRM), then re-enabled before the main loop.

    TI - any advice? 

    The latest code :

      // 1. Set up and configure required ADx and clock pins 
      // (set externally in the IOC module).
      IOCPortConfigureSet(IOID_23, IOC_PORT_MCU_I2S_BCLK,  IOC_STD_OUTPUT);
    //  IOCPortConfigureSet(IOID_11, IOC_PORT_MCU_I2S_BCLK,  IOC_STD_OUTPUT);
      IOCPortConfigureSet(IOID_2,  IOC_PORT_MCU_I2S_AD0,   IOC_STD_INPUT);
      IOCPortConfigureSet(IOID_25, IOC_PORT_MCU_I2S_WCLK,  IOC_STD_OUTPUT);
      IOCPortConfigureSet(IOID_24, IOC_PORT_MCU_I2S_MCLK,  IOC_STD_OUTPUT);
      // 2. Enable I2S peripheral and configure WCLK and MCLK audio clocks 
      // (set externally in the PRCM module).
      PRCMPeripheralRunEnable(PRCM_PERIPH_I2S);
      PRCMLoadSet();
      PRCMPeripheralSleepDisable(PRCM_PERIPH_I2S);
      PRCMLoadSet();
      PRCMPeripheralDeepSleepDisable(PRCM_PERIPH_I2S);
      PRCMLoadSet();  
      PRCMAudioClockConfigSet(PRCM_WCLK_SINGLE_PHASE | PRCM_WCLK_NEG_EDGE, 
                              I2S_SAMPLE_RATE_16K);
      PRCMLoadSet();
      // 3. Configure the serial audio interface format and the memory interface controller
      /* Set the following registers: I2S:AIFWCLKSRC, I2S:AIFDIRCFG, I2S:AIFFMTCFG,
        I2S:AIFWMSK0, I2S:AIFWMSK1, and I2S:AIFWMSK2. 
        BCLK must not be running when changing the I2S:AIFWCLKSRC register. */
      I2SControlTable g_controlTable;    // Define global
      g_pControlTable = &g_controlTable; // Assign pointer
      
      #define CHANBUFSIZE 128
      uint32_t inBuf[CHANBUFSIZE];
      uint32_t outBuf[CHANBUFSIZE];
      
      I2SBufferConfig(I2S0_BASE,
                      (uint32_t) &inBuf,    // is the address of the input buffer.
                      (uint32_t) &outBuf,   // is the address of the output buffer
                      I2S_DMA_BUF_SIZE_64,  // is the size of the DMA buffers. Must be > 0!
                      CHANBUFSIZE);    // is the size of the channel buffers.
      I2SClockConfigure(I2S0_BASE,
                        I2S_INT_WCLK |    // Clock source (internal)
                        I2S_NORMAL_WCLK); // Clock polarity (not inverted)
      
      I2SChannelConfigure(I2S0_BASE,
                          I2S_CHAN0_ACT,  // single phase chan 0
                          0,              // null for chan 1
                          0);             // null for chan 2
      
      I2SAudioFormatConfigure(I2S0_BASE,
                              I2S_MEM_LENGTH_24     | // Sample size
                              I2S_NEG_EDGE          | // Clock edge sampling
                              I2S_SINGLE_PHASE_FMT  | // Phase
                              I2S_WORD_LENGTH_24,     // Word length
                              1);
                              
      // 4. Enable BCLK (set externally in the PRCM module).
      HWREG(PRCM_BASE + PRCM_O_I2SBCLKSEL)  = 1; // use internally generated clock 
      PRCMLoadSet();
      PRCMAudioClockEnable();
      // 5. Configure and prepare the samplestamp generator:
      /*
      Set the I2S:STMPWPER register. 
        This number corresponds to the total size of the sample ring buffer used by the system.
      Set the two registers I2S:STMPINTRIG and I2S:STMPOUTTRIG > I2S:STMPWPER 
        to avoid false triggers before the samplestamp generator is started.
      */
      HWREG(I2S0_BASE + I2S_O_STMPWPER)     = 128;
      HWREG(I2S0_BASE + I2S_O_STMPINTRIG)   = 129;
      HWREG(I2S0_BASE + I2S_O_STMPOUTTRIG)  = 129;
      
      // 6. Enable the samplestamp generator:
      /*
      Set I2S:STMPCTRL.STMP_EN = 1
      */
      HWREG(I2S0_BASE + I2S_O_STMPCTL) &= I2S_STMPCTL_STMP_EN;
              
      // 7. Enable the serial audio interface:
      /*
      Set the I2S:AIFINPTRNEXT and the I2S:AIFOUTPTRNEXT registers 
        for first memory interface buffers.
      Set the I2S:AIFDMACFG register; 
        This number corresponds to the length of each block in the sample ring buffer used by the system.
      Set the I2S:AIFINPTRNEXT and the I2S:AIFOUTPTRNEXT registers 
        for second memory interface buffers.
      */
      I2SEnable(I2S0_BASE); 
      // 8. Start input and output audio streaming:
      /*
      Set the I2S:STMPINTRIG and the I2S:STMPOUTTRIG registers 
        so they correctly match the I2S:AIFINPTR and the I2S:AIFOUTPTR registers.                      
      */
      I2SSampleStampConfigure(I2S0_BASE, true, true);
      ti_lib_gpio_pin_write((1 << BOARD_IOID_MIC_POWER), 1);
      leds_off(LEDS_RED);
      leds_on(LEDS_GREEN);
      etimer_set(&et_periodic, PERIODIC_INTERVAL);
      printf("I2S_O_AIFWCLKSRC:\t %08lx\n", HWREG(I2S0_BASE + I2S_O_AIFWCLKSRC));
      while(1) {
        PROCESS_YIELD();
        if (ev == PROCESS_EVENT_TIMER && data == &et_periodic)
        {
          printf("Timer Fired\t Uptime:%ld\n", clock_seconds());
          // printf("IOID_2:\t\t\t %08lx\n", IOCPortConfigureGet(IOID_2));
          // printf("IOID_11:\t\t %08lx\n", IOCPortConfigureGet(IOID_11));
          // printf("IOID_24:\t\t %08lx\n", IOCPortConfigureGet(IOID_24));
          // printf("IOID_25:\t\t %08lx\n", IOCPortConfigureGet(IOID_25));
          
          printf("PRCM_O_I2SCLKGR:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SCLKGR));
          printf("PRCM_O_I2SCLKGS:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SCLKGS));
          printf("PRCM_O_I2SCLKGDS:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SCLKGDS));
          printf("PRCM_O_I2SCLKCTL:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL));
          printf("PRCM_O_I2SMCLKDIV:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SMCLKDIV));
          printf("PRCM_O_I2SBCLKDIV:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SBCLKDIV));
          printf("PRCM_O_I2SWCLKDIV:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SWCLKDIV));
          printf("PRCM_O_I2SBCLKSEL:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SBCLKSEL));
          
          printf("I2S_O_AIFWCLKSRC:\t %08lx\n", HWREG(I2S0_BASE + I2S_O_AIFWCLKSRC));
          printf("I2S_O_AIFDIRCFG:\t %08lx\n", HWREG(I2S0_BASE + I2S_O_AIFDIRCFG));
          printf("I2S_O_AIFFMTCFG:\t %08lx\n", HWREG(I2S0_BASE + I2S_O_AIFFMTCFG));
          printf("I2S_O_AIFWMASK0:\t %08lx\n", HWREG(I2S0_BASE + I2S_O_AIFWMASK0));
          printf("I2S_O_IRQMASK:\t\t %08lx\n", HWREG(I2S0_BASE + I2S_O_IRQMASK));
          printf("I2S_O_IRQFLAGS:\t\t %08lx\n", HWREG(I2S0_BASE + I2S_O_IRQFLAGS));
          printf("I2S_O_IRQSET:\t\t %08lx\n", HWREG(I2S0_BASE + I2S_O_IRQSET));
          printf("\n");
          
          leds_toggle(LEDS_GREEN);
          leds_toggle(LEDS_RED);
          etimer_set(&et_periodic, PERIODIC_INTERVAL*15);
        }
      }
  • Hi,mike. Does that work?Can you recieve data from cc2650 microphone?
  • All this code manages to do is produce an 8MHz MCLK. No BCLK is generated, so no bits are coming out of the microphone. I am quite confident I can convert the data to something useful in firmware, once the I2S bus is functioning as desired. 

    I have not received support in any form about this, despite 2 posts in this thread, and an older thread that I had given up on and deleted.

    If anyone can get the I2S bus functioning, I am happy to contribute some DSP firmware to produce PCM audio samples. 

  • instead of:
    I2SSampleStampConfigure(I2S0_BASE, true, true);
    try this:
    HWREG(I2S0_BASE + I2S_O_STMPINTRIG) = 0;
    HWREG(I2S0_BASE + I2S_O_STMPOUTTRIG) = 0;
  • Thanks Andrei. I gave this a shot and the mystery behavior continues.

    Before entering my while loop, AIFWCLKSRC = 2.
    In the loop its getting set back to zero.
    No change in the measured clock signals.

    Have you had any luck with I2S on the sensortag?
  • Hi Mike and Andrei,

    Have you gotten the I2S interface on CC26xx to work?

    Thanks!

  • Hi Ed!

    Yes! I2S interface on CC2650 works fine!!! For me anyway.

    I2S problem is that there is no actual drivers at the moment. So, the only way to make alive I2S is a step by step implementation of the initialization procedure described in the <swcu117d.pdf>  22.9.1. Start-up Sequence>. I did that for my desired use case. Unfortunately, due to time limitations, I have not had the opportunity (and necessity) to implement and test a full-featured driver.

    BR!

    Andrei

  • Thanks Andrei!

    I thought I was following the step-by-step initialization, but have yet to see any "life" at the I2S pins. Could you comment on what I might be missing (or doing incorrectly)? Thanks!

    Here is what I'm doing:

    Global variables:


    uint8_t micBuf0[NUM_MIC_SAMPLES_PACKET*3]; // Each sample is 3 bytes
    uint8_t micBuf1[NUM_MIC_SAMPLES_PACKET*3]; // Each sample is 3 bytes

    I2SControlTable g_controlTable; // Define global table space



    At my I2S peripheral setup:


    // Performed once
    // Sets up microphone
    g_pControlTable = &g_controlTable; // Assign pointer

    mic_i2sPinConfig();

    mic_i2sEnableBclk(false);
    mic_i2sClockConfig();
    mic_i2sPeriphConfig();
    mic_i2sEnableBclk(true);
    mic_i2sSampleStampConfig();

    mic_i2sMemoryConfig();





    where the functions are defined as:

    static void mic_i2sPinConfig() {
    // Assign physical pins for I2S operation
    IOCPortConfigureSet(IOID_0, // BCLK
    IOC_PORT_MCU_I2S_BCLK,
    IOC_STD_OUTPUT
    );
    IOCPortConfigureSet(IOID_1, // WCLK
    IOC_PORT_MCU_I2S_WCLK,
    IOC_STD_OUTPUT
    );
    IOCPortConfigureSet(IOID_7, // SD (in)
    IOC_PORT_MCU_I2S_AD0,
    IOC_STD_INPUT
    );
    }



    static void mic_i2sClockConfig() {
    PRCMPeripheralRunEnable(PRCM_PERIPH_I2S); // Set up I2S clocks
    PRCMPeripheralSleepDisable(PRCM_PERIPH_I2S); //
    PRCMPeripheralDeepSleepDisable(PRCM_PERIPH_I2S);//
    // Sample at 16 KHz for now
    PRCMAudioClockConfigSet( PRCM_WCLK_DUAL_PHASE
    | PRCM_WCLK_POS_EDGE,
    I2S_SAMPLE_RATE_16K
    );
    // Use this to set up custom sample rate
    //PRCMAudioClockConfigSetOverride(PRCM_WCLK_DUAL_PHASE | PRCM_WCLK_NEG_EDGE,
    // masterDiv,
    // bitDiv,
    // wordDiv
    // );
    PRCMLoadSet();
    // Inform I2S where to
    // get Clock
    I2SClockConfigure(I2S0_BASE, I2S_INT_WCLK | I2S_INVERT_WCLK);

    }



    static void mic_i2sPeriphConfig() {
    I2SAudioFormatConfigure(I2S0_BASE, // I2S registers
    I2S_MEM_LENGTH_24 // 24-bits packed in mem
    | I2S_POS_EDGE // Sampled on pos edge
    | I2S_DUAL_PHASE_FMT // 2 phases in I2S
    | I2S_WORD_LENGTH_24, // 24-bit samples
    1 // 1 bit delay
    );
    I2SChannelConfigure(I2S0_BASE, // I2S registers
    I2S_LINE_INPUT // Chan 0: I2S - Mono
    | I2S_MONO_MODE,
    I2S_LINE_UNUSED, // Chan 1: unused
    I2S_LINE_UNUSED // Chan 2: unused
    );
    }



    static void mic_i2sEnableBclk(bool en) {
    if(en == true) {
    PRCMAudioClockEnable(); // Turn on BCLK
    } else {
    PRCMAudioClockDisable(); // Turn off BCLK
    }
    }



    static void mic_i2sSampleStampConfig() {
    HWREG(I2S0_BASE + I2S_O_STMPWPER) = NUM_MIC_SAMPLES_PACKET - 1; // Number of samples per buffer
    HWREG(I2S0_BASE + I2S_O_STMPINTRIG) = NUM_MIC_SAMPLES_PACKET + 5; // Set up beyond end for now
    HWREG(I2S0_BASE + I2S_O_STMPOUTTRIG) = NUM_MIC_SAMPLES_PACKET + 5; // Set up beyond end for now

    I2SSampleStampEnable(I2S0_BASE); // Enables SampleStamp module
    }



    static void mic_i2sMemoryConfig() {
    // Memory Pointers will be set up in micStart()

    }



    Then when the mic is turned on:


    static void micStart() {
    I2SPointerSet(I2S0_BASE,true,micBuf0); // Set up first input pointer
    mic_i2sEnable(true); // Enable I2S; pointer copied
    I2SPointerSet(I2S0_BASE,true,micBuf1); // Set up second input pointer
    I2SSampleStampConfigure(I2S0_BASE,true,false); // Start SampleStamp
    }
  • Hi Ed!

    As I can see, you work via <cc26xxware>. Good choise! When I launch my project, I just fork it from ready TI example <SimpleBLEPeripheral>. So my initialization procedure inherit some ready methods to init PINs and setup ISR Handler. All I2S initialisation I have made at low level via HWREG(). If you interested, I can adjust my initialization procedure for I2S in according with your requirements. To make that, I need waveform diagram of I2S frame for your application.

    Andrei

  • Hi Andrei,

    I'm simply trying to implement a mono, 24-bit I2S interface (BLK, WS, SD signals; Data only received on left channel). I'm sure I can adjust the waveform shape via the parameters if I can get an initial wave. (I'm replacing a hardware implementation I did in an FPGA to interface to the CC2541.) But for now, I don't see any of the I2S pins on the CC2640 wiggle at all. I suspect that I've missed something in the initialization sequence. I was hoping you could compare what I'm doing with what you have done and see what I've missed...
    Thanks!
    Ed
  • Hi Ed! Hi Mike!

    For many reasons, it does not seem a good idea to look for issues in your code. Better I share with you my use case. I adjust I2S engine to communicate with SPI ADC. The trouble of ADC is the CS line, which I should pull up every cycle to start conversion. So it is wasting CPU time when it works via SPI engine. But with I2S engine we have one extra line - WCLK! After appropriate configuration it is possible to connect WCLK with CS on ADC. And launch conversion process of 256 elements, for instance. As a result is dramatic decrease of CPU interaction.

    When I launch my project, I just fork it from ready TI example <SimpleBLEPeripheral>. So my initialization procedure inherit some ready methods to init PINs and setup ISR Handler. All I2S initialisation I have made at low level via HWREG(). Here is code and waveform diagram:

    channel_1 - WCLK
    channel_2 - BCLK
    channel_3 - MOSI
    channel_4 - MISO

    /*
     * i2s_init.c
     *
     *  Created on: 20 ���. 2016 �.
     *      Author: Andrey
     */
    
    #include <inc/hw_types.h>
    
    #include <driverlib/prcm.h>
    #include <driverlib/i2s.h>
    
    #include <ti/sysbios/family/arm/cc26xx/Power.h>
    #include <ti/sysbios/family/arm/cc26xx/PowerCC2650.h>
    #include <ti/drivers/pin/PINCC26XX.h>
    
    #include "util.h"
    #include "Board.h"
    
    #define BCLK_DIV (16)                           /* 48MHz/16 = 3MHz */
    
    #define WCLK (IOID_1)
    #define BCLK (IOID_15)
    #define MOSI (IOID_18)
    #define MISO (IOID_19)
    
    #define I2S_BUFF_SIZE    (256)
    #define I2S_BUFF_IN_NUM  (2)
    #define I2S_BUFF_OUT_NUM (1)
    
    uint16_t i2s_in [I2S_BUFF_SIZE * I2S_BUFF_IN_NUM];
    uint16_t i2s_out[I2S_BUFF_SIZE * I2S_BUFF_OUT_NUM];
    
    static PIN_State pinState;
    static PIN_Handle pinHandle;
    static ti_sysbios_family_arm_m3_Hwi_Struct hwiStruct;
    
    static uint32_t index_in;
    static uint32_t index_out;
    
    
    static inline uint16_t* i2s_in_iter(void)
    {
        uint16_t* addr = &(i2s_in[I2S_BUFF_SIZE * index_in]);
        index_in++;
        index_in %= I2S_BUFF_IN_NUM;
    
        return addr;
    }
    
    static inline uint16_t* i2s_out_iter(void)
    {
        uint16_t* addr = &(i2s_out[I2S_BUFF_SIZE * index_out]);
        index_out++;
        index_out %= I2S_BUFF_OUT_NUM;
    
        return addr;
    }
    
    static inline void i2s_iter(void)
    {
        // DMA Output Buffer Next Pointer
        HWREG(I2S0_BASE + I2S_O_AIFOUTPTRNEXT) = (uint32_t)i2s_out_iter();
        // DMA Input Buffer Next Pointer
        HWREG(I2S0_BASE + I2S_O_AIFINPTRNEXT)  = (uint32_t)i2s_in_iter();
    }
    
    static inline void i2s_extractData(void)
    {
    }
    
    /*
     *  Termination Sequence implemented according to
     *  <swcu117d.pdf> 22.9.2. Termination Sequence.
     */
    static void i2s_terminate(void)
    {
        HWREG(I2S0_BASE + I2S_O_AIFDMACFG) = 0;
        HWREG(I2S0_BASE + I2S_O_STMPCTL) = 0;
        HWREG(I2S0_BASE + I2S_O_IRQCLR) = 0x3F;
        HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL) = 0;
        HWREG(PRCM_BASE + PRCM_O_CLKLOADCTL) = PRCM_CLKLOADCTL_LOAD;
        HWREG(I2S0_BASE + I2S_O_IRQMASK) = 0;
    }
    
    /*
     *  Interrupt service routine.
     *
     */
    static void i2s_Isr(UArg arg) {
    
        uint32_t flags = 0xf & HWREG(I2S0_BASE + I2S_O_IRQFLAGS);
    
        if (flags)
        {
            i2s_terminate();
        }
        else
        {
            i2s_iter();
        }
    
        i2s_extractData();
    }
    
    /*
     *  ======== i2s_init ========
     *
     *  Initialization Sequence implemented according to
     *  <swcu117d.pdf> 22.9.1. Start-up Sequence.
     */
    void i2s_init(void)
    {
        index_in  = 0;
        index_out = 0;
    
    
        // step 1. Set up and configure required ADx and clock pins (set externally in the IOC module).
        {
            PIN_Status res = PIN_SUCCESS;
            PIN_Config pinTable[] = {
    
                    WCLK | PIN_INPUT_DIS | PIN_PUSHPULL | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH,
                    BCLK | PIN_INPUT_DIS | PIN_PUSHPULL | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW,
                    MOSI | PIN_INPUT_DIS | PIN_PUSHPULL | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH,
                    MISO | PIN_INPUT_EN | PIN_PULLUP,
                    PIN_TERMINATE
            };
    
            /* Open and assign pins through pin driver */
            pinHandle = PIN_open(&pinState, pinTable);
    
            /* Are pins already allocated */
            if (NULL == pinHandle) {
                while(1);
            }
    
            /* Set IO muxing for the I2S pins */
            res |= PINCC26XX_setMux(pinHandle, WCLK, IOC_PORT_MCU_I2S_WCLK);
            res |= PINCC26XX_setMux(pinHandle, BCLK, IOC_PORT_MCU_I2S_BCLK);
            res |= PINCC26XX_setMux(pinHandle, MOSI, IOC_PORT_MCU_I2S_AD0);
            res |= PINCC26XX_setMux(pinHandle, MISO, IOC_PORT_MCU_I2S_AD1);
    
            if (PIN_SUCCESS != res) {
                while(1);
            }
        }
    
        // step 2. Enable I2S peripheral and configure WCLK and MCLK audio clocks (set externally in the PRCM
        // module).
        {
            Power_setConstraint(Power_SB_DISALLOW);
            if (!Power_setDependency(PERIPH_I2S)) while(1);
    
            // I2S Clock Gate For Run Mode
            HWREG(PRCM_BASE + PRCM_O_I2SCLKGR) = PRCM_I2SCLKGR_CLK_EN;
            // I2S Clock Gate For Sleep Mode
            HWREG(PRCM_BASE + PRCM_O_I2SCLKGS) = PRCM_I2SCLKGS_CLK_EN;
    
            // I2S Clock Control. (data and WCLK are sampled on the positive edge of BCLK, User Defined WCLK, CLK Disabled)
            HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL) = (PRCM_I2SCLKCTL_SMPL_ON_POSEDGE | (2 << PRCM_I2SCLKCTL_WCLK_PHASE_S));
            // WCLK Division Ratio. For User Defined HI+LO WCLK = MCUCLK / (BDIV*(WDIV[7:0] + WDIV[15:8]) [Hz]
            HWREG(PRCM_BASE + PRCM_O_I2SWCLKDIV) = (7 << 0) | (17 << 8);
            // MCLK Division Ratio (MCLK = MCUCLK/MDIV[Hz] = 48MHz/2 = 24Mhz)
            HWREG(PRCM_BASE + PRCM_O_I2SMCLKDIV) = 0;
    
            // Load Settings from buffers to actual registers.
            HWREG(PRCM_BASE + PRCM_O_CLKLOADCTL) = PRCM_CLKLOADCTL_LOAD;
        }
    
        // step 3. Configure the serial audio interface format and the memory interface controller:
        // � Set the following registers: I2S:AIFWCLKSRC, I2S:AIFDIRCFG, I2S:AIFFMTCFG,
        //   I2S:AIFWMSK0, I2S:AIFWMSK1, and I2S:AIFWMSK2. BCLK must not be running when changing
        //   the I2S:AIFWCLKSRC register.
        {
            // WCLK Source Selection. (Invert WCLK, Internal WCLK generator)
            HWREG(I2S0_BASE + I2S_O_AIFWCLKSRC) = (I2S_AIFWCLKSRC_WCLK_INV | I2S_AIFWCLKSRC_WCLK_SRC_INT);
    
            // Pin Direction
            HWREG(I2S0_BASE + I2S_O_AIFDIRCFG) = (I2S_AIFDIRCFG_AD1_IN | I2S_AIFDIRCFG_AD0_OUT);
    
            // Serial Interface Format Configuration.
            HWREG(I2S0_BASE + I2S_O_AIFFMTCFG) = (0 << I2S_AIFFMTCFG_DATA_DELAY_S) |
                                                 (I2S_AIFFMTCFG_MEM_LEN_24_16BIT)  |
                                                 (I2S_AIFFMTCFG_SMPL_EDGE_POS)     |
                                                 (0 << I2S_AIFFMTCFG_DUAL_PHASE_S) |
                                                 (16 << I2S_AIFFMTCFG_WORD_LEN_S);
    
            // Word Selection Bit Mask for Pin 0. (MONO)
            HWREG(I2S0_BASE + I2S_O_AIFWMASK0) = (1 << I2S_AIFWMASK0_MASK_S);
    
            // Word Selection Bit Mask for Pin 1. (MONO)
            HWREG(I2S0_BASE + I2S_O_AIFWMASK1) = (1 << I2S_AIFWMASK1_MASK_S);
    
            // Word Selection Bit Mask for Pin 2. (OFF)
            HWREG(I2S0_BASE + I2S_O_AIFWMASK2) = (0 << I2S_AIFWMASK2_MASK_S);
        }
    
        // step 4. Enable BCLK (set externally in the PRCM module).
        {
            // I2S Clock Control. (data and WCLK are sampled on the positive edge of BCLK, User Defined WCLK, CLK Enable)
            HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL) = (PRCM_I2SCLKCTL_SMPL_ON_POSEDGE | (2 << PRCM_I2SCLKCTL_WCLK_PHASE_S) | PRCM_I2SCLKCTL_EN);
            // I2S Clock Control. (Use internally generated clock)
            HWREG(PRCM_BASE + PRCM_O_I2SBCLKSEL) = PRCM_I2SBCLKSEL_SRC;
            // BCLK Division Ratio. (BCLK = MCUCLK/BDIV[Hz] = 48MHz/2 = 24Mhz)
            HWREG(PRCM_BASE + PRCM_O_I2SBCLKDIV) = BCLK_DIV;
    
            // Load Settings from buffers to actual registers.
            HWREG(PRCM_BASE + PRCM_O_CLKLOADCTL) = PRCM_CLKLOADCTL_LOAD;
        }
    
    
        // step 5. Configure and prepare the samplestamp generator:
        // � Set the I2S:STMPWPER register. This number corresponds to the total size of the sample ring
        //   buffer used by the system.
        // � Set the two registers I2S:STMPINTRIG and I2S:STMPOUTTRIG > I2S:STMPWPER to avoid false
        //   triggers before the samplestamp generator is started.
        {
            uint16_t tmp = I2S_BUFF_SIZE + 6;
    
            // WCLK Counter Period Value
            HWREG(I2S0_BASE + I2S_O_STMPWPER) = tmp++;
            // WCLK Counter Trigger Value for Input Pins
            HWREG(I2S0_BASE + I2S_O_STMPINTRIG) = tmp;
            // WCLK Counter Trigger Value for Output Pins
            HWREG(I2S0_BASE + I2S_O_STMPOUTTRIG) = tmp;
        }
    
        // step 6. Enable the samplestamp generator:
        // � Set I2S:STMPCTRL.STMP_EN = 1
        // � Optional steps:
        //   � Poll the I2S:STMPWCNT register and wait until the counter value is 2 or higher:
        // � When the value is 2 or higher, there are no more false increments (as described in
        //   Section 22.8.1, Counters and Registers).
        // � When the value is 4 or higher, the WCLK period is read out from the I2S:STMPXPER
        //   register. This is used to determine the sample rate when using an external clock source.
        // � Reset the WCLK counter by writing I2S:STMPWSET = 0
        {
            // SampleStaMP Generator Control Register
            HWREG(I2S0_BASE + I2S_O_STMPCTL) = I2S_STMPCTL_STMP_EN;
        }
    
        // step 7. Enable the serial audio interface:
        // � Set the I2S:AIFINPTRNEXT and the I2S:AIFOUTPTRNEXT registers for first memory interface
        //   buffers.
        // � Set the I2S:AIFDMACFG register; This number corresponds to the length of each block in the
        //   sample ring buffer used by the system.
        // � Set the I2S:AIFINPTRNEXT and the I2S:AIFOUTPTRNEXT registers for second memory interface
        //   buffers.
        {
            Hwi_Params hwiParams;
    
            // Setup HWI handler
            Hwi_Params_init(&hwiParams);
            Hwi_construct(&hwiStruct, INT_I2S, i2s_Isr, &hwiParams, NULL);
        }
    
        {
            // Masked Interrupt Status Register. (enable DMA_IN interrupt)
            HWREG(I2S0_BASE + I2S_O_IRQMASK) = I2S_IRQMASK_AIF_DMA_IN;
    
            i2s_iter();
    
            // DMA Buffer Size Configuration
            HWREG(I2S0_BASE + I2S_O_AIFDMACFG) = ((I2S_BUFF_SIZE - 1) << I2S_AIFDMACFG_END_FRAME_IDX_S);
    
            i2s_iter();
        }
    
        // step 8. Start input and output audio streaming:
        // � Set the I2S:STMPINTRIG and the I2S:STMPOUTTRIG registers so they correctly match the
        //   I2S:AIFINPTR and the I2S:AIFOUTPTR registers.
        {
            // Current Value of WCNT
            while (5 > HWREG(I2S0_BASE + I2S_O_STMPWCNT));
    
            // WCLK Counter Trigger Value for Input Pins
            HWREG(I2S0_BASE + I2S_O_STMPINTRIG) = 0;
            // WCLK Counter Trigger Value for Output Pins
            HWREG(I2S0_BASE + I2S_O_STMPOUTTRIG) = 0;
    
            // Interrupt Clear Register
            HWREG(I2S0_BASE + I2S_O_IRQCLR) = 0xF;
        }
    }
    
    

    BR! Andrei

  • Thanks Andrei, this is a solid example.

    Since I'm still on the sensortag, I had to make a few changes.

    BCLK is on DIO_11, with your example, I see a 3 MHz clock here.
    WCLK is on DIO_25, with your code, I see this signal go LOW for 16 counts of BCLK, and HI for 8 counts of BCLK.
    This matches your waveform.

    MICPWR is on DIO_13, I actuate that high when I enable BCLK
    AUDIO_DI (AD0) is on DIO_2, on the scope, I see the microphone output bitstream.

    I also had to change the pin direction for the I2S AD0 pin.

    You're the man. I owe you some beers if you're ever in portland!
    Cheers!
  • So you were able to get some noise out of the microphone?? I have been trying to use the SPI driver, but the microphone is dead as nothing is coming out of it.

    Can u please post the steps to use the I2S driver instead? After installing the I2S, what are the calls sequence needed and how to set up the pins ? Please Hellllpppp...
  • I posted my solution over at the BluetoothXX Smart Forum...

  • HI, I have viewed the i2s_init.c code and found it to be very helpful. I understand that the data that comes in stores into a DMA. Is there anyway that I can view the data stored in the DMA via UART? Is the data stored in the i2s_out[] array?
  • Hi, Selwyn!
    Yes! Data for I2S interaction stored in the arrays i2s_out[256] and i2s_in[2*256]. Array i2s_out[] for data to be transferred via DMA, via I2S-hw to MOSI-pin. Data from MISO-pin transferred via I2S-hw, via DMA into array i2s_in[]. Array i2s_in[] consist of two parts for ping-pong implementation.
  • Thanks for your reply Andrei.
    I am trying to connect a Digital microphone to the CC2650 module, but I am having some difficulty checking the data produced by the microphone. May I know which line of code in the i2s_init.c controls the data going in and out the DMA? Or is this data transfer automatic whenever I implement the I2S module?
  • Do you have an example of how do we extract data from i2s_extractData()? I have attached my program, could you see if I am doing things right? I am using a 24bit 2's complement digital microphone, and I am storing it in a int32 buffer. 

    /*
     * main.c
     *
     *  Created on: 4 Apr, 2016
     *      Author: Lenovo
     */
    
    
    /* Board Header files */
    #include "Board.h"
    
    /* BIOS Header files */
    #include <ti/sysbios/BIOS.h>
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Semaphore.h>
    #include <ti/sysbios/knl/Clock.h>
    
    /* XDCtools Header files */
    #include <stdlib.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <string.h>
    #include <xdc/std.h>
    #include <xdc/runtime/System.h>
    #include <xdc/runtime/Error.h>
    
    /* HW Header files */
    #include <inc/hw_types.h>
    #include <inc/hw_ints.h>
    
    /* RF Header files */
    #include "rfEasyLinkTx.h"
    
    /* UART Header files */
    #include <ti/drivers/UART.h> //RTOS
    
    /* AON IOC Header files */
    #include <driverlib/aon_ioc.h>
    #include <inc/hw_aon_ioc.h>
    
    /* I2S Header files */
    #include <driverlib/i2s.h>
    #include <driverlib/prcm.h>
    #include <ti/drivers/pin/PINCC26XX.h>
    
    /* Power Header files  */
    #include <ti/drivers/Power.h> //RTOS
    #include <ti/drivers/power/PowerCC26XX.h>
    
    /* SSI Header files */
    #include <ti/drivers/SPI.h>
    #include <ti/drivers/spi/SPICC26XXDMA.h>
    #include <ti/drivers/dma/UDMACC26XX.h>
    
    #define BCLK_DIV (16)	//BCLK division 48MHz/16 = 3MHz
    //#define BCLK_DIV (24)	//BCLK division 48MHz/16 = 2MHz
    //#define BCLK_DIV (48)	//BCLK division 48MHz/16 = 1MHz
    
    
    #define WCLK (IOID_6)
    #define BCLK (IOID_7)
    #define MISO (IOID_1)
    
    #define I2S_BUFF_SIZE		(512)
    #define I2S_BUFF_IN_NUM		(2)
    #define I2S_BUFF_OUT_NUM	(1)
    
    int32_t i2s_in[I2S_BUFF_SIZE];
    int32_t i2s_out[I2S_BUFF_SIZE];
    int32_t mic_out[I2S_BUFF_SIZE];
    
    /* Pin driver handle */
    static PIN_Handle pinHandle;
    static PIN_State pinState;
    static ti_sysbios_family_arm_m3_Hwi_Struct hwiStruct;
    
    uint32_t mic_buffer_size = 0;
    int32_t mic_output = 0;
    
    /* Debug */
    volatile unsigned char text[100] = {"\0"};
    
    Task_Struct myTask;
    UART_Handle uart;
    
    ///* Pin Table */
    PIN_Config PinTable[] =
    {
    	WCLK	| PIN_INPUT_DIS | PIN_PUSHPULL | PIN_GPIO_OUTPUT_EN | PIN_GPIO_HIGH,
    	BCLK	| PIN_INPUT_DIS | PIN_PUSHPULL | PIN_GPIO_OUTPUT_EN | PIN_GPIO_LOW ,
    	MISO	| PIN_INPUT_EN 	| PIN_PULLDOWN,
    	PIN_TERMINATE                                                                      /* Terminate list */
    };
    
    Char myTaskStack[512];
    
    static void i2s_terminate(void)
    {
    	HWREG(I2S0_BASE + I2S_O_AIFDMACFG) = 0;
    	HWREG(I2S0_BASE + I2S_O_STMPCTL) = 0;
    	HWREG(I2S0_BASE + I2S_O_IRQCLR) = 0x3F;
    	HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL) = 0;
    	HWREG(PRCM_BASE + PRCM_O_CLKLOADCTL) = PRCM_CLKLOADCTL_LOAD;
    	HWREG(I2S0_BASE + I2S_O_IRQMASK) = 0;
    }
    
    static void i2s_extract(void)
    {
    	sprintf(text, "%d\r", HWREG(I2S0_BASE + I2S_O_STMPWCNT));
    	UART_write(uart, (const long*)text, sizeof(text));
    }
    
    static void i2s_ihandler(UArg arg)
    {
    //	I2SEnable(I2S0_BASE);
    	I2SPointerUpdate(I2S0_BASE, true);
    	I2SPointerUpdate(I2S0_BASE, false);
    	i2s_extract();
    }
    
    void Uart_init(void)
    {
        UART_Params uartParams;
    
        /* Create a UART with data processing off. */
        UART_Params_init(&uartParams);
        uartParams.writeDataMode = UART_DATA_BINARY;
        uartParams.readDataMode = UART_DATA_BINARY;
        uartParams.readReturnMode = UART_RETURN_FULL;
        uartParams.readEcho = UART_ECHO_OFF;
        uartParams.baudRate = 19200;
        uart = UART_open(Board_UART0, &uartParams);
    
        if (uart == NULL) {
            System_abort("Error opening the UART");
        }
    }
    
    void i2s_init(void)
    {
    	/* 1.	Set up and configure required ADx and clock pins */
    	pinHandle = PIN_open(&pinState, PinTable);
    	if(NULL == pinHandle)
    	{
    		while(1);
    	}
    	IOCPortConfigureSet(BCLK, IOC_PORT_MCU_I2S_BCLK, IOC_STD_OUTPUT);
    	IOCPortConfigureSet(WCLK, IOC_PORT_MCU_I2S_WCLK, IOC_STD_OUTPUT);
    	IOCPortConfigureSet(MISO, IOC_PORT_MCU_I2S_AD0,  IOC_STD_INPUT );
    
    
        /* 2.	Enable I2S peripheral & configure WCLK and MCLK audio clocks  */
        Power_setConstraint(PowerCC26XX_SB_DISALLOW);
        Power_setDependency(PowerCC26XX_PERIPH_I2S);
    
        // I2S Clock Gate Gate For Run mode
        HWREG(PRCM_BASE + PRCM_O_I2SCLKGR) = PRCM_I2SCLKGR_CLK_EN;
        // I2S Clock gate for Sleep mode
        HWREG(PRCM_BASE + PRCM_O_I2SCLKGS) = PRCM_I2SCLKGS_CLK_EN;
    
        // I2S Clock Control (data and WCLK are sampled on the positive edge of BCLK, USER defined WCLK, CLK disabled)
        HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL) = (PRCM_I2SCLKCTL_SMPL_ON_POSEDGE | (2 << PRCM_I2SCLKCTL_WCLK_PHASE_S));
        // WCLK Division Ratio. (WCLK = MCUCLK/(BDIV*(WDIV[7:0] + WDIV[15:8]) [Hz])) = 48kHz
        HWREG(PRCM_BASE + PRCM_O_I2SWCLKDIV) = 0x2020;
        // MCLK Division Ratio. (MCLK = MCUCLK/MDIV [Hz] = 48MHz/24 = 2MHz)
        HWREG(PRCM_BASE + PRCM_O_I2SMCLKDIV) = 94;
    
        // Load settings from buffers to actual registers
        HWREG(PRCM_BASE + PRCM_O_CLKLOADCTL) = PRCM_CLKLOADCTL_LOAD;
    
    
    	/* 3.	Configure the serial audio interface format and the memory interface controller
    	 * 		Set the following registers: I2S:AIFWCLKSRC, I2S:AIFDIRCFG,
    	 *		I2S:AIFFMTCFG, I2S:AIFWMSK0, I2S:AIFWMSK1, and I2S:AIFWMSK2.
    	 * 		BCLK must not be running when changing the I2S:AIFWCLKSRC register. */
    	I2SControlTable g_controlTable;    // Define global
    	g_pControlTable = &g_controlTable; // Assign pointer
    
    	I2SClockConfigure(	I2S0_BASE,
    				I2S_INT_WCLK |    	// Clock source (internal)
    				I2S_INVERT_WCLK); 	// Clock polarity (not inverted)
    
    	I2SChannelConfigure(I2S0_BASE,
    			    I2S_MONO_MODE |   	// single phase chan 0
    			    I2S_LINE_INPUT,
    			    I2S_LINE_UNUSED,    // null for chan 1
    			    I2S_LINE_UNUSED);   // null for chan 2
    
    	I2SAudioFormatConfigure(I2S0_BASE,
    				I2S_MEM_LENGTH_24 	| // Sample size
    				I2S_POS_EDGE        | // Positive Clock edge sampling
    				I2S_DUAL_PHASE_FMT  | // Phase
    				I2S_WORD_LENGTH_24,   // Word length
    				1);
    
    	I2SBufferConfig(	I2S0_BASE,
    				(uint32_t)&i2s_in,		// address of input buffer
    				(uint32_t)&i2s_out,		// address of output buffer
    				I2S_DMA_BUF_SIZE_256,	// size of DMA buffer: 256
    				I2S_BUFF_SIZE);
    
    
    	/* 4.	Enable BCLK (set externally in the PRCM module) */
        // I2S clock control
        HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL) = (PRCM_I2SCLKCTL_SMPL_ON_POSEDGE | (2 << PRCM_I2SCLKCTL_WCLK_PHASE_S) | PRCM_I2SCLKCTL_EN);
        // I2S clock control (Use internally generated clock
        HWREG(PRCM_BASE + PRCM_O_I2SBCLKSEL) = PRCM_I2SBCLKSEL_SRC;
        // BCLK division ratio (BCLK = MCUCLK/BDIV[Hz] = 48MHz/16 = 3MHz)
        HWREG(PRCM_BASE + PRCM_O_I2SBCLKDIV) = BCLK_DIV;
    
        //Load setting from buffers to actual registers
        HWREG(PRCM_BASE + PRCM_O_CLKLOADCTL) = PRCM_CLKLOADCTL_LOAD;
    
    
        /* 5.	Configure and prepare and samplestamp generator */
        // Set the I2S:STMPWPER register.
        uint16_t tmp = I2S_BUFF_SIZE;
        /* This number corresponds to the total size of the sample ring buffer used by the system.
           Set the two registers I2S:STMPINTRIG and I2S:STMPOUTTRIG > I2S:STMPWPER
           to avoid false triggers before the samplestamp generator is started.
         */
        //WCLK Counter period value
        HWREG(PRCM_BASE + I2S_O_STMPWPER) = tmp;
        //WCLK Counter trigger value for input pins
        HWREG(PRCM_BASE + I2S_O_STMPINTRIG) = tmp + 1;
        //WCLK counter trigger value for output pins
        HWREG(PRCM_BASE + I2S_O_STMPOUTTRIG) = tmp + 1;
    
    
        /* 6.	Enable the samplestamp generator */
        I2SSampleStampEnable(I2S0_BASE);
    
    
        /* 7.	Enable the serial audio interface */
        // Set the I2S:AIFPTRNEXT and the I2S:AIFOUTPTRNEXT registers for first memory interface buffers
        // Set the I2S:AIFDMACFG register; This number corresponds to the length of each block in the sample ring buffer used by the system
        I2SEnable(I2S0_BASE);
        // Set the I2S:AIFINPTRNEXT and the I2S:AIFOUTPTRNEXT registers for second memory interface buffers.
        I2SPointerUpdate(I2S0_BASE, true);
        I2SPointerUpdate(I2S0_BASE, false);
    
        Hwi_Params hwiParams;
        // Setup HWI Handler
        Hwi_Params_init(&hwiParams);
        Hwi_construct(&hwiStruct, INT_I2S_IRQ, i2s_ihandler, &hwiParams, NULL);
    
        // Enable DMA_IN interrupt
        I2SIntEnable(I2S0_BASE, I2S_INT_DMA_IN);
        I2SPointerUpdate(I2S0_BASE, true);
        I2SPointerUpdate(I2S0_BASE, false);
    
    
        /* 8.	START INPUT AND OUTPUT AUDIO STREAMING */
        // Set the I2S:STMPINTRIG and the I2S:STMPOUTTRIG registers so they correctly match the I2S:AIFINPTR and the I2S:AIFOUTPTR registers
        I2SSampleStampConfigure(I2S0_BASE, true, false);
        // Interrupt clear register
        I2SIntClear(I2S0_BASE, I2S_INT_DMA_IN);
    }
    
    
    static void taskFxn(UArg a0, UArg a1)
    {
    	i2s_init();
    }
    
    int main(void)
    {
    	Task_Params taskParams;
    
        /* Call board init functions. */
        Board_initGeneral();
        Board_initUART();
    
    	/* Initialize UART peripheral */
    	Uart_init();
    
        /* Configure task. */
        Task_Params_init(&taskParams);
        taskParams.stack = myTaskStack;
        taskParams.stackSize = sizeof(myTaskStack);
        Task_construct(&myTask, taskFxn, &taskParams, NULL);
    
        /* Start BIOS */
        BIOS_start();
    
        return (0);
    }
    

  • Hi, Andrei

    I was also using the code from TI example <SimpleBLEPeripheral>, but I found I cannot read any register of I2S module using HWREG(...). Each time I read the register, the program crashes. I can write the registers and read other module's register. I really don't know why. Can you help me with that?

    Thanks so much in advance.

    Yinxu
  • Hi, Yinxu!

    Maybe you forgot to set constraint & dependency?

            /* Register power Constraint & Dependency. */
            Power_setConstraint(Power_SB_DISALLOW);
            if (!Power_setDependency(PERIPH_I2S)) while(1);
    

  • Hi, Andrei

    Thank you so much for your reply! I solved the problem. I used it the chip on Contiki, the code is different, but I think you are right. I only turned on the power domain in PRCM, but not enable the I2S in peripheral. Now, I can read the register.

    Thanks much again.
  • Hi Andrei,

    Could you explain your code a bit? In i2s_in_iter and i2s_out_iter functions, what is the purpose of the modulo operation after the increment?

    static inline uint16_t* i2s_in_iter(void)
    {
        uint16_t* addr = &(i2s_in[I2S_BUFF_SIZE * index_in]);
        index_in++;
        index_in %= I2S_BUFF_IN_NUM;
    
        return addr;
    }

    Best Regards,
    Veikko Kero

  • Hi Veikko,

    In a nutshell: the modulo operation used for cyclic motion over buffers: buff_No_0, ... last_buf, buff_No_0, ...

    In details: Here are two arrays of buffers: i2s_in[] for data collection and i2s_out[] for data transmission. One buffer size = I2S_BUFF_SIZE. The array i2s_in[] consist of I2S_BUFF_IN_NUM buffers and the i2s_out[] consist of I2S_BUFF_OUT_NUM buffers. Each time when I2S-hardware raises an interrupt, i2s_in_iter() and i2s_out_iter() are provide new values for hardware pointers: I2S_O_AIFINPTRNEXT and I2S_O_AIFOUTPTRNEXT.

    BR, Andrei

  • Hi Andrei,

    thanks for the reply.

    I'm using Conitiki and CC13xx Driver Library for my application, but my program freezes after enabling BCLK and trying to load settings to PRCM registers. Do you know how to setup the constraint & dependency in Contiki?

    Best Regards,
    Veikko

  • Hi Andrei,

    I also try to get I2S working on Contiki in order to retrieve audio data but we are struggling with setting up the whole interface. Have you made any progress in that regard and would be so kind to share your experience with me? (e.g. source code) Thanks in advance!
  • Hi Tobias!
    Unfortunately, my use case is not intended for sound processing.
    Try to read the following discussion:
    e2e.ti.com/.../463161
  • Hello Mike,

    I am also trying to enable microphone on sensortag with the help on code snippet from your response and Andrei. But for me all i can see is blank response and i am not sure if it is even ON or not. Below mentioned is my code for the same, can you please suggest where i might be going wrong -

    /*
    * i2s_init.c
    *
    * Created on: 20 ÿíâ. 2016 ã.
    * Author: Andrey
    */

    #include <inc/hw_types.h>

    #include <driverlib/prcm.h>
    #include <driverlib/i2s.h>
    #include <ioc.h>
    #include <ti-lib.h>
    #include "Board.h"
    #include "leds.h"

    #include "i2s-mic.h"

    //fft transform
    #include "lib/ifft.h"

    #define BCLK_DIV (16) /* 48MHz/16 = 3MHz */

    #define WCLK (IOID_25)//(IOID_27)
    #define BCLK BOARD_IOID_MIC_CLK
    #define MOSI (IOID_18)
    #define MISO BOARD_IOID_MIC_DATA


    #define I2S_BUFF_IN_NUM (2)
    #define I2S_BUFF_OUT_NUM (1)

    #define PERIODIC_INTERVAL 2

    #define I2S_BUFF_SIZE 256

    //table used to convert to oneCount (PDM) and fft
    int16_t i2s_in [I2S_BUFF_SIZE];
    int16_t i2s_out[I2S_BUFF_SIZE];



    uint16_t inBuf[I2S_BUFF_SIZE];
    uint16_t outBuf[I2S_BUFF_SIZE];

    /*
    static PIN_State pinState;
    static PIN_Handle pinHandle;
    static ti_sysbios_family_arm_m3_Hwi_Struct hwiStruct;
    */
    static uint32_t index_in;
    static uint32_t index_out;
    static uint32_t index_table;


    //PDM : counting ones in each 32bits value (sampling rate Fbit/24-->
    unsigned int
    ones32(unsigned int x)
    {
    /* 32-bit recursive reduction using SWAR...
    but first step is mapping 2-bit values
    into sum of 2 1-bit values in sneaky way
    */
    x -= ((x >> 1) & 0x55555555);
    x = (((x >> 2) & 0x33333333) + (x & 0x33333333));
    x = (((x >> 4) + x) & 0x0f0f0f0f);
    x += (x >> 8);
    x += (x >> 16);
    return(x & 0x0000003f);
    }


    static inline uint16_t* i2s_in_iter(void)
    {
    uint16_t* addr = (uint16_t *)&(inBuf[I2S_BUFF_SIZE * index_in]);
    index_in++;
    index_in %= I2S_BUFF_IN_NUM;

    index_table ++;

    /*
    uint16_t* addr = (uint16_t *)&( inBuf[ index_in ] );

    //not rewriting table, for security keep index max (test)

    if(index_in <= 127)
    {
    index_in ++;
    }

    //index_in %= I2S_BUFF_SIZE;
    */
    return addr;
    }

    static inline uint16_t* i2s_out_iter(void)
    {
    uint16_t* addr = (uint16_t *)&(outBuf[I2S_BUFF_SIZE * index_out]);
    index_out++;
    index_out %= I2S_BUFF_OUT_NUM;

    return addr;
    }

    static inline void i2s_iter(void)
    {
    // DMA Output Buffer Next Pointer
    HWREG(I2S0_BASE + I2S_O_AIFOUTPTRNEXT) = (uint32_t)i2s_out_iter();
    // DMA Input Buffer Next Pointer
    HWREG(I2S0_BASE + I2S_O_AIFINPTRNEXT) = (uint32_t)i2s_in_iter();
    }

    static void i2s_extractData(void)
    {
    /*
    uint16_t i;
    for(i=0; i < I2S_BUFF_SIZE ; i++)
    {
    i2s_in[i] = ((uint16_t) ones32(inBuf[i])) - 8;
    }*/


    //testing CIC filter
    uint16_t i, j;

    // filter averages variables
    int mov_avg1 = 0, mov_avg2 = 0;
    uint8_t cic_count = 8;

    int s2_comb1_2 = 0;
    int s2_comb1_1 = 0;
    int s2_comb2_2 = 0;
    int s2_comb2_1 = 0;
    ////


    j = 0 ;

    for(i=0; i < I2S_BUFF_SIZE ; i++)
    {
    // i2s_in[i] = ((uint16_t) ones32(inBuf[i])) - 8;
    i2s_in[i] = inBuf[i];
    printf("I2S Input:\t%d\n", i2s_in[i]);
    mov_avg1 += i2s_in[i];
    mov_avg2 += mov_avg1;


    if(--cic_count == 0 )
    {
    // complete the CIC cascade.
    cic_count = 8;

    mov_avg2 /= 8;



    // Finish the second CIC filter by implementing the
    // remaining comb stages, with the finished PCM sample
    // in v. Due to bit depth growth in the filters, v
    // ranges from -8192 to +8192, corresponding to a
    // positive real sample ranging from -1.0 to +1.0 with
    // 13 bits of precision.
    {
    int tmp1;//, tmp2;
    tmp1 = mov_avg2 - s2_comb1_2;
    s2_comb1_2 = s2_comb1_1;
    s2_comb1_1 = mov_avg2;
    mov_avg2 = tmp1 - s2_comb2_2;
    s2_comb2_2 = s2_comb2_1;
    s2_comb2_1 = tmp1;

    //final value reported in table
    i2s_out[j++] = mov_avg2;
    }
    /*
    mov_avg1 = 0;
    mov_avg2 = 0;
    */
    }

    }
    }

    /*
    * Termination Sequence implemented according to
    * <swcu117d.pdf> 22.9.2. Termination Sequence.
    */
    static void i2s_terminate(void)
    {
    HWREG(I2S0_BASE + I2S_O_AIFDMACFG) = 0;
    HWREG(I2S0_BASE + I2S_O_STMPCTL) = 0;
    HWREG(I2S0_BASE + I2S_O_IRQCLR) = 0x3F;
    HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL) = 0;
    HWREG(PRCM_BASE + PRCM_O_CLKLOADCTL) = PRCM_CLKLOADCTL_LOAD;
    HWREG(I2S0_BASE + I2S_O_IRQMASK) = 0;

    index_table =0;

    }

    /*
    * Interrupt service routine.
    *
    */
    void I2SHandler(void) {

    static uint32_t flags;
    flags = 0xf & HWREG(I2S0_BASE + I2S_O_IRQFLAGS);

    if (flags)
    {
    i2s_terminate();
    }
    else
    {
    i2s_iter();

    }

    if( index_table >= 254 )
    {
    int i;

    i2s_terminate();


    i2s_extractData();
    //fft
    ifft(i2s_out, i2s_in, I2S_BUFF_SIZE/8);
    printf("I2S FFT:\n");
    for(i=0;i < I2S_BUFF_SIZE/16; i++)
    {
    printf("\t%d\n", i2s_out[i]);
    }
    for(i=0;i < I2S_BUFF_SIZE; i++)
    {
    i2s_in[i] = 0 ;
    }
    printf("\n");


    } else
    {
    //i2s_extractData();
    }
    }

    /*
    * ======== i2s_init ========
    *
    * Initialization Sequence implemented according to
    * <swcu117d.pdf> 22.9.1. Start-up Sequence.
    */
    void i2s_init(void)
    {
    index_in = 0;
    index_out = 0;

    ti_lib_ioc_pin_type_gpio_output(BOARD_IOID_MIC_POWER);
    ti_lib_ioc_io_drv_strength_set(BOARD_IOID_MIC_POWER, IOC_CURRENT_2MA,
    IOC_STRENGTH_MIN);
    // ti_lib_gpio_pin_write((1 << BOARD_IOID_MIC_POWER),1);
    ti_lib_gpio_write_dio((1 << BOARD_IOID_MIC_POWER),1);

    // step 1. Set up and configure required ADx and clock pins (set externally in the IOC module).
    // IOCPortConfigureSet(BCLK, IOC_PORT_MCU_I2S_BCLK, IOC_STD_OUTPUT);
    IOCPortConfigureSet(BCLK, IOC_PORT_MCU_I2S_BCLK, IOC_STD_OUTPUT);
    IOCPortConfigureSet(MISO, IOC_PORT_MCU_I2S_AD0, IOC_STD_INPUT);
    // TODO : What about WCLK? We're only using a single Mic (dummy for now)
    IOCPortConfigureSet(WCLK, IOC_PORT_MCU_I2S_WCLK, IOC_STD_OUTPUT);
    // TODO : What about MCLK? (dummy for now)
    //IOCPortConfigureSet(IOID_24, IOC_PORT_MCU_I2S_MCLK, IOC_STD_OUTPUT);


    // 2. Enable I2S peripheral and configure WCLK and MCLK audio clocks
    // (set externally in the PRCM module).

    PRCMPeripheralRunEnable(PRCM_PERIPH_I2S);
    PRCMLoadSet();
    PRCMPeripheralSleepDisable(PRCM_PERIPH_I2S);
    PRCMLoadSet();
    PRCMPeripheralDeepSleepDisable(PRCM_PERIPH_I2S);
    PRCMLoadSet();
    PRCMAudioClockConfigSet(PRCM_WCLK_SINGLE_PHASE | PRCM_WCLK_POS_EDGE,
    I2S_SAMPLE_RATE_16K);


    /*custom config for 2,4MHZ BCLK
    uint32_t ui32MstDiv = 1;
    uint32_t ui32BitDiv = 10;
    uint32_t ui32WordDiv = 15;

    PRCMAudioClockConfigSetOverride(PRCM_WCLK_SINGLE_PHASE | PRCM_WCLK_POS_EDGE, ui32MstDiv,
    ui32BitDiv, ui32WordDiv);
    */


    PRCMLoadSet();


    // 3. Configure the serial audio interface format and the memory interface controller
    /* Set the following registers: I2S:AIFWCLKSRC, I2S:AIFDIRCFG, I2S:AIFFMTCFG,
    I2S:AIFWMSK0, I2S:AIFWMSK1, and I2S:AIFWMSK2.
    BCLK must not be running when changing the I2S:AIFWCLKSRC register. */
    I2SControlTable g_controlTable; // Define global
    g_pControlTable = &g_controlTable; // Assign pointer

    PRCMAudioClockDisable();

    I2SClockConfigure(I2S0_BASE,
    I2S_INT_WCLK | // Clock source (internal)
    I2S_INVERT_WCLK); // Clock polarity (not inverted)

    I2SChannelConfigure(I2S0_BASE,
    I2S_CHAN0_ACT |I2S_LINE_INPUT , // single phase chan 0
    0, // null for chan 1
    0); // null for chan 2

    I2SAudioFormatConfigure(I2S0_BASE,
    I2S_MEM_LENGTH_16 | // Sample size
    I2S_POS_EDGE | // Clock edge sampling
    I2S_SINGLE_PHASE_FMT | // Phase
    I2S_WORD_LENGTH_16, // Word length
    1); // bit clk delay format (1= I2s )

    I2SBufferConfig(I2S0_BASE,
    (uint32_t) &inBuf, // is the address of the input buffer.
    (uint32_t) &outBuf, // is the address of the output buffer
    I2S_DMA_BUF_SIZE_256, // is the size of the DMA buffers. Must be > 0!
    I2S_BUFF_SIZE); // is the size of the channel buffers.


    // 4. Enable BCLK (set externally in the PRCM module).

    HWREG(PRCM_BASE + PRCM_O_I2SBCLKSEL) = 1; // use internally generated clock
    PRCMLoadSet();
    PRCMAudioClockEnable();
    PRCMLoadSet();


    // 5. Configure and prepare the samplestamp generator:
    /*
    Set the I2S:STMPWPER register.
    This number corresponds to the total size of the sample ring buffer used by the system.
    Set the two registers I2S:STMPINTRIG and I2S:STMPOUTTRIG > I2S:STMPWPER
    to avoid false triggers before the samplestamp generator is started.
    */
    HWREG(I2S0_BASE + I2S_O_STMPWPER) = I2S_BUFF_SIZE;
    HWREG(I2S0_BASE + I2S_O_STMPINTRIG) = I2S_BUFF_SIZE+1;
    HWREG(I2S0_BASE + I2S_O_STMPOUTTRIG) = I2S_BUFF_SIZE+1;


    // 6. Enable the samplestamp generator:
    /*
    Set I2S:STMPCTRL.STMP_EN = 1
    */
    HWREG(I2S0_BASE + I2S_O_STMPCTL) = I2S_STMPCTL_STMP_EN;
    int32_t var = HWREG(I2S0_BASE + I2S_O_STMPWCNT);
    printf("WCNT\t%d\n",var);
    while (2 > HWREG(I2S0_BASE + I2S_O_STMPWCNT)){
    }

    HWREG(I2S0_BASE + I2S_O_STMPWSET) = 0;

    // 7. Enable the serial audio interface:
    /*
    Set the I2S:AIFINPTRNEXT and the I2S:AIFOUTPTRNEXT registers
    for first memory interface buffers.
    Set the I2S:AIFDMACFG register;
    This number corresponds to the length of each block in the sample ring buffer used by the system.
    Set the I2S:AIFINPTRNEXT and the I2S:AIFOUTPTRNEXT registers
    for second memory interface buffers.
    */

    //register i2s interrupt
    I2SIntRegister(I2S0_BASE,I2SHandler);



    //I2SEnable(I2S0_BASE);

    i2s_iter();

    // DMA Buffer Size Configuration
    HWREG(I2S0_BASE + I2S_O_AIFDMACFG) = I2S_BUFF_SIZE-1;

    i2s_iter();



    // 8. Start input and output audio streaming:
    /*
    Set the I2S:STMPINTRIG and the I2S:STMPOUTTRIG registers
    so they correctly match the I2S:AIFINPTR and the I2S:AIFOUTPTR registers.
    */
    I2SSampleStampConfigure(I2S0_BASE, true, false);


    I2SIntEnable(I2S0_BASE ,I2S_INT_ALL & (~I2S_INT_DMA_OUT) );
    //I2SEnable(I2S0_BASE);
    // Current Value of WCNT


    while (5 > HWREG(I2S0_BASE + I2S_O_STMPWCNT)){
    }



    // WCLK Counter Trigger Value for Input Pins
    HWREG(I2S0_BASE + I2S_O_STMPINTRIG) = 0;
    // WCLK Counter Trigger Value for Output Pins
    HWREG(I2S0_BASE + I2S_O_STMPOUTTRIG) = 0;

    index_in = 0 ;


    // Interrupt Clear Register
    HWREG(I2S0_BASE + I2S_O_IRQCLR) = 0x3F;




    //printf("I2S_O_AIFWCLKSRC:\t %08lx\n", HWREG(I2S0_BASE + I2S_O_AIFWCLKSRC));

    /* printf("PRCM_O_I2SCLKGR:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SCLKGR));
    printf("PRCM_O_I2SCLKGS:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SCLKGS));
    printf("PRCM_O_I2SCLKGDS:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SCLKGDS));
    printf("PRCM_O_I2SCLKCTL:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL));
    printf("PRCM_O_I2SMCLKDIV:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SMCLKDIV));
    printf("PRCM_O_I2SBCLKDIV:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SBCLKDIV));
    printf("PRCM_O_I2SWCLKDIV:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SWCLKDIV));
    printf("PRCM_O_I2SBCLKSEL:\t %08lx\n", HWREG(PRCM_BASE + PRCM_O_I2SBCLKSEL));

    printf("I2S_O_AIFWCLKSRC:\t %08lx\n", HWREG(I2S0_BASE + I2S_O_AIFWCLKSRC));
    printf("I2S_O_AIFDIRCFG:\t %08lx\n", HWREG(I2S0_BASE + I2S_O_AIFDIRCFG));
    printf("I2S_O_AIFFMTCFG:\t %08lx\n", HWREG(I2S0_BASE + I2S_O_AIFFMTCFG));
    printf("I2S_O_AIFWMASK0:\t %08lx\n", HWREG(I2S0_BASE + I2S_O_AIFWMASK0));
    printf("I2S_O_IRQMASK:\t\t %08lx\n", HWREG(I2S0_BASE + I2S_O_IRQMASK));
    printf("I2S_O_IRQFLAGS:\t\t %08lx\n", HWREG(I2S0_BASE + I2S_O_IRQFLAGS));

    printf("I2S_O_IRQSET:\t\t %08lx\n", HWREG(I2S0_BASE + I2S_O_IRQSET));*/
    printf("\n");

    index_in = 0 ;

    }

    void i2s_enable(bool enable)
    {

    if( !enable && HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL) != 0 )
    {
    //setting next DMA pointers to NULL to stop i2s after next interrupt

    // DMA Output Buffer Next Pointer to NULL
    HWREG(I2S0_BASE + I2S_O_AIFOUTPTRNEXT) = 0;
    // DMA Input Buffer Next Pointer to NULL
    HWREG(I2S0_BASE + I2S_O_AIFINPTRNEXT) = 0;
    } else if ( enable && HWREG(PRCM_BASE + PRCM_O_I2SCLKCTL) == 0 )
    {
    i2s_init();
    }
    }
  • Hi Mike, I'm going to do a CC1310 audio project now. I don't know the configuration of I2S. Can your I2S configuration work? Can you help me? My email:wiiliam@1688wdd.com, thank you very much.
  • William@1688wdd.com