基于STM32的DMA&Usart_TIM_ADC功能

使用的开发板正点原子精英版STM32F103ZET6,串口调试助手,MQ-2模块,PA-5

1主要模块

STM32 定时器

定时器介绍

16位向上、向下、向上/向下自动装载计数器
● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意
数值
● 4个独立通道:
─ 输入捕获
─ 输出比较
─ PWM生成(边缘或中间对齐模式)
─ 单脉冲模式输出
● 使用外部信号控制定时器和定时器互连的同步电路
● 如下事件发生时产生中断/DMA: ─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较
● 支持针对定位的增量(正交)编码器和霍尔传感器电路
● 触发输入作为外部时钟或者按周期的电流管理

定时器的时基单元主要有三个寄存器组成:16位计数器,自动重转载寄存器(包括一个影子寄存器),预分频器(控制计数器时钟),其中预分频计数器的时钟频率1——65535。由下面的图片可以看出预分频器对计数器值的控制。当计数器变化1的分频器系数不同导致的差别。
基于STM32的DMA&Usart_TIM_ADC功能基于STM32的DMA&Usart_TIM_ADC功能

计数器模式:计数器主要模式为:向上,向下,*对齐模式三种。计数器可以通过溢出触发更新,也可以设置TIM_ERG设置产生更新事件。同时也可以设置TIMx_CR1寄存器中的UDIS位,禁止事件更新。值得注意的是发生更新后所有的寄存器都会更新。我们在使用定时器高级功能时注意一些寄存器的标志位的灵活应用。
基于STM32的DMA&Usart_TIM_ADC功能
基于STM32的DMA&Usart_TIM_ADC功能
基于STM32的DMA&Usart_TIM_ADC功能
时钟源:
1:内部时钟(CK_INT)
2:外部时钟模式1:外部输入脚外部时钟模式2:外部触发输入 。
3:内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器器Timer1而作为另一个定时器Timer2的预分频器。

定时器代码

//代码这么优美相信不需要注释,如果有问题就查查固件库吧。
#include "TIM.h"

uint16_t TIM_Value;

void TIM_Init()
{	
		TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitStruct;
		NVIC_InitTypeDef	NVIC_InitStructure;
	
		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
	
		TIM_TimeBaseInitStruct.TIM_ClockDivision=TIM_CKD_DIV1;
		TIM_TimeBaseInitStruct.TIM_CounterMode=TIM_CounterMode_Up;
		TIM_TimeBaseInitStruct.TIM_Period=Auto_Counter;
		TIM_TimeBaseInitStruct.TIM_Prescaler=Div_Fre;
	
		TIM_TimeBaseInit(TIM,&TIM_TimeBaseInitStruct);
	
		NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
		
		NVIC_Init(&NVIC_InitStructure);

		TIM_Cmd(TIM,ENABLE);
		TIM_ITConfig(TIM,TIM_IT_Update,ENABLE);
}

void TIM5_IRQHandler()
{
	if(TIM_GetITStatus(TIM,TIM_IT_Update)!= RESET)
	{
		TIM_Value++;
		TIM_ClearITPendingBit(TIM,TIM_IT_Update);
	}
}

STM32 ADC

ADC介绍

● 12位分辨率
● 转换结束、注入转换结束和发生模拟看门狗事件时产生中断
● 单次和连续转换模式
● 从通道0到通道n的自动扫描模式
● 自校准
● 带内嵌数据一致性的数据对齐
● 采样间隔可以按通道分别编程
● 规则转换和注入转换均有外部触发选项
● 间断模式
● 双重模式(带2个或以上ADC的器件)
● ADC转换时间:
─ STM32F103xx增强型产品:时钟为56MHz时为1μs(时钟为72MHz为1.17μs)
● ADC供电要求:2.4V到3.6V
● ADC输入范围:VREF- ≤ VIN ≤ VREF+
● 规则通道转换期间有DMA请求产生。

ADC时钟是和APB2同步,RCC为APB2提供了一个专门的分频器。
ADC分为规则组和注入组:(16个)规则组转换顺序在ADC_SQRx中,(4个)注入组转换顺序在ADC_JSQR。如果ADC_SQRx或ADC_JSQR寄存器在转换期间被更改,当前的转换被清除,一个新的启动脉冲将发送到ADC以转换新选择的组。
单次转换模式:单次转换模式下,ADC只执行一次转换。该模式既可通过设置ADC_CR2寄存器的ADON位(只适用于规则通道)启动也可通过外部触发启动(适用于规则通道或注入通道),这时CONT位为0。

一旦选择通道的转换完成:
● 如果一个规则通道被转换:
─ 转换数据被储存在16位ADC_DR寄存器中
─ EOC(转换结束)标志被设置
─ 如果设置了EOCIE,则产生中断。
● 如果一个注入通道被转换:
─ 转换数据被储存在16位的ADC_DRJ1寄存器中
─ JEOC(注入转换结束)标志被设置
─ 如果设置了JEOCIE位,则产生中断。
ADC停止

//代码依然很优美,参考STM32中文参考手册

#include "ADC.h"

uint16_t ADC_Value;

