モータの鳴らし方byHanDen

電子工作の初心者がモータを演奏したりVVVF音を再現したりする方法を紹介するブログ ホビー向けの電子工作を基礎から書いていきます 記事のミス等のお問い合わせはTwitterにてお願いします。 当ブログを参考に製作をする際は必ず自己責任にて行ってください 当ブログを参考にしたことによる損害等の責任は一切負いません ドメイン取得につきURLを http://vvvf.blog.jp から http://blog.henden.net に変更しました

2018年02月

制御編 第8回 マイコンのPWM出力 その1 可変周波数PWMの理論とパラメータ決定

前回はデジタル入出力のお話をしましたが、今回は疑似アナログ出力ともいわれるPWMの出力を紹介します。PWMと言ってもなにかわからないかもしれないので、まずは、PWMがどんなものであるかをお見せしましょう。
1
この図の波形は電圧の波形を示しています。PWMというのはこのようにONOFFを高速で繰り返して、疑似的にアナログ出力を行おうとするものです。出力電圧は以下の式で表せます。

Vin:入力電圧[V]

Vout:出力電圧[V]

T:PWM周期[s]

t:ON時間[s]

Vout=Vin×(t/T)

つまり、ONの時間とOFFの時間の割合を調整して、0Vから入力電圧の範囲でいろいろな電圧を作り出せるというわけです。そして、PWM周期とON時間の比をデューティー比と言います。

そして、モータを演奏するうえでは非常に有用なのがPWM周期です。PWM周期が短くなると高い音周波数が高い音)が出て、PWM周期が短くなると低い音周波数が低い音)なります。ちなみに、周期から周波数は以下の式で変換できます。

f:PWM周波数[Hz]

f = 1/T

この式より、PWM周期が大きいとPWM周波数が低くなり低い音が、PWM周期が小さいとPWM周波数が高くなり高い音が出ることがよくわかると思います。つまり、モータ演奏はこのPWM周波数を調整して音色を演奏するというわけです。この章では、PWMの出力方法はもちろん、PWMの周波数の変更方法も紹介していきます。

 

マイコンのタイマーの動作

マイコンのPWM出力はArudinoでいうdelay()のようなものを使って、手動でONOFFを繰り返して出力するものではありません。マイコンのPWMはマイコン内部に搭載されているタイマーと呼ばれる機能を使って、波形を生成しています。

今回使用している「atmega328P」の場合はタイマーが3つ搭載されており、内部の最大カウントが8ビット(255)の8ビットタイマーが2つ、最大カウントが16ビット(65535)16ビットタイマーが1つ搭載されています。それぞれのタイマーで出せる周波数やデューティー比の精度が大幅に変わってきます。(もちろん16ビットタイマーの方が精度を出しやすい)

