kuroの覚え書き

96の個人的覚え書き

PICでC言語


永らく環境構築後放置していたPICマイコンC言語に着手してみた。

とりあえずはわけもわからないままサンプルコードをビルドしてライターで書き込んで実際に動かしてみる。

使用PICは12F675。まずはLEDを0.5秒間隔で点滅させるだけ。まあコレだけでもバイクのウィンカーリレーなどで役に立つけど。

とりあえずサンプルのソース

gpio.c


#include <pic.h>
#include "delay.h"

__CONFIG(UNPROTECT & BOREN & MCLRDIS & PWRTEN & WDTDIS & INTIO);

__IDLOC(0100);

main()
{
OSCCAL=_READ_OSCCAL_DATA();

GPIO5=0; /* GP5ピンの出力データを0にする */

while(1){

TRIS5=0; /* GP5ピンは出力ピン */

DelayMs(250); /* 500msec待つ */
DelayMs(250);

if(GPIO5) /* LEDの点滅をトグルする */
GPIO5=0;
else
GPIO5=1;
}
}

あとこのプログラムをビルドするにはヘッダファイルなど2つ別に要る

一つはdelay.h


/*
* Delay functions for HI-TECH C on the PIC
*
* Functions available:
* DelayUs(x) Delay specified number of microseconds
* DelayMs(x) Delay specified number of milliseconds
*
* Note that there are range limits: x must not exceed 255 - for xtal
* frequencies > 12MHz the range for DelayUs is even smaller.
* To use DelayUs it is only necessary to include this file; to use
* DelayMs you must include delay.c in your project.
*
*/

/* Set the crystal frequency in the CPP predefined symbols list in
HPDPIC, or on the PICC commmand line, e.g.
picc -DXTAL_FREQ=4MHZ

or
picc -DXTAL_FREQ=100KHZ

Note that this is the crystal frequency, the CPU clock is
divided by 4.

* MAKE SURE this code is compiled with full optimization!!!

*/

#ifndef XTAL_FREQ
#define XTAL_FREQ 4MHZ /* Crystal frequency in MHz */
#endif

#define MHZ *1000L /* number of kHz in a MHz */
#define KHZ *1 /* number of kHz in a kHz */

#if XTAL_FREQ >= 12MHZ

#define DelayUs(x) { unsigned char _dcnt; ?
_dcnt = (x)*((XTAL_FREQ)/(12MHZ)); ?
while(--_dcnt != 0) ?
continue; }
#else

#define DelayUs(x) { unsigned char _dcnt; ?
_dcnt = (x)/((12MHZ)/(XTAL_FREQ))|1; ?
while(--_dcnt != 0) ?
continue; }
#endif

extern void DelayMs(unsigned char);


もう一つはdelay.c


/*
* Delay functions
* See delay.h for details
*
* Make sure this code is compiled with full optimization!!!
*/

#include "delay.h"

void
DelayMs(unsigned char cnt)
{
#if XTAL_FREQ <= 2MHZ
do {
DelayUs(996);
} while(--cnt);
#endif

#if XTAL_FREQ > 2MHZ
unsigned char i;
do {
i = 4;
do {
DelayUs(250);
} while(--i);
} while(--cnt);
#endif
}

ソースの意味を理解しないとな

f:id:k-kuro:20071016011000j:image

右がPICにプログラムを書き込む「Writer509」左が書き込んだPIC12F675をブレッドボード上にてテスト中。

電源はUSBから拝借(PIC、Writerとも)


わかった事


/* この部分はコメント */


#include <pic.h>

これは必ず付けるお約束か?基本インクルードファイル。


#include "delay.h"

ヘッダファイルって言うのはアセンブラでいうサブルーチンを別ファイルにして呼び出しているようなもの?

delay.hは一定時間のウエイトを入れるのに必要なものらしい。


__CONFIG(UNPROTECT & BOREN & MCLRDIS & PWRTEN & WDTDIS & INTIO);

コンフィグレーションビットの記述。アセンブラでいう以下の設定


__CONFIG _CP_OFF & _BODEN_ON & _MCLRE_OFF & _PWRTE_ON & _WDT_OFF & _INTRC_OSC_NOCLKOUT

Cではpic.hに定義されている。というかpic.hから誘導されるデバイスごとのヘッダファイルに記述されているんだけど。


/* Configuration bit definitions */
#define CONFIG_ADDR 0x2007

/* Protection of data block */
#define CPD 0x3EFF /* data protect on */
#define UNPROTECT 0x3FFF /* data protect off */

