欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

串口数据使用DMA通道传输

程序员文章站 2024-02-21 20:57:16
...

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

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

本次为了减少CPU的占用,在配置串口的时候使用DMA进行数据的收发。
不多说 直接上干货!!!串口数据使用DMA通道传输
本次使用的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);
}
}

其他辅助函数:

//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,可用中断可不用

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

相关标签: 外设使用 stm32