STM32程序 关于Semhosting(半主机)和Microlib 以及Printf的关系

一,Keil中Printf导致程序无法运行到Main函数

在Keil中调试STM32程序,编译烧录后,发现程序不能运行,Main函数中点亮LED灯的语句没起作用,说明没有进入Main函数。用Keil调试的时候,虽然设置了Run to main(),

但发现确实进入不了Main函数。也就是程序烧录后其实无法进入Main函数运行。(这个和Semihosting的机制有关,后面再解释)

想到自己是因为调试的需要,添加了Printf语句,所以怀疑是这个问题,然后倒腾了下,勾选了Use MicroLIB:

烧录后,发现程序就可以运行了,Printf也有输出。(程序里已经有重写fputc重定向到串口的函数)

然后注释掉Printf语句,并且取消勾选Use MicroLIB,程序烧录到开发板上也能正常运行。

小结:

1. 要使用Printf函数,在Keil中,请勾选使用MicroLIB,重定向fputc到串口。

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       ch:  The characters that need to be send.
*
* @param       *f:  pointer to a FILE that can recording all information
*              needed to control a stream
*
* @retval      The characters that need to be send.
*
* @note
*/
int fputc(int ch, FILE* f)
{
    /* send a byte of data to the serial port */
    USART_TxData(DEBUG_USART, (uint8_t)ch);

    /* wait for the data to be send  */
    while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);

    return (ch);
}

2. 不勾选MicroLIB,即使重写了fputc到串口,使用Printf会使程序无法运行到Main函数。

    再提前加一点解释:

3. 不勾选MicroLIB而使用Printf,无法运行到Main函数,是因为Printf/scanf之类的函数受了Semihosting机制的影响

二,Keil中是必须要使用MicroLIB实现串口打印吗

因为要和同事的代码合并,他是在VSCODE中写的,所以我要迁移代码到VSCODE,但是搜了一圈发现VSCODE没有MicroLIB, 这个微库是Keil独有内置的(也许也有别的类似的,但我没有研究)。既然VSCODE没有Micro LIB,那么一定也有别的方式,那么Keil中是必须要使用MicroLIB来实现串口打印吗?答案是NO

然后突然想起之前了解到的所谓的半主机模式(英文原文Semihosting,这个“半主机”翻译感觉怪怪的,其实意思就是嵌入式设备因为硬件功能受限,而需要借用主机的一部分功能,这主要在调试的时候提供了方便,感觉  “半托管”  更直观贴切)。

Semihosting是一种机制,它允许嵌入式系统与主机计算机的操作系统进行通信,进行输入/输出操作,例如文件I/O,标准I/O(stdio)和其他与系统相关的功能。它使开发人员在开发和调试过程中与嵌入式系统进行交互,而无需额外的硬件或专用通信渠道。

使用半主机时,通常由嵌入式系统硬件处理的某些操作,例如从文件读取或写入文件,被转移(offload)到由主机的操作系统来操作。这使开发人员可以在调试目的时使用主机操作系统提供的熟悉的文件系统和I/O功能。

半主机通常涉及开发工具链提供的一小部分软件函数,这些函数充当嵌入式应用程序和主机操作系统之间的中介,为嵌入式系统与主机计算机之间的通信提供了便利。这些函数允许开发人员执行诸如打印调试消息、从主机读取输入或访问主机文件系统中的文件等任务。

半主机的一个常见用例是调试嵌入式软件应用程序,开发人员可以在其代码中使用标准的printf语句将调试信息输出到主机计算机的控制台,而不是依赖于专用的调试硬件。此外,半主机可用于任务,如将文件加载到嵌入式系统中,执行软件更新或访问主机操作系统提供的系统资源。

总的来说,半主机为开发人员提供了一种方便的方式,在开发和调试过程中与嵌入式系统进行交互,有助于简化开发流程并提高生产力。

所以就搜了一下,在Keil工程添加了以下代码禁用Semihosting :

//加入以下代码,支持printf函数,而不需要选择use MicroLIB

#pragma import(__use_no_semihosting)  // 确保没有从 C 库链接使用半主机的函数
void _sys_exit(int  x) //定义 _sys_exit() 以避免使用半主机模式
{
  x = x;
}
struct __FILE  // 标准库需要的支持函数
{
  int handle;
};
/* FILE is typedef ’ d in stdio.h. */
FILE __stdout;

然后同时取消勾选MicroLIB,发现程序也能正常运行,能使用串口打印输出。

小结:

Keil中也可以不使用MicroLIB,通过禁用Semihosting,正常使用C库,可以实现串口打印。

三、VSCODE中该怎么通过串口使用Printf

既然KEIL中可以不使用MicroLIB,那么VSCODE中没有MicroLIB也就不是问题了,同样禁用Semihosting,添加重定向fputc。但是却仍然出现问题,没有输出。

搜了一圈,发现在VSCODE中若使用GCC编译器,要重定向的函数不是fputc,而是_write和__io_putchar:

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       ch:  The characters that need to be send.
*
* @param       *f:  pointer to a FILE that can recording all information
*              needed to control a stream
*
* @retval      The characters that need to be send.
*
* @note
*/

int __io_putchar(int ch)
{
  /* send a byte of data to the serial port */
  USART_TxData(DEBUG_USART, ch);

  /* wait for the data to be send  */
  while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);

  return ch;
}

int _write(int fd, char* ptr, int len)
{
  int DataIdx;
  for (DataIdx = 0; DataIdx < len; DataIdx++)
  {
    __io_putchar(*ptr++);
  }
  return len;
}

注:上述USART相关函数实际上是极海MCU的,而不是STM32,因为我实际上是在极海APM32上做开发,是参照STM32而已。但原理是通的,具体函数名字不同,根据自己是使用标准库还是HAL库,做一下改变就行了。

小结:

不同编译环境下的重定向函数有所不同,

Keil、IAR等 IDE上面,都是用以下方式重定向的:

  • int fputc(int ch, FILE *f)
    int fgetc(FILE *f)

在 GCC 环境下,使用的是如下方式:

int _write(int file, char *ptr, int len)
int _read(int file, char *ptr, int len)

四、完整重定向代码(通用)

最后贴上极海SDK里面的重定向完整代码,供参考:

#if defined (__CC_ARM) || defined (__ICCARM__) || (defined(__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       ch:  The characters that need to be send.
*
* @param       *f:  pointer to a FILE that can recording all information
*              needed to control a stream
*
* @retval      The characters that need to be send.
*
* @note
*/
int fputc(int ch, FILE* f)
{
    /* send a byte of data to the serial port */
    USART_TxData(DEBUG_USART, (uint8_t)ch);

    /* wait for the data to be send  */
    while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);

    return (ch);
}

#elif defined (__GNUC__)

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       ch:  The characters that need to be send.
*
* @retval      The characters that need to be send.
*
* @note
*/
int __io_putchar(int ch)
{
    /* send a byte of data to the serial port */
    USART_TxData(DEBUG_USART, ch);

    /* wait for the data to be send  */
    while (USART_ReadStatusFlag(DEBUG_USART, USART_FLAG_TXBE) == RESET);

    return ch;
}

/*!
* @brief       Redirect C Library function printf to serial port.
*              After Redirection, you can use printf function.
*
* @param       file:  Meaningless in this function.
*
* @param       *ptr:  Buffer pointer for data to be sent.
*
* @param       len:  Length of data to be sent.
*
* @retval      The characters that need to be send.
*
* @note
*/
int _write(int file, char* ptr, int len)
{
    int i;
    for (i = 0; i < len; i++)
    {
        __io_putchar(*ptr++);
    }

    return len;
}

#else
#warning Not supported compiler type
#endif

