当前位置: 首页 > article >正文

沁恒微蓝牙从机添加服务和特征示例

蓝牙从机添加自定义服务特征示例 包括 Indicate 和 128bit UUID ...... 矜辰所致 ...增加特征值长度说明 2026/3/19前言在之前的文章《沁恒微蓝牙 GATT 应用框架说明》中我们已经详细了解了 GATT 中服务和特征值有关的应用框架官方也给出了添加自定义服务和特征的代码在实际应用中我们或许也需要根据需求增加服务和特征。所以本文内容就是说明以及实际演示一下在应用中如何添加不同的服务和特征值。相关博文沁恒微蓝牙 GATT 应用框架说明CH58x/CH59x 系列芯片从机示例解析一文搞清 BLE 蓝牙 UUID.我是矜辰所致全网同名尽量用心写好每一系列文章不浮夸不将就认真对待学知识的我们矜辰所致金石为开目录前言一、本文准备实现的示例二、官方示例说明2.1 服务声明2.2 特征值1Char1- 可读可写2.2.1 服务和特征申明的区别2.2.2 GATT_PERMIT_READ 和 GATT_PROP_READ2.2.3 关于名字 Unknown Characteristic2.2.4 可选的描述符2.3 特征值 2~32.4 特征值 4 - 通知Notify2.5 特征值 5 - 需要认证的读2.6 特征值长度说明三、示例测试3.1 增加 Indicate 属性特征值3.2 增加服务3.3 增加128bit UUID 服务四、示例下载结语一、本文准备实现的示例实际上示例 Peripheral 中的 gattprofile.c 已经完全展示了 5 种不同属性的服务实际应用需要增加完全可以复制一下把对应的名字修改一下就可以实现了。本文我们计划实现几个示例在官方示例自定义服务里增加特征值额外再增加我们自己的服务和特征值额外再增加 128bit UUID 的服务和特征值。我们只要搞清楚了官方示例提供的自定义服务特征值那么自己应用中的服务特征也不会有什么问题。二、官方示例说明我们需要参考的是gattprofile.c文件代码我们把此文件中的结构体数组simpleProfileAttrTbl[]搞清楚基本上就完成了大部分工作了。在文章 沁恒微蓝牙 GATT 应用框架说明 的 2.1.1 服务和特征值配置 小节关于服务和特征值的说明。我们把这个结构体数组拆分开来讲解说明能更快的让我们理解。2.1 服务声明负责服务声明的代码如下// Simple Profile Service{{ATT_BT_UUID_SIZE,primaryServiceUUID},/* type primaryServiceUUID0x2800*/GATT_PERMIT_WRITE,/* permissions*/0,/* handle */(uint8_t*)simpleProfileService/* pValue保存的值为本服务的UUID 0XFFE0 */},对于服务申明第一个成员{ATT_BT_UUID_SIZE, primaryServiceUUID}是固定的表示 “这是一个主服务” 。primaryServiceUUID在我以前的文章 一文搞清 BLE 蓝牙 UUID 中也说明过这个 UUID 类型为 0x2800 是用来声明这是一个服务的是个常量固定存放在 Flash 中的 所以的服务声明前面2个成员永远固定这么写就行了不能改变第二个成员权限需要是可读主机才能正确识别服务设置为GATT_PERMIT_READ实际应用这里按照标准的写成可读即可。这里额外说明一下华为手机连接过从机设备一次以后就会保存设备信息我测试的时候使用华为手机正常连接过设备后将第二个成员权限改成其他任意数值包括0重新烧录后连接都依然能够正常获取这是华为手机自己的策略问题。大家做测试的时候记得注意一下。第三个成员句柄写成 0连接上以后由协议栈自动分配最后一个成员是储存的值这里因为服务本身不是用来数据通信的这里的值保存的是服务自己的 UUID 就是示例中定义的 0xFFE0 。2.2 特征值1Char1- 可读可写表示特征值1 的代码如下// Characteristic 1 Declaration//// 1. 特征值1 - 声明项告诉手机特征值1支持读写{{ATT_BT_UUID_SIZE,characterUUID},//固定的类型0x2803Characteristic特征值声明0x2803GATT_PERMIT_READ,// 声明项本身的权限可读0,simpleProfileChar1Props},// 本特征值的属性为GATT_PROP_READ | GATT_PROP_WRITE 特征值1的功能读写// Characteristic Value 1//2. 特征值1 - 数值项存放特征值1的实际数据{{ATT_BT_UUID_SIZE,simpleProfilechar1UUID},//自定义的特征值的 0xFFE1GATT_PERMIT_READ|GATT_PERMIT_WRITE,// 数值项的访问权限可读可写0,simpleProfileChar1},// 特征值1的数据自定义的数组我们可读写修改// Characteristic 1 User Description// 3. 特征值1 - 描述项给特征值1起名字{{ATT_BT_UUID_SIZE,charUserDescUUID},// 类型用户描述0x2901GATT_PERMIT_READ,// 描述项本身的权限可读0,simpleProfileChar1UserDesp},// 描述项的值那这里我们可以明显看到这里特征的代码使用了 3 个属性成员表示而上面服务只需要一个成员表示这里我们先来说明一下为什么。2.2.1 服务和特征申明的区别在 BLE GATT 规范里每个特征值的标准结构是特征值声明Characteristic Declaration说明这是一个特征值包含它的属性读 / 写 / 通知等。特征值的值Characteristic Value真正的数据内容。可选描述符Descriptor如用户描述0x2901、客户端配置0x2902等。而对于服务来说服务的核心作用是告诉外部 “我有一个 UUID 为 0xFFE0 的主服务” 今次而已。使用一个属性项的 UUID0x2800 定义了 “这是服务声明”数据值0xFFE0 定义了 “是哪个服务”就完成了服务的核心声明。不需要额外的 “配置” 或 “描述”所以一个属性项就够了。所以一个完整的特征值需要多个属性项配合才能描述清楚而服务只需要一个属性项就能完成核心声明。下面上一张 BLE GATT 协议的数据组织结构示意图对于我们的示例代码而言gattAttribute_t结构体就是 GATT 成员属性表数组simpleProfileAttrTbl中每个{ }都是独立的 “属性项”有自己的 UUID 和数据值服务的 UUID0x2800 数据值服务 UUID就能完成声明仅需 1 项特征值需要 0x2803声明属性 自定义 UUID存数据 0x2901描述可选至少 2 项再给一张示意图2.2.2 GATT_PERMIT_READ 和 GATT_PROP_READ在特征值定义的时候有两个地方都会用到关于读写的宏定义如下图:对于有些新人可能会有一点迷糊两个地方都有读写而且这两个地方有什么区别那么它们是不是一定要一致 我们先来搞懂这两个位置的不同我们用一个表格来说明一下simpleProfileChar1Props特征属性比如 GATT_PROP_READ)GATT_PERMIT_xxx访问权限比如 GATT_PERMIT_READ位置特征值声明项0x2803的数据值pValue特性值的权限字段permissions作用对象告诉客户端比如手机这个特性支持什么功能告诉协议栈这个属性项实际能不能被读 / 写控制实际数据能否访问协议层发现阶段广播/读取声明数据访问阶段读/写值给谁看手机/主机看决定是否显示读/写按钮协议栈看决定是否允许操作简单总结一下就是GATT_PROP_*告诉外界你能做什么代码中在特征值的声明项的第四个成员pValue位置被使用GATT_PERMIT_*实际控制能不能做代码中在特征值的数值项的第二个成员permissions位置被使用。正常来说这两处的定义会保持一致但是不一致也可以存在我们举两个不同的例子。EX1// 场景数据只读但声明说有写权限故意不一致有特殊用途// 声明告诉手机这个特性可读写{{ATT_BT_UUID_SIZE,characterUUID},GATT_PERMIT_READ,0,simpleProfileChar1Props// GATT_PROP_READ | GATT_PROP_WRITE},// 实际值但协议栈禁止写入{{ATT_BT_UUID_SIZE,simpleProfilechar1UUID},GATT_PERMIT_READ,// ← 只有读没有写0,simpleProfileChar1},效果手机看到声明 → 显示可读写 → 用户看到写按钮手机尝试写入 → 协议栈拒绝 → 返回 Write Not Permitted 错误EX2 // 场景声明只读实际可写隐藏功能// 声明告诉手机只读{{ATT_BT_UUID_SIZE,characterUUID},GATT_PERMIT_READ,0,simpleProfileChar1Props// GATT_PROP_READ 只有读},// 实际值但协议栈允许写入{{ATT_BT_UUID_SIZE,simpleProfilechar1UUID},GATT_PERMIT_READ|GATT_PERMIT_WRITE,// ← 实际可写0,simpleProfileChar1},效果普通APP看不到写功能知道Handle的开发者可以直接写隐藏接口讲到这里我们再来看特征值1 的代码是不是感觉很明了了结合上文内容然后对着上面给出的代码注释仔细看一下是不是就清楚了。2.2.3 关于名字 Unknown Characteristic在上面给出的特征值1 的代码中在描述项的注释中我写了描述符是给本特征值取名字的而且代码中确实也给了值但是显示的是 Unknown Characteristic 。如果观察比较仔细在这个官方示例工程中 Profile 文件夹下面与gattprofile.c同样是添加服务特征的代码文件devinfoservice.c中所定义的特征值都没有加描述项但是它们都有显示名字如下图这里说明一下为什么会有这种情况首先在蓝牙协议标准层面给人看的“名字”应该定义在描述项Descriptor里具体是 0x2901 Characteristic User Description。蓝牙联盟Bluetooth SIG维护了一个标准 UUID 数据库。如果特征值的 UUID 是蓝牙联盟规定的标准 DIS 服务 UUID例如 Model Number 是 0x2A24那么 BLE调试助手/nRF Connect 等工具不需要描述符直接通过 UUID 查表就能显示 “Model Number String”。用户自定义的 UUID 128 位或者未注册的 16/32 位是私有的。通用工具无法查表为了严谨它们只能显示 “Unknown Characteristic” 并列出原始的 UUID 字符串以防误导用户即使加了 0x2901 描述符。只有点进去看描述符才能看到文字如下所以总结一下只要你的 UUID 是标准的就不需要描述符来告诉工具名字自定义的 UUID 即便自己使用描述符定义了名字依然会显示 Unknown Characteristic 。2.2.4 可选的描述符再简单说一个问题我们前面有说过如果不具备 Notify / Indicate 属性的话描述符是可以不需要的对于示例中的特征值1 我们都可以直接把描述项部分给删除也不影响使用如下这里这样修改以后特征值依然读写正常。 只是有一个小点改了以后会发现手机读取不到 Notify 了这是因为我们的 Notify 的位置发生了变化在gattprofile.c的最开头因为少了一个属性项我们改成 10 就正常了2.3 特征值 2~3把特征值 1 介绍完毕后面的特征值类似简单的过一遍就好:特征值2Char2- 只读特征值3Char3- 只写这两个很基本没什么需要特别说明的。2.4 特征值 4 - 通知Notify特征值4 是示例中唯一带 CCCD 的特性因为支持 Notify必须要多了一个客户端配置描述符0x2902。// Characteristic 4 Declaration{{ATT_BT_UUID_SIZE,characterUUID},GATT_PERMIT_READ,0,simpleProfileChar4Props},//声明本特征值的属性 GATT_PROP_NOTIFY// Characteristic Value 4{{ATT_BT_UUID_SIZE,simpleProfilechar4UUID},0,// 权限0因为 Notify 是服务器主动推送客户端不直接读写这个值0,simpleProfileChar4},// Characteristic 4 configuration{{ATT_BT_UUID_SIZE,clientCharCfgUUID},// 描述符类型客户端特征配置 0x2902GATT_PERMIT_READ|GATT_PERMIT_WRITE,// 权限可读可写用于客户端开启/关闭通知0,(uint8_t*)simpleProfileChar4Config},//Notifications and Indications are Enabled/disabled// Characteristic 4 User Description{{ATT_BT_UUID_SIZE,charUserDescUUID},GATT_PERMIT_READ,0,simpleProfileChar4UserDesp},大家以后自己添加具备 Notify/Indicate 属性的特征时候必须带上上面代码中的 Characteristic 4 configuration 部分而且格式与上面保持一致。2.5 特征值 5 - 需要认证的读特征值 5 也只是在 特征值的第二项数值项里面的权限成员定义为GATT_PERMIT_AUTHEN_READ需要和手机绑定才能正常读取数据// Characteristic 5 Declaration{{ATT_BT_UUID_SIZE,characterUUID},GATT_PERMIT_READ,0,simpleProfileChar5Props},// 属性GATT_PROP_READ 这个一样// Characteristic Value 5{{ATT_BT_UUID_SIZE,simpleProfilechar5UUID},GATT_PERMIT_AUTHEN_READ,// ⭐ 需要配对认证才能读0,simpleProfileChar5},// Characteristic 5 User Description{{ATT_BT_UUID_SIZE,charUserDescUUID},GATT_PERMIT_READ,0,simpleProfileChar5UserDesp},我们如果把官方示例中的服务和特征值的定义完全搞清楚基本上在后面应用中想要自己定义什么特征值是完全没问题的至于数据通信流程逻辑那些也是按照示例中的框架来添加即可这个后面有需要可能还会单独写一篇主机与从机数据收发流程解析。2.6 特征值长度说明示例中特征值长度都定义得比较小如下图// Length of characteristic in bytes ( Default MTU is 23 )#defineSIMPLEPROFILE_CHAR1_LEN1#defineSIMPLEPROFILE_CHAR2_LEN1#defineSIMPLEPROFILE_CHAR3_LEN1#defineSIMPLEPROFILE_CHAR4_LEN1#defineSIMPLEPROFILE_CHAR5_LEN5这里实际支持多大呢在蓝牙官方文档中有说明文档地址https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-62/out/en/host/attribute-protocol–att-.html特征值的最大长度位 512 字节。但是因为单个包最大长度有限制读MTU - 1 写 / 通知MTU - 3 。所以建议一般最大设置为 MTU - 3 确保不会有意外发生理论上来说超过的部分会分包发送 。但是在实际应用中我们一般会把特征值的长度设置为 244 因为用户数据最大一帧就只能发 244 。这部分相关说明我在之前博文沁恒微 CH585 芯片 Ble 传输速率测试说明 探讨过虽然文档说过特征值的最大长度位 512 字节但是根据我们的分析我们最大应该设置为 MTU-3 就是 509 字节。但是由为了一帧发完只要可以满足应用需求最大设置为 244 即可。三、示例测试对于本文来说我们主要是学会去增加自定义的服务和特征现在就来完成我们前面说过的想要实现的效果。因为最后我会把我测试的源码给大家下载所以在本次测试只会把基础步骤和关键点加以说明大部分都是代码复制粘贴改名字那些工作大家自己操作起来也只是花点时间没有技术难点。3.1 增加 Indicate 属性特征值首先我们在示例基础上增加一个带 Indicate 属性的特征值 第六个特征值直接在simpleProfileAttrTbl数组后面添加完成上述操作我们就已经可以在连接后看到这个特征值了这里只是能够显示这个特征值但是数据交互逻辑我们还没有增加我们接下来还要增加一些逻辑交互数据处理的操作代码。我们需要实现几个数据处理的操作分别如下我们在之前《沁恒微蓝牙 GATT 应用框架说明》文章中讲过具备 CCCD 属性的特征值是需要绑定的在SimpleProfile_AddService函数对应位置添加绑定信息然后对应几个地方也需要增加一条代码实现一个simpleProfile_Indicate函数可以参考例程原始的函数这里比 Notify 多一个 taskID 是因为蓝牙协议规定 Indication 发送后对方必须发送一个确认响应Confirmation返回这个返回必须有个载体。我们可以不用做任何处理发送 Indication 协议栈会自动处理只有当我们想要 “确认手机是否真的收到” 时才需要应用层去处理这个响应。函数simpleProfile_WriteAttrCB中增加特征值 6 的 CCCD 读写处理最后还需要在peripheral.c中实现一下 Indicate 发送这个其实就是按照示例的 Notify 发送写就行了没特别的在周期任务中添加一下周期发送至此我们新的具备 Indicate 属性的特征值就添加完成了我们可以看一下测试效果3.2 增加服务增加服务基本就是复制一遍 gattprofile.c 和 gattprofile.h 然后还有在 peripheral.c 文件中需要增加的一些逻辑和数据收发的代码别这个部分大家需要下载示例代码自己看即可需要说明的在上面文章中基本都说明了。最后手机连接读到的效果如下不过上面 nRF Connect 我还真不知道怎么读取显示成字符串也没在网上找到如何设置因为这个服务的第一个特征值我是用字符串处理的使用沁恒微 的 Ble 调试助手可以读取显示字符串3.3 增加128bit UUID 服务最后来测试一下 128bit UUID 服务和特征值的增加。可以肯定的是即便是 128bit UUID 整体的逻辑依旧是和 16 bit 的 UUID 一样的就等于代码框架不变我们把需要注意的地方说明一下。首先是服务于特征的定义声明我们用图片可以更好的说明对于自定义的 UUID 而言服务和特征值的 UUID 可以是一样的但是同一个服务下面不同的特征值 UUID 必须不一样。其他的流程基本一致还有一个地方就是在数据读写的时候就是在my_128bit_app_read_attr_callback和my_128bit_app_write_attr_callback函数种需要处理一下分支示例为 16 bit UUID因为我们测试的时候使用 2个特征一个是 只写一个是 Notify 我们这里上一下 写回调的处理staticbStatus_tmy_128bit_app_write_attr_callback(uint16_tconnHandle,gattAttribute_t*pAttr,uint8_t*pValue,uint16_tlen,uint16_toffset,uint8_tmethod){bStatus_tstatusSUCCESS;// 初始状态设为成功uint8_tnotifyApp0xFF;// 通知应用的标志0xFF表示无通知// If attribute permissions require authorization to write, return error // 检查属性是否要求授权写入if(gattPermitAuthorWrite(pAttr-permissions)){// Insufficient authorizationreturn(ATT_ERR_INSUFFICIENT_AUTHOR);//返回授权不足错误0x08}//根据UUID类型16位或128位if(pAttr-type.lenATT_BT_UUID_SIZE){/* 16-bit UUID 的处理按照官方示例来本来觉得这里不需要 但是描述符那些是标准的 16bit 的UUID 这些需要 */// 16-bit UUIDuint16_tuuidBUILD_UINT16(pAttr-type.uuid[0],pAttr-type.uuid[1]);switch(uuid){caseGATT_CLIENT_CHAR_CFG_UUID:PRINT(write 128 CCCD\r\n);statusGATTServApp_ProcessCCCWriteReq(connHandle,pAttr,pValue,len,offset,GATT_CLIENT_CFG_NOTIFY);break;default:// Should never get here! (characteristics 2 and 4 do not have write permissions)statusATT_ERR_ATTR_NOT_FOUND;break;}}else{// 128-bit UUID// status ATT_ERR_INVALID_HANDLE;// uint64_t uuid BUILD_UINT16(pAttr-type.uuid[12], pAttr-type.uuid[13]); // 处理128位UUID/* 这里需要处理 以下到底是哪个特征值的 UUID 这个自定义的 UUID 区分当然是需要自己处理 比如我们上面定义的就是第一个成员不一样我们这里测试就用最简单的方式 */uint8_ttest_uuidpAttr-type.uuid[0];//char1 为 01 cha2为 02printf(Full 128-bit UUID: );for(inti0;i16;i){printf(%02X ,pAttr-type.uuid[i]);}printf(\n);switch(test_uuid){case1://Validate the value// Make sure its not a blob operif(offset0){if(lenmy_128bit_app_CHAR1_LEN){statusATT_ERR_INVALID_VALUE_SIZE;}}else{statusATT_ERR_ATTR_NOT_LONG;}//Write the valueif(statusSUCCESS){tmos_memcpy(pAttr-pValue,pValue,my_128bit_app_CHAR1_LEN);notifyAppmy_128bit_app_CHAR1;}break;default:// Should never get here! (characteristics 2 and 4 do not have write permissions)statusATT_ERR_ATTR_NOT_FOUND;break;}}/*若特征值发生变更通过回调通知应用层 */// If a charactersitic value changed then callback function to notify application of changeif((notifyApp!0xFF)my_128bit_app_AppCBsmy_128bit_app_AppCBs-pfnmy_128bit_app_Change){my_128bit_app_AppCBs-pfnmy_128bit_app_Change(notifyApp);}return(status);}上面主要是注意 16bit UUID 的分支依然要保留用来处理 CCCD 的写操作因为 UUID 固定是 16bit 的 0x2902。然后第二就是在判断是哪一个特征值的时候是根据自定义的 UUID 去做区分的对于上面示例而言特征值的 UUID 就只有1个字节不一样就做了一下最简单的区分实际应用需要自己考虑合理性。最后实际测试的效果如下好了到这里示例展示圆满结束代码放下载地址放在下面需要自取。四、示例下载示例工程没有做独立处理需要放在 EVT 包对应位置运行。测试芯片 CH585M下载地址 https://gitee.com/qzh_projects/CH58x/tree/master/结语本文我们分析了一下沁恒微官方从机示例种添加自定义服务和特征值的部分代码在官方示例基础上继续给大家展示了不同的服务和特征的添加示例。相信以后以后在沁恒微芯片蓝牙服务和特征值的添加上大家都游刃有余。接下来计划 GATT 部分还需要一篇文章要结合主机示例说明一下数据收发的接口流程。这种基础说明文章写起来还是蛮累的怕简单的东西都没表述好又怕涉及到的其他该知道的东西没有讲到位累 /(ㄒoㄒ)/~~好了本文就到这里谢谢大家

