DATE: 2018/08/10(金)   CATEGORY: Nucleoマウス
Nucleo-32boardを使ったクラシックマウスの開発その13 ~TIM~


TIMを使った割り込みとPWM出力の話です。

特に新しいことはないのでいつもの通り備忘録です。


・割り込み

TIM機能を使って割り込み処理を行ってみたいと思います。
マウスを走らせる上でタイマー割り込みは欠かせません。

タイマーのカウントの仕組みを理解できていれば、
誰でもすぐに使えると思います。
機能のことでわからないことはリファレンスマニュアルを一読しましょう。

割り込み用のタイマーとしてTIM6を使います。
→TIMにはいくつか種類があり機能が異なるようです。
自分の用途に合わせて使用するTIMを選択しましょう。
リファレンスマニュアル参照。

それではCubeMXを使って設定していきましょう。

今回設定する割り込み周期は1 msとします。

TIM6設定

Timerへの供給クロックは72 MHzにしてあります。
プリスケーラは分周比であるのでカウント周期は、
\[1 \mathrm{MHz} = \frac{72 \times 10^6}{Prescaler+1} \]
となります。
これを1000倍すると1 msで割り込みが行えるので、
画像のような設定になります。
カウント値についても、-1することには気をつけましょう。

次にNVICの設定をしましょう。
TIMの設定をしただけでは、
カウントを数えてくれるようになっただけで割り込みは起きません。
どういう要因で割り込みが起きるかを設定します。

TIM6の割り込み要因

その後、System→NVIC
にて割り込み優先度を決定
します。

設定が終わったらコードを再生成して、
Lチカで動作確認を行いましょう。

まずは、自作のwait関数を作ります。
基本的にTIM内のレジスタを書き換える場合にはマクロ関数を使います。
→説明はHALマニュアルに書いてある


volatile uint32_t g_timCount;

void wait_ms(uint32_t waitTime) {

g_timCount = 0;
__HAL_TIM_SET_COUNTER(&htim6, 0);
while (g_timCount < waitTime) {
}

}


次に実際の割り込み処理を書いていきます。
割り込み関数はstm32f3xx_it.c内に定義されています。

今回は、関数内で割り込み回数を数えるだけでいいのでこれだけです。


void TIM6_DAC1_IRQHandler(void) {
/* USER CODE BEGIN TIM6_DAC1_IRQn 0 */

/* USER CODE END TIM6_DAC1_IRQn 0 */
HAL_TIM_IRQHandler(&htim6);
/* USER CODE BEGIN TIM6_DAC1_IRQn 1 */
g_timCount++;
/* USER CODE END TIM6_DAC1_IRQn 1 */
}


最後にmain文での動作を書いて終了です。
起動関数はいろいろ種類があるので、
用途によってどれを使うか確認しましょう。
何度も言いますが詳しくはHALマニュアルに書いてあります。

今回はカウントして割り込みができればいいので、
HAL_TIM_Base_Start_IT関数を使います。


/* USER CODE BEGIN 2 */
HAL_TIM_Base_Start_IT(&htim6);
/* USER CODE END 2 */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1) {
wait_ms(1000);
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_3);
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */

}
/* USER CODE END 3 */
}


Lチカできたら終了です。


・PWM出力

PWMの出力についてです。
スピーカーを使って確認してみます。

スピーカーはPA8に繋いであるので、
TIM1のCH1を使います。
項目が微妙に異なりますが、
基本は同じですね。

TIM1設定

コードを再生成したら、
HAL_TIM_PWM_Start関数でPWMを出せます。

音が鳴ったら確認完了です。

どうせなので、スピーカーの関数を作りましょう。

参考記事はこちら
タイマを使ったマイコンによる演奏 -AYATAKA WORKs

といっても簡単な物でいいので、
・音程を変えられる関数
・音楽データを再生する関数

くらいあればいいのではないでしょうか。

音程の周波数とカウンタ周期の関係は,
\[カウンタ周期= \frac{1\ \mathrm{MHz}}{目標周波数\ \mathrm{[Hz]}}-1 \]
ですね。

また、音データは音程と長さだけとして構造体を作りました。


typedef struct {
float interval;
uint16_t waittime;
} soundData;

const float DO = 261.626;
const float RE = 293.665;
const float MI = 329.628;
const float FA = 349.228;
const float SO = 391.995;
const float LA = 440.000;
const float SI = 493.883;

uint16_t calSoundCount(float tarHz) {
uint16_t tarCount;
if (tarHz == 0) {
tarCount = 0;
} else {
tarCount = (uint16_t) (1000000.0 / tarHz - 1.0);
}
return tarCount;
}

void playSoundData(uint16_t dataMaxNum
, soundData *playData) {

uint16_t i;
uint16_t cycleCount;

for (i = 0; i < dataMaxNum; i++) {
HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);
cycleCount = calSoundCount(playData[i].interval);
__HAL_TIM_SET_AUTORELOAD(&htim1 ,
cycleCount);
__HAL_TIM_SET_COMPARE(&htim1,
TIM_CHANNEL_1, cycleCount / 2);
HAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);
HAL_Delay(playData[i].waittime);
}
HAL_TIM_PWM_Stop(&htim1, TIM_CHANNEL_1);
}

#define TESTSOUNDNUM 8
soundData g_testSound[TESTSOUNDNUM];
void inputTestSound(void) {
g_testSound[0].interval = DO * 1.0;
g_testSound[0].waittime = 500;

g_testSound[1].interval = RE * 1.0;
g_testSound[1].waittime = 500;

g_testSound[2].interval = MI * 1.0;
g_testSound[2].waittime = 500;

g_testSound[3].interval = FA * 1.0;
g_testSound[3].waittime = 500;

g_testSound[4].interval = SO * 1.0;
g_testSound[4].waittime = 500;

g_testSound[5].interval = LA * 1.0;
g_testSound[5].waittime = 500;

g_testSound[6].interval = SI * 1.0;
g_testSound[6].waittime = 500;

g_testSound[7].interval = DO * 2.0;
g_testSound[7].waittime = 500;
}







スポンサーサイト

コメントの投稿

 管理者にだけ表示を許可する
Copyright © うむ夫の歩み. all rights reserved.