STM32 串口驱动,分层通信

2023-05-16

以前在使用串口的时候都是直接使用中断,每收发一个字节都要进一次中断,然后直接在中断进行封包,现在做了一个简单的分层设计,其实这个设计还是驱动设计,后期将逻辑层划分再细致一点,争取做到和linux的shell类似的分层。

软件分层如下

驱动层:串口、DMA、初始化,串口只开启接收空闲中断,DMA中断不开启。

缓冲区:利用malloc和free函数创建的链表,缓冲区管理有两个,一个是接收缓冲区,每次进入接收空闲中断就把数据扔到接收缓冲队列里面去;另一个是发送缓冲区,发送缓冲区无逻辑,这只是一个数据结构。

 

示意图中的数据指针实际上用的是uint8 数组,当然,第一个数据完全可以塞到第二个数据里面,但是如果使用的是M0芯片的时候,会有一个指针地址的对齐问题,这个就不展开说了,只要是问题都有规避办法的。

逻辑层:逻辑里面关于串口接收队列,因为无法保证发送方的数据连续,所以需要将接收缓冲队列的数据重新打包,打包函数主要检测接收队列是否有数据,如果有,进行数据打包,如果能保证数据帧的完整性,无粘包、无断包,数据打包函数可以去除。串口发送函数,串口发送函数定时10ms检测DMA发送通道是否为空,如果通道空,延时10ms启动DMA发送,发送数据在发送队列缓冲中获取,发送的帧间隔范围在10~20ms之间,延时10ms保证了发送帧间隔至少10ms。


设计一个发送函数的接口,有数据发送时,应用只管往里面扔数据,然后再用一个封包函数封起来,再加一个封包函数,对于用户而言只有一个发送函数的api,用户层只管发送数据,底层逻辑不要管,也不允许动。发送函数只管定时从发送队列里面取数据,取一帧,然后调用dma,然后等待帧间隔,进行下一帧数据获取,发送,做到软件层面的分层,责任划分明确,一个函数只干一件事。


软件设计思想说完,下面直接放代码。

usart.h

#ifndef _USART_H
#define _USART_H
#include "stm32f10x.h"

#define SEND_BUSY 1
#define SEND_IDLE 0
#define BOUND_RATE	38400			//串口通信波特率
#define SENDBUFF_SIZE 50
#define SENDBUFF_SIZE_INTIT 0
#define RECEBUFF_SIZE 200
#define USART1_DR_Base  0x40013804
#define COMM_SEND_INTERNAL_20MS	20

struct COM_DATA_ST
{
	uint8_t SendBuff[SENDBUFF_SIZE];
	uint8_t ReceBuff[RECEBUFF_SIZE];
	uint8_t Send_Complete_Flag;
	uint8_t Bus_Idle_Count;
};

void usart1_init(void);
void usart1_rev_irq(void);
void usart1_send_irq(void);
void DMA_Config(void);
void Send_data(uint8_t *ptr,uint8_t length);
void usart1_dma_send_irq(void);
void Check_Send_Quene(void);
void usart1_send_interval_deal(void);

#endif
usart.c
#include "stm32f10x.h"
#include "string.h"
#include "usart.h"
#include "quene.h"


struct COM_DATA_ST Com_Data;

struct node Rece_Quene;			/*接收的数据队列*/
struct node Send_Quene;			/*发送数据的队列*/
struct node Send_Ack_Quene;		/*发送应答数据的队列*/

uint8_t Get_DMA_State(void);

/*
 * Description:串口初始化
 * input:none
 * output:none
 * author:
 * date:2018-2-7
*/
void usart1_init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;

	NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;							//配置定时器中断通道
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);

	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);		//使能afio时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//使能GPIOA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);		//使能串口1时钟
		
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;							//串口1输入脚浮空
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;								//串口1输出脚配成多功能上下拉
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;					//多功能推挽输出
	GPIO_Init(GPIOA, &GPIO_InitStructure);

	USART_InitStructure.USART_BaudRate = BOUND_RATE;				//初始化串口参数
	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(USART1, &USART_InitStructure);								//初始化串口
	USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);							//空闲中断
	USART_Cmd(USART1, ENABLE);
	
	
}

/*
 * 函数名:DMA_Config
 * 描述  :DMA 串口的初始化配置
 * 输入  :无
 * 输出  : 无
 * 调用  :外部调用
 */
