Skip to content

Commit 65d9467

Browse files
committed
Ticker and Schedule updated from ESP8266 Arduino
squash! Ticker and Schedule updated from ESP8266 Arduino
1 parent b5b6dc9 commit 65d9467

File tree

5 files changed

+299
-48
lines changed

5 files changed

+299
-48
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ set(CORE_SRCS
3232
cores/esp32/Schedule.cpp
3333
cores/esp32/Stream.cpp
3434
cores/esp32/StreamString.cpp
35+
cores/esp32/Ticker.cpp
3536
cores/esp32/wiring_pulse.c
3637
cores/esp32/wiring_shift.c
3738
cores/esp32/WMath.cpp

cores/esp32/Schedule.cpp

Lines changed: 85 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "Schedule.h"
22
#include "PolledTimeout.h"
3+
#include "Ticker.h"
34
#ifdef ESP8266
45
#include "interrupts.h"
56
#include "coredecls.h"
@@ -10,88 +11,124 @@
1011

1112
typedef std::function<bool(void)> mFuncT;
1213

14+
constexpr uint32_t TICKER_MIN_US = 5000;
15+
1316
struct scheduled_fn_t
1417
{
15-
mFuncT mFunc;
16-
esp8266::polledTimeout::periodicFastUs callNow;
17-
schedule_e policy;
18-
19-
scheduled_fn_t() : callNow(esp8266::polledTimeout::periodicFastUs::alwaysExpired) { }
18+
mFuncT mFunc = nullptr;
19+
esp8266::polledTimeout::periodicFastUs callNow;
20+
schedule_e policy = SCHEDULE_FUNCTION_FROM_LOOP;
21+
scheduled_fn_t() : callNow(esp8266::polledTimeout::periodicFastUs::alwaysExpired) { }
2022
};
2123

2224
namespace {
23-
static circular_queue_mp<scheduled_fn_t> schedule_queue(SCHEDULED_FN_MAX_COUNT);
25+
static circular_queue_mp<scheduled_fn_t> schedule_queue(SCHEDULED_FN_MAX_COUNT);
2426
#ifndef ESP8266
25-
std::mutex schedulerMutex;
27+
std::mutex schedulerMutex;
2628
#endif
29+
30+
void ticker_scheduled(Ticker* ticker, const std::function<bool(void)>& fn, uint32_t repeat_us, schedule_e policy)
31+
{
32+
auto repeat_ms = (repeat_us + 500) / 1000;
33+
ticker->once_ms(repeat_ms, [ticker, fn, repeat_us, policy]()
34+
{
35+
if (!schedule_function([ticker, fn, repeat_us, policy]()
36+
{
37+
if (fn()) ticker_scheduled(ticker, fn, repeat_us, policy);
38+
else delete ticker;
39+
return false;
40+
}, policy))
41+
{
42+
ticker_scheduled(ticker, fn, repeat_us, policy);
43+
}
44+
});
45+
}
2746
};
2847

2948
bool IRAM_ATTR schedule_function_us(std::function<bool(void)>&& fn, uint32_t repeat_us, schedule_e policy)
3049
{
31-
scheduled_fn_t item;
32-
item.policy = policy;
33-
item.mFunc = std::move(fn);
34-
if (repeat_us) item.callNow.reset(repeat_us);
35-
return schedule_queue.push(std::move(item));
50+
if (repeat_us >= TICKER_MIN_US)
51+
{
52+
auto ticker = new Ticker;
53+
if (!ticker) return false;
54+
if (!schedule_function([ticker, fn = std::move(fn), repeat_us, policy]()
55+
{
56+
ticker_scheduled(ticker, fn, repeat_us, policy);
57+
return false;
58+
}, SCHEDULE_FUNCTION_WITHOUT_YIELDELAYCALLS))
59+
{
60+
delete ticker;
61+
return false;
62+
}
63+
return true;
64+
}
65+
else
66+
{
67+
scheduled_fn_t item;
68+
item.mFunc = std::move(fn);
69+
if (repeat_us) item.callNow.reset(repeat_us);
70+
item.policy = policy;
71+
return schedule_queue.push(std::move(item));
72+
}
3673
}
3774

3875
bool IRAM_ATTR schedule_function_us(const std::function<bool(void)>& fn, uint32_t repeat_us, schedule_e policy)
3976
{
40-
return schedule_function_us(std::function<bool(void)>(fn), repeat_us, policy);
77+
return schedule_function_us(std::function<bool(void)>(fn), repeat_us, policy);
4178
}
4279

4380
bool IRAM_ATTR schedule_function(std::function<void(void)>&& fn, schedule_e policy)
4481
{
45-
return schedule_function_us([fn = std::move(fn)]() { fn(); return false; }, 0, policy);
82+
return schedule_function_us([fn = std::move(fn)]() { fn(); return false; }, 0, policy);
4683
}
4784

4885
bool IRAM_ATTR schedule_function(const std::function<void(void)>& fn, schedule_e policy)
4986
{
50-
return schedule_function(std::function<void(void)>(fn), policy);
87+
return schedule_function(std::function<void(void)>(fn), policy);
5188
}
5289

5390
void run_scheduled_functions(schedule_e policy)
5491
{
55-
// Note to the reader:
56-
// There is no exposed API to remove a scheduled function:
57-
// Scheduled functions are removed only from this function, and
58-
// its purpose is that it is never called from an interrupt
59-
// (always on cont stack).
92+
// Note to the reader:
93+
// There is no exposed API to remove a scheduled function:
94+
// Scheduled functions are removed only from this function, and
95+
// its purpose is that it is never called from an interrupt
96+
// (always on cont stack).
6097

61-
static bool fence = false;
62-
{
98+
static bool fence = false;
99+
{
63100
#ifdef ESP8266
64-
InterruptLock lockAllInterruptsInThisScope;
101+
InterruptLock lockAllInterruptsInThisScope;
65102
#else
66-
std::lock_guard<std::mutex> lock(schedulerMutex);
103+
std::lock_guard<std::mutex> lock(schedulerMutex);
67104
#endif
68-
if (fence) {
69-
// prevent recursive calls from yield()
70-
return;
71-
}
72-
fence = true;
73-
}
105+
if (fence) {
106+
// prevent recursive calls from yield()
107+
return;
108+
}
109+
fence = true;
110+
}
74111

75-
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
112+
esp8266::polledTimeout::periodicFastMs yieldNow(100); // yield every 100ms
76113

77-
// run scheduled function:
78-
// - when its schedule policy allows it anytime
79-
// - or if we are called at loop() time
80-
// and
81-
// - its time policy allows it
82-
schedule_queue.for_each_requeue([policy, &yieldNow](scheduled_fn_t& func)
83-
{
84-
if (yieldNow) {
114+
// run scheduled function:
115+
// - when its schedule policy allows it anytime
116+
// - or if we are called at loop() time
117+
// and
118+
// - its time policy allows it
119+
schedule_queue.for_each_requeue([policy, &yieldNow](scheduled_fn_t& func)
120+
{
121+
if (yieldNow) {
85122
#ifdef ESP8266
86-
cont_yield(g_pcont);
123+
cont_yield(g_pcont);
87124
#else
88-
yield();
125+
yield();
89126
#endif
90-
}
91-
return
92-
(func.policy != SCHEDULE_FUNCTION_WITHOUT_YIELDELAYCALLS && policy != SCHEDULE_FUNCTION_FROM_LOOP)
93-
|| !func.callNow
94-
|| func.mFunc();
95-
});
96-
fence = false;
127+
}
128+
return
129+
(func.policy != SCHEDULE_FUNCTION_WITHOUT_YIELDELAYCALLS && policy != SCHEDULE_FUNCTION_FROM_LOOP)
130+
|| !func.callNow
131+
|| func.mFunc();
132+
});
133+
fence = false;
97134
}

cores/esp32/Ticker.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Ticker.cpp - esp32 library that calls functions periodically
3+
4+
Copyright (c) 2017 Bert Melis. All rights reserved.
5+
6+
Based on the original work of:
7+
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
8+
The original version is part of the esp8266 core for Arduino environment.
9+
10+
This library is free software; you can redistribute it and/or
11+
modify it under the terms of the GNU Lesser General Public
12+
License as published by the Free Software Foundation; either
13+
version 2.1 of the License, or (at your option) any later version.
14+
15+
This library is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18+
Lesser General Public License for more details.
19+
20+
You should have received a copy of the GNU Lesser General Public
21+
License along with this library; if not, write to the Free Software
22+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23+
*/
24+
25+
#include "Ticker.h"
26+
27+
Ticker::Ticker()
28+
: _timer(nullptr)
29+
{
30+
}
31+
32+
Ticker::~Ticker()
33+
{
34+
detach();
35+
}
36+
37+
void Ticker::_attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t callback, void* arg)
38+
{
39+
esp_timer_create_args_t _timerConfig;
40+
_timerConfig.arg = reinterpret_cast<void*>(arg);
41+
_timerConfig.callback = callback;
42+
_timerConfig.dispatch_method = ESP_TIMER_TASK;
43+
_timerConfig.name = "Ticker";
44+
if (_timer) {
45+
esp_timer_stop(_timer);
46+
esp_timer_delete(_timer);
47+
}
48+
esp_timer_create(&_timerConfig, &_timer);
49+
if (repeat) {
50+
esp_timer_start_periodic(_timer, milliseconds * 1000);
51+
}
52+
else {
53+
esp_timer_start_once(_timer, milliseconds * 1000);
54+
}
55+
}
56+
57+
void Ticker::detach() {
58+
if (_timer) {
59+
esp_timer_stop(_timer);
60+
esp_timer_delete(_timer);
61+
_timer = nullptr;
62+
}
63+
}
64+
65+
bool Ticker::active() const
66+
{
67+
return _timer;
68+
}
69+
70+
void Ticker::_static_callback(void* arg)
71+
{
72+
Ticker* _this = reinterpret_cast<Ticker*>(arg);
73+
if (!_this) return;
74+
if (_this->_callback_function) _this->_callback_function();
75+
}

