跳转到内容

RTC 与蓝牙

1.RTC

1.1. RTC 概述

RTC 的全称是“Real-Time Clock”,即实时时钟芯片。实时时钟芯片通过引脚向外部提供时间读写接口,通常由独立电池供电。因此,即使外部系统断电,也能保证芯片电路正常运行并准确计时。不同的时钟芯片内部机制可能不同,但 Linux 系统驱动会封装不同钟表芯片的操作细节,并为应用程序提供统一的时间操作接口。

1.1.1 开发板的 RTC 资源

EASY EAI Nano-TB 默认未搭载 RTC 电路。如果希望底板支持 RTC 功能,可以使用我们的 RTC 模块进行扩展

开发板的 RTC 资源

开发板的 RTC 资源

RTC 扩展的具体步骤:

首先关闭底板电源,将模块正面朝上插入底板的 40PIN 接口。详情请参考下图。

开发板的 RTC 资源

开发板的 RTC 资源

插紧后,上电。 使用 ls 命令可以确认 RTC 芯片是否已被系统识别。

Terminal window
ls /dev/rtc\*

开发板的 RTC 资源

开发板的 RTC 资源

确认驱动已正常加载后,可以通过以下命令访问驱动,并读取 RTC 芯片的全部信息。

Terminal window
cat /proc/driver/rtc

开发板的 RTC 资源

开发板的 RTC 资源

1.1.2 RTC 时间的读写

这里涉及两个时钟:RTC 芯片时钟和系统时钟。手动管理 RTC 时钟的本质是进行时钟同步,也就是将系统时钟同步到 RTC 芯片时钟,或将 RTC 芯片时钟同步到系统时钟。

系统时钟:

系统时钟本质上是一个 64 位整数,该整数表示与当前 EpochTime 的时间差(单位为秒),称为时间戳。该时钟由 CPU 主芯片的定时器维护,当 CPU 断电时,时间信息会丢失。操作系统的时钟命令是 date。

Terminal window
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 未运行时仍然保存时间信息。

将芯片时钟同步到系统时钟:

Terminal window
sudo hwclock --hctosys

将系统时钟同步到芯片时钟(或使用 sudo hwclock -w)

Terminal window
sudo hwclock --systohc

如果不想同步到系统时钟,只想查询 RTC 芯片时钟,请使用以下命令。

Terminal window
sudo hwclock -r

将芯片时钟同步到系统时钟:

将芯片时钟同步到系统时钟:

1.1.3 系统时钟的读写

本文档重点说明 RTC 时钟

1.1.4 时区与时间同步服务

  • 时区: RTC 时钟系统时钟都使用 UTC 时间。对于不同地区使用的时间,需要考虑时区的影响。

  • 时间同步服务: RTC 时钟不仅可以手动操作,也会受到时间同步服务的影响。

1.2. 快速开始

1.2.1 准备开发环境

在 PC 端的 Ubuntu 系统中执行 run 脚本,进入 EASY-EAI 编译环境。详情如下。

Terminal window
cd ~/develop_environment

1.2.2 下载源码并编译示例

首先,在虚拟机后台终端执行以下命令,创建外设示例源码的管理目录:

Terminal window
cd /opt
mkdir -p EASY-EAI-Nano-TB/demo

下载示例程序:

例如,将示例程序下载到“PC\D:”(路径没有指定,用户可选择任意位置)。

随后,将下载的示例复制到虚拟机文件系统中。步骤请参考下图。

下载源码并编译示例

下载源码并编译示例

最后,进入对应示例目录并执行编译操作。具体命令如下:

Terminal window
cd EASY-EAI-Nano-TB/demo/12_RTC
./build.sh

💡 注意:依赖库位于开发板上,因此交叉编译过程中需要保持 /mnt 挂载。

下载源码并编译示例

下载源码并编译示例

编译成功后,会在 Release 目录中生成名为 test-rtc 的可执行程序,并自动放置到开发板的 /userdata/ 目录中。

1.2.3 运行示例

通过串口调试或 SSH 访问开发板后台,进入示例所在目录:

Terminal window
cd /userdata

运行示例

运行示例

执行以下命令启动示例。

Terminal window
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。

Terminal window
apt-get install bluez

确认 BlueZ 是否正常安装:

Terminal window
bluetoothctl -v

如果如下所示输出版本号,则表示 BlueZ 已正常安装。

准备工作

准备工作

可以使用 hciconfig 工具确认蓝牙设备是否正常工作。

Terminal window
hciconfig -a

准备工作

准备工作

2.2. 蓝牙工具

hci* 系列工具通常用于直接操作蓝牙协议栈的 HCI 层,但在新版 BlueZ 工具集中已逐渐不再维护。目前用于操作蓝牙设备的主流工具是 bluetoothd 和 bluetoothctl。

