wo
integer型は8ビット(1バイト)の符号なし整数となります。したがって、0-255の範囲を記録できますが、符号の判断や負になる計算はできませんから注意してください。符号を考慮する場合は
signed int
と宣言する必要があります。short は1ビット、long は2バイトデータです。char 型は、int と同じになります。他に、
int1 int8,int16,int32
の宣言が可能です。これは、変数のサイズをビット数で指定します。
float は4バイトです。doubleは利用できません。
int some=0b00001010;
int ss=0x7B;
int const seg_data[ ]={0x3f,0x6,0x5b,4f,0x66,0x6d,0x7c,0x07,0x7f,0x67}; int v=4; pb=seg_data[v];
struct data_record { byte a [2]; byte b : 2; /*2 bits */ byte c : 3; /*3 bits*/ int d; }以下は列挙型のenumの例です。
enum boolean {false, true};
式や文の形式は通常のC言語と同じです。算術関数も利用できます。また、シフトやビット演算用の関数も提供されています。
enum や struct も利用できます。
RS232Cを接続すると、printf() でRS232Cに文字を出力します。format は少し特殊です。1バイトの整数にはu(符号なし)またはd(符号付)を利用します。2バイトの
int の表示には、lu(符号なし)またはld(符号付)を利用します。浮動少数表示はfですが、小数点以下の桁数を指定するにはwを利用します。
output_bit(pin,value) output_high(pin) output_low(pin) output_toggle(pin)端子の値を表示するには、発光ダイオードが便利です。output_low(PIN_A0) で、A0端子は L 出力になります。下図のように、LEDをA0端子に接続すると 330オームを通して発光ダイオード(LED)に電流が流れますから、LEDが点灯します。
val=0x3; output_a(val)val の第3ビット(この場合 0) はA3 に出力され A3 の端子は Low レベル(0V)になります。A3に接続するLEDの電流が流れ、点灯します。
val = input(pin); 例 val = input(PIN_A1);スイッチが押されているとval の値は0、押されていないときは1になります。抵抗はスイッチを押したとき、VCCとGNDが短絡(ショート)するのを防ぎます。
val = ~(input_a() & 0x0F)で、回転スイッチの値が2進数で val に入ります。
set_tris_a(int:value) | aポートの入出力を設定する |
output_a(int:value) | aポートに値を出力する |
output_toggle(pin) | 指定ピンの値を反転する |
output_bit(pin,value) | 指定ピンに値を設定する |
int input(pin) | 指定ピンに値を入力する |
int input_x() | 指定ポートの値を入力する(x=a,b,c) |
out_b_pullups(boolean:flag) | ポートBのプルアップを指定 |
#include <16f84.h> #use fast_io(a) #use fast_io(b) main() { set_tris_a(0xff); //ポートAを入力に設定 set_tris_b(0); while(1){ //繰り返し処理 output_bit(PIN_B0,input(PIN_A0)); } }このプログラムを以下の回路で実行すると、スイッチがオンのとき、LEDが点灯します。
16F87Xでは、RA4 端子はオープンコレクタ出力になります。出力として利用する場合はプルアップが必要です。また、16F648、88 系では
RA4は入力専用で、出力はできません。
絶対時間を指定した遅延を指定できます。まず、使用するクロックを指定します。これはプリプロセッサで指定します。
#use delay(clock 10000000)
この指定を行うと時間待ちを行う関数が利用できます。ミリ秒単位の遅延は、
delay_ms()
を利用します。
delay_us(val)
は、マイクロ秒単位での時間指定ですが、10マイクロ以下の細かい時間に指定は正確な動作はできません。
タイマーは、指定された信号のパルスの数を計数します。タイマー0は8bitの計数を行います。また、あらかじめ計数する信号を、1/2,1/4, ,1/128,1/256
に分周することもできます。以下に設定例を示します。
setup_counters(RTCC_INTERNAL,RTCC_DIV_256)
RTCC_INTERNALは計数信号を内部クロックの 1/4 の周波数の信号とします。RTCC_DIV_256 はこの信号をさらに 1/256
で使用することを指定します。他に、RTCC_DIV_128、RTCC_DIV_64 なども利用できます。
カウンターの値は
get_timer0();
で読み込み
set_timer0(val)
で値を設定できます。タイマー0で設定できる val は8ビットです。プリスケーラも8ビットですから、20MHzのクロックの場合、最大計数時間は10m秒程度です。
タイマー1は
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8)
で設定します。外部クロックを使用する場合、T1_EXTERNAL とします。また、分周は BY_8 以外に、BY_1、BY_2、BY_4 が利用できます。カウンターの値は
get_timer1();
で読み込み
set_timer1(val)
で値を設定できます。タイマー1は2バイトの計数値が利用できます。したがって、T1_DIV_BY_8 を組み合わせると、合計19ビットなります。20MHzのクロックの場合、最大計数時間は約100m秒程度まで係数できます。
タイマー2は、setup_timer_2 (mode, period, postscale)
で設定します。modeは T2_DISABLED, T2_DIV_BY_1, T2_DIV_BY_4, T2_DIV_BY_16 で指定します。T2_DISABLED
は利用しない場合、他は内部クロックの分割数です。period は8bit で周期を設定します。オーバーフローすると、2bit の postscale で分周したのち割り込みを起こします。カウンターの値は
get_timer2(); で読み込み set_timer1(val) で値を設定できます。
例
setup_timer_2 ( T2_DIV_BY_4, 0xc0, 2);
20MHzの場合、800nSごとに増加し、153.6μ秒でオーバーフローし、307.2μ秒毎に割り込みを起こします。
タイマー1と2は連携して、CCPと呼ばれる機能を提供します。CCPでは capture:取り込み、compare:比較、pwm::発振、の機能があります。capture
モードでは内部または外部からの信号で自動的に ccp_1(2) に取り込むことができます。正確な計数値の取り込みが必要な場合に利用します。また、compare
モードでは、計数値とccp_1 の値が等しくなると、外部信号をセットしたり割り込みを起こすことができます。
モードは次のように設定します。
setup_ccp1(mode);
modeは以下のような定数です。
CCP_OFF: ccpモードを使用しない
CCP_CAPTURE_FE:外部信号の立下りで取り込む
CCP_CAPTURE_RE:外部信号の立上がりで取り込む
CCP_CAPTURE_DIV_4:内部クロックの1/4で取り込む
CCP_CAPTURE_DIV_16:内部クロックの1/16で取り込む
CCP_COMPARE_SET_ON_MATCH:値が等しくなったら外部信号をセットする
CCP_COMPARE_CLR_ON_MATCH:値が等しくなったら外部信号をクリアする
CCP_COMPARE_INT:値が等しくなったら割り込みを起こす
CCP_COMPARE_RESET_TIMER:値が等しくなったらタイマーをリセットする
この機能は、次のPWM モードでも利用されるので、PWMと同時に利用することはできません。
PWMは 指定した周期と幅の発振信号を生成します。PWMを利用する場合
setup_ccp1(CCP_PWM);
でPWMモードを指定します。次に、
setup_timer_2(T2_DIV_BY_16, period, postScaler);
で、PWMのパルスの周期を指定します。T2_DIV_16はプリスケーラで、periodは周期の指定で最大値は255です。T2_DIV_BY_16
は他にT2_DIV_BY_4、T2_DIV_BY_1 が利用できます。period は内部で2bit追加されるため、合計10bitまで指定可能です。
PWMの周期は内部クロックの周期をTckをすると、
(period+1)*Tck*n*4
となります。n は T2_DIV_n で指定する値です。postScalerは プリスケ-ラ*周期 の信号をさらに分周し、割り込みのタイミングを指定します。パルスの幅は
set_pwm1_duty(width);
で設定します。widthは10bitで1023まで指定できます。8ビットの変数を指定すると、4倍の値が設定されます、PWMモードの場合、割り込みはありません。
例 周期25μSの方形波の場合、
20MHzクロックで
setup_ccp1(CCP_PWM);
setup_timer_2(T2_DIV_BY_1, 124, 1);
set_pwm1_duty(62);
16F873の出力端子は PC2
電圧生成と電圧比較機能を内蔵しているPICがあります。参照電圧の指定は16レベルのみです。このため、LOWとHIGHの二つのモードがあります。参照電圧は外部に取り出すこともできますが、次の比較器の比較電圧としても利用できます。
setup_vref(VREF_LOW | val); VCC*value/24 の電圧出力
setup_vref(VREF_HIGH | val); VCC*(1/4+value/32)の電圧出力
なぜか、<16f648a.h> には、以下の定義がありません。
#define VREF_LOW 0xa0
#define VREF_HIGH 0x80
追加が必要です。
比較器は2組あり、次のように設定します。
setup_comparater(mode);
modeは A0_VR_A1_VR のように比較器への入力を指定します。PICにより異なりますから、詳細はヘダファイルを参照してください。最初の2信号が比較器1、次の2信号が比較2への入力です。VRはvrefで設定する参照電圧です。NCは無接続を意味します。OUTは比較結果を出力する端子を指定します。16F873Aは以下のように定義されています。最後の16進数値はハードウエアに設定される定数です。
#define A0_A3_A1_A3 0xfff04
#define A0_A3_A1_A2_OUT_ON_A4_A5 0xfcf03
#define A0_A3_A1_A3_OUT_ON_A4_A5 0xbcf05
#define NC_NC_NC_NC 0x0ff07
#define A0_A3_A1_A2 0xfff02
#define A0_A3_NC_NC_OUT_ON_A4 0x9ef01
#define A0_VR_A1_VR 0x3ff06
#define A3_VR_A2_VR 0xcff0e
比較器の出力は、C1OUT, C2OUT として参照します。指定した入力で先頭の信号が次の信号より高いと 1 になります。比較電圧は設定可能ですから、比較にいわゆるヒステリシス機能を利用して雑音の影響を軽減することができます。
例(12f675)
A0とA1を参照電圧で比較します。
setup_comparator(A0_VR_A1_VR);
setup_vref(VREF_LOW | 15);
if (C1OUT == 0){......}
AD変換機能が内蔵されたPICがあります。AD変換を利用するには、まず、初期設定をします。以下はクロックの指定で、DIV_8,DIV_32
も可能です。
setup_adc(ADC_CLOCK_DIV_2)
以下は、アナログ入力可能な端子はすべてアナログ入力とする指定です。
setup_adc_ports( ALL_ANALOG)
次は入力するチャンネルを番号で指定します。
setup_channnel(0)
読み取りは、以下のように行います。
long i = read_adc()
チャンネル指定から、実際読み出すまで、A/Dクロック1*10+15 マイクロ秒 待つ必要があります。
RS232Cシリアル通信により COM ソケットのあるPCとデータの送受信が可能です。ただし、RS232Cは信号レベルがTTLでないため、外付けのレベル変換ICが必要です。RS232C機能が内臓されていないPICの場合はソフトウエアで処理を行います。
パソコンのC言語の標準入出力装置はキーボードとディスプレイですが、PICの場合は シリアル(RS232C)になります。非同期シリアルを利用するには、最初にuse
を宣言します。 XMIT、RCV、で送受信を行う端子を指定します。
#include <16f84.h>
#use delay(CLOCK=10000000)
#use RS232(BAUD=9600, XMIT=PIN_A3 ,RCV=PIN_A4)
送受信はprintf()、getc()、putc()、などの標準入出力関数を利用できます。printf()では、書式が指定できます。受信を行うと、受信完了まで戻りません。
SSP(Synchronous Serial port)は、クロック信号を同時に送ることで、非同期通信より高速な(数MBPS)通信が可能です。SSPにも、1:1で高速な通信を行う
SPI(Serial Peripheral Interface)と同時に複数のPICに接続できるI2Cがあります。
SPIは各1台のマスタとスレーブで構成します。クロック(SCK)を送り出すPCがマスタになり、SDOから相手のSDIにデータを送ります。このとき同時に、相手のSDOから自分のSDIにデータを取り込むことができます。
SPIの設定は以下のように行います。
setup_spi(mode)
ここで、modeは以下の設定をORして設定します。
mode; SPI_MASTER,SPI_SLAVE,
SPI_L_TOH,SPI_H_TO_L
SPI_CLK_DIV_4,SPI_CLK_DIV_16,SPI_CLK_DIV_64,
データの受信は、
b = spi_read()
で行います。この関数は、受信完了するまで戻りません。スレーブ側では必要なら
b = spi_data_is_in()
で受信完了をチェックします。
送信は、
spi_write(value)
で行います。先に送信したデータは受信することでクリアされます。送受信完了に伴い SSPIF 割り込みが発生します。
#use I2C(MASTER, SDA=pin, SCI = pin,STREAM=id,fast=speed) //マスタFASTで通信速度を指定できます。また、stream でI2Cの区別ができますから、ソフトで複数のI2CやSPIの送受信が可能です。マスタは送信開始、終了時に次の関数を実行します。
#use I2C(SLAVE, SDA=pin, SCI= pin, ADDRESS = val, fast=speed, STREAM=id,NOFORCE_SW) //スレーブ
i2c_start()データ送信の先頭でアクセスする対象のチップアドレスを書き出します。その後、複数のデータを連続送信できます。
i2c_stop()
i2c_write(byte)受信側では、
byte = i2c_read()でデータを受信します。この関数は、受信が完了するまで戻りません。SSP機能を内蔵している場合
byte = i2c_poll()で、データ受信をチェックできます。ハードウエアによる割り込み処理の内部では、以下の関数で、割り込み原因を判断できます。
state=i2c_isr_state() 0:アドレス一致 1-0x7f:マスタからの送信があった 0x80:マスタ送信でアドレスが一致した 0x81-0xff:送信が完了し応答があった
void rtc_control_init() { delay_ms(700); i2c_start(); i2c_write(0xa2); // 書き込みモード i2c_write(0x00); // control0のアドレス i2c_write(0x0); i2c_stop(); }以下は、1番地以後のデータを順に読み出します。最初にチップアドレス、次に内部アドレスを指定します。再度、
void rtc_date_read() { i2c_start(); i2c_write(0xa2); // 書き込みモード i2c_write(0x02); // 秒のアドレス i2c_start(); i2c_write(0xa3); // 読み込みモード sec= i2c_read(1); // 秒の値 min= i2c_read(1); // 分の値 hour= i2c_read(1); // 時の値 day= i2c_read(1); // 日の値 week= i2c_read(1); // 曜の値 month=i2c_read(1); // 月の値 year= i2c_read(0); // 年の値 i2c_stop(); }
#INT_SSP void i2c_isr() { state = i2c_isr_state(); if(state >= 0x80) i2c_write(send_buffer[state - 0x80]); else if(state > 0) rcv_buffer[state - 1] = i2c_read(); }
I2Cのメモリに連続書き込みする場合、ページ境界に注意する必要があります。I2Cメモリは内部のアドレスカウンタは8ビットのため、255の境界をまたいで連続書き込みを行うと現在のページの0番地を書き直してしまいます。
読み出し、書き込み用の関数があります。書き込みは数m秒が必要ですから、注意してください。
val = read_eerom(adrs);
write_eerom(adrs,val);