串口数据使用DMA通道传输
第一次写 希望各位博友指正
本次使用的是STM32F1的主控芯片,资料可在ST官网下载
DMA使用的目的:不用DMA发送是需要单片机实时参du与,由单片机一个一个地发送数据并进执行监控。但是如果用DMA,设置了起始地址,数据大小等参数后,就直接由专门的一个DMA模块进行数据发送,发送过程中单片机无需参与。发送完后会产生中断告知单片机。由此可知用DMA可以节省单片机资源,让单片可以在同一时间里干更多事。
本次为了减少CPU的占用,在配置串口的时候使用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,可用中断可不用
遇到问题可随时交流,欢迎各位大佬指正,互相学习。
上一篇: spark wordcount 第一个spark 程序
下一篇: 基于mysql数据库的密码问题详解