how to use MQX SAI driver for FRDM-K64f

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

how to use MQX SAI driver for FRDM-K64f

Jump to solution
4,441 Views
oleglavrukhin
Contributor IV

Hello everybody!

Firstly, I want to thank the team of developers MQX OS! this is my first experience with OS, and I really pleased to work with MQX! Many examples with detailed comments allow me to quickly build a working project! Thanks!

But I have some problems with SAI driver in MQX 4.1.1.

I want to build simply audio player on my FRDM-K64f: read WAV file from USB stick and  play sound via external DAC using SAI bus.

in my project I connected 2 example MQX projects: "mfs_usb" and "sai_dma_demo".

Using a "mfs_usb" i can open a WAV file from USB stick and read it,  from "sai_dma_demo" I took  a part of code which read WAV header, make a setting for SAI driver and sending data to SAI (function "Shell_play" from "sh_audio.c").

Now i have a partially working device: WAV header is correctly read, I have a BCLK and FrameSync signals in FRDM pins, but i don't have a data from SAI. 

A part of code from "sai_dma_demo" which must sending data to SAI:

/*Get the stats of the sai, this is the interface between application and driver which provides the status of sai.*/

    ioctl(device_ptr, IO_IOCTL_I2S_GET_STATISTICS, &stats);

    requests = header.DATA_SUBCHUNK.Subchunk2Size / stats.SIZE;

    remainder = header.DATA_SUBCHUNK.Subchunk2Size % stats.SIZE;

    if(remainder != 0)

    {

      requests += 1;

    }

    uint32_t num = 0;

    for(i = 0; i < requests; i ++)

    {

        /*Waiting for there are empty blocks*/

        ioctl(device_ptr, IO_IOCTL_I2S_WAIT_TX_EVENT,NULL);

        /* Get the info where to fill the data */

        ioctl(device_ptr, IO_IOCTL_I2S_GET_STATISTICS, &stats);

        if((i != requests - 1) || (remainder == 0))

        {

            num = fread(stats.IN_BUFFER, 1, stats.SIZE, file_ptr);

            if(!num)

            {

                return -1;

            }

        }

        else

        {

            num = fread(stats.IN_BUFFER, 1, remainder, file_ptr);

            if(!num)

            {

                return -1;

            }

            /*Fill zero to buffer*/

            uint32_t j = 0;

            for(j = remainder; j < stats.SIZE; j ++)

            {

                stats.IN_BUFFER[j] = 0;

            }

        }

        if (i == 0)

        {

            ioctl(device_ptr, IO_IOCTL_I2S_START_TX, NULL);

        }

        ioctl(device_ptr, IO_IOCTL_I2S_UPDATE_TX_STATUS, &stats.SIZE);

    }

when this cycle is working, i see what device  reading the data from USB stick, but actual time to read a 10-second WAV file is 70 seconds. i try to use some different USB sticks( because I read in the community that there is a problem with USB speed in older version MQX) , but time is come down to 45-50 second.

Then i try to send out data from flash memory. I included a file with 96-bytes sine wave table Hex code and changed part of code (SAI setting don't changed):

uint16_t a,b;

ioctl(i2s_ptr, IO_IOCTL_I2S_START_TX, NULL);

for (a=0; a<500; a++)

{

ioctl(i2s_ptr, IO_IOCTL_I2S_WAIT_TX_EVENT,NULL);

     for (b=0; b<96; b++)

       {

       stats.IN_BUFFER[b] = Sine_Wave_8_Bit[b];

       }

ioctl(device_ptr, IO_IOCTL_I2S_UPDATE_TX_STATUS, &stats.SIZE);

}

But i don't have a sound!

In "MQX_IO_User_Guide" in chapter "I2S Driver" i can't found a "IO_IOCTL_I2S_UPDATE_TX_STATUS", "IO_IOCTL_I2S_START_TX" and  "IO_IOCTL_I2S_WAIT_TX_EVENT" macros.

I would be grateful if the developers explain theirs functions.

I ask for help in solving two problems:

1) how is a correctly use a MQX SAI driver in FRDM-K64F?