相关文章:

沁恒微蓝牙从机添加服务和特征示例

蓝牙从机添加自定义服务特征示例 (包括 Indicate 和 128bit UUID ) ...... 矜辰所致 ...增加特征值长度说明 2026/3/19 前言 在之前的文章《沁恒微蓝牙 GATT 应用框架说明》中我们已经详细了解了 GATT 中服务和特征值有关…...

DeepSeek-R1 1.5B快速入门:3步搞定本地AI助手,无需显卡

DeepSeek-R1 1.5B快速入门:3步搞定本地AI助手,无需显卡 1. 引言:为什么选择DeepSeek-R1 1.5B? 想象一下,你正在解决一个复杂的数学问题,或者需要快速生成一段代码,但手头只有一台普通笔记本电…...

2026年口碑出色的AIGC降重网站,评测推荐,行业内AIGC降重供应商WritePass引领行业标杆

在学术写作领域,论文原创性与规范性始终是核心诉求,而AIGC(人工智能生成内容)的广泛应用,既提升了创作效率,也带来了“机械重复”“逻辑同质化”等新挑战。在此背景下,AIGC降重工具成为学术作者…...

067工控分布式集群云边协同国密级安全通信与等保合规体系

工控分布式集群云边协同国密级安全通信与等保合规体系 第三栏目第五篇|C/CGo双系统国密SM2/SM3/SM4等保2.0/3.0适配 一、核心痛点与定位 痛点:云边通信裸传易篡改、权限管控混乱、操作无审计留痕、密钥管理缺失、不符合工控等保要求、传统加密拖慢业务。…...

