CH582M 关于特征写入回调函数的问题

尊敬的技术人员,您好,目前在开发过程中尚存在几个小问题,烦请解答,谢谢。


目前连接的步骤为:扫描 - 建立连接 - 申请2M PHY - PHY申请回调中 - 请求最大MTU - MTU回调中发现服务 - 发现服务回调中发现特征,订阅其中一个notify特征,然后处理另一个可读写特征数据。共有两个特征,一个长度180字节可读写特征,一个2字节notify特征。MTU目前设置为247。


发送大量数据时:写入特征值(共180字节的结构体序列化数据) - 设备端的写入回调被调用 - 处理数据 - 使用另一个notify特征通知手机。


现在存在的问题是:

  1. ?1.?我现在使用CH582M与安卓手机建立连接,发送大量数据,在快速发送数据过程中手机会与设备断开连接,设备显示reason 13,为手机主动断开连接,上网搜寻后得知,建立连接后要先请求高优先级,但是实际测试中发现设置优先级对这个情况没有明显改善 ( 读写操作的相关等待延迟已妥善设置 )。

  2. 2 在设备端特征被写入的回调函数中,uint8_t* pValue 【有时候】无法直接使用结构体指针反序列化,反序列化指针使用const进行约束,在反序列化之前也会对实际的数据长度 len 进行检查。 反序列化无法使用的表现为主循环停止执行了,因为一般在断开连接后串口会打印出扫描信息,连接成功会也会打印read rssi信息。而在这种情况下,串口没有任何数据输出,此时使用调试器也无法正确连接。已确认此处理代码过程中不含有无限循环等阻止CPU执行的情况,也没有出现内存越界的情况。( 目前只能 tmos_memcpy到另一个缓冲区中处理 )

  3. 3. 设备notify特征值的时候,调用的一个库函数似乎使用了动态内存分配,因为是预编译的静态库,所以不容易做出修改,可否有办法改为静态全局缓冲区分配?我尝试过改为静态分配,但是无法成功notify。

  4. 4. 在使用BLE的时候,编译出来的固件体积较大,已开启 -Os 优化、移除未使用的输入段、链接时全局优化等选项,但体积仍然较大,不知道是否能够提供静态库的源代码以供精简冗余信息?(无法提供源代码的话请忽略此条) 因为在许多官方C代码中在处理不同情况的时候会使用很多条件与循环,这部分代码对于定制化的功能来说是冗余的。

  5. 5. 因MRS的代码索引等功能较为不完善,所以目前我们使用vscode + eide插件进行开发,编译器等组件直接使用MRS下的toolchain目录,但是目前使用WCH-LINKE进行调试的时候仍需使用MRS,因为在vscode中使用cortex-debug插件无法进行调试,但是能在网络上搜索到文章,可以对CH32V20X进行调试,我按照相同的步骤选择各个参数后,无法与目标设备建立连接。因为新版的的cortex-debug插件要求GCC 9以上,所以目前使用0.43版本,不知道是否有相关配置文件提供?

  6. 6. 这里借用了官方的OTA升级工具对比吞吐量,发现官方的OTA升级工具吞吐量比目前我们的测试程序高出许多,但是在研究了官方提供的Android Studio工程源代码之后发现与我们使用的方式是一样的,我们目前的工程代码(设备端)是从?Peripheral 例程修改而来的,请参考我上面提到的步骤,帮助分析一下是否有哪些地方存在问题?而且官方的OTA代码在接收到数据时还对 flash 进行了编程,根据官方手册的参数,编程flash速度要慢得多,我们的处理代码仅进行了耗时很短的操作,且在执行此测试时,我们关闭了用户自定义的中断,仅保留必要的中断。


以上是信息描述,非常感谢。






1、建议使用默认的1M速率,使用2M速率对速度的提升并不明显,而且需要考虑主机兼容性问题;

2、数据快速发送的时候,需要考虑蓝牙本身是否来得及处理。建议配置为最小连接间隔范围6-8,MTU配置为247;

