STM32F103基于标准库开发串口中断接收数据环形队列例程

Posted perseverance52

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STM32F103基于标准库开发串口中断接收数据环形队列例程相关的知识,希望对你有一定的参考价值。

STM32F103基于标准库开发串口中断接收数据环形队列例程


✨本示例源码来源于野火-STM32库开发实战指南,是一个值得学习借鉴的资源。

  • 📑一个完整的串口数据包通讯协议一般包含:帧头、地址信息、数据类型、数据长度、数据块、校验码、帧尾。在一般开发过程当中,如果仅仅是作为调试信息使用,那么串口的使用就没有必要代码这么健壮。毕竟功能越完善,所需要提供的代码越复杂,处理的逻辑增加,以及资源的占用。

🔖有关环形列队的优点可以自行去搜索科普,这里不做介绍了。

🛠环形列队实现

🔖接收数据环形列队实现依托开启串口中断接收以及空闲中断。

  • rx_data_queue.h
#ifndef __ESP_DATA_QUEUE_H_
#define __ESP_DATA_QUEUE_H_

#include "stm32f10x.h"

#include <string.h>
#include <stdio.h>

//缓冲队列的个数需要为2的幂
#define QUEUE_NODE_NUM        (2)            //缓冲队列的大小(有多少个缓冲区)
#define QUEUE_NODE_DATA_LEN   (2*1024 )       //单个接收缓冲区大小

//队列的主体数据类型接口
#define QUEUE_DATA_TYPE  				ESP_USART_FRAME
//队列的调试输出接口
#define DATA_QUEUE_LOG  				QUEUE_DEBUG
#define DATA_QUEUE_LOG_ARRAY 	QUEUE_DEBUG_ARRAY


//数据主体
typedef struct 

	char  *head; 	//缓冲区头指针	
	uint16_t len; //接收到的数据长度

ESP_USART_FRAME;


//队列结构
typedef struct 
	int         size;  /* 缓冲区大小          */
	int         read; /* 读指针              */
	int         write;   /* 写指针  */
	int read_using;	/*正在读取的缓冲区指针*/
	int write_using;		/*正在写入的缓冲区指针*/
	QUEUE_DATA_TYPE    *elems[QUEUE_NODE_NUM];  /* 缓冲区地址                   */
 QueueBuffer;


extern QueueBuffer rx_queue;


/*信息输出*/
#define QUEUE_DEBUG_ON         1
#define QUEUE_DEBUG_ARRAY_ON		0

