TIM1はロータリエンコーダを受信する
   ----ロータリエンコーダ回路----

ロータリエンコーダとは、ボリウムのような使われ方をする一種のスイッチです.
秋月電子でこのようなものを売ってます.


ロータリエンコーダには、軸を回すとオン/オフするスイッチが2ヶ入っていて、プルアップ抵抗をとりつけるとパルスが2ヶ出てきます.
この2ヶをA相B相と名付けると、軸の回転に伴って下図のような90度位相のパルスが出てきます.
2相パルスを利用すると、回転方向の識別ができます.



以下では、ロータリエンコーダーを受信するプログラムについて解説します.



どんなものを作るか?

ロータリエンコーダを取り付けて、グルグルまわすと、termminalソフトに数値が表示されるようにします.
TerminalソフトはPC上で動くソフトです.
私はTeraTermというソフトを使っています.
http://www.vector.co.jp/soft/win95/net/se320973.html
PCとの接続は、STM8SのUARTをUSBシリアル変換基板でUSBに変換し、PCに接続することで実現します.
Terminalソフト画面はこんなイメージです.
左端に、ロータリエンコーダによって増減する数値を表示します.
その横に、バーグラフで数値を表示します.




仕様を決める

総合ブロック図
このようになります.TIM1にロータリエンコーダを接続します.

PCと通信するために、STM8SのUART端子をFT232というICでUSBに変換します.
そして、USBケーブルでPCに接続します.
PCではTerminalソフトをつかって通信します.
したがって、PCを操作する人間から見えるのは、terminal ソフトという通信アプリの画面です.



STM8S内部回路構成
TIM1
ロータリエンコーダ
外部ピンのアサインはSTM8S105C6のハードウエア的に次のように決められています.
A相接続先    TIM1_CH1  PC1 (26pin) (CN2-2)
B相接続先    TIM1_CH2  PC2 (27pin) (CN2-3)
割り込みあり
UART
PCとの通信用
外部ピンのアサインはSTM8S105C6のハードウエア的に次のように決められています.
UART2_RX  PD6  (47pin)
UART2_TX  PD5  (46pin)
割り込みあり
clock
内部16MHzを使用(HSI)
16MHz


割り込み構成
TIM1 trigger割り込み
11番
ロータリエンコーダが動いたら割り込みをかける
UART受信割り込み
21番
UARTが文字を受信したら割り込みをかける



ハードウエアの準備

①USBシリアル変換基板
まず、UARTをUSBに変換する回路を、STM8S-DISCOVERYにどうやって取り付けるかを説明します.
秋月電子で売っているUSBシリアル変換基板をSTM8S-DISCOVERYに取り付けます.
回路図は下記です.配線は下記回路図の4本です.
●STM8SのPD5とPD6には、UARTの送信信号と受信信号が出てきますので、それをUSBシリアル基板に配線しています.
STM8S-DISCOVERYでは、電源はFLASH焼き回路から5V or 3.3Vが供給されますが、これを切断し、USBシリアル変換基板から3.3Vが供給されるようにします.
そのために、ジャンパーをつぎの1)2)のように設定 してください.
  1) USBシリアル変換基板のジャンパーピンは、下図の赤色の位置にします.
  2) STM8S-DISCOVERYのJP1のジャンパーピンは、外しておきます.



USBシリアル変換基板の取付完了状態はこのようになります.
右の黄色の囲いがUSBシリアル変換回路です.同基板に刺さっているMini-USBがPCのUSBへ行きます.
中央の水色の囲いがSTM8Sマイコンです.USBシリアル変換回路と配線されています.
左の赤い囲いは、ブロック図には描きませんでしたがSTM8Sを焼くために必須な回路です.同基板に刺さっているUSBケーブルは、STM8Sを焼くためのケーブルです.やはりPCに接続します.
この回路は、PCのUSBポートを都合2ヶ占有することになります.


プログラムを焼いてしまえば、焼き用のUSBケーブルは不要になるわけで、下図のようにUSBを外してしまいたくなるのが人情です.
ところが、USBケーブルを外しただけでは、STM8Sにリセットがかかってしまって動かなくなってしまいます.
そこで、下の写真の○の中にあるSB1とSB2をハンダごてでOPENにしてやればリセット線が切断されるので、STM8Sが動きます.
この方法は憶えておきましょう.