根据自己的MCU,修改USART相关的函数即可。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/537044.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Docker学习笔记(二):在Linux中部署Docker(Centos7下安装docker、环境配置,以及镜像简单使用)

一、前言 记录时间 [2024-4-6] 前置文章&#xff1a;Docker学习笔记&#xff08;一&#xff09;&#xff1a;入门篇&#xff0c;Docker概述、基本组成等&#xff0c;对Docker有一个初步的认识 在上文中&#xff0c;笔者进行了Docker概述&#xff0c;介绍其历史、优势、作用&am…

springboot相关报错解决

Caused by: java.lang.ClassNotFoundException: 目录 Caused by: java.lang.ClassNotFoundException: org.springframework.context.event.GenericApplicationListener spring-boot-dependencies:jar:2.1.9.RELEASE was not found org.springframework.context.event.Generi…

Mybatis-plus动态数据源

由于服务没有做微服务部署&#xff0c;需要在后台管理系统访问其他服务的库&#xff0c;所以需要用到动态数据源切换 引入依赖 mybatis-plus动态数据源依赖 <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot…

【生产实习-毕设】pyspark学生成绩分析与预测(上)

注意&#xff1a;数据由实习单位老师提供&#xff08;需要自行搜索下载&#xff09;&#xff0c;页面美化为下载模板。 项目介绍&#xff1a;前端页面输入影响成绩的属性&#xff0c;预测出成绩&#xff0c;并作可视化展示——属性对成绩的影响。使用python pyspark 进行数据预…

SpringBoot + Dobbo + nacos

SpringBoot Dobbo nacos 一、nacos https://nacos.io/zh-cn/docs/quick-start.html 1、下载安装包 https://github.com/alibaba/nacos/releases/下载后在主目录下&#xff0c;创建一个logs的文件夹&#xff1a;用来存日志 2、启动nacos 在bin目录下打开cmd运行启动命令&a…

小红的白色字符串

题目描述 小红拿到了一个字符串&#xff0c;她准备将一些字母变成白色&#xff0c;变成白色的字母看上去就和空格一样&#xff0c;这样字符串就变成了一些单词。 现在小红希望&#xff0c;每个单词都满足以下两种情况中的一种&#xff1a; 1.开头第一个大写&#xff0c;其余为…

简述OSI七层模型及每层的功能任务和协议

文章目录 一、OSI七层模型的功能和任务1.物理层2.数据链路层3.网络层4.传输层5.会话层6.表示层7. 应用层 二、OSI七层模型每层的协议 开放系统互连参考模型&#xff08;Open System Interconnect&#xff0c;简称OSI&#xff09;是国际标准化组织(ISO)和国际电报电话咨询委员会…

为什么选择成为程序员?

目录 兴趣和热爱高薪和就业机会持续学习和不断成长挑战和乐趣 兴趣和热爱 许多人选择成为程序员可能是热爱&#xff0c;对计算机&#xff0c;以及编程和科技产生了浓厚的兴趣&#xff0c;并且享受着解决每一个技术问题&#xff0c;构建应用程序和探索新技术所带来的乐趣。 谈到…

vue快速入门(十七)v-model数据双向绑定修饰符

注释很详细&#xff0c;直接上代码 上一篇 新增内容 v-model.trim 自动去除首尾空格v-model.number 自动转换成数字类型 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" con…

微信小程序(六)定位搜索

一、引言 作者上一章讲了微信小程序的地图实现微信小程序&#xff08;五&#xff09;地图-CSDN博客&#xff0c;但是还有一个功能是和地图紧密结合的&#xff0c;那就是位置搜索定位&#xff0c;这里作者讲讲实现和原理&#xff0c;包括城市筛选。 二、定位搜索实现 1、位置搜…

Mongodb前后端整合篇

一、前端篇 1.1mongoose介绍 Mongoose 是一个对象文档模型库&#xff0c;官网 http://www.mongoosejs.net/ 方便使用代码操作 mongodb 数据库pnpm i mongoose5.13.15 1.2初步使用 import mongoose from mongoose; //设置 strictQuery 为 true mongoose.set(strictQuery, true…