卡证检测矫正模型中小企业应用:低成本替代OCR前处理环节

卡证检测矫正模型中小企业应用:低成本替代OCR前处理环节 你是不是也遇到过这样的场景?财务同事拿着一叠发票和身份证复印件,一张张手动扫描、裁剪、摆正,就为了把它们“喂”给OCR系统识别。或者,开发团队为了一个卡证…...

Qwen3-0.6B-FP8多轮对话效果展示:复杂任务拆解与上下文记忆

Qwen3-0.6B-FP8多轮对话效果展示:复杂任务拆解与上下文记忆 最近在测试一些轻量级模型,看看它们在真实对话场景下的表现。今天的主角是Qwen3-0.6B-FP8,一个参数只有6亿的“小个子”。你可能觉得,这么小的模型,处理复杂…...

StructBERT语义匹配系统精彩案例:招聘平台简历-岗位匹配热力图分析

StructBERT语义匹配系统精彩案例:招聘平台简历-岗位匹配热力图分析 1. 项目背景与需求场景 在招聘行业,简历与岗位的匹配一直是核心痛点。传统的关键词匹配方法存在明显局限:一个写着"精通Java开发"的简历,可能被匹配…...

超级千问语音设计世界:5分钟上手,用文字指挥AI声音的像素冒险

