前言
凌思微LE501X采用Cortex M0的内核,支持BLE5.0和BLE Mesh,在价格、配置与低功耗上有较好的表现,可作为国产BLE备选替代方案
如有异议,欢迎留言指正
主要特性
- 支持蓝牙BLE5.0/BLE5.1
- 支持 125Kbps/500Kbps/1Mbps/2Mbps
- 接收灵敏度:-99.7dBm @1Mbps -97dBm @2Mbps -105dBm @125kbps
- 发送功率:+12dBm(最大)
- 链路增益:117dB @125kbps(最大)
- 支持 Single-Ended Antenna Output
- 支持蓝牙MESH:私有MESH与SIG MESH
- M0内核:
- 主频最大64Mhz
- 48Kb SRAM
- 512K Flash
- 系统功耗:RX 4.5mA TX4.3mA (3.3V 0dBm)
- 深度休眠:1.1uA(支持RTC、GPIO唤醒)
- 停机(shutdown):700nA(支持GPIO唤醒)
- 工作电压:1.8V~3.6V,典型3.3V
- 通用IO:支持最大34个IO
- 时钟
- 内部高速RC 16M 外部高度晶体 24M
- 内部低速RC 32.768Khz 外部低速晶体 32.768KHz
- 安全及加速单元
- ECC 椭圆曲线加密(256)
- AES 高级加密(256/192/128)
- T/DES 高级加密(192/128/64)
- 真随机数发生器(TRNG)
- 运算加速器(CALC)
- 音频接口
系统框图
存储分布
类别 | 大小 | 地址 |
---|
Falsh | 512K | 0x18000000 ~ 0x18080000 | SRAM | 48K | 0x20000000 ~ 0x2000C000 |
Flash应用分布
区域 | 地址 | 大小 |
---|
OTA配置 | 0x1807F000 - 0x1807FFFF | 4Kb | TFS数据存储 | 0x1807C000 - 0x1807EFFF | 12Kb | APP应用 | 0x18034000 - 0x1807BFFF | 288Kb | BLE协议栈 | 0x18002000 - 0x18033FFF | 200Kb | boot与信息数据 | 0x18000000 - 0x18001FFF | 8Kb |
软件开发
关于开发环境的搭建,官方提供了两种方式,具体工具请自行斟酌
- Keil+JLink
- VS Code+GCC+Python
系统启动流程
系统上电后会在boot rom中判断Boot PIN脚电平选择启动模式
检测
低电平
高电平
加载与跳转
BOOT ROM引导
Boot PIN电平
SBL 二级引导
UART启动模式
APP应用
UART交互
BLE_UART_SERVER(串口透传)
BLE_UART_SERVER(以下简称uart_server)是具备蓝牙串口透传功能且无安全要求的单连接示例。串口透传,指的是作为无线数据传输通道,蓝牙芯片将Uart上收到的数据不经任何处理直接发送给蓝牙对端,同时也将蓝牙收到的数据推送到Uart上。
流程
例程路径:<install_file>/dev/examples/ble/ble_uart_server
代码解析
main主函数入口
ble相关的接口api声明在头文件ls_ble.h 中,其中sys_init_app内部进行了系统相关配置与初始化(电源、时钟、IO、存储、中断…)
int main()
{
sys_init_app();
ble_init();
dev_manager_init(dev_manager_callback);
gap_manager_init(gap_manager_callback);
gatt_manager_init(gatt_manager_callback);
ble_loop();
}
串口初始化
dev_manager_callback 回调中进行串口初始化,默认选择PB0 PB1应用于UART1
static void ls_uart_init(void)
{
uart1_io_init(PB00, PB01);
io_pull_write(PB01, IO_PULL_UP);
UART_Server_Config.UARTX = UART1;
UART_Server_Config.Init.BaudRate = UART_BAUDRATE_115200;
UART_Server_Config.Init.MSBEN = 0;
UART_Server_Config.Init.Parity = UART_NOPARITY;
UART_Server_Config.Init.StopBits = UART_STOPBITS1;
UART_Server_Config.Init.WordLength = UART_BYTESIZE8;
HAL_UART_Init(&UART_Server_Config);
}
static void dev_manager_callback(enum dev_evt_type type,union dev_evt_u *evt)
{
switch(type)
{
case STACK_READY:
{
uint8_t addr[6];
bool type;
dev_manager_get_identity_bdaddr(addr,&type);
LOG_I("type:%d,addr:",type);
LOG_HEX(addr,sizeof(addr));
dev_manager_add_service((struct svc_decl *)&ls_uart_server_svc);
ls_uart_init();
HAL_UART_Receive_IT(&UART_Server_Config, &uart_server_rx_byte, 1);
ls_uart_server_init();
}
}
广播配置
#define UART_SVC_ADV_NAME "LS Uart Server"
static void create_adv_obj()
{
struct legacy_adv_obj_param adv_param = {
.adv_intv_min = 0x20,
.adv_intv_max = 0x20,
.own_addr_type = PUBLIC_OR_RANDOM_STATIC_ADDR,
.filter_policy = 0,
.ch_map = 0x7,
.disc_mode = ADV_MODE_GEN_DISC,
.prop = {
.connectable = 1,
.scannable = 1,
.directed = 0,
.high_duty_cycle = 0,
},
};
dev_manager_create_legacy_adv_object(&adv_param);
}
uuid配置
- 例程使用的nordic的通用串口uuid,可被nrfconnect工具识别
static const uint8_t ls_uart_svc_uuid_128[] = {0x9e,0xca,0xdc,0x24,0x0e,0xe5,0xa9,0xe0,0x93,0xf3,0xa3,0xb5,0x01,0x00,0x40,0x6e};
static const uint8_t ls_uart_rx_char_uuid_128[] = {0x9e,0xca,0xdc,0x24,0x0e,0xe5,0xa9,0xe0,0x93,0xf3,0xa3,0xb5,0x02,0x00,0x40,0x6e};
static const uint8_t ls_uart_tx_char_uuid_128[] = {0x9e,0xca,0xdc,0x24,0x0e,0xe5,0xa9,0xe0,0x93,0xf3,0xa3,0xb5,0x03,0x00,0x40,0x6e};
static const uint8_t att_decl_char_array[] = {0x03,0x28};
static const uint8_t att_desc_client_char_cfg_array[] = {0x02,0x29};
gap回调
static void gap_manager_callback(enum gap_evt_type type,union gap_evt_u *evt,uint8_t con_idx)
{
switch(type)
{
case CONNECTED:
connect_id = con_idx;
LOG_I("connected!");
break;
case DISCONNECTED:
connect_id = 0xff;
uart_server_mtu = UART_SERVER_MTU_DFT;
LOG_I("disconnected!");
start_adv();
break;
case CONN_PARAM_REQ:
break;
case CONN_PARAM_UPDATED:
break;
default:
break;
}
}
GATT回调
static void gatt_manager_callback(enum gatt_evt_type type,union gatt_evt_u *evt,uint8_t con_idx)
{
switch (type)
{
case SERVER_READ_REQ:
LOG_I("read req");
ls_uart_server_read_req_ind(evt->server_read_req.att_idx, con_idx);
break;
case SERVER_WRITE_REQ:
LOG_I("write req");
ls_uart_server_write_req_ind(evt->server_write_req.att_idx, con_idx, evt->server_write_req.length, evt->server_write_req.value);
break;
case SERVER_NOTIFICATION_DONE:
uart_server_ntf_done = true;
LOG_I("ntf done");
break;
case MTU_CHANGED_INDICATION:
uart_server_mtu = evt->mtu_changed_ind.mtu;
LOG_I("mtu: %d", uart_server_mtu);
ls_uart_server_data_length_update(con_idx);
break;
default:
LOG_I("Event not handled!");
break;
}
}
蓝牙透传(ble to uart)
ls_uart_server_write_req_ind 接口将接收到的数据通过串口发送
static void ls_uart_server_write_req_ind(uint8_t att_idx, uint8_t con_idx, uint16_t length, uint8_t const *value)
{
if(att_idx == UART_SVC_IDX_RX_VAL)
{
if(uart_server_tx_busy)
{
LOG_I("Uart tx busy, data discard!");
}
else
{
uart_server_tx_busy = true;
LS_ASSERT(length <= UART_SVC_BUFFER_SIZE);
memcpy(uart_server_tx_buf, (uint8_t*)value, length);
uart_server_tx_buf[length] = '\0';
LOG_I("recv[%d]:%s",length, uart_server_tx_buf);
HAL_UART_Transmit_IT(&UART_Server_Config, (uint8_t*)uart_server_tx_buf, length);
}
}
else if (att_idx == UART_SVC_IDX_TX_NTF_CFG)
{
LS_ASSERT(length == 2);
memcpy(&cccd_config, value, length);
}
}
串口透传(uart to ble)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(uart_server_rx_index < UART_SVC_BUFFER_SIZE)
{
uart_server_buf[uart_server_rx_index++] = uart_server_rx_byte;
}
else
{
LOG_I("uart server rx buffer overflow!");
}
HAL_UART_Receive_IT(&UART_Server_Config, &uart_server_rx_byte, 1);
}
- 定时检测发送,软定时器定时调用ls_uart_server_send_notification将串口接收的数据通过BLE进行发送
static void ls_uart_server_timer_cb(void *param)
{
if(connect_id != 0xff)
{
uint32_t cpu_stat = enter_critical();
ls_uart_server_send_notification();
exit_critical(cpu_stat);
}
uint8_t input_char = (uint8_t)SEGGER_RTT_GetKey();
if(connect_id == 0xff && input_char != 0xff && input_char > '0' && input_char <= '9')
{
ls_uart_server_update_adv_interval(input_char);
}
if(uart_server_timer_inst)
{
builtin_timer_start(uart_server_timer_inst, UART_SERVER_TIMEOUT, NULL);
}
}
实例验证
串口工具发送消息uart to ble,可以在APP上接收到;同理,在APP上发送ble to uart,也会在串口工具上打印出来
总结
蓝牙与外设中断存在竞争关系,考虑到中断上下文切换,不可在外设中断中调用BLE相关API; 开发前需要预烧录一次XXX_production.hex完整的固件才能进行debug调试
|