STM32F1使用DMA方式驱动串口和ADC

一、概述

DMA(直接存储器访问)传输方式无需CPU直接控制传输,也没有中断处理方式需要保存现场和恢复现场,其通过硬件为RAM和I/O设备开辟一条类似队列的数据通道来直接传输数据,可以准确快速地传输数据,能大大提高CPU的效率。但是每一时刻只允许DMA的其中一条通道传输数据。下面是STMF1的DMA1的通道一览表。

UART(全双工通信),就是我们常说的串口,但准确来说UART是指通用异步收发器,还有一种叫做USART(通用同步和异步收发器)。一般来说UART由波特率发生器、UART接收器和UART发送器组成,有三条信号线(Rx、Tx和GND)。这里说一下同步和异步的区别,同步是指发送方发出数据后,等接收方回应下一个数据包的通信方式;异步是指发送方发出数据后,不等接收方的回应,接着发出下个数据包的通信方式。

UART的通信协议一般包含:

起始位:先发出一个逻辑“0”的信号,表示要开始传输数据了;

数据位:紧接着起始位后,可以组织8位数据;

奇偶校验位:数据位之后加这个位,使“1”的位数可以自己构建成奇数或者偶数,用来校验数据传输的准确性;

停止位:最后跟一个逻辑“1”的信号,表示这一帧数据结束了。他可以占1、1.5、2个位;

空闲位:逻辑“1”的状态,表示这条链路没有数据传输;

STM32F1拥有1~3个ADC(模拟数字转换器),这些ADC可以独立使用,也可以使用双重模式以提高采样率。STM32的ADC是12位逐次逼近型的模拟数字转换器。它有18个通道,可以测量16个外部和2个内部信号源。各通道的A/D转换可以单次、连续、扫描或间断模式执行。ADC的结果可以左对齐或右对齐存储在16位数据寄存器中。STM32的ADC最大的转换速率为1MHz,也就是转换时间为1us(在ADCCLK=14M,采样周期为1.5个ADC时钟下得到),一般来说不要让ADC的时钟超出14M,否则会导致结果准确度下降。STM32将ADC的转换分为2个通道组:规则通道组和注入通道组。规则通道组相当于你正常运行的 程序,而注入通道组则相当于中断,所以注入通道的ADC转换是可以打断规则通道的转换的。所以灵活使用可以提高程序对时间处理的速度。下面是STM32F1的ADC通道与GPIO对应表:

二、代码实例

定长的串口DMA初始化代码如下:

void MyDMAConfig(void)
{
    DMA_InitTypeDef DMA_InitStructure;
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//使能DMA传输
    
    //Rx
    DMA_DeInit(DMA1_Channel5);//将DMA通道5重设为缺省值
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;//DMA外设串口基地址 
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)(&SendBuff);//DMA内存基地址 
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//DMA传输方向,从外设读取到内存  
	DMA_InitStructure.DMA_BufferSize = sizeof(DMA_Typedef);//缓存区大小 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址寄存器不变 
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址寄存器递增  
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据宽度为8位 
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//内存数据宽度为8位 
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//正常缓存模式  
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//DMA通道拥有中优先级 
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//DMA通道没有设置为内存到内存模式 
	DMA_Init(DMA_Channel5, &DMA_InitStructure);  
    DMA_Cmd(DMA1_Channel5,ENABLE);

    //Tx
    DMA_DeInit(DMA1_Channel4);//将DMA通道4重设为缺省值
	DMA_InitStructure.DMA_PeripheralBaseAddr = (u32)&USART1->DR;//DMA外设串口基地址 
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)(&SendBuff);//DMA内存基地址 
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;//DMA传输方向,从内存读取到外设  
	DMA_InitStructure.DMA_BufferSize = sizeof(DMA_Typedef);//缓存区大小 
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;//外设地址寄存器不变 
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;//内存地址寄存器递增  
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;//外设数据宽度为8位 
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;//内存数据宽度为8位 
	DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//正常缓存模式  
	DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//DMA通道拥有中优先级 
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//DMA通道没有设置为内存到内存模式 
	DMA_Init(DMA_Channel4, &DMA_InitStructure);  
    DMA_Cmd(DMA1_Channel4,DISABLE);
}
void uart_init(u32 bound)
{
    GPIO_InitTypeDef GPIO_InitStructure;
	USART_InitTypeDef USART_InitStructure;
	 
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);//使能DMA时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE);	

	//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); 

	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);

    USART_ITConfig(USART1, USART_IT_IDLE, ENABLE);//使能串口空闲中断
    USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);//使能串口DMA接收中断
    USART_DMACmd(USART1,USART_DMAReq_Tx,ENABLE);//使能串口DMA发送中断
    USART_Cmd(USART1, ENABLE);

    MyDMAConfig();
}
void Usart1Send(u8 len)
{
    DMA_Cmd(DMA1_Channel4,DISABLE);
    DMA_SetCurrDataCounter(DMA1_Channel4,len);
    DMA_Cmd(DMA1_Channel4,ENABLE);
}
void USART1_IRQHandler(void)
{
    if(USART_GetITStatus(USART1,USART_IT_IDLE) != RESET)
    {
        USART_DMACmd(USART1,USART_DMAReq_Rx,DISABLE);//一定要加这句,不然当输入长度大于缓冲区容量时,进入串口中断后DMA还会继续接收超出的那部分数据
        USART_ReceiveData(USART1);
        
        Usart1_Rec_Cnt = sizeof(DMA_Typedef) - DMA_GetCurrDataCounter(DMA1_Channel5);//算出本帧数据长度
        Usart1_Send(Usart1_Rec_Cnt);

        USART_ClearITPendingBit(USART1,USART_IT_IDLE);//清中断标志
        DMA_Cmd(DMA1_Channel5,DISABLE);
        DMA_SetCurrDataCounter(DMA1_Channel5,sizeof(DMA_Typedef));//重置接收数据长度
        DMA_Cmd(DMA1_Channel5,ENABLE);
        USART_DMACmd(USART1,USART_DMAReq_Rx,ENABLE);
    }
}

