CH585快速开始指南

01 快速了解 CH585

CH585 是一款符合 BLE 5.4 规范的无线 RISC-V MCU/SoC,片上集成 2Mbps 的 BLE 通讯外设,集成 NFC 通讯外设,集成高速 USB 控制器及收发器;SRAM 容量达 128KB,提供 512KB Flash,支持 OTA 无线升级;提供 UART、SPI、I2C、ADC、触摸按键、TIM、PWM、段码 LCD、LED 点阵屏接口等丰富的外设接口。

CH585 功能框图

CH585 采用自主可控的青稞(QingKe)V3C 型内核,支持 RV32IMBC 标准指令集,及沁恒提供的 XW 自定义指令集扩展,相较一般的 RISC-V 内核微控制器更加实用、代码密度更高。结合微控制器常用的应用场景,沁恒自主研发的 RISC-V 青稞内核针对性进行了多种优化与改进,支持硬件内存复制加速,支持免查表中断,支持中断自动硬件压栈,实现优秀的中断处理性能。

CH585 作为一款高集成度的无线 MCU/SoC,具有以下优秀特性:

  • USB 2.0 高速:内置一组 480Mbps 的 USB 2.0 高速控制器及 PHY,支持高/全速 Host 主机与 Device 设备模式,支持 1024 字节数据包与 DMA;另提供一组全速 USB 控制器及 PHY,支持全/低速 Host 与 Device 模式。借助高速 USB,芯片可直接与 PC、手机等设备进行高带宽的数据交换,无需外挂桥接芯片。
  • NFC 近场通讯:集成 NFC 收发器与兼容 ISO14443-A 的无线解码基带,同时支持读卡器(PCD)与卡(PICC)两种模式。PCD 模式支持以 106kbit/s 波特率对 ISO14443-A 类卡片进行操作,并支持多卡防碰撞;PICC 模式可模拟 ISO14443-A 类卡片,支持 NDEF 数据交互,可实现电子名片、蓝牙 OOB 配对等与手机的智能联动。
  • BLE 5.4:符合 Bluetooth Low Energy 5.4 规范,支持 2Mbps 与 1Mbps 物理层,接收灵敏度为 -95dBm@1Mbps(-93dBm@2Mbps),发送功率可在 -20dBm~+4.5dBm 范围内编程调节并支持动态调整;提供优化的协议栈与应用层 API,支持 Mesh 组网与蓝牙空中升级(OTA)。
  • 2.4G 高上报率:除 BLE 外,芯片还支持私有的 2.4G 无线模式,最高支持 8KHz 的无线上报率,适合无线键鼠、游戏外设等追求高上报率的无线场景。
  • 段式 LCD 驱动:内置段式 LCD 控制器,可直接驱动 112 个点(28×4)的段式 LCD 屏幕而无需外挂驱动 IC,支持 3.3V 与 2.5V 两档驱动电压、1/2~1/4 duty 与 1/2~1/3 bias,扫描频率可调,便于为仪表、面板类产品实现低功耗显示。
  • 充裕的存储资源:提供 128KB SRAM 与 512KB Flash,其中 SRAM 支持按 96KB+32KB 分块睡眠保持。BLE 协议栈、USB 高速大包缓冲、NFC 软解码等与用户应用可从容共存,深度睡眠亦不丢失数据。

02 硬件准备

在开始使用评估板前,首先需要下载评估板的资料包:https://www.wch.cn/downloads/CH585EVT_ZIP.html

下载后将其解压在方便找到的位置,以备使用。

02.1 CH585M-EVT-R1 硬件接口速览

CH585M-EVT-R1 评估板如下图所示:

CH585M-EVT-R1 评估板实物图与注解

该评估板搭载了蓝牙天线、NFC 天线、USB 接口、LED 指示灯等组件,并通过排针引出了全部 IO 端口,可用于客户进行功能评估验证、原型开发等用途。

CH585M-EVT-R1 评估板的原理图位于资料包的 EVT/PUB/CH585SCH.pdf 处。

02.2 各接口功能介绍

评估板的各部件功能介绍如下表:

部件 功能说明
USB 电源开关 切断或接通 5V 供电
USB 全速接口 连接芯片全速 USB 外设的接口
USB 高速接口 连接芯片高速 USB 外设的接口
RESET 按键(复位键) 外部手动复位(需先使能芯片的手动复位功能),连接到PB23
DOWNLOAD 按键(下载键) 上电时按住可进入 ISP 状态,连接到PB22
电源指示灯 电源指示,由 3.3V 网络供电
LED 指示灯 用户 LED,可按需连接到 GPIO,低电平有效

该评估板没有板载 USB 转串口芯片。下载固件可直接通过芯片自带的 USB 接口完成(详见 04.3 节);如需查看例程的串口日志打印,请自备一个 USB 转串口设备(例如基于 CH343 主芯片的 USB 转 TTL 小板或线缆),按附录中所述(除非例程中有特别说明或修改了默认的串口引脚)接线。

CH585M-EVT-R1 评估板的左侧区域为 NFC 天线,此区域包括一个 13.56MHz 的 NFC 线圈天线及其匹配电路,NFC 相关例程(EVT/EXAM/NFCA)均基于该天线工作。

注意:使用 NFC 功能时,应避免让金属物体贴近或遮挡天线区域(包括评估板的背面,若使用金属桌面请使用空纸盒等材料垫起评估板),以免影响读卡距离与检卡灵敏度。
同样地,在使用无线通讯功能时,也应避免让金属物体贴近或遮挡天线区域。

03 开发环境搭建

03.1 下载并安装 MounRiver Studio Ⅱ

