实验目的
本文主要是实现串口助手发送任意字符串,在屏幕上接收到相同的字符串。
实验环境
- 软件: stm32cubemx ,keilMDK,Configure Virtual Serial Port Driver,串口助手
- 硬件:stm32f103开发版
- 资料:stm32f103中文参考手册
实验现象
实验步骤
硬件引脚
如图表1-1所示,为stm32f103vet6芯片USART中TX,RX的引脚。本文使用ABP2总线中的USART1串口实现串口接收发送任意字符串。
引脚 | ABP2总线 | ABP1总线 | |||
---|---|---|---|---|---|
USART1 | USART2 | USART3 | UART4 | UART5 | |
TX | PA9 | PA2 | PB10 | PC10 | PC12 |
RX | PA10 | PA3 | PB11 | PC11 | PD2 |
软件设计
UASRT初始化
UASRT初始化就不介绍了,可以在本博客基于stm32cubemx实现串口接收与发送-基础进行学习。
回显任意字符串
1.基础
接收发送任意字符串中,本文使用的是中断函数。在基础篇中,可以知道,当进入中断函数时,函数调用的是HAL_UART_IRQHandler(&huart1); 这个函数,并且最终在HAL_UART_RxCpltCallback(huart);这个回调函数中进行编写,实现一些功能。由于这些函数都是HAL库中的函数,在接收发送过程中固定的字符个数,无法接收发送任意字符个数。那我们该如何实现接收发送任意个数的字符。
首先我们可以看参考手册中数据寄存器(USART_DR)一次接收发送时是什么样的。如图1-2所示:
从图中可以看出数据寄存器每次只能接收和发送9位,而一个字符为8位,因此串口每次接收和发送一个字符。那我们该如何实现接收和发送无限制字符串。其实可以这样。每当接收一个字符我们就把这个字符发送出去,这样就可以接收和发送任意字符串。在中断服务函数中,我们可以不用调用HAL_UART_IRQHandler函数,而是直接编写自己的中断函数。下面就是我写的中断函数内容:
void USART1_IRQHandler(void)
{
uint8_t ch;
if (__HAL_UART_GET_FLAG( &huart1, UART_FLAG_RXNE ) != RESET)//获取接收中断事件标志
{
ch=( uint16_t)READ_REG(huart1.Instance->DR);//接收字符
WRITE_REG ( huart1.Instance->DR,ch);//发送字符
}
}
为什么要这样写,我们可以和51单片机相比较,当51单片机接收一个字符是,RI=1,和__HAL_UART_GET_FLAG( &UartHandle, UART_FLAG_RXNE ) != RESET相当于一个意思。在51单片机中ch=SBUF,表示将接收到的字符放入ch里,类似于我们这ch=( uint16_t)READ_REG(UartHandle.Instance->DR)这个语句。那么WRITE_REG ( UartHandle.Instance->DR,ch);也就是发送字符。
这里问题来了,为什么在51单片机中,RI要软件清零,而这里不需要。这里我们就需要看芯片的参考手册了。如图1-3所示,在参考手册状态寄存器(USART_SR)中可以找到这个。
这里我们可以读到对USART_DR的读操作可以将该位清零。因此可以不需要和51单片机中RI需要软件清零。
当然,在初始化中要打开接收中断使能,否在串口中断无法进入。
__HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);//接收中断使能
这样就实现了串口任意接收和发送字符串了。
2. 提升
上面的代码中就是单纯的接收和发送任意字符串。如果我们需要发送一个字符串来控制单片机的某个东西,而且单片机还要发送一串字符,表示已经执行的我们所需要控制的的东西,那我们该怎么办。其实学我们专业都见过这样一个东西,计算机中dos,当我们写入某个指令,按回车就能反馈看的东西。在这里我们也可以用这个方式来实现这个功能。
步骤如下:
接收字符
判断字符是否为’\n’
为字符’\n’flag标志置1,关闭串口中断进行发送字符
flag置0,打开接收中断使能
将stm32f1xx_it.c文件中USART1_IRQHandler(void)放入main.c文件下,在中断函数中代码如下:void USART1_IRQHandler(void) { //HAL_UART_IRQHandler(&huart1); uint8_t ch ; if(__HAL_UART_GET_FLAG( &huart1, UART_FLAG_RXNE ) != RESET) { ch=( uint16_t)READ_REG(huart1.Instance->DR); USART_RX_BUF[USART_RX_STA&0X3FFF]=ch;//接收到的字符保存在 USART_RX_BUF 数组中 if(USART_RX_BUF[USART_RX_STA] == 0X0A && USART_RX_BUF[USART_RX_STA-1] == 0X0D)//判断是否为'\n' { flag = 1; //置1 __HAL_UART_DISABLE_IT(&huart1, UART_IT_RXNE);//关闭接收中断 } USART_RX_STA++; //计数接收到的字符的个数 } }
USART_RX_BUF数组和USART_RX_STA为全局变量,上面代码中我们将接收到的字符放入USART_RX_BUF数组中,一旦检测到数组中有’\n’时,flag置1,关闭中断。
然后在main.c 里while(1)中加入下面代码:while (1) { /* USER CODE END WHILE */ if(flag) //如果flag为1 { flag = 0;//flag为0 HAL_UART_Transmit(&huart1,USART_RX_BUF,USART_RX_STA,1000);发送字符,查询方式 for(uint16_t i = 0; i<USART_RX_STA; i++) //清空数组 { USART_RX_BUF[i] = 0; } USART_RX_STA = 0; 计数为0 __HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);//打开接收中断 } /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ }
在这段代码中表示当flag=1时,将接收到的字符发送出去,然后清空数组,计数为0最后在打开中断。当然,也可以在里面判断接收的字符串,然后发送自己想看的字符串。
代码的位置
代码的位置在main.c文件中,如图1-4,图1-5,图1-6所示: