element14 Community
element14 Community
    Register Log In
  • Site
  • Search
  • Log In Register
  • Community Hub
    Community Hub
    • What's New on element14
    • Feedback and Support
    • Benefits of Membership
    • Personal Blogs
    • Members Area
    • Achievement Levels
  • Learn
    Learn
    • Ask an Expert
    • eBooks
    • element14 presents
    • Learning Center
    • Tech Spotlight
    • STEM Academy
    • Webinars, Training and Events
    • Learning Groups
  • Technologies
    Technologies
    • 3D Printing
    • FPGA
    • Industrial Automation
    • Internet of Things
    • Power & Energy
    • Sensors
    • Technology Groups
  • Challenges & Projects
    Challenges & Projects
    • Design Challenges
    • element14 presents Projects
    • Project14
    • Arduino Projects
    • Raspberry Pi Projects
    • Project Groups
  • Products
    Products
    • Arduino
    • Avnet Boards Community
    • Dev Tools
    • Manufacturers
    • Multicomp Pro
    • Product Groups
    • Raspberry Pi
    • RoadTests & Reviews
  • Store
    Store
    • Visit Your Store
    • Choose another store...
      • Europe
      •  Austria (German)
      •  Belgium (Dutch, French)
      •  Bulgaria (Bulgarian)
      •  Czech Republic (Czech)
      •  Denmark (Danish)
      •  Estonia (Estonian)
      •  Finland (Finnish)
      •  France (French)
      •  Germany (German)
      •  Hungary (Hungarian)
      •  Ireland
      •  Israel
      •  Italy (Italian)
      •  Latvia (Latvian)
      •  
      •  Lithuania (Lithuanian)
      •  Netherlands (Dutch)
      •  Norway (Norwegian)
      •  Poland (Polish)
      •  Portugal (Portuguese)
      •  Romania (Romanian)
      •  Russia (Russian)
      •  Slovakia (Slovak)
      •  Slovenia (Slovenian)
      •  Spain (Spanish)
      •  Sweden (Swedish)
      •  Switzerland(German, French)
      •  Turkey (Turkish)
      •  United Kingdom
      • Asia Pacific
      •  Australia
      •  China
      •  Hong Kong
      •  India
      •  Korea (Korean)
      •  Malaysia
      •  New Zealand
      •  Philippines
      •  Singapore
      •  Taiwan
      •  Thailand (Thai)
      • Americas
      •  Brazil (Portuguese)
      •  Canada
      •  Mexico (Spanish)
      •  United States
      Can't find the country/region you're looking for? Visit our export site or find a local distributor.
  • Translate
  • Profile
  • Settings
Embedded and Microcontrollers
  • Technologies
  • More
Embedded and Microcontrollers
Blog Port an MBED design to non-MBED - Phase 4: Timeout Class and Blinky example
  • Blog
  • Forum
  • Documents
  • Quiz
  • Polls
  • Files
  • Members
  • Mentions
  • Sub-Groups
  • Tags
  • More
  • Cancel
  • New
Join Embedded and Microcontrollers to participate - click to join for free!
  • Share
  • More
  • Cancel
Group Actions
  • Group RSS
  • More
  • Cancel
Engagement
  • Author Author: Jan Cumps
  • Date Created: 17 Jan 2020 4:28 PM Date Created
  • Views 1873 views
  • Likes 8 likes
  • Comments 7 comments
  • lora
  • mbed
  • hercules
Related
Recommended

Port an MBED design to non-MBED - Phase 4: Timeout Class and Blinky example

Jan Cumps
Jan Cumps
17 Jan 2020

The port of the MBED Timeout works.

In this blog you can see the internals of this class and how to set up configuration and handling.

 

image

 

 

My port is for a Hercules TMS570LC43. I use the HAL utility and api for that controller where possible.

 

This was the most difficult part. The MBED Timeout inherits from Ticker. And that one inherits from TimerEvent.

There's good reasons for this. A Timeout is a Ticker that fires once. A Ticker is TimerEvent that can call a callbac method. A TimerEvent is a specialised class that only deals with setting and handling hardware timers and their interupts.

Like the majority of the MBED code, well designed, well abstracted.

 

I haven't done the same. I implemented everything in a Timeout class. My goal is to get SemTech's LoRa MBED example working on Hercules and I focus on that.

 

