看本博客之前,可以先看我这个不错的qemu博客:
关于windows上使用qemu分别仿真stm32和a9以及串口输出_标biao的博客
现在开始进入主题~(这里我都是在windows系统上进行的)
目标:stm32f407这个常用单片机的qemu仿真工程。
从上面引用那篇博客可以看出,qemu有两个可以尝试:官方qemu,RTthread的qemu。
先讲结论:RTthread的qemu能正确跑stm32f4工程(cubemx创建出的工程需要我们做一点儿修改,就是RCC初始化代码我们手动加上。RTthread studio创建的工程不需要任何修改),串口是成功输出到控制台上的。但是官方qemu不行(无论哪个IDE创建出来的stm32f4工程)。
创建裸机工程
首先,我们需要创建一个带有串口驱动代码的裸机工程,两种方法:
* RTthread studio这个IDE
我们在这个IDE中创建出一个stm32f407的裸机工程,接下来的参考我的博客 关于windows上使用qemu分别仿真stm32和a9以及串口输出_标biao的博客
1. RTthread的qemu
这个是能正确在RTthread的qemu运行的(串口能在控制台输出)(这个qemu被改造过了,支持了stm32f407芯片级模拟)。此外,我在这个IDE创建了一个stm32f405RG的工程,同样能正确在RTthread的qemu运行的(说明stm32f405RG的工程在stm32f407芯片也能运行)。
2. 官方的qemu
反正就是控制台没有输出,不知道程序有没有在这个qemu里运行起来或者死掉了,各种尝试,确实不行。
* ST官方的stm32CubeMX
这个其实不是一个IDE,而是ST官方的一个例程配置生成工具,被广泛使用。生成工程,步骤如下:(因为qemu并没有完全对芯片进行模拟,所以建立工程选择的串口等要特别小心,否则运行没有效果)
- 打开软件,点按照MCU生成工程
- 选择stm32f407zgt6芯片,开始项目
- 选中usart1,rtthread的qmeu只对usart1的asynchronous功能进行了模拟,别选错了。所以我们在产生的代码中看到,调用的库函数是HAL_UART_Init()(这里不是HAL_USART_Init),把usart1的基地址传进去的。
- 工程取个名字,选择Makefile工具,这样方便我们自己命令行编译
- 切换到Code Generator 页面,选择仅产生需要的库文件,这样能让工程变得小一些
- 把SystemClock_Config的Visibility勾选上,这样这个函数代码能产生在main文件,更直观
- 点击右上角的GENERATE CODE生成工程
- 打开文件夹,产生的工程如下:
- 我们在main函数添加串口打印代码 HAL_UART_Transmit(&huart1,"123\n",4, 100);
HAL_Delay(500); - 这个工程开始编译,
- 下载到板子上,我的是stm32f407ZGT6正点原子的探索者板子,按一下板子上复位按钮,能成功串口打印出来了的
- 下载到qemu,没有任何输出,假死状态
- 注意分析刚才产生的工程的源码,我们会发现 void SystemInit(void) 函数里面,
SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; 没有被使能。
RCC也没有复位初始化。导致了qemu不能跑,但是为什么板子还能跑呢,我也不知道原因,我猜可能板子有stlink这样的硬件调试器下载代码时候为单片机做了一些初始化之类的默认操作吧。但是qemu就比较严苛了,必须保证代码完全正确,才能运行,当然有时间可以去研究一下qemu的源码,学习学习。
我们可以看一下,正点原子keil例程里面,同样是hal库工程,人家就有初始化的,如下:
人家RT-Thread Studio生成的工程,也是有的
所以我觉得可能是stm32cubemx的一个bug???(或者是我前面生成工程时候没有设置对?有知道的告诉我一声哈,谢谢),就这个我对比了一两天,才找出来这个问题。 - 我们照猫画虎,修改一下。
修改1:关于FPU设置,这里要牵扯到很多宏,才能打开这个(比如编译器选项传入 __FPU_PRESENT = 1U,ARM_MATH_CM4,__CC_ARM,__TARGET_FPU_VFP等,可以参考一下keil如何开启fpu ),我们不打开也是都能正常跑程序的,但是可能有浮点运算时候就出问题了。简单起见,所以我们直接把这个条件宏给去掉即可(这是因为我们明确知道stm32f407是带fpu的)
修改2:RCC复位初始化那段代码加上
修改3:把94行的宏 USER_VECT_TAB_ADDRESS 给打开,这样SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET;这行代码就生效了
代码如下:
其实RCC复位初始化这段代码,HAL库自己有个库函数,HAL_RCC_DeInit(); 我们在main函数一开始就调用,就可以了,这样就不要添加上面那段RCC代码了,我已经测试过了,板子和qemu都能正确跑起来,控制台输出的,大家自己可以去试试。
我把整个工程包含那个RTthread的qemu都打包了,大家可以下载 stm32CubeMX生成qemu能跑的工程
注:给qemu下载程序运行,只能下载bin文件,不能是elf文件,否则控制台无输出(应该压根就没有运行不起来),如果是给qemu调试,那么有用elf文件。
为了验证官方的qemu是否也行,我按照这位作者的博客 (我最先在网上就看到这个了,之前就已经多次尝试过了不行)qemu上跑stm32 模拟stm32开发板
我同样的cubemx生成了stm32f405RG的工程,控制台就是始终没有输出。我还是不甘心,担心cubemx产生的工程有问题,然后用RT-Thread Studio产生了stm32f405RG的工程,官方qemu仍然控制台输出不了。然后按照他说的用stm32cubeIDE这个IDE配置环境生成了工程,可是控制台始终仍然没法输出。但是这些工程,同样的qemu命令,我下载到rtthread的qemu(它没有netduinoplus2 这个stm32f405RG机器,所以我用的是stm32f407-atk-explorer机器)就能成功运行控制台有输出。所以官方qemu确实还有问题啊,stm32方面确实需要自己从源码上改造它。
我搞了一个多星期,各种尝试,还是不行,无奈,放弃了,我觉得有可能他用的IDE版本,qemu版本和我的不同,导致的吧,但是他的博客里面没有说版本等信息,所以我也不再折腾他这个了,如果有老哥搞出来了,还请告诉我一下,谢谢。因此,才有了我这一连串的qemu研究的博客。
我怀疑是官方qemu对stm32的模拟不够好吧。有兴趣的话可以专门去研究一下qemu的源码,进一步改造改造呗。