There is a Time and Date for both Worlds

The Kinetis ARM Cortex-M4(F) is a wonderful machine: a 32bit architecture, plenty of FLASH and RAM, an ideal play field. I love the Kinetis Tower boards, and even more the Freedom board which has an ARM Cortex-M0+ on it. I have a lot of projects on S08, S12 and ColdFire at the university, and they are all using a lot of Processor Expert components. Processor Expert is such a great productivity tool: having software in components allows easy software re-use. With Processor Expert abstracting from the hardware, I can easily port my applications to new boards and processors. Well, until Processor Expert changed for Kinetis :-(.

Time to deal with this. There are two blocking points: the concept of LDD, and availability of components.

Logical Device Driver

Processor Expert for Kinetis introduces the new concept of LDD or ‘Logical Device Drivers’. LDD components have a LDD suffix (sic!), and are using an orange color icon in the Components Library View:

Logical Device Drivers

Logical Device Drivers

LDD components are very different from the ‘classical’ Processor Expert components I used and have created: they are more oriented to work with an RTOS. For example let’s have a look at the GPIO_LDD component. That component deals with a general purpose I/O port:

GPIO_LDD Component

GPIO_LDD Component

This component corresponds to the BitsIO (non-LDD) component:

BitsIO Component

BitsIO Component

Example LDD: Init() and Deinit()

The two components offer pretty much the same thing, but the API is completely different. The LDD component has Init() and Denit() functions. Below is the code of the GPIO_LDD Init():

LDD_TDeviceData* GPIO2_Init(LDD_TUserData *UserDataPtr)
{
  /* Allocate LDD device structure */
  GPIO2_TDeviceData *DeviceDataPrv;

  /* {FreeRTOS RTOS Adapter} Driver memory allocation: RTOS function call is defined by FreeRTOS RTOS Adapter property */
  DeviceDataPrv = (GPIO2_TDeviceData *)pvPortMalloc(sizeof(GPIO2_TDeviceData));
  #if FreeRTOS_CHECK_MEMORY_ALLOCATION_ERRORS
  if (DeviceDataPrv == NULL) {
    return (NULL);
  }
  #endif
  /* Save RTOS Device structure */
  DeviceDataPrv->EventFlags = 0x00U;   /* Clears stored events */
  DeviceDataPrv->UserData = UserDataPtr; /* Store the RTOS device structure */
  /* Interrupt vector(s) allocation */
  /* {FreeRTOS RTOS Adapter} Set interrupt vector: IVT is static, ISR parameter is passed by the global variable */
  INT_PORTE__BAREBOARD_RTOS_ISRPARAM = DeviceDataPrv;
  /* GPIOE_PDDR: PDD&=~0x10000000 */
  GPIOE_PDDR &= (uint32_t)~0x10000000UL;
  /* Initialization of Port Control registers */
  /* PORTE_PCR28: ISF=0,LK=0,MUX=1 */
  PORTE_PCR28 = (uint32_t)((PORTE_PCR28 & (uint32_t)~0x01008600UL) | (uint32_t)0x0100UL);
  /* PORTE_PCR28: ISF=1,IRQC=0x0B */
  PORTE_PCR28 = (uint32_t)((PORTE_PCR28 & (uint32_t)~0x00040000UL) | (uint32_t)0x010B0000UL);
  /* NVICIP91: PRI91=0x80 */
  NVICIP91 = (uint8_t)0x80U;
  /* NVICISER2: SETENA|=0x08000000 */
  NVICISER2 |= (uint32_t)0x08000000UL;
  /* Registration of the device structure */
  PE_LDD_RegisterDeviceStructure(PE_LDD_COMPONENT_GPIO2_ID,DeviceDataPrv);
  return ((LDD_TDeviceData *)DeviceDataPrv);
}

void GPIO2_Deinit(LDD_TDeviceData *DeviceDataPtr)
{
  (void)DeviceDataPtr;                 /* Parameter is not used, suppress unused argument warning */
  /* PORTE_PCR28: IRQC=0 */
  PORTE_PCR28 &= (uint32_t)~0x000F0000UL;
  /* GPIOE_PDDR: PDD&=~0x10000000 */
  GPIOE_PDDR &= (uint32_t)~0x10000000UL;
  /* Interrupt vector(s) deallocation */
  /* {FreeRTOS RTOS Adapter} Restore interrupt vector: IVT is static, no code is generated */
  /* Unregistration of the device structure */
  PE_LDD_UnregisterDeviceStructure(PE_LDD_COMPONENT_GPIO2_ID);
  /* Deallocation of the device structure */
  /* {FreeRTOS RTOS Adapter} Driver memory deallocation: RTOS function call is defined by FreeRTOS RTOS Adapter property */
  vPortFree((GPIO2_TDeviceDataPtr)DeviceDataPtr);
}

I highlighted line 7 and 48 which is using the RTOS dynamic memory allocation through the RTOS adapter.

Compared to the ‘old fashioned way’ of Processor Expert components, using  the LDD is not that simple and straight forward. As shown with the two above functions, I need to think about a Device Data structure and pass this along through the API. So things get more flexible, but as well much more complicated. So setting a big of an I/O port is not a simple Macro any more:

void GPIO1_SetFieldValue(LDD_TDeviceData *DeviceDataPtr, LDD_GPIO_TBitField Field, GPIO1_TFieldValue Value)
{
  (void)DeviceDataPtr;                 /* Parameter is not used, suppress unused argument warning */
  switch (Field) {                     /* no break */
    case LED_E4: {                     /* bit field #0 */
      GPIO_PDD_SetPortDataOutput(GPIO1_MODULE_BASE_ADDRESS,
        (
        GPIO_PDD_GetPortDataOutput(GPIO1_MODULE_BASE_ADDRESS)
        & ((GPIO1_TPortValue)(~((GPIO1_TPortValue)GPIO1_LED_E4_MASK)))
        )
        | (
        ((GPIO1_TPortValue)(Value << GPIO1_LED_E4_START_BIT))
        & ((GPIO1_TPortValue)GPIO1_LED_E4_MASK)
         )
       );
       break;
     }
     case LED_E1: {                     /* bit field #1 */
...
     }
    default:
      break;                           /* Invalid Field is not treated, result is undefined */
  } /* switch (Field) */
}

Instead, I need to pass several parameters. Yes, very flexible. But the footprint and run-time impact are not small. The ‘new’ way is much more suited for larger and dynamic systems, especially if you are using things like an MQX operating system. But as I am coming from the embedded world where ‘small and fast’ is key, this new LDD way was a shock. On the other side: the Kinetis is such a powerful architecture, maybe this does not matter? Time will tell.

Porting Applications

There is something else which really matters: all my code and and all my Processor Expert components did *not* work any more with this new concept :-(. Boomer. Away was the promise of ‘develop with Processor Expert, and you can easily migrate to other cores’. True, but not if I want to migrate to Kinetis :-(. I would say this was one of the biggest obstacles for me to move to the more powerful ARM cores. The ARM core already is very different, and this time even Processor Expert was not a big help with the transformation, unlike it was before to migrate between cores.

LED Example: Dual Interface

So it looks like I need to bite the bullet. Since then, I try to workaround the problem, and adding dual interfaces to my components. An example for this is the LED component which offers a ‘traditional’ (non-LDD) interface, but as well an interface to the LDD components:

LED with GPIO_LDD

LED with GPIO_LDD

Here one or multiple LED components is linked to a GPIO_LDD component. This is set up in the LED component with the enabled LDD settings:

LED LDD Properties

LED LDD Properties

I need to specify a field name (e.g. “LED_E1”, more about this below), which component to link to (e.g. “GPIO1”) and if the Init() functions of that LDD shall be called from the LED component initialization during PE_low_level_init() or not.

In the GPIO_LDD I set up my pins for that port, and the linkage between the LED component and the GPIO_LDD component with the Field Name (“LED_E1” in this example):

GPIO_LDD with Field Name Linkage

GPIO_LDD with Field Name Linkage

So how does this dual way works? Below is the example of the LED On() in the driver code:

void %'ModuleName'%.%On(void)
{
%if AnodeOnPortSide = 'yes'
  %if defined(GPIO_LDD) %- using LDD
  %@GPIO_LDD@'ModuleName'%.SetPortBits(NULL, %@GPIO_LDD@'ModuleName'%.%'GPIO_LDD_BitfieldName'%.MASK);
%else
  %@LEDPin@'ModuleName'%.SetVal();
%endif
%else
%if defined(GPIO_LDD) %- using LDD
  %@GPIO_LDD@'ModuleName'%.ClearPortBits(NULL, %@GPIO_LDD@'ModuleName'%.%'GPIO_LDD_BitfieldName'%.MASK);
%else
  %@LEDPin@'ModuleName'%.ClrVal();
%endif
%endif
}

For non-LDD components, it is simply using ClrVal() and SetVal() which actually are macros. For the LDD version it is using ClearPortBits() and SetPortBits() with NULL parameters as no RTOS or device parameter is requested.

The generated code looks this in the LDD world:

void LED1_On(void)
{
  GPIO1_ClearPortBits(NULL, GPIO1_LED_E1_MASK);
}

That dual interface is not ideal, bloats up the properties and the driver code, but at least that way I can use the components both for the non-LDD and LDD world.

What is available?

The other thing I run into is that a component is available, but not for Kinetis. I was porting one of my applications, and this application is using the TimeDate component which manages date and time information. “No problem”, I thought, as I had it listed in the Components Library:

TimeDate in the Components Library

TimeDate in the Components Library

But I get an error that this component is not supported in this version (of CodeWarrior). Oh! I should have noticed that the component is not in orange color. What I missed is paying attention to the filter:

TimeDate in the Components Library, Filtering Disabled

TimeDate in the Components Library, Filtering Disabled!

To bad! So this component is really not available for Kinetis. But hey, all what that component needs is a periodic timer source, so it is not too difficult to create my version of it.

GenericTimeDate for Kinetis

And indeed it is not that hard. Actually it took me a couple of hours, and here it is: a GenericTimeDate component which works both for the LDD and non-LDD world. I generated code with the current (non-LDD) TimeDate component, and then transformed what I needed into the GenericTimeDate component. It offers methods to set and get time and date:

GenericTimeDate Methods

GenericTimeDate Methods

The AddTick() method is called in a periodic way, e.g. from a timer. The original TimeDate component actually has a timer built in, but with my GenericTimeDate I can use whatever I want. That way I can use my RTOS tick callback as well for this:

void FRTOS1_vApplicationTickHook(void)
{
  /* Called for every RTOS tick. */
  TmDt1_AddTick();
}

The frequency of this tick timer needs to be specified in the component properties (10 ms in the example below):

GenericTimeDate Properties

GenericTimeDate Properties

The Initialization settings are used to set up date and time with some starting values.

Summary

Transforming existing non-LDD Processor Expert applications to Kinetis is not that easy, but I hope with the increased number of components with dual interfaces available things will get better. And if a component is not provided by Freescale for Kinetis, then it might not be as hard as it looks to create my own component to close that gap. In any case, I have now a Date and Time component I can use for both worlds. So it is the Time and Date to move more to Kinetis :-).

Happy Moving 🙂

11 thoughts on “There is a Time and Date for both Worlds

  1. Pingback: FatFs with Kinetis | MCU on Eclipse

  2. Pingback: A Shell for the Freedom KL25Z Board | MCU on Eclipse

  3. Pingback: Tutorial: Timer (LED) with Processor Expert for Kinetis | MCU on Eclipse

  4. Pingback: Processor Expert Maxim I2C RTC Driver the Arduino Data Logger Shield | MCU on Eclipse

  5. Excellent goods from you, man. I have understand your stuff previous to
    and you are just extremely wonderful. I really like what you
    have acquired here, really like what you’re stating and the way in which you say it. You make it enjoyable and you still take care of to keep it wise. I can’t wait to read
    far more from you. This is actually a wonderful site.

    Like

  6. I’m extremely pleased to uncover this website. I need to to thank you for your time for this wonderful read!! I definitely really liked every part of it and i also have you bookmarked to check out new stuff on your blog.

    Like

  7. Hi Erich,

    I found your blog very helpful. I am currently learning embedded programming from KL25Z board.
    In above example, Can you explain how to get date using TmDt1_GetDate() function. I am assigning byte but its not working.

    Like

  8. Pingback: McuOnEclipse Components: 27-Dec-2014 Release | MCU on Eclipse

What do you think?

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