tag 标签: colibri

相关博文
  • 热度 2
    2020-2-13 15:57
    4654 次阅读|
    1 个评论
    By Toradex 胡珊逢 1). 简介 定时器任务在 Linux 系统中是一项很普遍的应用,例如定期清理文件,在嵌入式系统则可以定时调用任务采集数据等。 Cron 是一个广为人知的定时计划任务管理器,但随着 systemd 兴起, systemd timers 也逐渐开始取代传统的 cron 。本文就将基于 Toradex Linux BSP 在 Colibri iMX8X 上如使用 systemd 定时器。 Systemd 定时器从触发时间上可以分为两种: • 单调定时器:从一个特定的时间开始后过一段时间触发,通常可以是系统启动或者 systemd 服务单元执行的开始时间 • 实时定时器:在某个特定时间触发,一般指时钟时间,这个就类似于 cron 定时任务 两种定时满足不同要求的任务,例如需要在某个明确并固定的时间来执行任务,如每周日晚上 12 点清理日志文件,则可以选择实时定时器。而对于有些无法实现预测执行时间,通常是要在系统启动后才定期执行,如某个任务在启动后每隔 10 分钟采样传感器数据,由于不确定系统启动时间,所有可以采用单调定时器。或者同时使用两个定时器功能。 Systemd 定时器的配置文件可以分为两部分,定时器单元以 .timer 后缀的 systemd 单元文件,以及服务单元以 .service 后缀的 systemd 单元文件。每个 .timer 文件通常对应一个同名的 .service 文件。定时器单元 .timer 文件中的 定义了该定时器何时以及如何触发。该定时器被触发后,执行对应的服务单元 .service ,其中的 定义了最终被执行的脚本或者应用。下面我们将演示如何使用。 2). 单调定时器 我们将设置一个系统启动 2 分钟后启动并在此之后每隔 15 分钟定期执行的定时任务。下面是所需的定时器单元 test1.timer 和服务单元 test1.service 。 ./ test1.timer ----------------------------------------- Description=Run every 15min and on boot OnBootSec=2min OnUnitActiveSec=15min WantedBy=timers.target ----------------------------------------- ./ test1.service ----------------------------------------- Description=Hello World ExecStart=/home/root/HelloWorld ----------------------------------------- 在 .timer 中 OnBootSec= 设置了该定时器需要在系统启动 2 分钟后被执行, OnUnitActiveSec= 设置了该定时器在成功执行后,每隔 15 分钟需要再次被执行。服务单元 test1.service 中 定义了 /home/root/HelloWorld 是需要被执行的应用。 把上面两个文件复制到 /etc/system/systemd 目录下, systemd-analyze 命令来核对文件是否有效。 ----------------------------------------- root@colibri-imx8x:/etc/systemd/system# systemd-analyze verify test1* # 启用 test1 定时任务,并在每次启动后都生效。设置完成后马上重启,查看该任务。 root@colibri-imx8x:/etc/systemd/system# systemctl start test1.timer root@colibri-imx8x:/etc/systemd/system# systemctl enable test1.timer root@colibri-imx8x:/etc/systemd/system# sync root@colibri-imx8x:/etc/systemd/system# reboot # 命令 systemctl list-timers 可以列出目前激活的定时任务。 root@colibri-imx8x:~# systemctl list-timers NEXT LEFT LAST PASSED UNIT ACTIVATES Tue 2020-02-11 16:39:19 CST 1min 30s left n/a n/a test1.timer test1.service Tue 2020-02-11 16:52:19 CST 14min left n/a n/a systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service 2 timers listed. Pass --all to see loaded but inactive timers, too. ----------------------------------------- 可以看到在系统启动后,还剩下 1 分 30 秒执行 test1.service ,这个是由 OnBootSec=2min 参数所设置的。 在等待数分钟再次查看定时器任务, test1.service 在 14 分钟后会被执行,这是由 OnUnitActiveSec=15min 参数所设置。 ----------------------------------------- root@colibri-imx8x:~# systemctl list-timers NEXT LEFT LAST PASSED UNIT ACTIVATES Tue 2020-02-11 16:52:19 CST 12min left n/a n/a systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service Tue 2020-02-11 16:54:28 CST 14min left Tue 2020-02-11 16:39:28 CST 26s ago test1.timer test1.service ----------------------------------------- 从系统日志里也可以观察到自 16:37:25 系统启动后,在 16:39:28 时 HelloWorld 被执行。 ----------------------------------------- root@colibri-imx8x:~# journalctl -u test1.service -- Logs begin at Tue 2020-02-11 16:37:25 CST, end at Tue 2020-02-11 16:40:49 CST. -- Feb 11 16:39:28 colibri-imx8x systemd : Started Hello World. Feb 11 16:39:28 colibri-imx8x HelloWorld : Hello world! ----------------------------------------- 再次等待 15 分钟以后,重新查看日志和定时器任务,在 16:54:30 时 HelloWorld 被执行,并且在 13 分钟后会再次执行。这符合定时器任务 test1 的设置预期。 ----------------------------------------- root@colibri-imx8x:~# journalctl -u test1.service -- Logs begin at Tue 2020-02-11 16:37:25 CST, end at Tue 2020-02-11 16:56:21 CST. -- Feb 11 16:39:28 colibri-imx8x systemd : Started Hello World. Feb 11 16:39:28 colibri-imx8x HelloWorld : Hello world! Feb 11 16:54:30 colibri-imx8x systemd : Started Hello World. Feb 11 16:54:30 colibri-imx8x HelloWorld : Hello world! root@colibri-imx8x:~# systemctl list-timers NEXT LEFT LAST PASSED UNIT ACTIVATES Tue 2020-02-11 17:09:30 CST 13min left Tue 2020-02-11 16:54:30 CST 1min 31s ago test1.timer test1.service Wed 2020-02-12 16:52:20 CST 23h left Tue 2020-02-11 16:52:20 CST 3min 42s ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service ----------------------------------------- 3). 实时定时器 这里我们将设置在每个小时第 10 分钟被执行的实时定时任务。下面是所需的定时器单元 test2.timer 和服务单元 test2.service 。 ./ test2.timer ----------------------------------------- Description=Run each 10min of hours OnCalendar=*-*-* *:10:00 WantedBy=timers.target ----------------------------------------- ./ test2.service ----------------------------------------- Description=Hello Toradex ExecStart=/home/root/HelloToradex ----------------------------------------- 在 test2.timer 中, OnCalendar= 设置定时器任务触发的时间。 OnCalendar 的时间格式为 DayOfWeek Year-Month-Day Hour:Minute:Second 。 其中 weekday 部分是可以省略的,定时器不匹配星期几,由后面的日期和时间部分来指定。如果填写则必须是英文中星期的缩写或者全称,如星期二是 Web 或者 Wednesday 。可以使用 “,” ,来指定多个星期天如 Mon,Web,Fri 。 “..” 用于指定连续的一段时间,如 Mon..Fri 指从周一到周五。 “,” 和 “..” 还可以混合使用来指定一周的几天。 日期和时间部分,可以使用 “*” 表示任何符合的日期或者时间。如 2020-*-* 1:23:00 表示 2020 年中每天的 1:23:00 时间触发定时器任务。也可以使用 “,” 来指定具体的而时间或日期, 2020-1,2-* 1,2:23:00 表示 2020 年中 1 月和 2 月每天的 1:23:00 和 2:23:00 触发定时器任务。日期、时间还有多种表达方式,包括时区,具体可以查看文章最后的参考链接。 在 .timer 中 OnCalendar=*-*-* *:10:00 将定时器设置为每天每小时第 10 分钟触发。 同样将上述两个文件复制到 /etc/systemd/system 目录,并启动该定时器。 ----------------------------------------- root@colibri-imx8x:/etc/systemd/system# systemctl enable test2.timer root@colibri-imx8x:/etc/systemd/system# systemctl start test2.timer # 系统当前时间是 17:29:28 ,距离 18:10:00 还有 40 分钟,因此在 40 分钟后 test2 定时器将被触发。 root@colibri-imx8x:~# date Tue Feb 11 17:29:28 CST 2020 root@colibri-imx8x:~# systemctl list-timers NEXT LEFT LAST PASSED UNIT ACTIVATES Tue 2020-02-11 17:41:57 CST 12min left n/a n/a systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service Tue 2020-02-11 18:10:00 CST 40min left n/a n/a test2.timer test2.service 2 timers listed. Pass --all to see loaded but inactive timers, too. # 在等待一段时间后,查看日志, 18:10:01 的时候 test2 定时器被触发,并执行 /home/root/HelloToradex 程序。 root@colibri-imx8x:~# journalctl -u test2.service -- Logs begin at Tue 2020-02-11 17:27:02 CST, end at Tue 2020-02-11 18:11:11 CST. -- Feb 11 18:10:01 colibri-imx8x systemd : Started Hello Toradex. Feb 11 18:10:01 colibri-imx8x HelloToradex : Hello Toradex! # 而距下一次触发的时间 19:10:00 还有一个小时。 root@colibri-imx8x:/etc/systemd/system# date Tue Feb 11 18:10:43 CST 2020 root@colibri-imx8x:~# systemctl list-timers NEXT LEFT LAST PASSED UNIT ACTIVATES Tue 2020-02-11 19:10:00 CST 59min left Tue 2020-02-11 18:10:01 CST 47s ago test2.timer test2.service Wed 2020-02-12 17:41:58 CST 23h left Tue 2020-02-11 17:41:58 CST 28min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service 2 timers listed. Pass --all to see loaded but inactive timers, too. # 那么在一个小时再次查看日志文件, 19:10:09 的时候 test2 定时器被触发。 root@colibri-imx8x:~# journalctl -u test2.service -- Logs begin at Tue 2020-02-11 17:27:02 CST, end at Tue 2020-02-11 19:23:40 CST. -- Feb 11 18:10:01 colibri-imx8x systemd : Started Hello Toradex. Feb 11 18:10:01 colibri-imx8x HelloToradex : Hello Toradex! Feb 11 19:10:09 colibri-imx8x systemd : Started Hello Toradex. Feb 11 19:10:09 colibri-imx8x HelloToradex : Hello Toradex! ----------------------------------------- 4). 混合定时器 除了上面单独使用单调或者实时定时器外,我们还可以同时利用两种定时器配置属性。 ./ test3.timer ----------------------------------------- Description=Run each 10min of hours and on boot OnBootSec=2min OnCalendar=*-*-* *:10:00 WantedBy=timers.target ----------------------------------------- ./ test3.service ----------------------------------------- Description=Hello Torizon ExecStart=/home/root/HelloTorizon ----------------------------------------- 这里 test3 定时器首先通过 OnBootSec=2min 设置为系统启动后 2 分钟触发,并且由 OnCalendar=*-*-* *:10:00 设置在每个小时第 10 分钟被执行的实时定时任务。这两个参数分别来自单调定时器和实时定时器配置。 ----------------------------------------- # 启动该定时器后重启系统 root@colibri-imx8x:/etc/systemd/system# systemctl enable test3.timer root@colibri-imx8x:/etc/systemd/system# sync root@colibri-imx8x:/etc/systemd/system# reboot # 系统启动后看到 test3 将会在 1 分 43 秒后触发。 root@colibri-imx8x:~# systemctl list-timers NEXT LEFT LAST PASSED UNIT ACTIVATES Tue 2020-02-11 19:27:30 CST 1min 42s left n/a n/a test3.timer test3.service Tue 2020-02-11 19:40:30 CST 14min left n/a n/a systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service # 在等待 2 分钟后, test3 已经被触发,并会在 42 分钟后再次触发,即 20:10:00 。 root@colibri-imx8x:~# systemctl list-timers NEXT LEFT LAST PASSED UNIT ACTIVATES Tue 2020-02-11 19:40:30 CST 12min left n/a n/a systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service Tue 2020-02-11 20:10:00 CST 42min left Tue 2020-02-11 19:27:31 CST 19s ago test3.timer test3.service # 从日志中看到,系统在 19:25:35 左右启动,在其后 2 分钟 19:27:31 触发了 test3 并执行 /home/root/HelloTorizon 。 root@colibri-imx8x:~# journalctl -u test3.service -- Logs begin at Tue 2020-02-11 19:25:35 CST, end at Tue 2020-02-11 19:28:09 CST. -- Feb 11 19:27:31 colibri-imx8x systemd : Started Hello Torizon. Feb 11 19:27:31 colibri-imx8x HelloTorizon : Hello Torizon! # 在等到 20:10:00 后查看定时器任务, test3 如预期在 20:10:04 的时候被触发, 44 分钟后下一次触发。 root@colibri-imx8x:~# systemctl list-timers NEXT LEFT LAST PASSED UNIT ACTIVATES Tue 2020-02-11 21:10:00 CST 44min left Tue 2020-02-11 20:10:04 CST 15min ago test3.timer test3.service Wed 2020-02-12 19:40:31 CST 23h left Tue 2020-02-11 19:40:31 CST 44min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service # 日志也记录了 20:10:04 时候 test3 被触发并执行 /home/root/HelloTorizon 。 root@colibri-imx8x:~# journalctl -u test3.service -- Logs begin at Tue 2020-02-11 19:25:35 CST, end at Tue 2020-02-11 20:25:26 CST. -- Feb 11 19:27:31 colibri-imx8x systemd : Started Hello Torizon. Feb 11 19:27:31 colibri-imx8x HelloTorizon : Hello Torizon! Feb 11 20:10:04 colibri-imx8x systemd : Started Hello Torizon. Feb 11 20:10:04 colibri-imx8x HelloTorizon : Hello Torizon! ----------------------------------------- 到这里我们已经演示了不同类型定时器的使用方法,有些读者可能已经发现定时器任务被触发的时间并不是很严格得按照所设定的时间。例如上面 test3 在第二次被触发的时间是 20:10:04 ,不是 20:10:00 。这是因为 Toradex Linux BSP 默认是普通的 Linux 内核,而非实时 Linux ,这就导致在任务时间分配上会存在一定的时间抖动。其次, systemd 定时器中的 AccuracySec 参数默认是 1min ,这设置了定时器的精度。定时器触发时间窗口为 OnCalendar= 、 OnActiveSec= 、 OnBootSec= 、 OnStartupSec= 、 OnUnitActiveSec= 所设置的时间开始到 AccuracySec= 结束。这样做的目的是减少不必要的 CPU 唤醒,从而降低功耗。为了提高定时器精度,可以将 AccuracySec= 设置到最小的 1us 。 为了避免在同一个时间触发多个定时器任务,从而导致系统资源紧张,可以使用 RandomizedDelaySec= 参数,为定时任务添加一个从 0 到 RandomizedDelaySec 的随机延时。 5). Systemd vs. Cron 上面的几个例子已经展示了 systemd 定时器的特点,其于传统的 Cron 比较如下: 优点: ./ Systemd 定时器可以利用 systemd 的依赖关系(如等待网络连接、某个文件的创建、其他应用就位后执行任务) ./ 定时任务会被记录到 journald 日志 ./ 定时器任务可以在系统 / 应用启动后触发 ./ 可以利用 cgroups ./ 可以单独执行任务而不依赖定时器( .timer 和 .service 分开配置) 缺点: ./ 相对复杂的配置过程, Cron 仅需一行命令即可 ./ Systemd 定时器默认不支持 MAILTO 功能 6). 总结 定时器任务作为嵌入式系统中常见的应用, systemd 定时器为用户提供更多的可配置功能以及优化选项。本文列举了 systemd 定时器基本操作方法,以及和 cron 对比,帮助用户更快得使用。更多的技术细节和功能请参考下面的链接内容。 参考: https://wiki.archlinux.org/index.php/Systemd/Timers https://www.freedesktop.org/software/systemd/man/systemd.timer.html https://jlk.fjfi.cvut.cz/arch/manpages/man/systemd.time.7 https://jlk.fjfi.cvut.cz/arch/manpages/man/systemd.timer.5
  • 热度 24
    2016-6-29 10:23
    2222 次阅读|
    0 个评论
    By Toradex Leonardo Graboski Veiga 1).  简介 物联网(Internet of Things)概念的本质其实就是关于发送数据到网络,所以称为云服务。随着时代发展和技术进步,人们可以使用尺寸更小功耗更低的电子设备并很容易的连接到云端,不过有一个问题却始终困扰着电子工程师们:如何使用这些获取的数据?而这正是物联网的主题。 在 Microsoft主页 上面有一些实际的应用例子来展示IoT的应用:一个电梯公司通过物联网来改善并提供预先维护;一个工业自动化公司通过物联网深入了解油气产业供应链,同时提供预先维护;还有一个公司通过IoT预测驾驶人员行为然后优化汽车利用。在读完这个系列文章后,我们期待读者可以拥有足够的知识和工具去部署应用来深度检视同时优化整个系统 – 不仅仅是积攒了一堆数据,而是从中获取了有用的结果! Azure 是Microsoft提供的云服务平台,提供了多种应用如数据库,虚拟机,应用服务,机器学习,数据流分析,媒体和CDN服务,大数据解决方案,以及包括IoT Hub的其他众多应用。就其提供的大量应用本身已经是使用Azure服务的很好理由,但Microsoft更进一步通过和Amazon Web Services的 对比 来进一步证明其方案是更好的 – 和前面相反,这是一个通过用户和时间来确认的强有力宣言。另外,高安全性,易于整合以及容易上手也是选择Azure服务的另一个理由。 本系列文章通过开发一个IoT应用,从读取现场传感器数据,展示数据到获取商业智能(BI)。所使用的用来连接传感器以及上传数据到云端的平台: Azure IoT certified partner  Toradex 的 Colibri VF61 计算机模块 +  Iris Carrier Board 。应用程序获取传感器数据然后上传到来自Microsoft Azure云解决方案的一个叫做Azure IoT Hub的IoT服务,然后就可以被各种所需要的Microsoft Azure服务来处理。这部分内容将在本系列文章的第二部分着重讲解,在此我们主要关注在如何配置Azure IoT Hub以及上传数据到它上面。 我们选用的IoT环境为模型车监控。为了演示方便,将Toradex平台和传感器置于遥控模型汽车内,如下图1所示;而图2则给出的目标应用的框图。   图1 :遥控汽车   图2 :应用框图 我们所选用的应用编程语言为Javascript 配合Node***: 一个服务器端(本文中即Toradex嵌入式系统)基于Chrome V8 引擎编译的Javascript解释器。这个选择是考虑到Azure IoT Hub SDKs 可以提供的开发库。但是需要注意的是现在IoT Hub SDKs正处于频繁更新中,每一次更新都会有些改变(至少Node相关),所以在使用前需要考虑清楚。本文所使用的Azure IoT Node包版本为1.0.1。 整个环境的搭建,从开发嵌入式系统应用,到配置Azure来获取数据我们分为3个主要步骤,下面会分别介绍: ./  配置Azure 环境 ./  添加设备并发送信息到IoT Hub ./ Toradex 嵌入式系统应用开发   2).  配置 Azure 环境 首先需要创建一个新的Azure账户:从Azure 网站可以申请30试用的免费账户。然后就可以使用账户里一定数目的信用额度来免费部署应用使用Azure 服务;同样,IoT Hub也有一个用于开发的包含有限资源的免费版本,且不受试用期限制。关于价格和IoT Hub的详细信息,请见 这里 。 设置好Azure账户后,需要创建IoT Hub。用新创建的Azure账户登陆Azure portal,选择 +New Internet of Things Azure IoT Hub。新的IoT Hub配置界面如下图3所示,“Pricing and scale tier”选项需要选择“Free”;然后在“Resource Group”选项创建一个新的资源组,另外“Location”选项需要和后面部署的服务保持一致;“Name”可以自由设定,而“IoT Hub Units”和”Device-to-cloud“选项在免费版本中则无法修改。点击“Create”后,服务就被部署了,这个过程可能需要几十秒时间。   图3 :从Azure Portal 创建IoT Hub 上面操作完成后,可以看到IoT Hub已经出现在控制台,也就是 Azure Portal主页 。点击后,如下图4所示页面会打开:里面包含“Essentials” 是如IoT Hub地区等基本信息;“Usage”是提供给系统管理员注册设备数目以及从设备发送信息数量的反馈信息;“Monitoring”是显示收到信息数量。   图4 :IoT Hub  主面板 仍然在IoT Hub主面板上,为了让其他应用也可以访问服务,”Settings“选项卡里面的“Shared access policies“选项需要被选中。在新打开的” Shared access policies“选项卡中,点击“iothubowner”规则选项,这个包含了本IoT Hub所有可能的权限。如下图5所示,“iothubowner”选项卡会打开,然后复制”Connection string – primary key”对应的内容留作后用:这个是下一步用于管理和监控这个IoT Hub服务的钥匙。   图5 :获取iothubowner connection string   3).  添加设备并发送信息到 IoT Hub 现在云端设置已经完成,我们需要在开发主机上面安装 iothub-explorer 工具来添加设备到IoT Hub,另外如果开发主机是Windows的话,也可以选择 Device Explorer 工具。鉴于本文所使用的开发主机系统为Ubuntu 14.04,我们采用iothub-explorer。需要注意所需Node版本为0.12.x或以上(根据说明如需全部功能工作需要4.x或以上版本),但目前apt-get工具只能安装0.10.x版本。为了解决这个问题,需要先后安装Node Version Manager(NVM)和Node 版本0.12.9. 然后在终端中使用NPM(Node Package Manager)来安装iothub-explorer。 --------------------- $ npm install iothub-explorer@latest --------------------- 然后可以运行iothub-explorer help参数来查看使用方法 --------------------- $ iothub-explorer help --------------------- 根据上面命令的打印结果,iothub-explorer 包含有create和monitor事件参数。首先,我们配合上面图5中获取的connection string使用iothub-explorer工具来创建一个设备“tdx_iot_car”。注意“--connection-string”参数用来显示设备connection string (和图5中获取的IoT Hub connection string不同),这个也需要保存下来用来连接这个新创建的设备到IoT Hub,是的可以使用Colibri VF61应用来发消息到Hub。 --------------------- $ iothub-explorer "HostName=toradex.azure-devices.net;SharedAccessKeyName=iothubowner;SharedAccessKey=putyoursharedaccesskeyfromtheconnectionstringhere" create tdx_iot_car --connection-string Created device tdx_iot_car - deviceId:                   tdx_iot_car generationId:               635931262207620183 etag:                       MA== connectionState:            Disconnected status:                     enabled statusReason:               null connectionStateUpdatedTime: 0001-01-01T00:00:00 statusUpdatedTime:          0001-01-01T00:00:00 lastActivityTime:           0001-01-01T00:00:00 cloudToDeviceMessageCount:  0 authentication: SymmetricKey:       primaryKey:   somesharedaccesskeyreturned       secondaryKey: somesecondaryaccesskeyreturned - connectionString: HostName=toradex.azure-devices.net;DeviceId=tdx_iot_car;SharedAccessKey=somesharedaccesskeyreturned ---------------------   4). Toradex 嵌入式系统应用开发 现在来设置 Colibri VF61  计算机模块 +  Iris 载板 。本文中使用Toradex发布的预编译Linux image( Colibri_VF_LinuxConsoleImageV2.5 ),如何刷写image到模块请参考 这里 。然后请参考下面步骤安装Node***, NPM 包和git – 安装过程需要一些时间,尤其是curl步骤。 --------------------- # opkg update # opkg install nodejs # opkg install tar # curl -L https://www.npmjs.com/install.sh | sh # opkg install git --------------------- 本文所展示的例程(send_data***)相关packages installer和node文件存放于 这里 ,可以通过下面命令将其clone到目标板上面并安装node packages --------------------- # git clone https://github.com/leograba/azure-iot-car.git # root@colibri-vf:~# cd azure-iot-car # root@colibri-vf:~# npm install --------------------- 现在我们可以在目标板上面运行例程向IoT Hub发送数据,但有几点需要解释下:例程使用HTTP协议通讯,但AMQP和MQTT协议也是支持的;变量“connecionString”数值必须和上面用iothub-explorer工具创建新设备时候所保存下来string一致: --------------------- var connectionString = "HostName=toradex.azure-devices.net;DeviceId=tdx_iot_car;SharedAccessKey=somesharedaccesskeyreturned" --------------------- Setinterval()循环函数随机产生数值发送到IoT Hub, 用来模拟传感器数据,如温度,声纳传感器距离数据,加速度和陀螺仪传感器,一些gps坐标数据和来自目标板的时间日期等。如何从真实传感器获取数据将在这个系列文章的下一篇进行说明。JSON Stringify() 函数用来产生一个JSON编码数据串,然后封装于Message object用于发送。下面是一个JSON格式数据串示例: --------------------- {"ObjectName":"toradex2", "ObjectType":"SensorTagEvent", "temp":24.889683, "acceleration: {"accel_x":10.018892,"accel_y":0.039468,"accel_z":-0.081328}, "gyroscope": {"gyro_x":-0.0532362,"gyro_y":-0.01597086,"gyro_z":0}, "distance":0.17017, "boardTime":1458064972706} --------------------- 正常情况下,在程序运行时候sendEvent()函数里面的callback函数不应打印任何串口输出。下面是在Colibri VF61上面运行程序并正常工作时候的串口打印输出: --------------------- # node send_data*** sending message to the IoT Hub sending message to the IoT Hub sending message to the IoT Hub sending message to the IoT Hub sending message to the IoT Hub sending message to the IoT Hub sending message to the IoT Hub sending message to the IoT Hub --------------------- 为了确保数据被收到,在Azure Portal IoT Hub面板会显示每天的消息计数,同时监控图表上面会有尖峰显示,如下图6所示。需要注意这些信息大概需要几十秒才会在portal上面显示出来。   图 6:  在Azure Portal 中确认数据被收到 另外,也可以使用iothub-explorer工具通过“monitor-event”参数配合device id来查看发送到IoT Hub的数据流,不过需要Colibri VF61程序要同步运行,而通过Azure Portal查看则不需要。下面图7展示了iothub-exploer收取目标板发送数据情况,上面是具体监测数据命令: --------------------- $ iothub-explorer "your_iothub_connection_string" monitor-events yourdevice ---------------------   图7 :用iothub-explorer 收取目标板发送数据   5).  总结 Microsoft Azure网站上面提供了很多文档用于帮助用户开发更复杂和稳固的应用。参考这些文档可以从中获取更多有用信息,如创建一个设备,或者从Hub获取设备发送的数据是可以通过编程来完成的。另外,在接下来的文章中我们将侧重连接真实传感器到Colibri VF61 + Iris载板,并传输真实传感器数据到IoT Hub, 这个也可以用作其他Azure 服务来给设备部署应用提供深度检视或变量操控。 我们希望通过本文可以让用户了解并最终使用Toradex 嵌入式系统方案配合Auzre IoT Hub服务,然后从中获益。同时,我们也想在这里感谢来自巴西的Grupo Viceri团队在Azure and Business Intelligence上面的丰富经验最终促成了这个IoT Car 项目。 本文最初发表于Embarcados.com, Portuguese,详见这里。
  • 热度 29
    2016-6-3 11:52
    1734 次阅读|
    0 个评论
    1). 简介 MQX是由NXP提供的实时操作系统,结构框图如下图所示, 包含内核,文件系统以及协议栈, 同时对于NXP的ARM处理器也可以很好的支持,可以很方便的开发和部署. 本文就基于Toradex Colibri VF61 (基于NXP VF61 SoC) ARM计算机模块搭配Colibri Eva Board开发板来演示编译和部署MQX应用. VF61为异构双核架构ARM处理器,包含一个Cortex-A5和一个Cortex-M4,通常应用场景为A5核心运行Linux或者WinCE系统作为控制和界面显示,M4核心运行一个实时操作系统来处理实时任务. 本文所演示的即是在M4核心上面部署运行MQX应用. 2). 准备 a). 硬件准备 ./ Colibri VF61计算机模块和Colibri Eva Board开发板 ./ Ubuntu Linux开发主机 b). 软件准备 ./ VF61模块A5核心运行Toradex Embedded Linux release V2.5 Beta3 ./ Ubuntu Linux开发主机 ./ MQX RTOS 4.1.2 Vybrid - 从这里下载 ./ Linaro bare-metal toolchain for Cortex-R/M, 4.8 2014-Q1 update - 从这里下载 3). 配置 a). 解压缩到”/home/username”目录下 ------------------------ $ tar xzf “~/MQX RTOS 4.1.2 for Vybrid Linux Base.gz.gz” $ tar xjf ~/gcc-arm-none-eabi-4_8-2014q1-20140314-linux.tar.bz2 ------------------------ b). 配置MQX ./ 文件 ”FSLMQXOS_4_1_2_Vybrid/build/common/make/global.mak” 设置交叉编译toolchain ------------------------ + TOOLCHAIN_ROOTDIR = /home/yourname/gcc-arm-none-eabi-4_8-2014q3/ ------------------------   ./ 文件  “FSLMQXOS_4_1_2_Vybrid/config/twrvf65gs10_m4/user_config.h” 由于VF61 A5和M4共享外设资源,因此在使用M4外设时候要通过修改A5 Device Tree来确保此外设没有被A5已经占用.本文后续只测试M4调用GPIO,因此将其他和A5冲突的外设资源都去掉,实际应用中可以根据具体情况做配置. ------------------------ ... #define BSPCFG_ENABLE_I2C0                  0 #define BSPCFG_ENABLE_II2C0                  0 ... #define BSPCFG_ENABLE_SPI0                   0 ... #define BSPCFG_ENABLE_RTCDEV               0 ... #define BSPCFG_ENABLE_ESDHC                0 #define BSPCFG_ENABLE_SAI                    0 ... #define BSPCFG_ENABLE_TTYB                   0 #define BSPCFG_ENABLE_ITTYB                  0   #define BSPCFG_ENABLE_TTYC                   1 #define BSPCFG_ENABLE_ITTYC                   0 ... ------------------------   ./ 文件 ”FSLMQXOS_4_1_2_Vybrid/mqx/source/bsp/twrvf65gs10_m4/twrvf65gs10_m4.h” 修改时钟 ------------------------ ... #define BSP_CORE_CLOCK                  (166700000) #define BSP_BUS_CLOCK                   (166700000) #define BSP_SYSTEM_CLOCK                (83300000) #define BSP_IPG_CLOCK                   (83300000) ... ------------------------ 4). 编译MQX a). 替换  “mqx/examples/hello/build/make/hello_twrvf65gs10_m4/build_gcc_arm.sh” 文件 从这里下载文件 b). 编译Helloworld Firmware ------------------------ $ cd mqx/examples/hello/build/make/hello_twrvf65gs10_m4/ $ ./build_gcc_arm.sh ... Build done Press any key to continue... ------------------------ c). 创建binary Firmware文件 ------------------------ $ cd ~/FSLMQXOS_4_1_2_Vybrid/mqx/examples/hello/build/make/hello_twrvf65gs10_m4/gcc_arm/ram_release $ export PATH=~/gcc-arm-none-eabi-4_9-2014q1/bin/:$PATH $ arm-none-eabi-objcopy -O binary hello_twrvf65gs10_m4.elf hello_twrvf65gs10_m4.bin ------------------------ d). 查看应用入口地址 由于二进制程序没有可以指明加载地址或入口地址的头部信息,因此需要在加载的时候手动添加 ------------------------ $ arm-none-eabi-objdump -f hello_twrvf65gs10_m4.elf   hello_twrvf65gs10_m4.elf:     file format elf32-littlearm architecture: arm, flags 0x00000112: EXEC_P, HAS_SYMS, D_PAGED start address 0x3f0007b1 ------------------------ e). 运行MQX 将bin文件复制到Colibri VF61 A5核心的Linux系统上面,运行下面命令启动mcc然后下载并运行MQX在M4核心 ------------------------ # modprobe mcc # mqxboot hello_twrvf65gs10_m4.bin 0x3f000000 0x3f0007b1 ------------------------ f). 查看M4串口输出 将Colibri开发板UARTB接口连接到开发主机 ------------------------ $ sudo minicom -D /dev/ttyUSB1 -d 115200 Welcome to minicom 2.7   OPTIONS: I18n Compiled on Jan  1 2014, 17:13:19. Port /dev/ttyUSB1, 14:58:22 Press CTRL-A Z for help on special keys   Hello World ------------------------ 5). GPIO demo 在M4运行MQX GPIO demo, 通过BSP_BUTTON1来控制打开和关闭BSP_LED1 a). 查看M4对应管脚定义 确定BSP_BUTTON1对应GPIO管脚为PTB16,BSP_LED1对应GPIO管脚为PTB0 ------------------------ $ vi FSLMQXOS_4_1_2_Vybrid/mqx/source/bsp/twrvf65gs10_m4/twrvf65gs10_m4.h   ... #define BSP_LED1                            (LWGPIO_PIN_PTB0) ... #define BSP_SW1                             (LWGPIO_PIN_PTB16) ... #define BSP_BUTTON1                         BSP_SW1 ... ------------------------ b). 确认M4 GPIO管脚资源 通过查询Colibri VF61手册和A5 Linux Device Tree文件, 发现PTB0被A5 Linux pwm0占用,而PTB16虽然被flexcan1占用但默认并为启用,因此首先需要根据这里的说明先重新编译A5 Linux Device Tree文件将pwm0 disable以免管脚资源冲突;然后在Colibri载板上面将PTB0 (SODIMM 59)连接到X21 LED1 (Pin 2), PTB16 (SODIMM 63)连接到X21 SW6 (Pin 11) c). 同样方法编译MQX GPIO Firmware ------------------------ $ cd ~/FSLMQXOS_4_1_2_Vybrid/mqx/examples/gpio/build/make/gpio_twrvf65gs10_m4 $ ./build_gcc_arm.sh $ cd gcc_arm/ram_release/ $ export PATH=~/gcc-arm-none-eabi-4_9-2014q1/bin/:$PATH $ arm-none-eabi-objcopy -O binary gpio_twrvf65gs10_m4.elf gpio_twrvf65gs10_m4.bin $ arm-none-eabi-objdump -f gpio_twrvf65gs10_m4.elf   gpio_twrvf65gs10_m4.elf:     file format elf32-littlearm architecture: arm, flags 0x00000112: EXEC_P, HAS_SYMS, D_PAGED start address 0x3f000e51 ------------------------ d). 部署并测试demo ./ 在VF61 A5 Linux上面部署并运行MQX GPIO到M4 ------------------------ # mqxboot gpio_twrvf65gs10_m4.bin 0x3f000000 0x3f000e51 ------------------------ ./ 在M4 串口输出打印按键3次 ------------------------ ====================== GPIO Example ====================== The (SW1) button is configured to trigger GPIO interrupt. Press the (SW1) button 3x to continue. ------------------------ ./ 按Colibri 开发板SW6 3次后,即可通过SW6控制LED1 ------------------------ Button pressed 3x   The (SW1) button state is now polled. Press the (SW1) button to switch LED on or off   Button pressed ------------------------ 6). MCC Pingpong demo 下面通过MCC Pinpong demo来测试A5和M4之间的信息传递 a). 配置MCC版本 MQX 4.1.2版本默认采用MCC V2版本,但也同时提供了V1版本源代码;由于目前VF61 Linux只支持MCC V1版本,因此首先要用V1版本源代码替换V2版本 ------------------------ $ cd ~/FSLMQXOS_4_1_2_Vybrid/mcc/source $ unzip mcc_version_1_2.zip -d ../.  //按提示选择全部替换 $ vi mcc_vf600.c //如下面红色部分修改 static const unsigned int mcc_cpu_to_cpu_vectors[] = { GIC_CPU_to_CPU_int0, NVIC_CPU_to_CPU_int0 }; ------------------------ b). 编译MCC ------------------------ $ cd ~/FSLMQXOS_4_1_2_Vybrid/mcc//build/make/mcc_twrvf65gs10_m4 $ ./build_gcc_arm.sh ------------------------ c). 编译Pingpong demo ------------------------ $ cd ~/FSLMQXOS_4_1_2_Vybrid/mcc/examples/pingpong/build/make/pingpong_example_twrvf65gs10_m4 $ ./build_gcc_arm.sh ------------------------ d). 部署运行测试Pingpong demo 和之前同样方法获得bin和入口地址,并通过A5 Linux运行到M4上面 ./ A5 Linux部署执行Pingpong demo ------------------------ $ mqxboot pingpong_example_twrvf65gs10_m4.bin 0x3f000000 0x3f001095 ------------------------ ./ 运行后, M4串口输出 ------------------------ Responder task started, MCC version is 001.002   ------------------------ ./ 然后在A5 Linux运行自带的Pingpong应用 ------------------------ mcc-pingpong ------------------------ ./ 然后M4串口输出 ------------------------ Responder task received a msg                                                   Message: Size=4, DATA = 1                                                       Responder task received a msg                                                    Message: Size=4, DATA = 3 ... ------------------------ ./ 同样A5串口输出 ------------------------ version: 001.002                                                                Message: Size=4, DATA = 2 after 0.004325 seconds.                                Message: Size=4, DATA = 4 after 0.004340 seconds.                               Message: Size=4, DATA = 6 after 0.004669 seconds. ... ------------------------ 7). 总结 本文简单展示了在VF61 M4核心上面运行MQX以及A5和M4核心通信例程,可见MQX可以非常方便的部署在NXP处理器上面,详细的MQX应用开发可以参考下面文档. FSLMQXOS_4_1_2_Vybrid/doc http://developer.toradex.com/knowledge-base/mqx-on-the-cortex-m4-of-a-colibri-vf61
  • 热度 25
    2016-5-26 15:36
    1472 次阅读|
    0 个评论
    By Toradex 胡珊逢 ARM平台嵌入式Linux下有些应用对系统启动时间有着特殊的要求。在很多场合下,这些系统并不需要针对所有任务立即就位,但是针对某些关键任务(例如接收以太网命令或者显示用户界面)则必须能够应对。该博文将提供一些方法和简单的步骤,基于Toradex  Colibri i.MX6  ARM系统模块上优化启动时间。   提示:  文中涉及到的部分方法需要重新编译 U-boot、内核以及文件系统。请参考文末所附Toradex开发者中心网站上的相关文章。   在我们开始动手优化之前, 我们需要一个合适的方法来测量启动时间。如果想要十分精准地测量启动时间,这甚至需要牵涉到硬件(例如 GPIO 和示波器)。在绝大多数场合下,通过监控系统串口控制台输出已经是相当准确了。Tim Bird 的  grabserial  是一个广泛使用的工具,可以用于查看串口控制台输出的时间信息。这个工具能够为收到的每一行信息添加上时间戳,如下面所示: -------------------------------------- ban@LinuxDev:~/software/grabserial-1.8.1$ sudo ./grabserial -d /dev/ttyUSB0 -t U-Boot 2015.04 (Dec 15 2015 - 16:07:42) CPU:   Freescale i.MX6DL rev1.1 at 792 MHz Reset cause: POR I2C:   ready DRAM:  512 MiB …… --------------------------------------   第一列数字代表时间戳(从收到第一个字符算起),第二行代表的是收到当前一行和上一行信息之间的时间间隔。   Linux 系统的启动,主要可以分为以下 3 个阶段,文章将逐一讨论。 - Boot loader - Linux kernel - User space (init system)   Boot loader   实际上在 boot loader 启动之前,还有两个步骤:硬件初始化和 boot ROM。硬件初始化需要满足电源上电顺序以及总线和处理器芯片复位时序要求。这个阶段的耗时一般是固定的,在 10 ~ 200 ms。   图 1 boot ROM 启动时间   图1 中,黄色信号是核心板 VCC,蓝色信号是串口输出 TX。从核心板上电到串口输出第一个字符之间的时间大约为 102ms。这个就是 Colibri I.MX6D 的 硬件初始化和 boot ROM 运行的时间。由于这部分代码是固化在SoC 上面,所以用户一般无法对其进行优化。 ARM 处理器从位于内部的 ROM 上启动固件。该固件从启动介质上加载 boot loader。该阶段的时间一般很短,取决于 boot loader 的大小。除了减小 boot loader 体积,很难做其他的优化。实际上能够做的优化和调整还是在 boot loader (U-Boot)。   目前发布的 V2.5 Beta 3 版本,从第一个字符输出到内核启动的时间约为 1.7 秒。主要涉及以下过程: - U-Boot 初始化(约 370 ms,从接收到的第一个字符算起) - Autoboot delay(1s)  - 读取 device tree  和内核加载(127 ms) - 加载device tree  和内核加载(约 100 ms) - 最后跳转至内核起始地址 Uboot 启动到内核启动时间:~1700ms 最显著的优化就是降低 Autoboot delay 。这个值可以使用下面的命令设置为 0: -------------------------------------- setenv bootdelay 0 saveenv --------------------------------------   这个也可以使用 CONFIG_BOOTDELAY 将其配置为默认值。在目前的发布版本中,如果将 bootdelay 设置为0,那么将会没有办法直接进入 boot loader 的命令行模式。U-Boot 提供一个选项CONFIG_ZERO_BOOTDELAY_CHECK,在 bootdelay 为 0 的情况下,用于检测一个字符。我们已经将其添加到下一个发布版本的默认配置中。 Uboot 启动到内核启动时间:~700ms   移除一些功能有助于减少分配时间和初始化这些功能的时间。例如移除 USB Client 和 DFU 功能,相关Patch请从 这里 下载。   这可以将 U-Boot 尺寸减小到 289 KB,缩短 U-Boot 读取时间 。用同样的方法,可以结合项目实际的外设需求,裁剪不需要的外设支持和功能。显然内核大小和加载时间具有线性关系,因此,优化内核尺寸将可以进一步提高启动时间。 Kernel 为了只测量内核启动的时间,可以使用 grabserial 的匹配功能重置 boot loader 输出信息中的时间。 -------------------------------------- sudo ./grabserial -d /dev/ttyUSB0 -t -m "^Starting kernel*" -q "^\ 0-9.]* Freeing unused kernel memory.*" --------------------------------------   启动结束的时间多少有点难以确定,因为内核将会继续初始化硬件,即使文件系统已经挂载和第一个用户空间进程(init)开始运行(延时初始化)。 “Freeing unused kernel memory” 是 init 进程启动前发出的最后消息,因此将其标记为内核线性任务的结束(请查看 kernel_init in init/main.c)。我将会使用这个信息的时间戳信息来比较启动时间。我们模块上默认内核的压缩尺寸为 4.8MB,启动时间为 4.6 秒。   Kernel 启动到 init 启动时间:4.6秒。   同 U-Boot 一样,Linux 内核也是同步地将信息发送到串口。具体的方法取决于所使用的串口,其会同步等待直到字符在串口上发送完毕。这个的优点在于,当内核崩溃的时候,那个时候的所有信息都是可见的。假如信息是异步输出,最后输出的信息将不 会指示内核所崩溃的地方。   内核中有一个参数,可以最大限度减少输出的信息:“Quiet”。然而,这也将屏蔽我们测试启动时间的字符信息(“Freeing unused kernel memory”)。最简单的方法输出这些信息是利用日志级别输出特定的信息。在'mm/page_alloc.c' 中搜索 “Freeing %s memory”。我将使用 ‘pr_alert’ 输出信息。这个方法起高了 3秒。 -------------------------------------- Starting kernel ... …… Freeing unused kernel memoryban@LinuxDev:~/software/grabserial-1.8.1$ --------------------------------------   Kernel 启动到 init 启动时间提高到 1.4秒。   进一步提高启动时间的另一个简单的方法是移除功能。Yocto 项目提供了一个方便的工具 ksize.py,这个需要在内核编译目录中运行。这个工具能够显示内核各个部分的大小。第一个表格显示了大致的概况(为了获得准确的概况, 编译之前使用 make clean)。 -------------------------------------- toradex@tdx-lab-linux:~/Toradex/webinar/linux-toradex$ ./ksize.py Linux Kernel                          total |       text       data        bss -------------------------------------------------------------------------------- vmlinux                             9321389 |    8589061     311188     421140 -------------------------------------------------------------------------------- drivers/built-in.o                  2973586 |    2810983     121247      41356 …… usr/built-in.o                          138 |        138          0          0 -------------------------------------------------------------------------------- sum                                 8174774 |    7475382     283956     415436 delta                               1146615 |    1113679      27232       5704 --------------------------------------   可以被安全移除的一般是应用相关的功能。浏览各个第一层目录,有助于快速确定最可能移除的对象。 另外一个可以帮助我们进一步分析,内核启动时各个功能所消耗的时间是使用 Linux 源码中scripts/bootgraph.pl 脚本,将启动日志进行图形化分析。为此,需要在 U-boot 中配置 -------------------------------------- setenv defargs 'enable_wait_mode=off galcore.contiguousSize=50331648 initcall_debug printk.time=1 quiet' --------------------------------------   在 Linux 导出日志 -------------------------------------- root@colibri-imx6:~# dmesg boot.log --------------------------------------   在电脑上运行以下命令 -------------------------------------- cat ./boot.log | ./scripts/bootgraph.pl boot.svg --------------------------------------   图 2 Kernel boot   根据上面工具的分析,以及实际项目外设和功能的需求,将不必要的驱动从内核中移除。例如我们将移除无线网络驱动、CAAM(i.MX6硬件加密加速模块)、文件系统(ext4, nfs, cifs 等)、SPI等功能。重新编译后,新的内核缩减到 3.6 MB。 -------------------------------------- Starting kernel ... …… Freeing unused kernel memoryban@LinuxDev:~/software/grabserial-1.8.1$ --------------------------------------   Kernel 启动到 init 启动时间提高到 0.9秒。   User Space Systemd 在 Linux 用户空间,初始化工作由 init 系统完成。 Toradex BSP 镜像使用 Ångströ标准启动 init 系统,其称为Systemd。Systemd 目前已成成为桌面 Linux的标准 init 系统,具有丰富的功能,特别是为动态系统所设计。Systemd 同样会影响启动时间。多个守护进程可以用同时启动(利用现在的多核系统); 支持在稍后的一个时间延时加载服务时激活 socket 以及支持按需启动的设备激活。并且,集成的日志守护进程 journald 由于使用二进制日志文件和完善的日志文件管理可以节省空间。 根据实际应用,一个嵌入式系统可能是相当静态的。因此,并不需要 Systemd 的动态功能。不幸的是,Systemd 并不是一个很模块化的系统,各个模块之间由相互依赖关系。这使得精简 Systemd 变得困难。这一节将分文两部分,第一部分使用 Systemd 启动优化技术,第二部分会使用 System V。 在这两部分中,我们使用 “Freeing unused kernel memory” 作为测量基准时间。 sudo ./grabserial -d /dev/ttyUSB0 -t -m "^\ 0-9.]* Freeing unused kernel memory.*"   在默认的 BSP 中 User space 启动到Login 的时间为 ~5.3 秒。 -------------------------------------- Failed to start Load Kernel Modules. …… colibri-imx6 login: --------------------------------------   由于采用了自己编译的内核,因此在加载模块驱动的时候提示错误。只需要将内核模块重新编译,放到目标板的 /lib/modules/ 目录中即可。当然,如果应用中需要相应的驱动,完全可以不加载这些模块驱动。 -------------------------------------- root@colibri-imx6:~# rm /etc/modules-load.d/libcomposite.conf --------------------------------------   systemctl 工具可以查看所有的启动项目 -------------------------------------- root@colibri-imx6:~# systemctl status --------------------------------------   Systemd 提供了 systemd-analyze 工具,当使用 “blame” 时,能够打印出各个服务以及其启动的时间。这个可以发现最消耗启动时间的服务。但是,其中的值可能具有迷惑性,因为测量的时间是实际流逝的时间。服务有可 能处于睡眠状态,这时的 CPU 其实在处理其他任务。所以在列表顶部的服务不一定是最耗时的,特别是在单核系统上。默认的 BSP 中并没有包含  systemd-analyze,可以通过下面的命令,在线安装。 -------------------------------------- root@colibri-imx6:~# opkg update root@colibri-imx6:~# opkg install systemd-analyze root@colibri-imx6:~# cat systemd.blame                                                                                                                    3.188s dev-mmcblk0p2.device                                                                                                                      1.080s systemd-journal-flush.service                                                                                                            ……                                                                                                                   178ms systemd-fsck-root.service --------------------------------------   systemd-analyze plot 也可以用图形的形式输出启动项的情况。 -------------------------------------- root@colibri-imx6:~# systemd-analyze plot systemd.svg --------------------------------------   图 3 Systemd boot   启动服务可以使用 disable 命令来关闭。有些服务(特别是 Systemd 自身提供的)可能需要掩码才能关闭它们。另外有一些可能是系统运行所需的。因此,在关闭服务时需要特别小心,而且一次只能处理一个。 -------------------------------------- root@colibri-imx6:~# systemctl disable usbg.service --------------------------------------   Toradex 的 BSP 中,采用 ConnMan 作为网络管理工具,可以很灵活的管理无线网络连接。 但 ConnMan 的启动会消耗较多的时间。对于不使用网络或仅用有线网络的情况,/etc/systemd/network/wired.network 配置可以有效降低启动时间。 Toradex 的 BSP 中,采用 ConnMan 作为网络管理工具,可以很灵活的管理无线网络连接。 但 ConnMan 的启动会消耗较多的时间。对于不使用网络或仅用有线网络的情况,/etc/systemd/network/wired.network 配置可以有效降低启动时间。 -------------------------------------- Name=eth0   DHCP=ipv4 --------------------------------------   在不需要记录系统日志的应用中,将日志存储功能禁用后,也可以在一定程度上缩减启动时间。 -------------------------------------- /etc/systemd/journald.conf Storage=none #Compress=yes --------------------------------------   内核参数中的 quiet,同样也适用于 Systemd。这个有助于 Systemd 的启动时间。 按照上述的优化,User space 启动到Login 的时间为 ~3.8 秒。 -------------------------------------- Freeing unused kernel memory: 280K (805f6000 - 8063c000) systemd-fsck : rootfs: clean, 21509/876392 files, 446716/3543040 blocks …… colibri-imx6 login: --------------------------------------     System V init 在很长一段时间内,Linux 也使用 SysV 作为标准的 init 系统。由于基于脚本的系统,这是模块化的,并且可以相对容易地精简系统。特别是对相对静态的系统,并不需要 Systemd 的设备激活和 socket 激活。此时,SysV可以是很好的选择。 Toradex 的 Linux BSP 基于 OpenEmbedded 框架发布,用户可以很方便的配置  SysV。所需的patch文件请从 这里 下载。   重新编译使用 SysV 的镜像 -------------------------------------- toradex@tdx-lab-linux:~/Toradex/oe-core/v2.5/oe-core/build$ bitbake  console-trdx-image --------------------------------------   在使用上面的镜像更新目标板后,在 Linux 执行 -------------------------------------- root@colibri-imx6:~# /usr/sbin/resize.sh --------------------------------------   如果使用的是 Colibri I.MX6D 512MB , 在 U-boot 中执行 -------------------------------------- Colibri iMX6 #  patch_ddr_size --------------------------------------   在使用  SysV 的系统上,同样也可以将一些不需要的启动项删除。 -------------------------------------- root@colibri-imx6:~#update-rc.d -f bootlogd remove root@colibri-imx6:~#update-rc.d -f mountall.sh remove root@colibri-imx6:~#update-rc.d -f networking remove --------------------------------------   按照上述的优化,User space 启动到Login 的时间为 ~1.1 秒。 -------------------------------------- Freeing unused kernel memory: 280K (805f6000 - 8063c000) INIT: version 2.88 booting ……   colibri-imx6 login: --------------------------------------   在 /etc/init.d 建立启动脚本加载应用 -------------------------------------- root@colibri-imx6:vi /etc/init.d/drawing.sh ### BEGIN INIT INFO # Provides:       toradex # Default-Start:  3 5 # Default-Stop:   0 1 2 6 # Description:    draw rectangles on fb0 ### END INIT INFO /home/root/rectangles root@colibri-imx6:chmod a+x drawing.sh root@colibri-imx6:update-rc.d drawing.sh defaults --------------------------------------   到此,使用上述方法,应用程序能在 2.55 秒时间内启动。 -------------------------------------- ban@LinuxDev:~/software/grabserial-1.8.1$ sudo ./grabserial -d /dev/ttyUSB0 -t …… Starting kernel ... …… colibri-imx6 login: -------------------------------------- 参考: Flashing Embedded Linux to iMX6 Modules http://developer.toradex.com/knowledge-base/flashing-linux-on-imx6-modules Build U-Boot and Linux Kernel from Source Code http://developer.toradex.com/knowledge-base/build-u-boot-and-linux-kernel-from-source-code OpenEmbedded (core) http://developer.toradex.com/knowledge-base/board-support-package/openembedded-%28core%29
  • 热度 25
    2016-5-5 17:15
    1954 次阅读|
    0 个评论
    By Toradex 秦海 1).  简介 随着ARM平台性能的日益强大和嵌入式设备的发展,对于多媒体处理如音视频播放,摄像头,流媒体处理等需求也日益增多,本文就通过几个基于嵌入式Linux下多媒体应用的示例来简单展示下使用Gstreamer框架进行多媒体处理的方法. Gstreamer 是一个基于Pipeline的多媒体框架,基于GObject,以C语言写成,目前是嵌入式Linux最为常用的处理多媒体应用框架. Element是Gstreamer最重要和基本的对象类,通过Plugin的形式提供,多个Elements可以组合为bin,并进一步聚合形成一个Pipeline完成一个多媒体应用处理. 2).  硬件准备 ./ 本文测试所使用平台为Toradex  Colibri i.MX6  ARM计算机核心板模块, 基于NXP i.MX6 SoC, 配合 Colibri开发板 . ./ 摄像头作为多媒体输入源使用Logitech 720P USB摄像头 3).  软件准备 ./ OS为Toradex官方Ycoto project兼容嵌入式 Linux发布  V2.5 beta3. ./ Gstreamer-0.10, 系统自带 ./ 安装gstreamer相关插件包 ---------------- root@colibri-imx6:~# opkg update root@colibri-imx6:~# opkg install gst-plugins-base-meta gst-plugins-good-meta   root@colibri-imx6:~# wget http://feeds.angstrom-distribution.org/feeds/v2015.06/ipk/glibc/armv7ahf-vfp-neon/base/libavcodec54_9.16-r0.9_armv7ahf-vfp-neon.ipk root@colibri-imx6:~# wget http://feeds.angstrom-distribution.org/feeds/v2015.06/ipk/glibc/armv7ahf-vfp-neon/base/libswscale2_9.16-r0.9_armv7ahf-vfp-neon.ipk root@colibri-imx6:~# wget http://feeds.angstrom-distribution.org/feeds/v2015.06/ipk/glibc/armv7ahf-vfp-neon/base/libavutil52_9.16-r0.9_armv7ahf-vfp-neon.ipk root@colibri-imx6:~# wget http://feeds.angstrom-distribution.org/feeds/v2015.06/ipk/glibc/armv7ahf-vfp-neon/base/libavformat54_9.16-r0.9_armv7ahf-vfp-neon.ipk   root@colibri-imx6:~# opkg install libavutil52_9.16-r0.9_armv7ahf-vfp-neon.ipk root@colibri-imx6:~# opkg install libavcodec54_9.16-r0.9_armv7ahf-vfp-neon.ipk root@colibri-imx6:~# opkg install libswscale2_9.16-r0.9_armv7ahf-vfp-neon.ipk root@colibri-imx6:~# opkg install libavformat54_9.16-r0.9_armv7ahf-vfp-neon.ipk   root@colibri-imx6:~# opkg install gst-ffmpeg ---------------- 4).  测试 gstreamer Gstreamer提供了两个非常方便的工具gst-launch和gst-inspect,在真正将Gstreamer pipeline集成到你的程序里面前,可以使用这两个工具在命令行下面进行pipeline的准备和测试,本文也主要基于这种模式进行演示,而详细的关于Gstreamer在程序中使用方法请参考 这里 . gst-inspect可以用来查看当前系统已经安装的plugin或者具体查看某个element的属性来判定pipeline中elements互联兼容性,详细说明请见 这里 . gst-launch 用于编译和运行一个pipeline,可以方便的对pipeline进行不同组合尝试并测试,如下面用来显示一个视频测试图案. ---------------- root@colibri-imx6:~# gst-launch videotestsrc ! 'video/x-raw-rgb, width=(int)640,height=(int)480' ! ximagesink ----------------     5).  应用示例 a).  播放摄像头   使用v4l2src元件采集摄像头视频,视频格式为YUY2/帧率30/分辨率640x480; 使用imxv4l2sink元件利用GPU加速播放采集的视频; 如果使用ximagesink元件来播放则没有硬件加速. ---------------- root@colibri-imx6:~# gst-launch v4l2src device=/dev/video0 ! 'video/x-raw-yuv, framerate=30/1, width=(int)640, height=(int)480, format=(fourcc)YUY2' ! imxv4l2sink disp-width=640 disp-height=480 ---------------- 目标板CPU占用率: 37.5%   b).  采集摄像头保存为文件 同样使用v4l2src元件采集摄像头视频; ffmpegcolorspace元件转换颜色格式为I420; vpuenc元件调用硬件VPU进行H.264编码; matroskamux元件进行mkv mux;  filesink元件输出到文件 ---------------- root@colibri-imx6:~# gst-launch -v v4l2src device=/dev/video0 ! ffmpegcolorspace ! 'video/x-raw-yuv, framerate=30/1, width=(int)640, height=(int)480, format=(fourcc)I420' ! queue max-size-buffers=2 ! vpuenc codec=6 ! matroskamux ! filesink location=output.mkv ---------------- 目标板CPU占用率: 40%   c).  回放视频文件 使用filesrc元件读取视频文件; aiurdemux元件进行demux; vpudec元件通过硬件VPU进行H.264解码; imxv4l2sink元件播放. ---------------- root@colibri-imx6:~# gst-launch filesrc location=/home/root/output.mkv typefind=true ! aiurdemux ! queue ! vpudec ! imxv4l2sink disp-width=640 disp-height=480 ---------------- 目标板CPU占用率: 3.5%   d). RTP 摄像头流媒体到远程主机 使用v4l2src元件采集摄像头; ffmpegcolorspace转换颜色格式; vpuenc元件调用硬件VPU进行H.264编码;rtph264pay元件进行h264流rtp封装; udpsink元件进行udp传输. ---------------- //Ubuntu主机IP: 10.20.1.116 //Colibri i.MX6目标板IP: 10.20.1.122   /* Colibri i.MX6 */ gst-launch -v v4l2src device=/dev/video0 ! ffmpegcolorspace! 'video/x-raw-yuv, framerate=30/1, width=(int)640, height=(int)480, format=(fourcc)I420' ! vpuenc codec=6 ! rtph264pay ! udpsink host=10.20.1.116 port=5000   /* Ubuntu 主机 */ //Ubuntu 14.04, 安装gstreamer-ffmpeg wget http://ppa.launchpad.net/mc3man/gstffmpeg-keep/ubuntu/pool/main/g/gstreamer0.10-ffmpeg/gstreamer0.10-ffmpeg_0.10.13-5ubuntu1~trusty2.1_amd64.deb sudo dpkg -i gstreamer0.10-ffmpeg_0.10.13-5ubuntu1~trusty2.1_amd64.deb //播放流媒体, caps设置从目标板获得 gst-launch-0.10 udpsrc port=5000 caps="application/x-rtp, media=(string)video, clock-rate=(int)90000, encoding-name=(string)H264, sprop-parameter-sets=(string)\"Z0JAHqaAoD2QAA\\=\\=\\,aM44gAA\\=\", payload=(int)96, ssrc=(uint)1172839120, clock-base=(uint)784846202, seqnum-base=(uint)58343" ! rtph264depay ! ffdec_h264 ! ximagesink ---------------- 目标板CPU占用率: 35%   6). Gstreamer1.0 应用示例 Colibri i.MX6 最新Linux发布版本V2.6beta1已经支持gstreamer1.0,升级方法请见这里 ./ 软件准备 安装相关插件包 ---------------- opkg update opkg install gstreamer1.0-plugins-base-meta opkg install gstreamer1.0-plugins-good-meta opkg remove --force-depends libgstimxcompositor0 //目前版本中需要移除否则无法使用gstreamer ---------------- ./ 应用示例 a).  播放摄像头 ---------------- root@colibri-imx6:~# gst-launch-1.0 imxv4l2src device=/dev/video0 ! 'video/x-raw, framerate=30/1, width=(int)640, height=(int)480, format=(string)YUY2' ! imxv4l2sink overlay-width=640 overlay-height=480 ---------------- 目标板CPU占用率: 30%   b).   采集摄像头保存为文件 ---------------- root@colibri-imx6:~# gst-launch-1.0 imxv4l2src device=/dev/video0 ! videoconvert ! 'video/x-raw, framerate=30/1, width=(int)640, height=(int)480, format=(string)I420' ! queue max-size-buffers=2 ! vpuenc_h264 ! matroskamux ! filesink location=output.mkv ---------------- 目标板CPU占用率: 16%   c).  回放视频文件 ---------------- root@colibri-imx6:~# gst-launch-1.0 filesrc location=/home/root/output.mkv typefind=true ! matroskademux ! queue ! vpudec ! imxv4l2sink overlay-width=640 overlay-height=480 ---------------- 目标板CPU占用率: 4%   d). RTP 摄像头流媒体到远程主机 ---------------- //Ubuntu主机IP: 10.20.1.116 //Colibri i.MX6目标板IP: 10.20.1.122   /* Colibri i.MX6 */ gst-launch-1.0 imxv4l2src device=/dev/video0 ! videoconvert ! 'video/x-raw, framerate=30/1, width=(int)640, height=(int)480, format=(string)I420' ! queue max-size-buffers=2 ! vpuenc_h264 ! rtph264pay ! udpsink host=10.20.1.116 port=5000 /* Ubuntu 主机 */ //Ubuntu 14.04, 安装libav (同0.10 ffmpeg) sudo apt-get install gstreamer1.0-libav   //播放流媒体 gst-launch-1.0 udpsrc port=5000 caps="application/x-rtp, media=(string)video, clock-rate=(int)90000,encoding-name=(string)H264, payload=(int)96" ! rtph264depay ! avdec_h264 ! videoconvert ! ximagesink ---------------- 目标板CPU占用率: 13%   e).  摄像头连接到远程主机tcp 视频流到目标版再rtp 回远程主机播放 ---------------- //Ubuntu主机IP: 10.20.1.116 //Colibri i.MX6目标板IP: 10.20.1.122   /* Ubuntu 主机 */ //采集摄像头并建立tcp server gst-launch-1.0 v4l2src device=/dev/video0 ! videoconvert ! 'video/x-raw, framerate=30/1, width=(int)640, height=(int)480, format=(string)I420' ! jpegenc ! tcpserversink host=10.20.1.116 port=5000   /* Colibri i.MX6 */ //从tcp server获取摄像头视频流,并进行H.264压缩后rtp回远程主机 gst-launch-1.0 tcpclientsrc host=10.20.1.116 port=5000 ! jpegdec ! queue max-size-buffers=2 ! vpuenc_h264 ! rtph264pay ! udpsink host=10.20.1.116 port=5001   /* Ubuntu 主机 */ //播放rtp视频流 gst-launch-1.0 udpsrc port=5001 caps="application/x-rtp, media=(string)video, clock-rate=(int)90000,encoding-name=(string)H264, payload=(int)96" ! rtph264depay ! avdec_h264 ! videoconvert ! ximagesink ---------------- 目标板CPU占用率: 31%   7).  总结 由上面示例可见Gstreamer是一个非常强大且配置灵活的多媒体处理框架,并且配合相应的plugin,也可以充分调用硬件GPU/VPU加速,使其能够在嵌入式设备上面利用有限的资源高效的实现广泛的多媒体应用.
相关资源