完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
1个回答
|
|
以前在使用串口的时候都是直接使用中断,每收发一个字节都要进一次中断,然后直接在中断进行封包,现在做了一个简单的分层设计,其实这个设计还是驱动设计,后期将逻辑层划分再细致一点,争取做到和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; } |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1780 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1621 浏览 1 评论
1081 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
728 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1679 浏览 2 评论
1938浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
731浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
570浏览 3评论
596浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
556浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 22:06 , Processed in 0.837976 second(s), Total 77, Slave 61 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号