I spent some effort getting a flexible set of timers running with the Hercules high end timer module.

I'm reusing that development here.

The main take-away for this post is that I've created a function huiSetTimeout(index, us).

It activates one of the available timers, addressed by index that will throw an interrupt every given period (us, in microseconds). When you call that same function with 0 microseconds, it stops that timer.

 

The Timeout Class

 

The class has two API methods; attach_us() and detach();

 

#include "Callback.h"
#include "timeout_api.h"

class Timeout  {
public:
  Timeout();
  void detach();
  void attach_us(mbed::Callback<void()> func, us_timestamp_t t);
protected:
  static void _timeouthandler(uint32_t id);
  mbed::Callback<void()> _function;  /**< Callback. */
};

 

attach_us() sets up the timeout with the given period (t), registers the callback method and starts the timer.

The callback mechanism is the one from MBED. Very nice. It allows to set a particular method of a particular instance of a particular object.

You'll see in the implementation that this method gets executed when the period t expired.

 

detach()is a mechanism to stop an active timeout. If you set a timeout of 1 minute and you call detach()after 30 seconds, the callback method will not get called.

Because our Timeout is a one-off mechanism, detach()is also called in the Timeout class itself when the timeout period expired. That's in line with MBED's design.

 

The protected method _timeouthandler() is for the implementation part. That one gets called when the host controller (Hercules)  interrupt is triggered.

It then calls the callback _function that was passed and stored in attach_us().

 

Here's the implementation:

 

#include <Timeout.h>
#include "timeout_api.h"

Timeout::Timeout(): _function() {
}

void Timeout::detach() {
  _function = nullptr;
   timeout_stop((uint32_t)this);
};

void Timeout::attach_us(mbed::Callback<void()> func, us_timestamp_t t) {
  _function = func;
  timeout_init((&Timeout::_timeouthandler), (uint32_t)this, t);
};

void Timeout::_timeouthandler(uint32_t id) {
  Timeout *handler = (Timeout*)id;
  mbed::Callback<void()> local = handler->_function;
  handler->detach();
  local.call();
}

 

The timeout_xxx() functions are discussed a little later. They serve as bridge between OO and procedural world, and as hardware abstractors.

 

