1 准备工作
本文采用SYD8811 Smart EVK 基础版开发板测量SYD8811芯片的功耗,按以下三个步骤做准备。
1.1 取出UART排针的两个跳线帽,即断开P0.15和P0.16和USB-TTL芯片的连接。
1.2 用烙铁断开JP1,即将SYD8811的VDD引脚和LDO芯片VR1(3.3V)断开。如果JP2是连接的,也要先断开。
1.3 测量电源(一般为3V或3.3V)分别板上【+3.3V】和【GND】排针,如果是用万用表测量,则按上图所示,将电流表(万用表)串接到3.3V电源线中。
2 测试程序设计
2.1 将广播超时设置为20秒
static void setup_adv_data()
{
struct gap_adv_params adv_params;
struct gap_ble_addr dev_addr;
adv_params.type = ADV_IND;
adv_params.channel = 0x07; // advertising channel : 37 & 38 & 39
adv_params.interval = 1600; // advertising interval : 1000ms (1600 * 0.625ms)
adv_params.timeout = 0x14; // 广播超时timeout的值配置为20s
SetAdvParams(&adv_params);
/*get bluetooth address */
GetDevAddr(&dev_addr);
ADV_DATA[11] = dev_addr.addr[0];
ADV_DATA[12] = dev_addr.addr[1];
ADV_DATA[13] = dev_addr.addr[2];
ADV_DATA[14] = dev_addr.addr[3];
ADV_DATA[15] = dev_addr.addr[4];
ADV_DATA[16] = dev_addr.addr[5];
SCAN_DATA[4] = dev_addr.addr[0];
SCAN_DATA[5] = dev_addr.addr[1];
SCAN_DATA[6] = dev_addr.addr[2];
SCAN_DATA[7] = dev_addr.addr[3];
SCAN_DATA[8] = dev_addr.addr[4];
SCAN_DATA[9] = dev_addr.addr[5];
SetAdvData(ADV_DATA, sizeof(ADV_DATA), SCAN_DATA, sizeof(SCAN_DATA));
*(uint8_t *)0x40020014 = 0x01; //设置ADV_PITV为1,降低功耗
}
2.2 在广播结束中断事件GAP_EVT_ADV_END判断将原来的StartAdv()函数去掉,用射频睡眠标识代替,这样每次广播20秒后,芯片的射频都会自动进入休眠状态。
void ble_evt_callback(struct gap_ble_evt *p_evt)
{
if(p_evt->evt_code == GAP_EVT_ATT_WRITE)
{
#ifdef _GPIO_LED_CONTROL_
// GPIO_Pin_Toggle(U32BIT(GPIO_LED_WRITEING));
#endif
ble_gatt_write(p_evt->evt.att_write_evt);
}
else if(p_evt->evt_code == GAP_EVT_ATT_READ)
{
#ifdef _GPIO_LED_CONTROL_
// GPIO_Pin_Toggle(U32BIT(GPIO_LED_READING));
#endif
ble_gatt_read(p_evt->evt.att_read_evt);
}
else if(p_evt->evt_code == GAP_EVT_CONNECTED)
{
connect_flag = 1; //连接状态
update_latency_mode=0;
Timer_Evt_Start(EVT_2S);
#ifdef _GPIO_LED_CONTROL_
// GPIO_Pin_Toggle(U32BIT(GPIO_LED_CONNECT));
#endif
DBGHEXDUMP("Connected addr:",p_evt->evt.bond_dev_evt.addr,sizeof(p_evt->evt.bond_dev_evt.addr));
}
else if(p_evt->evt_code == GAP_EVT_DISCONNECTED)
{
DBGPRINTF("Disconnected,reson:0x%02x\r\n", p_evt->evt.disconn_evt.reason);
start_tx = 0;
connect_flag = 0;
//clr uart rx fifo
uart_rx_buf.header = uart_rx_buf.tail;
Timer_Evt_Stop(EVT_1S_OTA);
setup_adv_data(); //断开连接之后功耗大10uA
StartAdv();
UartEn(false); //不允许RF sleep时关闭XO
DBGPRINTF(("start adv @ disc!\r\n"));
}
else if(p_evt->evt_code == GAP_EVT_ATT_HANDLE_CONFIGURE)
{
if(p_evt->evt.att_handle_config_evt.uuid == BLE_UART)
{
if(p_evt->evt.att_handle_config_evt.value == BLE_GATT_NOTIFICATION)
{
start_tx |= 0x01;
UartEn(true); //不允许RF sleep时关闭XO,休眠的时候因为32Mhz晶振还在,所以功耗很高
uart_0_ClrFiFo();
//clr uart rx fifo
uart_rx_buf.header = uart_rx_buf.tail;
#ifdef _GPIO_LED_CONTROL_
// GPIO_Set_Bits(U32BIT(GPIO_LED_NOTIFYEN));
#endif
DBGPRINTF(("UART notify Enabled!\r\n"));
}
else
{
start_tx &= ~0x01;
UartEn(false); //允许硬件自由控制32Mhz晶振,休眠的时候功耗很低
#ifdef _GPIO_LED_CONTROL_
// GPIO_Clear_Bits(U32BIT(GPIO_LED_NOTIFYEN));
#endif
DBGPRINTF(("UART notify Disabled!\r\n"));
}
}
}
else if(p_evt->evt_code == GAP_EVT_L2CAP_UPDATE_RSP)
{
switch(p_evt->evt.l2cap_update_rsp_evt.result)
{
case CONN_PARAMS_ACCEPTED:
#if defined(_DEBUG_) || defined(_SYD_RTT_DEBUG_)
DBGPRINTF(("update rsp ACCEPTED\r\n"));
{
struct gap_link_params link_app;
GetLinkParameters(&link_app);
#if defined(_DEBUG_) || defined(_SYD_RTT_DEBUG_)
dbg_printf("interval:%x latency:%x\r\n",link_app.interval,link_app.latency);
#endif
}
#endif
break;
case CONN_PARAMS_REJECTED:
#if defined(_DEBUG_) || defined(_SYD_RTT_DEBUG_)
DBGPRINTF(("update rsp REJECTED\r\n"));
#endif
break;
case CONN_PARAM_SMART_TIMEROUT:
#if defined(_DEBUG_) || defined(_SYD_RTT_DEBUG_)
DBGPRINTF(("update rsp TIMEROUT\r\n"));
#endif
break;
case CONN_PARAM_SMART_SUCCEED:
#if defined(_DEBUG_) || defined(_SYD_RTT_DEBUG_)
DBGPRINTF(("update rsp SUCCEED\r\n"));
#endif
break;
case CONN_PARAM_LATENCY_ENABLE:
#if defined(_DEBUG_) || defined(_SYD_RTT_DEBUG_)
DBGPRINTF(("Enable latency\r\n"));
#endif
break;
case CONN_PARAM_LATENCY_DISABLE:
#if defined(_DEBUG_) || defined(_SYD_RTT_DEBUG_)
DBGPRINTF(("Disable latency\r\n"));
#endif
break;
}
}
else if(p_evt->evt_code == GAP_EVT_CONN_UPDATE_COMP)
{
struct gap_link_params link;
GetLinkParameters(&link);
DBGPRINTF("CONN_UPDATE_COMP: %d, %d, %d\r\n", link.interval, link.latency, link.svto);
}
else if(p_evt->evt_code == GAP_EVT_ADV_END)
{
// StartAdv();
dbg_printf("20s ADV timerout, RF sleep.\r\n");
RF_Status = false;
RFSleep();
}
}
我们使用EFM32 Energy Profiler测量芯片的功耗,该测量工具的测量范围0.1uA到 50mA。在开发板上电20秒内可以测量出SYD8811的一秒广播一次的功耗,下面是实测值,大概为22uA。
20秒后,芯片的射频进入休眠,而芯片也处于浅睡状态,此时的功耗约为7uA。
如果觉得广播时间过短可以配置adv_params.timeout 的值为想要的时间,或者广播结束中断事件里面重启广播。
2.3 按键唤醒射频
首先是初始化按键IO口。
void KEY_LED_Init(void)
{
PIN_Set_GPIO(U32BIT(LED1) | U32BIT(LED2) | U32BIT(LED3) | U32BIT(LED4)| U32BIT(KEY1) | U32BIT(KEY2) | U32BIT(KEY3) | U32BIT(KEY4)/* | U32BIT(KEY5)*/ ,PIN_SEL_GPIO);
GPIO_Set_Input(U32BIT(KEY1) | U32BIT(KEY2) | U32BIT(KEY3) | U32BIT(KEY4), U32BIT(KEY1) | U32BIT(KEY2) | U32BIT(KEY3) | U32BIT(KEY4)); //设置输入反相
PIN_Pullup_Enable(T_QFN_48, U32BIT(KEY1) | U32BIT(KEY2) | U32BIT(KEY3) | U32BIT(KEY4));
GPIO_Input_Enable( U32BIT(KEY1) | U32BIT(KEY2) | U32BIT(KEY3) | U32BIT(KEY4));
GPIO_Set_Output(U32BIT(LED1) | U32BIT(LED2) | U32BIT(LED3) | U32BIT(LED4));
GPIO_Set_Bits(U32BIT(LED1) | U32BIT(LED2) | U32BIT(LED3) | U32BIT(LED4));
}
接着配置按键KEY2为外部中断触发。
io_irq_enable(U32BIT(KEY2), &GPIO_callback);
按键外部中断处理函数如下,即重新配置广播参数,开始广播。
void GPIO_callback(void)
{
uint32_t SW;
SW=GPIO_IRQ_CTRL->GPIO_INT;
if(SW&U32BIT(KEY2))
{
if(RF_Status == false)
{
RF_Status = true;
RFWakeup();
DelayMS(100);
UartEn(true);
ble_init();
UartEn(false);
DelayMS(100);
StartAdv();
dbg_printf("ADV restart 20s.\r\n");
}
}
}
当射频休眠后,按下开发板上的按键KEY2,即可重启广播。
为了方便测试,我将代码上传到这里。欢迎下载测试。