②TIM1_CH1の改造
STM8S-DISCOVERY基板のオリジナルではTIM1_CH1はタッチパネル機能に使われており、CN2-2に接続されていません.

ハンダごてで改造が必要です.
下の写真のようにSB3のハンダジャンパーを付け替えてください.
写真がわかりにくいかもですが、

①②③と3つのランドが並んでいるとして、オリジナルのSTM8S-DISCOVERYでは②+③がハンダショートされています.
これを変更し①+②をハンダでショートさせます.
そうすることでTIM1_CH1がCN2-2に接続されます.
この写真は改造後です.


③ロータリエンコーダの接続
最後に、ロータリエンコーダを接続します.
  -TIM1_CH1  PC1 (26pin) (CN2-2)    ロータリエンコーダのA相
  -
TIM1_CH2  PC2 (27pin) (CN2-3)    ロータリエンコーダのB相
A-Bを逆にしてしまったとしても回転方向が逆になるだけですのでそんなに気にしなくてもOKです.
プルアップ抵抗は10kΩを推奨しますが、1kΩ~100kΩの範囲ならどれでも動きます.(1kΩだと消費電流が少し増えますがね)





ソースコードの解説

サンプルプログラムはこちらをダウンロードしてください.
このサンプルプログラムには、複数のprojectが入っています.
active projectの設定はこうしてください.


以下は、project名test08のmain.c ファイルの解説です.

●#include "stdio.h"
#include "string.h"
Cの標準関数のsprintf()とかstrcat()を使いますので、インクルードします.

●#include "stm8s.h"
STMicro社提供のライブラリをつかうにはこれをインクルードします.

●#include "usrlib-uart.h"
UART関係のルーチンを usrlib-uart.c にまとめましたので、これをインクルードします.
usrlib-uart.cの解説は省略します.

●CLK_ClockSwitchConfig(CLK_SWITCHMODE_AUTO, CLK_SOURCE_HSI, DISABLE, CLK_CURRENTCLOCKSTATE_DISABLE);
CLK_HSIPrescalerConfig( CLK_PRESCALER_HSIDIV1 );
CLK_SYSCLKConfig( CLK_PRESCALER_CPUDIV1 );
クロックを、内部発信の16MHz(HSI)に設定しています.
また、プリスケーラの分周比を1に設定することで、CPUに16MHzのクロックが供給されます.
タイマにも16MHzが供給されます.
 
●GPIO_Init(GPIOD, GPIO_PIN_0, GPIO_MODE_OUT_PP_HIGH_FAST);
インジケータとして、LEDをチカチカさせます.そのためにPD0を出力に設定しています.

●TIM1_DeInit();
TIM1_EncoderInterfaceConfig(TIM1_ENCODERMODE_TI1, TIM1_ICPOLARITY_RISING, TIM1_ICPOLARITY_RISING);
TIM1_CounterModeConfig(TIM1_COUNTERMODE_CENTERALIGNED1);
TIM1_SetCounter(70);
TIM1_SetAutoreload(140);
タイマ1をエンコーダ受信機能として設定します.
ロータリエンコーダを右回しすると16bitカウンタが増加します.
ロータリエンコーダを左回しすると16bitカウンタが減少します.
140という数字は、16bitカウンタが0~140の範囲で動くように設定しています.
70という数字は16bitカウンタの初期値を70に設定するためです.
TIM1_COUNTERMODE_CENTERALIGNED1という引数が見えます.これがあるおかげで、ロータリーエンコーダを延々右回ししても140に貼り付き、延々左回ししても0に貼り付くという挙動を実現しています.


①TIM1->CCMR1 = ( (TIM1->CCMR1 & ~TIM1_CCMR_ICxF) | 15<<4 );
②TIM1_SelectInputTrigger(TIM1_TS_TI1FP1);
③TIM1_ITConfig(TIM1_IT_TRIGGER, ENABLE);

ロータリエンコーダが変化したら、割り込みをかけるように設定しています.
①はスイッチのチャタリング防止のためのフィルタの設定です.フィルタだけを変更するライブラリ関数が見当たらなかったのでレジスタを直に叩いています.
②により、TIM1_CH1由来の信号(A相)をTIM1のTRIGGERとして採用しています.
③は、そのTRIGGERで割り込むように設定しています.