为了便于一站式开发沁恒 RISC-V 内核的 MCU 产品,推荐使用 MounRiver Studio Ⅱ IDE 作为开发环境。MounRiver Studio Ⅱ(下简称 MRS2)是基于 VS Code 进行深度定制化开发打造的 RISC-V MCU 集成开发环境,内置预先配置好的 RISC-V GCC 工具链、下载工具、串口助手、开发辅助工具等各类功能,为用户提供开箱即用的开发环境。MRS2 完全免费,商用无盗版风险,且无需手动配置工具链、构建系统等即可进行开发。

MRS2 可从官方网站进行下载:https://www.mounriver.com/download

下载完成后,可直接双击打开压缩包中的 MounRiver_Studio_Setup_Vxxx.exe 启动安装程序。

安装 MounRiver Studio Ⅱ 开发环境

安装过程中可能会提示安装驱动,这是为了使用 USB 方式为 MCU 下载固件,以及与 WCH-LinkE 调试器通讯所需要的。

MRS2 安装后,默认为英文界面,可通过如下菜单切换为中文界面:

切换 MounRiver Studio Ⅱ 的界面语言

03.2 下载 CH585EVT 例程与资料包

CH585 的例程、库文件等资料均包含在 CH585EVT 资料包中,建议下载后将其解压在方便找到的位置,方便查阅使用。

以下对资料包的内容进行简单介绍,完整说明请查阅资料包中的 EVT/CH585_List.txt 文件说明。

  • 评估板的相关资料存放在 EVT/PUB 目录下;
  • CH585 的例程按外设分类存放在 EVT/EXAM 目录下;
    • 其中蓝牙相关例程位于 EVT/EXAM/BLE 目录下;
    • 库函数文件位于 EVT/EXAM/SRC 目录下;

04 快速开始第一个蓝牙例程

在蓝牙开发当中,BLE_UART 是最为常用的基础工程,常在此工程上继续开发其它功能,因此本指南将以此工程为例。为了进行串口数据的收发,还需要准备一个 USB 转串口设备,如基于 CH343 的 USB 转 TTL 串口线等,串口电平应当为 3.3V。

04.1 打开工程

启动 MRS2,并打开 CH585 资料包 EVT/EXAM/BLE/BLE_UART 中的工程。

或者,也可以在 EVT/EXAM/BLE/BLE_UART 目录下找到 BLE_UART.wvproj 工程文件,该文件格式已关联 MRS2,支持双击打开。

04.2 一键编译

工程打开后,点击工具栏的“重新编译”按钮,编译成功的输出日志如图所示:

编译例程代码

编译产物默认位于工程的 obj/ 目录下,包括一个 HEX 格式的固件,一般将此文件下载到芯片中即可。

提示:
工具栏中有三个编译按钮,从左到右分别是“增量编译”、“重新编译”和“增量编译并下载”。一般来说,当在这台计算机上第一次打开该工程时,或是“增量编译”遇到异常问题时,推荐使用“重新编译”,此选项会清除原有构建产物,并重新进行完整的编译过程。后续开发过程中,可使用“增量编译”以节省编译时间。当使用 WCH-LinkE 方式下载固件时,可使用“增量编译并下载”,在编译完成后会自动进行下载。
为了方便用户快速测试程序而不必先编译工程,资料包中的例程均包含一个已编译完成的 HEX 格式固件。
若不需要修改例程的代码,可直接下载此例程测试。

04.3 连接硬件并下载固件到评估板

编译生成固件后,下一步将固件下载到评估板中。CH585 内置出厂固化的 Bootloader,支持 USB 下载与串口(UART)下载两种 ISP 方式,无需额外购买下载器或仿真器。ISP 方式下载速度快、操作简单,受用户程序影响小,且同样适合量产使用。

两种下载方式的说明如下表所示:

下载方式 通道 / 引脚 说明
USB 下载 USB 全速口(FS)或 USB 高速口(HS) 推荐方式,速度快、操作简单
串口下载 UART1(PA8/PA9)或 UART0 重映射(PA14/PA15) 波特率 115200 至 3M,支持冷启动免按键下载

无论使用哪种下载方式,操作前都需要先让芯片进入 ISP 状态。芯片仅在上电复位(POR)后才会检测 BOOT 脚的电平,CH585 的默认 BOOT 脚为 PB22(低电平有效),评估板上的 DOWNLOAD 按键即连接至该引脚,按下时将其拉低。因此,进入 ISP 状态的关键在于“先确保芯片完全掉电,再按住 DOWNLOAD 按键上电”,即所谓的冷启动。若用户在 WCHISPStudio 中配置了其他 BOOT 脚,下次需将对应引脚置于有效电平才能进入 ISP 状态。

若要进入 ISP 状态,以下载程序或开启调试接口时,请按以下步骤操作:

  1. 关闭 USB 电源开关,使 CH585 完全掉电;
  2. 按住 DOWNLOAD 按键不松开;
  3. 打开 USB 电源开关(冷启动,产生上电复位);
  4. 松开 DOWNLOAD 按键,芯片即停留在 ISP 状态中,等待下载或其他操作。

补充说明:
使用 RESET 按键复位无法进入 ISP 状态,且 RESET 按键是否生效,取决于上次下载时是否将使能 RST 作为手工复位输入引脚选项选中。
芯片进入 ISP 状态后,若 10 秒内未被 WCHISPStudio 软件识别到,或 60 秒内未进行下载、校验等任何操作,将自动退出 ISP 状态,尝试执行芯片内的用户代码。
若芯片 Flash 的前 4K 为擦除后的空白值(如全新芯片或整片擦除后),上电后将自动停留在 ISP 状态,无需按住 DOWNLOAD 按键。

USB 下载是主要推荐的下载方式,直接使用评估板上的 USB 接口即可。在不便使用 USB 的场合,也可以通过串口下载。CH585 支持两组串口下载通道:

  • UART1:PA8(RXD)/ PA9(TXD);
  • UART0 重映射:PA14(TXD)/ PA15(RXD)。

