使用VS2015编译JSBSim

1.说明

最近在研究JSBSim的代码,首先百度了一些JSBSim相关的文档和教程,有一篇《Building_JSBSim_with_Visual_Cpp_2010》讲了如何利用VS2010编译JSBSim,参考这个教程编译完成了一个JSBSim工程。但是这个工程是好多年以前的了,在GitHub上下载的最新JSBSim工程已经无法使用VS2010来编译了,所以就用VS2015将JSBSim最新的代码编译了一遍,其中配置内容和VS2010略有不同,所以记录下来,以便参考。

2.代码下载

JSBSim项目的代码下载地址如下:

Sourceforge下载地址:https://sourceforge.net/p/jsbsim/code/ci/master/tree/

GitHub下载地址:https://github.com/JSBSim-Team/jsbsim/tree/master

其中Sourceforge下载地址打开如下图,在右侧的JSBSim版本分支中有三个,分别是master、SG_Path和major_refactor,三个分支应该是不同时期的JSBSim版本,其中master应该是比较新的版本,下载后可以使用VS2015进行编译。

根据SourceForge上的消息,JSBSim在2018年5月份开始迁移到GitHub上了,所以目前JSBSim项目的维护应该是在GitHub上进行着,如果要接触最新的JSBSim代码,可以考虑在GitHub上关注这个项目。

 

3.编译成可执行文件

下载得到JSBSim工程包,解压后打开sln文件,最新的JSBSim工程中sln文件只能用VS2015以上的版本才能打开,打开后的界面如下:

目前这个解决方案中包含了三个工程,第一个项目aeromatic是生成飞行器配置xml文件的工具,第三个工程prep_plot是辅助画图的工具,在此我们重点关注JSBSim工程。

在JSBSim工程在编译前需要进行相应的配置,配置和测试步骤如下:

  • Step1.右击JSBSim工程,选择属性,打开工程属性页

  • Step2.点击配置管理器按钮,进入配置管理器页面,选择相应的活动平台解决方案。其中的x64解决方案平台是为了编译成64位应用程序而使用的,而x86应用程序是为了编译成32位应用程序而使用的。在此处我们选择x86平台,解决方案配置选择Release。

  • Step3.关闭配置管理器页面后,在配置属性->调试页面下的命令参数一栏输入“--script=scripts\c1723.xml --outputlogfile=c1723.csv”,这是为了方便调试所输入的命令参数。

  • Step4.在配置属性 -> C/C++ -> 代码生成 -> 运行库一栏,设置运行库为多线程(/MT),这样设置的目的是将VS2015的运行时库作为lib链接到应用程序中,程序可以在没有安装VS2015的环境中运行。

  • Step5.编译程序,右击工程选择生成或者按F7即可开始编译工程,等待几分钟应该就可以编译成功了。
  • Step6.在成功编译之后,我们可以在工程的Release目录下找到JSBSim.exe,接下来可以运行测试。可以通过VS2015来运行JSBSim.exe,结合之前设置的命令行参数,在菜单栏选择调试->开始执行(不调试)或者按Ctrl+F5即可运行;也可以在工程目录下输入命令行指令来执行。为了方便,我写了一个批处理脚本进行测试,脚本内容如下:
del -Q c1723.csv

Release\JSBSim --script=scripts\c1723.xml --outputlogfile=c1723.csv

pause

执行效果如下图,同时在工程目录下会生成C1723.csv文件,该文件以表格的形式记录下了仿真过程中的所有数据,可以用来进行分析画图或者Replay。

 

4.编译成静态库