attach_us() stores the callback in a protected member and then calls a function to activate a (we don't knwo which one, see later) Hercules free timer.

detach()is removes the callback and disables the allocated Hercules timer.

Note to self. I should create a destructor that calls detach too.

edit: done:

 

Timeout::~Timeout() {
    detach();  // the functions called in this method can safely be called, even if attach_us() was never called
}

 

_timeouthandler() is static. It's a helper function, not related to an instance of the Timeout class,  that is called from the lower non-object-oriented world.

The mechanism works so that the underlying level passes it the address of the object that's linked to the interrupt as the parameter.

That object is first deactivated, by calling detach(). Then the callback associated with that object is executed.

 

The timeout_xxx() Helpers

 

These bridge functions help our class to work with the timers that are allocated on the Hercules timer.

 

#ifdef __cplusplus
extern "C" {
#endif

#define MAXTIMEOUTS 8

typedef uint64_t us_timestamp_t;
typedef void (*timeout_handler)(uint32_t id);

uint32_t timeout_init(timeout_handler handler, uint32_t id, us_timestamp_t t);
uint32_t timeout_stop(uint32_t id);
void timeoutfired(uint32_t id);

#ifdef __cplusplus
}
#endif

 

The timestamp is defined as 64 bit. But then I use 32 bits downstream. I've used 64 bits to be compliant with MBED. Discuss.

 

typedef void (*timeout_handler)(uint32_t id);

 

This is how the c code sees the c++ static void _timeouthandler(uint32_t id).

timeout_init() and timeout_stop() are the ones called by the Timeout class. They use the Hercules HET interupt API I created to activate and deactivate timers.

timeoutfired() gets called by the Hercules interrupt handler and will call the static method _timeouthandler().of the Timeout class.

 

Implementation:

 

#include "timeout_api.h"
#include "het_interrupt_utils.h"

typedef struct {
  timeout_handler handler;
  uint32_t id;
  boolean used;
} timeoutarray;

static timeoutarray __timeout_ids [MAXTIMEOUTS] = {0};

uint32_t timeout_init(timeout_handler handler, uint32_t id, us_timestamp_t t)
{
  // find first free location
  uint32_t i;
  for (i = 0; i < MAXTIMEOUTS; i++) {
    if (! __timeout_ids[i].used) {
      break; // found
    }
  }

  if (i < MAXTIMEOUTS) {
    __timeout_ids[i].handler = handler;
    __timeout_ids[i].id = id;
    __timeout_ids[i].used = true;
    huiSetTimeout(i, (uint32_t) t);
  }
  return i;
}

uint32_t timeout_stop(uint32_t id) {
  // find the right timer:
  uint32_t i;
  for (i = 0; i < MAXTIMEOUTS; i++) {
    if (__timeout_ids[i].id == id) {
      break;
    }
  }

  if (i < MAXTIMEOUTS) {
    huiSetTimeout(i, 0);
    __timeout_ids[i].handler = 0;
    __timeout_ids[i].id = 0;
    __timeout_ids[i].used = false;
  }
  return i;
}

void timeoutfired(uint32_t id) {
  __timeout_ids[id].handler(__timeout_ids[id].id);
  return;
}

 

This layer has an array to register what timers are in use and free.

Its attributes get set each time a timeout_init() is called.

That array holds a pointer to the static method discussed above. I could as well not store this, because its the same for every timer. Discuss.

It also holds the object that asked for the timer. It's later passed back as the parameter for the handler, and also the key we use to find the relevant entry when detach() is called.

Weak point: when you call timeout_init() before the timeout period expired, I get duplicates and detach() would call the first one only.

edit: fixed:

 

uint32_t timeout_init(timeout_handler handler, uint32_t id, us_timestamp_t t)
{
  // do we already use this object:
  uint32_t i;
  for (i = 0; i < MAXTIMEOUTS; i++) {
    if (__timeout_ids[i].id == id) {
      break;
    }
  }

  if (i == MAXTIMEOUTS) {
    // no, find first free location
    for (i = 0; i < MAXTIMEOUTS; i++) {
      if (! __timeout_ids[i].used) {
        break; // found
      }
    }
  }

  if (i < MAXTIMEOUTS) { // ...

 

There's not too much code in these functions. I hope they are understandable as they are now.

 

Test Bed

 

I've created a class called Engine that will set a timeout and that has a handler method for it. It's instantiated and run at program startup.

 

  gioInit();
  hetInit();
  huiInit();
  _enable_interrupt_();


  Engine *engine = new Engine();
  engine->run();

 

Here's the declaration:

 

class Engine {
public:
  Engine();
  virtual ~Engine();
  void run();
protected:
  virtual void OnTimeoutLedA( void );
};

 

and the implementation:

 

Timeout ledA_TimeoutTimer;

Engine::Engine() {
  // TODO Auto-generated constructor stub
}

Engine::~Engine() {
  // TODO Auto-generated destructor stub
}

void Engine::run() {
  ledA_TimeoutTimer.attach_us( mbed::callback( this, &Engine::OnTimeoutLedA ), 1000000 );
}

void Engine::OnTimeoutLedA(void)
{
  gioToggleBit(gioPORTB, 6);

  // reprime. This is for testing purpose, so a flashing led is fun
  ledA_TimeoutTimer.attach_us( mbed::callback( this, &Engine::OnTimeoutLedA ), 1000000 );
}

 

The timer is created as a static variable, common in MBED and Arduino. Should it be a class member? Discuss.

 

A stack capture can help to understand the wind-down from low level interrupt handler to the handler method in our Engine object:

 

image

 

The result of this exercise is a Blinky that's driven by the Timeout class. The project and all code is attached.

 

Related blog
Use C++ with HALCoGen C Projects

Port an MBED design to non-MBED - Phase 1: compile without errors

Port an MBED design to non-MBED - Phase 2a: DigitalOut Class Constructor
Port an MBED design to non-MBED - Phase 2b: DigitalOut Class and Blinky example
Port an MBED design to non-MBED - Phase 3: InterruptIn Class and example
Port an MBED design to non-MBED - Phase 4: Timeout Class and Blinky example
Port an MBED design to non-MBED - Phase 5: OO Callbacks Simplified
Attachments:
TMS570LC43_het_interrupt_cpp_20200117.zip
  • Sign in to reply

Top Comments

  • Jan Cumps
    Jan Cumps over 5 years ago +1
    From 48 tasks and 99+ (the limit of my error viewer) build errors to a successful build. First build with no todos, no errors, no warnings. Tomorrow I'll wire the SemTech LoRa radio to the Hercules and…
  • Jan Cumps
    Jan Cumps over 5 years ago in reply to Jan Cumps +1
    Jan Cumps wrote: ... Tomorrow I'll wire the SemTech LoRa radio to the Hercules and see what happens in the real world. Done. It doesn't work . That 'll be the next step. I tested and the GPIO and SPI basics…
  • Jan Cumps
    Jan Cumps over 5 years ago in reply to Jan Cumps +1
    This is the conversation on a working MBED platform (ST Nucleo): Here's the data: SPI Analysis results Generated: 18 januari 2020 ConfigurationSPI modeMode 0 (CPOL = 0, CPHA = 0)MOSIMISOIndexTimeHexBinDecASCIIHexBinDecASCII027…
  • Jan Cumps
    Jan Cumps over 5 years ago in reply to Jan Cumps

    That's more like it.

     

    MBED:

     

    image

     

     

    Hercules port:

    image

     

    The order isn't the same yet, but the answers to a SPI query are identical, and the ~CS signal behaves the same.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 5 years ago in reply to Jan Cumps

    ... does anyone know how to debug MBED OS2 applications?  Their windows-hosted MBED Studio doesn't support the OS vs 2 designs (nor my ST Nucleo board or the BBC:micro).

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 5 years ago in reply to Jan Cumps

    One of the reasons why I have a different sequence of communication, is that there's a bug in the SemTech MBED example:

     

        Reset( );
        RxChainCalibration( );
        IoInit( );

     

    RXChainCalibration() uses SPI calls, but they initiate the SPI mechanism only in the next function call, IoInit():

     

    void SX1276MB1xAS::IoInit( void )
    {
        AntSwInit( );
        SpiInit( );
    }

     

    That's one of the reasons I never recorderd that SPI sequence that I have in the start of my program when running the MBED design on the ST Nucleo. The Nucleo SPI interface never generated the signals.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 5 years ago in reply to Jan Cumps

    Better news: there is now communication with the SemTech radio:

    image

     

    The communication isn't correct. I have extra in my read/write functions.

    Now that I know it reacts, it 'll be easier to fix it.

    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
  • Jan Cumps
    Jan Cumps over 5 years ago in reply to Jan Cumps

    This is the conversation on a working MBED platform (ST Nucleo):

    image

     

    Here's the data:

     

     

    SPI Analysis results
    Generated: 18 januari 2020
    ConfigurationSPI modeMode 0 (CPOL = 0, CPHA = 0)MOSIMISOIndexTimeHexBinDecASCIIHexBinDecASCII027,80 μsCS_LOWCS_LOW131,20 μs0x060b0000011060x000b000000000346,00 μs0x000b0000000000x6c0b01101100108l557,40 μsCS_HIGHCS_HIGH658,80 μsCS_LOWCS_LOW762,40 μs0x070b0000011170x000b000000000977,00 μs0x000b0000000000x800b100000001281188,40 μsCS_HIGHCS_HIGH1289,80 μsCS_LOWCS_LOW1393,40 μs0x080b0000100080x000b00000000015108,00 μs0x000b0000000000x000b00000000017119,40 μsCS_HIGHCS_HIGH18128,60 μsCS_LOWCS_LOW
    • Cancel
    • Vote Up +1 Vote Down
    • Sign in to reply
    • More
    • Cancel
>
element14 Community

element14 is the first online community specifically for engineers. Connect with your peers and get expert answers to your questions.

  • Members
  • Learn
  • Technologies
  • Challenges & Projects
  • Products
  • Store
  • About Us
  • Feedback & Support
  • FAQs
  • Terms of Use
  • Privacy Policy
  • Legal and Copyright Notices
  • Sitemap
  • Cookies

An Avnet Company © 2025 Premier Farnell Limited. All Rights Reserved.

Premier Farnell Ltd, registered in England and Wales (no 00876412), registered office: Farnell House, Forge Lane, Leeds LS12 2NE.

ICP 备案号 10220084.

Follow element14

  • X
  • Facebook
  • linkedin
  • YouTube