eBox的中断结构参考了mbed,和我们平时所用的中断结构有些差异,不容易理解,最近仔细看了底层代码,终于搞清楚了,总结一下
一 首先要要搞清楚的几个概念:类的静态成员,实例成员
1 静态成员(static)对类以及类的所有实例有意义。即在该类的范围内所有类共享该成员。即使未创建实例,静态成员也存在,可以访问
2 类的实例成员仅对每个实例有意义,在创建实例之前,未分配内存,无法访问
成员分为属性和方法,对于属性:
1 静态属性,对应一块内存. 静态属性在使用前必须初始化
2 实例属性,每个实例属性个对应一块内存
对于方法:不管是静态方法还是实例方法,都是同一块内存,区别在于
1 静态方法,可以通过类名或者实例对象名调用。 例如:class::call(),object.call() . 静态方法不能直接调用实例方法(实例方法此时还未分配内存空间),可以通过对象名间接访问
2 实例方法,只可以通过实例对象名调用 object.call() ;实例方法可以调用静态方法
二 接下来在来看ebox中的中断相关代码
先来看绑定中断的过程
1 在类声明里如下两个,绑定中断的过程就是通过模版变量pirq的attach方法保存回调函数指针
// 绑定静态回调函数
void attach(void(*fptr)(void)){
pirq.attach(fptr);
}
FunctionPointer pirq;
2 声明了两个变量,一个函数指针,一个数组
typedefvoid(*exti_irq_handler)(uint32_t id);
static exti_irq_handler irq_handler;
staticuint32_t exti_irq_ids[16];
其中, 数组用来保存实例对象地址(id),函数指针指向类的静态方法(handel)
int exti_irq_init(uint8_t index,exti_irq_handler handler,uint32_t id)
{
exti_irq_ids[index]= id;
irq_handler = handler;
return0;
}
3 在类中实现一个静态方法,id为实例对象指针。前面说过静态方法可以通过对象名间接调用实例方法
void IRQ::irq_handler(uint32_t id)
{
IRQ *handler =(IRQ*)id;// 指向回调函数地址
handler->pirq.call();// 调用回调函数
}
4 在初始化的时候,将静态方法地址,和实例地址分别送入
exti_irq_init(13,(&IRQ::irq_handler),(uint32_t)this);
绑定完毕之后,我们就可以在中断中调用了
void EXTI4_15_IRQHandler(void)
{
if(LL_EXTI_IsActiveFlag_0_31(LL_EXTI_LINE_13)!= RESET)
{
LL_EXTI_ClearFlag_0_31(LL_EXTI_LINE_13);
/* Manage code in main.c.*/
irq_handler(exti_irq_ids[13]);
}
}
假设我们创建了类IRQ的实例 A, 那么中断的调用过程就是
中断服务-->IRQ::irq_handler(&A)-->A->pirq.call() 。也就是最终访问的是实例对象A中的变量pirq. 然后通过调用perq的call()方法执行回调函数。
以上是对非类成员方法作为回调函数的绑定和调用过程。 如果类成员方法做为回调函数,过程一致,只是我们在类里声明函数指针的时候,因为不知到具体回调的是哪个类的实例,无法定义该变量。所以用到了模版。模版的用途就是定义了一个通用的函数指针,我们在对该指针初始化的时候传入的是什么类型,就是什么类型
/** A class for storing and calling a pointer to a static or member function 用来保存静态或成员函数的指针类
*/
// R-返回值类型 A1-参数类型
template<typename R,typename A1>
classFunctionPointerArg1{
public:
/** Create a FunctionPointer, attaching a static function 创建函数指针
*
* @param function The static function to attach (default is none) 附加静态函数,默认为void
*/
FunctionPointerArg1(R (*function)(A1)=0){
attach(function);
}
/** Create a FunctionPointer, attaching a member function
* 附加成员函数,object 成员函数的对象的指针。 成员函数
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
* @param function The address of the member function to attach
*/
template<typename T>
FunctionPointerArg1(T *object, R (T::*member)(A1)){
attach(object, member);
}
/** Attach a static function
* 附件静态函数
* @param function The static function to attach (default is none)
*/
void attach(R (*function)(A1)){
_p.function = function;
_membercaller =0;
}
/** Attach a member function
* 附件成员函数
* @param object The object pointer to invoke the member function on (i.e. the this pointer)
* @param function The address of the member function to attach
*/
template<typename T>
void attach(T *object, R (T::*member)(A1)){
_p.object =static_cast<void*>(object);// 将对象转换成void* 类型
*reinterpret_cast<R (T::**)(A1)>(_member)= member;
_membercaller =&FunctionPointerArg1::membercaller<T>;//注册成员函数地址
}
/** Call the attached static or member function
*/
R call(A1 a){
if(_membercaller ==0&& _p.function){
return _p.function(a);
}elseif(_membercaller && _p.object){
return _membercaller(_p.object, _member, a);
}
return(R)0;
}
/** Get registered static function
*/
R(*get_function(A1))(){
return _membercaller ?(R(*)(A1))0:(R(*)(A1))_p.function;
}
#ifdef MBED_OPERATORS
R operator()(A1 a){
return call(a);
}
operatorbool(void)const{
return(_membercaller != NULL ? _p.object :(void*)_p.function)!= NULL;
}
#endif
private:
template<typename T>//对象类型
// 调用成员 对象,成员函数
static R membercaller(void*object,uintptr_t*member, A1 a){
T* o =static_cast<T*>(object);//类型转换
R (T::**m)(A1)=reinterpret_cast<R (T::**)(A1)>(member);
return(o->**m)(a);
}
union{
R (*function)(A1);// static function pointer 静态函数指针
void*object;// object this pointer 对象指针
} _p;// 用联合体保存指针,静态函数或者对象,只能保存其中一种
uintptr_t _member[4];// aligned raw member function pointer storage - converted back by registered _membercaller
// 函数指针
R (*_membercaller)(void*,uintptr_t*, A1);// registered membercaller function to convert back and call _m.member on _object
};
总结,中断回调的关键有两点
1 如何在类未实例化之前访问类成员--静态成员函数
2 如何在允许挂在中断回调函数的类里声明指针变量,以用来指向回调函数 -- 模版
所以只要理解了静态成员和模版就能够理解ebox中中断结构以及实现方法