串口下载使用的波特率为 115200、1M、3M,推荐使用 CH343 等高性能 USB 转串口芯片,可选择高波特率以加快下载速度。

具体操作步骤如下:

  1. 打开 WCHISPStudio,单击右侧的低功耗蓝牙系列按钮,打开对应工具。选择芯片型号,选中 USB 或 串口 下载接口,并在目标程序文件项中载入编译生成的 .hex 或 .bin 固件,按需要设置下载配置,如图所示。(串口方式下,波特率默认为 115200,若使用了 CH343 等高速 USB 转串口,可选择更高波特率以提高下载速度)
    USB 下载:第一步
  2. 若使用USB下载,如图所示,用 USB 数据线将评估板的 USB 接口(全速 FS 或高速 HS) 连接到 PC 并按上述步骤冷启动进入 ISP 状态后,PC 设备管理器中会出现一个 USB Module 设备(需要安装 CH372 驱动,若 MRS2 未能自动安装成功,驱动安装方法见 常见问题 Q3。)。
    若使用串口下载,在 WCHISPStudio 中选择对应的 COM 口,将 USB 转串口设备按 TXD 接 PA15、RXD 接 PA14(注意必须为 3.3V TTL 电平,切勿接入 RS232 电平)方式连接到评估板的对应针脚,同时将串口设备的 GND 连接到评估板的 GND 针脚,按上述步骤冷启动进入 ISP 状态。
    USB 下载:第二步
  3. 在 WCHISPStudio 中,点击下载按键即可完成擦除、写入与校验的全部操作,如图所示。
    USB 下载:第三步

小技巧:若使用 USB 下载,可以在第一步就勾选设备连接后自动下载选项,当一个处于 ISP 状态的 CH585 芯片通过 USB 连接到计算机后,WCHISPStudio 会自动识别到并进行下载,而不需要额外手动点击操作。此方法更加方便,可用于人工小批量烧录固件时简化操作。

关于串口免按键下载

串口下载时支持冷启动免按键下载:芯片掉电后重新上电时,会在约 40ms 的窗口内等待串口下载握手信号,若握手成功则自动进入 ISP 状态,无需按 DOWNLOAD 按键。此方法在使用脱机烧录器进行量产时非常方便,可以无需引出 BOOT 引脚信号,仅使用4个预留测试点即可。

注意:此方法需要先启动下载,再为目标芯片上电,因此要求在串口设备能被计算机识别和发送数据时,目标芯片保持掉电,以确保上电复位能够进入 ISP 状态或免按键下载能够正常握手。由于 CH585 功耗较低,需小心可能会出现串口通过 IO 漏电使 CH585 启动运行的情况,这会导致 CH585 无法产生上电复位。

串口免按键下载的大部分操作步骤与 USB 下载相似,以 UART0 重映射(PA14/PA15)为例:

  1. 打开 WCHISPStudio,单击右侧的低功耗蓝牙系列按钮,打开对应工具。选择芯片型号,选中 串口 下载接口,并在目标程序文件项中载入编译生成的 .hex 或 .bin 固件,按需要设置下载配置。
  2. 将 USB 转串口设备按 TXD 接 PA15、RXD 接 PA14 方式连接到评估板的对应针脚,同时将串口设备的 GND 连接到评估板的 GND 针脚。在 WCHISPStudio 中选择对应的 COM 口,在给评估板上电之前点击下载按键。
  3. 重新为评估板上电,此时工具会自动尝试免按键下载,若未成功,可改用按住 DOWNLOAD 按键重新上电的方式手动进入 ISP 状态。
  4. WCHISPStudio 会自动开始完成擦除、写入与校验的全部操作。

04.4 手机与评估板通过 BLE 通讯体验

CH585 的 BLE_UART 例程使用的数据串口为 UART3,即 PA4 为 RXD、PA5 为 TXD。将 USB 转串口设备按 TXD 接 PA4、RXD 接 PA5 方式连接到评估板的对应针脚,默认波特率为 115200。

若需观察日志,则另外使用一个 USB 转串口设备,按 RXD 接 PA14 方式连接到评估板的对应针脚,默认波特率为 115200。

MRS2 中内置了多种开发实用工具,可从菜单栏打开串口调试工具以观察日志或在数据串口上收发数据。

从 MRS2 中打开串口调试工具

为方便测试,需要使用一台手机打开 BLE 调试 APP 并通过蓝牙连接评估板。BLE 调试 APP 推荐使用 WCH 的 BLEAssist 调试工具(安卓平台)或 LightBlue(iOS 平台)。下载方法:安卓平台可从 https://www.wch.cn/downloads/BLEAssist_ZIP.html 下载解压后安装;iOS 平台可在 App Store 中搜索 LightBlue 软件并安装。

体验步骤如下(以安卓平台为例):

  1. 手机打开安装好的 BLE 调试助手(可能需要授予相应权限),搜索并连接广播名称为 ble_uart 的设备。
    BLE 例程体验:第一步
  2. 在手机上 BLE 调试助手中发送字符串 Hello CH585,PC 串口调试工具即时打印接收到的内容。
    BLE 例程体验:第二步 手机操作
    BLE 例程体验:第二步 PC 操作
  3. 在手机上 BLE 调试助手中启动 NOTIFY 接收通知数据,在 PC 上通过串口调试工具向数据串口发送字符串 Hello Phone,手机 BLE 调试助手界面即时收到内容(包括先前发出的内容)。
    BLE 例程体验:第三步 手机操作
    BLE 例程体验:第三步 PC 操作

小技巧:若周围的 BLE 设备较多,可通过过滤功能使 BLE 调试助手只显示广播名称含有特定字符的设备,如图所示。
BLE 例程体验:设置过滤

