Skip navigation

When you start a board bring up activity from scratch, there are many challenges. The first of these challenges is the choice of the development environment. Is there any commercial agreement with an IDE Vendor? How flexible is debugging on the IDE and build time, code footprint after optimization etc are related concerns. If the choice is a command line based build environment, the developer is free to choose what features he needs and how easily he can customize it for his requirements.

 

In case of a Graphical development environment, the IDE takes that decision for you. There are many Graphical IDEs some are commercially licensed and some are available with opensource licenses. Eclipse based IDEs are very famous across the opensource licensed Graphical IDEs.

 

Not just due to its extended support, flexibility and ease of use, but also because eclipse provides SDKs to auto generate code for quick application development, many semiconductor vendors(like NXP and Infineon) are giving eclipse based IDEs While I have worked with both commercial and opensource IDEs, my personal favorite has always been an eclipse based IDE. The following are some of the cool eclipse based tools that I have worked with:

  1. Infineon's DAVE is an eclipse based IDE which auto generates MISRA C compliant c code for low level drivers which are run on bare metal as well as on an RTOS for Infineon's micro controllers. Reference.
  2. Freescale's Processor expert is an eclipse plugin which generates C code for Freescale's microcontrollers for baremetal or RTOS(MQX, FreeRTOS, uCOSII) applications. Reference

 

 

The code generated here can be compiled with GCC(default), IAR or Tasking compilers.These are some reasons which make opensource Eclipse tools my favorite. Besides these, I always prefer my IDE to have the following features:

  1. Documentation extraction or generation from source code.
  2. Auto indentation,
  3. Auto complete option for variables, API, pre processor includes, etc.
  4. Performance analysis like code coverage
  5. Subversion control,
  6. Compare and Merge options for code references.

 

Eclipse based IDEs provide very easy interface or plugins for these features. Having said that, the custom board I had to bring up is Freescale MK64FX512 based microcontroller to run applications based on Freescale MQX 4.2 RTOS. MQX has a clearly defined structure as given in the following references:

Video Vault

Getting Started with MQX.

 

Briefly, to bring up the board with MQX RTOS:

  1. Identify the reference base board.
  2. If the base board configuration matches with your board hardware, go as is otherwise, customize the PSP(Processor Support Package) and BSP(Board Support Package) for your board and rebuild the PSP and BSP projects. The output is a library file for both these projects.
  3. Integrate the bsp.lib and psp.lib with the hello_world project given by MQX for all supported packages and configurations. While you can prepare a hello world project from scratch, picking up an existing project is easier and time saving.

I'll make a separate note on how to customize the BSP and PSP projects for custom hardware. This note is intended to describe the steps involved in the board bring up and customizing the development environment.

 

Here are the steps to bringup a Freescale K64 micro based custom board on MQX RTOS(Ver 4.2) on KDS IDE. My base board is TWR-K64. Yours can be a FRDM-K64 too which is very popular these days. I have taken the hello_world project in the example projects list for the bring up activity.

Add the following paths in the compiler-> include settings in settings page: "${workspace_loc:/${ProjName}/Includes/debug}"

"${workspace_loc:/${ProjName}/Includes/debug/bsp}"

"${workspace_loc:/${ProjName}/Includes/debug/psp}"

"${workspace_loc:/${ProjName}/Includes/debug/bsp/Generated_Code}"

"${eclipse_home}../toolchain/lib/gcc/arm-none-eabi/4.8.4/include"

"${eclipse_home}../toolchain/lib/gcc/arm-none-eabi/4.8.4/include-fixed"

"${eclipse_home}../toolchain/arm-none-eabi/include"

 

If you have any custom code or drivers or application codes, add their paths as follows: "${workspace_loc:/${ProjName}/}"

 

You can ensure that the firmware project is portable in this way as it avoids absolute links. Try to keep all all links relative to the work space or project location. Add the custom BSP and PSP libraries to Linker->Miscellaneous-> Other Objects as follows:

"${workspace_loc:/${ProjName}//bsp.a}"

"${workspace_loc:/${ProjName}//psp.a}"

 

Custom linker script can be added in Linker->General->Script Files as follows: "${workspace_loc:/${ProjName}//custom_linker_script.ld}"

 