3、write/noti收发数据的时候,如果数据比较多而且需要串口输出,则需要添加环形缓冲区功能,否则可能丢失数据,直接参考ble_uart例程;

4、从机主动使能noti参考:

CH573 CH582 CH579蓝牙从机(peripheral)例程讲解五(蓝牙从机使能通知) - WCH蓝牙应用分享 - 博客园 (cnblogs.com)

从机noti发送失败,检查代码返回值是否为0。如果非0则说明函数调用存在问题,例如handle不对,如果时有成功且报错pending,则可以将协议栈分配的缓存增大;

5、MRS搜索:ctrl+f。MRS打开界面有helpManual。其他的配置参考:

MRS_开发编译与设置相关问题汇总 - WCH_CH32 - 博客园 (cnblogs.com)

6、发送数据最大化:连接间隔配置为最小(查看连接间隔协商),MTU配置为最大(前两点是速度慢的最大原因),单连接多包,缓存加大。

蓝牙BLE从机Peripheral讲解一(广播间隔和连接间隔) - SweetTea_lllpc - 博客园 (cnblogs.com)


@TECH_Lpc 谢谢回复。

请看下面帖子


@TECH_Lpc 谢谢回复。

关于第二点,最小连接间隔已经配置为6了,但是似乎吞吐量也没那么大。

3. 我原文的意思是只是在串口输出个标志,不是把所有数据都输出。这个问题已经得到解决了,调试器的错误解决了,用调试器调试了一下,发现写入回调被调用的时候,直接转指针会?

HardFault_Handler       /* Hard Fault Handler */

,查看了pValue的偏移量为MEM_BUF+4389,不知道为何没有四字节对齐,我在查看了源代码,但是调用的地方是 .a 静态库里的位置,无法查看具体原因,我可以确认所有的写操作都是4字节对齐的,不知道被调用这里为何偏移量没有对齐。+4389>

4. 我不是使用两个CH582M连接,而是使用手机连接CH582M,手机端可以正常订阅Notify,只是设备端:


attHandleValueNoti_t noti;

    if (len > (peripheralMTU - 3)) {

        PRINT("Too large noti\n");

        return;

    }

    noti.len    = len;

    noti.pValue = GATT_bm_alloc(peripheralConnList.connHandleATT_HANDLE_VALUE_NOTInoti.lenNULL0);

    if (noti.pValue) {

        tmos_memcpy(noti.pValuepValuenoti.len);

        if (simpleProfile_Notify(peripheralConnList.connHandle&noti!= SUCCESS) {

            GATT_bm_free((gattMsg_t *)&notiATT_HANDLE_VALUE_NOTI);

        }

    }


这里 GATT_bm_alloc 似乎是从蓝牙协议栈分配了动态的内存,我不知道静态库的代码怎么写的,但是推测应该是蓝牙协议栈的预分配内存按索引分配的,还是属于静态内存,但是这个使用方法似乎不是很合适,有没有办法在这个位置直接使用外部静态分配?我自己尝试使用外部静态分配,但是修改之后就无法正常Notify了。


5. Eclipse 以前经常使用,各种操作都会用,只是对比vscode,很多代码跳转查询都还是显得效率低下。现在编码和编译没有问题。只是调试无法调试,我按照搜到的一片文章?https://www.cnblogs.com/wahahahehehe/p/16896184.html?进行了配置,但是无法使用调试,配置文件如下:


{

    "version""0.2.0",

    "configurations": [

        {

            "cwd""${workspaceRoot}",

            "executable""./build/Debug/bootloader.elf"//替换对应的elf文件路径

            "name""Debug with OpenOCD",

            "request""launch",

            "type""cortex-debug",

            "servertype""openocd",

            "searchDir": [],

            "runToEntryPoint""main",

            "showDevDebugOutput"true,

            "device""CH582M",

            "svdFile""C:/MounRiver/MounRiver_Studio/template/wizard/WCH/RISC-V/CH58X/NoneOS/CH58Xxx.svd"//MRS安装目录svd

            "toolchainPrefix""D:/MounRiver/MounRiver_Studio/toolchain/RISC-V Embedded GCC/bin/riscv-none-embed"//MRS安装目录gdb, 不包含"-gdb.exe"

            "configFiles": [

                "C:/MounRiver/MounRiver_Studio/toolchain/OpenOCD/bin/wch-riscv.cfg" //MRS安装目录cfg

            ]

        }

    ]

}



6. 如上所云,已经配置为单连接多包,链接间隔也已经配置为最小了,缓存也给的很大。


7. 另外想咨询一下,有没有精简版的 .a静态库,因为我目前开发的这个固件是一个平时不参与正常功能的固件,很多的东西是可以精简掉的。


我用官方一段源代码举例:

比如


SetSysClock(CLK_SOURCE_PLL_60MHz);

会调用如下官方代码:


void SetSysClock(SYS_CLKTypeDef sc)

{

    uint32_t i;

    sys_safe_access_enable();

    R8_PLL_CONFIG &= ~(1 << 5); //

    sys_safe_access_disable();

    if(sc & 0x20)

    { // HSE div

        if(!(R8_HFCK_PWR_CTRL & RB_CLK_XT32M_PON))

        {

            sys_safe_access_enable();

            R8_HFCK_PWR_CTRL |= RB_CLK_XT32M_PON; // HSE power on

            sys_safe_access_disable();

            for(i = 0i < 1200i++)

            {

                __nop();

                __nop();

            }

        }


        sys_safe_access_enable();

        R16_CLK_SYS_CFG = (0 << 6| (sc & 0x1f);

        __nop();

        __nop();

        __nop();

        __nop();

        sys_safe_access_disable();

        sys_safe_access_enable();

        SAFEOPERATE;

        R8_FLASH_CFG = 0X51;

        sys_safe_access_disable();

    }


    else if(sc & 0x40)

    { // PLL div

        if(!(R8_HFCK_PWR_CTRL & RB_CLK_PLL_PON))

        {

            sys_safe_access_enable();

            R8_HFCK_PWR_CTRL |= RB_CLK_PLL_PON; // PLL power on

            sys_safe_access_disable();

            for(i = 0i < 2000i++)

            {

                __nop();

                __nop();

            }

        }

        sys_safe_access_enable();

        R16_CLK_SYS_CFG = (1 << 6| (sc & 0x1f);

        __nop();

        __nop();

        __nop();

        __nop();

        sys_safe_access_disable();

        if(sc == CLK_SOURCE_PLL_80MHz)

        {

            sys_safe_access_enable();

            R8_FLASH_CFG = 0X02;

            sys_safe_access_disable();

        }

        else

        {

            sys_safe_access_enable();

            R8_FLASH_CFG = 0X52;

            sys_safe_access_disable();

        }

    }

    else

    {

        sys_safe_access_enable();

        R16_CLK_SYS_CFG |= RB_CLK_SYS_MOD;

        sys_safe_access_disable();

    }

    //更改FLASH clk的驱动能力

    sys_safe_access_enable();

    R8_PLL_CONFIG |= 1 << 7;

    sys_safe_access_disable();

}



完全可以去掉非锁相环部分,这部分代码对于需要节省空间的项目来说是冗余的,这一个地方就可以节省316字节, 一个地方可以节省几十字节到几百字节的话,很多地方加起来就可以节省很多空间了。因为目前编译、汇编、链接的优化全都给了:


    "global": {

        "output-debug-info""disable",

        "arch""rv32imac",

        "abi""ilp32",

        "code-model""medany",

        "misc-control""-msmall-data-limit=8 -mno-save-restore --specs=nosys.specs --specs=nano.specs"

    },

    "c/cpp-compiler": {

        "language-c""c99",

        "language-cpp""c++11",

        "optimization""level-size",

        "warnings""all-warnings",

        "C_FLAGS""-Wl,-Bstatic -ffunction-sections -fdata-sections -fmessage-length=0 -fsigned-char -flto"

    },

    "asm-compiler": {

        "ASM_FLAGS""-Wl,-Bstatic"

    },

    "linker": {

        "output-format""elf",

        "LD_FLAGS""-Wl,--cref -Wl,--gc-sections -nostartfiles",

        "LIB_FLAGS""-lISP583 -lCH58xBLE",

        "remove-unused-input-sections"true,

        "$toolName""auto"

    }


8. 还有请问关于外挂 spi nor-flash 的映射有资料吗?在内部flash不够使用的情况下是否可以直接执行外挂flash中的代码呢?


9. 关于PIC代码似乎无法使用。我写了一个工具固件,总大小为2020字节,我将此固件用C语言数组的形式嵌入到另一个固件中,在运行时,将二进制数据使用写入到flash+6F000h中,然后跳转运行,固定起始地址是可以使用的,但是我在编译时加入参数 -fPIC 编译成位置无关代码却不可以使用,请教一下这是为什么呢?另外我尝试了几种方式将PIC代码放入RAM,直接在RAM中执行: 


 (1) 使用__HIGH_CODE ,此方式无法实现正常执行。

 (2) 使用直接执行,直接将存储工具固件的数组首地址强制转换为函数指针,然后跳转执行,此方法也无法执行(此方法应该属于flash执行)。

 (3) 新建4字节对齐缓冲区,将数据tmos_memcpy一份到缓冲区,再尝试执行,也无法执行。


以上尝试方式所使用的工具固件均是使用 -fPIC 参数编译的位置无关代码。关于以上问题,还请赐教,非常感谢。


更新帖子,空白字符会被显示成 ? ,所以重新发一遍


忘了说了,mstatus 寄存器已经修改了,固件是有权限控制CPU跳转的,但是PIC代码没有成功执行



1、速度问题:BLE传输的速度:最小间隔7.5ms,MTU最大247,尝试将单连接多包开启,这样速度可以达到30+KB/s,注意需要在近距离的条件下,否则可能出现丢包的情况。直接参考speedtest例程查看测试的速度。

如果有BLE转串口/USB等的功能,则速度会下降,可以参考BLE_UART例程,MTU开启最大进行测试。

2、noti发送失败问题:在数据收发的时候需要进行内存分配,也就是从协议栈里面开辟一段堆用于数据的发送。noti会用到此功能。如果noti发送失败,查看返回值,最明显的现象是pending,这需要将协议栈缓存进一步扩大,或者直接在出现pending后重复调用发送即可。出现pending是可能的情况。

如果是其他的报错,检查代码是否参数传递错误,如handle。

如果是直接进入hardfault,打印PC指针查看报错,如执行noti四字节对齐问题。则查看自行分配的缓存四字节对齐:__attribute__((aligned(4))) uint32_t MEM_BUF[BLE_MEMHEAP_SIZE / 4];

3、使用其他编译器:我们真诚的邀请用户对芯片做客制化的开发。但同时需要注意官方提供的开发工具只有MRS,如使用MRS出现问题可以留下反馈。针对使用vscode问题,后续如果提供类似开发工具会第一时间提供或者在该工程师博客留言获取解决办法。

4、CH582的精简库只有从机功能,可以参考固定库:image.png

如果需要其他的精简库,发送邮件至邮箱:lpc@wch.cn,描述具体项目和应用,我们进行评估。

针对提到的代码冗余,EVT代码是为了适用所有开发者。

5、外挂FLASH可以参考V208的外挂FLASH。外挂FLASH可以用于存储信息,但是无法用于运行代码。

CH32V20xEVT.ZIP - k1体育 - 十年品牌 值得信赖 (wch.cn)

6、单片机没有动态链接,无法使用fPIC。



@TECH_Lpc 好的,我和同事整理一下资料然后发邮件过去。




只有登录才能回复,可以选择微信账号登录