超级千问语音设计世界:5分钟上手,用文字指挥AI声音的像素冒险 1. 引言:当像素风遇上AI语音 还记得小时候玩红白机时,那些简单却充满魔力的8-bit音效吗?现在,这种复古魅力与最先进的AI语音技术相遇了。&qu…...

FRAM vs EEPROM:为什么你的嵌入式项目应该考虑铁电存储器?

FRAM vs EEPROM:嵌入式系统存储技术的革新选择 在嵌入式系统设计中,存储器的选择往往决定了产品的性能边界。当工程师们还在为EEPROM的写入速度和耐久性妥协时,一种被称为"铁电存储器"(FRAM)的技术正在悄然改变游戏规则。想象一下&…...

Leather Dress Collection效果展示:12款皮革服饰在不同光照条件下的渲染效果

Leather Dress Collection效果展示:12款皮革服饰在不同光照条件下的渲染效果 1. 项目概述 Leather Dress Collection是一组基于Stable Diffusion 1.5的LoRA模型,专门用于生成各种皮革服装风格的图像。这套模型由Stable Yogi开发,包含12个不…...

树莓派4B上跑YOLOv8-Pose,从PyTorch到ONNX转换的完整避坑指南(附代码)

树莓派4B部署YOLOv8-Pose模型:从PyTorch到ONNX的高效转换实战 1. 为什么需要在树莓派上使用ONNX格式? 在资源受限的边缘设备上部署深度学习模型时,模型格式的选择直接影响运行效率。我们通过一组对比测试发现:同一张图片的推理耗时…...

