以前在使用串口的时候都是直接使用中断,每收发一个字节都要进一次中断,然后直接在中断进行封包,现在做了一个简单的分层设计,其实这个设计还是驱动设计,后期将逻辑层划分再细致一点,争取做到和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));
上一篇:STM32同时开启两个定时器,其一个定时器不能设置断点的原因
下一篇:STM32一直死在r1,[r0,#0x808]
推荐阅读最新更新时间:2024-11-05 16:52
设计资源 培训 开发板 精华推荐
- 血糖仪
- CH32V203C8T6核心板 也适用于其他C8T6
- 【模拟电路】简易星形呼吸灯
- 用于微处理器复位电路的 NCP300LSN185T1 1.85V 电压检测器的典型应用
- ADM00506,基于 MCP37XXX 高速流水线 ADC 数据采集卡的评估板
- 采用 EFR32MG12 Si7021 和 Si1141 的 ZigBee 智能插座参考设计
- RT9266B 大电流应用的典型应用 RT9266B 微型封装、高效率、升压 DC/DC 转换器
- DER-717 - 基于InnoSwitch3-CE的15 W适配器,具有±30 kV ESD能力
- LT4276CHUFD LTPoE++ 70W 电源在正向模式下的典型应用电路
- XHU_NFC 西华大学校园地图NFC卡