2) Was the problem solved slow reading from USB in MQX 4.1.1?

Thanks, Oleg.

Labels (2)
Tags (4)
1 Solution
2,648 Views
RadekS
NXP Employee
NXP Employee

Thank you for your bug report (with pin initialization). I will place it into our internal bug database. It will be fixed in one of next MQX releases.

I will also refer missing documentation in case of "IO_IOCTL_I2S_UPDATE_TX_STATUS", "IO_IOCTL_I2S_START_TX" and "IO_IOCTL_I2S_WAIT_TX_EVENT" ioctl commands.

Unfortunately my experiences with SAI are limited.

In short:

IO_IOCTL_I2S_WAIT_TX_EVENT starts DMA transfer from memory to I2S module.

IO_IOCTL_I2S_UPDATE_TX_STATUS restarts DMA transfer when new data block is available.

IO_IOCTL_I2S_WAIT_TX_EVENT simple wait till end of DMA transfer to the I2S module.

There should not be big differences between reading from USB stick and reading from SD card. Speeds should be comparable and there depends mainly on USB stick/SD card performance.

Problem with speed could be for example in case of Card reader application. In that case MCU reads from SD card and after that send it trough USB. Unfortunately current driver design doesn’t allow simultaneously access to USB and SD card.

Just idea: As I see, example code is written that it read and writes periodically (in the loop).

From my point of view it has sense when RAM buffer size is enough big that reading from USB and writing to I2S module will be divided into two separate tasks, that we can read from USB during time when I2S task waits for I2S transfer end (IO_IOCTL_I2S_WAIT_TX_EVENT effectively blocks task until buffer is empty). So, I guess that smaller double buffer has higher sense than one big with exception case when whole wav file fits to RAM buffer.

I hope it helps you.


Have a great day,
RadekS

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

View solution in original post

0 Kudos
12 Replies
2,648 Views
oleglavrukhin
Contributor IV

with pleasure.

Projects uses a external DAC CS4350 (Cirrus Logic). You must make a change in the I2C procedure (setup_4350() in my projects) to write a settings for DAC what You use.

and look for my comments in "tasks.c".

and don't remember to recompile cloned MQX BSP after change.

hope this help.

Tell me what you got.

good luck. 

Oleg

0 Kudos
2,647 Views
ryanlush
Contributor IV

Could I also have your MQX 4.1 directory?

0 Kudos
2,647 Views
oleglavrukhin
Contributor IV

my MQX directory is 1.5GB (because it have 7 cloning library for different project).  may be any individual folders?

pls, send message for me directly to incommail2017@mail.ru . 

regards, Oleg 

0 Kudos
2,647 Views
ryanlush
Contributor IV

Did you ever have any luck getting this to work? I have this working on MQX 4.0 with interrupts but I can't get it to work on MQX 4.2 using the DMA. I continue to get a BUFFER_ERROR.

    //See if the data transferring is dirty or the transfer is complete.
    //That means the data from buffer is slow, and the dma is transferring the old data.
    //Use the input index and output index.
    //As the dma is still transferring during the callback, so there are still some dirty data.
    if (stats->IN_BUFFER == stats->OUT_BUFFER)
    {
        ksai_info_ptr->TX_FIRST_IO = TRUE;
        /* Disable DMA request. */
        i2s_ptr->TCSR &= ~(I2S_TCSR_FWDE_MASK | I2S_TCSR_FRDE_MASK);
        i2s_ptr->TCSR &= ~I2S_TCSR_FEIE_MASK;
        //i2s_ptr->TCSR &= ~I2S_TCSR_TE_MASK;
        //That means the data is slow.
        if(stats->PACKETS_QUEUED <= stats->PACKETS_REQUESTED)
        {
            stats->BUFFER_ERROR ++;
            if (ksai_info_ptr->TX_CALLBACK)
            {
                ksai_info_ptr->TX_CALLBACK(ksai_info_ptr->TX_CALLBACK_PARAM,
                    SAI_TX_BUFFER_ERROR);
            }
        }
    }

