STM32H743IIT6串口学习笔记
串口作为最常用的调试工具之一,普及度应该是很高的,H743系列串口使用方法与中低端芯片一样,如果以前学习过标准库的串口,这篇文章理解起来应该没有多大难度。串口通信协议的介绍网上一搜一大堆,这里就不再赘述了,主要写一下应用过程,标准库和HAL库都有专门的串口收发函数,常见的发送函数就是改写fputc()函数,将printf()函数重映射到串口上,至于接收,我了解到的就有四种,第一种使用定时器超时接收
一、前言
串口作为最常用的调试工具之一,普及度应该是很高的,H743系列串口使用方法与中低端芯片一样,如果以前学习过标准库的串口,这篇文章理解起来应该没有多大难度。
串口通信协议的介绍网上一搜一大堆,这里就不再赘述了,主要写一下应用过程,标准库和HAL库都有专门的串口收发函数,常见的发送函数就是改写fputc()函数,将printf()函数重映射到串口上,至于接收,我了解到的就有四种,第一种使用定时器超时接收,原理呢就是设置一个定时器,每当串口收到数据就刷新定时,当串口没有新数据到达后,定时器超时接收结束;第二种是状态机,江协科技在讲串口通信的时候用过这种方式,我个人感觉不太好用;第三种跟第四种分别是使用中断和DMA。本次记录一下在cubeMX生成的工程下移植正点原子中断串口的过程。
二、cubeMX工程配置
按照惯例一般都是使用串口1的PA9、PA10引脚作为调试,但也有例外,所以使用之前我们需要打开原理图确认一下,如图,CH340接到了串口1,惯例设置。


接下来我们转到cube,配置一下所用串口,工程的基础设置不再赘述,都是通用流程。 串口参数使用的是默认配置。
然后按流程打开生成的代码即可。
三、keil代码编写
打开工程,并将上次点灯新建的hardware文件夹移植进来,流程不再赘述。
这里必须声明一下,由于cube生成的工程中已经有usart.c/h文件,所以我们新写的这俩文件不能叫这个名字,要不然编译时找不到我们写的文件。
我先把代码贴在这,然后再来分析过程。
头文件:
#ifndef __MYUSART_H__
#define __MYUSART_H__
#include "main.h"
#include "usart.h"
#define USART_REC_LEN 200 /* 定义最大接收字节数 200 */
#define RXBUFFERSIZE 1 /* 缓存大小 */
extern uint8_t g_usart_rx_buf[USART_REC_LEN]; /* 接收缓冲,最大USART_REC_LEN个字节.末字节为换行符 */
extern uint16_t g_usart_rx_sta; /* 接收状态标记 */
extern uint8_t g_rx_buffer[RXBUFFERSIZE]; /* HAL库USART接收Buffer */
void USART1_RX_Enble(void);
int USART1_GET_State(void);
int USART1_DATA_Len(void);
void USART1_GET_Data(uint8_t *buffer, uint16_t size);
#endif
源文件:
#include "MyUSART.h"
#include "stdio.h"
#include "string.h"
/* 接收缓冲, 最大USART_REC_LEN个字节. */
uint8_t g_usart_rx_buf[USART_REC_LEN];
/* 接收状态
* bit15, 接收完成标志
* bit14, 接收到0x0d
* bit13~0, 接收到的有效字节数目
*/
uint16_t g_usart_rx_sta = 0;
uint8_t g_rx_buffer[RXBUFFERSIZE]; /* HAL库使用的串口接收缓冲 */
//UART_HandleTypeDef g_uart1_handle; /* UART句柄 */
/*重定向printf输出到串口1*/
int fputc(int ch, FILE *f)
{
HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);
return ch;
}
/**
* @brief Rx传输回调函数
* @param huart: UART句柄类型指针
* @retval 无
*/
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
if (huart->Instance == USART1) /* 如果是串口1 */
{
if ((g_usart_rx_sta & 0x8000) == 0) /* 接收未完成 */
{
if (g_usart_rx_sta & 0x4000) /* 接收到了0x0d */
{
if (g_rx_buffer[0] != 0x0a)
{
g_usart_rx_sta = 0; /* 接收错误,重新开始 */
}
else
{
g_usart_rx_sta |= 0x8000; /* 接收完成了 */
}
}
else /* 还没收到0X0D */
{
if(g_rx_buffer[0] == 0x0d)
{
g_usart_rx_sta |= 0x4000;
}
else
{
g_usart_rx_buf[g_usart_rx_sta & 0X3FFF] = g_rx_buffer[0] ;
g_usart_rx_sta++;
if(g_usart_rx_sta > (USART_REC_LEN - 1))
{
g_usart_rx_sta = 0; /* 接收数据错误,重新开始接收 */
}
}
}
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)g_rx_buffer, RXBUFFERSIZE);
}
}
/**
* @brief 串口1中断服务函数
* @param 无
* @retval 无
*/
void USART_UX_IRQHandler(void)
{
HAL_UART_IRQHandler(&huart1); /* 调用HAL库中断处理公用函数 */
}
/*开启串口1接收功能*/
void USART1_RX_Enble(void)
{
HAL_UART_Receive_IT(&huart1,(uint8_t *)g_rx_buffer, RXBUFFERSIZE);
}
/*串口1收到数据?0否1是*/
int USART1_GET_State(void)
{
int state = 0;
if (g_usart_rx_sta & 0x8000)
state = 1;
return state;
}
/*串口1收到数据的长度*/
int USART1_DATA_Len(void)
{
int len = 0;
len = g_usart_rx_sta & 0x3fff;
return len;
}
/*转存串口1的数据,并清空串口1的接收buffer*/
void USART1_GET_Data(uint8_t *buffer, uint16_t size)
{
memset(buffer, '\0',size);
strcpy((char *)buffer, (char *)g_usart_rx_buf); //转存数据
memset(g_usart_rx_buf,'\0',USART_REC_LEN); //清空接收缓存
g_usart_rx_sta = 0; //清空接收标志位
}
这段代码接收部分移植自正点原子的串口实验,由于我使用的是cube生成的工程,引脚初始化这部分cube已经帮我们做了,所以不需要关心。前面说过HAL有专门的串口函数,所以无论怎么改,都还是基于HAL库的函数,顶多就是二次甚至是三次封装罢了。
(1)重映射printf()函数
这部分的原理已经有很多人讲过,就是重写fputc()函数,将其映射到串口1上,上面的代码里有,我就不啰嗦了,底层还是用的HAL库提供的串口函数。
(2)中断接收
我们在cube中使能了串口1的全局中断,但在工程当中改中断默认是关闭状态(具体什么原因我不太清楚,知道到小伙伴可以私信我一下让我长长见识),所以使用之前需要手动开启一下,要不然接受函数没反应。
正点原子在串口的头文件中定义了几个变量,分别是接收数组及最大长度、接收状态以及缓存大小等,我们直接拿过来用一下,在源文件中主要就是两个函数,一个是串口中断服务函数,另一个是串口接收回调函数,上面的源文件中有完整贴出,有兴趣的小伙伴可以自行研究。
剩下的就是我二次封装的一些函数了,主要是为了方便使用和移植,主要功能有使能串口1接收、获取接收状态、获取接收长度和数据转存,在正点原子的实验中,接受数组其实是可以外部调用的,但是需要手动清楚标志位,我嫌麻烦,于是我重新封装了一下。