/* Protection of program code */
#define PROTECT 0x3F7F /* code protection on */
#define UNPROTECT 0x3FFF /* code protection off */

/* Brown out detection enable */
#define BOREN 0x3FFF /* brown out reset enabled */
#define BORDIS 0x3FBF /* brown out reset disabled */

/* Master clear reset */
#define MCLREN 0x3FFF /* master clear reset function enabled */
#define MCLRDIS 0x3FDF /* master clear reset function disabled */

/* Power up timer enable */
#define PWRTDIS 0x3FFF /* power up timer disabled */
#define PWRTEN 0x3FEF /* power up timer enabled */

/* Watchdog timer enable */
#define WDTEN 0x3FFF /* watchdog timer enabled */
#define WDTDIS 0x3FF7 /* watchdog timer disabled */

/* Oscillator configurations */
#define RCCLK 0x3FFF /* GP4 = clock out signal/GP5 = RC osc */
#define RCIO 0x3FFE /* GP4 = IO/GP5 = RC osc */
#define INTCLK 0x3FFD /* internal osc/GP4 = clock out signal/GP5 = IO */
#define INTIO 0x3FFC /* internal osc/GP4 = IO//GP5 = IO */
#define EC 0x3FFB /* external clock */
#define HS 0x3FFA /* high speed crystal/resonator */
#define XT 0x3FF9 /* crystal/resonator */
#define LP 0x3FF8 /* low power crystal/resonator */

こんな感じ

次に


__IDLOC(0100);

これはちょっと悩んだ。詳しく説明されてないのね。で、結局ググったところここには4桁で何を書いておいてもいいんだとか。プログラムの通し番号でもバージョンNo.でもなんでもいいらしい。当然何も書かなくても問題無し。なんか面白い使いかたないかな。


main()
{
}

とりあえずこの{から}の間に書いた部分がプログラム本体という訳だね。しかしいったいmainの後ろに何も入っていない()を書くのはなんでなんだ?


OSCCAL=_READ_OSCCAL_DATA();

12F675は内部発振器を持っていて発振周波数が4MHzになるような補正値を各個体が持っている。この補正値を読み込むコマンド


GPIO5=0; /* GP5ピンの出力データを0にする */

一旦GP5をオフにするところからプログラムは始まっている。


while(1){

}

これで{から}までが無限ループする。(whileの特殊な使い方。)


TRIS5=0; /* GP5ピンは出力ピン */

アセンブラで言うところの


BSF STATUS,RP0
MOVLW 0H
MOVWF GPIO

バンクがどうとか考えなくていいので便利。なお正確には上のアセンブラの記述は


  TRISIO=0b00000000; // GPIO をすべて出力に設定

こうなるんだろうか?

ちなみにGPIO3は入力にしか使えないからこの様に設定したとしてもその部分は無視されるし、GPIO6,7はそもそも存在しないのでこれも無視されるはず。


DelayMs(250); /* 500msec待つ */
DelayMs(250);

DelayMsはdelay.hとdelay.cで定義されている関数。なのでビルドするときにはこれらファイルも一緒にしてやらないといけない。ミリ秒のウエイト関数 DelayMs(unsigned char cnt) は、cnt ミリ秒だけ待ち関数であり、cnt は255を越えてはいけない。らしい。delay.hとdelay.cはPICCのsampleフォルダにあるものをコピーして使う。ただしこれらのファイルはデフォルト4MHzになっているので他のクロックスピードで使うときはdelay.h の


 #define XTAL_FREQ 4MHZ

ここを変更しておくらしい。(数字をかえる)


if(GPIO5) /* LEDの点滅をトグルする */
GPIO5=0;
else
GPIO5=1;

実はこの記述がイマイチ理解できん。if(条件式)あってるなら○○、else違うならxxっていうんじゃないの?なんで条件式のところにGPIO5ってのが入ってるんだ?

わかった。

if(GPIO5)と書くと「GPIO5がHighのとき」という意味になるらしい。わかっている人には当たり前の事が素人にはチンプンカンプンでいかん。


┌─┬─┐
VDD┤   ├VSS
GP5┤   ├GP0
GP4┤   ├GP1
GP3┤   ├GP2
└───┘
12f675
┌─┬─┐
+5v ┤   ├ GND
GND-LED-1kΩ ┤   ├
┤   ├
+5V-1kΩ ┤   ├
└───┘
GP3は使ってないのでプルアップしておく