当前位置: 首页 > news >正文

百度为什么会k网站梅州seo

百度为什么会k网站,梅州seo,企业级网站内容管理系统,企业网站建设流程及费用串口DMA接收基本思路 串口DMA接收数据基本思路一、串口处理使用背景及常用处理方法二、串口DMA接收相关思路三、串口DMA发送相关思路 串口DMA接收数据基本思路 一、串口处理使用背景及常用处理方法 单片机经常有串口处理大量数据的场景,常用的串口处理数据方式有如…

串口DMA接收基本思路

  • 串口DMA接收数据基本思路
    • 一、串口处理使用背景及常用处理方法
    • 二、串口DMA接收相关思路
    • 三、串口DMA发送相关思路

串口DMA接收数据基本思路

一、串口处理使用背景及常用处理方法

  单片机经常有串口处理大量数据的场景,常用的串口处理数据方式有如下方式:

   a.串口接收中断接收 + 串口阻塞发送,即使用串口接收中断对数据进行接收。然后处理完毕后在主程序中调用串口发送函数进行发送。该方式每接收一个数据便进入一次接收中断,且发送是阻塞发送,会阻碍其他处理操作运行。适合数据量不大的场景。

   b.串口空闲中断(接收中断) + 环形缓冲区 + 串口阻塞发送,该方法使用串口空闲中断和接收中断配合环形缓冲区可以解决大部分的串口数据处理场景。适合数据量较大的场景。

   c.串口中断 + DMA接收 + DMA发送,该方法使用了DMA可以释放CPU的占用,CPU由每次接收触发中断变成接收完毕触发或DMA发送完毕触发,减少中断触发频率。适合数据量较大的场景。

  以上三种方式各有自己的优缺点,DMA串口尽管处理效率看起来是最高的,但是耗费了DMA资源。而DMA资源往往是单片机较为稀缺的资源。因此还是需要根据项目需求合理选择。本次主要是在资源富裕的情况下使用DMA进行串口的收发测试。

二、串口DMA接收相关思路

  DMA串口接收,大致流程如上图所示;因为本次使用的是STM32F1系列的DMA,只有传送任务完成一半的中断(DMA1_IT_HTx)。因此借用HTx中断实现双缓冲区效果进行接收。[本篇只针对串口DMA进行解析流程,DMA相关基础知识请参考其他文章]。

  如上图中,需要准备一个缓冲区(即用户自己管理的数组),这个数组需要在程序编译后有确定的地址(不要使用会被析构掉的数组地址)。原因是:开启DMA传送后,DMA传送是不经过CPU的直接地址之间的快速传送。此时CPU也会同步正常运行,而如果该地址会因CPU程序运行用作别的功能使用,则会使DMA传送数据出错。

  简单点来说,串口接收DMA传送过程类似:CPU跟DMA控制器下发一个指令说,等串口收到数据了,你把收到的数据搬运到仓库里存着(发送缓冲区),这次就先搬运100个。CPU下完这个指令后,就能去执行别的指令处理任务,DMA同时也会开始搬运的工作;这样就可以实现CPU等100个数据都接收完了,再一次性处理,降低了CPU一个一个数据处理的工作量。这里的等串口收到数据了,就是DMA请求,串口每接收完毕一个数据,就会通知DMA,我收到了一个数据,你可以来搬运了。而100个就是DMA传送的数量,即这次DMA只搬运100个数据就完成本次DMA传送。

  这样的好处就是,CPU只在下发DMA指令的时候占用一瞬间,便可以去干别的工作,等DMA传送完毕之后再进行数据批量处理,极大减少了CPU占用的时间。那么想想一下如下的场景,假如一次DMA传送完成了,CPU正在处理数据的时间,串口又来了新数据,这时候因为数据还没处理完,发送缓冲区还不能释放出来进行下一次DMA接收,这个时候就会导致数据丢失。于是这种机制还需要进一步改进。

