I'm Working with STM33F103, and I'm trying to implement a callback mechanism that would call methods on a given object to handle interrupts, so far I use the following solution:
class Nvic
{
public:
using function_pointer =void (*)(void);
template<typename T>
static void get_interrupt(int interrupt_number, T* object,void (T::*methode)())
{
class_vector[interrupt_number]=reinterpret_cast<Default_class*>(object);
method_vector[interrupt_number]=reinterpret_cast<default_method_type>(methode);
}
static void execute_interrupt(int interrupt_number)
{
(class_vector[interrupt_number]->*method_vector[interrupt_number])();
}
private:
struct Default_class{void default_method();};
using default_method_type =void(Default_class::*)(void);
static inline Default_class* class_vector[NUMBER_OF_INTERRUPTS];
static inline default_method_type method_vector[NUMBER_OF_INTERRUPTS];
}
extern "C"
{
void Some_ISR(void) __attribute__ ((interrupt));
void Some_ISR(void){Nvic::execute_interrupt(2);}
}
And if some object want to bind interrupt to itself:
class Some_class
{
public:
Some_class(int peripherial_iterrupt)
{
Nvic::get_interrupt(peripherial_iterrupt,this,&Some_class::my_interrupt);
}
private:
my_interrupt();
};
But I want to implement this based on std::function, unfortunately it turns out that std::function can dynamically allocate memory so it is not reentrant.
So is there any way to make sure that std::function is interrupt safe?
Maybe using an allocator will solve the problem, if I provide std::function, my custom allocator which allocates memory in some static memory buffer there will be no interrupt heap corruption problem.
Or maybe if I provide newlib-nano __malloc_lock and __mallock_free functions then heap memory allocation will be interrupt safe.
It also may allow me to use other STL containers and algorithms in interrupt safe environment, like this:
class Some_interface :private Some_peripherial
{
public:
Some_interface(int peripherial_number) :Some_peripherial(peripherial_number)
{
int interrupt_number =peripherial_number;
Nvic::get_interrupt(interrupt_number,this,&Some_interface::interrupt_routine);
}
void send_data(void* buff, size_t size)
{
for(size_t i=0;i<size;i++)
{
data_to_send.push_back((uint8_t)buff[i]);
}
}
void set_all_data_send_callback(std::function<void(void)> f){all_data_send_callback=f;}
private:
std::vector<uint8_t> data_to_send;
std::function<void(void)> all_data_send_callback;
void interrupt_routine()
{
if(data_to_send.empty()){if(all_data_send_callback)all_data_send_callback();};
else
{
Some_peripherial::send(data_to_send.back());
data_to_send.pop_back();
}
}
};
class Timer_task :private Timer_peripherial
{
public:
Timer_task(int peripherial_number) :Timer_peripherial(peripherial_number)
{
int interrupt_number =peripherial_number;
Nvic::get_interrupt(interrupt_number,this,&Timer_task ::on_interrupt);
Timer_peripherial::set_interrupt_on_evry_10ms();
}
template<typename T>
static void set_task(T* object,void (T::*methode)())
{
task =std::bind(std::mem_fn(methode),object);
}
private:
std::function<void(void)> task;
void on_interrupt()
{
if(task) task();
}
};
Some_interface interface_0{0};
Some_interface interface_1{1};
const char global_message[] ="global_message";
Timer_task timer_task{interface_1.send_data((void*)global_message,strlen(global_message))};
int main()
{
const char message_0[] ="123456";
const char message_1[] ="1256";
while(1)
{
interface_0.send_data((void*)message,strlen(message));
interface_1.send_data((void*)message,strlen(message));
delay();
}
}
Where data_to_send are filled form main and also form other method called from another interrupt.
We can also set all_data_send_callback if we want to.