面向对象的强大之处在于代码重用很方便,但是我们没法每次都添加这么多的JSBSim源代码到我们自己的工程之中,所以考虑将JSBSim的代码编译成静态库来使用,这样每次只需添加JSBSim的静态库就可以复用其中已有的许多轮子了。编译配置过程如下:

  • Step1.打开JSBSim工程属性页,在此之前,我们应该已经在配置管理器中配置好了要编译的平台版本了,此处选择的编译平台是Release Win32。接着在常规->配置类型一栏,将编译目标从应用程序(.exe)改为静态库(.lib)。

  • Step2.接着切换到C/C++ -> 预处理器 -> 预处理器定义一栏,选择编辑,打开预处理器定义编辑页面,在最后一行输入XML_STATIC这是为了在工程中预先给出XML_STATIC的宏定义,从而将XML工具相关的API导出

  • Step3.在属性页的C/C++ -> 代码生成 –> 运行库一栏设置为多线程 (/MT)

  • Step4.在右侧工程目录栏中,找到JSBSim.cpp文件,右击选择移除,因为我们的静态库不需要使用main函数。
  • Step5.编译成功之后,就可以在工程目录下Release目录下找到编译完成的JSBSim.lib,这就是我们所得到的静态库,接下来对我们的静态库进行测试。
  • Step6.首先在VS2015中新建一个工程,将该工程设置为空项目,命名为JSBSimLibTest。由于VS2015在创建新项目时将.sln文件和. .vcxproj文件分开放了,我们将其移到同一目录下,并在sln解决方案中重新添加工程文件。
  • Step7.拷贝JSBSim工程目录下的src文件夹和JSBSim.cpp到JSBSimLibTest工程目录中,同时将我们之前编译好的JSBSim.lib拷贝到src目录中。虽然我们已经不需要JSBSim的源代码了,但是头文件还是需要的。而JSBSim.cpp则是我们用来对静态库进行测试的调用代码,看其能否实现在原工程中同样的功能。
  • Step8.在JSBSimLibTest工程上右击->添加->现有项,打开文件选择窗口,选择JSBSim.cpp文件。此时可以在IDE中看到添加后的文件提示了许多错误,这是因为我们还没有配置头文件包含目录以及依赖库的关系。
  • Step9.打开属性配置页面,然后打开配置管理器,配置要编译的工程目标为Release,平台选择Win32,这是为了和我们之前编译静态库时的配置相统一,以免编译出错。
  • Step10.在工程属性页 -> C/C++ -> 常规 -> 附加包含目录一栏,点击编辑,打开附加包含目录编辑页面,输入两个附加包含目录,分别是“$(ProjectDir)src”和“$(ProjectDir)src\simgear\xml”,这样编译时工程就能够找到相应的头文件了。

 

  • Step11.在工程属性页 -> C/C++ -> 代码生成 –> 运行库一栏,设置运行库和静态库一样的多线程 (/MT)
  • Step12.在工程属性页 -> 链接器 -> 附加库目录一栏,添加一个附加库目录“$(ProjectDir)src”,接着在工程属性页->链接器->输入->附加依赖项一栏添加一个附加依赖库“JSBSim.lib”,这样我们就将之前编译得到的JSBSim.lib添加到我们的工程中了。
  • Step13.编译JSBSimLibTest工程,编译成功之后我们就得到了一个JSBSimLibTest.exe可执行文件,这个文件应该是可以实现和JSBSim.exe相同的功能的,可以运行测试一下,测试成功就说明我们编译的静态库是可用的。

 

5.总结

这篇使用说明是参考《Building_JSBSim_with_Visual_Cpp_2010》写的,目的是作为简要的参考,在编译和执行JSBSim的过程中也遇到的一些问题,在此进行简要说明。

  • 编译平台选择x64x86的区别

VS2015可以选择将应用程序编译成x64位目标机程序和x86 32位应用程序,之前没有尝试过x64位应用程序的编译,相比于32位应用程序,64位程序应该在性能上有所提升,但是32位程序同样可以在64位的机器上运行,相对来说兼容性更好一些,可以按需选择。

  • Debug/Release模式的区别

Debug模式相对于Release模式在编译时减少了很多优化选项,这样做牺牲了一部分的性能换来的调试的方便,在之前测试JSBSim.exe运行时,Release编译得到的应用程序运行明显更快,显然优化是很有效果的。要发布应用程序时还是选Release好了。

  • 运行时库MT/MD