SPSSAU极差分析实战:5分钟搞定正交试验最优组合

SPSSAU极差分析实战:5分钟掌握正交试验优化技巧 正交试验设计作为多因素优化问题的黄金工具,在材料科学、化工配方、工艺参数优化等领域有着广泛应用。但传统手工计算极差分析不仅耗时耗力,还容易在数据转换过程中出错。SPSSAU的智能化极差分…...

AS2301 4.5-30V 1.5A同步DC-DC,内置MOS,工作频率1.2Mhz

1、方案名称:AS2301 4.5-30V 1.5A同步DC-DC,内置MOS,工作频率1.2Mhz2、品牌:紫源微(Zymicro)3、描述:AS2301是一款具有内部功率MOSFET的低EMI签名,同步,降压,…...

RISC-V开发者的中科蓝讯内存管理解析:如何高效使用COM区和Bank区?

RISC-V开发者的中科蓝讯内存管理实战:COM区与Bank区的高效编程策略 在嵌入式开发领域,内存管理一直是决定系统性能的关键因素之一。对于采用RISC-V架构的中科蓝讯芯片开发者而言,理解并掌握COM区与Bank区的特性差异,能够显著提升程…...

失落方舟一期

目录 一,技能一 核心扩散 细节辅助扩散 折射 二,技能二 爆破扩散 弹头拖尾 三,技能三 坠落扩散 四,技能四 起手预备效果 起手爆开 投射拖尾 发射魔法能量 能量锁链 魔法符文 汇聚能量条带 暗能量球 坠地能量爆…...

