串口数据使用DMA通道传输

第一次写 希望各位博友指正

本次使用的是STM32F1的主控芯片,资料可在ST官网下载
DMA使用的目的:不用DMA发送是需要单片机实时参du与,由单片机一个一个地发送数据并进执行监控。但是如果用DMA,设置了起始地址,数据大小等参数后,就直接由专门的一个DMA模块进行数据发送,发送过程中单片机无需参与。发送完后会产生中断告知单片机。由此可知用DMA可以节省单片机资源,让单片可以在同一时间里干更多事。

本次为了减少CPU的占用,在配置串口的时候使用DMA进行数据的收发。
不多说 直接上干货!!!串口数据使用DMA通道传输_第1张图片
本次使用的DMA1的通道4和通道5,图中已标注。
干货代码直接上:

串口配置:

unsigned char USART1_Tx_Buffer[USART_DMA_BUFFER_SIZE];
unsigned char USART1_Rx_Buffer[USART_DMA_BUFFER_SIZE];
unsigned char USART2_Tx_Buffer[USART_DMA_BUFFER_SIZE];
unsigned char USART2_Rx_Buffer[USART_DMA_BUFFER_SIZE];
#define	USART1_TX_PORT									GPIOA
#define	USART1_TX_PIN									GPIO_Pin_9

#define	USART1_RX_PORT									GPIOA
#define	USART1_RX_PIN             						GPIO_Pin_10
//语音模块通讯通道
void USART1_Init(u16 BAUD)
{
    GPIO_InitTypeDef GPIO_InitStructure;                      
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);

    USART_DeInit(USART1);
    //UART1_TX   									PA9
    GPIO_InitStructure.GPIO_Pin = USART1_TX_PIN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_Init(USART1_TX_PORT, &GPIO_InitStructure);

    //UART1_RX	  									PA10
    GPIO_InitStructure.GPIO_Pin = USART1_RX_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
    GPIO_Init(USART1_RX_PORT, &GPIO_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//中断源
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    USART_InitStructure.USART_BaudRate = BAUD;//可直接根据使用波特率大小直接设置 一般通常用9600
    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);

    //IT config
    USART_ITConfig(USART1,USART_IT_TC,DISABLE);
    USART_ITConfig(USART1,USART_IT_RXNE,DISABLE);
    USART_ITConfig(USART1,USART_IT_IDLE,ENABLE);//空闲中断 关系着后面的DMA配置
    USART_Cmd(USART1, ENABLE);

    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
    USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);
}

DMA配置

void USART1_DMA_CONFIG(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

    DMA_DeInit(DMA1_Channel5);   //uart1 RX should use dma1 ch5
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;	// 初始化外设地址
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_Rx_Buffer;	// 内存地址
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; //外设作为数据来源
    DMA_InitStructure.DMA_BufferSize = USART_DMA_BUFFER_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_PeripheralDataSize_Byte;	// 内存字节宽度
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;	//正常模式,即满了就不在接收了,而不是循环存储
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;	//通道优先级高
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;  //内存与外设通信  而非内存到内存 
    DMA_Init(DMA1_Channel5, &DMA_InitStructure);
    DMA_Cmd(DMA1_Channel5,ENABLE);

    DMA_DeInit(DMA1_Channel4);	//uart1 TX should use dma1 ch4
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&USART1->DR;
    DMA_InitStructure.DMA_MemoryBaseAddr = (u32)USART1_Tx_Buffer;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //存储器作为数据来源
    DMA_InitStructure.DMA_BufferSize = USART_DMA_BUFFER_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_PeripheralDataSize_Byte;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
    DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_Init(DMA1_Channel4, &DMA_InitStructure);

    NVIC_InitStructure.NVIC_IRQChannel = DMA1_Channel4_IRQn;//通道4作为中断源
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2 ;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    DMA_ITConfig(DMA1_Channel4,DMA_IT_TC,ENABLE);
}


## 中断函数:
void USART1_IRQHandler(void)//串口1的中断服务函数
{
    uint32_t length;

    if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)
    {

        length = USART1->SR;
        length = USART1->DR;
        length = length;
        DMA_Cmd(DMA1_Channel5,DISABLE);

        length = USART_DMA_BUFFER_SIZE - DMA_GetCurrDataCounter(DMA1_Channel5);

        USART_ClearITPendingBit(USART1, USART_IT_IDLE);
        DMA_SetCurrDataCounter(DMA1_Channel5,USART_DMA_BUFFER_SIZE);
        DMA_Cmd(DMA1_Channel5,ENABLE);
    }
}
void DMA1_Channel4_IRQHandler(void)//DMA通道4的中断服务函数
{
    if(DMA_GetITStatus(DMA1_FLAG_TC4))
    {
        DMA_ClearFlag(DMA1_FLAG_GL4|DMA1_FLAG_TC4|DMA1_FLAG_TE4  );
        DMA_Cmd(DMA1_Channel4, DISABLE);
    }
}

特别强调:串口的空闲中断IDLE清除中断标志位和平常的接收发送中断标志位清除方法不一样,如代码上所写,先读取SR,再读取DR才能清除空闲中断标志位,而平常的接收发送中断只需要读取DR即可。

其他辅助函数:

//usart1发送函数
void usart1_send_char(u8 data)
{
    USART_SendData(USART1, (u8) data);
    while (USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
    {}
}

//usart1 dma发送函数
void dma_usart1_send(int length)
{
    DMA1_Channel4->CNDTR=length;//比如一共0-5 6个字节 数据 此时length=6 需要在第五个数据赋值后加1
    DMA_Cmd(DMA1_Channel4, ENABLE);
}

配置完成了现在就可以使用dma_usart1_send()函数发送所需要的的数据,如果接受数据使用的是DMA1的通道5,可用中断可不用

遇到问题可随时交流,欢迎各位大佬指正,互相学习。

你可能感兴趣的:(外设使用)