主题中讨论的其他器件:MSP430F5529
工具/软件:TI C/C++编译器
为了进行调试、我想让 printf 可用。 但是、当我将 printf 添加到从 CCS 中的 Resource Explorer 中提取的一些示例代码中时、printf 没有执行任何操作。
我无法找到有关如何实现 printf 以及默认情况下打印在何处的详细信息。 我尝试在编译器源代码中找到它、但为 printf 进行 grep 操作会返回太多结果、无法有效地解析。 如果有任何见解,将不胜感激。
This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
工具/软件:TI C/C++编译器
为了进行调试、我想让 printf 可用。 但是、当我将 printf 添加到从 CCS 中的 Resource Explorer 中提取的一些示例代码中时、printf 没有执行任何操作。
我无法找到有关如何实现 printf 以及默认情况下打印在何处的详细信息。 我尝试在编译器源代码中找到它、但为 printf 进行 grep 操作会返回太多结果、无法有效地解析。 如果有任何见解,将不胜感激。
您好!
请提供一些进一步的信息:
您应该能够在"CIO"控制台中看到 printf 输出。 使用 printf 打印内容时,该控制台视图应自动打开。
除了缺少 printf 输出之外、您的程序是否正常运行? 也就是说、您是否能够观察到执行到达 printf 并在其之后继续执行?
在 CCS 9.1.0中、当您创建一个新的 CCS 项目时、有一个"Hello World"示例可用。 您能否确认这是否适用于您的设备?
此致、
Mike、
可能有很多东西、但可能是堆栈大小。
这听起来像是我遇到的问题。 printf 及其变体使用大量堆栈、我们使用的 TI DSP 上大约为1K、不确定 MSP430上有多少堆栈。 在我们意识到这一点并大幅增加堆栈大小之前、症状是 printf 没有执行任何操作。 我们使用存储器查看器来观察堆栈区域、最后用已知模式填充该区域、然后观察堆栈区域被覆盖的大小。
劳埃德
如果问题是由堆栈大小限制引起的、我建议尝试从 MSP430-GCC 8.2.0.52开始提供的"-mTina-printf"选项。 MSP430-GCC 用户指南(slau646)对此进行了说明。
除了通过从 printf()中删除对可重入性的支持来节省 ROM 之外,该选项还减少了 RAM 的使用,因为 printf()系列函数通常需要的输出字符串的缓冲也已被删除。 输出的字符串在输出前仍会被缓冲(如果需要),但它在较低的级别发生,在 write() syscall 中,而不是在 printf()中。
此外、printf 的调用图与"-mTini-printf"没有那么深、因为某些支持函数已被删除、所以栈不太可能溢出。
作为对 MSP430F5529的快速测试、我观察到与默认值相比、使用 printf ()和"-mTini-printf"可以获得额外的~1000字节可用 RAM。
感谢您的提示、我怀疑我的应用程序将会经常处理堆栈限制、因此这肯定会有所帮助。
在本例中、printf 是在 ISR 内部实现的、不建议这样做。 我将一个简单的 printf 放在空工程中、现在在 CCS 控制台中读出它没有问题。
我目前正在使用 GNU v7.3.2.154、这意味着我很遗憾没有"-mTini-printf"可用。 正如我在另一个答复中提到的、我创建了一个新的空 CCS 项目、然后在 main 中添加了一个简单的 printf、并且在打印到控制台时没有问题。
现在、我的问题是、这是如何在背后工作的? 如果我想在 CCS 之外运行我的项目、如何通过 minicom 通过串行连接打印 printf? 我无法找到有关 CIO 控制台如何实现串行连接的详细信息、因此我仍然不确定 printf 如何能够在没有额外配置的情况下写入该接口。
Mike、
我认为、除非安装了调试探针、否则您不能使用 CIO、因为它通过 JTAG。 我通过编写一个例程来处理这一问题、该例程使用 sprintf 构建字符串、然后启动中断驱动的 UART 通道以发送结果。
劳埃德
[引用 user="EmbeddedMike"]我目前使用的是 GNU v7.3.2.154,这意味着我很遗憾没有"-mTiny-printf"可用。[/quot]
如果可能、我建议您升级到 MSP430-GCC 8.2.0.52、因为您也可以从其他功能/错误修复中受益。 如果您需要在 CCS 中“帮助>检查更新”,它应该会提供更新 MSP430-GCC。
或者、您也可以从以下位置下载工具链作为存档: http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSPGCC/latest/index_FDS.html
[引用 user="EmbeddedMike">现在我的问题是、它如何在背后工作? 如果我想在 CCS 之外运行我的项目、如何通过 minicom 通过串行连接打印 printf? 我无法找到有关 CIO 控制台如何实现串行连接的详细信息、因此我仍然不确定 printf 如何能够在没有额外配置的情况下写入该接口。
一种方法是使用您自己的实现来覆盖"write"函数、该实现通过 UART 或您希望的任何位置发送字符串。 如果您有 MSP430-GCC 源代码,则可以看到 write()的默认定义,它在"newlib/libglicy/MSP430/write.c"中使用 CIO。
write()的原型为:
int 写入(int fd、const char * buf、int len);
CIO 仅与了解 CIO 命令含义的调试客户端配合使用。 客户端通过从特定存储器位置读取要打印的字符串(如果是"写入")来执行繁重的提升。
感谢您提供详细信息、以解决我的主要问题。 但是、作为后续行动、我有两个问题:
说明
< >接受一系列参数,适用于每个 A
格式指定符来自<<*[format]>>、并写入
将数据格式化为< >、不终止 NUL
字符。 的行为< 如果存在则未定义
没有足够的参数用于该格式。 < >返回
到达格式字符串的末尾时。 如果有的话
参数多于格式要求的值、参数过多
忽略。
感谢 CIO 控制台上的信息、这很有意义。 是否公开提供了采用 sprintf 的 UART 实现? 如果这是您希望在工作中保持内部的内容、这是完全可以理解的。
[引用 user="EmbeddedMike">"stdout"在 MSP430上下文中如何工作? 在 printf 文档(可在 sprintf.c 中找到)中、它声明如下:
说明
< >接受一系列参数,适用于每个 A
格式指定符来自<<*[format]>>、并写入
将数据格式化为< >、不终止 NUL
字符。 的行为< 如果存在则未定义
没有足够的参数用于该格式。 < >返回
到达格式字符串的末尾时。 如果有的话
参数多于格式要求的值、参数过多
忽略。
我在 printf()的源代码中没有看到任何 write()函数,它看起来不像 MSP430那样有一个 Linux stdout 流。 我缺少 printf 和 write 之间的连接。
put()是缺少的连接。 传递给 printf()的字符串将从 printf()变为 put()再到 write()。 这是一种简化,因为有许多*printf*、*puts*和*write*函数在它们之间处理可重入性(或在*printf*的情况下进一步格式化),但这些是主函数。
printf()的目的只是通过将给定参数插入字符串中的格式指定符来格式化给定字符串。 字符串格式化后,将其传递给 put()。
您可以观察到,如果将没有格式说明符的常量字符串传递给 printf(),编译器会直接将其优化为 put(),因为 printf()不需要格式化功能。
在单线程应用程序中,put()不需要做太多,它可以将格式化的字符串传递给 write()。 write()被视为低级系统调用,它假定一旦调用字符串,就可以安全地将其写入指定的流中。 在多线程应用程序中,put()将获取锁定,以确保不会同时调用同一数据流的 write()。
"stdout"对于 MSP430来说并不真正具有特定的含义、因为没有运行的操作系统、所以您无法保证发送到"stdout"的字符串将会出现在何处。 它取决于如何实现 write()和调试服务器/客户端实现。
CCS CIO 机制似乎理解 stdout 和 stderr 之间的差异。 以下命令在 CIO 控制台中以红色字体打印"Hello World"(以指示 stderr):
fprintf (stderr、"Hello World\n");
而使用"stdout"时、文本以常规黑色字体显示。
[引用 user="EmbeddedMike">如果我的已发布代码调用 printf 但未覆盖 printf,会发生什么情况? 它只是什么也不做、还是 printf 的默认实现会挂起代码?
同样,它取决于如何实现 write()。 默认实现不会挂起、因为 CIO 机制只要求将输出字符串复制到特定的存储器地址、然后 CCS 调试客户端将读取和解释该地址。 因此当没有连接调试器时,代码仍会将 printf()字符串写入内存地址,但只会在之后继续,不会等待。
在 printf()调用之后,通过将代码置于闪烁和 LED 来进行快速测试,可以确认这是它的工作方式。
如果覆盖 write()以通过 UART 发送字符,并且该代码在等待接收某些数据包时循环,则可能会挂起。
您非常乐于助人、我非常感谢您的详尽回答!
Mike、
很抱歉、我无法发布代码、因为它是由客户付费的。 但它非常简单、一个使用 va_args 调用 MyPrintf 之类的函数、将 args 传递到 sprintf、并将生成的字符串发送到您自己的 UART 打印处理程序。
劳埃德
[引用 user="lslonim"]
很抱歉、我无法发布代码、因为它是由客户付费的。
[/报价]
不用担心!
[引用 user="lslonim"]
但它非常简单、一个使用 va_args 调用 MyPrintf 之类的函数、将 args 传递到 sprintf、并将生成的字符串发送到您自己的 UART 打印处理程序。
[/报价]
感谢您提供的信息、这很有道理。