void ADC1_5_Init(void )
{
		GPIO_InitTypeDef  GPIO_InitStructure;	
		ADC_InitTypeDef  ADC_InitStructure;
		NVIC_InitTypeDef NVIC_InitStructure;
	
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1|RCC_APB2Periph_GPIOA,ENABLE);
	
		ADC_DeInit(ADCx);
		RCC_ADCCLKConfig(RCC_PCLK2_Div8);
	
		GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;
		GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
		GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
		GPIO_Init(GPIOA,&GPIO_InitStructure);
	
		ADC_InitStructure.ADC_ContinuousConvMode=DISABLE;
		ADC_InitStructure.ADC_DataAlign=ADC_DataAlign_Right;
		ADC_InitStructure.ADC_ExternalTrigConv=ADC_ExternalTrigConv_None;
		ADC_InitStructure.ADC_Mode=ADC_Mode_Independent;
		ADC_InitStructure.ADC_NbrOfChannel=1;
		ADC_InitStructure.ADC_ScanConvMode=DISABLE;
		ADC_Init(ADCx,&ADC_InitStructure);
		
		NVIC_InitStructure.NVIC_IRQChannel=ADC1_2_IRQn;
		NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
		NVIC_InitStructure.NVIC_IRQChannelSubPriority=3;
		NVIC_Init(&NVIC_InitStructure);
		
		ADC_Cmd(ADCx,ENABLE);

		ADC_ResetCalibration(ADCx);
		while(ADC_GetResetCalibrationStatus(ADCx));
		ADC_StartCalibration(ADCx);
		while(ADC_GetSoftwareStartConvStatus(ADCx));
		
}

uint16_t	Get_ADC_Value(void )
{
		ADC_RegularChannelConfig(ADCx,ADC_Channle,1,ADC_SampleTime_55Cycles5);
		ADC_SoftwareStartConvCmd(ADCx,ENABLE);
	
		while(!ADC_GetFlagStatus(ADCx,ADC_FLAG_EOC));
	
		ADC_Value=ADC_GetConversionValue(ADCx);	
		return	ADC_Value;
}

STM32 Usart_DMA

DMA介绍

● 12个独立的可配置的通道(请求):DMA1有7个通道,DMA2有5个通道
● 每个通道都直接连接专用的硬件DMA请求,每个通道都同样支持软件触发。这些功能通过
软件来配置。
● 在同一个DMA模块上,多个请求间的优先权可以通过软件编程设置(共有四级:很高、高、
中等和低),优先权设置相等时由硬件决定(请求0优先于请求1,依此类推) 。
● 独立数据源和目标数据区的传输宽度(字节、半字、全字),模拟打包和拆包的过程。源和目
标地址必须按数据传输宽度对齐。
● 支持循环的缓冲器管理
● 每个通道都有3个事件标志(DMA半传输、DMA传输完成和DMA传输出错),这3个事件标志
逻辑或成为一个单独的中断请求。
● 存储器和存储器间的传输
● 外设和存储器、存储器和外设之间的传输
● 闪存、SRAM、外设的SRAM、APB1、APB2和AHB外设均可作为访问的源和目标。
● 可编程的数据传输数目:最大为65535

DMA让外设和寄存器可以直接沟通,不需要走MCU,可以说提高了MCU的执行速度,减轻了MCU负担,就是这些地址操作让人找不到北。
配置步骤:
使能DMA
DMA结构体
使能串口DMA
使能DMA通道同时设置缓存大小

#include "DMA.h"

			    					
void DMA1_CH4_Init(uint16_t Mem_ADDR)
{
		DMA_InitTypeDef DMA_InitStructure;

		RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
		
		DMA_DeInit(DMA_CHx); 
	
		DMA_InitStructure.DMA_BufferSize=ADC_Value_Size;
		DMA_InitStructure.DMA_DIR=DMA_DIR_PeripheralDST;
		DMA_InitStructure.DMA_M2M=DMA_M2M_Disable;
		DMA_InitStructure.DMA_MemoryBaseAddr=Mem_ADDR;
		DMA_InitStructure.DMA_MemoryDataSize=DMA_MemoryDataSize_HalfWord;
		DMA_InitStructure.DMA_MemoryInc=DMA_PeripheralInc_Disable;
		DMA_InitStructure.DMA_Mode=DMA_Mode_Normal;
		DMA_InitStructure.DMA_PeripheralBaseAddr=Per_Base_ADDR;
		DMA_InitStructure.DMA_PeripheralDataSize=DMA_MemoryDataSize_HalfWord;
		DMA_InitStructure.DMA_PeripheralInc=DMA_PeripheralInc_Disable;
		DMA_InitStructure.DMA_Priority=DMA_Priority_VeryHigh;

		DMA_Init(DMA_CHx, &DMA_InitStructure);
}

void DMA_Enable(DMA_Channel_TypeDef *DMA_CH)
{ 
	DMA_Cmd(DMA_CHx, DISABLE );  
 	DMA_SetCurrDataCounter(DMA_CHx,16);
 	DMA_Cmd(DMA_CHx, ENABLE);  
}	  

2总结

STM32的CUBE开发HAL库挺方便的,但是没有相关的配套程序(LCD代码)着实让人头疼的(懒得自己重新移植)。所以本着检测一下对于基础外设的想法就写了一下这个小程序。这个难度不高,基本ADC,TIM,DMA应用。每个单片机都有串口,定时器,中断系统,IO这些基本外设功能。还有ADC,IIC,SPI,DMA,RTC等这些基本的通信。这些简单的功能可以为进阶功能和更好的了解寄存器做准备。

上一篇:嵌入式笔记9


下一篇:STM32F103与M5311NBiot模组基于LWM2M协议连接onenet平台的笔记