#define QUEUE_INFO(fmt,arg...)           printf("<<-QUEUE-INFO->> "fmt"\\n",##arg)
#define QUEUE_ERROR(fmt,arg...)          printf("<<-QUEUE-ERROR->> "fmt"\\n",##arg)
#define QUEUE_DEBUG(fmt,arg...)          do\\
                                          if(QUEUE_DEBUG_ON)\\
                                          printf("[%d]"fmt"\\n",__LINE__, ##arg);\\
                                          while(0)

#define QUEUE_DEBUG_ARRAY(array, num)    do\\
																									 int32_t i;\\
																									 uint8_t* a = array;\\
																									 if(QUEUE_DEBUG_ARRAY_ON)\\
																									 \\
																											printf("\\n<<-QUEUE-DEBUG-ARRAY->>\\n");\\
																											for (i = 0; i < (num); i++)\\
																											\\
																													printf("%02x   ", (a)[i]);\\
																													if ((i + 1 ) %10 == 0)\\
																													\\
																															printf("\\n");\\
																													\\
																											\\
																											printf("\\n");\\
																									\\
																								 while(0)	

//输出队列的状态信息
#define cbPrint(cb)		    DATA_QUEUE_LOG("size=0x%x, read=%d, write=%d\\n", cb.size, cb.read, cb.write);\\
	  DATA_QUEUE_LOG("size=0x%x, read_using=%d, write_using=%d\\n", cb.size, cb.read_using, cb.write_using);


QUEUE_DATA_TYPE* cbWrite(QueueBuffer *cb);
QUEUE_DATA_TYPE* cbRead(QueueBuffer *cb);
void cbReadFinish(QueueBuffer *cb);
void cbWriteFinish(QueueBuffer *cb);
//void cbPrint(QueueBuffer *cb);
QUEUE_DATA_TYPE* cbWriteUsing(QueueBuffer *cb) ;
int cbIsFull(QueueBuffer *cb) ; 
int cbIsEmpty(QueueBuffer *cb) ;

void rx_queue_init(void);
void pull_data_from_queue(void);
void push_data_to_queue(char *src_dat,uint16_t src_len);


#endif




  • rx_data_queue.c
/**
  ******************************************************************************
  * @file    rx_data_queue.c
  * @author  fire
  * @version V1.0
  * @date    2015-01-xx
  * @brief   环形缓冲区,适用于接收外部数据时用作缓冲
  ******************************************************************************
  * @attention
  *
  * 实验平台:野火 IOT STM32 开发板 
  * 论坛    :http://www.firebbs.cn
  * 淘宝    :https://fire-stm32.taobao.com
  *
  ******************************************************************************
  */ 

#include "./usart/rx_data_queue.h"



//实例化节点数据类型
QUEUE_DATA_TYPE  node_data[QUEUE_NODE_NUM]; 
//实例化队列类型
QueueBuffer rx_queue;

//队列缓冲区的内存池
__align(4) char node_buff[QUEUE_NODE_NUM][QUEUE_NODE_DATA_LEN] ;



/*环形缓冲队列*/

/**
  * @brief  初始化缓冲队列
  * @param  cb:缓冲队列结构体
  * @param  size: 缓冲队列的元素个数
  * @note 	初始化时还需要给cb->elems指针赋值
  */
void cbInit(QueueBuffer *cb, int size) 

    cb->size  = size;	/* maximum number of elements           */
    cb->read = 0; 		/* index of oldest element              */
    cb->write   = 0; 	 	/* index at which to write new element  */
//    cb->elems = (uint8_t *)calloc(cb->size, sizeof(uint8_t));  //elems 要额外初始化

 
//(此函数改成了宏,在头文件)
/**
  * @brief  输出缓冲队列当前的状态信息
  * @param  cb:缓冲队列结构体
  */
//void cbPrint(QueueBuffer *cb) 
//
//    DATA_QUEUE_LOG("size=0x%x, read=%d, write=%d\\n", cb->size, cb->read, cb->write);
//	  DATA_QUEUE_LOG("size=0x%x, read_using=%d, write_using=%d\\n", cb->size, cb->read_using, cb->write_using);
//
 
/**
  * @brief  判断缓冲队列是(1)否(0)已满
  * @param  cb:缓冲队列结构体
  */
int cbIsFull(QueueBuffer *cb) 

    return cb->write == (cb->read ^ cb->size); /* This inverts the most significant bit of read before comparison */ 

 
/**
  * @brief  判断缓冲队列是(1)否(0)全空
  * @param  cb:缓冲队列结构体
  */		
int cbIsEmpty(QueueBuffer *cb) 

    return cb->write == cb->read; 


/**
  * @brief  对缓冲队列的指针加1
  * @param  cb:缓冲队列结构体
  * @param  p:要加1的指针
  * @return  返回加1的结果
  */	
int cbIncr(QueueBuffer *cb, int p) 

    return (p + 1)&(2*cb->size-1); /* read and write pointers incrementation is done modulo 2*size */

 
/**
  * @brief  获取可写入的缓冲区指针
  * @param  cb:缓冲队列结构体
  * @return  可进行写入的缓冲区指针
  * @note  得到指针后可进入写入操作,但写指针不会立即加1,
           写完数据时,应调用cbWriteFinish对写指针加1
  */
QUEUE_DATA_TYPE* cbWrite(QueueBuffer *cb) 

    if (cbIsFull(cb)) /* full, overwrite moves read pointer */
    
			return NULL;
				
		else
		
			//当wriet和write_using相等时,表示上一个缓冲区已写入完毕,需要对写指针加1
			if(cb->write == cb->write_using)
			
				cb->write_using = cbIncr(cb, cb->write); //未满,则增加1
			
		
		
	return  cb->elems[cb->write_using&(cb->size-1)];




/**
  * @brief 数据写入完毕,更新写指针到缓冲结构体
  * @param  cb:缓冲队列结构体
  */
void cbWriteFinish(QueueBuffer *cb)

    cb->write = cb->write_using;

 
/**
  * @brief  获取可读取的缓冲区指针
  * @param  cb:缓冲队列结构体
  * @return  可进行读取的缓冲区指针
  * @note  得到指针后可进入读取操作,但读指针不会立即加1,
					 读取完数据时,应调用cbReadFinish对读指针加1
  */
QUEUE_DATA_TYPE* cbRead(QueueBuffer *cb) 

		if(cbIsEmpty(cb))
			return NULL;
		
	//当read和read_using相等时,表示上一个缓冲区已读取完毕(即已调用cbReadFinish),
	//需要对写指针加1
	if(cb->read == cb->read_using)	
		cb->read_using = cbIncr(cb, cb->read);
	
	return cb->elems[cb->read_using&(cb->size-1)];



/**
  * @brief 数据读取完毕,更新读指针到缓冲结构体
  * @param  cb:缓冲队列结构体
  */
void cbReadFinish(QueueBuffer *cb) 
	
		//重置当前读完的数据节点的长度
		cb->elems[cb->read_using&(cb->size-1)]->len = 0;
	
    cb->read = cb->read_using;




//队列的指针指向的缓冲区全部销毁
void camera_queue_free(void)

    uint32_t i = 0;

    for(i = 0; i < QUEUE_NODE_NUM; i ++)
    
        if(node_data[i].head != NULL)
        
					//若是动态申请的空间才要free
//            free(node_data[i].head);
            node_data[i].head = NULL;
        
    

    return;



/**
  * @brief  缓冲队列初始化,分配内存,使用缓冲队列时,
  * @param  无
  * @retval 无
  */
void rx_queue_init(void)

  uint32_t i = 0;

  memset(node_data, 0, sizeof(node_data));
		 
	/*初始化缓冲队列*/
	cbInit(&rx_queue,QUEUE_NODE_NUM);

    for(i = 0; i < QUEUE_NODE_NUM; i ++)
    
        node_data[i].head = node_buff[i];
        
        /*初始化队列缓冲指针,指向实际的内存*/
        rx_queue.elems[i] = &node_data[i];
        
        DATA_QUEUE_LOG("node_data[i].head=0x%x,\\r\\nrx_queue.elems[i] =0x%x", (uint32_t)node_data[i].head,(uint32_t)rx_queue.elems[i]->head);

        memset(node_data[i].head, 0, QUEUE_NODE_DATA_LEN);
    
		
//	cbPrint(rx_queue);	




/**
  * @brief  往队列中写入数据的样例
  */
void push_data_to_queue(char *src_dat,uint16_t src_len)

	QUEUE_DATA_TYPE *data_p;
	uint8_t i;
	
	for(i=0;i<src_len;i++)
	
		/*获取写缓冲区指针,准备写入新数据*/
		data_p = cbWrite(&rx_queue);
		
		if (data_p != NULL)	//若缓冲队列未满,开始传输
				
			//往缓冲区写入数据,如使用串口接收、dma写入等方式
			*(data_p->head + i) = src_dat[i];
				data_p->len++;
			printf("\\r\\ndata_p->len =%d",data_p->len);
		else return;	
		
		cbPrint(rx_queue);	
		
	
	/*写入缓冲区完毕*/
	cbWriteFinish(&rx_queue);
	
	cbPrint(rx_queue);	




/**
  * @brief  从队列中取数据的样例
  */
void pull_data_from_queue(void)

	QUEUE_DATA_TYPE *rx_data;	
		
	/*从缓冲区读取数据,进行处理,*/
	rx_data = cbRead(&rx_queue); 

	if(rx_data != NULL)//缓冲队列非空
			
		//加上字符串结束符,方便直接输出字符串
		*(rx_data->head+rx_data->len) = '\\0';
		
		QUEUE_DEBUG("接收到的数据:%s",rx_data->head);
		QUEUE_DEBUG_ARRAY((uint8_t*)rx_data->head,rx_data->len);

		//使用完数据必须调用cbReadFinish更新读指针
		cbReadFinish(&rx_queue);
	




  • 串口配置函数
 /**
  * @brief  USART GPIO 配置,工作参数配置
  * @param  无
  * @retval 无
  */
void USART_Config(void)

	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;

	// 打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
	
	// 打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

	// 将USART Tx的GPIO配置为推挽复用模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

  // 将USART Rx的GPIO配置为浮空输入模式
	GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
	
	// 配置串口的工作参数
	// 配置波特率
	USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
	// 配置 针数据字长
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	// 配置停止位
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	// 配置校验位
	USART_InitStructure.USART_Parity = USART_Parity_No ;
	// 配置硬件流控制
	USART_InitStructure.USART_HardwareFlowControl = 
	USART_HardwareFlowControl_None;
	// 配置工作模式,收发一起
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
	// 完成串口的初始化配置
	USART_Init(DEBUG_USARTx, &USART_InitStructure);
	
	// 串口中断优先级配置
	NVIC_Configuration();
	
	// 使能串口接收中断
	USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);	
	USART_ITConfig ( DEBUG_USARTx, USART_IT_IDLE, ENABLE ); //使能串口总线空闲中断 	

	
	// 使能串口
	USART_Cmd(DEBUG_USARTx, ENABLE);	    

  • printf重定向代码
