25/1/16 嵌入式笔记 STM32F108

输入捕获

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);
  1. 频率计算

    • 通过两次捕获的计数器值之差,结合定时器的时钟频率,可以计算出信号的频率或周期。

输入捕获的配置步骤

使能定时器时钟

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. 编码器接口的基本原理

  1. 编码器信号

    • 旋转编码器通常输出两路正交信号(A相和B相),用于表示旋转方向和速度。
  2. 计数器

    • 编码器接口通过捕获A相和B相的边沿信号,驱动定时器的计数器(CNT)递增或递减。
  3. 速度计算

    • 通过读取计数器的值,可以计算出编码器的旋转速度。

速度计算

  • 编码器分辨率:假设编码器的分辨率为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协议

相关推荐
小卡规划1 小时前
检验统计量与p值笔记
笔记
羊小猪~~4 小时前
MYSQL学习笔记(二):基本的SELECT语句使用(基本、条件、聚合函数查询)
数据库·笔记·sql·学习·mysql·考研·数据分析
程思扬4 小时前
Android笔记: 实现点击事件透传到底部
android·前端·windows·经验分享·笔记·科技·ui
l1x1n06 小时前
No.33 笔记 | Docker入门:基础概念与实用指南
笔记·docker·eureka
安冬的码畜日常7 小时前
【Vim Masterclass 笔记11】S06L24 + L25:Vim 文本的插入、变更、替换与连接操作同步练习(含点评课)
笔记·vim
Suwg2098 小时前
《手写Mybatis渐进式源码实践》实践笔记(第九章 细化XML语句构建器)
xml·java·笔记·mybatis
Test_the_rookie8 小时前
mongDB学习笔记
笔记·学习
Mae_cpski8 小时前
【React学习笔记】第一章:React入门
笔记·学习·react.js
BIOS菜鸟工程师11 小时前
UEFI Spec 学习笔记---7 - Services — Boot Services(1)
java·笔记·学习
扶离_flee11 小时前
麦田物语学习笔记:代码链接UI实现时间日期对应转换
笔记·学习