STM32CubeMX学习笔记3-串口通信

 对于串口通信在这里就不再多做介绍了,有需要的可以自行查询资料,本章主要是完成串口的收发实验

一、轮询方式

1 、STM32CubeMX设置

        RCC设置外接HSE,时钟设置为168M

        USART1选择为异步通讯方式,波特率设置为115200Bits/s,传输数据长度为8Bit,无奇偶校验,1位停止位,其他保持默认

  • 重载printf
    C语言中的标准库中所用的标准输入输出函数,默认的输出设备是显示器,要实现串口或LCD的输出,必须重新定义标准库函数里与输出函数相关的函数。例如:printf输出到串口,需要将fputc里面的输出指向串口(重定向),方法如下:只要自己添加一个int fputc(int ch, FILE *f)函数,能够输出字符就可以了。

在usart.c文件后面添加如下代码

/* USER CODE BEGIN 0 */
#include "stdio.h"
/* USER CODE END 0 */

/* USER CODE BEGIN 1 */
int fputc(int ch, FILE *f){
		//HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);
	 HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,1000);//发送串口
    return ch;
}
/* USER CODE END 1 */

我在 usart.c重定义了这个fputc函数,但用不了printf,一用printf就会导致单片机无法启动,不知道是哪里的原因。

以上问题是由于microlib没有开启导致的,microlib 进行了高度优化以使代码变得很小。它的功能比缺省 C 库少,并且根本不具备某些 ISO C 特性。 某些库函数的运行速度也比较慢,如果要使用printf(),必须开启。

2024.3.8更新

因此我使用了另外一种重定义方法:定义完后用法是跟printf一样的,但是需要多定义个数组。

这里是使用sprintf函数将发送的内容放进tx_buf数组,而且sprintf会返回发送内容的长度,因此这里的重定义可以实现。

#include "stdio.h"
unsigned char tx_buf[256];
#define printf1(...) HAL_UART_Transmit(&huart1,tx_buf,sprintf((char*)tx_buf,__VA_ARGS__),0xffff);

main函数如下,使用printf注意包含stdio.h头文件

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 */
 HAL_UART_Transmit(&huart1,"HAL_UART_Transmit Test...",25,0xffff);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_13,GPIO_PIN_RESET); //LED0亮
		HAL_Delay(500);                                     //延时500ms
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_13,GPIO_PIN_SET);   //LED0灭
    HAL_Delay(500);                                     //延时500ms
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_14,GPIO_PIN_RESET); //LED1亮
		HAL_Delay(500);                                     //延时500ms
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_14,GPIO_PIN_SET);   //LED1灭
    HAL_Delay(500);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		
			
			printf("Hello STM32 %d\r\n",123);
			HAL_Delay(500);		
  }
  /* USER CODE END 3 */
}


将程序下载验证:

轮询收发:

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 */
unsigned char data[16]= {0};
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_13,GPIO_PIN_RESET); //LED0亮
		HAL_Delay(500);                                     //延时500ms
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_13,GPIO_PIN_SET);   //LED0灭
    HAL_Delay(500);                                     //延时500ms
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_14,GPIO_PIN_RESET); //LED1亮
		HAL_Delay(500);                                     //延时500ms
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_14,GPIO_PIN_SET);   //LED1灭
    HAL_Delay(500);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		// HAL_UART_Transmit(&huart1,"HAL_UART_Transmit Test...",25,0xffff);
		
		HAL_UART_Receive(&huart1, data, 16, 0xffff);//接收16个字符

		HAL_UART_Transmit(&huart1, data, 16, 0xffff);//发送16个字符
//			printf("\r\n printf test...%d\r\n",25);
			//printf("Hello STM32\r\n");
			HAL_Delay(500);		
  }
  /* USER CODE END 3 */
}

HAL_UART_Transmit(,,,)//传输函数,将数据发送出去

第一个函参,目串口结构体(可以到usart.c中查看,记得在前面加取址符&)

第二个函参,发送的数组地址

