完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
前言
串口通信可谓是所有单片机都具备的一种最基础的通信方式了,那么在本节中,我先将对单片机的通信原理进行初步讲解,再示范如何在STM32CubeMX中进行串口通信的配置。 通信接口 从通信接口上分,通信方式可分为并行通信和串行通信两种 并行通信
很多人开始学习的时候都分不清楚USART和UART有什么区别,这里我稍微解释一下 它们两个是同步和异步的区别。
工程配置 这次做一个返回接收到数据的实验,非常经典的串口通信时钟配置,老样子,不多说 外设选择USART1,配置成异步通信模式 通信配置如下
进入代码 按照我的习惯,生成代码后都会看一下外设的初始化部分,如果说是检查一下是否出错的话倒不至于,一方面是详细看看STM32CubeMX已经帮我们完成了哪些步骤,另一方面是看看有哪些是我们一会儿可以用得上的。我觉得这其实是一个好习惯。 比如在这个工程的初始化代码中,huart1是已经初始化的串口句柄,而USART1是串口1地址,这些是需要用到的。 UART_HandleTypeDef huart1; /* USART1 init function */ void MX_USART1_UART_Init(void) { 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(); } } 另外,我们看到stm32f4xx_it.c 这个文件,这个文件里包含了所有中断的处理过程,在《STM32CubeMX实战教程(三)——外部中断(中断及HAL_Delay函数避坑)》中我已经教过大家怎样去寻找中断回调函数,这里再次做一个示范。 首先,找到中断服务函数,可以看到里面调用了一个HAL_UART_IRQHandler /** * @brief This function handles USART1 global interrupt. */ void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ /* USER CODE END USART1_IRQn 0 */ HAL_UART_IRQHandler(&huart1); /* USER CODE BEGIN USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */ } 追踪到函数原型,可以看到这个函数还是比较长的 void HAL_UART_IRQHandler(UART_HandleTypeDef *huart) { uint32_t isrflags = READ_REG(huart->Instance->SR); uint32_t cr1its = READ_REG(huart->Instance->CR1); uint32_t cr3its = READ_REG(huart->Instance->CR3); uint32_t errorflags = 0x00U; uint32_t dmarequest = 0x00U; /* If no error occurs */ errorflags = (isrflags & (uint32_t)(USART_SR_PE | USART_SR_FE | USART_SR_ORE | USART_SR_NE)); if (errorflags == RESET) { /* UART in mode Receiver -------------------------------------------------*/ if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET)) { UART_Receive_IT(huart); return; } } /* If some errors occur */ if ((errorflags != RESET) && (((cr3its & USART_CR3_EIE) != RESET) || ((cr1its & (USART_CR1_RXNEIE | USART_CR1_PEIE)) != RESET))) { /* UART parity error interrupt occurred ----------------------------------*/ if (((isrflags & USART_SR_PE) != RESET) && ((cr1its & USART_CR1_PEIE) != RESET)) { huart->ErrorCode |= HAL_UART_ERROR_PE; } /* UART noise error interrupt occurred -----------------------------------*/ if (((isrflags & USART_SR_NE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET)) { huart->ErrorCode |= HAL_UART_ERROR_NE; } /* UART frame error interrupt occurred -----------------------------------*/ if (((isrflags & USART_SR_FE) != RESET) && ((cr3its & USART_CR3_EIE) != RESET)) { huart->ErrorCode |= HAL_UART_ERROR_FE; } /* UART Over-Run interrupt occurred --------------------------------------*/ if (((isrflags & USART_SR_ORE) != RESET) && (((cr1its & USART_CR1_RXNEIE) != RESET) || ((cr3its & USART_CR3_EIE) != RESET))) { huart->ErrorCode |= HAL_UART_ERROR_ORE; } /* Call UART Error Call back function if need be --------------------------*/ if (huart->ErrorCode != HAL_UART_ERROR_NONE) { /* UART in mode Receiver -----------------------------------------------*/ if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET)) { UART_Receive_IT(huart); } /* If Overrun error occurs, or if any error occurs in DMA mode reception, consider error as blocking */ dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR); if (((huart->ErrorCode & HAL_UART_ERROR_ORE) != RESET) || dmarequest) { /* Blocking error : transfer is aborted Set the UART state ready to be able to start again the process, Disable Rx Interrupts, and disable Rx DMA request, if ongoing */ UART_EndRxTransfer(huart); /* Disable the UART DMA Rx request if enabled */ if (HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR)) { CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR); /* Abort the UART DMA Rx stream */ if (huart->hdmarx != NULL) { /* Set the UART DMA Abort callback : will lead to call HAL_UART_ErrorCallback() at end of DMA abort procedure */ huart->hdmarx->XferAbortCallback = UART_DMAAbortOnError; if (HAL_DMA_Abort_IT(huart->hdmarx) != HAL_OK) { /* Call Directly XferAbortCallback function in case of error */ huart->hdmarx->XferAbortCallback(huart->hdmarx); } } else { /* Call user error callback */ #if (USE_HAL_UART_REGISTER_CALLBACKS == 1) /*Call registered error callback*/ huart->ErrorCallback(huart); #else /*Call legacy weak error callback*/ HAL_UART_ErrorCallback(huart); #endif /* USE_HAL_UART_REGISTER_CALLBACKS */ } } else { /* Call user error callback */ #if (USE_HAL_UART_REGISTER_CALLBACKS == 1) /*Call registered error callback*/ huart->ErrorCallback(huart); #else /*Call legacy weak error callback*/ HAL_UART_ErrorCallback(huart); #endif /* USE_HAL_UART_REGISTER_CALLBACKS */ } } else { /* Non Blocking error : transfer could go on. Error is notified to user through user error callback */ #if (USE_HAL_UART_REGISTER_CALLBACKS == 1) /*Call registered error callback*/ huart->ErrorCallback(huart); #else /*Call legacy weak error callback*/ HAL_UART_ErrorCallback(huart); #endif /* USE_HAL_UART_REGISTER_CALLBACKS */ huart->ErrorCode = HAL_UART_ERROR_NONE; } } return; } /* End if some error occurs */ /* UART in mode Transmitter ------------------------------------------------*/ if (((isrflags & USART_SR_TXE) != RESET) && ((cr1its & USART_CR1_TXEIE) != RESET)) { UART_Transmit_IT(huart); return; } /* UART in mode Transmitter end --------------------------------------------*/ if (((isrflags & USART_SR_TC) != RESET) && ((cr1its & USART_CR1_TCIE) != RESET)) { UART_EndTransmit_IT(huart); return; } } 根据以往的经验,直接去寻找带Callback的就是回调函数,但是这里面只出现的ErrorCallback,这是报错回调,当系统出错后才会进入的回调,可用于调试,显然不是我们需要的,其实,我们直接进UART_Receive_IT就可以发现里面有一个HAL_UART_RxCpltCallback,这个就是我们要找的回调函数了。接下来我们还是回到stm32f4xx_it.c 并在最下面加入代码 Buffer是在main.c中定义的uint8_t类型全局变量 每接受的一个字节后产生中断,将该字节数据返回并重新开启中断 函数不懂如何使用的话可以参考HAL库函数开发手册 这里的数字1可以改成别的数字以一次性接受更多字节,但Buffer需要更改成更大的数组 /* USER CODE BEGIN 1 */ void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(huart->Instance==USART1) { HAL_UART_Transmit(&huart1, &Buffer, 1, 0xff); HAL_UART_Receive_IT(&huart1,&Buffer,1); } } /* USER CODE END 1 */ 另外在main.c中,while循环前,串口初始化后,添加接收中断开启函数,这样在第一次接收到数据的时候才会触发中断,毕竟这些工作STM32CubeMX是没有帮我们完成的 HAL_UART_Receive_IT(&huart1,&Buffer,1); printf重定向 玩过STM32的人应该都很清楚printf重定向的意思,不知道也没关系,因为其实非常简单。说白了就是把printf函数用在STM32中,而printf的输出终端是串口。那么,怎么做到这一点呢,那就需要将printf函数重定向一下。稍微解释一下呢,就是printf函数底层是调用了fputc函数,而这个函数是一个弱化函数,在之前《STM32CubeMX实战教程(三)——外部中断(中断及HAL_Delay函数避坑)》中我也介绍过弱化函数的意思,所以我们只需要在usart.c中重写这个函数并使其作用于串口即可,具体如下 /* USER CODE BEGIN 1 */ int fputc(int ch,FILE *f) { HAL_UART_Transmit(&huart1,(uint8_t*)&ch,1,100); return ch; } /* USER CODE END 1 */ 这时候我们只需要包含stdio.h即可调用printf函数 下载验证 根据上面的工程,下载验证的话会出现一个问题,就是英文正常,如果输入中文则会返回乱码,解决方法是添加一个标志位,标记一组数据的末尾,并将数据接收完毕后一次性发送,而不是每接收到一帧数据后立刻发送。这点已在上传的工程中进行修改。工程文件已经上传,在《基于STM32CubeMX串口通信实验》。 非常抱歉由于CSDN官网上传的资源必须要设定积分,否则几乎无法通过审核,这里就没有办法免费开放给大家,不过源码在教程里已经非常详细了。 结语 非常感谢大家的阅读,如有不当或者错误的地方,欢迎指正,谢谢支持。 一个字一个字敲出来不容易,如果觉得有帮助,点个赞再走呗~ 祝大家事业蒸蒸日上! 奥里给~ |
|
|
|
只有小组成员才能发言,加入小组>>
3278 浏览 9 评论
2955 浏览 16 评论
3455 浏览 1 评论
8987 浏览 16 评论
4050 浏览 18 评论
1102浏览 3评论
570浏览 2评论
const uint16_t Tab[10]={0}; const uint16_t *p; p = Tab;//报错是怎么回事?
568浏览 2评论
用NUC131单片机UART3作为打印口,但printf没有输出东西是什么原因?
2301浏览 2评论
NUC980DK61YC启动随机性出现Err-DDR是为什么?
1856浏览 2评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-21 19:54 , Processed in 1.122551 second(s), Total 79, Slave 59 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号