話ばかりでは、分からないと思うので、実際にマイコンのタイマーがどのようなものであるかをデータシート(https://avr.jp/user/DS/PDF/mega328P.pdf )から見てみましょう。データシートの89ページからタイマーについて書かれているページがあります。

はじめにタイマー全体の概要を紹介するために例として、高速PWM動作を使ってみます。
2

マイコンのタイマーは、内部的に図の→で示したようなのこぎり波を描くカウンタがあります。時間(CPUのクロック数)に応じてこのカウンタの値が変化し、こののこぎり波と閾値を比較してPWMの波形を生成しています。CPUのクロック数に応じてPWMの波形を生成しているため、一定で正確なPWM周波数とデューティー比を出力できるというわけです。PWMは図で示したように、のこぎり波の値が閾値より小さい時ON状態になっていて、閾値より大きい時にOFF状態となってことがわかります。

マイコンでは、この上下する波形の形や、最大高さ傾き変更することができます。これらの変化させることで、生成するPWMの周波数を変更することができます。ここからは、波形の形、最大高さ、傾きによりPWM周波数が変えれることを紹介していきます。

 

波形の形

波形の形は2種類あり、先ほど示したようなカウンタが最大値となると0に戻るのこぎり波を描くタイプと、カウンタが最大値になると値が降下し始める三角波を描くタイプの2種類があります。この2種類の波形は動作種別により設定ができます。前者のタイプの波形は「高速PWM動作」を選んだ場合に生成され、後者のタイプの波形は「位相基準PWM動作」または「位相/周波数基準PWM」を選んだ場合に生成されます。

傾きと最大高さが同じ場合に、PWMの出力波形がどのようになるかを図示してみます。
3

この波形の図からもわかるように、のこぎり波を生成する「高速PWM動作」で生成されるPWM周波数は、三角波を生成する「位相基準動作」「位相/周波数基準動作」(位相/周波数基準動作はタイマー1のみ)で生成されるPWM周波数の約2倍の周波数になっていることがわかります。(約とつけたのは、完全な2倍ではないため)これによって、PWM周波数を2倍に変化させることはできますが、これだけでは音色を奏でることはできません。 そのため、ほかのテクニックを組み合わせる必要があります。

また、データシートの「位相基準動作」のページを確認すると、「位相/周波数基準動作」が使えるタイマー1の場合はTOP(波の最大高さ)を変更する場合は、「位相/周波数基準動作の使用を推奨する」と書かれています。そのため、今回使用する動作種別は「高速PWM動作」と、「位相/周波数基準動作」(タイマー1)or「位相基準動作」(タイマー0,1)をタイマーに応じて使い分け、各波形ごとに1種類の種別を使うことにします。

 

波の最大高さ

先ほどは、波の形を変えて周波数を変化させようとしましたが、今度は波の最大値(高さ)を変化させてPWM周波数を変化させる方法を紹介します。

傾きが同じ場合に最大値を変化させると生成される周波数が変化するというわけですが、言葉ではわかりにくいと思うので、どのようになるのかを図示してみます。
3

この図から、傾きが同じでも波の高さを高くすればPWM周波数は低くなり、高さを低くすればPWM周波数が高くなることがわかります。これより、PWMの周波数は波の高さに反比例することがわかると思います。

 波の最大高さの設定は、PWMの出力の設定によって可否が変わります。周波数を可変するPWMを生成する場合は、最大値が固定ではなくレジスタで設定できるものを選びます。(可変しない場合はTOP値0xFF(255)のものを選ぶ)8ビットタイマーである「タイマー0」と「タイマー2」の場合は波の最大高さはOCR0A」、「OCR2A」のレジスタで設定できます。また、16ビットタイマーである「タイマー1」の場合は波の最大高さを「ICR1」かOCR1Aで設定できますが、PWM周波数を頻繁に変える場合「ICR1」を使うと正しく動作しません。(データシートによると「ICR1」を変更する際に2重緩衝がされないため、条件によって正しく動作なると書かれており、波の高さを変更する場合は「OCR1A」の利用を推奨すると書かれています。筆者の環境で「ICR1」で周波数可変を試しましたが、正しく音が出ないときが発生しました。)そのため、波の高さを変える(周波数を変える)場合に使える波の最大値(TOP値)の設定レジスタは「OCR*Aとなります。

 次に、各タイマーで使える波の高さの最大値について紹介します。設定できる波の高さの最大値は8ビットタイマー(タイマー0,2)と16ビットタイマー(タイマー1)で大きく違うことに注意しなければなりません。 

8ビットタイマーはその名の通り、内部処理を8ビットで行っています。そのため、設定できる波の高さの最大値も8ビットで表せる数に限られます。つまり、最大値は255(2^8-1)というわけです。また、PWMを生成するにあたって波の高さが最低2は必要(最大高さ1以下だと、閾値との比較ができずPWMとして成立しない)です。そのため、原理上設定できる波の高さは2255というわけです。しかし、波の高さが低いとduty比の変更が困難になってくるので、実質的に使用できる波の高さの最低値は20~30程度として、実使用においての波の高さの範囲は30255となります。

16ビットタイマーの場合は内部処理が16ビットで行われているので波の最大高さは65,535となり、実質的に波の高さとして設定できるのは20~65,535となります。そのため、周波数の設定の自由度は圧倒的に16ビットタイマーの方が高いというわけです。

 

波の傾き

 波の形や高さを変えて周波数を変化させる方法以外にも波の傾きを変更して周波数を変更する方法を紹介しましたが、次は波の傾きを変えて周波数を変更する方法を紹介します。先ほどまでの方法に比べると直感的にもわかりやすいとは思いますが、今回も図示してみます。5

 この図から、傾きを小さくするとPWM周波数が低くなり、傾きを大きくするとPWM周波数が高くなることがわかります。これより、PWMの周波数は波の傾きからも変更できることが明らかですね。

 AVRマイコンではタイマーの波の傾きは、マイコンに接続されている水晶発振子の周波数をもとに決められています。ですが、プログラム上からは水晶発振子の周波数を変更することは基本的にできません。そのため、タイマーには分周器という水晶が一定の回数発振したら、カウントを1つ進めるという機能が搭載されています。この分周器の設定を変更することによりタイマーのカウンタの傾きを変更できるというわけです。それでは分周器を使った場合のタイマーの波形のイメージを実際に図示してみましょう。
7

分周のイメージとしては、このような感じのイメージになっています。(実際の動作とはちょっと違うかもしれないけど)つまり、分周をした分だけ傾きが小さくなります。図で示した8分周の場合は傾きが1/8になり、結果として出力される周波数も1/8となります。

分周器で設定できる分周はタイマーごとに決められており、Atmega328Pの場合設定できる分周は以下の通りです。
8

このようにいくつかの決められた分周の中から選択をすることで大まかな、周波数を決めることができます。また、外部からクロックを与えることでPWMを生成することも可能です。(ここでは紹介しません)

 

出力周波数の設計

先ほどまでは、PWMの理論と周波数を変える理論の紹介を行いましたが、次は実際に周波数を変えるためのパラメータの決定を行っていきたいと思います。設計の手順としては

波の形→波の傾き→波の高さ

で設計を行います。

初めに、波の形を決めます。のこぎり波の高速PWM動作三角波の位相基準動作(タイマー0,2)位相/周波数基準操作(タイマー1)のどちらかから選びます。特に理由がなければ三角波を選んでおけばよいでしょう。なお、ここで選ぶ波形によって使用する数式が変わるので注意が必要です。波の形を決めたら、周波数とパラメータの関係の数式からパラメータ選定を行います。データシートから出力周波数は以下の式で表せることがわかります。

9
初めに、この式に変形して以下のようにします。

10
そして、この式に、出力したいPWM周波数とシステムクロック(水晶の周波数)、そして適当な分周の値(8ビットタイマーで可聴周波数の場合は64分周程度を推奨)を代入して、TOP値を計算します。TOP値の計算結果が255(8ビットタイマーであるタイマー0,2),65535(16ビットタイマーであるタイマー1)を超えた場合分周の値を大きくします。逆にTOP値の計算結果が30を下回った場合分周の値を小さくします。分周の値を決めて、この式で計算したTOP値を波の高さとして設定すれば、出力したい周波数のPWMを出力することができます。

PS. 16ビットタイマーの場合でもTOP値が大きくなると扱いにくい場合は、必要に応じて分周の値を大きくしてください。

 

以上で任意の周波数のPWMを生成するための設定のパラメータは求まりました。次回は実際にプログラムに入力する方法を紹介します。

制御編 第7回 AVRマイコンのデジタル入出力

今回からは、実際にマイコンを使って特定の動作を行うプログラミングのお話を行っていきたいと思います。ここでは、レジスタの設定情報はもちろん、サンプルのプログラムも合わせて紹介していきたいと思います。

 

今回はマイコンの基本中の基本であるデジタル入出力のお話を行いたいと思います。

 

入出力設定

マイコンを使うときには初めにマイコンにピンを入力として使うか、出力として使うかを設定しなければなりません。

ピンを入力モードとして設定すると、インピーダンスは高くなります。逆に出力モードとして設定を行うと、ピンのインピーダンスは低くなります。そのため、回路上入力になる端子を出力設定すると、場合によっては過電流が流れマイコンが破損する場合があります。逆に回路上出力の個所を入力設定にすると出力先をドライブできなくなりますが、マイコンが破損する心配はほぼありません。(出力の条件次第(出力先の入力部をプルダウンやプルアップしていないなど)で出力先の回路が壊れる危険性はありますが…)この理由からAVRマイコンでは初期設定では、ピンは入力モードになっています。

レジスタ設定

入出力の設定を行うレジスタはAtmega328のデータシート(https://avr.jp/user/DS/PDF/mega328P.pdf )65ページから67ページにかけて書かれています。これより、PORTB,C,Dそれぞれの入出力を設定するレジスタはDDRB,C,Dであることがわかります。

例としてPORTBの入出力の設定レジスタであるDDRBのリストを引用します。
no title

DDRB」レジスタの0ビット目から7ビット目までそれぞれPB0~PB7までの入出力の設定を行うレジスタビットになっています。それぞれのピンに対応するビットを0にすると入力、1にすると出力になります。初期設定は上の図の「リセット値」になるのですべて0、つまりすべて入力モードになっています。

また、PORTBの場合以下のようにPB6,7(下図赤□)が水晶端子と重なっていることがわかります。1

このように特定の機能を持ったピンがある場合は、そのピンの持つ機能に合わせて入出力を設定します。今回のPB6,7は水晶入力なので、入力の設定にしておきます。(ピンの特殊機能を使う場合は基本的にここでの入出力設定は無視されて、正しい設定になるはずですが、一応保険のためにこのように設定しておきます。)

また、青で囲った部分は通常のピンとして利用できるピンです。このピンの中から使用するピンの入出力の設定を行います。(この中にはSPI通信の機能を持ったピンもあるので必要に応じて使い分けてください)

 

例として、PB1,3を出力、PB0,2,4,5を入力とする場合のレジスタ設定を紹介します。

2

上位の2ビットは使用不可なので0(入力)とし、ほかは出力ピンを1入力ピンを0としてビットを並べて2進数としたものを、16進数に変換を行い桁数の削減を行います。

 

デジタル出力(出力状態設定)/内部プルアップ設定

入出力の設定が終われば次は、マイコンのデジタル出力の設定を行います。これは、マイコンのピンからHレベルの信号を出力するかLレベルの信号を出力するかの設定です。また、ピンを入力モードとしたときに、ピンをマイコン内部でプルアップするかしないかの設定も同じレジスタで行います。

レジスタ設定

ピンの出力状態の設定を行うレジスタはデータシートの6567ページより、PORTB,C,Dそれぞれ、PORTB,C,Dであることがわかります。また、データシートの55ページの以下の情報から、プルアップが可能なことがわかります。
3

図の青で囲った、「PUD(MCUR4bit)(プルアップ禁止)レジスタは初期設定で0、つまりプルアップ許可(禁止でない)となっています。そのため、入力状態(DDRB,C,Dの対応ビットが0)である時に、PORTB,C,Dの対応ビットを1にすると、そのピンがプルアップされた入力になるというわけです。

 

次に例としてPORTBを紹介します。今回もリストを引用します。
4

入出力の設定と同じく、「PORTB」レジスタの0ビット目から7ビット目までそれぞれ、PB0からPB7までのピンの出力状態の設定を行うレジスタとなっております。それぞれのピンに対応するビットを0にするとLが出力され、1にするとHが出力されるようになっています。また、初期時(リセット時)はすべてLが出力されます。

 

例として、PB2,3H出力、PB4,5L出力、PB1をプルアップ入力、PB0をハイインピーダンス入力とした場合のレジスタ設定を紹介します。

まずは入出力の設定

DDRB 0b00111100

DDRB = 0x3C

次に出力状態設定
5

入出力設定は、上位2ビットが使用不可、下位2ビットが入力で、ほかは出力なので、その通りにビットを並べ、入出力の設定を行います。そして、出力状態の設定はPB2,3H出力、つまり2,3ビットが1、またPB1がプルアップなので1ビットが1になり、ほかはLまたはHZ(ハイインピーダンス)なので0となります。こちらも、最終的には桁数削減のために16進数表記にしています。

 

デジタル入力(ピン状態読み込み)

先ほどの設定でピンの出力状態の設定はできました。ですが、出力のレジスタでは入力モードの時にピンの状態を読み込むことができません。そのため、入力状態を取得するときには「入力レジスタ」と言われる別のレジスタを使用します。

レジスタ設定

ピンの入力状態を読み込むレジスタは、先ほどと同じくデータシートの65~67ページより、PORTB,C,Dのそれぞれに対して、PINB,C,Dであることがわかります。例として、PORTBの入力レジスタである、PINBについて紹介します。
6

このレジスタも、0ビット目から7ビット目までそれぞれPB0~PB7に対応しており、1を読み込んだ時はピンの状態がHレベル、0を読み込んだ時はピンの状態がLレベルとなっております。

また、このレジスタは種別としては読み書きの両対応のレジスタになっていますが、基本的には読み込みとして使用します。(PINBに書き込みを行うと、対応するPORTBの状態がトグルしますが、使うことはないと思います)

例として、PB3の状態を読み込む方法を紹介します。
7

特定のピンのみの状態を読み込む場合はAND演算を使って、読み込みたいビットのみを取り出します。(ほかのピンの状態は無視する必要があるため)また、必要に応じてビットシフトを行って、ビットをずらします。

 

以上で今回の(デジタル)入出力のお話は終了です。

 

サンプルプログラム

今回紹介したデジタル入出力を使った、サンプルプログラムの紹介を行いたいと思います。今回は、PD7にアクティブLOWのスイッチを接続し、内部プルアップを設定します。また、PB5端子にアクティブHIGHのLEDを接続し、スイッチが押されている間点灯するプログラムとします。

 

List1 DigitalInOut

#include <avr/io.h>

int main(void)

{

  DDRB = 0x20;//PBを出力

  DDRD = 0x00;//PD7は入力

  PORTD = 0x80;//PD7をプルアップ

  

    /* Replace with your application code */

    while (1)

    {

    if(PIND & 0x80){//H状態=ボタンが押されていないとき

      PORTB &= ~0x20;//PB5L出力

    }else{//L状態=ボタンが押されているとき

      PORTB |= 0x20;  //PB5H出力

    }

  }

}

 

プログラムの構成としては、初めに初期設定として、入出力の設定とプルアップの設定を行います。その後の無限ループ内で、PD7の状態を読み込み、Hの場合はPB5L状態、Lの場合はPB5H状態にしています。また、if文は0か非0かの判定であるので、状態判別時のビットシフトは省略できます。

また、今回の条件の場合は別の書き方として、while(1)の中を下のようにもすることができます。

while (1)

{

  PORTB = ~((PIND & 0x80) >> 2);

}


こちらの書き方の考え方は、入力信号をANDとビットシフト、反転を行い、if文などを使わずに、そのまま出力しています。

この2つの書き方は場合により使い分ければよいと思いますが、後者は比較的使う場面は少ないと思います。(マイコン的な考え方ではありますが…)まあ、考え方として知っておけば損はしないと思います。

 

今回はマイコンの入出力の方法を紹介しました。次回はマイコンで音楽を奏でるミソであるPWMの出力のお話をしていきたいと思います。

ギャラリー
  • HDDを演奏する楽器を作ってみた
  • HDDを演奏する楽器を作ってみた
  • HDDを演奏する楽器を作ってみた
  • HDDを演奏する楽器を作ってみた
  • 制作日記5 細かな基板を注文してみた
  • 制作日記5 細かな基板を注文してみた
  • 制作日記5 細かな基板を注文してみた
  • 制作日記5 細かな基板を注文してみた
  • 制作日記5 細かな基板を注文してみた
アクセスカウンター
  • 今日:
  • 昨日:
  • 累計: