从sx1276的参考设计https://os.mbed.com/media/uploads/GregCr/sx1276mb1las_e311v02a_sch.pdf中我们可以看出:
sx1276与mcu需要连接的io口,SPI(SCK、MISO、MOSI、NSS)、DIO0~DIO5;SPI口用于通讯,DIO~DIO5用于产生外部中断。其实,DIO0~DIO5可以根据软件设计取舍,SDK里面只用到了DIO0,查看sx1276的数据手册,可以看到DIO0~DIO5分别对应不同的功能:
从上面的图,我们可以看出,DIOx Mapping被设置为不同的值,DIOx产生的中断(电平跳变)对应不同的状态。
基本的思路:将DIOx连接到MCU--->对应的mcu口设置为外部中断模式--->设置DIOx Mapping的模式--->事件发生(RxDone接受完成、TxDone发送完成)--->产生外部中断--->回调事件函数。
以DIO0为例说明:
DIO0连接到了PA10,我们知道PA10对应的外部中断函数是
void EXTI15_10_IRQHandler( void )
注册过程,依次调用函数如下:
SX1276IoInit( )
GpioInit( &SX1276.DIO0, RADIO_DIO_0, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 0 );
SX1276IoIrqInit( DioIrq );
//DioIrq是函数指针数组, SX1276OnDio0Irq、 SX1276OnDio1Irq等是成员,分别是DIO0、DIO1等的外部中断回调函数
GpioSetInterrupt( &SX1276.DIO0, IRQ_RISING_EDGE, IRQ_HIGH_PRIORITY, irqHandlers[0] );
//设置DIO0为上升沿触发,高优先级,中断回调函数为irqHandlers[0];
GpioMcuSetInterrupt( obj, irqMode, irqPriority, irqHandler );
GpioIrq[( obj->pin ) & 0x0F] = obj; //注册回调函数
中断发生后,依次调用顺序如下:
void EXTI15_10_IRQHandler( void )//外部中断的中断函数
void HAL_GPIO_EXTI_Callback( uint16_t gpioPin ) //HAL库回调函数
GpioIrq[callbackIndex]->IrqHandler( GpioIrq[callbackIndex]->Context );//最终在这里调用上面注册的函数
SDK中用RTC作为定时器,调度MAC的运行。
基本的思路:初始化RTC定时器,定义一个时间事件定时器、设置定时时长、启动时间事件定时器、设置RTC闹钟时间、RTC闹钟时间到、调用对应的事件回调函数。
看一下结构体时间事件定时器的结构体,其中struct TimerEvent_s *Next;指向下一个时间事件。
/*!
* \brief Timer object description
*/
typedef struct TimerEvent_s
{
uint32_t Timestamp; //! Current timer value
uint32_t ReloadValue; //! Timer delay value
bool IsStarted; //! Is the timer currently running
bool IsNext2Expire; //! Is the next timer to expire
void ( *Callback )( void* context ); //! Timer IRQ callback function
void *Context; //! User defined data object pointer to pass back
struct TimerEvent_s *Next; //! Pointer to the next Timer object.
}TimerEvent_t;
时间事件定时器链表:SDK中以静态的方式创建了多个时间事件定时器,每个时间事件定时器作为链表的一个节点。
多个时间事件定时器处理:遍历定时器链表,设置定时时长最小的事件为当前RTC的闹钟时间,RTC闹钟时间到,处理当前事件,遍历定时器链表、再设置定时时长最小的事件为当前RTC的闹钟时间。
一个典型的时间事件定时器使用方法:
static TimerEvent_t LedRedTimer; //定义时间事件定时器节点
void LED_TipInit(void )
{
TimerInit( &LedRedTimer, LedRedTimerEvent ); //初始化时间事件
TimerSetValue( &LedRedTimer, 4000);//设置定时时长
}
static void LedRedTimerEvent(void)
{
TimerStop( &LedRedTimer );
LED_RED_OFF();
}
void LedRedTipOn(void) //启动定时器
{
TimerStart( &LedRedTipTimer );
LED_RED_ON();
}
上面示例了一个简单的LED灯闪烁的时间事件。
SDK中使用的stop模式,只能通过外部中断,或者RTC闹钟唤醒。
外部中断:SX1276的DIOx、用户应用的外部中断;
RTC闹钟:进入休眠后射频是关闭的,因此进入休眠之前至少要确保有一个时间事件在跑,负责进入休眠之后,将没法唤醒。通常情况下进入休眠之后,会有一个周期发送的时间事件定时器在跑的,SDK中的周期发送数据的时间事件定时器是TxNextPacketTimer。
更多精彩分享,请关注微信公众号:物联网思考