void DMA_Config(void)
{
    DMA_InitTypeDef DMA_InitStructure;
	NVIC_InitTypeDef NVIC_InitStructure;
	
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);								/*开启DMA时钟*/

	NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
	NVIC_Init(&NVIC_InitStructure);
 	
    DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;	   					/*设置DMA源:内存地址&串口数据寄存器地址*/
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)Com_Data.SendBuff;							/*内存地址(要传输的变量的指针)*/
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;								/*方向:从内存到外设*/		
    DMA_InitStructure.DMA_BufferSize = SENDBUFF_SIZE_INTIT;								/*传输大小DMA_BufferSize=SENDBUFF_SIZE*/
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 				/*外设地址不增*/	
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;							/*内存地址自增*/
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;			/*外设数据单位*/	
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;	 				/*内存数据单位 8bit*/
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal ;	 								/*DMA模式:一次传输,循环*/
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;  							/*优先级:中*/
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;									/*禁止内存到内存的传输	*/
    DMA_Init(DMA1_Channel4, &DMA_InitStructure); 									/*配置DMA1的4通道*/			   
	DMA_Cmd (DMA1_Channel4,ENABLE);													/*使能DMA*/
//	DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);  									/*配置DMA发送完成后产生中断*/
 	
    DMA_InitStructure.DMA_PeripheralBaseAddr = USART1_DR_Base;	   					/*设置DMA源:内存地址&串口数据寄存器地址*/
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)Com_Data.ReceBuff;							/*内存地址(要传输的变量的指针)*/
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;								/*方向:从外设到内存*/		
    DMA_InitStructure.DMA_BufferSize = RECEBUFF_SIZE;								/*传输大小DMA_BufferSize=SENDBUFF_SIZE*/
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; 				/*外设地址不增*/	
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;							/*内存地址自增*/
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;			/*外设数据单位*/	
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;	 				/*内存数据单位 8bit*/
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular ;	 							/*DMA模式:一次传输,循环*/
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;  							/*优先级:中*/
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;									/*禁止内存到内存的传输	*/
    DMA_Init(DMA1_Channel5, &DMA_InitStructure); 									/*配置DMA1的5通道*/			   
	DMA_Cmd (DMA1_Channel5,ENABLE);													/*使能DMA*/
	USART_DMACmd(USART1, USART_DMAReq_Rx, ENABLE);
}

/*
 * Description:串口1接收中断
 * input:none
 * output:none
 * author:
 * date:2018-2-7
*/
void usart1_rev_irq(void)
{
	char data_length;
	data_length = USART1->SR;
	data_length = USART1->DR;
	DMA_Cmd(DMA1_Channel5,DISABLE);
	data_length = RECEBUFF_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5);
	InsertNode(&Rece_Quene,Com_Data.ReceBuff,data_length);
	DMA_SetCurrDataCounter(DMA1_Channel5,RECEBUFF_SIZE);
	DMA_Cmd(DMA1_Channel5,ENABLE);
}

/*
 * Description:串口1发送完成中断
 * input:none
 * output:none
 * author:
 * date:2018-2-7
*/
void usart1_dma_send_irq(void)
{
	Com_Data.Send_Complete_Flag = 1;
}

/*
 * Description:串口1发送完成间隔处理
 * input:none
 * output:none
 * author:
 * date:2018-2-28
*/
void usart1_send_interval_deal(void)
{
	if(0 == Get_DMA_State()){
		if(Com_Data.Bus_Idle_Count<0xff)
			Com_Data.Bus_Idle_Count++;
	}else{
		Com_Data.Bus_Idle_Count=0;
	}
}

/*
 * Description:串口1发送数据api
 * input:*ptr:发送数据缓冲区指针,length:发送长度
 * output:none
 * author:
 * date:2018-2-28
*/
void Send_data(uint8_t *ptr,uint8_t length)
{
	memcpy(Com_Data.SendBuff,ptr,length);						/*拷贝数据到发送缓冲区中*/
	DMA_Cmd(DMA1_Channel4,DISABLE);						/*在发送数据之前必须关闭dma通道,否则无法修改发送的buffer长度*/
	DMA_SetCurrDataCounter(DMA1_Channel4,length);		/*修改发送数据长度*/
	DMA_Cmd(DMA1_Channel4,ENABLE);						/*启动dma通道*/
	USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);		/*启动串口发送*/
}

/*
 * Description:DMA发送忙检测
 * input:
 * output: SEND_BUSY:发送忙 SEND_IDLE:空闲
 * author:
 * date:2018-2-28
*/
uint8_t Get_DMA_State(void)
{
	if(DMA_GetCurrDataCounter(DMA1_Channel4))			/*检测dma是否发送完成*/
		return SEND_BUSY;
	else
		return SEND_IDLE;
}