What I dont understand is I feed the buffer 4096 bytes but then the interrupt handler tries to split this into 4 blocks of 4096 bytes. This is from the IO_IOCTL_I2S_UPDATE_TX_STATUS IOCTL

                   //Divide the dma transfer into four TCDs.
                    for(i = 0; i < tx_buffer->PERIODS; i ++)
                    {
                        dma_tcd_mem2reg(&io_info_ptr->TX_TCD, &i2s_ptr->TDR[io_info_ptr->TX_CHANNEL],
                                tx_format->SIZE, tx_buffer->START_PERIOD + i *tx_buffer->SIZE, tx_buffer->SIZE);
                        dma_transfer_submit(io_info_ptr->TX_DCH, &io_info_ptr->TX_TCD, &io_info_ptr->TX_TCD_SEQ);
                    }

0 Kudos
2,647 Views
oleglavrukhin
Contributor IV

Hello, Ryan!

Yes, I get a worked USB-mp3 player, player for MP3 and WAV files from Flash and web-radio, based on FRDM-K64f and MQX 4.1. I  can send my projects for You if this help. they was build in IAR 7.4 and I have a variants in Codewarrior 10.6.

I work with MQX 3 years ago and I now do not remember exactly all the details, but I remember what I set in <C:\Freescale\Freescale_MQX_4_1\mqx\source\bsp\frdmk64f\init_sai.h> in _bsp_ksai_init struct a "buffer size" = 1152 for clear play MP3 frames. And make a some change for make USB port faster. Then re-compile library.

Regards, Oleg

0 Kudos
2,647 Views
ryanlush
Contributor IV

Yes, could you please find a way to share your project with me?

I have tried changing the buffer size in init_sai.h but when I read back stats.SIZE it is also 4096. Then it tries to turn that into 4 4096 byte transfers.

0 Kudos
2,647 Views
oleglavrukhin
Contributor IV

IN the  MQX_4_1 for FRDM-k64 i found a incorrect init function of SAI pins for this board:

in the BSP module (C:\Freescale\Freescale_MQX_4_1_FRDMK64F\mqx\source\bsp\frdmk64f\init_gpio.c) in function "_bsp_sai_io_init" :

            /* Enable system clock to the I2S module */

            SIM_SCGC6 |= SIM_SCGC6_I2S_MASK;

            /* Enable I2S pins */

            PORTC_PCR8 |= PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK;  /* Configure port for MCLK output */

            /* GPIO for I2S0_RX_BCLK */

            PORTC_PCR9 |= PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK;  /* Configure Port for RX Bit Clock */

            /* GPIO for I2S0_TX_BCLK */

            PORTB_PCR18 |= PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK;  /* Configure Port for TX Bit Clock */

            /* GPIO for I2S0_TX_FS */

            PORTB_PCR19 |= PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK;  /* Configure port for TX Frame Sync */

            /* GPIO for SSI0_XD */

            PORTC_PCR7 |= PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK;  /* Configure port for TX Data */

            PORTC_PCR5 |= PORT_PCR_MUX(4) | PORT_PCR_DSE_MASK;  /* Configure port for RX Data */

But PORTC_PCR7 is RX Frame Sync, and PORTC_PCR1 must be configure as TX Data !

i change a pins initialization, but also don't have a data from SAI...

maybe someone will share their experiences in the use of SAI in MQX for FRDM-K64f????

0 Kudos
2,647 Views
rendalfr
Contributor I

Hi Oleg  & all,

-> Did you finally succeed to get some sound ?

Thanks to your bug report, I updated my app to fix PIN initialization :

PORTC_PCR1 |= PORT_PCR_MUX(6) | PORT_PCR_DSE_MASK;

But my decoded MP3 is ugly, I recognize my song, but it sounds saturated...

The DAC I'm using is a cheap ES9023 module that you can find on ebay

-> Did someone succeeded to use it with a FRDM-64F board ?

Among questions I'm trying to answer;

- Can such DAC be powered by the P5V_USB-10 PIN ? or an external power is needed

- The ES9023 have 2 ground pins (analog / digital), should they be both connected to the same board PIN ?

