当前位置:网站首页>STM32CubeMX 學習(5)輸入捕獲實驗
STM32CubeMX 學習(5)輸入捕獲實驗
2022-06-25 08:07:00 【小輝_Super】
個人學習記錄
一、新建工程

二、選擇芯片型號
我使用的開發板是正點原子 STM32F103ZET6 核心板

三、配置時鐘
開發板焊接了外部晶振,所以我 RCC(Reset and Cock Control) 配置選擇了 Crystal/Ceramic Resonator(石英/陶瓷諧振器),配置完成後,右邊的 Pinout view 裏相關引脚就會被標綠。

外部高速時鐘配置完成後,進入 Clock Configuration 選項,根據實際情况,將系統時鐘配置為 72 MHz,配置步驟如下,最後按下回車,軟件會自動調整分頻和倍頻參數。

四、配置調試模式
ST-Link 就是 Serial Wire 調試模式,一定要設置!!!
以前使用 M0 的芯片,不配置這個模式沒出現問題,但現在這個型號,如果不配置 Serial Wire 模式,程序一旦通過 ST-Link 燒錄到芯片中,芯片就再也不能被ST-Link 識別了。(後來我是通過 STMISP 工具燒錄程序/擦除後才恢複正常的)

五、定時器(輸入捕獲)參數配置
我將 TIM2 的通道 1 作為輸入捕獲測試通道,STM32CubeMX 會默認配置 PA0 作為輸入捕獲的 IO 口(PA0 有該複用功能,且不需要重映像,所以自動將 PA0 設為 TIM_CH1 的 GPIO),定時器的參數設定如下圖所示(輸入捕獲的配置可以不用改,默認捕獲上昇沿):

分頻系數為 72-1,意思就是 72 分頻(0錶示 1 分頻,1 錶示 2 分頻,以此類推),TIM2 的時鐘頻率為 72 MHz(下圖中,APB1 Timer clocks 的時鐘頻率為 72MHz,TIM2 掛載在 APB1 上)。將其進行 72 分頻後,頻率變成了 1MHz,即每秒計數 1000000 次。周期設置為 1000-1(這裏要减一,應該是因為計數值最小為 0),代錶著一個完整的計時周期為 1000 次計數,結合定時器計數頻率,定時器一次計時溢出所需的時間為 1ms。【頻率决定了輸入捕獲的捕獲周期,計時值設為 1000 只是為了方便計算】

輸入捕獲需要開啟定時器的中斷,無論是計時溢出還是輸入捕獲都需要使用到中斷。

六、生成 Keil 工程
設置 IDE 和 工程目錄及名稱:

將每種外設的代碼存放到不同的 .c /.h 文件中,便於管理(不然都會被放到 main.c 中)。