/*
 * Description:检测发送队列有没有数据
 * input:
 * output:  
 * author:
 * date:2018-2-28
*/
void Check_Send_Quene(void)
{
	uint8_t length;
	uint8_t send_data[50];
	
	if(Com_Data.Bus_Idle_Count>=COMM_SEND_INTERNAL_20MS){/*发送间隔20个毫秒*/
		Com_Data.Bus_Idle_Count=0;
		if(TURE == GetNodeData(&Send_Ack_Quene,send_data,&length)){/*优先发送ack队列*/
			Send_data(send_data,length);
			DeleNode(&Send_Ack_Quene);
		}else if(TURE == GetNodeData(&Rece_Quene,send_data,&length)){
			Send_data(send_data,length);
			DeleNode(&Rece_Quene);
		}
	}
		
}

/*
 * Description:检测接收队列并进行数据封包
 * input:
 * output: 
 * author:
 * date:2018-2-28
*/
void Check_Rece_Quene(void)
{
	uint8_t length;
	uint8_t Rece_data[50];
	
	if(TURE == GetNodeData(&Rece_Quene,Rece_data,&length)){
		DeleNode(&Rece_Quene);
		/*数据封包处理*/
	}
		
}

quene.h

#ifndef _QUENE_H
#define _QUENE_H
#include "stm32f10x.h"

#define TURE 1
#define FAULSE 0
#define TAIL_NODE 0xffff
#define HEAD_NODE 0

struct node
{
	uint8_t Length;						//有效数据
	uint8_t *Data;						//数据指针
	struct node *pNext;					//节点指针
};

uint8_t InsertNode(struct node *pHeader,uint8_t *data,uint8_t data_length);
uint8_t GetNodeData(struct node *pHeader,uint8_t *data,uint8_t *length);
uint8_t DeleNode(struct node *pHeader);
uint16_t  GetNodeNum(struct node *pHeader);

#endif

quene.c

#include "stdlib.h"
#include "string.h"
#include "quene.h"

/*
 * Description:插入节点
* input:*pHeader:头结点地址 *data:数据 data_length:数据长度
 * output:FAULSE:插入失败 
 * author:
 * date:2018-2-7
*/
uint8_t InsertNode(struct node *pHeader,uint8_t *data,uint8_t data_length)
{
	struct node *p=NULL;
	struct node *p1=NULL;
	
	p = pHeader;
	p1 =(struct node*)malloc(sizeof(struct node));
	
	if(NULL == p1)
		return FAULSE;
	
	p1->pNext = NULL;
	p1->Data = (uint8_t *)malloc(data_length);
	if(NULL == p1->Data)
		return FAULSE;
	memcpy(p1->Data,data,data_length);
	p1->Length = data_length;

	while(p->pNext != NULL)
		p=p->pNext;
		
	p->pNext = p1;
	
	
	return TURE;
}

/*
 * Description:删除节点
* input:*pHeader:头结点地址 
 * output:FAULSE:删除失败 TURE:删除成功
 * author:
 * date:2018-2-7
*/
uint8_t DeleNode(struct node *pHeader)
{
	struct node *p=pHeader,*p1;
	
	if(NULL == p)
	{
		return FAULSE;
	}
	
	p1 = p->pNext;
	p->pNext=p1->pNext;
	free(p1->Data);
	free(p1);
	
	return TURE;
}

/*
 * Description:获取链表长度
 * input:*pHeader:头结点地址 
 * output:长度
 * author:
 * date:2018-2-7
*/
uint16_t  GetNodeNum(struct node *pHeader)
{
	uint16_t i=0;
	struct node *p=pHeader;
	
	while(p->pNext != NULL){
		i++;
		p = p->pNext;
	}		
	
	return i;
}

/*
 * Description:获取队列节电数据
 * input:*pHeader:头结点地址 
 * output:FAULSE:
 * author:
 * date:2018-2-7
*/
uint8_t GetNodeData(struct node *pHeader,uint8_t *data,uint8_t *length)
{
	struct node *p=pHeader;
	if(p->pNext == NULL)/*节点空*/
		return FAULSE;
	
	p=p->pNext;
	memcpy(data,p->Data,p->Length);
	*length = p->Length;
	
	return TURE;
}



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

STM32 串口驱动,分层通信 的相关文章

随机推荐