Problem with I2S on teensy 4.0

Hello geeks ...
I try to understand how I2S works, and I wrote a very simple program to see an I2S output signal on an oscilloscope. I spent a lot of time reading reference manual "IMXRT1060RM_rev3_annotations.pdf" (I cannot put it under my pillow), and I pruned differents libraries (Paul's and others' ones) to extract the essentials things to configure a 48 kHz output signal on SAI2 port. This signal is monophonic and constant (32 bits word 0xAAAAAAAA). No interrupt, no dma, just testing FIFO status request bit in main loop to place a new 0xAAAAAAAA in the TCSR register. I was happy when I saw awaited 48 kHz SYNC, 3.072 MHz BCLK, and stable TxData : every configurations in my simplest "Config_I2S.h" attached file look correct. (With a very simple clock setting without any computation and without fractionnal system sollicitation).
But in facts, there is something which does not work.
I added an output signal on P0, toggling each time bit 16 = FIFO Request Flag in SAI2_TCSR is one, in the same time I put a new data. I added printing status before writing data (1 = request placed) and after writing data (should be 0 : request granted). It is not 0 after writing. The loop is not synchronised by 48 kHz I2S sample clock, but runs a lot faster, and P0 toggles approximatively at 840 kHz instead of 24 kHz (see signals.jpg).
So, if i try to scan data in a constant array instead of repeating always the same word, data is not output correctly. Same thing if I try to operate with interrupt. The program writes data in TCSR when the FIFO is not ready to accept new data, so this new data is ignored and lot of samples of the array are skipped ...
I have two boards, they produce the same thing.
Where is the error ?
 

Attachments

  • Config_I2S.h
    3.8 KB · Views: 17
  • Generateur_I2S.ino
    1.2 KB · Views: 18
  • Signals.jpg
    Signals.jpg
    137 KB · Views: 17
Last edited:
When FIFO Request FLAG status bit (FRF = bit 16) in I2S2_TCSR register is set, writing data in I2S2_TDR0 register does not reset this flag, whatever be the watermark written in I2S2_TCR1. But signals on 3 outputs BCLK, SYNC and TxData are consistent. So I made some error in my code so big that I do not see it, or in my understanding of SAI mechanics.
 
Last edited:
Here are my two files with verbose comments ...
Code:
/**********************************
 *                                *
 * Simplest I2S audio output test *
 *                                *
 * Board Teensy 4.0               *
 * - Output TX      P2            *
 * - Output SYNC    P3            *
 * - Output BCLK    P4             *
 *                                *
 **********************************/
#include "Config_I2S.h"
#include <Arduino.h>
int n = 0;
uint32_t S1, S2;
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode (0, OUTPUT); // Synchro oscilloscope
  while (!Serial) {};
  Serial.println ("I2S output test");
  Config_I2S();
  }
   
void loop() {
  do { S1 = (I2S2_TCSR >> 16) && 7; } while ((S1 && 1)==0); // 3 FIFO status bits in Tranceiver Control and Status Register
  I2S2_TDR0 = 0xAAAAAAAA; // Constant signal on left channel (only left channel used)
  digitalToggle (0);      // Should produce a 24 kHz square wave synchronous with 48 kHZ I2c (with some phase jitter)
//  asm volatile ("DSB");  // Tried it, does not change anything
  S2 = (I2S2_TCSR >> 16) && 7; // new status bits after writing in Transmit Data Register
  if (n++ < 32) { Serial.print (S1); Serial.print (S2); Serial.print(' '); } // Should write "10" blocks but actually write "11" : request bit NOT cleared
  }
Code:
/**********************
 *                    *
 * SAI2 configuration *
 *                    *
 **********************/

#include <Arduino.h>

void Config_I2S(void)

