【STM32+cubemx】0004 HAL库开发:uart串口的轮询和中断使用

本节我们来学习异步串口uart的应用,使用轮询和中断两种方式,来实现计算机向单片机发送数据,单片机处理之后再将数据返回。

1)cubemx生成代码

首先还是在cubemx中生成代码,选择器件、设置SYS(调试接口)、设置RCC(外部晶振时钟源)这几个步骤和前一节一样(也可以复制前面闪灯的工程,在上面修改)。

然后设置串口引脚,这里我们选择uart1,异步串口,选完后,已经使用的串口引脚PA9和PA10会变成绿色;然后选择开启串口全局中断:

之后,和上一节一样,在时钟选项卡设置主时钟为72M;在project Manager选项卡设置工程名和路径,生成工程代码。

2)uart的轮询模式

在keil中打开生成的工程,可以看到软件生成的代码中,main函数中已经有uart硬件初始化的代码。

如果我们使用串口的轮询模式,那么已经可以直接使用hal函数了:

HAL_UART_Transmit()和HAL_UART_Receive()。

如下图,是实现等待计算机发送16个字节数据,收到后,每个数据加1后再返回给计算机:

轮询模式使用时,函数的最后一个参数是超时数值,图中设置的是0xffff,也就是说函数的执行超时是65535ms。

在接收函数中,如果65535ms还未接收完16个字节的数据,则会向后执行,如果接收完则立即向后执行。发送数据的函数也一样。

轮询模式使用起来最简单,但是会占用大量的cpu时间,在等待接收和等待发送完毕时,cpu不能去做别的运算,只能在这里空等,运行的效率很低。

3)uart的中断模式

中断模式可以提高cpu的使用效率,在等待数据接收和发送时,cpu可以取做别的运算,只需在发送/接收完的一点时间进入中断做一些处理。

由于我们在cubemx中配置串口时,已经设置了串口的全局中断,所以这里可以很方便地使用下面这两个函数:

HAL_UART_Transmit_IT();发送函数好理解一些,就是将要发送的数据和长度填入,然后程序可以执行后面的任务,系统会自动使用中断模式将数据发完(之后只在每发完一个字节时进一次中断,占用很少的时间)。

HAL_UART_Receive_IT();接收函数,将要接收的数据和长度填入,然后程序可以执行后面的任务,系统会自动使用中断模式接收数据,当接收完指定长度的数据后,会产生一个中断;在实际使用时,要在中断回调函数中设置一个标志位,循环去检测标志位,才能知道它什么时候已收完。

我们以一个例子来看一下它们的使用方法:

先定义变量:

再重写接收函数的回调函数,在回调函数中设置标志位:

主循环中,先设置中断接收16个字节数据,然后等待,当接收满16个字节后,回调函数置标志位,主循环中检测到后,可以作进一步处理:

4)HAL库中断发送和接收的缺点

先讲讲HAL_UART_Transmit_IT()发送的这个函数。要注意这个函数使用时,如果前一次要发送的数据还未发完,又进行了一次新的发送,那么它会继续发送前一次的数据,本次发的数据不会执行。如果需要确保两次都发送出去,可以这样使用:

while(HAL_BUSY == HAL_UART_Transmit_IT(&huart1, str, 12));    

while(HAL_BUSY == HAL_UART_Transmit_IT(&huart1, str2, 4));

这里,如果前一次未发完,会一直等待到发完,再发送本次的数据,和轮询时的等待差不多。

HAL_UART_Receive_IT()接收的这个函数,最大的不方便就是你得事先设定接收多少数据,这个是比较违反直觉的,因为你并不会知道外部什么时候发数据过来,也不知道外部一次会发多少数据。这会使得处理接收数据产生一定的困难。

当然,你可以设置每次接收一个字节数据就产生一次中断,再对收到的数做处理,这是可以的,但是,这样的做的效率很低,尤其HAL库每进一次中断要判断很多情况,会占用大量的cpu时间;以前测试过用921600波特率,中断占用的cpu时间大约有1/3之多!另外,这个方法在同时有收和发时,可能会进入溢出错误的死锁;可能是由于同时出现了溢出中断和收数中断时,处理不完全的问题;有网友遇到过,没有深究。

一般来讲,串口使用fifo环形缓冲队列是很好的解决方案,下一节将详细讲讲,如何改写HAL库的中断函数,实现高效的串口收发。

欢迎关注我的公众号,文章同步更新,可留言获取相关资料和软件:

  • 8
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是使用HAL库STM32代码示例,用于与OpenMV进行串口通信。 ```c #include "stm32f1xx_hal.h" UART_HandleTypeDef huart2; // 串口句柄 void SystemClock_Config(void); // 系统时钟初始化函数 static void MX_GPIO_Init(void); // GPIO初始化函数 static void MX_USART2_UART_Init(void); // 串口初始化函数 int main(void) { HAL_Init(); // 初始化HAL库 SystemClock_Config(); // 初始化系统时钟 MX_GPIO_Init(); // 初始化GPIO MX_USART2_UART_Init(); // 初始化USART2 uint8_t data[10]; // 接收缓存 while (1) { HAL_UART_Receive(&huart2, data, 10, HAL_MAX_DELAY); // 接收数据 // 处理接收到的数据 ... HAL_UART_Transmit(&huart2, data, 10, HAL_MAX_DELAY); // 发送数据 } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct; RCC_ClkInitTypeDef RCC_ClkInitStruct; __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } 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_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000); HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0); } static void MX_USART2_UART_Init(void) { huart2.Instance = USART2; huart2.Init.BaudRate = 115200; huart2.Init.WordLength = UART_WORDLENGTH_8B; huart2.Init.StopBits = UART_STOPBITS_1; huart2.Init.Parity = UART_PARITY_NONE; huart2.Init.Mode = UART_MODE_TX_RX; huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE; huart2.Init.OverSampling = UART_OVERSAMPLING_16; if (HAL_UART_Init(&huart2) != HAL_OK) { Error_Handler(); } } static void MX_GPIO_Init(void) { __HAL_RCC_GPIOC_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_13; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); } ``` 在上述代码中,我们使用了USART2进行串口通信,波特率为115200,数据位为8位,停止位为1位,无校验位。在`main()`函数中,我们轮询接收串口数据,在接收到数据后处理并发送回去。请注意,此处使用HAL库的阻塞模式(即使用`HAL_MAX_DELAY`作为超时参数),这可能会导致程序挂起,应根据实际情况修改超时参数。 在使用HAL库进行串口通信时,需要在CubeMX中配置USART的相关参数,并在代码中调用`MX_USARTx_UART_Init()`函数进行初始化。请注意,不同型号的STM32芯片可能具有不同的USART外设,因此在使用之前应先查阅相关芯片的数据手册。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值