计 算 机 系 统 应 用 2009 年 第 12 期 嵌 入 式 Linux 下 温 湿 度 传 感 器 的 设 计 与 实 现 1 Design and Implementation of Temperature and Humidity Sensor Based on Embedded Linux 陈 博 刘 锦 高 ( 华 东 师 范 大 学 电 子 科 学 技 术 系 上 海 200241) 摘 要 : 在 嵌 入 式 应 用 领 域, 需 要 测 量 周 围 环 境 的 质 量 对 生 产 和 工 作 进 行 监 控 和 预 警 通 过 比 较 设 计 方 案, 提 出 在 嵌 入 式 Linux 下, 基 于 PXA310 平 台 温 湿 度 传 感 器 的 设 计 与 实 现 方 法 在 Linux 操 作 系 统 下 通 过 对 驱 动 程 序 接 口 调 用, 完 成 温 湿 度 数 据 读 取 和 预 警, 并 对 Linux 驱 动 程 序 编 写 进 行 比 较 实 验 表 明, 本 方 案 硬 件 和 软 件 设 计 切 实 可 行, 提 高 了 环 境 测 量 的 准 确 度 和 系 统 性 能 的 实 时 性 关 键 词 : Linux PXA310 驱 动 程 序 实 时 性 1 引 言 在 工 业 控 制 和 工 业 生 产 领 域 中, 传 感 器 对 于 工 业 控 制 和 生 产 环 境 的 监 控 作 用 不 言 而 喻 传 统 的 传 感 器 监 控 系 统 大 都 采 用 单 片 机 控 制, 其 监 控 的 准 确 度 和 实 时 性 不 太 令 人 满 意 本 文 寻 找 到 一 套 切 实 可 行 的 传 感 器 设 计 方 案, 其 利 用 温 湿 度 传 感 器 芯 片, 基 于 PXA310 硬 件 平 台 和 Linux 操 作 系 统, 能 有 效 监 控 现 场 温 湿 度 变 化 在 周 围 环 境 发 生 变 化, 不 能 满 足 工 作 要 求 时, 可 以 获 取 监 控 数 据 并 提 出 预 警, 提 高 生 产 和 工 作 环 境 检 测 的 可 靠 性 及 实 时 性 2 温 湿 度 传 感 器 电 路 设 计 比 较 了 一 些 传 感 器 应 用 设 计 方 案 后, 选 用 SHT10 芯 片 为 嵌 入 式 温 湿 度 传 感 器 的 核 心 部 件 它 外 围 电 路 简 便, 相 比 其 他 传 感 器 芯 片 (DS18B20) 有 其 独 到 优 势 [1] STH10 每 秒 可 进 行 3 次 温 湿 度 测 量, 数 据 精 度 14 bit 并 且 工 作 稳 定 其 测 量 采 用 CMOSens 专 利 [2], 所 以 在 测 量 效 率 和 精 度 上 要 好 于 DS18B20 DS18B20 采 用 单 总 线 控 制 方 案 (1-wire), 大 约 每 秒 测 量 一 次,9 位 数 字 式 温 度 数 据 ; 只 提 供 温 度 测 量 其 在 生 产 环 境 检 测 要 求 严 格 时, 就 显 得 精 度 和 功 能 有 些 不 足 2.1 SHT10 简 介 SHT10 是 一 款 高 度 集 成 的 温 湿 度 传 感 器 芯 片, 提 供 全 量 程 标 定 数 字 输 出 传 感 器 包 括 一 个 电 容 性 聚 合 体 湿 度 敏 感 元 件 和 一 个 用 能 隙 材 料 制 成 的 温 度 敏 感 元 件, 他 们 与 一 个 14 位 A/D 转 换 器 以 及 一 个 串 行 接 口 电 路 设 计 在 同 一 个 芯 片 上 面 其 通 过 标 定 得 到 校 准 系 数 以 程 序 形 式 储 存 在 芯 片 OTP 内 存 中, 并 利 用 两 线 制 串 行 接 口 与 内 部 电 压 调 整, 使 外 围 系 统 集 成 变 得 快 速 而 简 单 2.2 SHT10 工 作 原 理 SHT10 芯 片 电 源 3.3V 传 感 器 上 电 后, 等 待 11ms 来 完 成 休 眠 状 态 通 信 复 位 和 启 动 传 输 命 令 后, 发 送 一 组 测 量 命 令 ( 00000101 表 示 相 对 湿 度 RH, 00000011 表 示 温 度 T), 控 制 器 要 等 待 测 量 结 束 这 个 过 程 需 要 大 约 11/55/210ms, 分 别 对 应 8/12/14bit 测 量 SHT10 通 过 下 拉 DATA 至 低 电 平, 表 示 测 量 结 束 控 制 器 触 发 SCK 时 钟 前, 必 须 等 待 这 个 数 据 备 妥 信 号 才 能 将 测 量 数 据 正 确 读 入 测 量 和 通 讯 结 束 后,SHT10 自 动 转 入 休 眠 模 式 数 据 传 送 采 用 两 线 制 串 行 接 口 ( 与 I2C 接 口 不 兼 容 ) 2.3 SHT10 电 路 原 理 图 SHT10 采 用 LCC 封 装, 其 DATA 和 SCK 引 脚 分 别 连 接 到 PXA310 的 GPIO78 和 GPIO79 PXA310 通 过 模 拟 时 序 方 式 实 现 对 外 围 温 湿 度 传 感 器 的 控 制 和 数 据 读 写 操 作 由 于 SHT10 对 于 温 湿 度 灵 敏 度 很 高, 1 基 金 项 目 : 上 海 市 科 委 重 点 项 目 (075115002) 收 稿 时 间 :2009-03-10 126 实 用 案 例 Application Case
2009 年 第 12 期 计 算 机 系 统 应 用 在 系 统 集 成 时 应 尽 量 远 离 发 热 源 ( 如 MCU LCD 等 ), 否 则 测 量 结 果 会 有 所 偏 离 ; 为 SHT10 布 线 时, 周 围 应 尽 量 铺 地 减 少 周 围 器 件 对 其 的 干 扰 SHT10 电 路 原 理 图 如 图 1 所 示 图 1 SHT10 电 路 原 理 图 3 Linux 温 湿 度 传 感 器 驱 动 程 序 实 现 单 片 机 控 制 的 传 感 器 设 备 中, 单 片 机 通 常 是 单 线 程 运 行 在 进 行 温 湿 度 测 量 时, 单 片 机 需 要 等 待 测 试 结 果 返 回, 其 方 法 阻 碍 了 其 他 测 试 和 操 作 的 同 步 执 行 在 嵌 入 式 Linux 系 统 中, 驱 动 程 序 将 测 试 任 务 送 入 任 务 队 列, 交 出 CPU 控 制 权, 继 而 进 行 其 他 实 时 任 务 运 行, 待 内 核 空 闲 再 进 入 任 务 队 列 完 成 传 感 器 的 测 量, 以 此 提 高 系 统 执 行 的 效 率 和 实 时 性 3.1 Linux 温 湿 度 传 感 器 设 备 加 载 [3] 温 湿 度 传 感 器 使 用 Linux 内 核 的 Miscdevice 数 据 结 构 在 驱 动 程 序 初 始 化 时 将 设 备 注 册 到 内 核 Miscdevice 是 字 符 设 备, 其 主 设 备 号 为 10, 设 备 及 设 备 接 口 函 数 定 义 如 下 所 示 static struct file_operations sht10_fops = owner:this_module, // 所 属 的 设 备 模 块 read: sht10_read, // 数 据 读 取 操 作 ; static struct miscdevice my_sht10 =.minor=4, // 次 设 备 号 为 4.name="SHT10", // 设 备 名 称 SHT10.fops=&sht10_fops, // 设 备 可 用 相 关 操 作 ; 驱 动 程 序 加 载 设 备 时 将 调 用 内 核 的 注 册 函 数 在 Linux2.4 和 2.6 内 核 中, 几 乎 所 有 Linux 驱 动 程 序 都 依 靠 如 下 函 数 加 载 模 块 [4] static int init sht10_init(void) misc_register(&my_sht10); // 注 册 SHT10 设 备 return 0; // 操 作 成 功 返 回 0 驱 动 程 序 初 始 化 完 成 后, 上 层 应 用 程 序 可 以 调 用 sht10_fops 中 的 sht10_read 函 数 进 行 温 湿 度 的 读 取 操 作 3.2 Linux 温 湿 度 传 感 器 设 备 操 作 进 行 数 据 读 取 前, 首 先 要 在 驱 动 程 序 中 开 辟 4 个 字 节 的 数 据 空 间, 用 于 存 放 温 度 和 湿 度 测 量 值 这 里 定 义 全 局 变 量 数 据 缓 冲 区 为 unsigned char buf [4] 读 取 SHT10 温 湿 度 数 据 前, 需 要 进 行 端 口 初 始 化 和 SHT10 复 位 操 作, 然 后 将 任 务 送 于 任 务 队 列 并 阻 塞 线 程 [5], 当 任 务 完 成 返 回 后 再 唤 醒 线 程, 将 读 到 数 据 传 递 给 上 层 应 用 程 序 做 进 一 步 处 理 程 序 流 程 图 和 实 现 函 数 如 图 2 所 示 图 2 驱 动 程 序 流 程 图 static int measure_sht10(u8 checksum,u8 mode) unsigned char error=0; // 设 备 无 应 答, 标 识 清 0 int i=0; // 用 来 指 示 数 据 存 放 位 置 start_trans(); // 模 拟 时 序, 启 动 传 输 switch(mode) // 选 择 测 量 方 式 case TEMP:error+=write_byte (MEASURE_ TEMP); Application Case 实 用 案 例 127
计 算 机 系 统 应 用 2009 年 第 12 期 i=1;break; // 测 量 温 度, 指 示 存 放 位 置 case HUMI:error+=write_byte(MEASURE_ HUMI); break; // 测 量 湿 度 while(1) // 等 待 SHT10 应 答, 退 出 if(read_data()==0) break; if(i) // 查 看 i, 存 放 数 据 buf[0]=read_byte(ack); // 将 测 量 温 度 数 据 存 放 于 buf[1]=read_byte(ack); //buf[0] 和 buf[1], 并 应 答 else buf[2]=read_byte(ack); // 将 测 量 湿 度 数 据 存 放 于 buf[3]=read_byte(ack); //buf[2] 和 buf[3] 并 应 答 // 最 后 读 效 验, 无 应 答 checksum=read_byte(noack); return error; // 返 回 错 误 标 识 上 述 函 数 中 start_trans; write_byte; read_ data; read_byte 分 别 利 用 PXA310 引 脚 模 拟 时 序 完 成 启 动 传 输 写 字 节, 读 一 位 数 据 和 读 字 节 的 操 作 static ssize_t sht10_read(struct file *file,char *buffer,size_t count,loff_t *ppos) port_init(); // 初 始 化 PXA310 端 口 reset_sht10(); // 复 位 SHT10 tasklet_schedule(&sht10_tasklet); // 将 任 务 送 于 任 务 队 列 wait_for_completion(&comp); // 阻 塞 线 程, 等 待 完 成 copy_to_user(buffer,(char *)&buf,sizeof (buf)); // 将 读 到 的 数 据 返 回 用 户 return 0; // 空 间, 退 出 内 核 tasklet_schedule() 调 度 执 行 指 定 的 tasklet, 在 获 得 运 行 机 会 之 前 只 会 调 度 一 次, 如 果 在 运 行 时 被 调 度, 则 完 成 后 会 被 再 次 运 行 [6] wait_for_completion() 这 个 函 数 进 行 一 个 不 可 打 断 的 等 待, 如 果 有 代 码 调 用 它, 并 且 没 有 完 成 这 个 任 务, 结 果 会 是 一 个 不 可 杀 死 的 进 程 copy_to_user() 将 内 核 空 间 数 据 传 向 上 层 用 户 空 间, 并 让 上 层 测 试 程 序 做 进 一 步 处 理 3.3 Linux 温 湿 度 传 感 器 设 备 阻 塞 操 作 由 于 温 湿 度 传 感 器 测 量 需 要 一 定 时 间, 为 提 高 系 统 运 行 效 率 和 实 时 性, 在 驱 动 程 序 中 阻 塞 线 程, 交 出 内 核 控 制 权, 等 待 操 作 完 成 后 唤 醒 线 程, 提 高 系 统 利 用 率 complete() 在 函 数 中 就 是 唤 醒 一 个 等 待 的 读 取 线 程 任 务 队 列 实 现 函 数 如 下 所 示 static int sht10_do_tasklet(void) unsigned int error=0; // 无 应 答, 标 识 清 0 unsigned char checksum=0; // 效 验 清 0 error+=measure_sht10(checksum,temp); error+=measure_sht10(checksum,humi); complete(&comp); // 完 成 测 量, 唤 醒 线 程 if(error!=0) // 测 量 有 误, 提 示 printk("wrong in measure error==>%d\n",error); 示 else printk("data correct!\n"); // 测 量 无 误 输 出 提 return error; // 返 回 错 误 标 识 Tasklet 可 以 使 测 量 操 作 在 系 统 负 荷 不 重 时 被 调 用, 或 是 被 立 即 执 行, 但 始 终 不 会 晚 于 下 一 个 CPU clock Tasklet 始 终 在 中 断 期 间 运 行, 并 且 在 调 度 他 的 同 一 CPU 上 运 行 对 比 单 片 机 系 统, 在 单 线 程 情 况 下, 一 般 在 sht10_read() 中 调 用 2 次 measure_ sht10() 来 等 待 测 量 完 成, 测 量 效 率 依 赖 2 次 测 量 消 耗 的 时 间 ; 但 在 Linux 驱 动 程 序 中, 使 用 Tasklet 方 式 操 作,2 次 测 量 过 程 不 会 对 其 他 线 程 产 生 影 响, 在 有 其 他 实 时 事 件 需 要 及 时 处 理 时 ( 如 网 络, 视 频 ), 可 以 更 有 效 提 高 驱 动 运 行 效 率, 降 低 对 其 他 实 时 处 理 产 生 的 影 响 128 实 用 案 例 Application Case
2009 年 第 12 期 计 算 机 系 统 应 用 4 温 湿 度 传 感 器 测 试 与 验 证 驱 动 程 序 完 成 以 后, 需 要 相 应 测 试 程 序 验 证 驱 动 程 序 编 写 的 正 确 性 由 于 驱 动 程 序 中 不 能 对 数 据 进 行 浮 点 数 运 算, 所 以 测 试 程 序 必 须 将 驱 动 程 序 传 递 来 的 数 据 进 行 浮 点 数 运 算 才 能 得 到 相 应 的 温 湿 度 值 4.1 温 湿 度 传 感 器 测 试 环 境 在 实 验 室 常 温 下, 测 试 程 序 多 次 调 用 驱 动 程 序 中 读 温 湿 度 的 函 数 接 口 获 得 测 试 数 据, 来 验 证 设 计 的 正 确 和 可 靠 并 考 虑 实 验 室 内 常 温 下, 相 对 湿 度 与 温 度 具 有 非 线 性 关 系, 计 算 湿 度 值 时 需 要 考 虑 温 度 的 补 偿 关 系, 其 关 系 如 图 3 所 示 图 3 SORH 转 换 到 相 对 湿 度 为 补 偿 湿 度 传 感 器 的 非 线 性 以 获 取 准 确 数 据, 并 考 虑 实 际 温 度 与 测 试 参 考 温 度 (25 ) 不 同, 使 用 如 下 公 式 修 正 读 数 RH linear =C 1 +C 2 x SO RH + C 3 x SO 2 RH RH true =( t c 25) x (t 1 +t 2 x SORH) + RH linear RH linear 是 温 度 修 正 系 数,RHtrue 是 相 对 湿 度,SORH 是 传 感 器 返 回 的 湿 度 值 进 行 12bit 湿 度 检 测 时, 参 数 取 值 如 下 表 所 示 表 1 湿 度 转 换 系 数 与 温 度 补 偿 系 数 由 于 能 隙 材 料 研 发 的 温 度 传 感 器 具 有 极 好 线 性,14bit 温 度 值 参 考 如 下 公 式 Temperature = d 1 +d 2 x SOT 温 度 转 换 系 数 取 值 如 下 表 所 示,SOT 是 传 感 器 返 回 的 温 度 值 表 2 温 度 转 换 系 数 利 用 上 述 温 湿 度 转 换 公 式 和 系 数 可 以 得 出 温 湿 度 测 量 值 4.2 温 湿 度 传 感 器 测 试 途 径 与 效 率 验 证 在 测 试 程 序 中, 考 虑 上 述 测 量 环 境 下 温 湿 度 之 间 的 非 线 性, 调 用 驱 动 程 序 的 sht10_read 函 数 将 读 到 的 温 湿 度 数 据 返 回 上 层 测 试 程 序 进 行 浮 点 数 运 算, 将 计 算 值 通 过 串 口 输 出, 达 到 测 试 验 证 的 目 的 测 试 程 序 的 实 现 如 下 所 示 static void calc_sht10(float *humi, float *temp) float rh=*humi; float t=*temp; float rh_line; float rh_true; t=t*d2+d1; // 温 度 转 换 公 式 rh_line=c3*rh*rh+c2*rh+c1; // 相 对 湿 度 转 换 公 式 rh_true=(t-25)*(t1+t2*rh)+rh_line; // 相 对 湿 度 温 度 补 偿 if(rh_true>100)rh_true=100; // 超 出 范 围 if(rh_true<0.1)rh_true=0.1; printf("humidity is: %.2f%RH\n",rh_true); printf("temperature is: %.2f'C\n",t); int main(int argc, char *argv[]) // 主 函 数 int fd; float temp,humi; // 温 湿 度 数 据 char buffer[4]; // 数 据 缓 冲 fd = open("/dev/sht10", 0); // 打 开 文 件 if (fd < 0) // 打 开 失 败, 退 出 perror("open device /dev/sht10"); exit(1); read(fd,buffer,sizeof(buffer));// 读 取 温 湿 度 值 temp=(float)((buffer[0]<<8) buffer[1]); humi=(float)((buffer[2]<<8) buffer[3]); calc_sht10(&humi, &temp); // 温 湿 度 数 值 转 换 Application Case 实 用 案 例 129
计 算 机 系 统 应 用 2009 年 第 12 期 close(fd); //关闭文件 时间会大于上表中的测量时间 并且随着任务的增加 return 0; //退出 测量时间也会相应的增加 完成的时间也受到外界中 断的影响 内核会在任务不繁忙时完成测量操作 上 测试完成后 考察驱动程序运行效率 即在驱动 表测试结果并未受到系统中其他驱动程序和中断的 程序的 tasklet_schedule 和 copy_to_user 前分别对 影响 对比开发手册中理论测量时间可以看到 使用 PXA310 的 OSCR 时间计数寄存器进行时间读取 计 任务队列的方法对改善系统处理能力与实时性效果 算此次温湿度测量所用时间 计算公式如下所示 明显 此外,实现温湿度传感器驱动程序还需要清楚了 Ti me=(osc R2-OSCR1)/OSCR_FREQ OSCR2 是唤醒线程后的时间 OSCR1 是进入任务 解 SHT10 读写时序 读取温度和湿度所需要的时间不 队列前的时间 OSCR_FREQ 是 PXA310 内部时钟频 同 如果应用程序中得出的温湿度值超过预期值 就 率 3.25MHz 这样就可以计算出每次温湿度读取消耗 可以打开 GPIO 驱动模块 触发系统板上的蜂鸣器达 的时间 以此对比 SHT10 开发文档中理论测量时间 到预警效果 值 确定实际驱动程序运行的效率 6 5 实验结果与分析 超级终端中插入驱动模块 运行测试程序 可以 在终端上看到测试结果(如图 4) 结语 此设计方案已经应用于嵌入式无声交互控制系统 的检测 并且运行正常 实践证明 该嵌入式 Linux 温湿度传感器设计方案可行有效 线程阻塞提高系统 运行效率 在环境测量准确度和系统实时性方面得到 了令人满意的效果 由于此方案基于 Linux 操作系统 和 PXA310 平台 其在多任务 实时快速处理上具有 一定的优势 参考文献 图 4 超级终端测试结果 系统功能实现后 利用上述 Time 计算公式计算 驱动程序中温湿度测量消耗的时间 实际测试结果如 表 3 所示 表 3 驱动程序中实际测量消耗的时间 1 清风电子.18B20.pdf,2007.5.http://www.mcubbs.net 2 Sensirion. C-Datasheet_SHTxx_2.04.pdf,2005.5. http:// www.sensi- rion.com/humidity 3 王粉花.基于 LINUX 设备驱动程序的设计与实现.计 算机工程, 2006,32(23):278 280. 4 刘淼.嵌入式系统接口设计与 Linux 驱动程序开发. 北京:北京航空航天大学出版社, 2006.5. 5 宋宝华.LINUX 设备驱动开发详解.北京:人民邮电出 上表的测试结果不仅和传感器的响应速度有关 而且还与系统中其他运行的线程有关 当系统中有高 一级任务到来或其他实时事件需要处理时 实际测量 130 实用案例 Application Case 版社, 2008. 6 魏永明,狄岳,钟书毅.LINUX 设备驱动程序.第 3 版. 北京:中国电力出版社, 2006.