使用的开发板正点原子精英版STM32F103ZET6,串口调试助手,MQ-2模块,PA-5
1主要模块
STM32 定时器
定时器介绍
16位向上、向下、向上/向下自动装载计数器
● 16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意
数值
● 4个独立通道:
─ 输入捕获
─ 输出比较
─ PWM生成(边缘或中间对齐模式)
─ 单脉冲模式输出
● 使用外部信号控制定时器和定时器互连的同步电路
● 如下事件发生时产生中断/DMA: ─ 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
─ 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
─ 输入捕获
─ 输出比较
● 支持针对定位的增量(正交)编码器和霍尔传感器电路
● 触发输入作为外部时钟或者按周期的电流管理
定时器的时基单元主要有三个寄存器组成:16位计数器,自动重转载寄存器(包括一个影子寄存器),预分频器(控制计数器时钟),其中预分频计数器的时钟频率1——65535。由下面的图片可以看出预分频器对计数器值的控制。当计数器变化1的分频器系数不同导致的差别。
计数器模式:计数器主要模式为:向上,向下,*对齐模式三种。计数器可以通过溢出触发更新,也可以设置TIM_ERG设置产生更新事件。同时也可以设置TIMx_CR1寄存器中的UDIS位,禁止事件更新。值得注意的是发生更新后所有的寄存器都会更新。我们在使用定时器高级功能时注意一些寄存器的标志位的灵活应用。
时钟源:
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等这些基本的通信。这些简单的功能可以为进阶功能和更好的了解寄存器做准备。