Photoshop图层混合模式全解析:从原理到实战应用(附常用组合推荐)

Photoshop图层混合模式全解析:从原理到实战应用(附常用组合推荐) 当你在深夜赶设计稿时,是否曾对着图层面板上那27种混合模式发愣?作为从业12年的资深视觉设计师,我至今仍记得第一次用"正片叠底"…...

Python爬虫实战:构建文本资源去重引擎(精确+语义级)

㊗️本期内容已收录至专栏《Python爬虫实战》,持续完善知识体系与项目实战,建议先订阅收藏,后续查阅更方便~ ㊙️本期爬虫难度指数:⭐⭐⭐ (进阶) 🉐福利: 一次订阅后,专栏内的所有文…...

粘结磁环BNP-12的参数和典型用途

BNP是粘结钕铁硼材料的牌号前缀,其中B代表粘结,N代表钕铁硼,P代表磁粉,BNP-12是其中的一个性能等级,以下是您想要了解的参数及用途介绍。下图片为粘结钕铁硼BNP-12L磁参数范围上面为压制/粘结BNP-12系列的典型参数区间…...

AD9739子卡设计中ADCLK914时钟buffer的关键作用解析

1. 为什么AD9739子卡必须使用ADCLK914时钟buffer 在高速数据转换系统设计中,时钟信号的完整性往往决定了整个系统的性能上限。AD9739作为一款高性能DAC芯片,其工作频率范围覆盖0.8-3GHz,这对时钟信号的质量提出了极高要求。我在实际项目中遇到…...