第三个函参,要发送的字符个数

第四个函参,阻塞时间,超时跳出函数

相关的函数可以在stm32f4xx_uart.c文件中查找。

串口发送指定长度的数据。如果超时没发送完成,则不再发送,返回超时标志(HAL_TIMEOUT)。

下载验证:

轮询方式:CPU不断查询IO设备,如设备有请求则加以处理。例如CPU不断查询串口是否传输完成,如传输超过则返回超时错误。轮询方式会占用CPU处理时间,效率较低。

二、中断方式

采用中断的方式进行数据传输,需要打开串口中断。

把串口中断的优先级设置为1,防止跟其他中断干扰。 

 打开中断后生成代码,在usart.c文件中会自动生成中断初始化代码

/*****usart.c文件中的UART初始化函数以及IO口配置函数*****/
void MX_USART1_UART_Init(void){
    //....该函数与轮询方式的UART初始化函数相同....
}

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 */
  }
}

 在usart.c中自定义该回调函数 __weak void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart),并编写中断函数代码,HAL库的中断进行完之后,并不会直接退出,而是会进入中断回调函数中,用户可以在其中设置代码, 串口中断接收完成之后,会进入该函数,该函数为空函数,用户需自行修改,因为中断接收函数只能触发一次接收中断,所以我们需要在中断回调函数中再调用一次中断接收函数

extern uint8_t RxMsg[20];
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
  if(huart->Instance == USART1){
    HAL_UART_Transmit(&huart1,RxMsg,10,0xffff); //将接收的数据通过串口1发送回去
    HAL_UART_Receive_IT(&huart1,RxMsg,10);      //再次开启接收中断
  }
}

 HAL_UART_Receive_IT(&huart1,RxMsg,10);//因为串口中断接收,以中断方式接收指定长度数据。大致过程是,设置数据存放位置,接收数据长度,然后使能串口接收中断。接收到数据时,会触发串口中断。再然后,串口中断函数处理,直到接收到指定长度数据,而后关闭中断,进入中断接收回调函数,不再触发接收中断,(只触发一次中断),因此需要在回调函数中再次启动接收中断。

还有其他的一些中断相关函数:

HAL_UART_IRQHandler(UART_HandleTypeDef *huart);  //串口中断处理函数
HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);  //串口发送中断回调函数
HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef *huart);  //串口发送一半中断回调函数(用的较少)
HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);  //串口接收中断回调函数
HAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);//串口接收一半回调函数(用的较少)
HAL_UART_ErrorCallback();串口接收错误函数

串口中断处理函数
HAL_UART_IRQHandler(UART_HandleTypeDef *huart);  
功能:对接收到的数据进行判断和处理  判断是发送中断还是接收中断,然后进行数据的发送和接收,在中断服务函数中使用
如果接收数据,则会进行接收中断处理函数
如果发送数据,则会进行发送中断处理函数

串口查询函数
HAL_UART_GetState();  判断UART的接收是否结束,或者发送数据是否忙碌
举例:     
while(HAL_UART_GetState(&huart4) == HAL_UART_STATE_BUSY_TX)   //检测UART发送结束

在main.c中定义数据并启动接收中断,在main函数中需要先启动接收中断。 

/*****main.c文件中编写相关代码*****/
/* USER CODE BEGIN PV */
uint8_t RxMsg[20];
/* USER CODE END PV */
/* USER CODE BEGIN 2 */
HAL_UART_Receive_IT(&huart1,RxMsg,10);//必须启动串口接收中断,要不然中断会没反应
/* USER CODE END 2 */
while (1){
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_13,GPIO_PIN_RESET); //LED0亮
		HAL_Delay(500);                                     //延时500ms
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_13,GPIO_PIN_SET);   //LED0灭
    HAL_Delay(500);                                     //延时500ms
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_14,GPIO_PIN_RESET); //LED1亮
		HAL_Delay(500);                                     //延时500ms
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_14,GPIO_PIN_SET);   //LED1灭
    HAL_Delay(500);
  /* USER CODE END WHILE */
}

 下载验证:当单片机接收到10个字符时触发中断,然后进入回调函数,在回调函数中将数据发送出去,并启动下一次中断。

