IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 嵌入式 -> STM32-NUCLEO-F411RE—捕获PWM信号测出占空比和频率 -> 正文阅读

[嵌入式]STM32-NUCLEO-F411RE—捕获PWM信号测出占空比和频率

使用STM32CubeMX生产初始化代码,底板硬件使用NUCLEO-F411RE开发板

在这里插入图片描述

一、新建工程

在这里插入图片描述

二、选择芯片型号

我使用的是STM的NUCLEO开发板 ,在筛选其中填入411
在这里插入图片描述

三、配置时钟

首先选择外部晶振:
在这里插入图片描述
开发板焊接了外部晶振,所以我 RCC(Reset and Cock Control) 配置选择了 Crystal/Ceramic Resonator(石英/陶瓷谐振器),配置完成后,右边的 Pinout view 里相关引脚就会被标绿。

配置时钟频率:
在这里插入图片描述
外部高速时钟配置完成后,进入 Clock Configuration 选项,根据实际情况,将系统时钟配置为 96MHz,最后按下回车,软件会自动调整分频和倍频参数。

四、配置调试模式

在这里插入图片描述

ST-Link 就是 Serial Wire 调试模式,一定要设置!!!
以前使用 M0 的芯片,不配置这个模式没出现问题,但现在这个型号,如果不配置 Serial Wire 模式,程序一旦通过 ST-Link 烧录到芯片中,芯片就再也不能被ST-Link 识别了。(后来我是通过 STMISP 工具烧录程序/擦除后才恢复正常的)

五、根据内部原理分析

在这里插入图片描述
这里以TIM_CH1为例,当从CH1输入一个PWM波,通过输入滤波后将会产生两路信号:tim_ti1fp1 & tim_ti1fp2,分别送至tim_ic1 & tim_ic2,也就是说一个TI信号将会被映射成两路的IC信号,所以可以通过进行边沿检测来测量PWM的频率以及占空比。

六、配置定时器模式参数

具体步骤如下:

1、设置定时器Slave Mode为Reset Mode,也就是当检测到上升沿时,定时器复位;

2、PWM由CH1进入,触发源设置为TI1FP1,并设置IC1为上升沿捕获;

3、当第一次捕获到上升沿时,定时器复位,计数寄存器CNT清零;

4、当IC2捕获到下降沿时,计数器CNT的值将会被存到捕获寄存器CCR2中;

5、当IC1再次捕获到上升沿时,计数器CNT的值将会被存到捕获寄存器CCR1中,同时将定时器复位;

因此,CCR1的值就是周期,CCR2的值就是占空比。

配置如下:

我们将PA8的TIM1-CH1设置为捕获输入管脚,通道1设置为直接模式,通道2设置为间接模式
在这里插入图片描述

预分频系数设为APB总线频率设置为96-1,因为内部是加1操作所以要在这里减1,同样预装载值也要减1操作,为了后面方便计算我这里设置成1000-1

输入捕获能捕获到的最小的频率为 96M / { (ARR+1)x(PSC+1) } 注意,这里的计数周期不能设置的太小,如果我们设置的计数周期 < PWM周期那么就无法捕获PWM脉冲,一般驱动电机的PWM是10k ~ 25kHz,我们设置的周期为1ms,对应频率为1kHz,那么就可以捕获1kHz以上的PWM 信号

而最终计算得到的频率值 = 分频后得到的时钟频率 / 上升沿个数 = ( 定时器时钟频率 / 预分频系数 )/ 上升沿个数 ;
最终计算的到的占空比 = 下降沿个数 / 上升沿个数;

输入捕获需要开启定时器的中断,无论是计时溢出还是输入捕获都需要使用到中断。
在这里插入图片描述
配置NVIC(Nested Vector Interrupt Controller):
在这里插入图片描述

七、生成 Keil 工程

设置 IDE 和 工程目录及名称:
在这里插入图片描述
将每种外设的代码存放到不同的 .c /.h 文件中,便于管理(不然都会被放到 main.c 中)。
在这里插入图片描述

八、中断函数写在哪

在使用标准库时,我们是将中断处理写在最底层的中断处理函数中,如 EXTI0_IRQHandler(),但 Hal 库增加了回调函数,将中断底层一些必要的操作 “隐藏” 了起来(如清除中断)。

中断的调用顺序是(以 EXTI0 为例):EXTI0_IRQHandler() —> HAL_GPIO_EXTI_IRQHandler() —> HAL_GPIO_EXTI_Callback()。

TIM2 的中断服务函数已经在 stm32f1xx_it.c 中定义(STM32CubeMX 自动生成的)

/**
  * @brief This function handles TIM1 global interrupt.
  */
void TIM1_IRQHandler(void)
{
  /* USER CODE BEGIN TIM1_IRQn 0 */

  /* USER CODE END TIM1_IRQn 0 */
  HAL_TIM_IRQHandler(&htim1);
  /* USER CODE BEGIN TIM1_IRQn 1 */

  /* USER CODE END TIM1_IRQn 1 */
}

HAL_TIM_IRQHandler() 是 HAL 库的定时器总中断,里面代码很多,这里不展示,我们只需要知道一点——当 TIM2 计数值溢出或发生其他事件(如捕获到上升/下降沿信号)时,系统会执行一系列的中断回调函数,其中包括我们将要用到的 计数溢出回调函数HAL_TIM_PeriodElapsedCallback() 和 输入捕获回调函数HAL_TIM_IC_CaptureCallback()

九、代码部分

下面是生成 Keil 工程中关于 TIM1(输入PWM捕获)初始化的代码:

