diff --git a/examples/FunctionInterruptSimulation/FunctionInterruptSimulation.cpp b/examples/FunctionInterruptSimulation/FunctionInterruptSimulation.cpp index 5f0adb99..d7fffbdf 100644 --- a/examples/FunctionInterruptSimulation/FunctionInterruptSimulation.cpp +++ b/examples/FunctionInterruptSimulation/FunctionInterruptSimulation.cpp @@ -1,58 +1,61 @@ #include -#include -#include "function.h" +#include "etl/function.h" +#include "etl/callback_service.h" -//******************************** -// Fake UART Rx register. -//******************************** -char get_char() +enum VectorId { - static char c = 'A'; - return c++; -} + TIM1_CC_IRQ_HANDLER = 42, + TIM2_IRQ_HANDLER = 43, + TIM3_IRQ_HANDLER = 44, + USART1_IRQ_HANDLER = 52, + USART2_IRQ_HANDLER = 53, + VECTOR_ID_RANGE = USART2_IRQ_HANDLER - TIM1_CC_IRQ_HANDLER + 1, + VECTOR_ID_OFFSET = TIM1_CC_IRQ_HANDLER +}; -//******************************** -// Interrupt vectors & callbacks. -//******************************** -// Callback interfaces. -// Note that they do not require any knowledge about the callee apart from the parameter type. -etl::ifunction* timer1_callback; // A pointer to a callback taking no parameters. -etl::ifunction* timer2_callback; // A pointer to a callback taking no parameters. -etl::ifunction* timer3_callback; // A pointer to a callback taking no parameters. -etl::ifunction* uart1_rx_callback; // A pointer to a callback taking a char parameter. -etl::ifunction* uart2_rx_callback; // A pointer to a callback taking a char parameter. +typedef etl::callback_service InterruptVectors; + +// Ensure that the callback service is initialised before use. +InterruptVectors& GetInterruptVectorsInstance() +{ + static InterruptVectors interruptVectors; + + return interruptVectors; +} extern "C" { + InterruptVectors& interruptVectors = GetInterruptVectorsInstance(); + // Function called from the timer1 interrupt vector. - void Timer1Interrupt() + void TIM1_CC_IRQHandler() { - (*timer1_callback)(); + interruptVectors.callback(); } // Function called from the timer2 interrupt vector. - void Timer2Interrupt() + void TIM2_IRQHandler() { - (*timer2_callback)(); + interruptVectors.callback(); } // Function called from the timer3 interrupt vector. - void Timer3Interrupt() + void TIM3_IRQHandler() { - (*timer3_callback)(); + interruptVectors.callback(); } - // Function called from the UART1 rx interrupt vector. - void Uart1RxInterrupt() + // Function called from the usart1 interrupt vector. + void USART1_IRQHandler() { - (*uart1_rx_callback)(get_char()); + interruptVectors.callback(); } - // Function called from the UART2 rx interrupt vector. - void Uart2RxInterrupt() + // Function called from the usart2 interrupt vector. + void USART2_IRQHandler() { - (*uart2_rx_callback)(get_char()); + interruptVectors.callback(); } } @@ -63,34 +66,29 @@ class Timer { public: - // Constructor. - Timer() + Timer(int interruptId) + : callback(*this) { + GetInterruptVectorsInstance().register_callback(interruptId, callback); } // Handler for interrupts from the timer. - void MemberTimerInterruptHandler() + void InterruptHandler(const size_t id) { - std::cout << "Timer interrupt (member)\n"; + std::cout << "Timer interrupt (member) : ID " << id << "\n"; } - // Static handler for interrupts from the timer. - static void StaticTimerInterruptHandler() - { - std::cout << "Timer interrupt (static)\n"; - } + etl::function_mp callback; }; //******************************** // Free function timer driver. //******************************** -void FreeTimerInterruptHandler() +void FreeTimerInterruptHandler(const size_t id) { - std::cout << "Timer interrupt (free)\n"; + std::cout << "Timer interrupt (free) : ID " << id << "\n"; } -etl::function_fv free_callback; - //******************************** // UART driver. //******************************** @@ -99,29 +97,37 @@ class Uart public: // Constructor. - Uart(int port_id) - : port_id(port_id) + Uart(int port_id, int interruptId) + : port_id(port_id), + callback(*this) { + GetInterruptVectorsInstance().register_callback(interruptId, callback); } - // Handler for rx interrupts from the UART. - void RxInterruptHandler(char c) + // Handler for interrupts from the UART. + void InterruptHandler(const size_t id) { - std::cout << "UART" << port_id << " Rx char interrupt : Received '" << c << "'\n"; + std::cout << "UART" << port_id << " : ID " << id << "\n"; } + etl::function_mp callback; + int port_id; }; -// Declare the driver instances. -Timer timer; -Uart uart1(0); -Uart uart2(1); +void UnhandledInterrupt(const size_t id) +{ + std::cout << "Unhandled Interrupt : ID " << id << "\n"; +} -etl::function_imv timer_member_callback; -etl::function_fv<&Timer::StaticTimerInterruptHandler> timer_static_callback; -etl::function_imp uart1_callback; -etl::function_imp uart2_callback; +// Declare the driver instances. +Timer timer(TIM1_CC_IRQ_HANDLER); +Uart uart1(0, USART1_IRQ_HANDLER); +Uart uart2(1, USART2_IRQ_HANDLER); + +// Declare the callbacks for the free functions. +etl::function_fp timer_free_callback; +etl::function_fp unhandled_callback; //******************************** // Test it out. @@ -129,21 +135,17 @@ etl::function_imp uart2_callb int main() { // Setup the callbacks. - // This may be part of the cross platform interface. - timer1_callback = &timer_member_callback; - timer2_callback = &timer_static_callback; - timer3_callback = &free_callback; - uart1_rx_callback = &uart1_callback; - uart2_rx_callback = &uart2_callback; + InterruptVectors& interruptVectors = GetInterruptVectorsInstance(); + + interruptVectors.register_callback(timer_free_callback); + interruptVectors.register_unhandled_callback(unhandled_callback); // Simulate the interrupts. - Timer1Interrupt(); - Timer2Interrupt(); - Timer3Interrupt(); - Uart1RxInterrupt(); - Uart2RxInterrupt(); + TIM1_CC_IRQHandler(); + TIM2_IRQHandler(); + USART1_IRQHandler(); + USART2_IRQHandler(); + TIM3_IRQHandler(); // Unhandled! return 0; } - - diff --git a/examples/FunctionInterruptSimulation/vs2017/FunctionInterruptSimulation.vcxproj b/examples/FunctionInterruptSimulation/vs2017/FunctionInterruptSimulation.vcxproj index 3c2ce147..ca2c92e1 100644 --- a/examples/FunctionInterruptSimulation/vs2017/FunctionInterruptSimulation.vcxproj +++ b/examples/FunctionInterruptSimulation/vs2017/FunctionInterruptSimulation.vcxproj @@ -29,7 +29,7 @@ {5157DB15-C255-4E47-9FB1-AF388437F90F} Win32Proj FunctionInterruptSimulation - 10.0.15063.0 + 10.0.17763.0 @@ -104,12 +104,12 @@ - Use + NotUsing Level3 Disabled _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ../../../src + ../../../include;%(AdditionalIncludeDirectories) Console diff --git a/include/etl/callback_service.h b/include/etl/callback_service.h new file mode 100644 index 00000000..cd098d5d --- /dev/null +++ b/include/etl/callback_service.h @@ -0,0 +1,159 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2019 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_CALLBACK_SERVICE_INCLUDED +#define ETL_CALLBACK_SERVICE_INCLUDED + +#include "platform.h" +#include "nullptr.h" +#include "static_assert.h" +#include "function.h" +#include "array.h" + +namespace etl +{ + //*************************************************************************** + /// An indexed callback service. + /// \tparam RANGE The number of callbacks to handle. + /// \tparam OFFSET The lowest callback id value. + /// The callback ids must range between OFFSET and OFFSET + RANGE - 1. + //*************************************************************************** + template + class callback_service + { + public: + + //************************************************************************* + /// Reset the callback service. + /// Sets all callbacks to the internal default. + //************************************************************************* + callback_service() + : unhandled_callback(*this), + p_unhandled(nullptr) + { + lookup.fill(&unhandled_callback); + } + + //************************************************************************* + /// Registers a callback for the specified id. + /// Compile time assert if the id is out of range. + /// \tparam ID The id of the callback. + /// \param callback Reference to the callback. + //************************************************************************* + template + void register_callback(etl::ifunction& callback) + { + ETL_STATIC_ASSERT(ID < (OFFSET + RANGE), "Callback Id out of range"); + ETL_STATIC_ASSERT(ID >= OFFSET, "Callback Id out of range"); + + lookup[ID - OFFSET] = &callback; + } + + //************************************************************************* + /// Registers a callback for the specified id. + /// No action if the id is out of range. + /// \param id Id of the callback. + /// \param callback Reference to the callback. + //************************************************************************* + void register_callback(const size_t id, etl::ifunction& callback) + { + if ((id >= OFFSET) && (id < (OFFSET + RANGE))) + { + lookup[id - OFFSET] = &callback; + } + } + + //************************************************************************* + /// Registers an alternative callback for unhandled ids. + /// \param callback A reference to the user supplied 'unhandled' callback. + //************************************************************************* + void register_unhandled_callback(etl::ifunction& callback) + { + p_unhandled = &callback; + } + + //************************************************************************* + /// Executes the callback function for the index. + /// Compile time assert if the id is out of range. + /// \tparam ID The id of the callback. + //************************************************************************* + template + void callback() + { + ETL_STATIC_ASSERT(ID < (OFFSET + RANGE), "Callback Id out of range"); + ETL_STATIC_ASSERT(ID >= OFFSET, "Callback Id out of range"); + + (*lookup[ID - OFFSET])(ID); + } + + //************************************************************************* + /// Executes the callback function for the index. + /// \param id Id of the callback. + //************************************************************************* + void callback(const size_t id) + { + if ((id >= OFFSET) && (id < (OFFSET + RANGE))) + { + (*lookup[id - OFFSET])(id); + } + else + { + unhandled(id); + } + } + + private: + + //************************************************************************* + /// The default callback function. + /// Calls the user defined 'unhandled' callback if it exists. + //************************************************************************* + void unhandled(size_t id) + { + if (p_unhandled != nullptr) + { + (*p_unhandled)(id); + } + } + + /// The default callback for unhandled ids. + etl::function_mp, + size_t, + &callback_service::unhandled> unhandled_callback; + + /// Pointer to the user defined 'unhandled' callback. + etl::ifunction* p_unhandled; + + /// Lookup table of callbacks. + etl::array*, RANGE> lookup; + }; +} + +#endif diff --git a/include/etl/version.h b/include/etl/version.h index 868c269e..ef318fff 100644 --- a/include/etl/version.h +++ b/include/etl/version.h @@ -38,8 +38,8 @@ SOFTWARE. ///\ingroup utilities #define ETL_VERSION_MAJOR 14 -#define ETL_VERSION_MINOR 7 -#define ETL_VERSION_PATCH 2 +#define ETL_VERSION_MINOR 8 +#define ETL_VERSION_PATCH 0 #define ETL_VERSION ETL_STRINGIFY(ETL_VERSION_MAJOR) ETL_STRINGIFY(ETL_VERSION_MINOR) ETL_STRINGIFY(ETL_VERSION_PATCH) #define ETL_VERSION_W ETL_WIDE_STRING(ETL_CONCAT(ETL_CONCAT(ETL_VERSION_MAJOR, ETL_VERSION_MINOR), ETL_VERSION_PATCH)) diff --git a/support/Release notes.txt b/support/Release notes.txt index 1968df1f..a9ad0685 100644 --- a/support/Release notes.txt +++ b/support/Release notes.txt @@ -1,3 +1,8 @@ +=============================================================================== +14.8.0 +Added etl::callback_service +Useful for abstracting the interrupt system from C++ handlers. + =============================================================================== 14.7.2 Added #include to message_router.h for improved cross platform compatibility. diff --git a/test/codeblocks/ETL.cbp b/test/codeblocks/ETL.cbp index b07b8954..c573df20 100644 --- a/test/codeblocks/ETL.cbp +++ b/test/codeblocks/ETL.cbp @@ -161,6 +161,7 @@ + @@ -341,6 +342,7 @@ + diff --git a/test/test_callback_service.cpp b/test/test_callback_service.cpp new file mode 100644 index 00000000..67de9b16 --- /dev/null +++ b/test/test_callback_service.cpp @@ -0,0 +1,333 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2019 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "UnitTest++.h" + +#include "etl/function.h" +#include "etl/callback_service.h" + +namespace +{ + const size_t SIZE = 3U; + const size_t OFFSET = 5U; + + typedef etl::callback_service Service; + + //***************************************************************************** + bool global_called = false; + bool member1_called = false; + bool member2_called = false; + bool unhandled_called = false; + size_t called_id = UINT_MAX; + + //***************************************************************************** + // The global function taking no parameters. + //***************************************************************************** + void global(size_t id) + { + global_called = true; + called_id = id; + } + + //***************************************************************************** + // The external unhandled callback. + //***************************************************************************** + void unhandled(size_t id) + { + unhandled_called = true; + called_id = id; + } + + //***************************************************************************** + // The test class with member functions. + //***************************************************************************** + class Test + { + public: + + Test() + : callback(*this) + { + } + + void member1(size_t id) + { + member1_called = true; + called_id = id; + } + + void member2(size_t id) + { + member2_called = true; + called_id = id; + } + + // Callback for 'member1'. + etl::function_mp callback; + }; + + Test test; + + // Callback for 'member2'. + etl::function_imp member_callback; + + // Callback for 'global'. + etl::function_fp global_callback; + + // Callback for 'unhandled'. + etl::function_fp unhandled_callback; +} + +//***************************************************************************** +// Initialises the test results. +//***************************************************************************** +struct SetupFixture +{ + SetupFixture() + { + called_id = UINT_MAX; + global_called = false; + member1_called = false; + member2_called = false; + unhandled_called = false; + } +}; + +namespace +{ + enum + { + GLOBAL = OFFSET, + MEMBER1, + MEMBER2, + OUT_OF_RANGE + }; + + SUITE(test_callback_service) + { + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_global_compile_time) + { + Service service; + + service.register_callback(global_callback); + service.register_callback(test.callback); + service.register_callback(member_callback); + + service.callback(); + + CHECK_EQUAL(GLOBAL, called_id); + CHECK(global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_global_run_time) + { + Service service; + + service.register_callback(GLOBAL, global_callback); + service.register_callback(MEMBER1, test.callback); + service.register_callback(MEMBER2, member_callback); + + service.callback(GLOBAL); + + CHECK_EQUAL(GLOBAL, called_id); + CHECK(global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_member1_compile_time) + { + Service service; + + service.register_callback(global_callback); + service.register_callback(test.callback); + service.register_callback(member_callback); + + service.callback(); + + CHECK_EQUAL(MEMBER1, called_id); + CHECK(!global_called); + CHECK(member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_member1_run_time) + { + Service service; + + service.register_callback(GLOBAL, global_callback); + service.register_callback(MEMBER1, test.callback); + service.register_callback(MEMBER2, member_callback); + + service.callback(MEMBER1); + + CHECK_EQUAL(MEMBER1, called_id); + CHECK(!global_called); + CHECK(member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_member2_compile_time) + { + Service service; + + service.register_callback(global_callback); + service.register_callback(test.callback); + service.register_callback(member_callback); + + service.callback(); + + CHECK_EQUAL(MEMBER2, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_unhandled_out_of_range_run_time_default) + { + Service service; + + service.register_callback(global_callback); + service.register_callback(test.callback); + service.register_callback(member_callback); + + service.callback(OUT_OF_RANGE); + + CHECK_EQUAL(UINT_MAX, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_unhandled_out_of_range_run_time_user_supplied) + { + Service service; + + service.register_callback(global_callback); + service.register_callback(test.callback); + service.register_callback(member_callback); + + service.register_unhandled_callback(unhandled_callback); + + service.callback(OUT_OF_RANGE); + + CHECK_EQUAL(OUT_OF_RANGE, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_unhandled_not_registered_compile_time_default) + { + Service service; + + service.register_callback(global_callback); + service.register_callback(member_callback); + + service.callback(); + + CHECK_EQUAL(UINT_MAX, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_unhandled_not_registered_run_time_default) + { + Service service; + + service.register_callback(GLOBAL, global_callback); + service.register_callback(MEMBER2, member_callback); + + service.callback(MEMBER1); + + CHECK_EQUAL(UINT_MAX, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(!unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_not_registered_compile_time_user_supplied) + { + Service service; + + service.register_callback(global_callback); + service.register_callback(member_callback); + + service.register_unhandled_callback(unhandled_callback); + + service.callback(); + + CHECK_EQUAL(MEMBER1, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(unhandled_called); + } + + //************************************************************************* + TEST_FIXTURE(SetupFixture, test_callback_unhandled_run_time_user_supplied) + { + Service service; + + service.register_callback(GLOBAL, global_callback); + service.register_callback(MEMBER2, member_callback); + + service.register_unhandled_callback(unhandled_callback); + + service.callback(MEMBER1); + + CHECK_EQUAL(MEMBER1, called_id); + CHECK(!global_called); + CHECK(!member1_called); + CHECK(!member2_called); + CHECK(unhandled_called); + } + }; +} diff --git a/test/vs2017/etl.vcxproj b/test/vs2017/etl.vcxproj index 6679be4f..d1662b17 100644 --- a/test/vs2017/etl.vcxproj +++ b/test/vs2017/etl.vcxproj @@ -373,6 +373,7 @@ + @@ -565,6 +566,7 @@ + diff --git a/test/vs2017/etl.vcxproj.filters b/test/vs2017/etl.vcxproj.filters index 161a765b..888dafde 100644 --- a/test/vs2017/etl.vcxproj.filters +++ b/test/vs2017/etl.vcxproj.filters @@ -723,6 +723,9 @@ Source Files + + ETL\Frameworks + @@ -1145,6 +1148,9 @@ Source Files + + Source Files +