值得注意的是当你发送的数据超过10个字符时,会只读取前10个字符,剩下的会在下一次中断中接收,(有时候可能会导致中断卡死)需要重启复位才能恢复原来的正常数据

 三、DMA模式

如果要打开DMA,必须先打开中断

这里DMA模式选择普通模式,其他默认即可

把DMA的中断优先级也改成1.

将DMA和中断也进行重定义:

unsigned char tx_buf[256];
#define printf1(...) HAL_UART_Transmit(&huart1,tx_buf,sprintf((char*)tx_buf,__VA_ARGS__),0xffff);//普通模式

#define DMA_printf1(...) HAL_UART_Transmit_DMA(&huart1,tx_buf,sprintf((char*)tx_buf,__VA_ARGS__));//DMA模式

#define IT_printf1(...) HAL_UART_Transmit_IT(&huart1,tx_buf,sprintf((char*)tx_buf,__VA_ARGS__));//中断模式

DMA模式的使用方法与中断的使用方法一致,在main函数添加以下程序,测试DMA的发送功能:

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_DMA_Init();
  MX_USART1_UART_Init();
  /* USER CODE BEGIN 2 */
unsigned char data[16]= {0};
HAL_UART_Transmit(&huart1,"HAL_UART_Transmit Test...",strlen("HAL_UART_Transmit Test..."),0xffff);
HAL_UART_Transmit_IT(&huart1,TxMsg,sizeof(TxMsg));
HAL_UART_Receive_IT(&huart1,RxMsg,10);
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		HAL_GPIO_WritePin(GPIOD,GPIO_PIN_13,GPIO_PIN_RESET); //LED0亮
		HAL_Delay(500);                                     //延时500ms
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_13,GPIO_PIN_SET);   //LED0灭
    HAL_Delay(500);                                     //延时500ms
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_14,GPIO_PIN_RESET); //LED1亮
		HAL_Delay(500);                                     //延时500ms
    HAL_GPIO_WritePin(GPIOD,GPIO_PIN_14,GPIO_PIN_SET);   //LED1灭
    HAL_Delay(500);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		// HAL_UART_Transmit(&huart1,"HAL_UART_Transmit Test...",25,0xffff);
		
//		HAL_UART_Receive(&huart1, data, 16, 0xffff);

//		HAL_UART_Transmit(&huart1, data, 16, 0xffff);
//			printf("\r\n printf test...%d\r\n",25);
			//printf("Hello STM32\r\n");
		printf1("Hello STM32 %d\r\n",123);
		HAL_Delay(500);
		DMA_printf1("DMA_printf1 %d\r\n",234);
		HAL_Delay(500);
		IT_printf1("IT_printf1 %d\r\n",456);
//			HAL_Delay(500);		
  }
  /* USER CODE END 3 */
}

下载验证:

以上介绍了三种串口通信的三种模式,三种模式的处理速度也是不一样的。

首先是轮询模式,由于存在阻塞时间,所以处理速度最慢,但数据是完整的。

其次是中断模式,由于中断是利用自身的资源来进行的,而DMA不占用自身资源,所以中断会比DMA慢上一点。

最快是DMA模式,DMA模式是非阻塞状态,且不占据自身资源,处理速度很快,但可能存在数据丢失的情况。 

HAL_UART_Transmit();//串口发送数据,使用超时管理机制
HAL_UART_Receive();//串口接收数据,使用超时管理机制
HAL_UART_Transmit_IT();//串口中断模式发送
HAL_UART_Receive_IT();//串口中断模式接收
HAL_UART_Transmit_DMA();//串口DMA模式发送
HAL_UART_Transmit_DMA();//串口DMA模式接收

以上讲解的都是定长接收数据的方法,下一章将讲解不定长数据的收发。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值