05 进阶开发:如何基于例程修改代码

05.1 认识 BLE_UART 的代码结构

CH585EVT 资料包中,BLE_UART 工程的目录结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
BLE_UART/
├── APP/ # 应用层代码
│ ├── peripheral_main.c # 程序入口:系统初始化与主循环
│ ├── peripheral.c # 应用任务:广播/连接事件、串口收发、透传逻辑
│ ├── include/peripheral.h # 应用任务的头文件
│ ├── ble_uart_service/ # 透传 GATT 服务(含多个版本,默认选择 16bit UUID 版)
│ └── app_drv_fifo/ # 环形 FIFO 缓冲区组件
├── Profile/ # 通用 GATT 服务
│ ├── gattprofile.c # 简单 GATT 服务(Simple GATT Profile)
│ └── devinfoservice.c # 设备信息服务(DIS)
├── ../HAL/ # 各例程共用的硬件抽象层(RTC、睡眠、按键、LED)
│ └── include/CONFIG.h # BLE 协议栈全局配置宏(见 05.2 节)
├── ../LIB/ # BLE 协议栈库文件及头文件(CH58xBLE_LIB.h 等)
└── obj/ # 构建产物

其中,以下四部分是在开发中需要重点关注的:

程序入口:APP/peripheral_main.c

在程序的 main() 函数中,按顺序完成全部初始化,然后进入主循环,其中执行 TMOS 调度器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main(void)
{
#if(defined(DCDC_ENABLE)) && (DCDC_ENABLE == TRUE)
PWR_DCDCCfg(ENABLE); // 使能片内 DCDC 供电(取决于工程配置)
#endif
HSECFG_Capacitance(HSECap_18p); // 配置外部高速晶振的负载电容,对应约 10pF 负载电容的晶振
SetSysClock(SYSCLK_FREQ); // 配置系统时钟
...
#ifdef DEBUG
... // DEBUG 宏开启时,初始化日志串口 UART0(PA14/PA15)
#endif
PRINT("%s\n", VER_LIB); // 打印协议栈版本号
CH58x_BLEInit(); // 初始化 BLE 协议栈
HAL_Init(); // 初始化硬件抽象层(为 TMOS 运行做硬件初始化)
GAPRole_PeripheralInit(); // 初始化 GAP 外设(Peripheral)角色
Peripheral_Init(); // 初始化应用任务:注册事件、配置广播数据
app_uart_init(); // 初始化数据串口 UART3 及收发 FIFO
Main_Circulation(); // 进入主循环
}

主循环中只有两个函数,分别是 TMOS 任务调度与串口处理:

1
2
3
4
5
6
7
8
void Main_Circulation()
{
while(1)
{
TMOS_SystemProcess(); // 运行 TMOS 任务调度器,BLE 协议栈事件均在此完成
app_uart_process(); // 串口发送处理,并触发 TMOS 事件从蓝牙发出串口收到的数据
}
}

应用任务:APP/peripheral.c

这是蓝牙相关代码的主要落脚点,核心内容包括:

  • 广播与 GAP 参数:advertData[](广播数据)、scanRspData[](扫描响应数据,含设备名)、DEFAULT_ADVERTISING_INTERVAL(广播间隔)等常量定义;
  • Peripheral_Init():注册应用任务,配置广播数据、广播间隔、配对参数,添加 GATT 服务;
  • Peripheral_ProcessEvent():任务事件处理函数,处理 SBP_START_DEVICE_EVT(启动设备)、SBP_PARAM_UPDATE_EVT(连接参数更新)、UART_TO_BLE_SEND_EVT(串口至蓝牙的数据发送)等事件;
  • peripheralStateNotificationCB():GAP 角色状态回调,在广播、连接建立、断开等状态变化时被调用;断开连接后在 Peripheral_LinkTerminated() 中自动重新开启广播。

同时,串口 3 的 IRQ 处理函数也位于此文件中:

  • UART3_IRQHandler():数据串口接收中断,将收到的字节写入接收 FIFO。

串口透传服务:APP/ble_uart_service/

默认的串口透传服务选择 ble_uart_service_16bit.c 中的实现,使用 16bit UUID,包含两个特征值:

  • RX 特征(0xFFF2,Write Without Response):手机至设备方向的数据写入,对应”蓝牙收、串口发”;
  • TX 特征(0xFFF1,Notify):设备至手机方向的通知,对应”串口收、蓝牙发”。

服务 UUID 0xFFF0 是 BLE UART 透传类应用常用的自定义服务,许多 BLE 串口助手也内置了该 UUID 的解析模板,可直接收发数据。

提示:该目录下提供了多个版本的服务实现,通过 MRS2 工程配置的排除编译选项来选择 ble_uart_service_16bit.c 实现,其余三个文件默认不使用,其差异点如下:
ble_uart_service.c:使用 128bit UUID
ble_uart_service_same_16bit_char.c:使用 16bit UUID,且收发共用同一特征值
ble_uart_service_same_char.c:使用 128bit UUID,且收发共用同一特征值

当手机写入数据或开关 Notify 时,协议栈回调 on_bleuartServiceEvt() 并传递相应的事件信息,其中 BLE_UART_EVT_BLE_DATA_RECIEVED 即为收到手机数据对应的事件。

数据流向

串口与 BLE 的双向透传由两个环形 FIFO 缓冲区桥接,整体数据流如下:

串口到蓝牙方向:

  • UART3 收到数据,在 IRQ 处理函数中装入 app_uart_rx_fifo 缓冲区,设置标志;
  • app_uart_process() 函数检测到标志,触发 UART_TO_BLE_SEND_EVT 事件;
  • TMOS 任务处理事件,调用 ble_uart_notify() 通过 Notify 将数据发送到已连接的手机。