函数不难理解,我在这简单说一下(我水平有限可能会有错误,欢迎批评指正),这里传进来一个数组和大小,首先将要用的空间清空,然后将接受缓冲中的数据复制到这块空间中,再清空缓冲区和接受标志位,这样我们调用的时候就不用管这些乱七八糟的东西了,直接定义一个数组,调用函数拿到我们所需的数据即可。
(3)主函数调用
这里根据正点原子实验改的,直接把主函数贴到这,有兴趣的小伙伴自己看吧。下篇学习看门狗。
int main(void)
{
/* USER CODE BEGIN 1 */
int Rx_MaxSize = 256;
uint8_t len;
uint16_t times = 0;
uint8_t rx_buff[Rx_MaxSize];
/* USER CODE END 1 */
/* MPU Configuration--------------------------------------------------------*/
MPU_Config();
/* 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 */
USART1_RX_Enble();
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
if (USART1_GET_State()) /* 接收到了数据? */
{
len = USART1_DATA_Len(); /* 得到此次接收到的数据长度 */
printf("\r\n您发送的消息为:\r\n");
/*117-120三行代码效果等同于123-124*两行*/
USART1_GET_Data(rx_buff, Rx_MaxSize);
HAL_UART_Transmit(&huart1, (uint8_t *)rx_buff, len, 1000);
printf("\r\n");
/*转存之前的数据在g_usart_rx_buf数组内,该数组可以被外部调用,但需要手动清空标志位*/
// printf("\r\ng_usart_rx_buf:%s\r\n",g_usart_rx_buf);
// USART1_GET_Data(rx_buff);
// printf("\r\n%s\r\n",rx_buff);
}
else
{
times++;
if (times % 5000 == 0)
{
printf("\r\nSTM32H743开发板 串口实验\r\n");
}
if (times % 200 == 0)
{
printf("请输入数据,以回车键结束\r\n");
}
if (times % 30 == 0)
{
LED_TURN(100); /* 闪烁LED,提示系统正在运行 */
}
HAL_Delay(10);
}
}
/* USER CODE END 3 */
}
更多推荐



所有评论(0)