{ // Master clock configuration 6,144 MHz
    // ------------------------------------
    CCM_CCGR5 |= CCM_CCGR5_SAI2(CCM_CCGR_ON);                       // CG10 = 11 SAI2 clock enabled
    CCM_ANALOG_PLL_AUDIO = CCM_ANALOG_PLL_AUDIO_POST_DIV_SELECT(2)  // Post divider by 1
                           | CCM_ANALOG_PLL_AUDIO_BYPASS              // Keep some stable clock while configuring future one
                         | CCM_ANALOG_PLL_AUDIO_ENABLE              // PLL4 on
                         | CCM_ANALOG_PLL_AUDIO_DIV_SELECT(32);     // PLL4 frequency 32*24 MHz = 768,00 MHz
    CCM_ANALOG_PLL_AUDIO_NUM = 0;                                   // Numerator of fractional system not sollicited
    CCM_ANALOG_PLL_AUDIO_DENOM = 1;                                  // Any denominator 
    CCM_ANALOG_PLL_AUDIO_CLR = CCM_ANALOG_PLL_AUDIO_POWERDOWN;      // Switch on PLL
    while (!(CCM_ANALOG_PLL_AUDIO & CCM_ANALOG_PLL_AUDIO_LOCK)) {};    // Wait for pll-lock
    CCM_ANALOG_MISC2_CLR = (CCM_ANALOG_MISC2_DIV_MSB
                         | CCM_ANALOG_MISC2_DIV_LSB);                         // Two bits cleared divide by 1
    CCM_ANALOG_PLL_AUDIO_CLR = CCM_ANALOG_PLL_AUDIO_BYPASS;                  // Clear bypass, now use this clock
    CCM_CSCMR1 = (CCM_CSCMR1 & ~(CCM_CSCMR1_SAI2_CLK_SEL_MASK))
               | CCM_CSCMR1_SAI2_CLK_SEL(2);                                                // SAI2_CLK_SEL = 2 : clock from PPL4 Audio
    CCM_CS2CDR = (CCM_CS2CDR & ~(CCM_CS2CDR_SAI2_CLK_PRED_MASK | CCM_CS2CDR_SAI2_CLK_PODF_MASK))
                 | CCM_CS2CDR_SAI2_CLK_PRED(4)                                        // divide by 5
                 | CCM_CS2CDR_SAI2_CLK_PODF(24);                                      // and then by 25
    IOMUXC_GPR_GPR1 = (IOMUXC_GPR_GPR1 & ~(IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL_MASK))
                    | (IOMUXC_GPR_GPR1_SAI2_MCLK_DIR                                // SAI2 master clock = output
                    | IOMUXC_GPR_GPR1_SAI2_MCLK3_SEL(0));                        // from root 

    // Ports configuration
    // -------------------
    CORE_PIN33_CONFIG = 2;    // P33 MCLK output (test point on bottom side)
    CORE_PIN2_CONFIG     = 2;  // P2  SAI2_TX_DATA output
    CORE_PIN3_CONFIG     = 2;  // P3     SAI2_TX_SYNC output
    CORE_PIN4_CONFIG     = 2;  // P4  SAI2_TX_BCLK output

// Transmitter configuration
// -------------------------
#define tsync 0                                          // rsync = 1-tsync

    I2S2_TMR = 1;                     // Only left channel used, right unused (all bits = 0);
    I2S2_TCR1 = I2S_TCR1_RFW(1);      // Transmit FIFO watermark
    I2S2_TCR2 = I2S_TCR2_SYNC(tsync)    // uses its own clock
                | I2S_TCR2_MSEL(1)    // Audio MasterCLock = SAI2_CLK_ROOT
                | I2S_TCR2_BCP        // Bit Clock polarity falling edge
                | I2S_TCR2_BCD        // internal clock
                | I2S_TCR2_DIV(0);    // Divided by 2x(0+1) = 2; thus 3.072 MHz
    I2S2_TCR3 = I2S_TCR3_TCE;         // Channel 0 enabled (only this one avaailable)
    I2S2_TCR4 = I2S_TCR4_FRSZ(1)      // Frame size 2 words
                | I2S_TCR4_SYWD(31)   // Sync Width 32 bits
                | 0x20                // optional CHMOD = 1 for "Output mode" (0's instead of tree-state on right channel)
                | I2S_TCR4_MF         // MSB First
                | I2S_TCR4_FSE        // Frame sync early
                | I2S_TCR4_FSP        // Frame sync polarity active high.
                | I2S_TCR4_FSD;       // Frame sync internal
    I2S2_TCR5 = I2S_TCR5_WNW(31)
                | I2S_TCR5_W0W(31)
                | I2S_TCR5_FBT(31);      // Frame format : everything 32 bits
    I2S2_TCSR = I2S_TCSR_FR                      // FiFo reset
              | I2S_TCSR_BCE;                  // Normaly not necessary
    
    // Push anything in the FIFO in order to delay first request
    // ---------------------------------------------------------
    for (int i = 0; i < 16; i++) I2S2_TDR0 = 0; 

    // And now, go !
    // -------------
    I2S2_TCSR |= I2S_TCSR_TE; }
 
Easily done … I always have to think. My rule is two characters is logical (==, !=, &&, || etc.), one for bitwise (&, ^, |, ~). The exception is logical not (!).
 
Easily done … I always have to think. My rule is two characters is logical (==, !=, &&, || etc.), one for bitwise (&, ^, |, ~). The exception is logical not (!).
Might be old world but I use "and" and "or". I do use ! instead of "not", but ! does not confuse me, or lead to easy errors which are damnably difficult to find.
 
"&&" is larger than "&". Should apply to large things (several bits) and "&" to small things (logical = boolean = 1 bit only). The mistake cannot be done in fortran or basic ...
 
Back
Top