蓝牙到串口方向:

  • 手机通过 Write 将数据发送到 CH585;
  • CH585 TMOS 任务处理蓝牙事件,在 on_bleuartServiceEvt() 回调函数中判断事件类型;
  • 事件类型为收到蓝牙数据,将收到的数据装入 app_uart_tx_fifo 缓冲区;
  • app_uart_process() 函数判断 UART3 的发送 FIFO 有空闲,从 app_uart_tx_fifo 缓冲区取出数据装入 UART3 的发送 FIFO。

提示:例程的数据串口为 UART3(RXD@PA4、TXD@PA5),如需改用其他串口,需要修改 peripheral.c 中 app_uart_init()、app_uart_process() 与中断服务函数中的相关代码。

05.2 动手尝试:修改硬件相关配置和无线发射功率

BLE 例程的许多硬件相关行为并不在业务代码实现中硬编码,而是集中在配置文件 EXAM/BLE/HAL/include/CONFIG.h 中以宏的形式配置。该文件的开头注释列出了全部可配置项(具体可用性以实际有的宏为准),包括:

配置宏 作用 默认值
DCDC_ENABLE 是否使能片内 DCDC 供电 FALSE
HAL_SLEEP 是否开启睡眠功能 FALSE
CLK_OSC32K RTC 低速时钟源选择:0 = 外部 32768Hz 晶振;1 = 内部 RC 32000Hz;2 = 内部 32768Hz 1
BLE_MEMHEAP_SIZE 协议栈使用的 RAM 大小 6KB
BLE_BUFF_MAX_LEN 单连接最大包长(影响 MTU 上限) 27
BLE_TX_POWER 无线发射功率 LL_TX_POWEER_0_DBM(0dBm)
PERIPHERAL_MAX_CONNECTION 最多同时保持的从机连接数 1

CONFIG.h 中所有宏均以 #ifndef ... #define 形式给出默认值。由于例程中所有 BLE 工程共享同一份 CONFIG.h 副本,修改后可能与其它工程预期的行为不同,因此直接使用例程时推荐在工程的预定义宏(预处理符号)中覆盖它们,而不是直接改动 CONFIG.h 文件本身。具体添加方法可参见例 1。当然,直接修改 CONFIG.h 中的默认值同样可行,只是要注意该文件被 EXAM/BLE 下的所有蓝牙例程共用。

以下给出蓝牙开发中常用的参数修改的例子。

例 1:修改无线发射功率至 +4dBm

发射功率由 BLE_TX_POWER 宏决定,可选择的取值范围定义在 EXAM/BLE/LIB/CH58xBLE_LIB.h 中,可在 -20dBm ~ +4dBm 之间选择(精度约 ±2dBm):

1
2
3
4
5
6
7
#define LL_TX_POWEER_MINUS_20_DBM       0x01
#define LL_TX_POWEER_MINUS_15_DBM 0x03
...
#define LL_TX_POWEER_0_DBM 0x15 // 默认 0dBm
...
#define LL_TX_POWEER_3_DBM 0x2B
#define LL_TX_POWEER_4_DBM 0x3B // 最大 +4dBm

例如将发射功率设置为 +4dBm,可如下修改 CONFIG.h 中的默认值:

1
2
3
#ifndef BLE_TX_POWER
#define BLE_TX_POWER LL_TX_POWEER_4_DBM
#endif

修改后重新编译并下载即可生效。提高发射功率可增大通讯距离,但会相应增加功耗;在满足通讯需求的前提下,建议使用能够满足应用需求的最低功率档位。

05.3 动手尝试:修改 BLE 广播名称与广播间隔

例程的设备名出现在 APP/peripheral.c 的两处,修改时需保持两处一致:

第一处是扫描响应数据,位于 scanRspData[],手机扫描列表里看到的名称即来自这里:

