(三)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/XQ0L6
中断勾上
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 ConfigurationPA9 ------> USART1_TXPA10 ------> 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 ConfigurationPA9 ------> USART1_TXPA10 ------> 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/1xmEUNd82dunSmh_NyI09fg?pwd=6dqr
提取码:6dqr
希望这篇文章对你有所帮助。