- 文章背景:學(xué)習(xí)到STM32裸板開發(fā)中的串口收發(fā)實驗,苦于沒錢買開發(fā)板,現(xiàn)在的電腦也鮮有自帶串口的,所以想著能否空手套白狼,直接利用KEIL5在線完成仿真。沒想到還確實有辦法!
-
所需軟件:
- 1 KEIL5
- 虛擬串口:VSPD
- 虛擬串口調(diào)試器SSCOM
- 實驗?zāi)康?/strong>:在KEIL5中完成基于STM32F103開發(fā)板的程序編寫,達到PC機利用串口向開發(fā)板發(fā)送字符串,開發(fā)板通過串口收到數(shù)據(jù)后,再將其原封不動的發(fā)回PC的目的。
1、下載并安裝VSPD(Virtual Serial Port Driver)備用
- 下載鏈接:
- 軟件界面:按照下載壓縮包文件的要求完成安裝和破解(建議安裝7.2之前的版本,網(wǎng)上流傳的的較新版本破解不完美,使用了幾天后就提示can not pair port不能使用了),安裝完畢后如下圖所示:

]
-
使用方法:
- 1 選擇 列表中出現(xiàn) 的串口 組對 (只顯示還沒有使用的)
- 2 點擊 按鈕 Add pair 創(chuàng)建 串口 組對
- 3 剛創(chuàng)建的 組對 出現(xiàn)在 Virtual ports之下
- 4 串口列表中,不再有它倆的組對(上圖顯示的即為創(chuàng)建的COM1和COM2串口對)
- 5 可以在“控制面板”--“設(shè)備管理器”驗證 剛創(chuàng)建成功的虛擬串口 組對
image-20210930220926122
創(chuàng)建成功后,就可以像物理串口一樣去使用它們。一端使用你的程序打開虛擬串口COM1, 另一端使用 串口助手 打開虛擬串口COM2。 因為 虛擬串口COM2和 COM1是相互關(guān)聯(lián)的一對,所以, 從COM2發(fā)送的數(shù)據(jù),COM1將會接收到, 反之 從COM1發(fā)送的數(shù)據(jù), COM2將會接收到,這樣就可以方便地調(diào)試你的程序了 。
創(chuàng)建后的虛擬串口將會一直存在, 如果不再使用,可以刪除它們。(先在左側(cè)選擇要刪除的串口對,而后右側(cè)點擊按鈕 Delete pair 刪除。刪除串口對后設(shè)備管理器中也將沒有它們。
2、程序編寫
-
創(chuàng)建工程文件夾Usart,并建立CMSIS、FWLib、User三個文件夾:
- 復(fù)制STM32Fx標(biāo)準(zhǔn)外設(shè)庫(STM32F10x_StdPeriph_Lib_V3.5.0)中的
Libraries\CMSIS\CM3\CoreSupport\中的core_cm3.c和core_cm3.h這2個文件到Usart\CMSIS\中; - 復(fù)制STM32Fx標(biāo)準(zhǔn)外設(shè)庫(STM32F10x_StdPeriph_Lib_V3.5.0)中的
Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\中的stm32f10x.h、system_stm32f10x.c、system_stm32f10x.h3個文件到Usart\CMSIS\中; - 復(fù)制STM32Fx標(biāo)準(zhǔn)外設(shè)庫(STM32F10x_StdPeriph_Lib_V3.5.0)中的
Libraries\CMSIS\CM3\DeviceSupport\ST\STM32F10x\startup\中的arm文件夾到Usart\CMSIS\StartUp\中; - 復(fù)制STM32Fx標(biāo)準(zhǔn)外設(shè)庫(STM32F10x_StdPeriph_Lib_V3.5.0)中的
Libraries\STM32F10x_StdPeriph_Driver\中的inc和src兩個文件夾到Usart\FWLib\中; - 復(fù)制STM32Fx標(biāo)準(zhǔn)外設(shè)庫(STM32F10x_StdPeriph_Lib_V3.5.0)中的
Libraries\Project\STM32F10x_StdPeriph_Template\中的stm32f10x_conf.h、stm32f10x_it.c、stm32f10x.h、main.c、system_stm32f10x.c這幾個文件復(fù)制到Usart\User\中;
- 復(fù)制STM32Fx標(biāo)準(zhǔn)外設(shè)庫(STM32F10x_StdPeriph_Lib_V3.5.0)中的
打開KEIL5,點擊Project—new uVision Project,選擇剛才建立的Usart文件夾,為工程創(chuàng)建工程名Usart后,點擊保存后,建立工程(選擇STM32F103RB開發(fā)板)。
在左側(cè)工程樹中,右擊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中添加

配置目標(biāo)工程1:左側(cè)結(jié)構(gòu)樹下右擊Target1,選擇Option for Target選項,選擇C\C++選項卡,如圖所示定義兩個預(yù)處理符號:
USE_STDPERIPH_DRIVER,STM32F10X_MD- img-juvWdMC6-1633059129325
配置目標(biāo)工程2:設(shè)置頭文件搜索路徑。選擇C\C++選項卡,如圖所示定義3個搜索路徑:
.\CMSIS;.\FWlib\inc;.\User:- 633059129327
編輯main.c函數(shù),構(gòu)建USART收發(fā)主函數(shù):
/* ****************************************************************************** * @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****/中斷處理函數(shù)文件stm32f10x_it.c不用修改,因為本程序中未使用到中斷。
點擊編譯按鈕(Rebuilde:編譯工程下的所有文件;Builde:編譯當(dāng)前文件),使之達到error 0,warning 0 的編譯結(jié)果。
- image-20210930235422693
3、KEIL中關(guān)于串口的設(shè)置
- 1 創(chuàng)建調(diào)試初始化的ini文件
在Keil uVision5開發(fā)工具的文件夾 D:\Keil_v5之下新建一個配置文件map_com.ini(利用txt文檔建立后更改后綴為ini),內(nèi)容為:
MODE COM1 9600,0,8,1
ASSIGN COM1 <S1IN> S1OUT
意思為:將 COM1 設(shè)置為 9600 波特率,無奇偶校驗,8 位數(shù)據(jù)位,1位停止位(要和KEIL工程中程序里設(shè)置的一致)。這是針對ARM系列的。但如果是C51的開發(fā)板,上述配置文件的第二行的<SIN>和SOUT就不需要添加數(shù)字了,因為一般C51只有一個串口,不需要利用數(shù)字來區(qū)分是開發(fā)板上的哪一個串口。
-
將KEIL軟件仿真器的串口映射到 COM1(加載配置文件)
-
1)在 Keil 軟件內(nèi),點擊菜單的 Project -- Options for Target(Alt+F7),或者工具條的【配置選型】圖標(biāo)
2)點選 Debug 選項頁, 點選 左上側(cè)的 Use simulator
3)Initialization File 框之后的 ... 按鈕,選擇 初始化文件 map_com.ini , 點擊 打開 -- OK 返回。
img-o7U5GpqU-1633059129331 -
4、啟動調(diào)試
- 先點 菜單 Debug -- Start/Stop Debug Session(Ctrl+F5) , 再點 Debug -- Run(F5) 或者工具條上的 Debug ,進入調(diào)試界面,然后點擊工具條上的全速運行,啟動調(diào)試運行。

