【问题标题】:millis() function output using Serial.print()使用 Serial.print() 的 millis() 函数输出
【发布时间】:2021-03-09 03:45:27
【问题描述】:

这是代码

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);

}

void loop() {
  // put your main code here, to run repeatedly:
  Serial.println(millis());

}

这是输出。

15:26:28.813 -> 0 15:26:28.813 -> 0 15:26:28.932 -> 0 15:26:28.932 -> 0 15:26:28.932 -> 0 15:26:28.932 -> 0 15:26:28.932 -> 0 15:26:28.932 -> 0 15:26:28.932 -> 0 15:26:28.932 -> 0 15:26:28.932 -> 0 15:26:28.932 -> 0 15:26:28.932 -> 0 15:26:28.932 -> 0 15:26:28.932 -> 0 15:26:28.932 -> 1 15:26:28.932 -> 1 15:26:28.932 -> 1 15:26:28.932 -> 1 15:26:28.932 -> 1 15:26:28.932 -> 1 15:26:28.932 -> 1 15:26:28.932 -> 4 15:26:28.932 -> 7 15:26:28.932 -> 10 15:26:28.932 -> 14 15:26:28.932 -> 18 15:26:28.932 -> 22 15:26:28.932 -> 26 15:26:28.932 -> 30 15:26:28.932 -> 34 15:26:28.932 -> 38 15:26:28.932 -> 43 15:26:28.932 -> 47 15:26:28.932 -> 51 15:26:28.932 -> 55 15:26:28.959 -> 59 15:26:28.959 -> 64 15:26:28.959 -> 68 15:26:28.959 -> 72 15:26:28.959 -> 76 15:26:28.959 -> 80 15:26:28.990 -> 84 15:26:28.990 -> 89 15:26:28.990 -> 93 15:26:28.990 -> 97 15:26:28.990 -> 101 15:26:28.990 -> 106 15:26:28.990 -> 111 15:26:29.029 -> 116 15:26:29.029 -> 121 15:26:29.029 -> 126 15:26:29.029 -> 133 15:26:29.029 -> 138 15:26:29.029 -> 143 15:26:29.029 -> 148 15:26:29.029 -> 153 15:26:29.064 -> 158 15:26:29.064 -> 163 15:26:29.064 -> 168 15:26:29.064 -> 174 15:26:29.064 -> 179 15:26:29.064 -> 184 15:26:29.064 -> 189 15:26:29.101 -> 195 15:26:29.101 -> 200 15:26:29.101 -> 205 15:26:29.101 -> 210 15:26:29.101 -> 216 15:26:29.101 -> 221 15:26:29.101 -> 226 15:26:29.136 -> 231 15:26:29.136 -> 236 15:26:29.136 -> 241 15:26:29.136 -> 246 15:26:29.136 -> 251 15:26:29.136 -> 257 15:26:29.136 -> 263 15:26:29.173 -> 268 15:26:29.173 -> 273 15:26:29.173 -> 278 15:26:29.173 -> 283 15:26:29.173 -> 288 15:26:29.173 -> 293 15:26:29.173 -> 299 15:26:29.210 -> 304 15:26:29.210 -> 309 15:26:29.210 -> 314 15:26:29.210 -> 319 15:26:29.210 -> 324 15:26:29.210 -> 330 15:26:29.210 -> 335 15:26:29.248 -> 340 15:26:29.248 -> 346 15:26:29.248 -> 351 15:26:29.248 -> 356 15:26:29.248 -> 361 15:26:29.248 -> 366 15:26:29.248 -> 371 15:26:29.284 -> 376 15:26:29.284 -> 381 15:26:29.284 -> 387 15:26:29.284 -> 392 15:26:29.284 -> 398 15:26:29.284 -> 403 15:26:29.284 -> 408 15:26:29.320 -> 413 15:26:29.320 -> 418 15:26:29.320 -> 423 15:26:29.320 -> 429 15:26:29.320 -> 434 15:26:29.320 -> 439 15:26:29.320 -> 444 15:26:29.354 -> 449 15:26:29.354 -> 454 15:26:29.354 -> 459 15:26:29.354 -> 465 15:26:29.354 -> 471 15:26:29.354 -> 476 15:26:29.387 -> 481 15:26:29.387 -> 486 15:26:29.387 -> 491 15:26:29.387 -> 496 15:26:29.387 -> 501 15:26:29.387 -> 506 15:26:29.387 -> 512 15:26:29.422 -> 517 15:26:29.422 -> 522 15:26:29.422 -> 528 15:26:29.422 -> 533 15:26:29.422 -> 538 15:26:29.422 -> 543 15:26:29.422 -> 548 15:26:29.456 -> 553 15:26:29.456 -> 559 15:26:29.456 -> 564 15:26:29.456 -> 569 15:26:29.456 -> 574 15:26:29.456 -> 579 15:26:29.492 -> 584 15:26:29.492 -> 589 15:26:29.492 -> 595 15:26:29.492 -> 601 15:26:29.492 -> 606 15:26:29.492 -> 611 15:26:29.492 -> 616 15:26:29.529 -> 621 15:26:29.529 -> 626 15:26:29.529 -> 631 15:26:29.529 -> 636 15:26:29.529 -> 642 15:26:29.529 -> 647 15:26:29.529 -> 652 15:26:29.567 -> 657 15:26:29.567 -> 663 15:26:29.567 -> 668 15:26:29.567 -> 673 15:26:29.567 -> 678 15:26:29.567 -> 684 15:26:29.567 -> 689 15:26:29.602 -> 694 15:26:29.602 -> 699 15:26:29.602 -> 704 15:26:29.602 -> 709 15:26:29.602 -> 714 15:26:29.602 -> 719 15:26:29.602 -> 724 15:26:29.638 -> 731 15:26:29.638 -> 736 15:26:29.638 -> 741 15:26:29.638 -> 746 15:26:29.638 -> 751 15:26:29.638 -> 756 15:26:29.638 -> 761 15:26:29.672 -> 766 15:26:29.672 -> 772 15:26:29.672 -> 777 15:26:29.672 -> 782 15:26:29.672 -> 787 15:26:29.672 -> 792 15:26:29.672 -> 798 15:26:29.706 -> 803 15:26:29.706 -> 808 15:26:29.706 -> 814 15:26:29.706 -> 819 15:26:29.706 -> 824 15:26:29.706 -> 829 15:26:29.742 -> 834 15:26:29.742 -> 839 15:26:29.742 -> 844 15:26:29.742 -> 849 15:26:29.742 -> 855

