STM32 不依赖中断的DMA串口

  蓝牙的HCI是依赖串口的,音频通信的特点在于:

  1、不定长度

  2、数据量庞大,速度至少在1M 一般2M或以上,才能获得比较好的效果。

  因为不熟悉stm32,所以看了网上很多的DMA串口的例子,都是依赖中断的。中断的问题在于无法保证收到完整的数据包,但听说闲置中断可以解决这个问题,可能效果更好一点。但为了让代码有更好的跨平台特性,我还是想不用中断解决高速的、不定长度的串口收发问题,这里提出一种解决办法。

   uint8_t Uart_Rx[UART_RX_LEN];

 DMA_InitTypeDef DMA_InitStructure;
 DMA_DeInit(DMA1_Channel3);
 DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)(&USART3->DR);
 DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)Uart_Rx;
 DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; 
 DMA_InitStructure.DMA_BufferSize = UART_RX_LEN; 
 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; 
 DMA_InitStructure.DMA_Mode = DMA_Mode_Circular
 DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh; 
 DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; 
 DMA_Init(DMA1_Channel3,&DMA_InitStructure);
 DMA_Cmd(DMA1_Channel3,ENABLE);

  DMA_Mode 选择 用回环模式。其实我一直认为正常模式的DMA不如回环模式,原因在于:在使用DMA_Mode_Nomal的时候,接收完数据必须重置DMA,那么在遇到有些包一个分两次接收的时候就会显得非常不灵活。必须把前半个包先拷贝出来,然后和后面的包拼在一起,而且我怀疑DMA重置的过程可能会导致丢数据(不确定)。回环模式的优点在于不需要重新重置DMA,理论上只要处理够快就不会丢数据。 但是我觉得stm32,对DMA_Mode_Circular 的支持并不是很好。

012345678910111213141516171819

假设当前的DMA bUFF有20个长度。在向内存数据拷贝的时候,从前向后的当拷贝完buff[19],后自动拷贝buff[0]。

固态库的定义如下

typedef struct
{
  __IO uint32_t CCR;
  __IO uint32_t CNDTR;  //DMA待输入的数量 ,假设当前DMA buff长度为20。 若收到两字节的数据,则该硬件寄存器的值为20-2=18。

              //即 实际DMA收到的数据 为 DMA buff_len-CNDTR
  __IO uint32_t CPAR;    //硬件地址
  __IO uint32_t CMAR;    //内存地址
} DMA_Channel_TypeDef;

我们无法告诉哪一段内存是已经处理过的,哪一段是没处理过的。

假设,DMA收到 18个字节的数据,我们已经处理10个字节的数据,CNDTR依然是18。于是,用软件实现了该功能,

 

/*****DMA_uart.C********/

 

#include "DMA_uart.h"

 

struct DMA_uart_publish* DMA_create_publish(DMA_Channel_TypeDef *dc)
{
 struct DMA_uart_publish* du= rt_malloc(sizeof(DMA_Channel_TypeDef));
 du->core_uart_rrptr = (rt_uint8_t *)dc->CMAR;
 du->core_uart_readdr = (rt_uint8_t *)(dc->CMAR+dc->CNDTR);
 du->s_channel = dc;
 return du;
}

 


void DMA_usrt_get_buff(struct DMA_uart_publish* du,rt_uint8_t *buff, rt_uint32_t size)
{
 while(size--)
 {
    *buff++ = *du->core_uart_rrptr++;    //core_uart_rrptr标记当前的读指针。
    if(du->core_uart_rrptr>=du->core_uart_readdr)  //DMA回环
    {  
        du->core_uart_rrptr = (rt_uint8_t*)du->s_channel->CMAR;
    }
 } 
}

/*****DMA_uart.H********/

#include <rtthread.h>
#include <stm32f10x.h>
/*
struct DMA_uart_ops
{
 void (*get_hci_head)(struct DMA_uart_handle *);
};
*/

struct DMA_uart_publish
{
 rt_uint8_t *core_uart_rrptr; //DMA read base
 rt_uint8_t *core_uart_readdr;
 //rt_uint8_t *core_uart_curptr; //DMA current ptr
 DMA_Channel_TypeDef *s_channel; 
};
void DMA_usrt_get_buff(struct DMA_uart_publish* du,rt_uint8_t *buff, rt_uint32_t size);
struct DMA_uart_publish* DMA_create_publish(DMA_Channel_TypeDef *dc);

#endif

 

//函数数据正确的前提是处理速度够。 

//得到未处理的数据的长度

uint16_t DMA_uart_get_rx_length(struct DMA_uart_publish* du)
{
 uint16_t rx_len = DMA_GetCurrDataCounter(du->s_channel) ;
 if ((uint32_t)du->core_uart_rrptr - du->s_channel->CMAR > rx_len)
 {
  rx_len = rx_len +  (uint32_t)(du->core_uart_readdr - du->core_uart_rrptr);
 }
 else
 {
  rx_len = rx_len - ((uint32_t)du->core_uart_readdr - du->s_channel->CMAR);
 }
 return rx_len;
}

  总结:此方法还是仅适合于串口做主要功能的应用。在串口数据量不大时,应采用中断的方式。

转载于:https://www.cnblogs.com/HowToRaid/p/5061284.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值