蓝牙的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 的支持并不是很好。
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
假设当前的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;
}
总结:此方法还是仅适合于串口做主要功能的应用。在串口数据量不大时,应采用中断的方式。
原文:http://www.cnblogs.com/HowToRaid/p/5061284.html