(三)STM32F407 cubemx串口中断通讯

(三)STM32F407 cubemx串口中断通讯

    这篇文章主要是个人的学习经验,想分享出来供大家提供思路,如果其中有不足之处请批评指正哈。
废话不多说直接开始主题,本人是基于STM32F407VET6芯片,但是意在你看懂这篇文章后,不管是F1,F4,H7等一系列系统串口通讯配置都能明白如何通过参考手册去学习配置。而不是Ctrl c,Ctrl v。
    串口通讯其实cubemx已经把步骤精简的不能再精简了,但是秉持着不断学习的理念,有必要去大概学习一下串口协议,以及cubemx是如何根据参考手册封装的。

在这里插入图片描述以上是串口协议比较重要的几个点,跟着参考手册一个一个点的讲解,了解串口协议的重点。首先串口主要有3根线TX、RX、GND。根据以上的几个点知一个串口数据包主要由1.起始位 2.数据字 3.停止位构成其他的寄存器都是围绕它们为它们服务。


在这里插入图片描述接下来我将参照串口助手一步步讲解。
在这里插入图片描述


1)波特率

发送和接收由一共用的波特率发生器驱动,当发送器和接收器的使能位分别置位时,分别为其产生时钟。波特率从某种意义上来说,它就是时钟的一种表现形式,115200意思是1秒内产生115200个高低电平变化。一个8位字长的串口数据=8字长+1起始位+1停止位=10;也就是1秒钟能发送115200/10=11520个数据,也就是1/11520=86us平均86us发一个‘A’(数据)。

在这里插入图片描述



2)停止位

USART支持多种停止位的配置:0.5、1、1.5和2个停止位。
1.1个停止位:停止位位数的默认值。
2.2个停止位:可用于常规USART模式、单线模式以及调制解调器模式
3. 0.5个停止位:在智能卡模式下接收数据时使用。
4.1.5个停止位:在智能卡模式下发送和接收数据时使用。空闲帧包括了停止位
在这里插入图片描述停止位是通讯双方需要一致的,通常情况都是默认为1。在USART__CR2中编程停止位的位数。
若停止位需要更改长度

方一cubemx自动配置
在这里插入图片描述
方二代码hal库代码调用
在这里插入图片描述在这里插入图片描述hal库只提供了两种一个停止位,和两个停止位的定义


3)起始位中断信号产生
在USART中,如果辨认出一个特殊的采样序列,那么就认为侦测到一个起始位。该序列为:1110X0X0X0000
在这里插入图片描述起始位的开始标志是1110低电平下降沿,当下降沿触发时它会间隔采样,X就是它采样的信号是不确定的,但是若需要进入串口中断必须每一次采样的X(采样点为3,5,7,8,9,10)都为0才可确认收到起始位,这时设置RXNE标志位,如果RXNEIE=1,则产生中断。若其中采样X为1不管什么原因,返回空闲状态等待下降沿。

4)数据位
USART可以根据USART_CR1的M位接收8位或9位的数据字
8位数据的信号是和ASCLL码表一一对应的比如‘A’=65=0x41=01000001 8位传输的数据就是低高低低低低低高
在这里插入图片描述


5)奇偶校验
设置USART_CR1寄存器上的PCE位,可以使能奇偶控制(发送时生成一个奇偶位,接收时进行奇偶校验)。根据M位定义的帧长度,可能的USART帧格式列在下表中。
在这里插入图片描述偶校验:校验位使得一帧中的7或8个LSB数据以及校验位中’1’的个数为偶数。
例如:数据=00110101,有4个1’,如果选择偶校验(在USART_CR1中的PS=0),校验位将是’0’。4个1凑个偶数,校验位为0
奇校验:此校验位使得一帧中的7或8个LSB数据以及校验位中’1’的个数为奇数。
例如:数据=00110101,有4个’1’,如果选择奇校验(在USART_CR1中的PS=1),校验位将是’1’。4个1凑个奇数,还需一个1,校验位为1
这是参考手册的例子很清楚,但是可能是自己才疏学浅本人并不知道校验位有什么很大的作用,有大佬的话可以评论区指导一下。
在这里插入图片描述我们默认使用的是第一种00
在这里插入图片描述
0x0000u这样的常数一律默认为int型0,不是什么字节。

关键内容讲完了开始cubemx配置实现功能

基础时钟配置等请见: http://t.csdnimg.cn/e15bu

在这里插入图片描述中断勾上
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
在这里插入图片描述
keil5处点击魔术棒,给红框处打上勾。

在这里插入图片描述
dubug处我用的下载器是DAP可能和你们的不一样,勾上重新下载程序后复位功能。

  uint8_t AA='A';
  HAL_UART_Transmit(&huart1,&AA,1,10000);

HAL_UART_Transmit函数语法很简单,只要配置串口号,内容,内容长度,发送时间即可


主函数

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	uint8_t AA='A';
  HAL_UART_Transmit(&huart1,&AA,1,10000);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

考虑到重定义函数printf只有一个串口可用,本人给大家提供了printf转译其他串口也可使用printf