///重定向c库函数printf到串口,重定向后可使用printf函数
int fputc(int ch, FILE *f)

		/* 发送一个字节数据到串口 */
		USART_SendData(DEBUG_USARTx, (uint8_t) ch);
		
		/* 等待发送完毕 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);		
	
		return (ch);


///重定向c库函数scanf到串口,重写向后可使用scanf、getchar等函数
int fgetc(FILE *f)

		/* 等待串口输入数据 */
		while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

		return (int)USART_ReceiveData(DEBUG_USARTx);


  • main.c
#include "stm32f10x.h"
#include "bsp_usart.h"
#include "./usart/rx_data_queue.h"

/**
  * @brief  主函数
  * @param  无
  * @retval 无
  */
int main(void)
	
  /*初始化USART 配置模式为 115200 8-N-1,中断接收*/
  USART_Config();
		
	rx_queue_init();
	
	/* 发送一个字符串 */
	Usart_SendString( DEBUG_USARTx,"这是一个串口中断接收回显实验\\n");
	printf("STM32F103VCT6\\r\\n");

  while(1)
		
		//获取数据并输出
		//实际应用中可参考pull data的方式获取数据进行处理
		pull_data_from_queue();
		

  • 📜串口打印信息

📚程序源码

⚡基于Keil5,ARMcompiler:5.06

链接:https://pan.baidu.com/s/1zDyGGNJTdWg6jI9RSsqvxw 
提取码:wdr0

以上是关于STM32F103基于标准库开发串口中断接收数据环形队列例程的主要内容,如果未能解决你的问题,请参考以下文章

STM32F103VET6基于STM32CubeMX创建串口中断+ DMA 不定长数据接收

STM32学习笔记 二基于STM32F103C8T6和STM32CubeMX实现UART串口通信数据收发

STM32F103VE基于标准库下DHT11数据串口打印输出

STM32标准库_04 | 串口接收不定长数据

STM32F103VET6基于Arduino开发框架下FreeRTOS串口1不能正常工作解决方案

基于STM32F103的网络天气时钟--------单片机串口获取天气