【D3.js Tidy tree绘制树形图,单棵树,左右树,平移,拖拽,树形中的天花板实现,源码实现】

这里写自定义目录标题 D3.js Tidy tree绘制树形图,单棵树,左右树,平移,拖拽,树形中的天花板实现,源码实现D3 简介D3 官网有很多例子,这里说的是Tidy tree[树形图表svg][左侧关系->中间对象<-右侧关系 ] 树形实现 D3.js Tidy tree绘制树形图,单棵树,左右树,平移,拖拽,树形…

网易云信携手 DCloud,共同助力应用开发效率飞升

近日&#xff0c;持续数月的 DCloud 2023 插件开发大赛正式放榜&#xff0c;网易云信音视频呼叫组件获得了本次大赛二等奖。 作为大赛获奖的优秀插件&#xff0c;云信 RTC 呼叫组件已正式在 DCloud 官方插件市场上线&#xff0c;方便企业开发者快速下载和集成&#xff0c;以丰富…

如何快速写一份简历

文章目录 如何快速写一份简历一些写简历的技巧 最近一段时间一直在忙简历相关的事情&#xff0c;起初是有一个其他行业的朋友问我&#xff0c;说这些简历我写了好久真难写&#xff0c;我说你可以借助AI&#xff0c;现在这种工具多了去了&#xff0c;为什么不借助呢&#xff1f;…

最简单的ubuntu安装docker教程

本文参考自docker官方教程&#xff1a;ubuntu上安装docker 一、安装Docker 第一步&#xff1a;添加Docker官方的GPG密钥 直接复制所有代码&#xff0c;作为一行运行即可 sudo apt-get update sudo apt-get install ca-certificates curl sudo install -m 0755 -d /etc/apt/k…

Redis性能管理和集群的三种模式(二)

一、Redis集群模式 1.1 redis的定义 redis 集群 是一个提供高性能、高可用、数据分片、故障转移特性的分布式数据解决方案 1.2 redis的功能 数据分片&#xff1a;redis cluster 实现了数据自动分片&#xff0c;每个节点都会保存一份数据故障转移&#xff1a;若个某个节点发生故…

WordPress上建立电商平台

WordPress是一个功能强大的内容管理系统&#xff08;CMS&#xff09;&#xff0c;它可以被用来建立各种类型的网站&#xff0c;包括电商平台。要在WordPress上建立电商平台&#xff0c;你可以遵循以下步骤。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#x…

.NET MAUI使用Visual Studio Android Emulator(安卓模拟器)运行

Android Emulator&#xff08;安卓模拟器&#xff09;运行&#xff1a; 安卓模拟器一直卡在不动&#xff1a; 在某些情况下&#xff0c;在“打开或关闭 Windows 功能”对话框中启用 Hyper-V 和 Windows 虚拟机监控程序平台后可能无法正确启用Hyper-V。 我就是开启Hyper-V才把安…

2024 MathorCupB 题 甲骨文智能识别中原始拓片单字自动分割与识别研究

一、问题重述 甲骨文是我国目前已知的最早成熟的文字系统&#xff0c;它是一种刻在龟甲或兽骨上的古老文字。甲骨文具有极其重要的研究价值&#xff0c;不仅对中国文明的起源具有重要意义&#xff0c;也对世界文明的研究有着深远影响。在我国政府的大力推动下&#xff0c;甲骨…

DS数模-Mathorcup妈妈杯C题思路

2024Mathorcup数学建模挑战赛&#xff08;妈妈杯&#xff09;C题保姆级分析完整思路代码数据教学 C题题目&#xff1a;物流网络分拣中心货量预测及人员排班 接下来我们将按照题目总体分析-背景分析-各小问分析的形式来 总体分析&#xff1a;题目要求我们处理的是一个关于物流…