打開串口調(diào)試助手,選擇COM2(因為我們KEIL里設(shè)置的開發(fā)板上使用的串口為COM1,之前已經(jīng)通過虛擬串口驅(qū)動VSPD建立了COM1和COM2的連接,所以串口間才可以相互發(fā)送數(shù)據(jù))
- image-20210930235959423
在菜單欄“端口號“中選擇COM2(注意:波特率一定要是 9600,與程序映射的 COM1 保持一致),而后點擊打開串口。之后會如上圖顯示
在右下角文本輸入框中,輸入字符A,并點擊發(fā)送,可在輸出窗口中看到接收到的字符A
- img-vHPRBqza-1633059129
可以在KEIL中打開USART1串口觀察窗,查看由PC機發(fā)給STM32的數(shù)據(jù)
- mg-gV2ThQX4-163305912
-
具體查看端口信息:
- 菜單 Peripherals -- General Purpose I/O -- GPIO
- 在 GPIO ODR 右側(cè),可以看到 按實際電平,具體位的 勾選與否。
33059129337 -
邏輯分析儀查看端口的圖形顯示
- 點擊工具欄 的 啟動調(diào)試 -- 邏輯分析儀 -- Logic Analyzer -- Setup... -- 新加圖標(biāo)
- 輸入 PORTE.6 , 選擇 Bit ,就可以查看其電平變化的以及求得時差
- mg-GMzfZhV0-163305912
5、實物下載
當(dāng)你有實際的開發(fā)板和調(diào)試線(USB轉(zhuǎn)串口驅(qū)動)的時候,又怎么實現(xiàn)程序的下載和調(diào)試呢?
- 利用**st-link 調(diào)試線 **
- 安裝驅(qū)動程序后,連接設(shè)備與計算機,點擊菜單的 Project -- Options for Target
- 1) 在 Debug 選項頁
- (1)勾選右側(cè)的 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 圖標(biāo),即可下載

-
利用USB轉(zhuǎn)串口線
- 1) 下載安裝 CH340驅(qū)動(USB串口驅(qū)動)_XP_WIN7共用驅(qū)動程序
- 2) 使用USB線連接設(shè)備,下載啟動 FlyMcu 下載工具
- 3)點擊菜單上的 搜索串口,找到COMx:空閑的USB-SERIAL CH340 (自己的線)
- 4)點擊 按鈕 ... ,找到生成的 .hex 文件
- 5)點擊 開始編程, 直到完成即可。

- 以上就是利用KEIL5完成STM編程、調(diào)試和下載的所有有實物和無實物的編程調(diào)試辦法。需要注意的是我們在線仿真調(diào)試好的程序可能和程序下載到實際板子上進行運行的結(jié)果不同,原因在于仿真時候速度比較慢,而實際運行速度比較快,所以如有需要可以在適當(dāng)語句后面加入延時函數(shù)來解決速度匹配問題。