在之前的项目中就遇到过相关的问题,具体内容可以自行百度,可以参考《你所不知道的C和C++运行库》,本文在编译时选择MT,主要是为了减小应用程序对VS2015的依赖,可以在其他环境中独立运行。

  • 静态库添加到低版本工程中

之前干过这样的事,将VS2010编译的静态库或者动态库在VC6.0的工程中使用,还是可以用的。同样将VS2015编译的静态库给VS2010或者VC6.0应该也是可以用的,可以尝试一下。

  • JSBSim的用处和使用方法

面向对象的代码是可以复用的,我们有了JSBSim.lib和相关的头文件,那么就可以在自己的工程中复用相关的代码了。不过JSBSim中的各个轮子还有待研究,如果以后有需要,就可以利用其中的代码实现自己的功能了。

参考文章:

[1]Building_JSBSim_with_Visual_Cpp_2010.pdf(下载地址1)(下载地址2

[2]你所不知道的C和C++运行库

 

<think>嗯,用户的问题是关于STM32F1的I2C HAL库中使用中断和DMA模式进行内存读取操作的,特别是三个函数HAL_I2C_Mem_Read、HAL_I2C_Mem_Read_IT和HAL_I2C_Mem_Read_DMA的使用方法、区别以及组合方式。首先,我需要回忆一下这三个函数的不同之处。 根据引用资料,HAL_I2C_Mem_Read是阻塞模式,也就是同步传输,函数会一直等待直到传输完成或者超时。而HAL_I2C_Mem_Read_IT和HAL_I2C_Mem_Read_DMA则是非阻塞模式,分别使用中断和DMA来实现异步传输。用户可能在使用过程中遇到了性能问题,想要优化代码,或者需要同时处理其他任务,所以想了解如何有效利用中断和DMA。 接下来,我需要确认每个函数的具体参数和使用方法。比如,HAL_I2C_Mem_Read需要指定设备地址、内存地址、数据大小、超时时间等。而中断版本和DMA版本则不需要超时参数,但需要启用相应的中断或DMA配置。同时,DMA模式需要配置DMA通道,并处理传输完成中断。 另外,用户提到的组合方式,可能是指如何在不同场景下结合使用这些函数。比如,在需要高效传输大量数据时使用DMA,而在需要即时响应时使用中断。或者,可能需要同时使用中断和DMA来处理不同的传输阶段。 还需要注意硬件配置,比如I2C的GPIO模式是否正确设置为开漏输出,以及是否外接上拉电阻。引用5提到,如果硬件I2C没有上拉电阻,可能会导致通信失败,这点需要提醒用户检查硬件连接。 最后,用户可能需要示例代码来说明如何使用这些函数,以及如何处理回调函数和错误中断。确保在回答中覆盖这些点,结构清晰,分步骤解释每个函数的使用场景和配置步骤,并结合引用中的信息进行说明。</think>在STM32F1中通过I2C使用中断和DMA模式进行内存读取操作时,需要理解以下三个核心函数的关系与使用场景: ### 一、函数功能对比 | 函数 | 传输模式 | 特点 | 适用场景 | |------------------------|--------------|-----------------------------------------------------------------------|------------------------| | `HAL_I2C_Mem_Read` | **阻塞模式** | 同步执行,占用CPU直到传输完成或超时 | 简单场景、小数据量 | | `HAL_I2C_Mem_Read_IT` | **中断模式** | 异步执行,通过I2C事件中断驱动 | 实时性要求较高的场景 | | `HAL_I2C_Mem_Read_DMA` | **DMA模式** | 异步执行,由DMA控制器完成数据传输,极大减少CPU占用 | 大数据量、高效能需求 | ### 二、硬件配置要点 1. **GPIO配置**:必须将SCL/SDA引脚设置为复用开漏模式(Alternate Function Open-Drain),且**必须外接4.7KΩ上拉电阻**(硬件I2C的电气特性要求)[^5] 2. **中断优先级**:在CubeMX中需配置I2C事件中断和DMA流中断优先级 3. **DMA通道选择**:根据STM32F1的DMA映射表选择正确的通道(如DMA1 Channel6/7) ### 三、函数使用详解 #### 1. 阻塞模式(HAL_I2C_Mem_Read) ```c HAL_StatusTypeDef status = HAL_I2C_Mem_Read( &hi2c1, // I2C句柄 0xA0, // 设备地址(7位地址左移1位) 0x00, // 内存地址 I2C_MEMADD_SIZE_8BIT, // 地址长度(8位/16位) pData, // 数据缓冲区指针 32, // 数据长度 1000); // 超时时间(ms) ``` #### 2. 中断模式(HAL_I2C_Mem_Read_IT) ```c // 步骤1:启动传输 HAL_I2C_Mem_Read_IT(&hi2c1, 0xA0, 0x00, I2C_MEMADD_SIZE_8BIT, pData, 32); // 步骤2:实现回调函数 void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c) { if(hi2c->Instance == I2C1) { // 处理接收完成事件 } } // 步骤3:错误处理 void HAL_I2C_ErrorCallback(I2C_HandleTypeDef *hi2c) { // 处理通信错误 } ``` #### 3. DMA模式(HAL_I2C_Mem_Read_DMA) ```c // 步骤1:配置DMA __HAL_LINKDMA(&hi2c1, hdmarx, hdma_i2c1_rx); // 关联DMA句柄 // 步骤2:启动传输 HAL_I2C_Mem_Read_DMA(&hi2c1, 0xA0, 0x00, I2C_MEMADD_SIZE_8BIT, pData, 1024); // 步骤3:实现DMA回调 void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c) { // 处理DMA传输完成 } ``` ### 四、组合应用方案 1. **中断+DMA混合模式**: - 使用`HAL_I2C_Mem_Read_DMA`启动传输 - 通过`HAL_I2C_ER_IRQHandler`处理错误中断 - 在`HAL_I2C_EV_IRQHandler`中处理事件中断 ```c // 在stm32f1xx_it.c中添加中断处理 void I2C1_EV_IRQHandler(void) { HAL_I2C_EV_IRQHandler(&hi2c1); } void I2C1_ER_IRQHandler(void) { HAL_I2C_ER_IRQHandler(&hi2c1); } ``` 2. **分块传输策略**: ```c #define BLOCK_SIZE 256 void MultiBlock_Read() { HAL_I2C_Mem_Read_DMA(&hi2c1, 0xA0, 0x00, I2C_MEMADD_SIZE_8BIT, pData, BLOCK_SIZE); } void HAL_I2C_MemRxCpltCallback(I2C_HandleTypeDef *hi2c) { static uint32_t offset = BLOCK_SIZE; if(offset < TOTAL_SIZE) { HAL_I2C_Mem_Read_DMA(hi2c, 0xA0, offset, I2C_MEMADD_SIZE_8BIT, pData+offset, BLOCK_SIZE); offset += BLOCK_SIZE; } } ``` ### 五、关键问题排查 1. **通信超时**:检查SCL/SDA波形是否正常,确认从设备响应ACK 2. **DMA传输不启动**:确保`__HAL_DMA_ENABLE()`被调用,DMA通道未冲突 3. **数据错位**:验证I2C时钟配置(`hi2c1.Init.ClockSpeed`)是否符合从设备要求 4. **中断未触发**:检查NVIC配置是否使能,中断优先级是否合理 ### 六、性能优化建议 1. 使用DMA双缓冲技术减少传输间隙 2. 在`HAL_I2C_MemRxCpltCallback`中预处理数据 3. 合理设置I2C时钟分频(标准模式100kHz,快速模式400kHz)
评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值