/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file    tim.c
  * @brief   This file provides code for the configuration
  *          of the TIM instances.
  ******************************************************************************
  * @attention
  *
  * Copyright (c) 2022 STMicroelectronics.
  * All rights reserved.
  *
  * This software is licensed under terms that can be found in the LICENSE file
  * in the root directory of this software component.
  * If no LICENSE file comes with this software, it is provided AS-IS.
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "tim.h"

/* USER CODE BEGIN 0 */

/* USER CODE END 0 */

TIM_HandleTypeDef htim1;

/* TIM1 init function */
void MX_TIM1_Init(void)
{

  /* USER CODE BEGIN TIM1_Init 0 */

  /* USER CODE END TIM1_Init 0 */

  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_SlaveConfigTypeDef sSlaveConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_IC_InitTypeDef sConfigIC = {0};

  /* USER CODE BEGIN TIM1_Init 1 */

  /* USER CODE END TIM1_Init 1 */
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 96-1;
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim1.Init.Period = 1000-1;
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  htim1.Init.RepetitionCounter = 0;
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
  if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_TIM_IC_Init(&htim1) != HAL_OK)
  {
    Error_Handler();
  }
  sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;
  sSlaveConfig.InputTrigger = TIM_TS_TI1FP1;
  sSlaveConfig.TriggerPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
  sSlaveConfig.TriggerFilter = 0;
  if (HAL_TIM_SlaveConfigSynchro(&htim1, &sSlaveConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
  sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
  sConfigIC.ICPrescaler = TIM_ICPSC_DIV1;
  sConfigIC.ICFilter = 0;
  if (HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
  sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_FALLING;
  sConfigIC.ICSelection = TIM_ICSELECTION_INDIRECTTI;
  if (HAL_TIM_IC_ConfigChannel(&htim1, &sConfigIC, TIM_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
  /* USER CODE BEGIN TIM1_Init 2 */

  /* USER CODE END TIM1_Init 2 */

}

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* tim_baseHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(tim_baseHandle->Instance==TIM1)
  {
  /* USER CODE BEGIN TIM1_MspInit 0 */

  /* USER CODE END TIM1_MspInit 0 */
    /* TIM1 clock enable */
    __HAL_RCC_TIM1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();
    /**TIM1 GPIO Configuration
    PA8     ------> TIM1_CH1
    */
    GPIO_InitStruct.Pin = GPIO_PIN_8;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
    GPIO_InitStruct.Alternate = GPIO_AF1_TIM1;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* TIM1 interrupt Init */
    HAL_NVIC_SetPriority(TIM1_CC_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(TIM1_CC_IRQn);
  /* USER CODE BEGIN TIM1_MspInit 1 */

  /* USER CODE END TIM1_MspInit 1 */
  }
}

void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* tim_baseHandle)
{

  if(tim_baseHandle->Instance==TIM1)
  {
  /* USER CODE BEGIN TIM1_MspDeInit 0 */

  /* USER CODE END TIM1_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_TIM1_CLK_DISABLE();

    /**TIM1 GPIO Configuration
    PA8     ------> TIM1_CH1
    */
    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_8);

    /* TIM1 interrupt Deinit */
    HAL_NVIC_DisableIRQ(TIM1_CC_IRQn);
  /* USER CODE BEGIN TIM1_MspDeInit 1 */

  /* USER CODE END TIM1_MspDeInit 1 */
  }
}

/* USER CODE BEGIN 1 */

/* USER CODE END 1 */

主函数 工作

__IO uint16_t TIM1_IC2Value = 0;
__IO uint16_t TIM1_IC1Value = 0;
__IO float TIM1_DutyCycle = 0;
__IO float TIM1_Frequency = 0;

  MX_TIM1_Init();

开启捕获通道

HAL_TIM_IC_Start_IT (&htim1,TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT (&htim1,TIM_CHANNEL_2);	

我们的实验代码的核心部分为中断回调函数:

//定时器输入捕获中断处理回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)// 捕获中断发生时执行
{
		if(htim ->Channel == HAL_TIM_ACTIVE_CHANNEL_1)
		{
			/* 获取输入捕获值 */
			TIM1_IC1Value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
			TIM1_IC2Value = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_2);
			
			// 注意:捕获寄存器CCR1和CCR2的值在计算占空比和频率的时候必须加1
			if (TIM1_IC1Value != 0)
			{
				/* 占空比计算 */
				TIM1_DutyCycle = (float)((TIM1_IC2Value+1) * 100) / (TIM1_IC1Value + 1);

				/* 频率计算 */
				TIM1_Frequency = (96000000/(96))/(float)(TIM1_IC1Value + 1);
				
				printf("TIM1_占空比:%0.2f%%   TIM1_频率:%0.2fHz\n",TIM1_DutyCycle,TIM1_Frequency);
			}
			else
			{
				TIM1_DutyCycle = 0;
				TIM1_Frequency = 0;
			}
		
		}
}

十、测试部分

在这里插入图片描述

  嵌入式 最新文章
基于高精度单片机开发红外测温仪方案
89C51单片机与DAC0832
基于51单片机宠物自动投料喂食器控制系统仿
《痞子衡嵌入式半月刊》 第 68 期
多思计组实验实验七 简单模型机实验
CSC7720
启明智显分享| ESP32学习笔记参考--PWM(脉冲
STM32初探
STM32 总结
【STM32】CubeMX例程四---定时器中断(附工
上一篇文章      下一篇文章      查看所有文章
加:2022-09-13 11:33:13  更:2022-09-13 11:34:40 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/28 17:18:45-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计