输入捕获
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
TIM_TimeBaseStruct.TIM_Period = 0xFFFF; // 自动重装载值
TIM_TimeBaseStruct.TIM_Prescaler = 71; // 预分频值
TIM_TimeBaseStruct.TIM_ClockDivision = 0;
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);
-
频率计算:
- 通过两次捕获的计数器值之差,结合定时器的时钟频率,可以计算出信号的频率或周期。
输入捕获的配置步骤
使能定时器时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能TIM2时钟
配置定时器基本参数
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
TIM_TimeBaseStruct.TIM_Period = 0xFFFF; // 自动重装载值
TIM_TimeBaseStruct.TIM_Prescaler = 71; // 预分频值
TIM_TimeBaseStruct.TIM_ClockDivision = 0;
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);
配置输入捕获模式
TIM_ICInitTypeDef TIM_ICStruct;
TIM_ICStruct.TIM_Channel = TIM_Channel_1; // 选择通道1
TIM_ICStruct.TIM_ICPolarity = TIM_ICPolarity_Rising; // 捕获上升沿
TIM_ICStruct.TIM_ICSelection = TIM_ICSelection_DirectTI; // 直接输入
TIM_ICStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 不分频
TIM_ICStruct.TIM_ICFilter = 0x0; // 无滤波
TIM_ICInit(TIM2, &TIM_ICStruct);
使能捕获中断
TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE); // 使能捕获中断
NVIC_EnableIRQ(TIM2_IRQn); // 使能TIM2中断
使能定时器
TIM_Cmd(TIM2, ENABLE); // 使能TIM2
完整代码
#include "stm32f10x.h"
volatile uint16_t CaptureValue1 = 0; // 第一次捕获值
volatile uint16_t CaptureValue2 = 0; // 第二次捕获值
volatile uint16_t Period = 0; // 信号周期
volatile uint32_t Frequency = 0; // 信号频率
void TIM2_IC_Init(void) {
// 使能TIM2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 配置定时器基本参数
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
TIM_TimeBaseStruct.TIM_Period = 0xFFFF; // 自动重装载值
TIM_TimeBaseStruct.TIM_Prescaler = 71; // 预分频值
TIM_TimeBaseStruct.TIM_ClockDivision = 0;
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);
// 配置输入捕获模式
TIM_ICInitTypeDef TIM_ICStruct;
TIM_ICStruct.TIM_Channel = TIM_Channel_1; // 选择通道1
TIM_ICStruct.TIM_ICPolarity = TIM_ICPolarity_Rising; // 捕获上升沿
TIM_ICStruct.TIM_ICSelection = TIM_ICSelection_DirectTI; // 直接输入
TIM_ICStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 不分频
TIM_ICStruct.TIM_ICFilter = 0x0; // 无滤波
TIM_ICInit(TIM2, &TIM_ICStruct);
// 使能捕获中断
TIM_ITConfig(TIM2, TIM_IT_CC1, ENABLE); // 使能捕获中断
NVIC_EnableIRQ(TIM2_IRQn); // 使能TIM2中断
// 使能定时器
TIM_Cmd(TIM2, ENABLE);
}
void TIM2_IRQHandler(void) {
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET) {
if (CaptureValue1 == 0) {
// 第一次捕获
CaptureValue1 = TIM_GetCapture1(TIM2);
} else {
// 第二次捕获
CaptureValue2 = TIM_GetCapture1(TIM2);
// 计算周期
if (CaptureValue2 > CaptureValue1) {
Period = CaptureValue2 - CaptureValue1;
} else {
Period = (0xFFFF - CaptureValue1) + CaptureValue2;
}
// 计算频率
Frequency = 1000000 / Period; // 假设定时器时钟为1MHz
// 重置捕获值
CaptureValue1 = 0;
CaptureValue2 = 0;
}
// 清除中断标志
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1);
}
}
int main(void) {
// 初始化输入捕获
TIM2_IC_Init();
while (1) {
// 主循环
}
}
编码器接口
1. 编码器接口的基本原理
编码器信号:
- 旋转编码器通常输出两路正交信号(A相和B相),用于表示旋转方向和速度。
计数器:
- 编码器接口通过捕获A相和B相的边沿信号,驱动定时器的计数器(CNT)递增或递减。
速度计算:
- 通过读取计数器的值,可以计算出编码器的旋转速度。
速度计算
-
编码器分辨率:假设编码器的分辨率为1000脉冲/转。
-
速度计算:
- 速度 = 编码器计数器值 / 编码器分辨率。
编码器接口的配置步骤
使能定时器时钟
-
STM32的外设(如定时器)默认是关闭时钟的,以节省功耗。
-
使用外设前,必须使能其时钟,否则外设无法工作。
-
这里使能了TIM2的时钟,因为我们将使用TIM2的编码器接口功能。
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能TIM2时钟
配置定时器基本参数
自动重装载值(TIM_Period):
设置定时器计数器的最大值。这里设置为
0xFFFF
(16位定时器的最大值),表示计数器从0计数到65535后溢出。编码器模式下,计数器会根据编码器的脉冲信号递增或递减,因此需要足够大的计数范围。
预分频器(TIM_Prescaler):
用于分频定时器的时钟频率。这里设置为0,表示不分频,定时器直接使用输入时钟频率。
如果编码器脉冲频率较高,可以适当增加预分频值以降低计数器频率。
时钟分频(TIM_ClockDivision):
- 用于分频定时器的输入时钟。这里设置为0,表示不进行额外的时钟分频。
计数模式(TIM_CounterMode):
- 设置计数器的计数模式。编码器模式下,计数模式通常设置为向上计数(
TIM_CounterMode_Up
),但实际计数方向由编码器信号决定。
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
TIM_TimeBaseStruct.TIM_Period = 0xFFFF; // 自动重装载值
TIM_TimeBaseStruct.TIM_Prescaler = 0; // 不分频
TIM_TimeBaseStruct.TIM_ClockDivision = 0;
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);
配置编码器接口模式
编码器模式(TIM_EncoderMode_TI12):
设置定时器为编码器模式,使用TIM2的通道1和通道2(TI1和TI2)作为编码器的输入信号。
编码器模式会根据A相和B相信号的边沿变化来驱动计数器递增或递减。
输入捕获极性(TIM_ICPolarity_Rising):
- 设置编码器信号的捕获边沿。这里设置为上升沿触发,表示在A相和B相信号的上升沿时捕获计数器值。
TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
配置输入捕获通道
通道选择(TIM_Channel):
- 选择定时器的通道1和通道2作为编码器的输入信号。
捕获极性(TIM_ICPolarity):
- 设置捕获信号的边沿。这里设置为上升沿触发。
输入选择(TIM_ICSelection):
- 设置输入信号的来源。这里设置为直接输入(
TIM_ICSelection_DirectTI
),表示直接使用TIM2的通道1和通道2作为输入。输入分频(TIM_ICPrescaler):
- 设置输入信号的分频。这里设置为不分频(
TIM_ICPSC_DIV1
),表示每个边沿都触发捕获。输入滤波(TIM_ICFilter):
- 设置输入信号的滤波。这里设置为无滤波(
0x0
),表示不对输入信号进行滤波。
TIM_ICInitTypeDef TIM_ICStruct;
TIM_ICStruct.TIM_Channel = TIM_Channel_1; // 选择通道1
TIM_ICStruct.TIM_ICPolarity = TIM_ICPolarity_Rising; // 捕获上升沿
TIM_ICStruct.TIM_ICSelection = TIM_ICSelection_DirectTI; // 直接输入
TIM_ICStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 不分频
TIM_ICStruct.TIM_ICFilter = 0x0; // 无滤波
TIM_ICInit(TIM2, &TIM_ICStruct);
TIM_ICStruct.TIM_Channel = TIM_Channel_2; // 选择通道2
TIM_ICInit(TIM2, &TIM_ICStruct);
使能定时器
使能定时器后,定时器开始工作,计数器会根据编码器信号的变化递增或递减。
如果不使能定时器,编码器接口将无法工作。
TIM_Cmd(TIM2, ENABLE); // 使能TIM2
完整代码
#include "stm32f10x.h"
volatile int16_t EncoderCount = 0; // 编码器计数器值
void TIM2_Encoder_Init(void) {
// 使能TIM2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 配置定时器基本参数
TIM_TimeBaseInitTypeDef TIM_TimeBaseStruct;
TIM_TimeBaseStruct.TIM_Period = 0xFFFF; // 自动重装载值
TIM_TimeBaseStruct.TIM_Prescaler = 0; // 不分频
TIM_TimeBaseStruct.TIM_ClockDivision = 0;
TIM_TimeBaseStruct.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStruct);
// 配置编码器接口模式
TIM_EncoderInterfaceConfig(TIM2, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);
// 配置输入捕获通道
TIM_ICInitTypeDef TIM_ICStruct;
TIM_ICStruct.TIM_Channel = TIM_Channel_1; // 选择通道1
TIM_ICStruct.TIM_ICPolarity = TIM_ICPolarity_Rising; // 捕获上升沿
TIM_ICStruct.TIM_ICSelection = TIM_ICSelection_DirectTI; // 直接输入
TIM_ICStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 不分频
TIM_ICStruct.TIM_ICFilter = 0x0; // 无滤波
TIM_ICInit(TIM2, &TIM_ICStruct);
TIM_ICStruct.TIM_Channel = TIM_Channel_2; // 选择通道2
TIM_ICInit(TIM2, &TIM_ICStruct);
// 使能定时器
TIM_Cmd(TIM2, ENABLE);
}
int main(void) {
// 初始化编码器接口
TIM2_Encoder_Init();
while (1) {
// 读取编码器计数器值
EncoderCount = TIM_GetCounter(TIM2);
// 主循环
}
}
模数转换器
ADC基本结构
ADC(Analog-to-Digital Converter,模数转换器)用于将模拟信号转换为数字信号。STM32的ADC模块通常包括以下主要部分:
1.1 模拟看门狗(Analog Watchdog)
作用:监控ADC转换结果,当转换结果超出设定的阈值范围时,触发中断或事件。
应用场景:用于检测模拟信号是否在正常范围内。
1.2 中断输出控制
作用:在ADC转换完成、模拟看门狗触发等事件发生时,产生中断信号。
应用场景:用于通知CPU处理ADC转换结果。
1.3 CPU
- 作用:处理ADC转换结果,执行相应的逻辑。
1.4 ADC转换器
作用:将模拟信号转换为数字信号。
分辨率:STM32的ADC通常支持12位分辨率,即转换结果为0到4095。
1.5 AD数据寄存器
作用:存储ADC转换结果。
规则组结果:存储规则组通道的转换结果。
注入组结果:存储注入组通道的转换结果。
1.6 规则组(Regular Group)
作用:用于常规的ADC转换,支持多个通道按顺序转换。
特点:规则组转换结果存储在单个寄存器中。
1.7 注入组(Injected Group)
作用:用于高优先级的ADC转换,支持多个通道按顺序转换。
特点:注入组转换结果存储在多个寄存器中,优先级高于规则组。
1.8 温度传感器
作用:内置温度传感器,用于测量芯片温度。
特点:通常连接到ADC的某个通道。
1.9 VREFINT
作用:内部参考电压,用于校准ADC转换结果。
特点:通常连接到ADC的某个通道。
1.10 START
作用:启动ADC转换。
触发方式:可以通过软件或外部事件触发。
1.11 CLOCK
作用:提供ADC转换的时钟信号。
来源:通常由RCC(Reset and Clock Control)模块提供。
1.12 触发控制
作用:控制ADC转换的触发方式。
触发源:可以是定时器、外部信号等。
1.13 RCC
作用:提供ADC模块的时钟信号。
配置:通过RCC配置ADC的时钟频率。
1.14 开关控制
作用:控制ADC模块的开启和关闭。
应用场景:用于节省功耗。
#include "stm32f10x.h" // 包含STM32F10x系列的头文件
void ADC_Init(void) {
// 1. 使能GPIOA和ADC1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1, ENABLE);
// 2. 配置GPIOA的引脚0为模拟输入模式
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0; // 选择引脚0
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AIN; // 模拟输入模式
GPIO_Init(GPIOA, &GPIO_InitStruct);
// 3. 配置ADC1
ADC_InitTypeDef ADC_InitStruct;
ADC_InitStruct.ADC_Mode = ADC_Mode_Independent; // 独立模式
ADC_InitStruct.ADC_ScanConvMode = DISABLE; // 单通道模式
ADC_InitStruct.ADC_ContinuousConvMode = DISABLE; // 单次转换模式
ADC_InitStruct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None; // 软件触发
ADC_InitStruct.ADC_DataAlign = ADC_DataAlign_Right; // 数据右对齐
ADC_InitStruct.ADC_NbrOfChannel = 1; // 1个通道
ADC_Init(ADC1, &ADC_InitStruct);
// 4. 配置ADC1的通道0(PA0)
ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);
// 5. 使能ADC1
ADC_Cmd(ADC1, ENABLE);
// 6. 校准ADC1
ADC_ResetCalibration(ADC1); // 重置校准寄存器
while (ADC_GetResetCalibrationStatus(ADC1)); // 等待重置完成
ADC_StartCalibration(ADC1); // 开始校准
while (ADC_GetCalibrationStatus(ADC1)); // 等待校准完成
}
uint16_t ADC_Read(void) {
// 1. 启动ADC转换
ADC_SoftwareStartConvCmd(ADC1, ENABLE);
// 2. 等待转换完成
while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));
// 3. 读取转换结果
return ADC_GetConversionValue(ADC1);
}
int main(void) {
// 初始化ADC
ADC_Init();
while (1) {
// 读取ADC值
uint16_t adcValue = ADC_Read();
// 处理ADC值(例如:打印到串口或控制LED)
// ...
}
}
直接存储器DMA
通信接口
USART协议