金种子酒业网站建设/windows优化大师下载
文章目录
- 背景
- 挑战与困难
- 如何整合编译?
- error: non-ASM statement in naked function is not supported
- error: '#pragma import' is an ARM Compiler 5 extension, and is not supported by ARM Compiler 6
- error: redefinition of '__FILE'
- 改造demo中的cout
- 改造delete运算符
- 总结与展望
背景
在文章使用C/C++实现线性代数计算——环境bringup 中,围绕Eigen介绍了使用在各种场合下的环境bringup和编译问题,本文接着上文的内容,详细记录一下基于MDK5+Eigen+STM32来实现一个线性代数计算器的过程,里面还是有很多问题可以学习分享一下的。
挑战与困难
首先,Eigen是一个C++框架的开源项目,虽说不依赖什么OS之类的东西,但是若要完全跑在STM32裸机中,还是有一些问题和适配难点需要解决的,本文先详细介绍一下如果规避这些问题,给大家一个参考,解决思路可能不是最优的,如果你有更好的解决思路,不妨评论一下,我们一起相互学习交流一下~~
本文使用的环境时MDK5.35 + eigen3.4.0 + STM32F103ZE系列开发板
如何整合编译?
在 使用C/C++实现线性代数计算——环境bringup 一文的文末,简单提了一下在Keil中的编译环境配置,借助MDK官方提供的启动文件,能编译链接生成一个bin文件。如果要想真的在STM32里面跑起来是远远不够的,首先里面的printf函数、std::cout输出流在stm32上就没有,所以为了验证编译出的bin文件到底能不能直接烧写到stm32里面直接跑,笔者找了一个stm32串口demo程序,在里面接入Eigen,然后调用Eigen提供的矩阵运算API,通过串口发出来(本文末会将改造后的demo发出来供大家参考)。具体实现的效果如下:
项目中关键文件目录树如下:
添加官方提供的example.c文件和cpp文件(详见 使用C/C++实现线性代数计算——环境bringup ,后面不再赘述),不过需要修改一下,具体如下:
- 考虑到在main.c里已经有一个main函数了,并且后面会主要借助main.c里的main函数初始化外设,所以需要将example.c里面有一个int main函数改个名字,
void test_eigen()
,然后在main函数里加上如下语句:
(PS:我这里的改法仅仅是为了方便验证Eigen的功能,一个完整、规范的项目最好不要瞎几把跨文件用extern声明函数,项目复杂之后可读性会非常差,这是一个反例,大家不要学我!)
然后,按照之前的文章,修改Target里面的ARM Compiler选择版本6,C/C++里面版本和之前文章一样即可。然后点击编译,会有很多错误,我们一个一个来解决。
error: non-ASM statement in naked function is not supported
完整的报错是:…/CORE/core_cm3.c(445): error: non-ASM statement in naked function is not supported
这个报错的根因是ARM Ver6 Compiler不支持旧版本的core_cm3.c里面的C中的汇编指令,解决的思路有三个:
- 编译器换回V5(换回V5不支持C++编译,行不通);
- 把core_cm3.c和core_cm3.h升级到新版本(这个没试过,不过应该可以,用新版的stcCubeMX生成一个demo,看看里面是不是新的,然后替换掉旧的好不好使,笔者没试这个路子);
- 把core_cm3.c从项目中删了(试了,可以,为啥可以删掉?可能是用V5编译生成过.o文件,即便从项目里把core_cm3.c删掉了,链接时仍能用缓存的目标文件);
error: ‘#pragma import’ is an ARM Compiler 5 extension, and is not supported by ARM Compiler 6
具体报错信息是:
…/SYSTEM/usart/usart.c(39): error: ‘#pragma import’ is an ARM Compiler 5 extension, and is not supported by ARM Compiler 6 [-Warmcc-pragma-import]
#pragma import(__use_no_semihosting)
^
这个错误说的很明白,V6版本的arm编译器不支持’#pragma import’ 语法,那为啥要用到 ‘#pragma import’ 语句呢?回到代码里看一下,这一行具体是#pragma import(__use_no_semihosting)
,它的作用是关掉arm的半主机模式。
所谓半主机模式:是用于ARM目标的一种机制;可将来自STM32单片机应用程序的输入输出请求传送至运行仿真器的PC主机。使用此机制可以启用C库中的函数,如printf()和scanf(),来使用PC主机的屏幕和键盘。禁掉后方可使用重定向手段,将printf函数的标准输出重定向到串口上,这样就可以用printf函数打印字符串,然后在串口里读到数据,这种骚操作非常适合调试,具体细节可以参考:【stm32串口打印】printf函数的使用方法,注意事项,原理以及拓展,个人学习理解总结。
那如果用了V6版本的编译器,就不能禁掉半主机模式了,下面会将平提方法,我们先接着看错误。
error: redefinition of ‘__FILE’
具体报错信息是这个:
…/SYSTEM/usart/usart.c(41): error: redefinition of ‘__FILE’
struct __FILE
对应的源码还是:
//
//加入以下代码,支持printf函数,而不需要选择use MicroLIB
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{ int handle; }; FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{ x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{ while((USART1->SR&0X40)==0);//循环发送,直到发送完毕 USART1->DR = (u8) ch; return ch;
}
原因是在stdio.h里面已经有一个struct __FILE定义了,为啥这里又要重新定义一个struct __FILE?还是因为要用printf函数,因为printf函数本质上就是将格式化后的字符串打印到标准输出上,本质上标准输出就是一个FILE类型的变量(一切皆文件?以后再探究了,这里先不展开了)。
所以综合来看,我们只需要先不用printf函数打印字符串应该就能规避上述两个问题了,这里笔者提供的平提方案是:
#define USART_SEND_BUFFER_SIZE (128)
#define USART_RECV_BUFFER_SIZE (128)
void USART_SendString(char *str)
{uint8_t idx = 0;while (*(str+idx)){USART_SendData(USART1, *(str+idx));while(USART_GetFlagStatus(USART1, USART_FLAG_TC)!=SET);idx++;}
}void u_printf(const char *format,...)
{char String[USART_SEND_BUFFER_SIZE] = {0};__va_list arg;//定义一个参数列表变量va_list是一个类型名,arg是变量名va_start(arg,format); //从format位置开始接收参数表放在arg里面//sprintf打印位置是String,格式化字符串是format,参数表是arg,对于封装格式sprintf要改成vsprintfvsprintf(String,format,arg);va_end(arg); //释放参数表USART_SendString(String);//发送String
}
原理相当于是重写了一个接口叫u_printf,传参啥的跟printf函数是一样的,但是输出到的是串口。
改造demo中的cout
除了上面的问题,还有在binary_library.cpp中用了std::cout方法打印矩阵的值,由于stm32没有OS,所以也不存在什么标准IO流了,这里也需要改造,具体做法是:
将:
void MatrixXd_print(const C_MatrixXd *m)
{std::cout << c_to_eigen(m) << std::endl;
}
改为:
void MatrixXd_print(const C_MatrixXd *m)
{MatrixXd cpp_m = c_to_eigen(m);unsigned char r = cpp_m.rows();unsigned char c = cpp_m.cols();char val[32] = {0};while (r){while(c){sprintf(val, "%.3f \t", cpp_m(r - 1, c - 1));USART_SendString(val);c--;}r--;c = cpp_m.cols();USART_SendString("\r\n");}
}
除了void MatrixXd_print(const C_MatrixXd *m)
函数,void Map_MatrixXd_print(const C_Map_MatrixXd *m)
也是一样的,改造后的内容如下:
void Map_MatrixXd_print(const C_Map_MatrixXd *m)
{MatrixXd cpp_m = c_to_eigen(m);unsigned char r = cpp_m.rows();unsigned char c = cpp_m.cols();char val[32] = {0};while (r){while(c){sprintf(val, "%.3f \t", cpp_m(r - 1, c - 1));USART_SendString(val);c--;}r--;c = cpp_m.cols();USART_SendString("\r\n");}
}
本质上原理是将Eigen中MatrixBase类提供的operator<<运算符重载方法改成了C语言中能直接用的方法。
改造delete运算符
在binary_library.cpp文件中,void MatrixXd_delete(C_MatrixXd *m)
和void Map_MatrixXd_delete(C_Map_MatrixXd *m)
函数用了delete运算符释放堆内存,本身在cpp文件中是支持的,但是放到stm32中这么操作就会导致Hardfault,这里也需要改造一下,改成free函数,如下:
void MatrixXd_delete(C_MatrixXd *m)
{
// delete &c_to_eigen(m);if (NULL != m){free(m);m = NULL;}
}// skip .....void Map_MatrixXd_delete(C_Map_MatrixXd *m)
{
// delete &c_to_eigen(m);if (NULL != m){free(m);m = NULL;}
}
这里还需要啰嗦一句,按理说stm32里面没跑什么os,连内存管理机制都没有,free()函数还是delete运算符都是无意义的,为啥用new或者malloc就没区别,free()换成delete就不行呢?先埋个引子,以后再去探究了,如果有懂的大哥,也帮忙解答一下,感谢。
总结与展望
本文主要介绍了将Eigen项目移植到stm32开发板上遇到的一些问题以及解决办法,完整的例程请关注VX公众号“24K纯学渣”回复关键词“stm32_eigen”获取。
当前,笔者提供的demo还是太简单,基本上就做了一个矩阵运算和打印,只是卖出了第一步,可扩展的空间着实不小,例如:
- 加上交互功能,可以是串口式的、CLI式的、或者复杂一些整个可触摸的LCD,做成像手机APP一样的;
- 除了矩阵基本运算,还可以加一些复杂的比如正交分解、求解行列式值、求特征值、特征向量;
- 除了线性代数运算以外,还可以求解微分方程,机器人运动学解逆、无人机视觉定位等等;
如果你也刚好对上述内容感兴趣,欢迎来学习交流噢!
相关文章:

使用STM32实现一个线性代数计算器
文章目录 背景挑战与困难如何整合编译?error: non-ASM statement in naked function is not supportederror: #pragma import is an ARM Compiler 5 extension, and is not supported by ARM Compiler 6error: redefinition of __FILE 改造demo中的cout改造delete运…...

我在高职教STM32——串口通信(4)
大家好,我是老耿,高职青椒一枚,一直从事单片机、嵌入式、物联网等课程的教学。对于高职的学生层次,同行应该都懂的,老师在课堂上教学几乎是没什么成就感的。正因如此,才有了借助 CSDN 平台寻求认同感和成就感的想法。在这里,我准备陆续把自己花了很多心思的教学设计分享…...

Redis 缓存中间件 缓存数据库
Redis 缓存中间件 缓存数据库 nginx web服务 PHP 转发动态请求 tomcat web页面也可以转发动态请求 springboot 自带tomcat 所有的数据库不支持高并发,一旦访问量激增,数据库很快就会崩溃。 Redis 非关系型数据库 nosql not only sql 不仅仅是sql 键值对…...

51、PHP 实现简单的快速排序
题目: PHP 实现简单的快速排序 描述: function simpleQuickSort(array $list) {$length count($list);if( $length < 1){return $list;}else{$pivot $list[0];$left_list array();$right_list array();for($i 1; $i < $length; $i){if($lis…...

如何应对机器视觉软件中时间篡改与许可绕过的挑战?
在机器视觉行业,软件许可绕过和时间篡改问题存在,这些行为对企业的正常运营和市场竞争力造成了严重威胁。机器视觉软件通常包含复杂的算法和大量的数据处理能力,广泛应用于制造、医疗和安防等领域。然而,未经授权的使用和人为篡改…...

python文件的读写
要在Python中读写文件,你可以使用以下方法: 1. 打开文件:使用open()函数打开文件,它接受两个参数:文件名和模式。模式可以是只读(r)、写入(w)、追加(a&#…...

2024下《网络工程师》案例简答题,刷这些就够了!
距离2024下半年软考已经越来越近了,不知道今年备考软考网络工程师的同学们开始准备了吗? 简答题一直是网工拿分的重点区域,对于许多考生来说,也往往是最具挑战性的部分。今天我就把那些重要的案例简答题类型整理汇总给大家&#x…...

Astro 实现TodoList网页应用案例
Astro 是一个现代化的静态站点生成器和前端框架,它具有独特的设计理念:岛屿架构。它允许开发人员使用组件化的方式构建内容优先的网站,将各种技术栈(如React、Vue、Svelte等)的组件无缝集成到同一个项目中。 1、创建项…...

计算机毕业设计Hadoop+Spark旅游景点可视化 旅游景点推荐系统 景区游客满意度预测与优化 Apriori算法 景区客流量预测 旅游大数据 景点规划
### 开题报告 **论文题目:** 基于Spark的旅游景点可视化系统的设计与实现 **研究背景与意义:** 随着旅游业的快速发展,人们对旅游信息的获取和处理需求越来越高。传统的旅游信息系统虽然能够提供静态的数据查询和展示功能,但在…...

MySQL存储
目录 1. MySQL存储引擎概述 2. 存储引擎的作用 3.存储引擎类型 4. 查看支持的存储引擎 6. InnoDB存储引擎 7. MyISAM与InnoDB的区别 8. 存储引擎的选择 9. 修改默认存储引擎 1. MySQL存储引擎概述 在MySQL中,数据通过不同的技术存储在文件(或内存…...

手势传感器 - 从零开始认识各种传感器【第十八期】
手势传感器|从零开始认识各种传感器 1、什么是手势传感器 手势传感器是一种能够感知人类手势或动作的传感器。它可以捕捉、识别和解释人类的手部动作或姿势,并将其转换成电信号或数字信号,通过识别人体的手势动作来实现与电子设备的交互,如控…...

【未来餐饮】 配送设置
一、创建门店 关键信息 1. 门店名字要有辨识度,尽量不和其他客户重名 2. 地址要具体到门牌号 3. 定位要和上面的地址一致 可以复制地址搜索地图,然后选择位置 二、创建配送模板 新建模板 填写模板 命名模板,勾上真省钱,然后保…...

移动式气象设备:灵活应对,精准监测的气象先锋
在气象监测领域,随着科技的进步和需求的多样化,移动式气象设备逐渐崭露头角,成为现代气象观测中不可或缺的一部分。这些设备以其灵活性高、部署迅速、监测精准的特点,广泛应用于应急响应、农业生产、户外探险、科研考察等多个领域…...

【AI落地应用实战】DAMODEL深度学习平台部署+本地调用ChatGLM-6B解决方案
ChatGLM-6B是由清华大学和智谱AI开源的一款对话语言模型,基于 General Language Model (GLM)架构,具有 62亿参数。该模型凭借其强大的语言理解和生成能力、轻量级的参数量以及开源的特性,已经成为在学术界和工业界引起了广泛关注。 本篇将介…...

英伟达开始引领下一波浪潮:物理AI
这可能会是AI技术形态的一个转折点,大模型的下一个形态,不再是人和模型一轮一轮的即时问答了。 当地时间 7 月 29 日,在美国丹佛举行的第 51 届 SIGGRAPH 计算机图形学会议上,英伟达创始人、CEO 黄仁勋与 Meta 创始人、CEO 马克・扎克伯格进…...

SQLServer设置端口
在SQL Server中设置端口是一个涉及多个步骤的过程,这些步骤旨在确保数据库服务器能够在新指定的端口上安全、高效地运行。以下是对SQL Server设置端口的详细阐述,包括默认端口、更改端口的步骤、验证更改以及相关的安全考虑。 一、SQL Server默认端口 …...

诊断技巧分享 | 用WPS500压力传感器测试空调压力波形?
最近收到咨询,问我们WPS500压力传感器能不能测汽车的空调压力波形?如果可以的话,应该怎么测? 是可以的。WPS500压力传感器的最大测试压力是34.5 bar,匹配对应的管子的接头,可以测试空调的动态波形。 要做这…...

W1R3S靶机全通详细教程
文章目录 w1r3s主机发现主机扫描 端口扫描tcp端口扫描UDP扫描漏洞扫描 攻击面分析FTP渗透匿名登录 web渗透目录爆破 cuppa cms文件包含漏洞getshell提权 w1r3s 引言 近些日子看红笔大佬的靶机精讲视频时,他的一句话让我感受颇深,很多视频在讲解时&…...

GitHub Revert Merge Commit的现象观察和对PR的思考
文章目录 前言Pull Request 为什么会是这样?Pull Request Branch的差异 ?Two Dot Diff和Three Dot Diff 老生常谈: Merge 和 Rebasegit mergegit rebase Revert Main分支中的一个Merge Commit现象描述解决方案: Revert Feature分支中的一个Merge Commi…...

使用JavaFx Fxml笔记
使用JavaFx Fxml实现账号密码登录 HelloApplication.java:package com.example.dr295cmonth7;import javafx.application.Application; import javafx.fxml.FXMLLoader; import javafx.geometry.Insets; import javafx.scene.Parent; import javafx.scene.Scene; i…...

友盟U-APM——优秀的前端性能监控工具
在数字化转型浪潮的推动下,移动应用已成为企业连接用户、驱动业务增长的核心载体。然而,随着应用复杂度的日益提升,用户对于应用性能稳定性的期待也水涨船高。面对应用崩溃、卡顿、加载缓慢等频发问题,如何确保应用的流畅运行,成为产研团队亟待解决的关键挑战。在此背景下,友盟…...

人工智能与机器学习原理精解【10】
文章目录 数值优化基础理论线性模型基本形式特性应用学习算法 向量输入的二次函数的凸性概述二次函数的一般形式凸函数的定义分析二次函数的凸性注意 详细解释向量输入的二次函数的凸性分析一、二次函数的一般形式二、凸函数的定义三、二次函数的Hessian矩阵四、判断二次函数的…...

TypeScript 简介
文档 typeScript官网中文文档:https://www.tslang.cn/index.html中文文档(简洁点):https://typescript.bootcss.comMDN 前言 JavaScript 引入编程社区已有 20 多年,如今已成为有史以来使用最广泛的跨平台语言之一。JavaScript 最初是一种用…...

什么是知识库?为什么我需要一个?
在互联网的历史上,知识库的定义已经多次改变。最初,它是一个术语,用于描述任何比常见关系“数据库”更先进的复杂数据存储系统。 现在,随着 SaaS 的出现,知识库一词有了更多不同的含义。 根据定义,知识库…...

MySQL学习(16):视图
视图是一种虚拟临时表,并不真正存储数据,它的作用就是方便用户查看实际表的内容或者部分内容 1.视图的使用语法 (1)创建 create view 视图名称 as select语句; #视图形成的虚拟表就来自于select语句所查询的实际表,…...

android13关机按钮 去掉长按事件 去掉启动到安全模式 删除关机长按
总纲 android13 rom 开发总纲说明 目录 1.前言 2.界面效果 3.问题分析 4.代码修改 5.编译替换运行 6.彩蛋 1.前言 在Android操作系统中,关机按钮通常具有多种功能,包括短按关机、长按启动语音助手或重启设备等。在某些情况下,用户或设备管理员可能希望自定义关机按…...

递归求数组和
...

MySQL数据库介绍
前言:本博客仅作记录学习使用,部分图片出自网络,如有侵犯您的权益,请联系删除 一、数据库介绍 1、什么是数据库 数据库就是一个存放计算机数据的仓库,这个仓库是按照一定的数据结构(数据结构是指数据的组织…...

向量数据库性能测试工具(VectorDBBench.com)性价比排名
排名 向量数据库(不同硬件配置) 价格/性能比 QP$(每百万次查询所花费的价格)中型数据集, OpenAI 无标量过滤 QP$(每百万次查询所花费的价格)中型数据集, OpenAI 低标量过滤 QP$(每百万次查询所花费的价格)中型数据集, OpenAI 高标量过滤 QP$(每百万次查询所花费的价…...

2024年的AI人工智能风口是Python?一篇文章告诉你为什么!
Python是一种面向对象的、解释型的、通用的、开源的脚本编程语言,它之所以非常流行,我认为主要有三点原因: 1.Python 简单易用,学习成本低,看起来非常干净; 2.Python 标准库和第三库众多,功能…...