Now build your application, clear the compiler issues and you are ready to go! Besides this, you can also add the Doxygen, Emsys register View plugins for documentation and debugging. Visit  this reference for more details on this.

 

Happy coding!

CSMG Sarma

MQX SPI Slave Configuration

Posted by CSMG Sarma Jun 11, 2018

I was surprised to learn MQX(Ver 4.2) does not support SPI Slave configuration.

Reference

There are many workarounds in the community but the information is in bits and pieces. So i decided to write this note.

I wanted to configure SPI0 Channel on MK64FX512VLQ12 board as Slave driven by interrupt in which can call MQX APIs. I have MQX Ver 4.2 installed.  My base BSP is derived from TWR-K64 board BSP given by MQX. IDE is KDS.f Reference hardware is TWR-K64 board. 

 

The options available are:

  1. Create your own Driver. Integrate it with BSP.
  2. Integrate Processor Expert generated code to your MQX application.
  3. Integrate(Enable) Processor Expert in your MQX Application.
  4. Integrate Legacy MQX SPI(Ver 3.6 or old) driver Code to your MQX application.

 

Initially I was hesitant of modifying the BSP. But now it looks to be the best option compared with the other options.

The files needed to be modified for SPI to work as Slave are:

1. user_config.c: I added the following lines here:   

/**
      * Custom BSP macros.
      * */

      #define BSPCFG_ENABLE_SPI0_SLAVE 1 //SPI0 as slave.
      #define SPI0_SLAVE_POLL 0
      /**
      * @see NVIC implementation on MQX for the following https://community.nxp.com/docs/DOC-335593
      * */

     //#define MQX_ROM_VECTORS 1

SPI0_SLAVE_POLL is set to 1 if SPI0 channel polls for data from Master. And set to 0 if interrupt are expected on every event of SPI Master sending data.

MQX_ROM_VECTORS is an MQX macro, define if you wish to have your ISR in the ROM address unlike the BSP registered ISR, which run from RAM. Breifly, this macro setting decide if you want to use _int_install_kernel_isr() or _int_install_isr() to register your ISR. See the following link for more details.

Reference

 

2. init_bsp.c: I added the following lines here:   

#if (BSPCFG_ENABLE_SPI0_SLAVE == 1)
      spi0_slave_init();
      #endif


Based on user_config.h, spi0_slave_init() would be called. Its definition looks like this:

     void spi0_slave_init(void)
      {
           SIM_MemMapPtr sim = SIM_BASE_PTR;
           PORT_MemMapPtr pctl;
          VDSPI_REG_STRUCT_PTR spi0_base = (void *)SPI0_BASE_PTR;
          /* GPIO init */
           pctl = (PORT_MemMapPtr)PORTD_BASE_PTR;
           /** 2 chipselects
                * Select whichever is needed for a Master. */

           pctl->PCR[0] = PORT_PCR_MUX(2); /* DSPI0.PCS0 */
          pctl->PCR[1] = PORT_PCR_MUX(2); /* DSPI0.SCK */
           pctl->PCR[2] = PORT_PCR_MUX(2); /* DSPI0.SOUT */
           pctl->PCR[3] = PORT_PCR_MUX(2); /* DSPI0.SIN */
          /* Enable clock gate to DSPI0 module */
           sim->SCGC6 |= SIM_SCGC6_SPI0_MASK;
          /* MCR Configuration */
           spi0_base->MCR = 0x00; //Overwrite the Default value of MCR.
          spi0_base->MCR |= DSPI_MCR_ROOE_MASK ;
          spi0_base->CTAR[0] = DSPI_CTAR_FMSZ(15); //frame size = 16 bits.
           spi0_base->RSER &= ~(DSPI_SR_TFFF_MASK); //TFFF flag generates Interrupt requests.
          /* Install Interrupts to BSP */
           if (SPI0_SLAVE_POLL == 0)
           {
                     spi0_base->RSER |= DSPI_RSER_TCF_RE_MASK; //generate Transmission complete requests.
            }
     }

This definition is in a new file I added to BSP. 


3. vectors.c: Replaced DEFAULT_VECTOR with spi0_slave_isr(my custom ISR) in __vector_table at SPI0_ISR location. It originally looked like this: 

  DEFAULT_VECTOR, /* 0x2A 0x000000A8 - ivINT_SPI0 */


Now its like this:

     spi0_slave_isr, /* 0x2A 0x000000A8 - ivINT_SPI0

This is useful if you are running the ISR from ROM. If you are registering your ISR to MQX, with will be over written. If ISR is not registered to MQX, then MQX APIs(like signalling events or posting semaphore) couldn't be used. So make your choice wisely.

 

4. <>_ISR.c: This file contains is my custom ISR handler for SPI0 interrupts which occur when data is received on SPI0 channel. This is file is part of my MQX application project and not BSP. The ISR looks as follows:

     #if (MQX_ROM_VECTORS == 1) //if bypassing kernel(BSP's) ISRs
      void spi0_slave_isr(void)
      {
     #else
      void spi0_slave_isr(void * parameter)
      {
      (void)parameter; //to satisify compiler.
     #endif
          static volatile uint8_t * r_buf; //= spi_slave_buffer.r_buf;
           static volatile uint8_t * w_buf; // = spi_slave_buffer.w_buf;
           static volatile uint8_t len; // = spi_slave_buffer.len_buf;

           LED_TOGGLE(&led_blue);

           if((spi0_base->SR & DSPI_SR_RFDF_MASK) == DSPI_SR_RFDF_MASK)
           {//Read RX FIFO
                test_flag |= SPI_RX_ISR;
                spi0_base->SR |= DSPI_SR_RFDF_MASK; /* clear the interrupt flag*/
           }
           else if((spi0_base->SR & DSPI_SR_TCF_MASK) == DSPI_SR_TCF_MASK)
           {//Transfer Complete Flag, Write to TX FIFO now
                test_flag |= SPI_TX_ISR;
                spi0_base->SR |= DSPI_SR_TCF_MASK; /* clear the interrupt flag*/
           }
          if (_lwevent_set(&lwevent_spi_slave, 0x01) == MQX_OK)
           {
                LED_TOGGLE(&led_green);
           }
      }

 

There is a task which is blocked waiting for the event it looks something like this:   

if ((test_flag & SPI_RX_ISR) == SPI_RX_ISR)
      {
           test_flag &= ~SPI_RX_ISR;
           spi_slave_buffer.r_buf[0] = (uint8_t)(DSPI_POPR_RXDATA_GET(spi0_base->POPR) & 0x00FF);
           spi_slave_buffer.r_buf[1] = (uint8_t)((DSPI_POPR_RXDATA_GET(spi0_base->POPR) >> 8) & 0x00FF);
           printf("\treceived 0x%x 0x%x\n", spi_slave_buffer.r_buf[0], spi_slave_buffer.r_buf[1]);

           res = spi_slave_buffer.r_buf[1];
           res |= (((uint32_t )spi_slave_buffer.r_buf[0]) << 8);
           spi0_base->SR |= DSPI_SR_TCF_MASK; //clear the TCF Flag.
           spi0_base->PUSHR = res; //write the data
      }
      else if((test_flag & SPI_TX_ISR) == SPI_TX_ISR)
      {
           test_flag &= ~SPI_TX_ISR;
          res = 0;
          while (spi_slave_buffer.len_buf > res)
           {
                spi0_base->PUSHR = spi_slave_buffer.w_buf[res];
                res++;
           }
           printf("\ttransmission complete\n");
      }
      else
      {
           printf("\tSomething's wrong with SPI0_Slave!!\n");
      }

I have tried this ISR be registering it to MQX as well as running it from ROM directly. If you want to run the ISR from ROM table you need to remove MQX APIs and ensure proper synchronization between ISR and the tasks(this is removed here for brevity).

 

With this, you are ready to go! To conclude, modifying the BSP for an interrupt driven SPI Slave driver is easier than any other option. Also there is no other documented description available. I had tried the other 3 options I mentioned at the beginning of this note. But ended up in loosing lot of time and no logical conclusion. Its unfortunate that MQX has not closed this issue which has been existing and known to them for so long.

 

Hope this helps.

 

PS: I have removed some of the lines for brevity and to keep the note simple. Don't panic!

 

Happy hacking!

Sarma