RK3588平台安装Zigbee2MQTT 服务器

交叉编译安装Zigbee2MQTT 服务器1、搭建Node.js环境(虚拟机)2、安装zigbee2mqtt服务(虚拟机)3、设备环境搭建启动Z2M服务效果展示1、搭建Node.js环境(虚拟机) cd /opt wget https://nodejs.org/dist/v22.1…...

【stm32_1】集成开发环境的搭建 + KEIL5使用STM32标准固件库源码建立M4工程模板

1.MDK软件的下载 使用该链接直接下载所需mdk:https://armkeil.blob.core.windows.net/eval/MDK***.EXE 比如指定5.26版本,https://armkeil.blob.core.windows.net/eval/MDK526.EXE MDK软件的结构 2.软件安装完成后,要安装ST公司提供的芯片支持包xxxx.p…...

探索C++标准库中的算法:<algorithm> 头文件概览

探索C标准库中的算法&#xff1a; 头文件概览 在C编程的广阔天地里&#xff0c;标准库犹如一座宝库&#xff0c;为开发者提供了丰富多样的工具和组件&#xff0c;极大地简化了开发流程&#xff0c;提升了代码效率与质量。本文将带您走进<algorithm>的世界&#xff0c;一窥…...

WSL2 中部署 Pixel Mind Decoder:Windows 开发者的 Linux 模型测试方案