我正在尝试开发一种算法,使用和不使用巨型阵列来存储 3 轴加速度数据以计算在任一轴上行驶的距离。 这是我能从 Arduino 得到的最准确的时间吗? 这个输出根本不准确。 我用错了吗? 为什么在这些间隔之后会输出这个时序? 我在哪里可以找到有关此的更多信息?

【问题讨论】:

  • This output is not at all accurate 了解“缓冲”
  • @KamilCuk 能否请您建议更多关键字连同缓冲一起放入谷歌?
  • 类似于“arduino 串行缓冲区打印”,但也建议学习“标准 C 缓冲模式”。 note here.
  • 请不要张贴文字图片。请将文本作为文本发布到您的问题中。采取tour
  • 你可以学习所有关于缓冲的知识,但这只会回答为什么你的测试有缺陷的问题,这不是你问的问题——不要分心,但要知道带回家这里是您不应该在收集时间关键数据的同时尝试输出结果,因为输出将支配时间。这确实是一个 X-Y 问题;您可能会更好地描述您的系统并询问如何进行距离计算(并且可能在不影响该计算的情况下输出结果)。

标签: c++ arduino embedded


【解决方案1】:

这是我能从 Arduino 获得的最准确的时间吗?

还有micros()。请注意,精度不是分辨率。

我是不是用错了?

没有。

为什么在这些间隔之后输出这个计时?

millis() 的输出只是放在内部缓冲区中。在 pseudocode 中,它通常是这样工作的:

char internal_buffer[SOME_SIZE];
void Serial::println(SOME_TYPE stuff) {
     if (internal_buffer_is_full()) {
          // actively wait for at least space for stuff to place
          internal_buffer_wait_for_space_for_stuff(stuff);
     }
     internal_buffer += stuff; // just add the data to internal buffer
     if (!hardware::is_writing()) {
           hardware::start_writing(internal_buffer); // writes __asynchronously__!
     }
}