cores/esp32/Ticker.h

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
Ticker.h - esp32 library that calls functions periodically
3+
4+
Copyright (c) 2017 Bert Melis. All rights reserved.
5+
6+
Based on the original work of:
7+
Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
8+
The original version is part of the esp8266 core for Arduino environment.
9+
10+
This library is free software; you can redistribute it and/or
11+
modify it under the terms of the GNU Lesser General Public
12+
License as published by the Free Software Foundation; either
13+
version 2.1 of the License, or (at your option) any later version.
14+
15+
This library is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18+
Lesser General Public License for more details.
19+
20+
You should have received a copy of the GNU Lesser General Public
21+
License along with this library; if not, write to the Free Software
22+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23+
*/
24+
25+
#ifndef TICKER_H
26+
#define TICKER_H
27+
28+
extern "C" {
29+
#include "esp_timer.h"
30+
}
31+
#include <functional>
32+
#include <Schedule.h>
33+
34+
class Ticker
35+
{
36+
public:
37+
Ticker();
38+
~Ticker();
39+
40+
typedef void (*callback_with_arg_t)(void*);
41+
typedef std::function<void(void)> callback_function_t;
42+
43+
void attach_scheduled(float seconds, callback_function_t callback)
44+
{
45+
attach(seconds, [callback]() { schedule_function(callback); });
46+
}
47+
48+
void attach(float seconds, callback_function_t callback)
49+
{
50+
_callback_function = std::move(callback);
51+
_attach_ms(seconds * 1000, true, _static_callback, this);
52+
}
53+
54+
void attach_ms_scheduled(uint32_t milliseconds, callback_function_t callback)
55+
{
56+
attach_ms(milliseconds, [callback]() { schedule_function(callback); });
57+
}
58+
59+
void attach_ms(uint32_t milliseconds, callback_function_t callback)
60+
{
61+
_callback_function = std::move(callback);
62+
_attach_ms(milliseconds, true, _static_callback, this);
63+
}
64+
65+
template<typename TArg>
66+
void attach(float seconds, void (*callback)(TArg), TArg arg)
67+
{
68+
static_assert(sizeof(TArg) <= sizeof(void*), "attach() callback argument size must be <= sizeof(void*)");
69+
// C-cast serves two purposes:
70+
// static_cast for smaller integer types,
71+
// reinterpret_cast + const_cast for pointer types
72+
_attach_ms(seconds * 1000, true, callback, arg);
73+
}
74+
75+
template<typename TArg>
76+
void attach_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg)
77+
{
78+
static_assert(sizeof(TArg) <= sizeof(void*), "attach() callback argument size must be <= sizeof(void*)");
79+
_attach_ms(milliseconds, true, callback, arg);
80+
}
81+
82+
void once_scheduled(float seconds, callback_function_t callback)
83+
{
84+
once(seconds, [callback]() { schedule_function(callback); });
85+
}
86+
87+
void once(float seconds, callback_function_t callback)
88+
{
89+
_callback_function = std::move(callback);
90+
_attach_ms(seconds * 1000, false, _static_callback, this);
91+
}
92+
93+
void once_ms_scheduled(uint32_t milliseconds, callback_function_t callback)
94+
{
95+
once_ms(milliseconds, [callback]() { schedule_function(callback); });
96+
}
97+
98+
void once_ms(uint32_t milliseconds, callback_function_t callback)
99+
{
100+
_callback_function = std::move(callback);
101+
_attach_ms(milliseconds, false, _static_callback, this);
102+
}
103+
104+
template<typename TArg>
105+
void once(float seconds, void (*callback)(TArg), TArg arg)
106+
{
107+
static_assert(sizeof(TArg) <= sizeof(void*), "attach() callback argument size must be <= sizeof(void*)");
108+
_attach_ms(seconds * 1000, false, callback, arg);
109+
}
110+
111+
template<typename TArg>
112+
void once_ms(uint32_t milliseconds, void (*callback)(TArg), TArg arg)
113+
{
114+
static_assert(sizeof(TArg) <= sizeof(void*), "attach() callback argument size must be <= sizeof(void*)");
115+
_attach_ms(milliseconds, false, callback, arg);
116+
}
117+
118+
void detach();
119+
bool active() const;
120+
121+
protected:
122+
void _attach_ms(uint32_t milliseconds, bool repeat, callback_with_arg_t callback, void* arg);
123+
static void _static_callback(void* arg);
124+
125+
callback_function_t _callback_function = nullptr;
126+
127+
protected:
128+
esp_timer_handle_t _timer;
129+
};
130+
131+
132+
#endif//TICKER_H

0 commit comments

Comments
 (0)