当前位置:网站首页>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。
边栏推荐
- Opencv daily function structure analysis and shape descriptor (8) Fitline function fitting line
- Luogu p2839 [national training team]middle (two points + chairman tree + interval merging)
- [deep learning lightweight backbone] 2022 edgevits CVPR
- Machine learning notes linear regression of time series
- 洛谷P6822 [PA2012]Tax(最短路+边变点)
- Functions should not specify operation types through variables
- 牛客:飞行路线(分层图+最短路)
- [supplementary question] 2021 Niuke summer multi school training camp 1-3
- 420-二叉树的层序遍历2(429. N 叉树的层序遍历、515.在每个树行中找最大值、116.填充每个节点的下一个右侧节点指针、104.二叉树的最大深度、111.二叉树的最小深度)
- 电子学:第011课——实验 10:晶体管开关
猜你喜欢
三台西门子消防主机FC18配套CAN光端机进行光纤冗余环网组网测试
电子学:第014课——实验 15:防入侵报警器(第一部分)
Application of can optical transceiver of ring network redundant can/ optical fiber converter in fire alarm system
电子学:第010课——实验 8:继电振荡器
50 pieces of professional knowledge of Product Manager (IV) - from problem to ability improvement: amdgf model tool
自制坡道,可是真的很香
共话云原生数据库的未来
CAN总线工作状况和信号质量“体检”
基于Anaconda的模块安装与注意事项
剑指offer刷题(简单等级)
随机推荐
Introduction to the main functions of the can & canfd comprehensive test and analysis software lkmaster of the new usbcan card can analyzer
图像超分综述:超长文一网打尽图像超分的前世今生 (附核心代码)
Electronics: Lesson 014 - Experiment 15: intrusion alarm (Part I)
电子学:第008课——实验 6:非常简单的开关
Bat start NET Core
Electronics: Lesson 012 - Experiment 13: barbecue LED
剑指offer刷题(中等等级)
【补题】2021牛客暑期多校训练营4-n
將數據導入到MATLAB
电子学:第012课——实验 13:烧烤 LED
Number theory template
Pychart's wonderful setting: copy immediately after canceling the comment and bring #
50. pow (x, n) - fast power
Electronics: Lesson 011 - experiment 10: transistor switches
洛谷P2486 [SDOI2011]染色(树链+线段树 + 树上区间合并 )
不怕百战失利,就怕灰心丧气
Niuke: flight route (layered map + shortest path)
TCP的那点玩意儿
Drawing of clock dial
Opencv minimum filtering (not limited to images)