在这里插入图片描述

  假如我们将接收缓冲区Buffer人工区分成两个片区,CPU要一次性处理100个数据,分为每次处理50个数据,再借助半满中断就能实现如上图的过程。

  1、CPU下发DMA传送指令,传送100个数据。此时设置接收缓冲区大小为100个。

  2、当接收到50个数据时,即接收的数据填满接收缓冲区前50个数据时,进入DMA半满中断。在半满中断中通知CPU将接收到的数据搬入FIFO中(此时DMA仍旧在向后50个地址写入数据)。

  3、当接收到100个数据时,进入DMA完成中断。在DMA完成中断中,首先重置DMA继续进入下一轮的100个数据接收状态(这时由于接收缓冲区前50个地址数据已经被搬入FIFO中,可以直接使能DMA进行继续接收),然后CPU将后50个数据搬入FIFO中。

  4、CPU在前台对FIFO中数据进行逐一处理。

  可以看到,如果整体流程变为这样时,CPU提前对数据进行搬运,耗费一定CPU资源,但能够做到串口丢数据最少的操作。当然这样能够持续进行的前提一定是CPU将数据搬入FIFO的时间,要小于DMA将数据搬入数组的时间。以串口波特率为115200(bit/s)为例,数据位8位,停止位1位,总计一个有效数据为9位。接收1个数据耗时:1/115200 * 9 = 7.8e-5 (该时间还未加上DMA搬运时间);而单片机假如按照1M的速率进行处理1Byte数据,也能到达1e-6速率。因此CPU肯定是能够在DMA传送完毕之前,完成前半个缓冲区域的数据处理。

  即使这样,目前还剩最后一个问题需要解决;假设我们串口目前只有80个数据需要接收,按照我们优化后的程序去执行,想想会发生什么?由于我们CPU给DMA下发的是接收100个数据才完成。在接收到50个数据时,进入半满中断搬运;然后继续接收后面的50个数据。显而易见,DMA只能再接收到30个数据,便再也等不到数据了。这时会一直等不到DMA完成中断,等不到DMA完成中断,DMA接收的数据便无法被存在入用户FIFO中。造成数据本次不能完整的接收(只有再接收20个数据,凑满100个才能触发搬运)。

  为了解决该问题,需要加入串口空闲中断,即在串口空闲时,通知CPU来将数据搬运到FIFO中,同时重置DMA接收。这样便能接收不定长的数据,下一次数据变成30个也能完成接收。而串口空闲中断需要的信息有:当前接收缓冲区的首地址(是起始地址?还是一半的地址?)和需要搬移的数量。缓冲区首地址可以通过变量进行记录,DMA启动后起始地址为0,DMA半满中断触发后起始地址为缓冲区的一半。搬移数据量可以通过DMA计数器获得,DMA内部存在一个计数器,比如我们这里给的是初值100,DMA控制器每搬运一个,该值就会自减1,如变为99,98,97……。计数器变为50,触发半满中断;直到计数器变为0,则触发DMA完成中断。这样我们便可以通过获取该计数器值来计算本次DMA已经传送了多少数据。如上面所说的80个数据,此时计算规则为:100-20-50 = 30;100为DMA总传送数量;20为当前DMA计数器的值;50为buffer的起始地址(前50个数据已经传送完毕),程序部分如下:

#define USART1_DMA_BUF_MAX_LEN 128
#define USART1_DMA_FIFO_MAX_LEN 512_fifo_t   dma_rx_fifo_uart1;
uint8_t   dma_uart1_fifo_buf[USART1_DMA_FIFO_MAX_LEN]={0};
uint8_t   dma_uart1_rx_buf[USART1_DMA_BUF_MAX_LEN] = {0};
uint8_t   dma_uart1_tx_buf[USART1_DMA_BUF_MAX_LEN] = {0x01,0x02};
uint8_t   lock_state = 0;uart_dev uart_dev_uart1 =
{USART1 ,   //串口外设1115200 ,   //buad&dma_rx_fifo_uart1,  //DMA_RX_FIFOdma_uart1_rx_buf  ,  //DMA_RX_BUFdma_uart1_tx_buf     //DMA_TX_BUF
};/*** @brief  初始化串口GPIO* @param  * @retval 0:成功; 1:失败
*/
uint32_t uart_dev_gpio_init()
{GPIO_InitTypeDef  gpio_initstruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//PA9	TXDgpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP;gpio_initstruct.GPIO_Pin = GPIO_Pin_9;gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &gpio_initstruct);//PA10	RXDgpio_initstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;gpio_initstruct.GPIO_Pin = GPIO_Pin_10;gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &gpio_initstruct);return 0;
}/*** @brief  初始化串口外设* @param  * @retval 0:成功; 1:失败
*/
uint32_t uart_dev_uart_init (uart_dev* uart_dev)
{  USART_InitTypeDef usart_initstruct;NVIC_InitTypeDef  nvic_initstruct;RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);usart_initstruct.USART_BaudRate = uart_dev ->baud;usart_initstruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;		//无硬件流控usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;						//接收和发送usart_initstruct.USART_Parity = USART_Parity_No;									//无校验usart_initstruct.USART_StopBits = USART_StopBits_1;								    //1位停止位usart_initstruct.USART_WordLength = USART_WordLength_8b;							//8位数据位USART_Init(uart_dev->USARTx, &usart_initstruct);USART_ClearFlag(USART1, USART_FLAG_TC|USART_FLAG_RXNE);                       //清除默认标志位(主要是TC)USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);                                  //串口DMA接收使能USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);                                  //串口DMA发送使能USART_Cmd(USART1, ENABLE);													  //使能串口USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);					              //使能空闲中断nvic_initstruct.NVIC_IRQChannel = USART1_IRQn;nvic_initstruct.NVIC_IRQChannelCmd = ENABLE;nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 0;nvic_initstruct.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&nvic_initstruct);return 0;
}/*** @brief  初始化串口DMA* @param  * @retval 0:成功; 1:失败
*/
uint32_t uart_dev_DMA_init(uart_dev* uart_dev)
{NVIC_InitTypeDef  nvic_initstruct;DMA_InitTypeDef   DMA_USART1_InitStructure;RCC_AHBPeriphClockCmd (RCC_AHBPeriph_DMA1,ENABLE);DMA_DeInit(DMA1_Channel5);DMA_USART1_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &USART1->DR           ;    //USART1数据接收寄存器DMA_USART1_InitStructure.DMA_MemoryBaseAddr     = (uint32_t) uart_dev ->dma_rx_buff;    //接收寄存器区间地址DMA_USART1_InitStructure.DMA_DIR                = DMA_DIR_PeripheralSRC      ;          //从外设读取; 外设 ——> 内存DMA_USART1_InitStructure.DMA_BufferSize         = USART1_DMA_BUF_MAX_LEN     ;          //传送长度DMA_USART1_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable  ;          //外设地址不增DMA_USART1_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable       ;          //内存地址自增DMA_USART1_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;          //传送数据为8位DMA_USART1_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte    ;          //MemoryDataSizeDMA_USART1_InitStructure.DMA_Mode               = DMA_Mode_Normal            ;          //普通模式 单次DMA_USART1_InitStructure.DMA_Priority           = DMA_Priority_VeryHigh      ;          //传送优先级非常高DMA_USART1_InitStructure.DMA_M2M                = DMA_M2M_Disable            ;          //从外设触发DMA_Init(DMA1_Channel5,&DMA_USART1_InitStructure);//开启DMADMA_Cmd(DMA1_Channel5,DISABLE);DMA_SetCurrDataCounter(DMA1_Channel5, USART1_DMA_BUF_MAX_LEN);DMA_Cmd(DMA1_Channel5,ENABLE);DMA_ClearFlag(DMA1_IT_TC5|DMA1_IT_HT5); DMA_ITConfig(DMA1_Channel5,DMA_IT_HT|DMA_IT_TC|DMA_IT_TE,ENABLE);  //使能传送一半及全部传送完毕中断nvic_initstruct.NVIC_IRQChannel = DMA1_Channel5_IRQn;nvic_initstruct.NVIC_IRQChannelCmd = ENABLE;nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 0;nvic_initstruct.NVIC_IRQChannelSubPriority = 2;NVIC_Init(&nvic_initstruct);   return 0;
}/*** @brief  清除DMAx通道* @param  * @retval 0:成功; 1:失败
*/uint32_t uart_dev_clear_dma_channel(DMA_Channel_TypeDef* DMAy_Channelx)
{DMA_Cmd(DMAy_Channelx,DISABLE);DMA_SetCurrDataCounter(DMAy_Channelx,USART1_DMA_BUF_MAX_LEN);DMA_Cmd(DMAy_Channelx,ENABLE);return 0;
}/*
************************************************************
*	函数名称:	USART1_IRQHandler
*
*	函数功能:	串口1收发中断
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/
void USART1_IRQHandler(void)
{uint8_t temp = 0;uint16_t recv_size = 0;if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断{temp = USART1->DR;USART_ClearFlag(USART1, USART_FLAG_RXNE);}else if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) //空闲中断{temp = USART1->SR; //清除中断标志temp = USART1->DR;USART_ClearFlag(USART1, USART_IT_IDLE);recv_size = USART1_DMA_BUF_MAX_LEN - DMA_GetCurrDataCounter(DMA1_Channel5) - uart_dev_uart1.dma_rx_buff_curr_addr; //重置DMAuart_dev_clear_dma_channel(DMA1_Channel5);fifo_write(uart_dev_uart1.dma_rx_fifo, &uart_dev_uart1.dma_rx_buff[uart_dev_uart1.dma_rx_buff_curr_addr], recv_size);uart_dev_uart1.dma_rx_buff_curr_addr = 0;     }}/*
************************************************************
*	函数名称:	DMA1_Channel5_IRQHandler(串口接收中断)
*
*	函数功能:	DMA1_Channel5中断
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/
void DMA1_Channel5_IRQHandler(void)
{uint16_t recv_size = 0;if(DMA_GetITStatus(DMA1_IT_HT5) != RESET)   //DMA传送一半中断{DMA_ClearITPendingBit(DMA1_IT_HT5);    // 清除传输传送一半中断中断标志位	//搬移前半个buf数据recv_size = USART1_DMA_BUF_MAX_LEN - DMA_GetCurrDataCounter(DMA1_Channel5); fifo_write(uart_dev_uart1.dma_rx_fifo, uart_dev_uart1.dma_rx_buff, recv_size); //更新当前地址uart_dev_uart1.dma_rx_buff_curr_addr += recv_size;}else if(DMA_GetITStatus(DMA1_IT_TC5) != RESET)  //DMA传送完成中断{DMA_ClearITPendingBit(DMA1_IT_TC5);    // 清除传输完成中断标志位	recv_size = USART1_DMA_BUF_MAX_LEN - DMA_GetCurrDataCounter(DMA1_Channel5) - uart_dev_uart1.dma_rx_buff_curr_addr;      //重置DMAuart_dev_clear_dma_channel(DMA1_Channel5);fifo_write(uart_dev_uart1.dma_rx_fifo, &uart_dev_uart1.dma_rx_buff[uart_dev_uart1.dma_rx_buff_curr_addr], recv_size);uart_dev_uart1.dma_rx_buff_curr_addr = 0;}else if(DMA_GetITStatus(DMA1_IT_TE5) != RESET){UsartPrintf(USART2," DMA ERROR \r\n");}}

三、串口DMA发送相关思路

  如果理解了发送原理,接收原理相对来说便简单的多;将需要发送的数据填入发送buff,通知DMA将数据进行发送便可以实现。这里需要注意的是因为串口接收和发送DMA同属于一个DMA的不同通道,两个是没法同时进行的,如果是收发双工同时使用这一个DMA,可能会地效率有所影响。

  需要注意的是,因为发送是非阻塞进行的;一定要获取该次发送完成后,再进行下一次发送;当然若发送大量数据可以仿照接收,也使用类似与双buffer的方式进行发送。

/*** @brief  初始化串口DMA 并发送数据(发送串口)* @param  * @retval 
*/
void uart_dev_DMA_tx_data(uart_dev* uart_dev )
{NVIC_InitTypeDef  nvic_initstruct;DMA_InitTypeDef   DMA_USART1_InitStructure;//使能DMA1 时钟RCC_AHBPeriphClockCmd (RCC_AHBPeriph_DMA1,ENABLE);DMA_DeInit(DMA1_Channel4);DMA_USART1_InitStructure.DMA_PeripheralBaseAddr = (uint32_t) &USART1->DR           ;    //USART1数据接收寄存器DMA_USART1_InitStructure.DMA_MemoryBaseAddr     = (uint32_t) uart_dev ->dma_tx_buff;    //发送寄存器区间地址DMA_USART1_InitStructure.DMA_DIR                = DMA_DIR_PeripheralDST            ;  //往外设发送; 内村 ——> 外设DMA_USART1_InitStructure.DMA_BufferSize         = USART1_DMA_BUF_MAX_LEN           ;  //传送长度DMA_USART1_InitStructure.DMA_PeripheralInc      = DMA_PeripheralInc_Disable  ;  //外设地址不增DMA_USART1_InitStructure.DMA_MemoryInc          = DMA_MemoryInc_Enable       ;  //内存地址自增DMA_USART1_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;  //传送数据为8位DMA_USART1_InitStructure.DMA_MemoryDataSize     = DMA_MemoryDataSize_Byte    ;  //MemoryDataSizeDMA_USART1_InitStructure.DMA_Mode               = DMA_Mode_Normal            ;  //普通模式 单次DMA_USART1_InitStructure.DMA_Priority           = DMA_Priority_High          ;  //传送优先级高DMA_USART1_InitStructure.DMA_M2M                = DMA_M2M_Disable            ;  //从外设触发DMA_Init(DMA1_Channel4,&DMA_USART1_InitStructure);//开启DMADMA_Cmd(DMA1_Channel4,DISABLE);DMA_SetCurrDataCounter(DMA1_Channel4, USART1_DMA_BUF_MAX_LEN);DMA_Cmd(DMA1_Channel4,ENABLE);DMA_ClearFlag(DMA1_IT_TC4); DMA_ITConfig(DMA1_Channel4,DMA_IT_TC|DMA_IT_TE,ENABLE);  //使能传送一半及全部传送完毕中断nvic_initstruct.NVIC_IRQChannel = DMA1_Channel4_IRQn;nvic_initstruct.NVIC_IRQChannelCmd = ENABLE;nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 0;nvic_initstruct.NVIC_IRQChannelSubPriority = 3;NVIC_Init(&nvic_initstruct);   }/*
************************************************************
*	函数名称:	DMA1_Channel4_IRQHandler
*
*	函数功能:	DMA1_Channel4中断
*
*	入口参数:	无
*
*	返回参数:	无
*
*	说明:		
************************************************************
*/void DMA1_Channel4_IRQHandler(void)
{uint16_t recv_size = 0;if(DMA_GetITStatus(DMA1_IT_TC4) != RESET)   //DMA传送完成{uart_dev_uart1.dma_tx_idle_state = 1;DMA_ClearITPendingBit(DMA1_IT_TC4);    // 清除传输完成	}else if(DMA_GetITStatus(DMA1_IT_TE4) != RESET){UsartPrintf(USART2," DMA uart tx ERROR \r\n");}}
http://www.zhongyajixie.com/news/22848.html

相关文章:

  • 高州做网站免费网络推广100种方法
  • 五金配件店 东莞网站建设百度自动点击器
  • 怎么做淘宝网站步骤爱站网站seo查询工具
  • 湖北做网站公司百度经验官网首页
  • 今天新闻摘抄十条南京企业网站排名优化
  • wordpress主题在哪里设置aso应用商店优化
  • 天河做网站要多少钱上海网络推广联盟
  • 网站建设网站建设 网站制作关键词app
  • 定制开发app的设计流程单页关键词优化费用
  • 网站源码 预览seo优化专员工作内容
  • 容桂品牌网站建设优惠进入百度搜索首页
  • 做美股的数据网站seo教程搜索引擎优化
  • 腾讯网静态网站建设全网营销一站式推广
  • 手机上如何做微电影网站刷关键词优化排名
  • b2b电子商务网站的模式有哪些24小时最新国际新闻
  • 制作二维码的软件app杭州seo博客
  • java主要就是做网站吗旺道seo推广系统怎么收费
  • 网站的关键词搜索怎么做微博推广效果怎么样
  • 网站模板 整站源码职业技术培训机构
  • 一起做网站郑州优帮云首页推荐
  • 西安 北郊网站建设站内关键词排名软件
  • 聊城 网站制作热门搜索
  • 做外包软件的网站爱站网关键词排名
  • 做住宿网站挣钱吗微博搜索引擎优化
  • 中国商务部市场建设司网站郑州网络优化实力乐云seo
  • 做网站新科网站建设网站怎么进入
  • 我想注册网站我怎么做班级优化大师的利和弊
  • 有利于优化的网站模板怎么做神马搜索排名seo
  • wordpress 无法搜索结果重庆seo网站建设
  • 韩国食品网站设计欣赏seo服务是什么意思