AB2相ロータリエンコーダにはチャタリングを阻止する効果があると上で書きましたが、なぜここで改めてチャタリング防止をするのでしょうか?
それは、上のタイミングチャートのカウント動作については、AB2相方式によってチャタリングを阻止できるのですが、こと割り込みについてはA相信号をそのまま割り込みに使っています.
ゆえに、A相にチャタリングがあると、割り込みがカンカンカンカンと発生してなんか嫌な感じだなと.
それでフィルタを入れています.
もっとも、後述の
TIM1_ClearFlag(TIM1_FLAG_TRIGGER)で多重割り込みの被害を防止してはいるのですが、念のためです.
チャタリング防止フィルタの引数は次のようになっています.数字が増えるほどフィルタが強くなります.
test08では最強の15に設定しています.
それでも
16MHz÷32÷8 = 16uSec と短すぎて焼け石に水ではあるがな.......
  0: フィルタなし
  1: fMASTERで2クロック後に再チェック
  2: fMASTERで4クロック後に再チェック
  3: fMASTERで8クロック後に再チェック
  4: fMASTER/2で6クロック後に再チェック
  5: fMASTER/2で8クロック後に再チェック
  6: fMASTER/4で6クロック後に再チェック
  7: fMASTER/4で8クロック後に再チェック
  8: fMASTER/8で6クロック後に再チェック
  9: fMASTER/8で8クロック後に再チェック
10: fMASTER/16で5クロック後に再チェック
11: fMASTER/16で6クロック後に再チェック
12: fMASTER/16で8クロック後に再チェック
13: fMASTER/32で5クロック後に再チェック
14: fMASTER/32で6クロック後に再チェック
15: fMASTER/32で8クロック後に再チェック  


●TIM1_Cmd(ENABLE);
TIM1を起動します.

●UART2_DeInit();

UART2_Init((u32)115200, UART2_WORDLENGTH_9D, UART2_STOPBITS_1, UART2_PARITY_ODD, UART2_SYNCMODE_CLOCK_DISABLE, UART2_MODE_TXRX_ENABLE);   
UART2_ITConfig(UART2_IT_RXNE_OR, ENABLE);
UART2_Cmd(ENABLE);
UARTの設定です.説明は省略します.

●enableInterrupts();
割り込みを動かします.

●while (1)    {    }
メーンルーチンの仕事はおしまい.無限ループをまわします.

●TIM1_Trigger_isr(void) interrupt 11
TIM1の割り込み処理ルーチンです.割り込みがかかるとこのルーチンに入ってきます.
interrupt 11という修飾子が、TIM1のTRIGGER割り込みだという意味を持っています.
なんで11番なのかは、こちらの一覧表に書かれています.

●GPIO_WriteReverse(GPIOD,GPIO_PIN_0);
インジケータとしてLEDを点滅させます.

●TIM1_CNT_value = TIM1_GetCounter()/2;
16bitカウンタの値を読みます.
よーくみると÷2してます.
この理由は、ロータリエンコーダの仕様が、1クリック回転させると2パルス出る仕様なようなので、1クリック回転で数値が1増減するように補正したいからです.

●sprintf(TXStr,"%3u ",TIM1_CNT_value);
SerialPutString(TXStr);
TXStr[0]=0;
UART経由でterminalソフトに16bitカウンタの値を表示します.

●while(1)    {
if(TIM1_CNT_value==0) break;
TIM1_CNT_value--;
strcat(TXStr,"*");    }
SerialPutString(TXStr);
SerialPutString("\r\n");
UART経由でterminalソフトに16bitカウンタの値のバーグラフを表示します.

●TIM1_ClearFlag(TIM1_FLAG_TRIGGER);
次回の割り込みに備えてフラグをクリアします.



サンプルプログラムの動作結果

いずれのprojectでも、terminalソフトには、ロータリエンコーダの回転に応じてバーコードが増減する様が表示されます.


下記は、test08~test08eまでのprojectのTIM1設定のキモのところを抜粋してあります.
引数を変えると動作がどう変わるかを検証した結果です.

test08
TIM1_EncoderInterfaceConfig(TIM1_ENCODERMODE_TI1, TIM1_ICPOLARITY_RISING, TIM1_ICPOLARITY_RISING);
TIM1_CounterModeConfig(TIM1_COUNTERMODE_CENTERALIGNED1);
TIM1_SetCounter(70);
TIM1_SetAutoreload(140);
TIM1_SelectInputTrigger(TIM1_TS_TI1FP1);
TIM1_ITConfig(TIM1_IT_TRIGGER, ENABLE);


動作は、右回しでカウンタ増、左回しでカウンタ減 となっています.

test08b
TIM1_EncoderInterfaceConfig(TIM1_ENCODERMODE_TI1, TIM1_ICPOLARITY_FALLING, TIM1_ICPOLARITY_FALLING);
TIM1_CounterModeConfig(TIM1_COUNTERMODE_CENTERALIGNED1);
TIM1_SetCounter(70);
TIM1_SetAutoreload(140);
TIM1_SelectInputTrigger(TIM1_TS_TI1FP1);
TIM1_ITConfig(TIM1_IT_TRIGGER, ENABLE);


引数をFALLINGにした.
動作は、右回しでカウンタ増、左回しでカウンタ減で、test08と同じ.

test08c
TIM1_EncoderInterfaceConfig(TIM1_ENCODERMODE_TI1, TIM1_ICPOLARITY_RISING, TIM1_ICPOLARITY_FALLING);
TIM1_CounterModeConfig(TIM1_COUNTERMODE_CENTERALIGNED1);
TIM1_SetCounter(70);
TIM1_SetAutoreload(140);
TIM1_SelectInputTrigger(TIM1_TS_TI1FP1);
TIM1_ITConfig(TIM1_IT_TRIGGER, ENABLE);


引数を、RISING/FALLINGにしたところ、
動作は、右回しでカウンタ減、左回しでカウンタ増で、test08と逆転します.
2相パルスの片側だけを反転すると、回転方向の検出結果が逆転してしまうんですね.

test08d
TIM1_EncoderInterfaceConfig(TIM1_ENCODERMODE_TI1, TIM1_ICPOLARITY_FALLING, TIM1_ICPOLARITY_RISING);
TIM1_CounterModeConfig(TIM1_COUNTERMODE_CENTERALIGNED1);
TIM1_SetCounter(70);
TIM1_SetAutoreload(140);
TIM1_SelectInputTrigger(TIM1_TS_TI1FP1);
TIM1_ITConfig(TIM1_IT_TRIGGER, ENABLE);


引数を、FALLING/RISINGにしたところ、
動作は、右回しでカウンタ減、左回しでカウンタ増で、test08と逆転します.
2相パルスの片側だけを反転すると、回転方向の検出結果が逆転してしまうんですね.

test08e
TIM1_EncoderInterfaceConfig(TIM1_ENCODERMODE_TI1, TIM1_ICPOLARITY_RISING, TIM1_ICPOLARITY_RISING);
TIM1_CounterModeConfig(TIM1_COUNTERMODE_UP);
TIM1_SetCounter(70);
TIM1_SetAutoreload(140);

TIM1_SelectInputTrigger(TIM1_TS_TI1FP1);
TIM1_ITConfig(TIM1_IT_TRIGGER, ENABLE);


TIM1_COUNTERMODE_UP という引数に変えました.
この引数には、下記の5つの選択肢がありますが、エンコーダとしては.....
CENTERALIGNED1 だけ憶えておけばいいでしょう.
なぜなら、.....UPと.....DOWNでは、
カウンタが0に到達すると140に飛んでしまうんです.
カウンタが140に到達すると0に飛んでしまうんです.
そんな動作させてエンコーダを使うことなんかないですよね?
.....CENTERALIGNED2  と .....CENTERALIGNED3 という引数もありますが、エンコーダでは使わないと思うので、憶えておかなくてOKです.
  TIM1_COUNTERMODE_UP  
  TIM1_COUNTERMODE_DOWN  
  TIM1_COUNTERMODE_CENTERALIGNED1  
  TIM1_COUNTERMODE_CENTERALIGNED2  
  TIM1_COUNTERMODE_CENTERALIGNED3  


inserted by FC2 system