プロメモグラム

誰が見てもわかるような文章を目指す

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で指定。

計算式

[latex] 周期 = {(PR2 + 1)}cdot 4 cdot Tosc cdot (TMR2の分周(プレスケール)値) [/latex]
[latex] パルス幅 = (CCPRxL:DCxB) cdot Tosc cdot (TMR2の分周値) [/latex]
[latex] デューティ比 = frac{パルス幅}{周期} = frac{(CCPRxL:DCxB)}{4(PR2 + 1)} [/latex]
[latex] 分解能 = frac{log{[4(PR2 + 1)]}}{log{2}} [/latex]

式中の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 &lt;&lt; 4);
    EN = 1;
   __delay_us(20);
    EN = 0;
    __delay_us(20);
}

void lcd_data(unsigned char data)
{
    RS = 1;
    PORTB = (data &amp; 0xF0);
    EN = 1;
   __delay_us(20);
    EN = 0;
    __delay_us(20);

    PORTB = (data &amp; 0x0F) &lt;&lt; 4;
    EN = 1;
    __delay_us(20);
    EN = 0;
    __delay_us(20);
}

void lcd_cmd(unsigned char data)
{
    RS = 0;
    PORTB = (data &amp; 0xF0);
    EN = 1;
    __delay_us(20);
    EN = 0;
    __delay_us(20);

    PORTB = (data &amp; 0x0F) &lt;&lt; 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なら

[latex] 周期 = クロック周期 cdot プリスケール(分周)値 cdot 2^8 [/latex]

16bitなら

[latex] 周期 = クロック周期 cdot プリスケール(分周)値 cdot mathrm{2}^{16} [/latex]

この式で割り込む周期を求められるが、右辺の2のn乗の部分をTMR0レジスタの初期値を変更することで変えられる。 この数字は0からスタートして256でオーバーフローして割り込みするわけだから、初期化時と割り込みフラグをリセットする前に TMR0レジスタに何か値をいれれば任意にできる。未知数Xとして方程式とけばいいだけ。

せっかくHTMLで書いたのに間違えてビジュアルモードに書いてたからulタグとかめちゃくちゃだよ

Androidのxml編集中にUnexpected namespace prefix "xmlns" found for tag LinearLayout とかいうエラーを吐く

xmlでデザインしている時、

本を読みながらソースを丸写ししていると出てきたエラー

 

Unexpected namespace prefix "xmlns" found for tag LinearLayout

 

xmlnsから始まる予期せぬ名前空間がLinearLayoutから見つかりました。

意味がわからない…

 

ググってみると一つのxmlファイルに

はひとつで良いらしい。 この本android2.3のものだからか、もしくは別の理由か。 修正方法はわかったからどうでもいい。

 

TeXをWindows7にインストール&TeXStudio

Windows7TeXをインストールするのは非常に簡単になった。

TeXのインストー

『あべのりページ』

http://www.math.sci.hokudai.ac.jp/~abenori/soft/index.html

ここからTeXインストーラをダウンロードし、実行すればコンパイラであるplatexやその他必要なビュアなどを

ダウンロードからインストールまですべてしてくれるので、困ることがない。

 

TeXStudioのインストー

TeXStudioは1画面でプレビューと編集ができるので、便利(fullHDくらいないと厳しいが)

インストールは楽だが、このままだと日本語対応していないので、いくつか設定事項。

1.コンパイラLaTeXに変更

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オプションをつける必要がある。