主函数部分

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
extern UART_HandleTypeDef huart1;
/* USER CODE END PTD */
/* 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 */
extern uint8_t uart1_rxbuf[10];
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* USER CODE BEGIN 1 */
  /* USER CODE END 1 */
  /* MCU Configuration--------------------------------------------------------*/
  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();
  /* USER CODE BEGIN Init */
  /* USER CODE END Init */
  /* Configure the system clock */
  SystemClock_Config();
  /* USER CODE BEGIN SysInit */
  /* USER CODE END SysInit */
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
	uint8_t AA='A';
  HAL_UART_Transmit(&huart1,&AA,1,10000);
	u1_printf("您的身高:%.0f cm\r\n",2);
  /* USER CODE END 2 */
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */
    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}
/**
  * @brief System Clock Configuration
  * @retval None
  */
void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
  /** Configure the main internal regulator output voltage
  */
  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 168;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
  /** Initializes the CPU, AHB and APB buses clocks
  */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
  * @brief  This function is executed in case of error occurrence.
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  __disable_irq();
  while (1)
  {
  }
  /* USER CODE END Error_Handler_Debug */
}
#ifdef  USE_FULL_ASSERT
/**
  * @brief  Reports the name of the source file and the source line number
  *         where the assert_param error has occurred.
  * @param  file: pointer to the source file name
  * @param  line: assert_param error line source number
  * @retval None
  */
void assert_failed(uint8_t *file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
     ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */


usart.h

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USART_H__
#define __USART_H__
#ifdef __cplusplus
extern "C" {
#endif
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* USER CODE BEGIN Includes */
#include <stdio.h>
#include "main.h"
#include "stdarg.h"		//包含需要的头文件 
#include "string.h"     //包含需要的头文件
/* USER CODE END Includes */
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN Private defines */
#define USART1_RX_ENABLE     0      //是否开启接收功能  1:开启  0:关闭
#define USART1_TXBUFF_SIZE   256    //定义串口1 发送缓冲区大小 256字节
#define USART1_RXBUFF_SIZE   256               //定义串口1 接收缓冲区大小 256字节
/* USER CODE END Private defines */
void MX_USART1_UART_Init(void);
/* USER CODE BEGIN Prototypes */
void u3_printf(char* fmt,...) ;
/* USER CODE END Prototypes */
#ifdef __cplusplus
}
#endif
#endif /* __USART_H__ */


usart.c

/* Includes ------------------------------------------------------------------*/
#include "usart.h"

/* USER CODE BEGIN 0 */

/* 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 = 115200;
  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 */

  /* 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_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART1;
    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 */
__align(8) char Usart1_TxBuff[USART1_TXBUFF_SIZE];  
void u1_printf(char* fmt,...) 
{  
	unsigned int i,length;
	va_list ap;
	va_start(ap,fmt);
	vsprintf(Usart1_TxBuff,fmt,ap);
	va_end(ap);	
	
	length=strlen((const char*)Usart1_TxBuff);		
	while((USART1->SR&0X40)==0);
	for(i = 0;i < length;i ++)
	{			
		USART1->DR = Usart1_TxBuff[i];
		while((USART1->SR&0X40)==0);	
	}	
}
/* USER CODE END 1 */


实验效果
在这里插入图片描述


串口发送中断主函数

	HAL_UART_Receive_IT(&huart1,&Data, sizeof(Data));//开启中断接收数据
	uint8_t IT_SEND[]="IT_SEND";
	HAL_UART_Transmit_IT(&huart1,IT_SEND, sizeof(IT_SEND)/sizeof(IT_SEND[0]));
	u1_printf("\r\n");

只需要在主函数处添加这一部分代码。


效果图
在这里插入图片描述串口中断接收本人采用的方式是结束位为0x5c串口接收结束,长度不可大于15个字节,实例代码如下

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	if(huart == &huart1)
	{
		  if(recv_end_flag_1==1)
			{
				 for(int i=0;i<sizeof(rx_buffer1)/sizeof(rx_buffer1[0]);i++)
				{
					 rx_buffer1[i]=0;
				}
				recv_end_flag_1 = 0;          //数据清空
			}
			rx_buffer1[Cnt]=Data;
			Cnt++;
			if(rx_buffer1[Cnt-1] == 0x5C)//判断是否为'\'结尾
			{
				//rx_buffer1[Cnt-1] = 0x0a; 
				u1_printf("\n接收到的数据为:\n");
				HAL_UART_Transmit(&huart1,rx_buffer1,Cnt,10000);//显示在串口助手
				Cnt = 0;
				recv_end_flag_1 = 1;          //数据接收完成
				for(int i=0;i<sizeof(rx_buffer1)/sizeof(rx_buffer1[0]);i++)
				{
					 rx_buffer1[i]=0;
				}
			}
		HAL_UART_Receive_IT(&huart1,&Data,1);//继续接收数据
	}
} 

在这里插入图片描述显示效果如图。

整体的所有代码以及数据手册链接:
链接: https://pan.baidu.com/s/1QYyTPbHfvcL2_uebvgt1GQ?pwd=a3gk
提取码:a3gk
希望这篇文章对你有所帮助。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RouDragon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值