- is the MCLK signal really required ? (There's a 50MHz crystal on the module), but i do not see a lot of differences if i connect the MCLK cable or not & if I call ioctl(IO_IOCTL_I2S_SET_CLOCK_SOURCE_EXT) or not ...

- the data that is sent to the driver... must be 16bits, SIGNED or UNSIGNED  ?

Thanks !

Lucas

0 Kudos
2,647 Views
oleglavrukhin
Contributor IV

Hi, Lucas!

I could not the sound from MQX SAI driver, now i work with KDS 2.0 + SDK 1.0.0, and make a working project to play WAV file from USB stick via external DAC (cs4350 from Cirrus).

i see, you want to make a MP3 player? I try to use HELIX and LIBMAD MP3 decoders, but sound have a distortion (roar and clicking). i try to use some HELIX libraries (from project for STM32F4 and PIC), they have a difference polyphase synthesis filter realisations (С and ASM), but don't get a clear sound... and what a decoder You uses? may be we can build together?

Best regards, Oleg

0 Kudos
2,648 Views
rendalfr
Contributor I

Hi Oleg,

Yes, I'd like to create a simple mp3 player and even a web radio player...

For the player; I succeeded to compile mpg123 using the GCC toolchain

For the decoder (DAC?); I bought a 22$ ES9023 DAC on ebay

I made a small abstraction layer around my player, I run it on both:

- FRDM-64F & I2S output on MQX 4.1.1

- Linux & Alsa output

On FRDM-64F, my sound is not clear at all, I can just recognize that it play my mp3... but the duration of the song is about 20% longer than on linux...

Currently, I put my academic project in standby, since I'm even unable to play a simple sinusoid


On your side, you say you have a working WAV player with SDK 1.0.0 ? but not with MQX SAI driver, correct ?


BR

Lucas

0 Kudos
2,648 Views
oleglavrukhin
Contributor IV

Hi, Lucas!

i want to build FRDM-64F web-radio too :smileyhappy: but can't get a working software MP3 decoder...

While I studied the MQX drivers ended my CW  license :smileycry:. I had to start working with the KDS.

Yes, I have WAV player to play files from usb stick via I2S port and external DAC. I used Kinetis SDK examples : sai_demo And  USB_msd_fatfs. Sound is fine, but PCM file is too big..

Then i try to connect software MP3 decoder, but could not get the desired result. if You give me your e-mail, i can send my project to You, and You can send me your project with mpg123.

regards,

Oleg

0 Kudos
2,649 Views
RadekS
NXP Employee
NXP Employee

Thank you for your bug report (with pin initialization). I will place it into our internal bug database. It will be fixed in one of next MQX releases.

I will also refer missing documentation in case of "IO_IOCTL_I2S_UPDATE_TX_STATUS", "IO_IOCTL_I2S_START_TX" and "IO_IOCTL_I2S_WAIT_TX_EVENT" ioctl commands.

Unfortunately my experiences with SAI are limited.

In short:

IO_IOCTL_I2S_WAIT_TX_EVENT starts DMA transfer from memory to I2S module.

IO_IOCTL_I2S_UPDATE_TX_STATUS restarts DMA transfer when new data block is available.

IO_IOCTL_I2S_WAIT_TX_EVENT simple wait till end of DMA transfer to the I2S module.

There should not be big differences between reading from USB stick and reading from SD card. Speeds should be comparable and there depends mainly on USB stick/SD card performance.

Problem with speed could be for example in case of Card reader application. In that case MCU reads from SD card and after that send it trough USB. Unfortunately current driver design doesn’t allow simultaneously access to USB and SD card.

Just idea: As I see, example code is written that it read and writes periodically (in the loop).

From my point of view it has sense when RAM buffer size is enough big that reading from USB and writing to I2S module will be divided into two separate tasks, that we can read from USB during time when I2S task waits for I2S transfer end (IO_IOCTL_I2S_WAIT_TX_EVENT effectively blocks task until buffer is empty). So, I guess that smaller double buffer has higher sense than one big with exception case when whole wav file fits to RAM buffer.

I hope it helps you.


Have a great day,
RadekS

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos