Tutorial: Adafruit WS2812B NeoPixels with the Freescale FRDM-K64F Board – Part 4: Timer

This is Part 4 of a Mini Series. In Part 3, I described the software concepts (see “Tutorial: Adafruit WS2812B NeoPixels with the Freescale FRDM-K64F Board – Part 3: Concepts“). In this post I describe how to set-up the timer to trigger later DMA operations. The goal is to drive Adafruit’s NeoPixel (WS2812B) with the Freescale FRDM-K64F board:

NeoPixels with FRDM-K64F

NeoPixels with FRDM-K64F

Mini Series Tutorial List

  1. Tutorial: Adafruit WS2812B NeoPixels with the Freescale FRDM-K64F Board – Part 1: Hardware
  2. Tutorial: Adafruit WS2812B NeoPixels with the Freescale FRDM-K64F Board – Part 2: Software Tools
  3. Tutorial: Adafruit WS2812B NeoPixels with the Freescale FRDM-K64F Board – Part 3: Concepts
  4. Tutorial: Adafruit WS2812B NeoPixels with the Freescale FRDM-K64F Board – Part 4: Timer
  5. Tutorial: Adafruit WS2812B NeoPixels with the Freescale FRDM-K64F Board – Part 5: DMA

Outline

In this article, I explain how to use the FTM (Flex Timer Module) of the Kinetis to generate 3 waveforms explained in the earlier article (see “Tutorial: Adafruit WS2812B NeoPixels with the Freescale FRDM-K64F Board – Part 3: Concepts“). To be able to verify the waveforms, I’m configuring the timer to toggle external pins in PWM mode so I can inspect the signals. I’m using Kinetis Design Studio V3.0.0 with the Kinetis SDK V1.2.

Adding Kinetis SDK Files

Although DMA will be covered in a next article, I’m adding now all the necessary files to the project. I prefer to copy the files to my project: copy the files and folders from the SDK installation into the Eclipse project structure. I need the following main files/folders added to my project:

  1. EDMA: SDK\platform\src\edma
  2. FTM: SDK\platform\drivers\ftm
  3. GPIO: SDK\platform\drivers\gpio

💡 There is a link to the project sources/files on GitHub.

SDK Main Files

SDK Main Files

The reality is, because of the complex directory structure of the SDK and all the HAL (Hardware Abstraction Layer) files, tons of other files are needed too :-(.

The image below shows all the files necessary:

Kinetis SDK Files

Kinetis SDK Files

Because of that many directories, I need to add the folders to the compiler include path settings too :-(:

💡 I hope that in the future the SDK will be just one source folder and one folder for the header files.

So in the project settings, I need to have the include paths configured:

💡 There is a link to the project sources/files on GitHub.

Compiler Include Paths

Compiler Include Paths

💡 Another option would have been to use Processor Expert: Processor Expert would know which files are needed, and automatically add them to the project and update the project settings. But as Manya did not want to use Processor Expert, things are now much harder to do. It is all your fault, Manya! 😉

For reference (you might copy-paste the lines into the project settings), here are my include paths:

"../Sources"
"../Project_Settings/Startup_Code"
"../SDK/platform/CMSIS/include"
"../SDK/platform/hal/src/mcg"
"../SDK/platform/hal/src/ftm"
"../SDK/platform/drivers/inc"
"../SDK/platform/system/src/clock"
"../SDK/platform/drivers/src/gpio"
"../SDK/platform/devices"
"../SDK/platform/system/src/clock/MK64F12"
"../SDK/platform/drivers/src/edma"
"../SDK/platform/drivers/src/ftm"
"../SDK/platform/hal/src/gpio"
"../SDK/platform/hal/src/osc"
"../SDK/platform/hal/src/dmamux"
"../SDK/platform/system/inc"
"../SDK/platform/system/src/interrupt"
"../SDK/platform/osa/src"
"../SDK/platform/hal/src/edma"
"../SDK/platform/osa/inc"
"../SDK/platform/hal/src/sim/MK64F12"
"../SDK/platform/hal/inc"
"../SDK/platform/hal/src/port"

💡 I’m using project relative (“../SDK/” paths, because I want to have my project standalone from the Kinetis SDK installation.

Clock and Timer Configuration

The next things are two defines I need to add to the compiler preprocessor settings:

CLOCK_SETUP
"FSL_OSA_BM_TIMER_CONFIG=0"
Preprocessor Defines

Preprocessor Defines

The first one enables the 60 MHz peripheral/system clock during startup, the second tells that I don’t want and need a timer for the OS-Awareness layer.

Adding DMAPixel Files

Time to get started with coding :-). I want to store all my low-level timer and DMA stuff into a module ‘DMAPixel‘. Therefore I create (use context menu on folder, then New > Header File and New > Source File) the DMAPixel.c and DMAPixel.h files:

Added DMAPixel

Added DMAPixel

The interface for now is very simple, just an initialization routine:

#ifndef SOURCES_DMAPIXEL_H_
#define SOURCES_DMAPIXEL_H_

/*! \brief Initialize the timer and DMA */
void DMA_Init(void);

#endif /* SOURCES_DMAPIXEL_H_ */

The initialization routine for now just looks like this:

void DMA_Init(void) {
  InitHardware();
  ResetFTM(FTM0_IDX);
  StartStopFTM(FTM0_IDX, true); /* start FTM timer */
  for(;;) {
    /* for test only, let the FTM run */
  }
}

Hardware Initialization

I’m going to use PTC1, PTC2 and PTC3 as toggle/output pins of the timer. I have to clock the peripheral domain for port C with SIM_HAL_EnableClock(). With InitFlexTimer() the timer gets initialized (will cover that laters). Lastly, the three pins need to be muxed:

static void InitHardware(void) {
  /* Enable clock for PORTs */
  SIM_HAL_EnableClock(SIM, kSimClockGatePortC);

  /* Setup board clock source. */
  g_xtal0ClkFreq = 50000000U;           /* Value of the external crystal or oscillator clock frequency of the system oscillator (OSC) in Hz */
  g_xtalRtcClkFreq = 32768U;            /* Value of the external 32k crystal or oscillator clock frequency of the RTC in Hz */

  /* FTM and FTM Muxing */
  InitFlexTimer(FTM0_IDX);
  PORT_HAL_SetMuxMode(PORTC,1UL,kPortMuxAlt4); /* use PTC1 for channel 0 of FTM0 */
  PORT_HAL_SetMuxMode(PORTC,2UL,kPortMuxAlt4); /* use PTC2 for channel 1 of FTM0 */
  PORT_HAL_SetMuxMode(PORTC,3UL,kPortMuxAlt4); /* use PTC3 for channel 2 of FTM0 */
}

FTM Block Diagram

The picture below shows the FTM (Flex Timer Module) of the K64F:

FTM Block Diagram

FTM Block Diagram

There is a clock select (CLKS) which selects the clock source. I’m going to us the 60 MHz system clock as input clock, with a prescaler of 1.

To get a 1.25 µs period, I use a timer match value of 0x4B:

0.00000125/(1/60'000'000) = 75 = 0x4B

The corresponding values for the channels are:

  • Channel 0: 0x10
  • Channel 1: 0x2A
  • Channel 2: 0x40

I will use these values in the timer intitialization which comes next.

FTM Timer Initialization

The following function initializes the timer. It uses the Kinetis SDK API to initialize the peripheral and to configure it.

/* FTM related */
#define NOF_FTM_CHANNELS  3 /* using three FTM0 channels, running with 60 MHz system clock */
#define FTM_CH0_TICKS     (0x10)  /* delay until 0xFF */
#define FTM_CH1_TICKS     (0x2A)  /* at 0.4us write data */
#define FTM_CH2_TICKS     (0x40)  /* at 0.8us clear bits  */
#define FTM_PERIOD_TICKS  (0x4B)  /* 1.25 us period */

static void InitFlexTimer(uint32_t instance) {
  ftm_pwm_param_t flexTimer0_ChnConfig0 = { /* FTM channel configuration */
    .mode = kFtmEdgeAlignedPWM,
    .edgeMode = kFtmHighTrue,
    .uFrequencyHZ = 1000U, /* dummy value, will change it later on */
    .uDutyCyclePercent = 10U, /* dummy value, will change it later on */
    .uFirstEdgeDelayPercent = 0U,
  };
  ftm_user_config_t flexTimer0_InitConfig0 = {
    .tofFrequency      = 0U,
    .isWriteProtection = false, /* FTM is not write protected */
    .BDMMode           = kFtmBdmMode_00, /* default mode for debug: timer will be stopped, can modify registers */
    .syncMethod        = (uint32_t)(kFtmUseSoftwareTrig) /* using software synchronization */
  };
  FTM_Type *ftmBase = g_ftmBase[instance];

  /* initialize the driver */
  FTM_DRV_Init(instance, &flexTimer0_InitConfig0); /* initialize the driver with a default configuration */
  FTM_DRV_SetTimeOverflowIntCmd(instance, false); /* disable interrupt */
  FTM_DRV_SetFaultIntCmd(instance, false); /* disable interrupt */
  FTM_DRV_SetClock(instance, kClock_source_FTM_SystemClk, kFtmDividedBy1); /* use system clock with a divider of 1 */

  /* configure timer */
  FTM_HAL_ClearTimerOverflow(ftmBase); /* clear timer overflow */

  /* enable PWM mode for the channels */
  FTM_HAL_EnablePwmMode(ftmBase, (ftm_pwm_param_t*)&flexTimer0_ChnConfig0, 0);
  FTM_HAL_EnablePwmMode(ftmBase, (ftm_pwm_param_t*)&flexTimer0_ChnConfig0, 1);
  FTM_HAL_EnablePwmMode(ftmBase, (ftm_pwm_param_t*)&flexTimer0_ChnConfig0, 2);

  /* based on Ref manual, in PWM mode CNTIN is to be set 0*/
  FTM_HAL_SetCounterInitVal(ftmBase, 0);

  /* set the module counters */
  FTM_HAL_SetMod(ftmBase, FTM_PERIOD_TICKS);
  FTM_HAL_SetChnCountVal(ftmBase, 0, FTM_CH0_TICKS);
  FTM_HAL_SetChnCountVal(ftmBase, 1, FTM_CH1_TICKS);
  FTM_HAL_SetChnCountVal(ftmBase, 2, FTM_CH2_TICKS);
}

💡 You might notice that I’m using many low level HAL (Hardware Abstraction Layer) API calls of the Kinetis SDK, and *not* much of the SDK API. The reason is that the existing API in V1.2 does not allow to configure three channels without starting them, among other details I need to have them working with DMA.

Inspecting the FTM Registers

A good way to look at the FTM register values is of course to read the reference manual :-). But another good way is to use the register viewer (see “Updated Eclipse EmbSysReg Viewer with extra Freescale SVD Files“) in Eclipse:

FTM Register Settings

FTM Register Settings

💡 Be careful with what you are displaying with the register view, as reading some bits and peripherals can have side effects. I was once reading too many registers, stopped the FTM from working. Close the view and re-open it solves the problem, if you do not remember any more which registers you are reading.

Running the FTM Timer

The code above only initializes the timer. Starting is done with

StartStopFTM(FTM0_IDX, true); /* start FTM timer */

With an implementation as below:

static void StartStopFTM(uint32_t instance, bool startIt) {
  FTM_Type *ftmBase = g_ftmBase[instance];

  if (startIt) {
    FTM_HAL_SetClockSource(ftmBase, kClock_source_FTM_SystemClk); /* clock timer */
  } else {
    FTM_HAL_SetClockSource(ftmBase, kClock_source_FTM_None); /* disable clock */
  }
}

So all what it does is either clock the FTM or disables the clocking for it.

Calling from main()

To test my timers, I have to call DMA_Init() it from main():

#include "fsl_device_registers.h"
#include "DMAPixel.h"

int main(void) {
  int i = 0;

  DMA_Init();
  for (;;) {
      i++;
  }
  /* Never leave main */
  return 0;
}

Testing the Timer Signals

So with a logic analyzer I can verify that the timer channels are running correctly:

Probing Signals with Logic Analyzer

Probing Signals with Logic Analyzer

And indeed, they are looking good 🙂

Waveforms and Timing

Waveforms and Timing

Summary

In this article I’m using the Kinetis SDK with the Kinetis Design Studio to generate 3 waveforms, using 3 channels of the Kinetis FTM (Flex Timer Module). The Kinetis SDK provides a lot of drivers, but the same time it requires a learning curve. Unfortunately the SDK did not offer the functionalities I would have expected, e.g. for the FTM to be able to properly initialize multiple channels and run it. That is easily possible with Processor Expert which Manya did not want to use, so I had to end up using HAL macros and methods instead. The good thing with this HAL approach is that I easily can bypass the SDK and get closer to the bare metal. I still can use the SDK functions as inspiration, but using the HAL instead. At least for this project that worked for me. Still, using the SDK with Processor Expert is much easier as it deals with the project setup and adding all the files to the project.

The project sources can be found on GitHub:
https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/KDS/FRDM-K64F120M/FRDM-K64F_NeoPixel_SDK

In the next post I’ll explain how to trigger DMA requests from it. So stay tuned …

Happy Timing 🙂

Links

11 thoughts on “Tutorial: Adafruit WS2812B NeoPixels with the Freescale FRDM-K64F Board – Part 4: Timer

  1. Pingback: Tutorial: Adafruit WS2812B NeoPixels with the Freescale FRDM-K64F Board – Part 3: Concepts | MCU on Eclipse

  2. Pingback: Tutorial: Adafruit WS2812B NeoPixels with the Freescale FRDM-K64F Board – Part 2: Software Tools | MCU on Eclipse

  3. Pingback: Tutorial: Adafruit WS2812B NeoPixels with the Freescale FRDM-K64F Board – Part 1: Hardware | MCU on Eclipse

  4. Pingback: Tutorial: Adafruit WS2812B NeoPixels with the Freescale FRDM-K64F Board – Part 5: DMA | MCU on Eclipse

  5. Hi Erich,
    I’m trying to do the same using the MCUXpresso IDE (11.3.0), of course it is a bit different but I can pretty much replicate it until the point: “Adding Kinetis SDK Files”.
    Here I’m colliding with the difference between the 2 SDKs (different data structure, filenames, …). Moreover I suspect that if you had made this project today, you would have used the “MCUXpresso config tool”, isn’t it?
    So, how should I proceed? I tried to open the config tool, but this device has too much settings for me (I never use it)…
    Could you eventually explain it, or if you already done, point me to a page where this is shown?

    Kind regards,
    AGA

    Like

    • Hi AGA,
      Things are totally different with the MCUXpresso SDK as there is no Processor Expert and the SDK 1.x is really different from what is in the SDK 2.x available today.
      What I did is I have used that code base from this article and moved it over to the SDK 2.x world. But there is no Config Tool support afik for DMA (yet), but not that big of a problem:
      things are pretty much the same from the C coding point of view between Processor Expert and the SDK, I only added the necessary macros.
      How this looks and works: you can see this in this project on GitHub: https://github.com/ErichStyger/MetaClockClock
      There I have it working for the K22FN512 and K02: from the DMA perspective they are very similar (well, I think even the same) as the DMA on the K64F.
      I hope this helps,
      Erich

      Like

  6. Hi Erich,

    many thanks! I was just asking about the Config Tool because I though that current examples and projects (from NXP) are mainly based on this.
    For me it is not absolutely a problem add these files to the project.
    I’ll try to work on this.

    Kind regards,
    AGA

    Liked by 1 person

What do you think?

This site uses Akismet to reduce spam. Learn how your comment data is processed.