下面是生成 Keil 工程中關於 TIM2(輸入捕獲)初始化的代碼:
/* TIM2 init function */
void MX_TIM2_Init(void)
{
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = {
0};
TIM_MasterConfigTypeDef sMasterConfig = {
0};
TIM_IC_InitTypeDef sConfigIC = {
0};
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72 - 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 1000 - 1;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
if (HAL_TIM_IC_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &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(&htim2, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
}
七、中斷函數寫在哪
在使用標准庫時,我們是將中斷處理寫在最底層的中斷處理函數中,如 EXTI0_IRQHandler(),但 Hal 庫增加了回調函數,將中斷底層一些必要的操作 “隱藏” 了起來(如清除中斷)。
中斷的調用順序是(以 EXTI0 為例):EXTI0_IRQHandler() —> HAL_GPIO_EXTI_IRQHandler() —> HAL_GPIO_EXTI_Callback()。
TIM2 的中斷服務函數已經在 stm32f1xx_it.c 中定義(STM32CubeMX 自動生成的)
/** * @brief This function handles TIM2 global interrupt. */
void TIM2_IRQHandler(void)
{
/* USER CODE BEGIN TIM2_IRQn 0 */
/* USER CODE END TIM2_IRQn 0 */
HAL_TIM_IRQHandler(&htim2);
/* USER CODE BEGIN TIM2_IRQn 1 */
/* USER CODE END TIM2_IRQn 1 */
}
HAL_TIM_IRQHandler() 是 HAL 庫的定時器總中斷,裏面代碼很多,這裏不展示,我們只需要知道一點——當 TIM2 計數值溢出或發生其他事件(如捕獲到上昇/下降沿信號)時,系統會執行一系列的中斷回調函數,其中包括我們將要用到的 計數溢出回調函數HAL_TIM_PeriodElapsedCallback() 和 輸入捕獲回調函數HAL_TIM_IC_CaptureCallback()。
八、測試示例
實驗中用到了串口,上文配置中沒提及,串口配置可以參考 STM32CubeMx 學習(2)USART 串口實驗
我的實驗代碼的核心部分為中斷回調函數:
// 定時器計數溢出中斷處理回調函數
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(IC_DONE_FLAG == 0) // 未完成捕獲
{
if(IC_START_FLAG == 1) // 已經捕獲到了高電平
{
IC_TIMES++; // 捕獲次數加一
}
}
}
//定時器輸入捕獲中斷處理回調函數
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)// 捕獲中斷發生時執行
{
if(IC_DONE_FLAG == 0) // 未完成捕獲
{
if(IC_START_FLAG == 1) // 原來是高電平,現在捕獲到一個下降沿
{
IC_VALUE = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 獲取捕獲值
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1); // 先清除原來的設置
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);// 配置為上昇沿捕獲
IC_START_FLAG = 0; // 標志複比特
IC_DONE_FLAG = 1; // 完成一次高電平捕獲
}
else // 捕獲還未開始,第一次捕獲到上昇沿
{
IC_TIMES = 0; // 捕獲次數清零
IC_VALUE = 0; // 捕獲值清零
IC_START_FLAG = 1; // 設置捕獲到了上邊沿的標志
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1); // 先清除原來的設置
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);// 配置為下降沿捕獲
}
__HAL_TIM_SET_COUNTER(htim,0); // 定時器計數值清零
}
}
完整 main.c
/* USER CODE BEGIN Header */
/** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @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 "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include <stdio.h>
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
uint32_t IC_TIMES; // 捕獲次數,單比特1ms
uint8_t IC_START_FLAG; // 捕獲開始標志,1:已捕獲到高電平;0:還沒有捕獲到高電平
uint8_t IC_DONE_FLAG; // 捕獲完成標志,1:已完成一次高電平捕獲
uint16_t IC_VALUE; // 輸入捕獲的捕獲值
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/** * @brief The application entry point. * @retval int */
int main(void)
{
/* USER CODE BEGIN 1 */
uint32_t time = 0;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_TIM2_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1); //開啟TIM2的捕獲通道1
__HAL_TIM_ENABLE_IT(&htim2,TIM_IT_UPDATE); //使能更新中斷
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
HAL_Delay(10);
if(IC_DONE_FLAG == 1) // 如果完成一次高電平捕獲
{
IC_DONE_FLAG = 0; // 標志清零
time = IC_TIMES * 1000; // 脈沖時間為捕獲次數 * 1000us
time += IC_VALUE; // 加上捕獲時間(小於1ms的部分)
printf("High level: %d us\n", time);
}
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/** * @brief System Clock Configuration * @retval None */
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {
0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {
0};
/** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
// 定時器計數溢出中斷處理回調函數
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(IC_DONE_FLAG == 0) // 未完成捕獲
{
if(IC_START_FLAG == 1) // 已經捕獲到了高電平
{
IC_TIMES++; // 捕獲次數加一
}
}
}
//定時器輸入捕獲中斷處理回調函數
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)// 捕獲中斷發生時執行
{
if(IC_DONE_FLAG == 0) // 未完成捕獲
{
if(IC_START_FLAG == 1) // 原來是高電平,現在捕獲到一個下降沿
{
IC_VALUE = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1); // 獲取捕獲值
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1); // 先清除原來的設置
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING);// 配置為上昇沿捕獲
IC_START_FLAG = 0; // 標志複比特
IC_DONE_FLAG = 1; // 完成一次高電平捕獲
}
else // 捕獲還未開始,第一次捕獲到上昇沿
{
IC_TIMES = 0; // 捕獲次數清零
IC_VALUE = 0; // 捕獲值清零
IC_START_FLAG = 1; // 設置捕獲到了上邊沿的標志
TIM_RESET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1); // 先清除原來的設置
TIM_SET_CAPTUREPOLARITY(htim,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING);// 配置為下降沿捕獲
}
__HAL_TIM_SET_COUNTER(htim,0); // 定時器計數值清零
}
}
/* USER CODE END 4 */
/** * @brief This function is executed in case of error occurrence. * @retval None */
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
實驗效果:
PA0 對應我開發板上的一個按鍵,當輕觸(未按下)該按鍵時,串口會不停打印一些無用的高電平持續時間,這些無用脈沖的持續時間很接近, 都是 10ms 左右,說明按鍵的抖動電平持續時間大約為 10ms。
當長按按鍵,再松開,就會打印按鍵按下的時間,比如下圖兩個被紅圈圈中的數據,第一次的高電平持續時間為4.35s,第二次高電平持續時間為 1.59s。

边栏推荐
- 静态网页服务器
- Drawing of clock dial
- FM signal, modulated signal and carrier
- c#中设置lable控件的TextAlign属性控制文字居中的方法
- 挖掘微生物暗物质——新思路
- 【红旗杯?】补题
- WebSocket的理解以及应用场景
- Atlas conflict Remote Code Execution Vulnerability (cve-2022-26134 vulnerability analysis and protection
- Allgero reports an error: program has encoded a problem and must exit The design will be saved as a . SAV file
- Anaconda based module installation and precautions
猜你喜欢

唐老师讲运算放大器(第七讲)——运放的应用

Importer des données dans MATLAB

c#ColorDialog更改文本颜色和FontDialog更改文本字体的使用示例

C disk drives, folders and file operations

Apache CouchDB 代码执行漏洞(CVE-2022-24706 )批量POC

Modeling and fault simulation of aircraft bleed system

深度学习系列48:DeepFaker

Static web server

Force buckle 272 Closest binary search tree value II recursion

Electronics: Lesson 008 - Experiment 6: very simple switches
随机推荐
洛谷P2486 [SDOI2011]染色(树链+线段树 + 树上区间合并 )
php数组函数大全
初体验完全托管型图数据库 Amazon Neptune
[supplementary question] 2021 Niuke summer multi school training camp 6-n
Force buckle 272 Closest binary search tree value II recursion
FM信号、调制信号和载波
Can transparent cloud gateway caniot and candtu record can messages and send and receive can data remotely
Mr. Tang's lecture on operational amplifier (Lecture 7) -- Application of operational amplifier
产品经理专业知识50篇(四)-从问题到能力提升:AMDGF模型工具
Bat start NET Core
电子学:第012课——实验 13:烧烤 LED
Black dot = = white dot (MST)
洛谷P1073 [NOIP2009 提高组] 最优贸易(分层图+最短路)
不怕百战失利,就怕灰心丧气
使用Adobe Acrobat Pro调整PDF页面为统一大小
Atlas conference vulnerability analysis collection
C disk drives, folders and file operations
三台西门子消防主机FC18配套CAN光端机进行光纤冗余环网组网测试
socket问题记录
Looking for b-end product manager after years? I almost ruined myself