LE5010-凌思微电子蓝牙芯片的开发记录(四)
- 今天写一篇文章来解释一下在使用le5010这颗芯片的一个困扰一些工程师的问题:在SDK中为啥不允许我们创建一个while循环,然后在里面实现我们的一些逻辑呢?我们的用户代码又在哪实现呢?
- 带着这两个问题,我简单介绍下这个芯片的背景,蓝牙芯片和普通的MCU有什么区别呢?其实也就多了一个ble罢了,我们可以理解为一颗MCU带的一个外设,但是这个外设对时许的要求会很高,因为在蓝牙协议栈里有着复杂的逻辑,然而这里面最为重要的也就是时序了,在什么时候去干什么事,以及超时了会引发什么后果,这都是与时序有关的,这个时候我们就会涉及一个中断优先级的问题了,这颗芯片在设置优先级的时候是将ble的等级设置为最高的,这个也能体现这个蓝牙在整个系统中的一个重要性了,举个“栗子”:在蓝牙交互的时候会有一个维持连接的动作,说白了也就是蓝牙连接上后,没有进行数据的收发,怎么才不会断开呢?这个时候就需要在主从机之间有一个“打招呼”的动作:“发空包”,如果主从机在设置的超时时间内没有进行数据的交互以及空包的发送,这个时候就会断开连接,并且断开连接的原因是因为超时断开。这个例子我们可以看到在这个指定的时间内如果没有干指定的事便会出现一些问题,这个例子里直接就会断连。
- 上面的例子大家只是看到了结果,那造成这个结果的原因又有哪些呢?其实这个原因有很多,有软件上的也有硬件上的,只有严格按照原厂大佬们的建议来操作一般情况都不会出现问题。
- 我这边以我的客户出现这种情况为例,给大家做一个参考:
软件上: 在代码上中断的优先级是很重要的,在出现这种情况下,大家一定要看下自己蓝牙中断是不是被其他中断给打断了,这会影响到蓝牙的时序。 - 介绍下中断优先级在哪修改:
/**这个函数在platform.c里面,大家没有把握的时候不要修改这个文件**/
static void irq_priority()
{
__NVIC_SetPriority(SVCall_IRQn,2);
NVIC->IP[0] = IRQ_NVIC_PRIO(EXTI_IRQn,3) | IRQ_NVIC_PRIO(WWDT_IRQn,3) | IRQ_NVIC_PRIO(LPWKUP_IRQn,3) | IRQ_NVIC_PRIO(BLE_IRQn,1);
NVIC->IP[1] = IRQ_NVIC_PRIO(RTC_IRQn,3) | IRQ_NVIC_PRIO(DMA_IRQn,3) | IRQ_NVIC_PRIO(QSPI_IRQn,3) | IRQ_NVIC_PRIO(ECC_IRQn,3);
NVIC->IP[2] = IRQ_NVIC_PRIO(CACHE_IRQn,3) | IRQ_NVIC_PRIO(TRNG_IRQn,3) | IRQ_NVIC_PRIO(IWDT_IRQn,3) | IRQ_NVIC_PRIO(CRYPT_IRQn,3);
NVIC->IP[3] = IRQ_NVIC_PRIO(PDM_IRQn,3) | IRQ_NVIC_PRIO(BLE_WKUP_IRQn,1) | IRQ_NVIC_PRIO(ADC_IRQn,3) | IRQ_NVIC_PRIO(ADTIM1_IRQn,3);
NVIC->IP[4] = IRQ_NVIC_PRIO(BSTIM1_IRQn,3) | IRQ_NVIC_PRIO(GPTIMA1_IRQn,3) | IRQ_NVIC_PRIO(GPTIMB1_IRQn,3) | IRQ_NVIC_PRIO(BLE_ERR_IRQn,1);
NVIC->IP[5] = IRQ_NVIC_PRIO(LVD33_IRQn,3) | IRQ_NVIC_PRIO(GPTIMC1_IRQn,3) | IRQ_NVIC_PRIO(LPTIM_IRQn,3) | IRQ_NVIC_PRIO(I2C1_IRQn,3);
NVIC->IP[6] = IRQ_NVIC_PRIO(I2C2_IRQn,3) | IRQ_NVIC_PRIO(SPI1_IRQn,3) | IRQ_NVIC_PRIO(SPI2_IRQn,3) | IRQ_NVIC_PRIO(UART1_IRQn,3);
NVIC->IP[7] = IRQ_NVIC_PRIO(UART2_IRQn,3) | IRQ_NVIC_PRIO(UART3_IRQn,3) | IRQ_NVIC_PRIO(BLE_FIFO_IRQn,1) | IRQ_NVIC_PRIO(BLE_CRYPT_IRQn,1);
}
以上带有BLE的前缀的都是ble的中断,这个就是操控着我们协议栈的一些中断。
- 用户代码的添加
- 在协议栈内注册一个user_event定时器,定时调度
/**调度间隔时间**/
#USER_EVENT_PERIOD 20 // 20ms (min_value >=10ms and min_unit=1ms)
- 向协议栈注册一个user_event的定时器
#include "builtin_timer.h"
static void ls_user_event_timer_init(void);
static void ls_user_event_timer_cb(void *param);
static struct builtin_timer *user_event_timer_inst = NULL;
static void ls_user_event_timer_init(void)
{
user_event_timer_inst =builtin_timer_create(ls_user_event_timer_cb);
builtin_timer_start(user_event_timer_inst, USER_EVENT_PERIOD, NULL);
}
- 在定时器的回调函数内处理相关应用层代码
static void ls_user_event_timer_cb(void *param)
{
/**
user_code
*/
builtin_timer_start(user_event_timer_inst, USER_EVENT_PERIOD, NULL);
}
- user_event_timer初始函数添加位置
- 因user_event_timer是向协议栈注册,故需要协议栈初始化完成,否则无效。导致该函数有一定的位置制约。最早设置位置如下:
static void dev_manager_callback(enum dev_evt_type type,union dev_evt_u
*evt)
{
switch(type)
{
case STACK_READY:
{
ls_user_event_timer_init();
}
}
}
- 用户的代码可以通过这种方式进行添加,这种也就是起了一个软件定时器,但是这个定时器不会干扰到ble的操作,所以可以去替换调while循环。
硬件上: 蓝牙对硬件的要求也是很高的,一般的硬件设计稍有不慎就会中枪,给你看看我有几个客户的一些板子的错误实例:
- 因为板子很小,修改起来是有一定的难度的。但是还是需要修改的,因为这种设计有三点必须要注意的地方没有考虑到。
①天线必须要留有净空区,并且净空区的大小会影响你的信号质量的,并且天线走线时尽量短、直。不能出现90°角。
②晶振下方不能走任何线。
③所有进入芯片的电源必须先经过电容再进入芯片内,不能旁路了,那种起不到效果。经过电容后的电源就直接进芯片了,不要走过孔进入芯片,也不能在其下方走线了,尽量短直。