irpas技术客

凌思微LE501X开发一 (蓝牙串口透传实例解析)_玉怀一捧雪_蓝牙透传

未知 6314

前言

凌思微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 MESHM0内核: 主频最大64Mhz48Kb SRAM512K 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接口,支持数字MIC1路I2S接口 系统框图

存储分布 类别大小地址Falsh512K0x18000000 ~ 0x18080000SRAM48K0x20000000 ~ 0x2000C000
Flash应用分布 区域地址大小OTA配置0x1807F000 - 0x1807FFFF4KbTFS数据存储0x1807C000 - 0x1807EFFF12KbAPP应用0x18034000 - 0x1807BFFF288KbBLE协议栈0x18002000 - 0x18033FFF200Kbboot与信息数据0x18000000 - 0x18001FFF8Kb
软件开发

关于开发环境的搭建,官方提供了两种方式,具体工具请自行斟酌

Keil+JLinkVS Code+GCC+Python 系统启动流程

系统上电后会在boot rom中判断Boot PIN脚电平选择启动模式

#mermaid-svg-rCMxwpp0NnsG1wx0 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-rCMxwpp0NnsG1wx0 .error-icon{fill:#552222;}#mermaid-svg-rCMxwpp0NnsG1wx0 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-rCMxwpp0NnsG1wx0 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-rCMxwpp0NnsG1wx0 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-rCMxwpp0NnsG1wx0 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-rCMxwpp0NnsG1wx0 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-rCMxwpp0NnsG1wx0 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-rCMxwpp0NnsG1wx0 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-rCMxwpp0NnsG1wx0 .marker.cross{stroke:#333333;}#mermaid-svg-rCMxwpp0NnsG1wx0 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-rCMxwpp0NnsG1wx0 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-rCMxwpp0NnsG1wx0 .cluster-label text{fill:#333;}#mermaid-svg-rCMxwpp0NnsG1wx0 .cluster-label span{color:#333;}#mermaid-svg-rCMxwpp0NnsG1wx0 .label text,#mermaid-svg-rCMxwpp0NnsG1wx0 span{fill:#333;color:#333;}#mermaid-svg-rCMxwpp0NnsG1wx0 .node rect,#mermaid-svg-rCMxwpp0NnsG1wx0 .node circle,#mermaid-svg-rCMxwpp0NnsG1wx0 .node ellipse,#mermaid-svg-rCMxwpp0NnsG1wx0 .node polygon,#mermaid-svg-rCMxwpp0NnsG1wx0 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-rCMxwpp0NnsG1wx0 .node .label{text-align:center;}#mermaid-svg-rCMxwpp0NnsG1wx0 .node.clickable{cursor:pointer;}#mermaid-svg-rCMxwpp0NnsG1wx0 .arrowheadPath{fill:#333333;}#mermaid-svg-rCMxwpp0NnsG1wx0 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-rCMxwpp0NnsG1wx0 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-rCMxwpp0NnsG1wx0 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-rCMxwpp0NnsG1wx0 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-rCMxwpp0NnsG1wx0 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-rCMxwpp0NnsG1wx0 .cluster text{fill:#333;}#mermaid-svg-rCMxwpp0NnsG1wx0 .cluster span{color:#333;}#mermaid-svg-rCMxwpp0NnsG1wx0 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-rCMxwpp0NnsG1wx0 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 检测 低电平 高电平 加载与跳转 BOOT ROM引导 Boot PIN电平 SBL 二级引导 UART启动模式 APP应用 UART交互 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] = '\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,也会在串口工具上打印出来

总结

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


1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,会注明原创字样,如未注明都非原创,如有侵权请联系删除!;3.作者投稿可能会经我们编辑修改或补充;4.本站不提供任何储存功能只提供收集或者投稿人的网盘链接。

标签: #蓝牙透传 #LE5010采用Cortex