iPodから音楽データを取り出すという夢を見た。
先日突然、使っているMacBookのHDが音を立て、動かなくなりました…orz
CD-ROMブートさえもできない状況で、
仕方がないので、HDを換装しました。
バックアップを取ってなかったので、データは無くなっちゃいました。
自分の中で無くなって悲しいデータといえば、この3つ。
- 写真
- 音楽
- 開発中のアプリのソース
特に、音楽データは、量がそこそこあったし、
iTunes Storeで買ったものもあったので、
なかなか痛手だなぁ、と思っていたのですが、
使っていたiPod nano(第6世代)をPCにつないで、
認証すると、あらまぁ!
購入していた曲は元通りiTunesに。
ですが、もちろんCDから取り込んだ曲は入らず。
あきらめきれないので、方策を探した結果、いろんなツールがあるもんですねぇ。
(グレーゾーンじゃないかと思うので、紹介は控えますが…)
でも、iPodから音楽データを取り出すためだけにインストールしたくないので、
ツール類をインストールせずに自力でなんとかしてみよう…
という夢を見ました。
以下では、夢の内容を記します。
NSArrayのindexOfObjectの戻り値とNSNotFoundについて
最近、Objective-cについて勉強し始めました。
参考にさせていただいてるのはコチラ!
Cocoaの素、Objective-Cを知ろう − @IT
分かりやすくて、とても勉強になります!ありがとうございます!!
今のところ、(4)まで進んだのですが、
サンプルプログラムを動かしてみるとWarningが出てくる。
バージョンも変わってるだろうし、仕方がない。
具体的には、リンク先の下記の部分。
配列とループ処理を理解しよう (2/4):Cocoaの素、Objective-Cを知ろう(5) - @IT
//(4)ある要素のインデックスを取得
int objectIndex = [array indexOfObject:@"pqr"];
if (objectIndex == NSNotFound) {
NSLog(@"'pqr' is not found.\n");
}
出力されるWarningはこんな感じ。
$ gcc -o main main.m -framework Foundation
main.m: In function ‘main’:
main.m:20: warning: comparison is always false due to limited range of data type
調べてみたら「比較が常にfalseになってるよ」的なことだということで、
実行ファイルを実行してみると、案の定「NSLog(@"'pqr' is not found.\n");」が出てない。
ググると似たような人発見!
NSNotFoundにはまった。 - メソッド屋の日記
なるほどー!
難しいことはさておきw、
NSNotFoundの値が変わっちゃってるっていうことね。
で、どうすりゃいいんだ、っていうことでさらに調べたところ、
リファレンスによると、indexOfObjectの戻り値はNSUIntegerなので、以下のように修正。
// int objectIndex = [array indexOfObject:@"pqr"]; NSUInteger objectIndex = [array indexOfObject:@"pqr"];
無事、Warningも出なくなり、
「'pqr' is not found.」も出力されるようになりましたとさ。
めでたしめでたし。
職業訓練47日目 H8マイコン制御技術(LCD-SCI制御技術)
LCDの制御
使用したLCDはコチラ!(多分…)
LCDキャラクタディスプレイモジュール(16×2行バックライト無): ディスプレイ・表示器 秋月電子通商-電子部品・ネット通販
16文字×2行のアスキー文字を1文字につき5×7ドットあるいは5×10ドットで表示する。
外部との接続には、12本(2列6本)のピンヘッダを利用する。
LCDの基本構造
パス…。
またWikipediaで逃げよう…。
液晶ディスプレイ - Wikipedia
市販LCDの使用方法
表示データはアスキーコード形式で8ビットのまま、
あるいは8ビットを4ビットずつ2回に分けて読み込む。
表示データの読み込みは、すべて外部からの制御により行われる。
内部に読み込まれたデータは、文字表示位置に対応するメモリに書き込まれ、
書き改められるまで内容は保持され、表示され続ける。
データの転送と表示
初期設定完了後、表示データをLCDに転送可能な状態になる。
表示データはRAM領域の特定の場所を定義し、そこに書き込む。
そのデータを中継用RAM領域に1バイトずつ順番に読み出し、LCDに転送し、表示させる。
LCD表示プログラムの流れ
使用したテキストでは、LCDにSWのON/OFF状態を表示させたり、LEDをチカチカさせたり、
わざと?冗長なプログラムにしていたので、
バッサリ表示処理にしぼって、かつ、同じ処理を自分が勝手にまとめたプログラムをお送ります。
.CPU 300HA .SECTION VECT, CODE, LOCATE=H'000000 LCD_D: .EQU H'FFEF12 P3DR: .EQU H'FFFFC6 P3DDR: .EQU H'FFFFC4 RS: .BEQU 4,P3DR E_SIG .BEQU 5,P3DR LCD162 .EQU H'FFEF13 .SECTION ROM, CODE, LOCATE=H'000100 MOV.L #H'FFFF00, ER7 MOV.B #H'FF, R0H MOV.B R0H, @P3DDR ; LCDのソフトウェアリセット JSR @TIME5MS JSR @TIME5MS JSR @TIME5MS MOV.B #B'00100011, R0L JSR @LCD_RST JSR @LCD_RST JSR @LCD_RST MOV.B #B'00100010, R0L JSR @LCD_RST ; LCDの初期設定 MOV.B #B'00101000, R0L JSR @LCD_SET MOV.B #B'00001110, R0L JSR @LCD_SET MOV.B #B'00000110, R0L JSR @LCD_SET ; LCDの初期画面表示 MOV.B #B'00000001, R0L JSR @LCD_SET MOV.B #32, R0L MOV.L #LCD162, ER1 MOV.L #MOJI, ER2 INIT_DISP: MOV.B @ER2+, R0H MOV.B R0H, @ER1 INC.L #1, ER1 DEC.B R0L BNE INIT_DISP JSR @LCDDSP LOOP: JMP @LOOP ; ------------------------ ; サブルーチン ; ------------------------ ; LCD文字出力16文字×2行転送サブルーチン LCDDSP: PUSH.L ER0 PUSH.L ER1 MOV.B #B'00000010, R0L JSR @LCD_SET ; LCD表示1行目の16文字転送 MOV.B #16, R0L MOV.L #LCD162, ER1 LCDDSP1: MOV.B @ER1+, R0H MOV.B R0H, @LCD_D BSET RS JSR @LCD_OUT4 BCLR RS DEC.B R0L BNE LCDDSP1 MOV.B #B'11000000, R0L MOV.B R0L, @LCD_D BCLR RS JSR @LCD_OUT4 ; LCD表示2行目の16文字転送 MOV.B #16, R0L MOV.L #LCD162 + 16, ER1 LCDDSP2: MOV.B @ER1+, R0H MOV.B R0H, @LCD_D BSET RS JSR @LCD_OUT4 BCLR RS DEC.B R0L BNE LCDDSP2 POP.L ER1 POP.L ER0 RTS ; LCDにデータを転送 LCD_SET: MOV.B R0L, @LCD_D BCLR RS JSR @LCD_OUT4 JSR @TIME5MS RTS ; LCDのソフトウェアリセットサブルーチン LCD_RST: MOV.B R0L, @LCD_D BCLR RS JSR @LCD_OUT8 JSR @TIME5MS RTS ; LCDへのデータ/コマンドの転送(8ビット) LCD_OUT8: PUSH.L ER0 BSET E_SIG MOV.B @LCD_D, R0L MOV.B R0L, @P3DR JSR @TIME100US BCLR E_SIG JSR @TIME100US POP.L ER0 RTS ; LCDへのデータ/コマンドの転送(4ビット) ; 上位4ビット送出 LCD_OUT4: PUSH.L ER0 BSET E_SIG MOV.B @LCD_D, R0L SHLR.B R0L SHLR.B R0L SHLR.B R0L SHLR.B R0L JSR @OUT4 ; 上位4ビット送出 BSET E_SIG MOV.B @LCD_D, R0L JSR @OUT4 POP.L ER0 RTS ; 4ビット送出サブルーチン OUT4: AND.B #B'00001111, R0L MOV.B @P3DR, R0H AND.B #B'11110000, R0H OR.B R0H, R0L MOV.B R0L, @P3DR JSR @TIME5MS BCLR E_SIG JSR @TIME5MS RTS ; 5msのタイマ TIME5MS: MOV.L #10000, ER6 ER6DEC: DEC.L #1, ER6 NOP BNE ER6DEC RTS ; 100usのタイマ TIME100US: MOV.L #200, ER6 JSR @ER6DEC RTS .ALIGN 2 .SECTION LCDDATA, DATA, LOCATE=H'000B00 MOJI: .SDATA "H8/3048F TEST BD 1111 11111111 " .END
これでLCDに、
H8/3048F TEST BD 1111 11111111
って表示されるはず…。
手元に実機がないのにソースいじちゃったから動かない可能性大だけど…。
そして、本来は"1"の部分がSWのON/OFFで変化するんだけど…。
本当は、
(・ω・)ノ Hello, world!
って表示したかったんだけど、アスキーコードの転送の仕方が分からず、断念しました。
アァー、残念…orz
職業訓練46日目 H8マイコン入門(アセンブラ言語による実習)
割込み制御
割込み
実行中の処理を一時停止して、他の処理を行った後に最下位する機能。
●割込み動作の流れ
IRQ端子を使った割込み
IRQ0〜5端子に"0"を入力することで、割込みが発生する。
→使用するピンによって6種類の割込みプログラムを選択できる。
IRQイネーブルレジスタ(IER):端子別に許可/禁止の設定
IRQセンスコントロールレジスタ(ISCR):割込み信号の有効な動作タイミングを設定
実際に製作した回路は、
プッシュSWからシュミットトリガゲート(74LS19)を介して、
IRQ0端子に信号を入力する。
.CPU 300HA .SECTION PROG13, CODE, LOCATE=H'000000 .DATA.L MAIN ; メイン処理は、000000番地から .ORG H'000030 ; IRQ0の割込みベクタアドレス .DATA.L IRQ0 ; 割込みプログラムの開始アドレス IER .EQU H'FFFFF5 ; IRQイネーブルレジスタ ISCR .EQU H'FFFFF4 ; IRQセンスコントロールレジスタ P1DR .EQU H'FFFFC2 P1DDR .EQU H'FFFFC0 P5DR .EQU H'FFFFC8 P5DDR .EQU H'FFFFCA .SECTION ROM, CODE, LOCATE=H'000100 MAIN: MOV.L #H'FFF00, ER7 MOV.B #H'FF, R0L MOV.B R0L, @P1DDR MOV.B R0L, @P5DDR BSET #0, @IER ; IRQ0の割込みを許可 BSET #0, @ISCR ; 割込みパルスの立下がりエッジで割込みを行う LDC #0, CCR ; 割込み許可 MOV.B #B'01111111, R0L ; LED点灯データ(ポート1) LOOP: MOV.B R0L, @P1DR JSR @TIM2 ROTR.B R0L JMP @LOOP ;----- 割込み処理 ----- IRQ0: PUSH.W R0 ; レジスタの待避 PUSH.L ER5 PUSH.L ER6 MOV.B #D'3, R0H ; LED点灯回数データ YET: MOV.B #H'FF, R0L ; LED点灯データ(ポート5) JSR @TIM2 MOV.B #H'00, R0L MOV.B R0L, @P5DR JSR @TIM2 DEC.B R0H ; 点灯回数 - 1 BNE YET ; 点灯回数が0でなければ、YETへ POP.L ER6 ; レジスタの回復 POP.L ER5 POP.W R0 RTE ; 割込みから復旧 ;---------------------- TIM2: MOV.W #D'50, E5 ; 0.5秒のタイマサブルーチン L2: JSR @TIM1 DEC.W #1, E5 BNE L2 RTS TIM1: MOV.L #D'20000, ER6 ; 10msのタイマサブルーチン L1: DEC.L #1, ER6 NOP BNE L1 RTS .END
上記のプログラムを実行すると、
0.5秒ごとにポート1の点灯するLEDが1個ずつ移動していき、
プッシュSWが押されると、ポート1のLEDが停止し、ポート5のLEDが3回点滅、
その後、またポート1のLEDが移動し始めるはず。
NMI端子を使った割込み
NMIとは、マスク(禁止)できない「ノンマスカブル割込み」のこと。
CCRの割込みマスクビットを"0"にしても禁止できない、優先度の高い割込み。
NMI端子の入力エッジは、システムコントロールレジスタ(SYSCR)で設定。
製作した回路は、上記のIRQ端子を使った回路と一緒で、
割込み信号を入力する端子がNMI端子になっただけ。
.CPU 300HA .SECTION PROG14, CODE, LOCATE=H'000000 .DATA.L MAIN ; メイン処理は、000000番地から .ORG H'00001C ; NMIの割込みベクタアドレス .DATA.L IRQ0 ; 割込みプログラムの開始アドレス SYSCR .EQU H'FFFFF2 ; システムコントロールレジスタ P1DR .EQU H'FFFFC2 P1DDR .EQU H'FFFFC0 P5DR .EQU H'FFFFC8 P5DDR .EQU H'FFFFCA .SECTION ROM, CODE, LOCATE=H'000100 MAIN: MOV.L #H'FFF00, ER7 MOV.B #H'FF, R0L MOV.B R0L, @P1DDR MOV.B R0L, @P5DDR BCLR #2, @SYSCR ; 割込みパルスの立下がりエッジで割込みを行う MOV.B #B'11111110, R0L ; LED点灯データ(ポート1) LOOP: MOV.B R0L, @P1DR JSR @TIM2 ROTL.B R0L ; IRQ端子のプログラムとは逆の向き JMP @LOOP ;----- 割込み処理 ----- IRQ0: PUSH.W R0 ; レジスタの待避 PUSH.L ER5 PUSH.L ER6 MOV.B #D'3, R0H ; LED点灯回数データ YET: MOV.B #H'FF, R0L ; LED点灯データ(ポート5) JSR @TIM2 MOV.B #H'00, R0L MOV.B R0L, @P5DR JSR @TIM2 DEC.B R0H ; 点灯回数 - 1 BNE YET ; 点灯回数が0でなければ、YETへ POP.L ER6 ; レジスタの回復 POP.L ER5 POP.W R0 RTE ; 割込みから復旧 ;---------------------- TIM2: MOV.W #D'50, E5 ; 0.5秒のタイマサブルーチン L2: JSR @TIM1 DEC.W #1, E5 BNE L2 RTS TIM1: MOV.L #D'20000, ER6 ; 10msのタイマサブルーチン L1: DEC.L #1, ER6 NOP BNE L1 RTS .END
IRQ端子の例とやってることは一緒ですね。
職業訓練45日目 H8マイコン入門(アセンブラ言語による実習)
A-D, D-Aコンバータの制御
A-Dコンバータ
アナログデータをデジタルデータに変換する装置。
- 分解能:10ビット
- 方式:逐次変換方式
- チャネル数:8
- 変換を行うアナログ電圧の範囲:0〜5V
- 最大4入力の変換結果を16ビットのA-Dデータレジスタ4個に保持可能。
複数の入力を扱う場合、入力端子を切り替えながら順次変換を行う。
- 単一モード:1チャネルのみを扱う
- スキャンモード:指定した最大4チャネルを扱う
変換の実行→A-Dコントロール/ステータスレジスタのADST(ビット5)を"1"にセット
変換の終了→A-Dコントロール/ステータスレジスタのADF(ビット7)が"1"にセットされる
分解能が10ビットで、0〜5Vの電圧を入力した場合
5V / 2^10 = 5 / 1024 = 0.00488...V ≒ 4.88mV
→アナログ電圧がおよそ4.88mV変化するごとに、デジタルデータが"1"変化する
実習で製作した回路は、
5V電源→10kΩの可変抵抗→チャネル0のAN0端子に入力
ポート1のLED0〜7に変換結果を出力
といった感じ。
では、A-Dコンバータを利用したプログラムを。
.CPU 300HA .SECTION PROG11, CODE, LOCATE=H'000000 ADCSR .EQU H'FFFFE8 ; ADコントロール/ステータスレジスタ ADDRA .EQU H'FFFFE0 ; ADデータレジスタ P1DR .EQU H'FFFFC2 P1DDR .EQU H'FFFFC0 .SECTION ROM, CODE, LOCATE=H'000100 MOV.L #H'FFFF00, ER7 MOV.B #H'FF, R0L MOV.B R0L, @P1DDR ; ポート1を出力に設定 MOV.B #H'00, R0H ; 単一モード、チャネル選択(AN0)データ MOV.B R0H, @ADCSR ; ADCSR設定 LOOP: BSET #5, @ADCSR ; AD変換スタート L1: BTST #7, @ADCSR ; 変換終了チェック BEQ L1 MOV.B @ADDRA, R1L ; 変換結果の取り出し NOT.B R1L ; "1"で点灯するように反転 MOV.B R1L, @P1DR BCLR #7, @ADCSR ; AD変換終了フラグのリセット JMP @LOOP .END
上記のプログラムを実行すると、
可変抵抗をクルクル回すと、LEDがチカチカするはず。
アナログの量(電圧)が、デジタル(LED)に変換されているわけですね。
D-Aコンバータ
デジタルデータをアナログデータに変換する装置。
- 分解能:8ビット
- チャネル数:2
分解能が8ビットで、0〜5Vの電圧を出力する場合
5V / 2^8 = 5 / 256 = 0.0195...V ≒ 19.5mV
→デジタルデータが"1"変化するごとに、アナログ電圧がおよそ19.5mV変化する
製作した回路は、
ポート2のDIPスイッチ8ビットを入力、
ポート1のLED8ビットにデジタルデータを出力、
ポート7にテスタを接続し、アナログデータを測定しました。
では、プログラムはこちら。
.CPU 300HA .SECTION PROG12, CODE, LOCATE=H'000000 DACR .EQU H'FFFFDE ; DAコントロールレジスタ DADR0 .EQU H'FFFFDC ; DAデータレジスタ P1DR .EQU H'FFFFC2 P1DDR .EQU H'FFFFC0 P2DR .EQU H'FFFFC1 P2DDR .EQU H'FFFFC3 P2PCR .EQU H'FFFFD8 .SECTION ROM, CODE, LOCATE=H'000100 MOV.L #H'FFFF00, ER7 MOV.B #H'00, R0L MOV.B #H'FF, R0H MOV.B R0H, @P1DDR MOV.B R0L, @P2DDR MOV.B R0H, @P2PCR ; ポート2のプルアップ有効 LOOP: MOV.B @P2DR, R1L ; ポート2からデータ入力 MOV.B R1L, @P1DR NOT.B R1L MOV.B R1L, @DADR0 ; DAデータレジスタに転送 MOV.B #H'40, R1H ; DA変換設定データ MOV.B R1H, @DACR ; DA変換スタート JMP @LOOP .END
DIPスイッチを上げたり下げたりすると、テスタの値が変化するのが観測できるはず。
職業訓練44日目 H8マイコン入門(アセンブラ言語による実習)
パルスモータの制御
パルスモータとは
パルス電力を入力として回転する(→マイコン制御と相性が良い)
長所:正確な回転(位置決め)を制御できる。
N極とS極をもつロータ(回転子)の周囲に電磁石(固定子)を配置した構造になっていて、
電磁石を磁化するパルスデータによって正確な位置決め制御ができる。
制御方式
- 1相励磁方式:1相のみに電流を流す
- 2相励磁方式:2相に電流を流す、1相励磁の2倍のトルクが得られる
- 1-2相励磁方式:1相励磁と2相励磁を交互に行う、移動角度が1相励磁の半分になる
パルスモータの回転制御
面倒なので回路図は割愛…。
回路の特徴としては、
といったあたりかな。
- 1相励磁方式
.CPU 300HA .SECTION PROG6, CODE, LOCATE=H'000000 P1DR .EQU H'FFFFC2 P1DDR .EQU H'FFFFC0 P6DR .EQU H'FFFFCB P6DDR .EQU H'FFFFC9 .SECTION ROM, CODE, LOCATE=H'000100 MOV.L #H'FFFF00, ER7 MOV.B #H'FF, R0L MOV.B R0L, @P1DDR MOV.B R0L, @P6DDR MOV.B #B'10001000, R0H ; 回転データ LOOP: MOV.B R0H, @P6DR MOV.B R0H, R1H NOT.B R1H MOV.B R1H, @P1DR ; 回転データをLEDに出力 JSR @TIM2 ROTR.B R0H ; 右に1ビットローテイト JMP @LOOP TIM2: MOV.W #D'30, E5 ; 0.3秒のタイマサブルーチン L2: JSR @TIM1 DEC.W #1, E5 BNE L2 RTS TIM1: MOV.L #D'20000, ER6 ; 10msのタイマサブルーチン L1: DEC.L #1, ER6 NOP BNE L1 RTS .END
- 2相励磁方式
上記コードの回転データを"1100"に変更する。
; MOV.B #B'10001000, R0H ; 回転データ MOV.B #B'11001100, R0H ; 回転データ
- 1-2相励磁方式
1相励磁と2相励磁を交互に出力する。
実行すると、1相励磁に比べてステップ角が半分になる。
.CPU 300HA .SECTION PROG7, CODE, LOCATE=H'000000 P1DR .EQU H'FFFFC2 P1DDR .EQU H'FFFFC0 P6DR .EQU H'FFFFCB P6DDR .EQU H'FFFFC9 .SECTION ROM, CODE, LOCATE=H'000100 MOV.L #H'FFFF00, ER7 MOV.B #H'FF, R0L MOV.B R0L, @P1DDR MOV.B R0L, @P6DDR MOV.B #B'10001000, R0L ; 回転データ1 MOV.B #B'11001100, R0H ; 回転データ2 LOOP: MOV.B R0L, @P6DR ; 回転データ1を出力 MOV.B R0L, R1H NOT.B R1H MOV.B R1H, @P1DR JSR @TIM2 MOV.B R0H, @P6DR ; 回転データ2を出力 MOV.B R0H, R1H NOT.B R1H MOV.B R1H, @P1DR JSR @TIM2 ROTR.B R0L ROTR.B R0H ; 0.3秒のタイマサブルーチン TIM2: MOV.W #D'30, E5 L2: JSR @TIM1 DEC.W #1, E5 BNE L2 RTS ; 10msのタイマサブルーチン TIM1: MOV.L #D'20000, ER6 L1: DEC.L #1, ER6 NOP BNE L1 RTS .END
DCモータの制御
ドライバICによる回転方向制御
使用したドライブIC:TA7257P
→DCモータの回転を、正転、逆転、ストップ、ブレーキの4パターンを制御できる。
TA7257Pの動作
IN1 | IN2 | OUT1 | OUT2 | 動作 |
---|---|---|---|---|
0 | 0 | Z | Z | ストップ |
0 | 1 | L | H | 正転 |
1 | 0 | H | L | 逆転 |
1 | 1 | L | L | ブレーキ |
※Zはハイインピーダンス(ってWikipediaに書いてあった…)
回路図はサックリと割愛…。
.CPU 300HA .SECTION PROG8, CODE, LOCATE=H'000000 P5DR .EQU H'FFFFCA P5DDR .EQU H'FFFFC8 P2DR .EQU H'FFFFC3 P2DDR .EQU H'FFFFC1 P1PCR .EQU H'FFFFD8 .SECTION ROM, CODE, LOCATE=H'000100 MOV.L #H'FFFF00, ER7 MOV.B #H'00, R0L MOV.B #H'FF, R0H MOV.B R0L, @P2DDR MOV.B R0H, @P2PCR ; ポート2のプルアップを有効 MOV.B R0H, @P5DDR LOOP: MOV.B @P2DR, R0L ; ポート2のDIPスイッチからデータを入力 NOT.B R0L ; ONで"1"とするため反転 BTST #0, R0L ; DIPスイッチ1をチェック BEQ L1 ; スイッチがOFFの場合、L1へ BSET #0, R1L ; ビット0のLED点灯 BSET #2, R1L ; モータICの1ピンを"1" JMP @B1 L1: BCLR #0, R1L ; ビット0のLED消灯 BCLR #2, R1L ; モータICの1ピンを"0" B1: BTST #1, R0L ; DIPスイッチ2をチェック BEQ L2 ; スイッチがOFFの場合、L2へ BSET #1, R1L ; ビット1のLED点灯 BSET #3, R1L ; モータICの2ピンを"1" JMP @B2 L2: BCLR #1, R1L ; ビット1のLED消灯 BCLR #3, R1L ; モータICの2ピンを"0" B2: MOV.B R1L, @P5DR ; ポート5へ点灯、回転データを出力 JMP @LOOP .END
上記のプログラムを実行すると、
となるはず。
テキストには、
「モータの回転軸に触れながら制御を行えば、ストップとブレーキの違いを体感できる」
とありましたが、鈍感な自分には残念ながら…。
PWM機能による速度制御
ITUのPWMモードでDCモータを動作させる。
- カウンタクロックの設定(クロックの分周を設定する)
- カウンタクリア条件の設定(GRAまたはGRBでクリアする、今回はGRBでクリアとする)
- PWMモードの設定
- GRAの設定(PWM波形が"1"に立ち上がるタイミングを設定する)
- GRBの設定(PWM波形が"0"に立ち上がるタイミングを設定する)
- カウント開始
例)出力周波数が10kHz(周期10^-4 = 0.0001s)として、
クロックが0.625μsで、分周を1/8に設定すると、以下の式でGRAが割り出せる。
→0.0001s / (0.0625μs * 8) = 0.0001s / 0.0000005s = 200 = C8
デューティー比50%のPWM波形を発生させるなら、
GRAには"C8"を設定し、GRBにはC8 * 50% = 100なので"64"を設定する。
と、ここでPWM波形をオシロスコープで確認するプログラムを書くところですが、
だんだん面倒になってきたので割愛…。
PWM機能を使ってDCモータを動作する回路ですが、回路図はやっぱり割愛するとして、
回路の主なパーツは以下の通り。
.CPU 300HA .SECTION PROG10, CODE, LOCATE=H'000000 TSTR .EQU H'FFFF60 ; タイマスタートレジスタ TMDR .EQU H'FFFF62 ; タイマモードレジスタ TCR0 .EQU H'FFFF64 ; タイマコントロールレジスタ GRA0 .EQU H'FFFF6A ; ジェネラルレジスタGRA0 GRB0 .EQU H'FFFF6CA ; ジェネラルレジスタGRB0 P4DR .EQU H'FFFFC7 P4DDR .EQU H'FFFFC5 P4PCR .EQU H'FFFFDA .SECTION ROM, CODE, LOCATE=H'000100 MOV.L #H'FFFF00, ER7 MOV.B #H'00, R0L MOV.B #H'FF, R0H MOV.B R0L, @P4DDR MOV.B R0H, @P4PCR MOV.B #B'00100011, R0H ; GRAによりクリア、1/8分周データ MOV.B R0H, @TCR0 BSET #0, @TMDR ; PMWモード設定 MOV.W #H'00C8, E1 MOV.W E1, @GRA0 ; GRA0設定 LOOP: BTST #4, @P4DR ; ポート4のスイッチをチェック BEQ D50 ; スイッチがONならD50へ MOV.W #H'00C8, E1 ; GRB0データ(デューティー比100%) MOV.W E1, @GRB0 ; GRB0設定 JMP @D100 D50: MOV.W #H'0064, E1 ; GRB0データ(デューティー比50%) MOV.W E1, @GRB0 ; GRB0設定 D100: BSET #0, @TSTR ; ITUカウンタスタート JMP @LOOP .END
上記のプログラムを実行すると、
スイッチ1を押すとモーターがゆっくり(デューティー比50%)で回転し、
スイッチ1を離すとモーターが速く(デューティー比100%)で回転するはず。
以上、モーターの制御でした!
あー、まとめるのに何日かかったのだろうか…orz
職業訓練43日目 H8マイコン入門(アセンブラ言語による実習)
LEDの制御
LEDの点滅
LEDを点滅させるにはタイマプログラムを作る必要があり、
タイマには2つの実現方法がある。
- 繰り返し処理で時間をかせぐ
- 内蔵タイマ(ITU)を利用する
まずは1.の繰り返し処理を利用したコードから。
下記のコードを実行すると、5秒経過するたびにLEDの点灯がローテイトするはず。
.CPU 300HA .SECTION PROG4, CODE, LOCATE=H'000000 P1DR .EQU H'FFFFC2 P1DDR .EQU H'FFFFC0 .SECTION ROM, CODE, LOCATE=H'000100 MOV.L #H'FFFF00, ER7 MOV.B #H'FF, R0L MOV.B R0L, @P1DDR ; ポート1を出力に設定 MOV.B #'01111111, R0L ; LED点灯データ LOOP: MOV.B R0L, @P1DR ; ポート1へ点灯データを出力 JSR @TIM2 ; タイマサブルーチンの呼び出し ROTR.B R0L ; 右に1ビットローテイト JMP @LOOP TIM2: MOV.W #D'500, E5 ; 5秒のタイマサブルーチン L2: JSR @TIM1 DEC.W #1, E5 BNE L2 RTS TIM1: MOV.L #D'20000, ER6 ; 10msのタイマサブルーチン L1: DEC.L #1, ER6 ; ER6から1を引く NOP ; 何もしない、時間稼ぎ BNE L1 ; ER6≠0ならL1にジャンプ RTS .END
細かい計算はというと、
H8命令セットから、各処理のステート数を調べて、
どれぐらいの時間がかかっているのか算出する。
基準となる1ステート(クロック)の時間は、
CPUの動作周波数が16MHzなので、
1ステート = 0.0625μs
(1s/16000000Hz=0.0000000625s=0.0000625ms=0.0625μs)となる。
各処理のステート数は以下の通り。
ニーモニック | ステート数 |
---|---|
DEC.L | 2 |
NOP | 2 |
BNE | 4 |
つまり、サブルーチンL1の中では、
0.0625μs * 8ステート = 0.5μs かかっているので、
それを20000回繰り返すことによって、
10msのサブルーチン(TIM1)が作られている。
(0.5μs * 20000 = 10000μs = 10ms)
10msのサブルーチン(TIM1)を500回繰り返して、
5秒ずつ点滅する処理の出来上がり、というわけですね。
では、続きまして「2. 内蔵タイマ(ITU)を利用する」バージョンを。
インテグレーテッドタイマの使用
ITU(Integrated Timer Unit):5チャネル16ビットのタイマ機能
ITU内のカウンタ(TCNT)は、0000H〜FFFFHまでカウントアップした後、
0000Hに戻ってカウントを始める。
FFFFHまでカウントするのにどれくらいかかるかというと、
1クロック = 0.0625μsなので、
0.0625μs * FFFFH =
0.0625μs * 65535 = 4095μs ≒ 約4ms で数え終わる計算になります。
4msでは早過ぎる場合は、クロック周波数を低くするプリスケーラ機能(分周機能)を利用する。
この機能を使えば、周波数を1/2、1/4、1/8に分周して、カウント時間を延ばすことが可能。
1/8に分周したとすると、FFFFHまでカウントするのに、
4ms * 8 = 32ms かかることになる。
プリスケーラの設定含め、ITUの具体的な設定方法は以下を参照。
(タイトルに思いっきり3052Fと書いてありますが…)
http://homepage1.nifty.com/rikiya/program/h83052f_itu1/h83052f_itu1.html
ITUを利用してLEDを点滅させるプログラムはコチラ。
.CPU 300HA .SECTION PROG5, CODE, LOCATE=H'000000 P1DR .EQU H'FFFFC2 P1DDR .EQU H'FFFFC0 TSTR .EQU H'FFFF60 ; タイマスタートレジスタ TCR0 .EQU H'FFFF64 ; タイマコントロールレジスタ0 TSR0 .EQU H'FFFF67 ; タイマステータスレジスタ0 .SECTION ROM, CODE, LOCATE=H'000100 MOV.L #H'FFFF00, ER7 MOV.B #H'FF, R0L MOV.B R0L, @P1DDR ; ポート1を出力に設定 MOV.B #H'03, R0H ; 1/8分周データ MOV.B R0H, @TCR0 ; プリスケーラ設定 MOV.B #'11111110, R0L ; LED点灯データ BSET #0, @TSTR ; ITUカウントスタート L1: MOV.B R0L, @P1DR L2: BTST #2, @TSR0 ; オーバーフローフラグのチェック BEQ L2 ; フラグが"0"の場合、L2へジャンプ ROTL.B R0L BCLR #2, @TSR0 ; オーバーフローフラグのクリア JMP @L1 .END
カウンタのスタート後に、
タイマステータスレジスタ(TSR0)の2ビット目(オーバーフローフラグ:OVF)をチェックして、
OVFが"0"の場合、チェックを続ける(BEQ L2)。
OVFが"1"になったら、LED点灯データを左にローテートし、
オーバーフローフラグを"0"にリセットし、点灯データを出力する。
実行すると、32msごとにLEDの点灯がローテートするはず。