2.2.1 bluetoothd

这是用于管理蓝牙驱动的应用层服务,通常只需保持其运行即可。可使用 ps 命令确认它是否在后台运行:

Terminal window
sudo ps -ef \| grep -i bluetoothd

bluetoothd

bluetoothd

在 Ubuntu 系统中,无需手动启动或停止该进程。bluetoothd 通过 systemctl 服务进行管理。bluetoothd 服务的启动/停止以及状态确认命令如下:

Terminal window
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

bluetoothctl

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

bluetoothctl

bluetoothctl

打开蓝牙芯片电源的命令:

Terminal window
power on

打开蓝牙芯片电源的命令:

打开蓝牙芯片电源的命令:

进入 advertise 子菜单:

修改芯片名称,使其他蓝牙主机(Host)可以扫描并发现该设备。

Terminal window
menu advertise
name EASY-EAI-Nano-TB

进入 advertise 子菜单:

进入 advertise 子菜单:

之后,通过 back 命令返回上一级菜单:

Terminal window
back

2.3. BLE 协议通信

BLE (Bluetooth Low Energy) 基于 GATT。

2.3.1 开发板作为主机(Host)时

首先,将手机上的蓝牙调试助手 App 设置为从机:开启从机模式,并开始广播(Advertising)。

开发板作为主机(Host)时

开发板作为主机(Host)时

接着,在开发板端(bluetoothctl 内)执行开始扫描停止扫描连接设备

开始扫描:

Terminal window
scan on

发现目标设备后停止扫描:

Terminal window
scan off

列出扫描到的设备(找到 MAC 地址):

Terminal window
devices

与目标设备进行配对、信任(Trust)和连接:

Terminal window
pair xx:xx:xx:xx:xx:xx
trust xx:xx:xx:xx:xx:xx
connect xx:xx:xx:xx:xx:xx

蓝牙连接成功后,在蓝牙主机(开发板)中进入 gatt 子菜单:

Terminal window
menu gatt

查看特征属性:

Terminal window
list-attributes

(从 BLE 调试助手 App 可知,fff1 用于从机发送,fff2 用于从机接收。)

从机发送的特征属性如下:

Terminal window
Characteristic (Handle 0x0000)
/org/bluez/hci0/dev_78_C3_C4_C4_94_8D/service002f/char0030
0000fff1-0000-1000-8000-00805f9b34fb
Unknown

从机接收的特征属性如下:

Terminal window
Characteristic (Handle 0x0000)
/org/bluez/hci0/dev_78_C3_C4_C4_94_8D/service002f/char0034
0000fff2-0000-1000-8000-00805f9b34fb
Unknown

2.3.1.1 主机接收,从机发送

首先选择 fff1。

Terminal window
select-attribute /org/bluez/hci0/dev_78_C3_C4_C4_94_8D/service002f/char0030

接着开启通知(notify)。

Terminal window
notify on

主机接收,从机发送

主机接收,从机发送

随后,操作 App,向开发板发送 1 Byte 的 Hex 数据。

主机接收,从机发送 )

主机接收,从机发送

2.3.1.2 主机发送,从机接收

将属性切换并选择为 fff2。

Terminal window
select-attribute /org/bluez/hci0/dev_78_C3_C4_C4_94_8D/service002f/char0034

随后执行 write 操作。

Terminal window
write 0x67

最终,可以在 App 上接收到开发板发送的数据。

主机发送,从机接收

主机发送,从机接收

2.3.2 开发板作为从机时

下载示例程序:

例如,将示例程序下载到“PC\D:”(路径没有指定,用户可选择任意位置)。

之后,将 bluetooth-gatt 文件夹传输到开发板,并在开发板上打开新的终端,编译并运行 gatt-server 服务(用于与主机通信的应用程序)。

开发板作为从机时

开发板作为从机时

开发板作为从机时

开发板作为从机时

再次返回 bluetoothctl,执行以下命令开始蓝牙广播:

Terminal window
advertise on

开发板作为从机时

开发板作为从机时

使用 BLE 调试助手 App 扫描并连接开发板的蓝牙。

开发板作为从机时

开发板作为从机时

2.3.2.1 主机接收,从机发送

通过 BLE 调试助手 App 读取开发板的数据。

主机接收,从机发送

主机接收,从机发送

执行上述操作后,开发板上的 gatt-server 会输出以下信息:

主机接收,从机发送

主机接收,从机发送

2.3.2.2 主机发送,从机接收

通过 BLE 调试助手 App 向开发板发送数据。

主机发送,从机接收

主机发送,从机接收

开发板上的 gatt-server 会接收到以下信息:

主机发送,从机接收

主机发送,从机接收