1.タイマーの紹介
STM32F1シリーズは、インターネットベースの製品に加えて8
、基本タイマー、汎用タイマー、高度なタイマーに分けられた合計タイマーです。
基本タイマー TIM6
とTIM7
、16ビットのみ、タイミングのみ、外部IOなしでカウントアップするタイマー。
汎用タイマー TIM2/3/4/5
は、カウントアップ/カウントダウンが可能な16ビットタイマーで、計時、出力比較、入力キャプチャが可能です。各タイマーには4つの外部IOがあります。
アドバンストタイマー TIM1/8
は、カウントアップ/カウントダウンが可能な16ビットタイマーで、計時、出力比較、入力キャプチャ、および三相モーター相補出力信号が可能です。各タイマーには8つの外部IOがあります。
2.新築
1. STM32CubeMXソフトウェアを開き、[新しいプロジェクト]をクリックします
2.MCUとパッケージを選択します
3.クロック
RCC設定を構成し、 HSE(外部高速クロック)を
選択し、水晶/セラミック共振子(水晶発振器/セラミック共振子)のクロック構成を選択し、システムクロックSYSCLKを72MHzに構成し、
HCLKの値を72に変更します。 Enterキーを押します。すべての構成を自動的に変更します
4.デバッグモード
を設定することは非常に重要なステップです。
設定しないと、最初のプログラミングプログラムの後でデバッガのSYS設定が認識されません。[シリアルワイヤとしてデバッグ]を選択してください。
5. GPIO
GPIO設定を構成し、右の図でLEDライトの対応するピンを見つけ、GPIO_Outputを選択し、低レベルのライトを出力します。カスタムラベルを追加できます。
3、TIM6基本タイマー
3.1パラメータ設定
設定のTimers
選択でTIM6
、Activated
アクティベーションを確認します
でParameter Settings
特定のパラメータ。
Tclkは内部クロックCK_INTであり、APB1プリスケーラーで除算されます。APB1プリスケーラー係数が1の場合、周波数は変更されません。それ以外の場合、周波数は2倍され、ライブラリ関数のAPB1プリスケーラー係数は2になります。つまり、図に示すように、PCLK1 = 36M、タイマークロックTclk = 36 * 2 = 72Mです。
タイマーオーバーフロー時間:
すべて= 1 /(Tclk /(psc + 1))∗(arr + 1)
- タイマークロックTclk:72MHz
- プリスケーラpsc:71
- 自動リロードレジスタ到着:999
即 Tout = 1/(72MHz/(71+1))∗(999+1) = 1ms
- プリスケーラ(クロックプリスケーラ番号):72-1
则驱动计数器的时钟 CK_CNT = CK_INT(即72MHz)/(71+1) = 1MHz
- カウンターモード:アップ(アップカウントモード)
基本定时器只能是向上计数
- カウンター期間(自動リロード値):1000-1
则定时时间 1/CK_CLK*(999+1) = 1ms
- auto-reload-preload(自動リロード):有効(有効) `
- TRGOパラメーター(トリガー出力):無効
在定时器的定时时间到达的时候输出一个信号(如:定时器更新产生TRGO信号来触发ADC的同步转换)
3.2NVIC構成
タイマー割り込みを有効にする
3.3コードを生成する
アプリケーション開発環境のパスアイテム名とアイテム入力の選択IDEMDK-ARM V5
各ペリフェラルはフックではなく個別の’.c/.h’
ファイルを生成します
:すべての初期化コードはmain.cで生成され
ますチェック済み:初期化コードファイルは関連するペリフェラルで生成されます。たとえば、GPIO初期化コードはgpio.cで生成されます。
[コードを生成]をクリックしてコードを生成します
3.4割り込みコールバック関数を変更する
stm32f1xx_it.c
割り込みサービスルーチンファイルを開き、TIM6_IRQHandler()
タイマー割り込みハンドラと呼ばれるTIM6割り込みサービスルーチン割り込みサービス関数を見つけます。HAL_TIM_IRQHandler()
stm32f1xx_hal_tim.c
ファイルを開き、タイマー割り込みハンドラーのプロトタイプを見つけますHAL_TIM_IRQHandler()
。その主な役割は、生成されるタイマー割り込みの種類を判別し、割り込みフラグをクリアしてから、割り込みコールバック関数を呼び出すことHAL_TIM_PeriodElapsedCallback()
です。
/ *注:この関数は変更しないでください。コールバックが必要な場合は
、HAL_GPIO_EXTI_Callbackをユーザーファイルに実装できます
* /
この関数は変更しないでください。コールバック関数を使用する必要がある場合は、関数を再実装してください。ユーザーファイル内。
HAL_TIM_PeriodElapsedCallback()
公式プロンプトによる__weak
と、弱められたフラグである関数を再度定義する必要があります。これを持つ関数は弱められた関数です。つまり、他の場所でまったく同じ名前とパラメーターを持つ関数を記述でき、コンパイラーは無視します。この関数。代わりにUNUSED(htim)
、作成した関数を実行します。これはエラー防止の定義です。渡されたタイマー番号が処理を行わない場合、コンパイラーは警告を報告しません。実際、開発中に割り込みサービス関数について気にする必要はありません。割り込みコールバック関数を見つけて書き直すだけで済みます。このコールバック関数には、ここには反映されていないもう1つの非常に便利な側面があります。複数の割り込みが有効になっている場合、STM32CubeMXは複数の割り込みのサービス関数を自動的に編成し、コールバック関数を呼び出します。つまり、割り込みの数に関係なく、コールバック関数を書き直して着信タイマー番号を判断するだけで済みます。
次にstm32f1xx_it.c
、このドキュメントの最後に追加しますHAL_TIM_PeriodElapsedCallback()
/* USER CODE BEGIN 1 */
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint32_t time = 0;
if(htim->Instance == TIM6) // 定时器6基地址
{
// 自定义应用程序
time++; // 每1ms进来1次
if(time == 1000) // 每1秒LED灯翻转一次
{
HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);
time = 0;
}
}
}
/* USER CODE END 1 */
3.5タイマースタート機能の追加
ここで、main関数に入り、whileループの前にタイマー関数を追加しますHAL_TIM_Base_Start_IT()
。ここで渡されるhtim6は、タイマーが初期化された直後の構造体です。
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* 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_TIM6_Init();
/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim6);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
実験的な現象は、LEDライトが1秒に1回点滅することです
3.6HALライブラリと標準ライブラリコードの比較
STM32CubeMXは、HALライブラリによって生成されたコードを使用します。
/**
* @brief TIM6 Initialization Function
* @param None
* @retval None
*/
static void MX_TIM6_Init(void)
{
/* USER CODE BEGIN TIM6_Init 0 */
/* USER CODE END TIM6_Init 0 */
TIM_MasterConfigTypeDef sMasterConfig = {
0};
/* USER CODE BEGIN TIM6_Init 1 */
/* USER CODE END TIM6_Init 1 */
htim6.Instance = TIM6;
htim6.Init.Prescaler = 72-1;
htim6.Init.CounterMode = TIM_COUNTERMODE_UP;
htim6.Init.Period = 1000-1;
htim6.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim6) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim6, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN TIM6_Init 2 */
/* USER CODE END TIM6_Init 2 */
}
/**
* @brief This function handles TIM6 global interrupt.
*/
void TIM6_IRQHandler(void)
{
/* USER CODE BEGIN TIM6_IRQn 0 */
/* USER CODE END TIM6_IRQn 0 */
HAL_TIM_IRQHandler(&htim6);
/* USER CODE BEGIN TIM6_IRQn 1 */
/* USER CODE END TIM6_IRQn 1 */
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint32_t time = 0;
if(htim->Instance == TIM6)
{
// 自定义应用程序
time++;
if(time == 1000)
{
HAL_GPIO_TogglePin(LED_G_GPIO_Port,LED_G_Pin);
time = 0;
}
}
}
HAL_TIM_Base_Start_IT(&htim6);
STM32標準ライブラリコードを使用します。
/**
@brief 定时器中断配置(使用TIM6基本定时器)
@param 无
@return 无
*/
void BASIC_TIM_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/*
可以从上图看出基本定时器和通用定时器使用APB1总线,
高级定时器使用APB2总线。
*/
// 开启定时器时钟,即内部时钟 CK_INT=72M
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
/*
预分频将输入时钟频率按1~65536之间的值任意分频,分频值决定了计数频率。
计数值为计数的个数,当计数寄存器的值达到计数值时,产生溢出,发生中断。
如系统时钟为72MHz,预分频 TIM_Prescaler = 71,
计数值 TIM_Period = 1000,
则 TIM_Period * (TIM_Prescaler + 1) / 72000000 = 0.001,
即每1ms产生一次中断。
*/
// 自动重装载寄存器周的值(计数值)
TIM_TimeBaseStructure.TIM_Period = 1000;
// 累计 TIM_Period 个频率后产生一个更新或者中断
// 时钟预分频数为 71,
// 则驱动计数器的时钟 CK_CNT = CK_INT / (71+1)=1M
TIM_TimeBaseStructure.TIM_Prescaler = 71;
// 时钟分频因子 ,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
// 计数器计数模式,基本定时器只能向上计数,没有计数模式的设置
//TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
// 重复计数器的值,基本定时器没有,不用管
//TIM_TimeBaseStructure.TIM_RepetitionCounter=0;
/*
完成时基设置
*/
// 初始化定时器
TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure);
/*
为了避免在设置时进入中断,这里需要清除中断标志位。
如果是向上计数模式(基本定时器采用向上计数),
则采用函数 TIM_ClearFlag(TIM6, TIM_FLAG_Update),
清除向上溢出中断标志。
*/
// 清除计数器中断标志位
TIM_ClearFlag(TIM6, TIM_FLAG_Update);
// 使能计数器
TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);
// 开启计数器
TIM_Cmd(TIM6, ENABLE);
// 暂时关闭定时器的时钟,等待使用
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, DISABLE);
}
/**
@brief NVIC初始化(使用TIM6基本定时器)
@param 无
@return 无
*/
void BASIC_TIM_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
// 设置中断组为 0
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
// 设置中断来源
NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn ;
// 设置主优先级为 0
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
// 设置抢占优先级为 3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
#define BASIC_TIM_IRQHandler TIM6_IRQHandler
// 1ms发生一次中断,time 记录中断次数
uint16_t time;
void BASIC_TIM_IRQHandler(void)
{
if(TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)
{
time++;
TIM_ClearITPendingBit(TIM6, TIM_FLAG_Update);
}
}
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
MX_TIM6_Init();
対応BASIC_TIM_Config();BASIC_TIM_NVIC_Config();
HAL_TIM_Base_Init(&htim6)
対応TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure)
HAL_TIM_Base_Start_IT(&htim6);
対応RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);
四、注意が必要な事項
追加するユーザーコードUSER CODE BEGIN N
とUSER CODE END N
その間に追加するユーザーコード。それ以外の場合は、STM32CubeMXがコードを再生成した後、次に使用すると削除されます。
• 2021年1月14日にLeungによって書かれました
•リファレンス:STM32CubeMXシリーズチュートリアル3:基本タイマー
STM32CubeMX実際の戦闘チュートリアル(4)-基本タイマー(まだ点灯)「組み込み-STM32
開発ガイド」パート2基本-第4章タイマー(HALライブラリ)