- 文章背景:学习到STM32裸板开发中的串口收发实验,苦于没钱买开发板,现在的电脑也鲜有自带串口的,所以想着能否空手套白狼,直接利用KEIL5在线完成仿真。没想到还确实有办法!
- 所需软件:
- 1 KEIL5
- 虚拟串口:VSPD
- 虚拟串口调试器SSCOM
- 实验目的:在KEIL5中完成基于STM32F103开发板的程序编写,达到PC机利用串口向开发板发送字符串,开发板通过串口收到数据后,再将其原封不动的发回PC的目的。
1、下载并安装VSPD(Virtual Serial Port Driver)备用
- 下载链接:
- 软件界面:按照下载压缩包文件的要求完成安装和破解(建议安装7.2之前的版本,网上流传的的较新版本破解不完美,使用了几天后就提示can not pair port不能使用了),安装完毕后如下图所示:
]
-
使用方法:
- 1 选择 列表中出现 的串口 组对 (只显示还没有使用的)
- 2 点击 按钮 Add pair 创建 串口 组对
- 3 刚创建的 组对 出现在 Virtual ports之下
- 4 串口列表中,不再有它俩的组对(上图显示的即为创建的COM1和COM2串口对)
- 5 可以在“控制面板”–“设备管理器”验证 刚创建成功的虚拟串口 组对
创建成功后,就可以像物理串口一样去使用它们。一端使用你的程序打开虚拟串口COM1, 另一端使用 串口助手 打开虚拟串口COM2。 因为 虚拟串口COM2和 COM1是相互关联的一对,所以, 从COM2发送的数据,COM1将会接收到, 反之 从COM1发送的数据, COM2将会接收到,这样就可以方便地调试你的程序了 。
创建后的虚拟串口将会一直存在, 如果不再使用,可以删除它们。(先在左侧选择要删除的串口对,而后右侧点击按钮 Delete pair 删除。删除串口对后设备管理器中也将没有它们。
2、程序编写
-
创建工程文件夹Usart,并建立CMSIS、FWLib、User三个文件夹:
- 复制STM32Fx标准外设库(STM32F10x_StdPeriph_Lib_V3.5.0)中的
Libraries\CMSIS\CM3\CoreSupport\
中的core_cm3.c
和core_cm3.h
这2个文件到Usart\CMSIS\
中; - 复制STM32Fx标准外设库(STM32F10x_StdPeriph_Lib_V3.5.0)中的
Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\
中的stm32f10x.h、system_stm32f10x.c、system_stm32f10x.h
3个文件到Usart\CMSIS\
中; - 复制STM32Fx标准外设库(STM32F10x_StdPeriph_Lib_V3.5.0)中的
Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\
中的arm
文件夹到Usart\CMSIS\StartUp\
中; - 复制STM32Fx标准外设库(STM32F10x_StdPeriph_Lib_V3.5.0)中的
Libraries\STM32F10x_StdPeriph_Driver\
中的inc
和src
两个文件夹到Usart\FWLib\
中; - 复制STM32Fx标准外设库(STM32F10x_StdPeriph_Lib_V3.5.0)中的
Libraries\Project\STM32F10x_StdPeriph_Template\
中的stm32f10x_conf.h
、stm32f10x_it.c
、stm32f10x.h
、main.c
、system_stm32f10x.c
这几个文件复制到Usart\User\
中;
- 复制STM32Fx标准外设库(STM32F10x_StdPeriph_Lib_V3.5.0)中的
-
打开KEIL5,点击Project—new uVision Project,选择刚才建立的Usart文件夹,为工程创建工程名Usart后,点击保存后,建立工程(选择STM32F103RB开发板)。
-
在左侧工程树中,右击Target 1,选择Manerge Project Items
- 为Target1建立FWLIB、STARTUP、CMSIS、USER四个组,并分别添加如下文件:
- FWLIB中添加
Usart\FWLib\src\
中的所有文件; - STARTUP中添加3种类型的板子的启动文件:startup_stm32f10x_hd.s,startup_stm32f10x_md.s,startup_stm32f10x_ld.s;
- CMSIS中添加core_cm3.c文件;
- USER中添加
Usart\User
中的所有文件。
- FWLIB中添加
-
配置目标工程1:左侧结构树下右击Target1,选择Option for Target选项,选择C\C++选项卡,如图所示定义两个预处理符号:
USE_STDPERIPH_DRIVER,STM32F10X_MD
-
配置目标工程2:设置头文件搜索路径。选择C\C++选项卡,如图所示定义3个搜索路径:
.\CMSIS;.\FWlib\inc;.\User
: -
编辑main.c函数,构建USART收发主函数:
-
/* ****************************************************************************** * @file main.c * @author Leon * @version V1.0 * @date 2021-9-28 * @brief PC send data to STM32 with USART, then STM32 send the data back to PC ****************************************************************************** */ /* Includes ------------------------------------------------------------------*/ #include "stm32f10x.h" #include <stdio.h> /* Private typedef -----------------------------------------------------------*/ /* Private define ------------------------------------------------------------*/ /* Private macro -------------------------------------------------------------*/ /* Private variables ---------------------------------------------------------*/ /* Private function prototypes -----------------------------------------------*/ void RCC_Configuration(void); void GPIO_Configuration(void); void USART_Configuration(void); /* Private functions ---------------------------------------------------------*/ /** * @brief Main program. * @param None * @retval None */ int main(void) { vu16 i = 0; RCC_Configuration(); GPIO_Configuration(); USART_Configuration(); while(1){ /*Wait for USART1 to receive over*/ if(USART_GetFlagStatus(USART1, USART_IT_RXNE) == SET){ /* Send data received to USART1 */ USART_SendData(USART1, USART_ReceiveData(USART1)); /* Short delay */ for(i = 0; i<500; i++); //while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET); } } } /** * @brief RCC configuration program, enable USART and GPIOA. * @param None * @retval None */ void RCC_Configuration(void) { ErrorStatus HSEStartUpStatus; RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); HSEStartUpStatus = RCC_WaitForHSEStartUp(); if(HSEStartUpStatus == SUCCESS) { RCC_HCLKConfig(RCC_SYSCLK_Div1); RCC_PCLK2Config(RCC_HCLK_Div1); RCC_PCLK1Config(RCC_HCLK_Div2); FLASH_SetLatency(FLASH_Latency_2); FLASH_PrefetchBufferCmd(FLASH_PrefetchBuffer_Enable); /* PLL CLK Source is HSE, Mull 9, PLL = 8M * 9 = 72M */ RCC_PLLConfig(RCC_PLLSource_HSE_Div1,RCC_PLLMul_9); RCC_PLLCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while(RCC_GetSYSCLKSource() != 0x8); } RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1, ENABLE); } /** * @brief Config USATR1 Pin which Tx(GPIOA.9), Rx(GPIOA.10). * @param None * @retval None */ void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; GPIO_Init(GPIOA, &GPIO_InitStructure); } /** * @brief Config USART1(Baund=9600, DataLen=8,StopBit=1, No ECC,) Disable USART CLK, CLK Polar low, Catch data at second edage LastBit clk not from SCLK. * @param None * @retval None */ void USART_Configuration(void) { USART_InitTypeDef USART_InitStructure; USART_ClockInitTypeDef USART_ClockInitStructure; USART_InitStructure.USART_BaudRate = 9600; USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; USART_InitStructure.USART_Parity = USART_Parity_No; USART_InitStructure.USART_StopBits = USART_StopBits_1; USART_InitStructure.USART_WordLength = USART_WordLength_8b; USART_Init(USART1, &USART_InitStructure); USART_ClockInitStructure.USART_Clock = USART_Clock_Disable; USART_ClockInitStructure.USART_CPHA = USART_CPHA_2Edge; USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low; USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable; USART_ClockInit(USART1, &USART_ClockInitStructure); USART_Cmd(USART1, ENABLE); } /******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
-
辑配置文件stm32f10x_conf.h,使能工程用到的GPIO、RCC、USART、FLASH模块:
-
/** **************************************************************************** * @file stm32f10x_conf.h * @author leon * @version V1.0 * @date 2021/9/18 * @brief Library configuration file. ****************************************************************************** * @attention * * THE PRESENT FIRMWARE WHICH IS FOR GUIDANCE ONLY AIMS AT PROVIDING CUSTOMERS * WITH CODING INFORMATION REGARDING THEIR PRODUCTS IN ORDER FOR THEM TO SAVE * TIME. AS A RESULT, STMICROELECTRONICS SHALL NOT BE HELD LIABLE FOR ANY * DIRECT, INDIRECT OR CONSEQUENTIAL DAMAGES WITH RESPECT TO ANY CLAIMS ARISING * FROM THE CONTENT OF SUCH FIRMWARE AND/OR THE USE MADE BY CUSTOMERS OF THE * CODING INFORMATION CONTAINED HEREIN IN CONNECTION WITH THEIR PRODUCTS. * * <h2><center>© COPYRIGHT 2011 STMicroelectronics</center></h2> ****************************************************************************** */ /* Define to prevent recursive inclusion -------------------------------------*/ #ifndef __STM32F10x_CONF_H #define __STM32F10x_CONF_H /* Includes ------------------------------------------------------------------*/ /* Uncomment/Comment the line below to enable/disable peripheral header file inclusion */ //#include "stm32f10x_adc.h" //#include "stm32f10x_bkp.h" //#include "stm32f10x_can.h" //#include "stm32f10x_cec.h" //#include "stm32f10x_crc.h" //#include "stm32f10x_dac.h" //#include "stm32f10x_dbgmcu.h" //#include "stm32f10x_dma.h" //#include "stm32f10x_exti.h" #include "stm32f10x_flash.h" //#include "stm32f10x_fsmc.h" #include "stm32f10x_gpio.h" //#include "stm32f10x_i2c.h" //#include "stm32f10x_iwdg.h" //#include "stm32f10x_pwr.h" #include "stm32f10x_rcc.h" //#include "stm32f10x_rtc.h" //#include "stm32f10x_sdio.h" //#include "stm32f10x_spi.h" //#include "stm32f10x_tim.h" #include "stm32f10x_usart.h" //#include "stm32f10x_wwdg.h" //#include "misc.h" /* High level functions for NVIC and SysTick (add-on to CMSIS functions) */ /* Exported types ------------------------------------------------------------*/ /* Exported constants --------------------------------------------------------*/ /* Uncomment the line below to expanse the "assert_param" macro in the Standard Peripheral Library drivers code */ /* #define USE_FULL_ASSERT 1 */ /* Exported macro ------------------------------------------------------------*/ #ifdef USE_FULL_ASSERT /** * @brief The assert_param macro is used for function's parameters check. * @param expr: If expr is false, it calls assert_failed function which reports * the name of the source file and the source line number of the call * that failed. If expr is true, it returns no value. * @retval None */ #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__)) /* Exported functions ------------------------------------------------------- */ void assert_failed(uint8_t* file, uint32_t line); #else #define assert_param(expr) ((void)0) #endif /* USE_FULL_ASSERT */ #endif /* __STM32F10x_CONF_H */ /******************* (C) COPYRIGHT 2011 STMicroelectronics *****END OF FILE****/
-
中断处理函数文件stm32f10x_it.c不用修改,因为本程序中未使用到中断。
-
点击编译按钮(Rebuilde:编译工程下的所有文件;Builde:编译当前文件),使之达到error 0,warning 0 的编译结果。
3、KEIL中关于串口的设置
- 1 创建调试初始化的ini文件
在Keil uVision5开发工具的文件夹 D:\Keil_v5之下新建一个配置文件map_com.ini(利用txt文档建立后更改后缀为ini),内容为:
MODE COM1 9600,0,8,1
ASSIGN COM1 <S1IN> S1OUT
意思为:将 COM1 设置为 9600 波特率,无奇偶校验,8 位数据位,1位停止位(要和KEIL工程中程序里设置的一致)。这是针对ARM系列的。但如果是C51的开发板,上述配置文件的第二行的<SIN>和SOUT就不需要添加数字了,因为一般C51只有一个串口,不需要利用数字来区分是开发板上的哪一个串口。
-
将KEIL软件仿真器的串口映射到 COM1(加载配置文件)
-
1)在 Keil 软件内,点击菜单的 Project – Options for Target(Alt+F7),或者工具条的【配置选型】图标
2)点选 Debug 选项页, 点选 左上侧的 Use simulator
3)Initialization File 框之后的 … 按钮,选择 初始化文件 map_com.ini , 点击 打开 – OK 返回。
-
4、启动调试
- 先点 菜单 Debug – Start/Stop Debug Session(Ctrl+F5) , 再点 Debug – Run(F5) 或者工具条上的 Debug ,进入调试界面,然后点击工具条上的全速运行,启动调试运行。
-
打开串口调试助手,选择COM2(因为我们KEIL里设置的开发板上使用的串口为COM1,之前已经通过虚拟串口驱动VSPD建立了COM1和COM2的连接,所以串口间才可以相互发送数据)
-
在菜单栏“端口号“中选择COM2(注意:波特率一定要是 9600,与程序映射的 COM1 保持一致),而后点击打开串口。之后会如上图显示
-
在右下角文本输入框中,输入字符A,并点击发送,可在输出窗口中看到接收到的字符A
-
]
-
可以在KEIL中打开USART1串口观察窗,查看由PC机发给STM32的数据
-
]
-
具体查看端口信息:
- 菜单 Peripherals – General Purpose I/O – GPIO
- 在 GPIO ODR 右侧,可以看到 按实际电平,具体位的 勾选与否。
]
-
逻辑分析仪查看端口的图形显示
- 点击工具栏 的 启动调试 – 逻辑分析仪 – Logic Analyzer – Setup… – 新加图标
- 输入 PORTE.6 , 选择 Bit ,就可以查看其电平变化的以及求得时差
-
]
5、实物下载
当你有实际的开发板和调试线(USB转串口驱动)的时候,又怎么实现程序的下载和调试呢?
- 利用**st-link 调试线 **
- 安装驱动程序后,连接设备与计算机,点击菜单的 Project – Options for Target
- 1) 在 Debug 选项页
- (1)勾选右侧的 Use ST-Link Debugger
- (2) 点击 Setting 按钮
- (3) 在 Debug选项页的 选择 ort: SW Max:1.8MHz
- (4) 在 Flash Download 选项页
- 勾选 Program, Verify, Reset and Run
- 选择 STM32F10x High-density… 512k
- 2) 在 Utilities 选项页
- 勾选 Use Debug Driver
- 勾选 Update Target before Debugging
- 3)点击 工具栏的 load 图标,即可下载
]
- 利用USB转串口线
- 1) 下载安装 CH340驱动(USB串口驱动)_XP_WIN7共用驱动程序
- 2) 使用USB线连接设备,下载启动 FlyMcu 下载工具
- 3)点击菜单上的 搜索串口,找到COMx:空闲的USB-SERIAL CH340 (自己的线)
- 4)点击 按钮 … ,找到生成的 .hex 文件
- 5)点击 开始编程, 直到完成即可。
]
- 以上就是利用KEIL5完成STM编程、调试和下载的所有有实物和无实物的编程调试办法。需要注意的是我们在线仿真调试好的程序可能和程序下载到实际板子上进行运行的结果不同,原因在于仿真时候速度比较慢,而实际运行速度比较快,所以如有需要可以在适当语句后面加入延时函数来解决速度匹配问题。
参考资料:无实物Keil软件仿真与串口助手的联机调试_吴超_新浪博客 (sina.com.cn)