RTC 与蓝牙
1.RTC
1.1. RTC 概述
RTC 的全称是“Real-Time Clock”,即实时时钟芯片。实时时钟芯片通过引脚向外部提供时间读写接口,通常由独立电池供电。因此,即使外部系统断电,也能保证芯片电路正常运行并准确计时。不同的时钟芯片内部机制可能不同,但 Linux 系统驱动会封装不同钟表芯片的操作细节,并为应用程序提供统一的时间操作接口。
1.1.1 开发板的 RTC 资源
EASY EAI Nano-TB 默认未搭载 RTC 电路。如果希望底板支持 RTC 功能,可以使用我们的 RTC 模块进行扩展。

开发板的 RTC 资源
RTC 扩展的具体步骤:
首先关闭底板电源,将模块正面朝上插入底板的 40PIN 接口。详情请参考下图。

开发板的 RTC 资源
插紧后,上电。 使用 ls 命令可以确认 RTC 芯片是否已被系统识别。
ls /dev/rtc\*
开发板的 RTC 资源
确认驱动已正常加载后,可以通过以下命令访问驱动,并读取 RTC 芯片的全部信息。
cat /proc/driver/rtc
开发板的 RTC 资源
1.1.2 RTC 时间的读写
这里涉及两个时钟:RTC 芯片时钟和系统时钟。手动管理 RTC 时钟的本质是进行时钟同步,也就是将系统时钟同步到 RTC 芯片时钟,或将 RTC 芯片时钟同步到系统时钟。
系统时钟:
系统时钟本质上是一个 64 位整数,该整数表示与当前 EpochTime 的时间差(单位为秒),称为时间戳。该时钟由 CPU 主芯片的定时器维护,当 CPU 断电时,时间信息会丢失。操作系统的时钟命令是 date。
date # 查询系统时间date -s "2023-09-20 11:18:00" # 修改系统时间💡 注意:Epoch Time 指一个特定时间,即 1970 年 1 月 1 日 0 时 0 分 0 秒。假设当前距离 1970 年 1 月 1 日 0 时 0 分 0 秒已经过去 N 秒,那么 Linux 系统中的时间值就是 N。
RTC 芯片时钟: 这是 RTC 芯片内部维护的时间。系统断电后,它由电池供电。因此,即使系统断电,RTC 时间也会继续正常运行。RTC 芯片时钟的作用是在 Linux 未运行时仍然保存时间信息。
将芯片时钟同步到系统时钟:
sudo hwclock --hctosys将系统时钟同步到芯片时钟(或使用 sudo hwclock -w)
sudo hwclock --systohc如果不想同步到系统时钟,只想查询 RTC 芯片时钟,请使用以下命令。
sudo hwclock -r
将芯片时钟同步到系统时钟:
1.1.3 系统时钟的读写
本文档重点说明 RTC 时钟。
1.1.4 时区与时间同步服务
-
时区: RTC 时钟和系统时钟都使用 UTC 时间。对于不同地区使用的时间,需要考虑时区的影响。
-
时间同步服务: RTC 时钟不仅可以手动操作,也会受到时间同步服务的影响。
1.2. 快速开始
1.2.1 准备开发环境
在 PC 端的 Ubuntu 系统中执行 run 脚本,进入 EASY-EAI 编译环境。详情如下。
cd ~/develop_environment1.2.2 下载源码并编译示例
首先,在虚拟机后台终端执行以下命令,创建外设示例源码的管理目录:
cd /optmkdir -p EASY-EAI-Nano-TB/demo下载示例程序:
例如,将示例程序下载到“PC\D:”(路径没有指定,用户可选择任意位置)。
随后,将下载的示例复制到虚拟机文件系统中。步骤请参考下图。

下载源码并编译示例
最后,进入对应示例目录并执行编译操作。具体命令如下:
cd EASY-EAI-Nano-TB/demo/12_RTC./build.sh💡 注意:依赖库位于开发板上,因此交叉编译过程中需要保持 /mnt 挂载。

下载源码并编译示例
编译成功后,会在 Release 目录中生成名为 test-rtc 的可执行程序,并自动放置到开发板的 /userdata/ 目录中。
1.2.3 运行示例
通过串口调试或 SSH 访问开发板后台,进入示例所在目录:
cd /userdata
运行示例
执行以下命令启动示例。
sudo ./test-rtc运行结果如下。

