在上一章节,我们了解了MM32W0系列蓝牙模块的中断式例程,也使用AT指令对模块进行了简单的操作。下面我们将对软件架构进行简单的讲解。
阻塞式例程介绍
对于大部分的低功耗设备来说,CPU都是处在休眠模式中,只在接收到特定数据的时候被唤醒处理少量数据,这种应用场景比较适合阻塞方式运行,这种方式配置简单,CPU大部分时间都被蓝牙服务占用,在收到来自射频模块的IRQ信号时需要及时处理,因此,用户的代码不允许出现阻塞。
我们先看一下例程中的main函数:
int main(void)
{
unsigned long temp=0x800000;
SystemClk_HSEInit(); //系统时钟配置为48MHz
#ifdef USE_UART //目前只支持UART接收与发送AT指令
#ifdef USE_AT_CMD //开启AT指令功能
SleepStop = 0x01; //空闲时低功耗,0x00不休眠,0x01睡眠,0x02停机模式
#endif
#endif
#ifdef USE_UART //开启UART功能,修改全局宏定义改变UART和对应引脚
uart_initwBaudRate(); //波特率默认9600,可以修改uart.c中的BaudRate变量
#endif
#ifdef USE_I2C //开启I2C功能
IIC_Init(I2C1); // I2C1,标准模式,SCL PB6 ,SDA PB7 ,SendDataFlag PA10
#endif
#ifdef USE_USB //开启USB功能
usb_test(); //使用PA11、PA12,枚举为USB HID设备
#endif
//SysTick_Count每1ms加一,系统时钟改变时应调用SysTick_Config()函数
SysTick_Configuration();
//启用SPI2,在芯片内部与射频模块通信,速度应不低于6Mhz
SPIM_Init(SPI2,/*0x06*/0x06);
IRQ_RF(); //配置PB8的IRQ 功能处理射频模块信号,用于低功耗唤醒
while(temp--); //延时,方便烧录程序
radio_initBle(0x48, &ble_mac_addr); //初始化射频模块(3dBm),并获取MAC地址
printf("\r\nMAC:%02x-%02x-%02x-%02x-%02x-%02x\r\n", ble_mac_addr[5],ble_mac_addr[4],ble_mac_addr[3],ble_mac_addr[2],ble_mac_addr[1],ble_mac_addr[0]);
ble_run(160*2); //广播间隔320*0.625=200 ms,
}
对于蓝牙,必要的系统资源有:用于计时的Systick和与射频模块通信的SPI2,Systick使用SysTick\_Count变量计时,也可以使用这个变量主动避开与IRQ处理任务的冲突。
目前程序支持RUN MODE,SLEEP MODE,STOP MODE模式,其中,在stop模式下MCU可以通过任意一个外部中断线唤醒,比如MM32W073NTB封装,在芯片设计上将IRQ与PB8共用一个GPIO口,在MM32W073PFB封装上,IRQ是独立的GPIO,需要用户在设计时将IRQ连接到任意一个GPIO口修改外部中断唤醒源即可实现stop唤醒模式。
例程中通过修改全局宏定义,可以启用UART、IIC和USB,目前都支持AT指令方式。
蓝牙的广播间隔由ble\_run()中的参数决定,单位为0.625ms。
与中断式例程不同的地方:
- 蓝牙服务会定时调用接口函数McuGotoSleepAndWakeup();
- ble\_run()是一个阻塞函数,后续程序将不会执行,中断模式中ble\_run参数为0,且不是阻塞函数;
- 中断式例程中蓝牙广播间隔由ble\_run\_interrupt\_start()函数的参数决定,阻塞式例程中蓝牙广播间隔由ble\_run()的参数决定。
图2 阻塞式程序流程
阻塞式程序流程如上图。实际使用中,蓝牙服务将定时调用UsrProcCallback()函数,在连接后允许时调用gatt\_user\_send\_notify\_data\_callback()。两个函数的处理时间应尽可能短,不能影响IRQ信号的处理,否则可能出现蓝牙连接断开、无蓝牙广播等问题。
我们可以将用户程序放在callback.c的UsrProcCallback()函数中定时执行,如例程中的AT指令的处理CheckAtCmdInfo()函数,注意不要阻塞。可以将发送自身状态的程序放在gatt\_user\_send\_notify\_data\_callback()函数中,同样不得阻塞。