前言

凌思微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接口
系统框图

蓝牙mesh多少米_LE5010

存储分布

类别

大小

地址

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交互


SDK下载地址
BLE_UART_SERVER(串口透传)

BLE_UART_SERVER(以下简称uart_server)是具备蓝牙串口透传功能且无安全要求的单连接示例。串口透传,指的是作为无线数据传输通道,蓝牙芯片将Uart上收到的数据不经任何处理直接发送给蓝牙对端,同时也将蓝牙收到的数据推送到Uart上。

流程

蓝牙mesh多少米_LE501X BLE_02

例程路径:<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] = '\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();//开临界保护
        // LOG_I("uart timer out, length=%d", uart_server_rx_index);
        ls_uart_server_send_notification();//ble发送串口数据
        exit_critical(cpu_stat);//关临界保护
    }
    uint8_t input_char = (uint8_t)SEGGER_RTT_GetKey();//读rtt值
    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,也会在串口工具上打印出来

蓝牙mesh多少米_随机数_03

总结

蓝牙与外设中断存在竞争关系,考虑到中断上下文切换,不可在外设中断中调用BLE相关API
开发前需要预烧录一次XXX_production.hex完整的固件才能进行debug调试