ADC扫描模式规则多通道连续转换代码如下:

#define    ADC1_DR_Address       ((u32)0x4001244C)    //ADC数据寄存器地址
#define    ADC1_SampleChannel    0x0002               //通道掩码
#define    ADC1_SampleTime       ADC_SampleTime_13Cycles5    //采样时间
#define    ADC1_SampleTimes      1                           //采样次数
#define    ADC1_NbrOfChannel     1                           //转换通道
#define    ADC1_SampleBufferSize    (ADC1_NbrOfChannel * ADC1_SampleTimes)//缓存大小
__IO uint16_t ADC1_SampleBuffer[10];//数据缓存区
void ADC_Init(void)
{
    uint8_t i;
	uint16_t temp = 0x0001;
	uint8_t rank = 1;

    ADC_InitTypeDef ADC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
	DMA_InitTypeDef DMA_InitStructure;

    RCC_ADCCLKConfig(RCC_PCLK2_Div6);    
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;		
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    ADC_DeInit(ADC1);
	ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;							
	ADC_InitStructure.ADC_ScanConvMode = ENABLE;	//开启扫描模式							
	ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;	//开启连续转换						
	ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;	 		
	ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;	 					
	ADC_InitStructure.ADC_NbrOfChannel = ADC1_NbrOfChannel;		 				
	ADC_Init(ADC1, &ADC_InitStructure);		

    for (i = 0;i < 16;i++)
	{
		if (ADC1_SampleChannel & temp)
		{
			ADC_RegularChannelConfig(ADC1, i, rank++, ADC1_SampleTime);	
		}
		temp <<= 1;	
	}

    ADC_Cmd(ADC1, ENABLE);														
	ADC_ResetCalibration(ADC1);		 											
	while(ADC_GetResetCalibrationStatus(ADC1));	   								
	ADC_StartCalibration(ADC1);													
	while(ADC_GetCalibrationStatus(ADC1));	

    DMA_DeInit(DMA1_Channel1);	   												
	DMA_InitStructure.DMA_PeripheralBaseAddr = ADC1_DR_Address;					
	DMA_InitStructure.DMA_MemoryBaseAddr = (u32)ADC1_SampleBuffer;				
	DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;							
	DMA_InitStructure.DMA_BufferSize = ADC1_SampleBufferSize;					
	DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;			
	DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;						
	DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;	
	DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;	   		
	DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;								
	DMA_InitStructure.DMA_Priority = DMA_Priority_High;							
	DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;			 					
	DMA_Init(DMA1_Channel1, &DMA_InitStructure);	   							
	DMA_Cmd(DMA1_Channel1, ENABLE);			                                    
	ADC_DMACmd(ADC1, ENABLE);                                                   
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);   
}

ADC_RegularChannelConfig的参数Rank的含义:这个在你多通道采集和DMA应用时会有很大的作用,比如你需要多通道采集,你设置每个通道的采集顺序其实就是用这个变量来做的,假设你定义channel 1的rank=1,channel 2的rank=2,那么对应你在DMA缓存空间的变量数组ADCDMA[0]就是channel 1的转换结果,ADCDMA[1]就是通道2的转换结果。

三、总结

以上就是我整理的DMA的简单应用,可能有些地方讲的不好,请各位大佬指出。谢谢!

猜你喜欢

转载自blog.csdn.net/SammySum/article/details/88955908