USART 配置为DMA模式的接收和DMA模式的发送
DMA接收模式配置介绍:
- DMA的接收模式就是外界对单片机的串口进行写入,由DMA总线把写入到USART->DR寄存器的字节数据搬运到数组缓冲区当中。
- 一般情况下需要配合着USART的空闲中断(USART_IT_IDLE)运行。
DMA发送模式配置介绍:
- DMA的发送模式就是把数组缓冲区当中的数据通过DMA总线不断的往USART->DR寄存器写入,以便达到不需要CPU干预的发送模式。
- 一般情况下需要配合着USART的发送完成中断(USART_IT_TC)执行。
注意事项都在代码当中加着备注,可直接COPY跑,不在拗述。在上代码之前还是希望可以先看一下这几个状态标志位的作用是什么:
这几个标志位可以帮助理解后面的中断服务函数处理的过程。
#下面直接上代码,可直接COPY运行,其中注释的地方有bug希望额外关注#
char Usart_Rx_Buf[USART_RBUFF_SIZE];
char sendbuf[1024];
/**
* @brief USART1配置为DMA方式接收数据和发送数据
* @param 波特率
* @retval 无
* @attention
*
* 当串口配置为DMA模式的时候需要结合USART的空闲中断和发送完成中断使用
* USART——DMA接收模式,即从USART->DR寄存器数据搬运到数组缓冲区,串口
* 配置为空闲中断。
* 根据数据手册可知,当外界往USART->DR寄存器写入一字节完成时,RXNE标
* 志位会被硬件置一,此时DMA启动不断的搬运数据到数组缓冲区,因为RXNE标志位
* 清除需要手动清零,所以当总线不再忙的时候也即没有数据的时候的,USART1串口
* 响应空闲中断,这个时候进入中断服务函数。
* 通过对SR状态寄存器的读,和USART-DR寄存器的读,可以清除空闲标志位。重新配
* 置DMA通道的数据个数,再次开启DMA接收模式,等待外界发送数据,单片机接收数据
* 即可
*
* 串口配置为DMA发送模式,把数组缓冲区的数据不断的搬运到USART->DR寄存器。
* 根据数据手册得知,当USART->DR内的值被移位到移位寄存器的时候,TXE标志位
* 被置1,如果使能了DMA,则DMA会把内存的数据搬运到USART->DR,对DR寄存器的写操
* 作会使TXE置0,当新的数据被移位出去之后,TXE再次被置1,这时DMA又可以再次搬运
* 一字节。
*/
void USART1_DMA_Config(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
DMA_InitTypeDef DMA_InitStructure;
/* The DMA bus is mounted on the AHB bus.*/
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;
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);
/* 这里有一个这样的bug,即当开启TC发送完成中断之后,硬件会自动的
发送一个空字符并响应一次发送完成中断,若想正常的发送出去,则必须
先开启USART串口才行 */
/*先使能串口的功能*/
USART_Cmd(USART1, ENABLE);
/* 再开启发送完成中断,使其硬件正常发送空字符*/
USART_ITConfig(USART1, USART_IT_TC, ENABLE);
/* 开启空闲中断 */
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);
/* 开启串口的DMA功能 */
USART_DMACmd(USART1, USART_DMAReq_Tx|USART_DMAReq_Rx, ENABLE);
/* 配置USART1_RX所在的DMA通道5*/
DMA_DeInit(DMA1_Channel5);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART_DR_ADDRESS;
DMA_InitStructure.DMA_MemoryBaseAddr =(uint32_t)Usart_Rx_Buf;;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
DMA_InitStructure.DMA_BufferSize = USART_RBUFF_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;
/* 这里为DMA接收模式,配置的为循环模式,顾名思义当数据超过规定的个数之后循环覆盖接收 */
DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel5, &DMA_InitStructure);
DMA_Cmd(DMA1_Channel5, ENABLE);
/* 配置USART1_TX所在的DMA通道4 */
DMA_DeInit(DMA1_Channel4);
DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)USART_DR_ADDRESS;
DMA_InitStructure.DMA_MemoryBaseAddr =(uint32_t)sendbuf;
DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
DMA_InitStructure.DMA_BufferSize = 0;
DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;
DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
/* DMA发送模式,配置的普通模式,即发送一次即可,如果是循环模式则不断的循环发送 */
DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
DMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
DMA_Init(DMA1_Channel4, &DMA_InitStructure);
/*清除所有的DMA标志位*/
DMA_ClearFlag(DMA1_FLAG_GL5);
/* transmisson error,开启DMA1通道5RX接收传输错误中断 */
DMA_ITConfig(DMA1_Channel5, DMA_IT_TE, ENABLE);
/* transmisson error,开启DMA1通道4TX发送传输错误中断 */
DMA_ITConfig(DMA1_Channel4, DMA_IT_TE, ENABLE);
/* 注意发送or接收异常,异常就异常了并没处理异常之后怎么办*/
}
u16 USART1_DMA_SendData(void* buffer, u16 size)
{
/* 如果数据长度为0,则不再发送数据*/
if(!size)
return 0;
/* 每当传输一个字节则CNDTR就会减1,当为0的时候退出while()循环 */
while(DMA_GetCurrDataCounter(DMA1_Channel4));
if(buffer)
/* 把数据copy到sendbuf缓冲区当中,也就是配置的DMA发送Source地址 */
memcpy(sendbuf, buffer,(size > 1024?1024:size));
/* 开启DMA内存到外设的传输 */
DMA_Cmd(DMA1_Channel4, DISABLE);
DMA1_Channel4->CNDTR = size;// 设置发送长度
DMA_Cmd(DMA1_Channel4, ENABLE); // 启动DMA发送
return size;
}
void USART1_IRQHandler(void)
{
if (USART_GetITStatus (USART1, USART_IT_IDLE ) != RESET ) /* 查询状态寄存器的同时就清除了标志位 */
{
DMA_Cmd(DMA1_Channel5, DISABLE);
/* 清除标志位,返回DR寄存器的值就是读操作,这样就关掉了IDLE中断的标志位 */
USART_ReceiveData ( USART1 );
DMA_ClearFlag( DMA1_FLAG_TC5 );
DMA1_Channel5->CNDTR = USART_RBUFF_SIZE;
DMA_Cmd(DMA1_Channel5, ENABLE);
}
if(USART_GetITStatus(USART1,USART_IT_TC)!= RESET) //全部数据发送完成,产生该标记
{
/* 根据数据手册得知,必须对USART->DR寄存器写操作才可以清除发送完成标志位*/
USART_ClearITPendingBit(USART1, USART_IT_TC);
DMA_Cmd(DMA1_Channel4, DISABLE); // 关闭DMA
DMA1_Channel4->CNDTR=0; // 清除数据长度
}
}