1
2
3
4
5
6
static uint8_t scanRspData[] = {
// complete name
9, // 该项目的字节数,改名后务必同步修改
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
'b', 'l', 'e', '_', 'u', 'a', 'r', 't',
...

例程默认广播名为 ble_uart。若要改为 MyBLEDevice(11 个字符),则修改为:

1
2
3
4
5
6
static uint8_t scanRspData[] = {
// complete name
12, // 该项目的字节数,改名后务必同步修改
GAP_ADTYPE_LOCAL_NAME_COMPLETE,
'M', 'y', 'B', 'L', 'E', 'D', 'e', 'v', 'i', 'c', 'e',
...

注意 AD 结构遵循”长度 + 类型 + 数据”格式,第一个长度字节包含 AD 类型字节本身,即长度 = 名称字节数 + 1(参见其他 AD 项:连接间隔范围 4 字节数据对应长度 0x05,发射功率 1 字节数据对应长度 0x02)。改名后若忘记同步修改长度,会导致广播数据解析异常。

第二处是 GATT 设备名,位于 attDeviceName[]。在连接建立后,主机读取到的设备名(GAP 特征中的 Device Name)来自此数组:

1
static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "ble_uart";

将其改为 "MyBLEDevice" 即可。修改后重新编译并下载即可生效,若没有变化可关闭 BLE 调试助手再打开以消除旧数据残留。

广播间隔由 peripheral.c 中的宏定义控制,单位为 625μs:

1
2
// What is the advertising interval when device is discoverable (units of 625us, 80=50ms)
#define DEFAULT_ADVERTISING_INTERVAL 160 // 160 × 625μs = 100ms

该值在 Peripheral_Init() 中被同时写入广播间隔的最小值与最大值(TGAP_DISC_ADV_INT_MIN / TGAP_DISC_ADV_INT_MAX)。将其改为 1600 即广播间隔变为 1s。

广播间隔的取舍:间隔越短,主机越容易快速扫描到设备、连接建立越快,但广播功耗越高;间隔越长越省电,但被发现所需的时间也更长。BLE 规范允许的广播间隔范围为 20ms ~ 10.24s,产品量产时一般建议取 100ms ~ 1s 之间。

05.4 动手尝试:添加一个定时打印日志功能

在产品开发中,经常会遇到“按给定间隔执行某段代码”的需求,例如定时打印日志、周期读取传感器、定时上报状态等。在 BLE 工程里,这类周期性任务不能用一般的阻塞式延时来实现,而应使用 TMOS 的周期性触发事件机制。下面以“每 1 秒打印一行日志”为例进行演示。

为什么不能用阻塞式 delay?这似乎是最容易想到的方法,在主循环里这样写:

1
2
3
4
5
6
7
8
9
10
void Main_Circulation()
{
while(1)
{
mDelaymS(1000); // 使用一般的阻塞式延时
PRINT("hello\r\n");
TMOS_SystemProcess(); // 轮询调度器
app_uart_process(); // 串口处理
}
}

这样虽然能实现“每秒打印一次”,却会带来严重问题:芯片库函数 mDelaymS() 在延时等待时完全占用 CPU 资源,在它执行的 1 秒内,TMOS_SystemProcess() 完全得不到运行机会,这将导致 TMOS 管理的所有事件(包括 BLE 所需要的广播、连接参数更新、数据收发、连接超时监控等)都会被挂起。而 BLE 中的监督超时(Supervision Timeout)机制要求,若从机在约定的时间内没有响应主机,主机会判定连接丢失并主动断开。

在例程中,默认的 DEFAULT_DESIRED_CONN_TIMEOUT 为 100 × 10ms = 1s,也就是说,如果蓝牙事件超过 1 秒得不到响应,主机就会认为从机失去响应,直接断开连接,表现为设备频繁无故掉线或无法连接。

而 TMOS 的周期事件触发机制可以优雅地解决这个问题。TMOS 会按照给定的周期触发给定的事件,当该任务被 TMOS 调度到后就会执行对应事件的处理代码。这避免了为了等待指定时间间隔而使用阻塞式延时的问题。使用周期性任务并不复杂,方法如下:

第 1 步:定义一个新的 TMOS 事件

在 TMOS 中,事件的定义使用一个 16 位的位掩码(bit mask),每个任务最多可定义 15 个用户事件(0x0001 ~ 0x4000),其中 0x8000(SYS_EVENT_MSG)被系统保留用于消息投递,不要占用。每个事件只能占用一个位,因此新增事件时,取一个与已有事件不冲突的位即可。

打开 APP/include/peripheral.h,在已有的事件定义下方新增一行:

1
2
3
4
5
6
// Peripheral Task Events
#define SBP_START_DEVICE_EVT 0x0001
#define SBP_READ_RSSI_EVT 0x0004
#define SBP_PARAM_UPDATE_EVT 0x0008
#define UART_TO_BLE_SEND_EVT 0x0010
#define SBP_PERIODIC_EVT 0x0020 // 新增:周期打印日志事件
小技巧:如果怕不小心写错,可以把事件的定义写成 (1<<x) 的形式,其中 x 表示第几个事件,取值的范围是 0 到 14。

第 2 步:在初始化过程中启动周期任务

tmos_start_reload_task() 是 TMOS 的周期性触发接口,调用后会在 time 个 tick 后触发一次事件,并且会每过 time 个 tick 再次触发事件,如此往复。

打开 APP/peripheral.c,在 Peripheral_Init() 函数末尾(tmos_set_event(Peripheral_TaskID, SBP_START_DEVICE_EVT); 之后,只要确保事件所在的任务已经被创建)添加一行:

1
2
// 每 1 秒触发一次 SBP_PERIODIC_EVT;tmos_start_reload_task 触发后会自动重新装载定时器,实现周期性
tmos_start_reload_task(Peripheral_TaskID, SBP_PERIODIC_EVT, MS1_TO_SYSTEM_TIME(1000));

与之类似的还有另外两个常用接口:

TMOS 事件接口 行为
tmos_set_event(taskID, event) 立即触发一次
tmos_start_task(taskID, event, time) 延迟 time 个 tick 后触发一次
tmos_start_reload_task(taskID, event, time) 延迟 time 个 tick 后触发,并自动周期性重复触发

time 参数是 tmosTimer 类型的 tick 数,每个 tick 为 625μs(即宏 SYSTEM_TIME_MICROSEN),与 05.3 节广播间隔的时间基准一致。为了避免手算 tick 数的不便,库提供了换算宏 MS1_TO_SYSTEM_TIME(x),将 x 毫秒自动换算为对应的 tick 数。本例中的 MS1_TO_SYSTEM_TIME(1000) 表示 1 秒触发一次;若想改为 500ms,把参数换成 MS1_TO_SYSTEM_TIME(500) 即可。

第 3 步:处理对应的事件

在 Peripheral_ProcessEvent() 函数中,仿照已有事件的处理方式,在 UART_TO_BLE_SEND_EVT 分支之后新增一个分支:

1
2
3
4
5
6
7
if(events & SBP_PERIODIC_EVT)
{
static uint16_t tick = 0;
PRINT("Periodic tick: %d\r\n", ++tick);
// 此处可执行所需的周期性代码
return (events ^ SBP_PERIODIC_EVT);
}

编译下载验证

重新编译并下载固件(方法见 04.3 节),用串口调试工具观察日志(接线方法见 04.4 节),可以看到每 1 秒打印一行:

周期性打印日志

同时用手机 BLE 调试助手连接设备,透传收发数据一切正常,定时任务不影响蓝牙功能的正常运行。

一些说明

  • 事件处理完要像例程中其他分支一样,用 return (events ^ SBP_PERIODIC_EVT) 把本次事件位清除,否则该事件会一直被判定为“已就绪未处理”,导致任务被反复调度;
  • 调用 tmos_start_reload_task 触发的事件会被自动地周期性触发,无需(也不应)在处理函数里再次调用 tmos_start_reload_task,否则事件周期会越走越快;
  • 事件处理代码中不要做耗时操作(如长延时、大块 Flash 擦写),单次执行时间最好不超过连接间隔的一半,以免影响蓝牙通讯。

其他关于 TMOS 使用的内容,可参见《沁恒低功耗蓝牙软件开发参考手册》,位于资料包的 EVT/EXAM/BLE/沁恒低功耗蓝牙软件开发参考手册.PDF 处。

注意:TMOS 调度器不是魔法,不会凭空创造出更多的 CPU 时间。如果执行任务所需要的 CPU 时间太多,以至于 TMOS 无法及时调度到蓝牙事件,蓝牙仍然可能会出现工作异常的情况。

05.5 动手尝试:收到特定字节开头的包点亮 LED

蓝牙开发中,有一类需求是使用主机控制从机执行某些操作,例如通过蓝牙让设备执行开灯、关灯、启动电机等动作。这类应用的实现思路并不复杂:在收到数据后,按预先约定的方式解析数据,然后执行对应的动作。本小节以“收到以字符 1 开头的包点亮 LED、以字符 0 开头的包熄灭 LED”为例,演示如何在手机 APP 中发送特定指令,让评估板上的 LED 响应控制。

硬件准备:把 LED 接到 GPIO

BLE_UART 例程中不包含控制 LED 的功能。评估板上设计有用户 LED,直接引出到 P2 排针的 LED0、LED1 针脚。用户可以使用杜邦线(或跳线帽)把 LED 接到任意需要的 GPIO 上,本小节中连接到 PB8,接线方法如下:

  1. 在评估板 P2 排针上找到 LED0 引脚(具体位置请对照评估板丝印);
  2. 用一根杜邦线将 LED0 引脚与 PB8 引脚相连(在相邻的位置);
  3. 确认连接牢固、没有短路。

第 1 步:在 Peripheral_Init() 中初始化 LED 引脚

打开 APP/peripheral.c,在 Peripheral_Init() 函数末尾(tmos_set_event(Peripheral_TaskID, SBP_START_DEVICE_EVT); 之后)添加 LED 引脚初始化:

1
2
GPIOB_SetBits(GPIO_Pin_8);                        // 初始输出高电平,LED 熄灭
GPIOB_ModeCfg(GPIO_Pin_8, GPIO_ModeOut_PP_5mA); // 配置 PB8 为推挽输出

GPIOB_ModeCfg() 用于配置 PB 口引脚的工作模式,GPIO_ModeOut_PP_5mA 表示推挽输出、5mA 驱动强度档位;GPIOB_SetBits() / GPIOB_ResetBits() 分别用于将引脚输出高电平 / 低电平。这三个函数都是 CH58x 标准外设库(StdPeriphDriver)提供的 GPIO 操作接口,声明位于 CH58x_gpio.h,默认情况下已被包含。

第 2 步:在数据接收回调中解析命令

BLE_UART 例程中,手机发来的数据由协议栈回调到 on_bleuartServiceEvt(),事件类型 BLE_UART_EVT_BLE_DATA_RECIEVED 即“收到手机数据”,数据内容位于 p_evt->data.p_data(指针)与 p_evt->data.length(长度)。在该分支中、原有透传逻辑之前,加入对首字节的判断:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
case BLE_UART_EVT_BLE_DATA_RECIEVED:
PRINT("BLE RX DATA len:%d\r\n", p_evt->data.length);

// 命令控制:包首字节为字符 '1' 点亮 LED,为 '0' 熄灭 LED(低电平点亮)
if(p_evt->data.length >= 1)
{
if(p_evt->data.p_data[0] == '1')
{
GPIOB_ResetBits(GPIO_Pin_8); // 输出低电平,点亮 LED
PRINT("LED ON\r\n");
}
else if(p_evt->data.p_data[0] == '0')
{
GPIOB_SetBits(GPIO_Pin_8); // 输出高电平,熄灭 LED
PRINT("LED OFF\r\n");
}
}

//for notify back test
// ...原有透传代码保持不变...

编译下载验证

重新编译并下载固件(方法见 04.3 节),用手机 BLE 调试助手连接设备(连接方法见 04.4 节),然后:

  1. 在 BLE 调试助手的发送框中输入字符 1 并发送,评估板上的 LED 点亮,该字符也被透传到串口;
  2. 输入字符 0 并发送,LED 熄灭,同样该字符也被透传;
  3. 输入 Hello CH585 并发送,LED 状态不变,串口透传功能正常。

一些说明

  • p_evt->data.p_data[0] 取的是包的首个字节。若想支持更长的命令(例如 LED1 开灯、LED0 关灯),可以继续判断 p_data[1]、p_data[2] 等后续字节,原理相同。
  • 若解析数据包需要的时间较长,可只复制到某个缓冲区,然后触发另外一个独立的事件来异步处理。

本小节的“首字节命令字”模式是嵌入式设备较为常用的简单命令协议雏形:首字节表示命令,后续字节表示参数。在此基础上,可以扩展出开关机、模式切换、参数配置等丰富的控制指令;配合 05.4 节的周期任务,即可实现“定时上报状态并实时响应命令”的常用业务逻辑。

06 常见问题

Q1:点击下载提示“请先选择目标设备”或设备列表没有出现设备?

  • 检查 USB 线是否为仅充电线(无数据线)。
  • 确认执行“按住 DOWNLOAD 按键插入 USB 线”的动作时是否是冷启动(上电复位)。
  • 检查设备管理器中是否出现无驱动的未知设备,若有则重新安装 CH372 驱动程序:https://www.wch.cn/downloads/CH372DRV_EXE.html

Q2:固件下载成功后串口没有任何日志输出?

  • 检查波特率配置(默认 115200 8N1),将 USB 转串口设备自身的 TXD 和 RXD 短接并在计算机上发送数据,查看是否能正确收到相同数据,排除 USB 转串口设备问题。
  • 评估板没有板载 USB 转串口芯片,两个 USB 接口均为原生 USB 外设。请检查外接 USB 转串口设备与芯片 TXD(如 PA14)的接线是否牢固、TX/RX 是否接反、是否连接了 GND 线等。

Q3:手机 APP 搜不到 BLE 广播?

  • 检查芯片天线附近的阻抗匹配电路或板载 PCB 天线是否被金属遮挡。
  • 确认芯片是否正常运行(观察串口日志是否有正常的蓝牙初始化输出)。
  • 使用万用表测量 VINTA 所接电容两端电压,应为 1.05V 左右。
  • 若使能了 DC-DC,PCB 上必须有设计 10uH 的电感等外围电路,可不使能 DC-DC 测试。
  • 若将 CLK_OSC32K 设置为 0,PCB 上必须有设计外部的 32.768kHz 晶振,否则会导致 TMOS 时基错误,可将此配置设置为 1 以使用内部 32.768kHz 时钟再测试。

Q4:代码跑飞/修改后烧不进程序怎么办?

CH585 的 Bootloader 在冷启动(上电复位)时无条件执行,若检测到 PB22 为低(或 PB11 为高,取决于设置)则进入 ISP 状态,此时可以重新下载新的代码、重新开启两线仿真等,因此可以使用此方法来救砖。

若 Bootloader 未检测到以上有效电平,会尝试执行用户代码。

Q5:为什么芯片上电后要等数十秒(10/20/60/120s)才开始运行程序?

此情况可能是因为芯片进入了 ISP 状态,等待上位机进行操作。请检查 PB22 是否上电时为低电平(或有对地电容),或是 PB11 上电时为高电平(若设置为 PB11 引脚控制进入 ISP 状态)。

附录

关于日志输出

在 CH585EVT 例程资料中,大部分例程默认使用 串口 0(UART0) 来输出日志打印,波特率为 115200;串口的 TXD 映射到 PA14,RXD 映射到 PA15。由于大部分例程并不使用串口读取,可仅将 USB 转串口设备的 RXD 连接到 PA14,IO 电平一般为 3.3V(与 VIO33 相等)。

软件开发范式说明

CH585 的蓝牙协议栈已经与沁恒开发的 TMOS(Task Management Operating System)轮询式调度器集成,可自动处理蓝牙通讯事件。TMOS 作为调度核心,BLE 协议栈及其相关的应用都围绕它来实现。TMOS 不是传统意义上的操作系统,而是一种以实现多任务为核心的系统资源管理机制。

TMOS 采用类似 OSAL 的事件驱动设计风格,更加节约 RAM 资源、降低功耗,但这可能与流行的 RTOS 开发范式有所不同。在 TMOS 中,任务(task)是通过事件被调度的,每个任务最多可以包括 15 个由用户定义的事件。

不同的任务之间不会发生抢占,因此为了保障蓝牙通讯的正常进行,单次事件执行时间不应太长,以不超过蓝牙连接间隔的一半为好,以免蓝牙相关的任务因得不到 CPU 时间而出现断线等情况。在产品开发中,应当尽量避免使用阻塞式操作,尽可能采用中断-事件驱动方式来实现业务逻辑,从而更高效地利用资源、降低功耗。

当需要执行某个任务时,实际上需要触发某个任务中的一个事件,在该任务被调度器轮询到时,该事件对应的业务代码即被执行。TMOS 调度事件的时间基准一般来自 RTC 时间,故传入的时间参数一般是 tmosTimer 值(类似于 RTOS 中的 Tick 值),单位是 625μs。

TMOS 中,主要有三种常用的触发事件方式,包括立即触发、延迟触发、周期性触发。

  • 立即触发:bStatus_t tmos_set_event( tmosTaskID taskID, tmosEvents event )
  • 延迟触发:BOOL tmos_start_task( tmosTaskID taskID, tmosEvents event, tmosTimer time )
  • 周期性触发:bStatus_t tmos_start_reload_task( tmosTaskID taskID, tmosEvents event, tmosTimer time )

关于 CH585M-EVT-R1 评估板的跳线帽

评估板上共有两处跳线帽,分别为 VCC 跳线和 NFC_CTR 跳线。

  • VCC 跳线:电流测量跳线,串联在主芯片 CH585M 的 3.3V 电源路径中。
    跳线帽短接时,评估板为芯片正常供电;摘下跳线帽后,可在跳线柱之间串入电流表来测量芯片的净功耗(作用类似于 Nucleo 评估板上的 IDD 跳线)。出厂默认插上此跳线帽。
  • NFC_CTR 跳线:用于通过 PA7 引脚控制 NFC_CTR 信号,从而控制 NFC 天线的接收分压电路。
    通过配合 NFC 例程中的“天线信号自动控制”功能,可自动调节天线信号幅度、在宽电压范围内实现读卡工作。该项属于进阶调试内容,《CH585 NFC读卡器应用指南》中有详细说明。若使用 PA7 引脚作其他功能时受影响,或不需要此功能,可取下此跳线帽妥善保存。出厂默认插上此跳线帽。

关于 CH585 芯片资料的获取

在沁恒官网可通过搜索功能查找所需型号的芯片资料:

搜索芯片资料

Your browser is out-of-date!

Update your browser to view this website correctly.&npsb;Update my browser now

×