WSL2 中部署 Pixel Mind Decoder&#xff1a;Windows 开发者的 Linux 模型测试方案 1. 为什么选择WSL2进行AI模型测试 对于Windows开发者来说&#xff0c;直接在原生系统上部署和测试Linux环境下的AI模型往往面临诸多挑战。依赖关系复杂、环境配置繁琐、性能损耗大等问题常常…...

船舶/无人艇/无人船,线性nomoto响应型操纵运动,回转实验和Z型实验MATLAB仿真程序(...

船舶/无人艇/无人船&#xff0c;线性nomoto响应型操纵运动&#xff0c;回转实验和Z型实验MATLAB仿真程序&#xff08;欧拉法&#xff09;今天咱们来聊聊船舶运动控制的仿真实现。以无人艇为研究对象的时候&#xff0c;线性Nomoto模型就像汽车的方向盘——虽然简化了物理特性但足…...

NumPy数组切片语法

NumPy切片基于视图而非副本的核心机制&#xff0c;掌握这个原则能避免大量坑。让我从基础到高级逐层拆解&#xff1a;一、基础语法与维度NumPy切片遵循 [start:终止:step] 的通用模式&#xff0c;不同维度用逗号分隔&#xff1a;import numpy as np# 一维数组切片 arr np.arra…...

C++编程中的迭代器失效问题解析

C编程中的迭代器失效问题解析 在C编程的世界里&#xff0c;迭代器&#xff08;Iterator&#xff09;是一种强大的工具&#xff0c;它允许程序员以统一的方式遍历容器中的元素&#xff0c;而无需关心容器内部的具体实现。然而&#xff0c;迭代器在使用过程中也伴随着一些潜在的问…...

CLIP-GmP-ViT-L-14图文匹配测试工具:在Ubuntu服务器上的生产环境部署详解

CLIP-GmP-ViT-L-14图文匹配测试工具&#xff1a;在Ubuntu服务器上的生产环境部署详解 想把那个能看懂图片的AI模型&#xff0c;也就是CLIP&#xff0c;稳稳当当地跑在自己的服务器上吗&#xff1f;特别是那个功能更强的CLIP-GmP-ViT-L-14版本。你可能在网上看过不少教程&#…...

Z-Image-Turbo-辉夜巫女提示词工程入门:从C语言逻辑到自然语言描述的转换技巧

Z-Image-Turbo-辉夜巫女提示词工程入门&#xff1a;从C语言逻辑到自然语言描述的转换技巧 如果你是一名程序员&#xff0c;尤其是熟悉C语言这类强调逻辑和结构的开发者&#xff0c;第一次接触提示词工程时&#xff0c;可能会感到一种熟悉的“别扭”。我们习惯了用 if-else 定义…...

别再只用RSA了!手把手教你用Java SM2国密算法给接口数据加个密

Java开发者必看&#xff1a;从RSA到SM2国密算法的平滑迁移实战 当我们需要在API接口或数据传输中实现非对称加密时&#xff0c;RSA往往是大多数Java开发者的默认选择。但你可能不知道的是&#xff0c;在相同安全强度下&#xff0c;国密SM2算法的计算速度比RSA快得多&#xff0c…...

【OpenClaw 全面解析:从零到精通】第 004 篇:OpenClaw 在 Linux/Ubuntu 上的安装与部署实战

系列说明&#xff1a;本系列共计 20 余篇&#xff0c;全面介绍 OpenClaw 开源 AI 智能体框架。本文为系列第 004 篇&#xff0c;聚焦于 OpenClaw 在 Linux/Ubuntu 上的安装与部署实战。建议先阅读 第 003 篇&#xff1a;OpenClaw 技术依赖与生态栈详解。 摘要 本文详细介绍在 …...