文章目录
使用HAL库(或标准库)方式,设置USART1 波特率为115200,1位停止位,无校验位,分别采用中断方式、DMA方式完成下列任务:TM32系统给上位机(win10)连续发送“hello windows!”;当上位机给stm32发送字符“stop”后,stm32暂停发送“hello windows!”;发送一个字符“start”后,stm32继续发送;
一、中断方式
1.1、设计思路
1.1.1、某些HAL库源码解析(可以不看)
使用中断方式,即采用HAL_UART_Receive_IT
函数来实现。这是接收中断函数,输入一个字符即会触发一次中断,然后调用中断处理函数USART1_IRQHandler
,该函数中默认执行HAL_UART_IRQHandler
。
if (errorflags == RESET)
{
/* UART in mode Receiver -------------------------------------------------*/
if (((isrflags & USART_SR_RXNE) != RESET) && ((cr1its & USART_CR1_RXNEIE) != RESET))
{
UART_Receive_IT(huart);
return;
}
}
如果没有发生错误,则判断是否接收到数据(RXNE标志位被置位)并且接收中断使能(RXNEIE标志位被置位)。如果是则进入UART_Receive_IT(huart);
这个函数的主要作用是从缓冲区读取数据,将数据存储到用户自定义的接收缓冲区中。
if (huart->RxState == HAL_UART_STATE_BUSY_RX)
{
if(huart->Init.WordLength == UART_WORDLENGTH_9B){
//这里是数据长度为9,我用的是8,所以直接看后面...
}else{
//将数据存到8位临时变量中,将接收缓冲区指针递增一个字节
if (huart->Init.Parity == UART_PARITY_NONE)
{
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x00FF);
}
else
{
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->DR & (uint8_t)0x007F);
}
}
}
//开始判断是否剩余需要读取的个数为0,即接收中断中定义的size以及全部接收完。如果全部读取完成则禁用UART的接收中断、奇偶校验错误中断,错误中断,并将接收状态设置为READY。然后调用接收完成回调函数;如果没有全接收完,则继续接收。
if (--huart->RxXferCount == 0U)
{
__HAL_UART_DISABLE_IT(huart, UART_IT_RXNE);
__HAL_UART_DISABLE_IT(huart, UART_IT_PE);
__HAL_UART_DISABLE_IT(huart, UART_IT_ERR);
huart->RxState = HAL_UART_STATE_READY;
#if (USE_HAL_UART_REGISTER_CALLBACKS == 1)
huart->RxCpltCallback(huart);
#else
HAL_UART_RxCpltCallback(huart);
#endif
return HAL_OK;
}
1.1.2、程序执行流程设计
因为start和stop一个为5位,一个为4位,所以将接收中断函数的size设置为4,这样就可以在每读取完4个字节就进入接收完成回调函数,进行数据的处理。回调函数的大致流程:
- 如果从串口1接收到的数据为
stop
则直接将RxFlag置为1,从而停止hello winows!
的输出,并且重新启动接收中断。
if(huart->Instance == USART1&&strcmp((char *)RxBuffer,"stop")==0){
RxFlag = 1;
HAL_UART_Receive_IT(huart, (uint8_t*)&RxBuffer, LENGTH);
}
- 如果接受到的数据为
star
,则将flag标记为0,0表示还没有读取完(flag的作用是为了表示是否还需要多读取额外的一个字符,即总共读取5个字符),然后开启接收中断,接收一个字符
else if(huart->Instance == USART1&&strcmp((char *)RxBuffer,"star")==0&&flag==1){
flag = 0;
HAL_UART_Receive_IT(huart, (uint8_t*)&ch, 1);
}
- 如果接受到的数据是
t
并且flag == 0
,则总共接收到的五个字符是start
所以就将RxFlag置为0,继续输出hello windows!
,然后开启接收中断,4个字符
if(huart->Instance == USART1 && ch == 't' && flag == 0){
RxFlag = 0;
flag = 1;
ch = '0';
HAL_UART_Receive_IT(huart, (uint8_t*)&RxBuffer, LENGTH);
}
1.2、工程创建
可以参考上一篇博客https://blog.csdn.net/wakeup_high/article/details/134081617中的创建工程板块。
1.3、代码
这个实验都只在main.c文件中添加了代码
- 添加头文件
#include "stdio.h"
#include "string.h"
- 添加变量
uint8_t RxBuffer[LENGTH]; //字符缓冲区,LENGTH定义为4
uint8_t RxFlag = 0;//是否接受到stop
uint8_t ch;//额外字符't'的缓冲区
uint8_t flag = 1;//是否需要继续接收额外字符
- 添加while循环
while (1)
{
/* USER CODE END WHILE */
if(RxFlag == 0){
HAL_UART_Transmit(&huart1,(uint8_t*)"hello windows!\r\n",16,0xffff);
HAL_Delay(1000);
}
/* USER CODE BEGIN 3 */
}
- 添加中断接收回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
if(huart->Instance == USART1&&strcmp((char *)RxBuffer,"stop")==0){
RxFlag = 1;
HAL_UART_Receive_IT(huart, (uint8_t*)&RxBuffer, LENGTH);
}else if(huart->Instance == USART1&&strcmp((char *)RxBuffer,"star")==0&&flag==1){
flag = 0;
HAL_UART_Receive_IT(huart, (uint8_t*)&ch, 1);
}
if(huart->Instance == USART1 && ch == 't' && flag == 0){
RxFlag = 0;
flag = 1;
ch = '0';
HAL_UART_Receive_IT(huart, (uint8_t*)&RxBuffer, LENGTH);
}
}
1.4、运行
1.5、波形图
二、DMA方式
2.1、执行流程
DMA是直接存储器访问,用于在外设和存储器之间以及存储器和存储器之间进行高速数据传输。DMA传输过程的初始化和启动由CPU完成,过程由DMA控制器来执行,节省了CPU参与。同时开启空闲中断,当串口助手发送完一个数据后,触发空闲中断,对数据进行判断是否为stop或者start然后修改RxFlag的状态,控制hello windows!
的输出。
- 开启空闲中断,DMA接收数据
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);//开启空闲中断
HAL_UART_Receive_DMA(&huart1, (uint8_t*)RxBuffer, LENGTH);//DMA接收数据使能
- 空闲中断处理
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE END USART1_IRQn 0 */
/* USER CODEBEGIN USART1_IRQn 1 */
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET){
HAL_UART_DMAStop(&huart1);//停止DMA,避免覆盖RXBuffer
__HAL_UART_CLEAR_FEFLAG(&huart1);
if(strcmp((char *)RxBuffer, "stop")==0){
RxFlag = 1;
}else if(strcmp((char *)RxBuffer, "start")==0){
RxFlag = 0;
}
//HAL_UART_Transmit_DMA(&huart1, (uint8_t*)RxBuffer, strlen((char *)RxBuffer));
memset(RxBuffer,'\0',LENGTH);//因为start和stop长度不一样,重新覆盖不会完全覆盖,所以清0
HAL_UART_Receive_DMA(&huart1, (uint8_t*)RxBuffer, LENGTH);
}
/* USER CODE END USART1_IRQn 1 */
}
2.2、工程创建
RCC修改晶振,时钟配置都与前面相同,只是多了DMA分配:
2.3、代码
- main.h
#define LENGTH 100
extern uint8_t RxFlag;//是否接受到stop
extern uint8_t RxBuffer[LENGTH];//接收缓冲区
extern uint8_t RxCount;//接收数,等于LENGTH-已经接受到的
//因为这些变量在另一个c文件中调用了,所以要设置成外部变量,否则会因为一些莫名其妙覆盖
- main.c
//初始化
uint8_t RxFlag=0;
uint8_t RxBuffer[LENGTH]={0};
uint8_t RxCount=0;
//main函数块中
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart1, (uint8_t*)RxBuffer, LENGTH);
//while循环
while (1)
{
/* USER CODE END WHILE */
if(RxFlag == 0){
HAL_UART_Transmit_DMA(&huart1, (uint8_t*)"hello windows!\r\n", 16);
HAL_Delay(1000);
}
/* USER CODE BEGIN 3 */
}
- stm32f1xx_it.c中
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE END USART1_IRQn 0 */
/* USER CODEBEGIN USART1_IRQn 1 */
if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET){
HAL_UART_DMAStop(&huart1);
__HAL_UART_CLEAR_FEFLAG(&huart1);
if(strcmp((char *)RxBuffer, "stop")==0){
RxFlag = 1;
}else if(strcmp((char *)RxBuffer, "start")==0){
RxFlag = 0;
}
//HAL_UART_Transmit_DMA(&huart1, (uint8_t*)RxBuffer, strlen((char *)RxBuffer));
memset(RxBuffer,'\0',LENGTH);//
HAL_UART_Receive_DMA(&huart1, (uint8_t*)RxBuffer, LENGTH);
}
/* USER CODE END USART1_IRQn 1 */
}