概述
前言
凌思微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)
- 音频接口
- 2路PDM接口,支持数字MIC
- 1路I2S接口
系统框图
存储分布
类别 | 大小 | 地址 |
---|---|---|
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脚电平选择启动模式
SDK下载地址
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(); //ble初始化
dev_manager_init(dev_manager_callback);//设备管理初始化并注册回调
gap_manager_init(gap_manager_callback);//gap初始话与回调注册
gatt_manager_init(gatt_manager_callback);//gatt初始化与回调注册
ble_loop(); //循环处理ble事件
}
串口初始化
dev_manager_callback
回调中进行串口初始化,默认选择PB0 PB1应用于UART1
static void ls_uart_init(void) //串口初始化
{
uart1_io_init(PB00, PB01); //tx rx
io_pull_write(PB01, IO_PULL_UP);
UART_Server_Config.UARTX = UART1; //uart1
UART_Server_Config.Init.BaudRate = UART_BAUDRATE_115200;//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);//配置
}
//dev 管理回调
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);//获取mac
LOG_I("type:%d,addr:",type);
LOG_HEX(addr,sizeof(addr));
dev_manager_add_service((struct svc_decl *)&ls_uart_server_svc);//添加ble 串口服务
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, //广播间隔 32*0.625 = 20ms
.adv_intv_max = 0x20,
.own_addr_type = PUBLIC_OR_RANDOM_STATIC_ADDR, //公共 随机静态地址
.filter_policy = 0,
.ch_map = 0x7, //37 38 39 全通道
.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回调
- 获取BLE连接、断开相关事件
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://连接参数更新请求事件
//LOG_I
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: //app读请求
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: //mtu交换事件
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] = '