stm32学习总结:4、Proteus8+STM32CubeMX+MDK仿真串口收发
文章目录
一、前言
前面我们已经实现点灯,下面就做一些数据交互的内容,先搞一下串口让数据可以用串口打印以及通过虚拟终端显示出来(就相当于有个显示屏可以显示数据了)。
二、资料收集
串口概念:https://zh.wikipedia.org/zh-cn/串行端口
https://blog.csdn.net/weixin_50303783/article/details/113813781
Proteus8配置串口元器件和虚拟终端:https://blog.csdn.net/acktomas/article/details/105175794
虚拟串口工具:VSPD(https://www.eltima.com/products/vspdxp/)或者HHD Software Virtual
Serial Port Tools(免费:https://freevirtualserialports.com/)
https://blog.csdn.net/weixin_39510813/article/details/135069554?spm=1001.2014.3001.5502
串口工具:SSCOM、XCOM、微软商店的串口调试工具等(推荐https://apps.microsoft.com/detail/9NBLGGH43HDM?rtc=1&hl=zh-cn&gl=CN)
三、STM32CubeMX配置串口
1、配置开启USART1
- 开启USART1,配置为异步模式,并设置全局中断,就可以看到右侧的引脚PA9、PA10为TX和RX(设置底板的时候也可以通过这里来确认引脚位置,避免搞反了):
设置属性为9600比特率、8个数据位、一个停止位,无奇偶校验:
2、设置usart中断优先级
- 设置优先级:
3、配置外设独立生成.c和.h
- 配置外设生成独立的.c和.h(否则我们后面就找不到usart.c和usart.h了,都在main.c中了,这种结构太差了,所以建议这里都勾选一下):
参数设置我们使用默认的即可,所以这里最简单的串口配置就完成了(时钟我们这里也暂时不配置使用HSE)。
可以直接右上角 GENERATE CODE生成代码,然后打开MDK工程了。
四、MDK串口收发代码(中断回调接收后发送接收到的内容)
MDK中主要改三个地方,usart.c、usart.h、stm32f1xx_it.c:
1、usart.h
usart.h我们在用户可添加的位置增加相关宏和全局变量(这里设置了接收中断的缓存大小为1,一个字节一个字节接收处理):
c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file usart.h
* @brief This file contains all the function prototypes for
* the usart.c file
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USART_H__
#define __USART_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN Private defines */
#define USART_REC_LEN 200
#define EN_USART1_RX 1
extern uint8_t USART_RX_BUF[USART_REC_LEN];
extern uint16_t USART_RX_STA;
#define RXBUFFERSIZE (1)
extern uint8_t aRxBuffer[RXBUFFERSIZE];
/* USER CODE END Private defines */
void MX_USART1_UART_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __USART_H__ */
2、usart.c
usart.c增加中断处理以及中断回调,在回调中处理接收内容(HAL_UART_RxCpltCallback回调中我这里是根据报文头和报尾确定一包数据后进行业务处理调用,这里暂时收到什么就发送什么):
c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file usart.c
* @brief This file provides code for the configuration
* of the USART instances.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "usart.h"
/* USER CODE BEGIN 0 */
#include <string.h>
/* USER CODE END 0 */
UART_HandleTypeDef huart1;
/* USART1 init function */
void MX_USART1_UART_Init(void)
{
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 9600;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
if (HAL_UART_Init(&huart1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
//弿启接收中断:标志位UART_IT_RXNE,并且设置接收缓冲以及接收缓冲接收最大数据量
HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, RXBUFFERSIZE);
/* USER CODE END USART1_Init 2 */
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
if(uartHandle->Instance==USART1)
{
/* USER CODE BEGIN USART1_MspInit 0 */
/* USER CODE END USART1_MspInit 0 */
/* USART1 clock enable */
__HAL_RCC_USART1_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
GPIO_InitStruct.Pin = GPIO_PIN_9;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_10;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USART1 interrupt Init */
HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_MspInit 1 */
/* USER CODE END USART1_MspInit 1 */
}
}
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{
if(uartHandle->Instance==USART1)
{
/* USER CODE BEGIN USART1_MspDeInit 0 */
/* USER CODE END USART1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_USART1_CLK_DISABLE();
/**USART1 GPIO Configuration
PA9 ------> USART1_TX
PA10 ------> USART1_RX
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);
/* USART1 interrupt Deinit */
HAL_NVIC_DisableIRQ(USART1_IRQn);
/* USER CODE BEGIN USART1_MspDeInit 1 */
/* USER CODE END USART1_MspDeInit 1 */
}
}
/* USER CODE BEGIN 1 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if(huart->Instance == USART1)
{
//printf("uart recv now:%02x ", aRxBuffer[0]);
//printf("\r\n");
HAL_UART_Transmit(&huart1, aRxBuffer, 1, 1000);
while(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_TC) != SET);
}
}
/* USER CODE END 1 */
3、stm32f1xx_it.c
stm32f1xx_it.c中根据全局中断的回调判断串口状态并调用接收中断:
c
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file stm32f1xx_it.c
* @brief Interrupt Service Routines.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "usart.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */
/* USER CODE END TD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t aRxBuffer[RXBUFFERSIZE];
/* USER CODE END 0 */
/* External variables --------------------------------------------------------*/
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN EV */
/* USER CODE END EV */
/******************************************************************************/
/* Cortex-M3 Processor Interruption and Exception Handlers */
/******************************************************************************/
/**
* @brief This function handles Non maskable interrupt.
*/
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
while (1)
{
}
/* USER CODE END NonMaskableInt_IRQn 1 */
}
/**
* @brief This function handles Hard fault interrupt.
*/
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
}
/**
* @brief This function handles Memory management fault.
*/
void MemManage_Handler(void)
{
/* USER CODE BEGIN MemoryManagement_IRQn 0 */
/* USER CODE END MemoryManagement_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
/* USER CODE END W1_MemoryManagement_IRQn 0 */
}
}
/**
* @brief This function handles Prefetch fault, memory access fault.
*/
void BusFault_Handler(void)
{
/* USER CODE BEGIN BusFault_IRQn 0 */
/* USER CODE END BusFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_BusFault_IRQn 0 */
/* USER CODE END W1_BusFault_IRQn 0 */
}
}
/**
* @brief This function handles Undefined instruction or illegal state.
*/
void UsageFault_Handler(void)
{
/* USER CODE BEGIN UsageFault_IRQn 0 */
/* USER CODE END UsageFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_UsageFault_IRQn 0 */
/* USER CODE END W1_UsageFault_IRQn 0 */
}
}
/**
* @brief This function handles System service call via SWI instruction.
*/
void SVC_Handler(void)
{
/* USER CODE BEGIN SVCall_IRQn 0 */
/* USER CODE END SVCall_IRQn 0 */
/* USER CODE BEGIN SVCall_IRQn 1 */
/* USER CODE END SVCall_IRQn 1 */
}
/**
* @brief This function handles Debug monitor.
*/
void DebugMon_Handler(void)
{
/* USER CODE BEGIN DebugMonitor_IRQn 0 */
/* USER CODE END DebugMonitor_IRQn 0 */
/* USER CODE BEGIN DebugMonitor_IRQn 1 */
/* USER CODE END DebugMonitor_IRQn 1 */
}
/**
* @brief This function handles Pendable request for system service.
*/
void PendSV_Handler(void)
{
/* USER CODE BEGIN PendSV_IRQn 0 */
/* USER CODE END PendSV_IRQn 0 */
/* USER CODE BEGIN PendSV_IRQn 1 */
/* USER CODE END PendSV_IRQn 1 */
}
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_stm32f1xx.s). */
/******************************************************************************/
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
uint32_t timeout = 0;
uint32_t maxDelay = 0x1FFFF;
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
timeout = 0;
while (HAL_UART_GetState(&huart1) != HAL_UART_STATE_READY)//等待就绪
{
timeout++; //超时处理
if(timeout > maxDelay) break;
}
timeout = 0;
memset(aRxBuffer, '\0', sizeof(aRxBuffer));
while(HAL_UART_Receive_IT(&huart1, (uint8_t *)aRxBuffer, RXBUFFERSIZE) != HAL_OK)//丿次处理完成之后,重新弿启中断并设置RxXferCount丿1
{
timeout++; //超时处理
if(timeout > maxDelay) break;
}
/* USER CODE END USART1_IRQn 1 */
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
之后mdk进行编译生成hex即可,这个之前已经章节已经说过了,不再赘述。
五、创建虚拟串口并通过Proteus配置及仿真串口通信
1、创建虚拟串口
参考我这里推荐的串口工具创建一组虚拟串口:https://blog.csdn.net/weixin_39510813/article/details/135069554?spm=1001.2014.3001.5502
或者使用其它虚拟串口工具也可以,只要保证后续有串口可用即可。
2、添加串口元器件及虚拟终端
P后搜索COMPIM后添加:
添加虚拟终端,在仪器栏选择VIRTUAL TERMINAL即虚拟终端后添加:
3、接线配置元器件并烧入hex
上节的LED灯我们先可以不动,然后P9、P10对应的口接TXD、RXD,相当于将STM32的TX和RX映射到电脑的虚拟串口上,然后虚拟终端的RX链接串口的RX即可,这样我们通过虚拟串口工具虚拟两个串口,一个串口分配给STM32来映射,一个用来通过串口工具在电脑上进行收发模拟和STM32的串口进行收发。
对于串口原件还需要选择一下串口,设置一下波特率、停止位这些:
虚拟终端也需要配置一下:
之后在STM32元器件上配置一下hex和时钟频率:
4、进行仿真测试
开始仿真并运行,然后通过串口工具定时发送即可:
5、可能的问题
- 1、虚拟终端的串口有时候无法弹出,根据这里顺利解决了(调试-》恢复弹出窗口):
https://blog.csdn.net/qq_36047832/article/details/90609487
- 2、此外,似乎STM32回显返回给串口的内容总是和发送给STM32的内容不一致,发送的时候是对的,但是返回的时候是乱码,最后发现接收中断的地方没有对接收空间进行memset导致了乱码(此外,还可能和时钟频率有关系,适当调高时钟频率):
六、最后
这样子基于串口通信的STM32应用开发基本就入门了,串口通信由于比较常见,所以这里先简要总结了一下,后续的内容就可以通过串口来打印日志了,STM32F103有两组串口,一组我们可以用来打印日志,一组用来和外部通信做交互。接下来我们接着去总结一下按键、蜂鸣器、定时器、看门狗等内容,再考虑继续往下总结I2C、ADC、SPI等接口来读取一些传感器信息以及读写flash等。