PIC18F25K22でサーボモータを動かす
標準のPWMだとできない?
PIC18F25K22というか、PICにはCCPというモジュールが搭載されていて、PWMが標準で使える。 しかしながらこのモジュールはあまり長い周期の設定ができない(高周波数だとより厳しい)。 →だいたいのサーボモータは20msの周期中のパルス幅で角度を指定しているから使えない? このように判断した。
だからTMR0で0.1msごとに割り込みを入れて、0.1msの精度でパルスをON、OFFするようにプログラムを書いたらできた。 PICの事良く知らないけど、こんなに割り込んじゃって他のコードに影響出ないか不安。 このコードでつかうサーボは周期が20.0ms。真ん中が1.5ms、1.0~2.0msで角度を決められる。
時間出来たらもう少し詳しく書こう。
ソースコード
/* * PWMが低周波数で実現できなそうなのでタイマー0でやる */ #include<xc.h> //コンフィギュレーションビット(内部オシレータを使用) #pragma config IESO = OFF, FOSC = INTIO67, FCMEN = OFF //クロック周波数の指定(8MHz) #define _XTAL_FREQ 8000000 //大域変数 int count = 0; int countvar; //0.1ms単位で立ち上がるタイミング指定 int main() { int i; //内部クロックに設定(8MHz) OSCCON = 0b01100010; //全ポートデジタル入出力 ANSELA = 0; ANSELB = 0; ANSELC = 0; //全ポート出力 TRISA = 0; TRISB = 0; TRISC = 0; //全ポートOFF PORTA = 0x00; PORTB = 0x00; PORTC = 0xFF; //タイマ設定 T0CON = 0b01001001; //割り込み設定 TMR0L = 0x37; TMR0IF = 0; TMR0IE = 1; PEIE = 1; GIE = 1; //TIMER0 Start T0CONbits.TMR0ON = 1; while(1) { for(i=10 ; i<=20 ; i+=5) { countvar = i; __delay_ms(90); __delay_ms(90); } } return 0; } void interrupt isr(void) { TMR0L = 0x37; TMR0IF = 0; count ++; if(count == countvar) { PORTA = 0; } else if(count == 200) { PORTA = 0xFF; count = 0; } }
PIC18F25K22におけるPWMの使用方法についてまとめてみた。
PIC18F25K22におけるPWMの使用方法についてまとめてみた。
概要
PWMの概要については書かない。 PICではCCPモジュール(Capture Compare PWM)の一つとして位置づけられている。 PIC18なんかではCCPが2つ搭載されていたり、CCPを拡張したEnhanced CCPが含まれていたりする。 PIC18F25K22には5つ搭載されており、CCP1~CCP3がEnhanced CCP、CCP4とCCP5が通常のCCPである。 今回はCCP4とCCP5のみ扱うことにする。
PWMにおいて必要なことは3つ。
- デューティ比(デューティサイクル)
- 周期
- 分解能(resolutionだから解像度とも言う?)
この3つをレジスタで指定してやると動くはずである。 しかし、レジスタに直接値を入れることはできない。幾つかのビットとそれを引数とする計算式によって 値が定まるでしょう。
レジスタ
PWMに関するレジスタは4つ有る。 ここからレジスタ名に出てくる小文字のxは数字がはいるものとする(データシートでもそんな風に書いてるから従うよ)
CCPxCON
このレジスタはEnhanced CPPとCPPで中身が違うので面倒であるがひとまず通常のCPPであるCCP4CONから。
未使用 | 未使用 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
---|---|---|---|---|---|---|---|
- | - | DC2B1 | DC2B0 | CCP2M3 | CCP2M2 | CCP2M1 | CCP2M0 |
- DC4Bx デューティ比を10bitで表す場合の最小位ビットとなる。残りの8bitはCCPR2Lで指定。 ちなみにPWM以外のモードでは不使用。
- CCP4Mx…4ビットの羅列によって別々の意味を成す。 このビット列によってPWMかその他2つとして使うかを決定できる。今回はPWMを使うので1100と入れる。 詳細の表はデータシート参照。時間あればここに載せたいが、和訳できないのです。
PR2
PWMでHIGHとなる時間を0から255で指定。
T2CON
CCPはTIMER2の機能を使用している。 このコンフィギュレーションレジスタはタイマ2に関する設定を記述する。
未使用 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
---|---|---|---|---|---|---|---|
- | T2OUTPS3 | T2OUTPS2 | T2OUTP1 | T2OUTPS0 | TMR2ON | T2CKPS1 | T2CKPS0 |
- T2OUTPSx ポストスケールの設定。今回は使わない
- TMR2ON Timer2を使用するかどうか 1:使用 0:不使用
- T2CKPSx プレスケールの設定。計算式に含まれる。xは0でも1でOK。 00 :プレスケール1 01 :プレスケール4 1x :プレスケール16
CCPRxL
デューティ比に使用する。0~255で指定。
計算式
式中のToscはクロック周期(Tosc = 1/Fosc) CCPRxL:DCxBは上位ビットと下位ビットを繋げて10bitとしたもの
まとめ
ここまで書いたが、使用する場合のことだけかいて、中でどのように動いているかを 全く書かなかったので理解が深まらない気がしてくる。よく言えば簡潔?
LCDのSD1602HULBをpic18F45K20で制御
参考文献
普通最後に書くところだけどほとんどここを参考にしたので、最初に書く。 PICで遊ぶ電子工作 - 液晶ディスプレイを使う
ハードウェア部
すでに資料があるのに自分で新たにつくるのは正直面倒。
ソフトウェア部
仕組みはともかく、作ったソースコードのみを公開することにする。 上のサイトを参考にすれば似たようなものができるだろう。
#include <xc.h> //コンフィギュレーションビット(内部オシレータを使用) #pragma config BOREN = ON, CPD = OFF, DEBUG = OFF, WRT = OFF, FOSC = HS, WDTE = OFF, CP = OFF, LVP = OFF, PWRTE = ON //クロック周波数の指定(10MHz) #define _XTAL_FREQ 10000000 //各種ポート名 #define EN PORTBbits.RB3 #define RS PORTBbits.RB1 #define RW PORTBbits.RB2 #define DB4 PORTBbits.RB4 #define DB5 PORTBbits.RB5 #define DB6 PORTBbits.RB6 #define DB7 PORTBbits.RB7 //プロトタイプ宣言 void write4bit(unsigned char); void picInit(); void lcdInit(); void lcdTest(); void lcd_data(unsigned char); void lcd_cmd(unsigned char); void write4bit(unsigned char); //メイン関数 void main() { //PICマイコンの初期化 __delay_ms(49); picInit(); //ディスレイ初期化開始 lcdInit(); //テスト用(LEDを点滅させながらLCDに文字を表示) while(1) { __delay_ms(49); PORTAbits.RA0 = 1; __delay_ms(49); PORTAbits.RA0 = 0; lcdTest(); } } void picInit() { OSCCON = 0b00000000 ; //クロック周波数の指定 ANSEL = 0b00000000 ; //デジタルで出力 ANSELH = 0b00001000 ; // デジタルで出力 TRISA = 0b00000000 ; //すべてのピンを出力にする TRISB = 0b00000000 ; TRISC = 0b00000000 ; TRISD = 0b00000000 ; PORTA = 0b00000000 ; //すべてのピンをLowにする PORTB = 0b00000000 ; PORTC = 0b00000000 ; PORTD = 0b00000000 ; PORTAbits.RA2 = 1; } void lcdInit() { EN = 0; RS = 0; RW = 0; //8ビットモードへ __delay_ms(20); write4bit(0x3); __delay_ms(10); write4bit(0x3); __delay_ms(5); write4bit(0x3); //4ビットモードへ __delay_ms(5); write4bit(0x2); __delay_ms(5); //2桁表示 lcd_cmd(0x28); __delay_ms(5); //LCD ON lcd_cmd(0x08); __delay_ms(5); //ディスプレイオン lcd_cmd(0x0D); __delay_ms(5); //エントリーモードセット lcd_cmd(0x06); __delay_ms(5); } void lcdTest()//0x44を出力し続ける。 { lcd_cmd(0x80); __delay_ms(20); lcd_cmd(0x80); __delay_ms(20); lcd_cmd(0x80); __delay_ms(20); lcd_data(0x44); } void write4bit(unsigned char data) { PORTB = (data << 4); EN = 1; __delay_us(20); EN = 0; __delay_us(20); } void lcd_data(unsigned char data) { RS = 1; PORTB = (data & 0xF0); EN = 1; __delay_us(20); EN = 0; __delay_us(20); PORTB = (data & 0x0F) << 4; EN = 1; __delay_us(20); EN = 0; __delay_us(20); } void lcd_cmd(unsigned char data) { RS = 0; PORTB = (data & 0xF0); EN = 1; __delay_us(20); EN = 0; __delay_us(20); PORTB = (data & 0x0F) << 4; EN = 1; __delay_us(20); EN = 0; __delay_us(20); }
PICでTIMER0を使う(PIC18F25K22 , XC8)
基本的にどのPICでも使用方法は同じであるが、変数名が若干違ったりするので、注意が必要。 結局頑張って使うPICのデータシートを読むしか無いのかな。英語わからないけど……
具体的手順
- TIMERの設定レジスタに値を代入(TMR0CON)
- タイマー割り込みを許可
- 割り込み発生時の関数を定義
以上の順序で設定をしていこう。
TIMERの設定レジスタに値を代入
代入する値の詳細は↓のデータ参照
[cpp] T0CON = 0bxxxxxxxx; [/cpp]
タイマー割り込みを許可
[cpp] TMR0IF = 0; //TIMER0のフラグをリセット TMR0IE = 1; //TIMER0による割り込みを許可 PEIE = 1; //割り込みを許可 GIE = 1; //全体の割り込みを許可 [/cpp]
割り込み発生時の関数を定義
割り込みにはタイマー割り込みだけでなく、SPIやUSARTの送受信等さまざまな割り込みが存在しているが、すべての割り込み時に この関数が呼び出されることになる。 今回は他の割り込みを使用しないのでそのまま処理を書き始めても問題ないが、他に割り込みさせる場合はTMR0IFが 1になっていることを確認するなどの方法で場合分けする必要がある。
[cpp] void interrupt isr(void) { TMR0IF = 0; //TIMER0のフラグをリセット
/* 処理 */
} [/cpp]
その他関係するレジスタ
TMR0レジスタ…このレジスタをサイクルごとにインクメントして、オーバーフローして値が0に戻った時に割り込み が発生している模様。だいたいのPICでは8ビットである(0~255) もし16bitモードにしているのであれば、TMR0HレジスタとTMR0Lレジスタを使用する。 INTCON…割り込みを制御している。PEIEやGIE、TMR0IFで直接ビット指定できるので気にするな。
データ
TMR0CON
bit7 | bit6 | bit5 | bit4 | bit3 | bit2 | bit1 | bit0 |
---|---|---|---|---|---|---|---|
TMR0ON | T08BIT | T0CS | T0SE | PSA | T0PS2 | T0PS1 | T0PS0 |
- TMR0ON…タイマーを有効/無効(有効:1 , 無効:0)
- T08BIT…1:カウンターの桁数を指定(16bit : 0 , 8bit :1) 8bitだと0~255、16bitだと0~65535までクロックごとにインクリメント
- T0CS…TIMER0のクロックセレクト。(T0CKI pinを使用:1 , 内部クロックを使用:0) T0CKI pinはRA4です。(PIC18F25K22)
- T0SE…T0CKIpinにおけるTIMER0の検出方法(立ち下がりエッジ:1 , 立ち上がりエッジ:0) 検出時にカウンターをインクリメントする。立ち上がりエッジはLOWからHIGHに電圧が変化したときにインクリメント。 立ち下がりはその逆。
- PSA…TIMER0のプリスケーラアサインメントビット プリスケーラとは分周のこと、1/2に分周するなら2回に一度だけエッジを取り出す。 分周することによってより長い時間間隔で割り込みを行えるってはっきり分かんだね。 1 : プリスケーラを使用しない。 0 : プリスケーラを使用する。
- T0PS…プリスケーラをどの割合で行うか 111:クロックの1/256の周波数。つまり周期が256倍。 110:1/128 101:1/64 100:1/32 011:1/16 010:1/8 001:1/4 000:1/2
追記:任意の秒数で割り込み
8bitなら
16bitなら
この式で割り込む周期を求められるが、右辺の2のn乗の部分をTMR0レジスタの初期値を変更することで変えられる。 この数字は0からスタートして256でオーバーフローして割り込みするわけだから、初期化時と割り込みフラグをリセットする前に TMR0レジスタに何か値をいれれば任意にできる。未知数Xとして方程式とけばいいだけ。
せっかくHTMLで書いたのに間違えてビジュアルモードに書いてたからulタグとかめちゃくちゃだよ
Androidのxml編集中にUnexpected namespace prefix "xmlns" found for tag LinearLayout とかいうエラーを吐く
TeXをWindows7にインストール&TeXStudio
Windows7にTeXをインストールするのは非常に簡単になった。
TeXのインストール
『あべのりページ』
http://www.math.sci.hokudai.ac.jp/~abenori/soft/index.html
ここからTeXインストーラをダウンロードし、実行すればコンパイラであるplatexやその他必要なビュアなどを
ダウンロードからインストールまですべてしてくれるので、困ることがない。
TeXStudioのインストール
TeXStudioは1画面でプレビューと編集ができるので、便利(fullHDくらいないと厳しいが)
インストールは楽だが、このままだと日本語対応していないので、いくつか設定事項。
2.LaTeXのコマンドのlatex部分をplatex-utf8に置き換える。
3.スペルチェックが英語になっているので、日本語に赤い下線が入ってしまう。
OptionのTeXStudioの設定から構文の強調表示でスペルチェックのチェックを外せばおk
MacでもMacTexというインストーラが存在する。エディタも同時にインストールされ設定もプロファイル変更のみで済むので楽であったが。TeXStudioで文字化けが発生して挫折したorz
プロキシを通してgemのインストール【proxy】【gem】
学内LANからインストールするときプロキシを通るので
sudo gem install em-websocket
だとインストールできない。
プロキシを通す場合は
sudo gem install -p http://ip:port em-websocket
と-pオプションをつける必要がある。