运行示例
1.3. C 语言使用示例
这是 RTC 的 C 语言使用示例。代码路径为 12_RTC/test-rtc/main.c。 可作为编码参考。以下代码展示了 RTC 时钟读写操作的流程:
int main(int argc, char const *argv[]){ const char *strDateTime = "2023-09-21 15:22:37";
// 将字符串转换为 tm 结构体类型的时间信息 struct tm tm = {0}; strptime(strDateTime, "%Y-%m-%d %H:%M:%S", &tm);
// 打开 RTC 设备 int rtc_fd = open("/dev/rtc0", O_RDWR); if (rtc_fd < 0) { perror("open RTC device /dev/rtc0 faild."); close(rtc_fd); return -1; }
printf("---参数设置前的日期时间---\n"); system("date");
/*** 1. 停止网络时间同步服务 ***/ system("systemctl stop ntp.service");
/*** 2. 将预先设置的时间写入 RTC 时钟 ***/ struct rtc_time rtc_tm; rtc_tm.tm_sec = tm.tm_sec; rtc_tm.tm_min = tm.tm_min; rtc_tm.tm_hour = tm.tm_hour; rtc_tm.tm_mday = tm.tm_mday; rtc_tm.tm_mon = tm.tm_mon; rtc_tm.tm_year = tm.tm_year; if (ioctl(rtc_fd, RTC_SET_TIME, &rtc_tm) < 0) { perror("set data time to rtc0"); perror("RTC 时间设置失败"); close(rtc_fd); return -1; }
/*** 3. 将 RTC 时钟同步到系统时钟 ***/ // 读取刚才写入的 RTC 时钟参数 if (ioctl(rtc_fd, RTC_RD_TIME, &rtc_tm) < 0) { perror("RTC 时间读取失败"); close(rtc_fd); return -1; } close(rtc_fd);
tm.tm_sec = rtc_tm.tm_sec; tm.tm_min = rtc_tm.tm_min; tm.tm_hour = rtc_tm.tm_hour; tm.tm_mday = rtc_tm.tm_mday; tm.tm_mon = rtc_tm.tm_mon; tm.tm_year = rtc_tm.tm_year; struct timeval tv; tv.tv_sec = mktime(&tm); tv.tv_usec = 0;
// 将时间同步到系统时钟 if(0 != settimeofday(&tv, (struct timezone *)0)){ perror("系统时间设置失败"); }
printf("---参数设置后的日期时间---\n"); system("date");
return 0;2.蓝牙
2.1. 蓝牙概述
在蓝牙协议栈中,蓝牙数据传输主要使用两种协议:SPP(经典蓝牙串口协议)和 BLE(Bluetooth Low Energy,低功耗蓝牙协议)。
-
单模蓝牙模块: 仅支持 SPP 或 BLE 中的一种。
-
双模蓝牙模块: 同时支持 SPP 和 BLE。
EASY-EAI-Nano-TB 采用的蓝牙模块为 DB37,它是仅支持 BLE 协议的单模蓝牙模块。
2.1.1 BlueZ
BlueZ 是目前最成熟的开源蓝牙协议栈,是一个基于 GNU General Public License (GPL) 发布的开源项目。它是 Linux 官方蓝牙协议栈(自 Linux 2.4.6 起已成为 Linux 内核的一部分),并广泛应用于主流 Linux 发行版。 也就是说,可以将“如何在 Linux 上使用蓝牙?”理解为“如何使用 BlueZ?”。
具体来说,BlueZ 是 Linux 官方支持的开源蓝牙协议栈处理工具集,其中包含以下工具: bccmd, bluemoon, bluetoothctl, bluetoothd, btattach, btmon, ciptool, hciattach, hciconfig, hcidump, hcitool, hex2hcd, l2ping, l2test, mpris-proxy, rctest, rfcomm, sdptool。
2.1.2 准备工作
首先安装 BlueZ。
apt-get install bluez确认 BlueZ 是否正常安装:
bluetoothctl -v如果如下所示输出版本号,则表示 BlueZ 已正常安装。

准备工作
可以使用 hciconfig 工具确认蓝牙设备是否正常工作。
hciconfig -a
准备工作
2.2. 蓝牙工具
hci* 系列工具通常用于直接操作蓝牙协议栈的 HCI 层,但在新版 BlueZ 工具集中已逐渐不再维护。目前用于操作蓝牙设备的主流工具是 bluetoothd 和 bluetoothctl。
2.2.1 bluetoothd
这是用于管理蓝牙驱动的应用层服务,通常只需保持其运行即可。可使用 ps 命令确认它是否在后台运行:
sudo ps -ef \| grep -i bluetoothd
bluetoothd
在 Ubuntu 系统中,无需手动启动或停止该进程。bluetoothd 通过 systemctl 服务进行管理。bluetoothd 服务的启动/停止以及状态确认命令如下:
sudo systemctl status bluetooth.service ## 确认 bluetoothd 服务状态sudo systemctl start bluetooth.service ## 启动 bluetoothd 服务(重启后状态不会保存)sudo systemctl stop bluetooth.service ## 停止 bluetoothd 服务(重启后状态不会保存)sudo systemctl enable bluetooth.service ## 启用服务(设备重启后 bluetoothd 会自动启动)sudo systemctl disable bluetooth.service ## 禁用服务(设备重启后 bluetoothd 不会自动启动)2.2.2 bluetoothctl
这是一个通过 D-Bus 与 bluetoothd 交互的工具,相当于 bluetoothd 服务的客户端。bluetoothctl 通过 bluetoothd 间接操作蓝牙硬件。bluetoothctl 内置 shell 交互功能,在命令行中直接执行 bluetoothctl 工具后,可以进入该工具的内部 shell。

bluetoothctl
输入 help 可以查看该工具支持的命令。

bluetoothctl
打开蓝牙芯片电源的命令:
power on
打开蓝牙芯片电源的命令:
进入 advertise 子菜单:
修改芯片名称,使其他蓝牙主机(Host)可以扫描并发现该设备。
menu advertisename EASY-EAI-Nano-TB
进入 advertise 子菜单:
之后,通过 back 命令返回上一级菜单:
back2.3. BLE 协议通信
BLE (Bluetooth Low Energy) 基于 GATT。
2.3.1 开发板作为主机(Host)时
首先,将手机上的蓝牙调试助手 App 设置为从机:开启从机模式,并开始广播(Advertising)。

开发板作为主机(Host)时
接着,在开发板端(bluetoothctl 内)执行开始扫描、停止扫描和连接设备。
开始扫描:
scan on发现目标设备后停止扫描:
scan off列出扫描到的设备(找到 MAC 地址):
devices与目标设备进行配对、信任(Trust)和连接:
pair xx:xx:xx:xx:xx:xxtrust xx:xx:xx:xx:xx:xxconnect xx:xx:xx:xx:xx:xx蓝牙连接成功后,在蓝牙主机(开发板)中进入 gatt 子菜单:
menu gatt查看特征属性:
list-attributes(从 BLE 调试助手 App 可知,fff1 用于从机发送,fff2 用于从机接收。)
从机发送的特征属性如下:
Characteristic (Handle 0x0000) /org/bluez/hci0/dev_78_C3_C4_C4_94_8D/service002f/char0030 0000fff1-0000-1000-8000-00805f9b34fb Unknown从机接收的特征属性如下:
Characteristic (Handle 0x0000) /org/bluez/hci0/dev_78_C3_C4_C4_94_8D/service002f/char0034 0000fff2-0000-1000-8000-00805f9b34fb Unknown2.3.1.1 主机接收,从机发送
首先选择 fff1。
select-attribute /org/bluez/hci0/dev_78_C3_C4_C4_94_8D/service002f/char0030接着开启通知(notify)。
notify on
主机接收,从机发送
随后,操作 App,向开发板发送 1 Byte 的 Hex 数据。
)
主机接收,从机发送
2.3.1.2 主机发送,从机接收
将属性切换并选择为 fff2。
select-attribute /org/bluez/hci0/dev_78_C3_C4_C4_94_8D/service002f/char0034随后执行 write 操作。
write 0x67最终,可以在 App 上接收到开发板发送的数据。

主机发送,从机接收
2.3.2 开发板作为从机时
下载示例程序:
例如,将示例程序下载到“PC\D:”(路径没有指定,用户可选择任意位置)。
之后,将 bluetooth-gatt 文件夹传输到开发板,并在开发板上打开新的终端,编译并运行 gatt-server 服务(用于与主机通信的应用程序)。

开发板作为从机时

开发板作为从机时
再次返回 bluetoothctl,执行以下命令开始蓝牙广播:
advertise on
开发板作为从机时
使用 BLE 调试助手 App 扫描并连接开发板的蓝牙。

开发板作为从机时
2.3.2.1 主机接收,从机发送
通过 BLE 调试助手 App 读取开发板的数据。

主机接收,从机发送
执行上述操作后,开发板上的 gatt-server 会输出以下信息:

主机接收,从机发送
2.3.2.2 主机发送,从机接收
通过 BLE 调试助手 App 向开发板发送数据。

主机发送,从机接收
开发板上的 gatt-server 会接收到以下信息:

主机发送,从机接收