首先它全部被缓冲。然后异步将缓冲区写入输出,主线程继续其操作。当缓冲区已满时,主线程会主动等待缓冲区中至少有一些空间,然后再将要写入的数据放入缓冲区。预计您会看到短暂的“刷新”,其中 缓冲 输出稍微输出,然后缓冲继续。

如果你想刷新缓冲数据,有Serial.flush()

【讨论】:

  • 在哪里可以找到更多相关信息? Arduino 驱动程序是开源的吗?
  • 是的。并且 arduino 具有可理解且庞大的(与其他项目相比......)文档。专业提示:在 google 中用“github”写一些东西,比如“arduino github”,99% 的时间都是you'll find the sourcesHere is implementation of AVR hardwareserial,看起来像一个简单的循环缓冲区。
  • 我在哪里可以学习更多关于循环缓冲区之类的内容,这是来自哪个主题?@KamilCuk
  • 嗯,我相信进一步的学习是书籍,然后是更多的书籍和教程。在 stackoverflow 上有一个很好的 C 和 C++ 书籍列表。然后继续深造上大学。我记得的循环缓冲区也在 wiki 上进行了解释。
  • 并不是那么简单,这甚至不是 OPs 测试中时序问题的主要原因。 UART 串​​行输出为串行 USB 接口(和 FTDI 芯片或单独的 AVR)的 UART 输入提供信号,该接口也具有缓冲功能,并且 USB 事务本质上是单工主从,并且依赖于主机的时序。然后串行数据最终在具有通用非实时操作系统的软管计算机上,增加了更多的缓冲,甚至更少的确定性。
【解决方案2】:

您的测试主要取决于 USB 串行通信的性能、粒度和非确定性时序,而不是 millis() 的精度 - 时序也是由连接的 PC 提供的,不是实时的系统并拥有自己的缓冲行为只会添加另一个变量。

millis()micros() 都与您板上的时钟源一样准确,典型的晶体振荡器可以达到 50ppm 或 0.005% 左右,陶瓷谐振器可能达到 0.5%,而典型的 TCXO 大约为 2ppm (0.0002%)。

在调用这些函数之间执行的任何代码都需要时间,并且诸如println() 输出到 USB CDC/ACM 串行并在 PC 上捕获的操作将特别具有不确定性。

如果您真的想确认精度,您应该使用更快、更具确定性的输出,例如数字 IO,并确定引脚范围。例如:

void setup() 
{
  pinMode(2, OUTPUT);
}

void loop() 
{
  digitalWrite( 2, millis() & 1 ) ;
}

这将以 500Hz 的频率切换引脚,占空比为 1:1(1ms 低:1ms 高)

【讨论】:

  • 定时依赖主机?什么?我在哪里可以阅读更多关于此的信息? Arduino 怎么可能有自己的闪存呢?
  • @ShridharSharma 我不确定您是否可以在一个地方“读到它”,我在实时系统开发方面 30 年的经验告诉我。这是多种因素结合的结果: USB 的工作方式以及它与在这种情况下它所模拟的串行端口的根本不同,主机不是实时操作系统的事实,以及主机上按顺序运行的更多软件和缓冲的事实实现虚拟 COM 端口而不是在目标上运行。
  • 您的时间戳由主机提供,是对目标和主机执行组合时间的衡量,而不是对 millis() 的直接衡量。我不确定你为什么认为 Arduino 拥有自己的闪存与这个问题有关。
  • 这是我想去的一个兔子洞。感谢上帝,我选择了嵌入式系统。您可能建议我使用的任何工具,以便我可以更好地理解这些协议。例如逻辑分析仪、DSO 或其他什么?
  • @ShridharSharma 从 Arduino 软件框架开始,它旨在隐藏所有复杂性,并且严重缺乏任何类型的调试器。它的能力相当有限。如果您想深入了解这种细节,您可能想从 Arduino 毕业。在 ST Nucleo 板上运行 mbed 是显而易见的一步。借助“离线”mbed 开发人员工作室,您可以获得调试功能,Nucleo 板包括 STLink 片上调试支持,并具有与 Arduino Shield 兼容的 I/O 连接器。 mbed 框架包括 RTOS 和“裸机”配置文件。
猜你喜欢
  • 2012-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-23
  • 1970-01-01
相关资源
最近更新 更多