PIC16F877A(PICマイコン) ~ C言語 ~

PIC16F877A(PICマイコン) ~ C言語 ~

TEditorMX(テキストエディタ)をご活用頂ければと思いまして、『PIC16F877A(PICマイコン) ~ C言語 ~』です。 アセンブリ言語バージョンと同等のC言語プログラムをご紹介致します。

PICマイコンをやってみたところ、やはりPICのハードウェアとアセンブリ言語をある程度理解しないとC言語でプログラミングできないと思います。 なのでその辺の知識が無い方はまずアセンブリ言語バージョンの方をご覧になって下さいね。



ここで使用したCコンパイラはMicrochipさんの XC8 ver2.05(無償版)です。 MPLAB X IDE 同様、頑張ってインストールして下さいね。 なお、XC8はコマンドラインからも使用できますのでTEditorMXのコンパイル機能を使ってコンパイルできます。

ちなみに最初はSDCCを使おうと思ったのですが、コンパイルすると原因不明のワーニングが出力されるため断念しました。 PICは種類がたくさんあるため、まだ十分に対応できていないのかもしれません。

CPU周りの回路はアセンブリ言語バージョンのものと全く同じものです。 ですので基板も同じものを使用しました。

ページのTOPへ




(1)ソースファイル

ソースファイルのアーカイブはioc.zipserialc.zipの2つです(下線部をクリックしてダウンロードして下さいね)。 ioc.zipはスイッチの入力とLEDへの出力をC言語に書き換えたものです。 serialc.zipはシリアル通信をC言語に書き換えたものです。 それぞれのソースファイルには対応するアセンブリ言語コードをコメントとして記述しておきましたのでご覧ください。

ページのTOPへ




(2)環境設定

環境設定はインストールしたCコンパイラ(XC8)をコマンドラインから実行できるようにパスを通すバッチファイルを作成(修正)します。 アーカイブに含めた setpath.bat は私(管理人)がXC8をデフォルトでインストールしたときのものです。 ご自分の環境に合わせてXC8のbinフォルダにパスを通して下さい。

setpath.bat

set Path="C:\Program Files (x86)\Microchip\xc8\v2.05\bin";%Path%

            ※下線部をご自分の環境に合わせて書き換えて下さい


setpath.batを作成(修正)したらコマンドプロンプトを開き、setpath.batを実行後xc8と入力して実行してみて下さい。 正しくパスが通っていれば下図のような感じに表示されると思います。

実際にやってみたときの様子



ページのTOPへ




(3)コンパイル


XC8のbinフォルダにパスを通すことができればコンパイル用バッチファイル(compile.bat)が実行可能です。 ただし、compile.batはソースファイルを格納しているフォルダ(src)の親フォルダをカレントフォルダにして実行する必要があります。 ちょっと面倒ですが、これはXC8が多くのファイルを出力してくるため、ソースファイルと分離したいがための対策です。

コマンドプロンプトを開いて上の項で作成(修正)したsetpath.batを実行後、更にcompile.batを実行してみて下さい。 上手くいけば下図のような感じになると思います。 またmain.hexファイル(PICに書き込むファイル)が作成されていることも確認してみて下さい。

実際にやってみたときの様子



XC8のコンパイルスイッチですが、-E オプションを指定するとエラーが発生した各行にファイル名が出力されますのでTEditorMXでタグジャンプできるようになります。 逆に -E オプションを指定しないとファイル名が取得できないためTEditorMXでタグジャンプできません。
--ASMLIST オプションを付けますとリストファイル(.lst)が作成されます。 必要な方は指定しましょう。


ページのTOPへ




(4)C言語ソースファイル

アーカイブをダウンロードするのが面倒な人のためにソースプログラムをダラダラと掲載してみることにします。


main.c(ioc.zip)

/* 
 * I/O入出力サンプルプログラム
 *
 * アセンブリ言語をC言語に置き換えただけのものです。
 * 元のアセンブラソースをコメントとして記述してあります。
 */

// configuration bits  (FOSCは発振器の種類・周波数で変化します。その他はOFFでとりあえずOKです)
//	__CONFIG	_FOSC_HS & _WDTE_OFF & _PWRTE_OFF & _BOREN_OFF & _LVP_OFF & _CPD_OFF & _WRT_OFF & _CP_OFF
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

#include	<xc.h>


//-------------------------------------------------------------
// 初期化
//-------------------------------------------------------------

void init( void )
{
/*
; BANK0選択
	BCF	STATUS, RP0
	BCF	STATUS, RP1

;念のためI/Oポート出力を0に
	CLRF	PORTA
	CLRF	PORTB
	CLRF	PORTC
	CLRF	PORTD
	CLRF	PORTE
*/

// 念のためI/Oポート出力を0に
	PORTA = 0;
	PORTB = 0;
	PORTC = 0;
	PORTD = 0;
	PORTE = 0;


/*
;BANK1を選択
	BSF	STATUS, RP0

;PORTDを入力に設定
	MOVLW	0xff
	MOVWF	TRISD
*/

// PORTDを入力に設定
	TRISD = 0xff;

/*
;PORTD以外をすべて出力に設定
	CLRF	TRISA
	CLRF	TRISB
	CLRF	TRISC
	CLRF	TRISE
*/

// PORTD以外をすべて出力に設定
	TRISA = 0;
	TRISB = 0;
	TRISC = 0;
	TRISE = 0;

/*
;BANK0を選択
	BCF	STATUS, RP0
*/
}


//-------------------------------------------------------------
// メインループ
//
// ※MPLAB X IDE ではmain関数はint型の戻り値を返すようですが、
// ここでは不要と思われるのでvoid型にしてあります。
//-------------------------------------------------------------
void main( void )
{
/*
;PORTDを読み込み、そのままPORTBに出力
	MOVF	PORTD, w
	MOVWF	PORTB
	GOTO	main

	END
*/
	unsigned char	w;

	init();

// PORTDを読み込み、そのままPORTBに出力
	for(;;) {
		w = PORTD;
		PORTB = w;
	}
}



main.c(serialc.zip)
/* 
 * シリアル通信サンプルプログラム
 *
 * 機能:パソコンとRS-232Cのクロスケーブルで接続してターミナルソフトで文字をMCU側に送ると
 *   オウム返しで文字を送信し返します。
 *
 * 基本的にアセンブリ言語ソースをC言語に置き換えただけのものです。(若干、異なるところがあります)
 * 元のアセンブラソースをコメントとしてC言語ソースの上に記述してあります。
 *
 */

// configuration bits  (FOSCは発振器の種類・周波数で変化します。その他はOFFでとりあえずOKです)
//	__CONFIG	_FOSC_HS & _WDTE_OFF & _PWRTE_OFF & _BOREN_OFF & _LVP_OFF & _CPD_OFF & _WRT_OFF & _CP_OFF
#pragma config FOSC = HS        // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)


#include	<xc.h>



//-------------------------------------------------------------
// グローバル変数
//-------------------------------------------------------------
static volatile unsigned char	rxtmp;		// 割り込みルーチンで使用するので volatile が必要です



//-------------------------------------------------------------
// 割り込み処理
//-------------------------------------------------------------
void /*high_priority*/ interrupt tcInt(void)	// high_priority はワーニングが出るのでコメントにしました
{
/* ユーザーズガイドより (コーディングの例)
	if (TMR0IE && TMR0IF) { // any timer 0 interrupts?
		TMR0IF=0;
		++tick_count;
	}
	if (TMR1IE && TMR1IF) { // any timer 1 interrupts?
		TMR1IF=0;
		tick_count += 100;
	}
*/

// 受信割り込みのとき
	if( PIR1bits.RCIF ) {
		rxtmp = RCREG;
//		PORTB = rxtmp;
	}

// process other interrupt sources here, if required
	return;
}



//-------------------------------------------------------------
// 初期化
//-------------------------------------------------------------
void init( void )
{

// グローバル変数初期化
	rxtmp = 0;

/*
; BANK0選択
	BANKSEL	PORTA
;念のためI/Oポート出力を0に
	CLRF	PORTA
	CLRF	PORTB
	CLRF	PORTC
	CLRF	PORTD
	CLRF	PORTE
*/
// 念のためI/Oポート出力を0に
	PORTA = 0;
	PORTB = 0;
	PORTC = 0;
	PORTD = 0;
	PORTE = 0;

/*
;BANK1を選択
	BANKSEL	TRISD

;PORTDを入力に設定
	MOVLW	0xff
	MOVWF	TRISD
*/
// PORTDを入力に設定
	TRISD = 0xff;

/*
;PORTC,PORTD以外をすべて出力に設定
	CLRF	TRISA
	CLRF	TRISB
	CLRF	TRISE
*/
// PORTC,PORTD以外をすべて出力に設定
	TRISA = 0;
	TRISB = 0;
	TRISE = 0;

/*
;RS232C設定
;RCSTA
	BANKSEL	RCSTA
	MOVLW	B'10010000'
	MOVWF	RCSTA

;TXSTA
	BANKSEL	TXSTA
	MOVLW	B'00100110'
;	MOVLW	B'00100100'
	MOVWF	TXSTA
;SPBRG
	BANKSEL	SPBRG
	MOVLW	D'64'
	MOVWF	SPBRG
*/
// RS232C設定
	RCSTA = 0x90;	// B'1001_0000'
	TXSTA = 0x26;	// B'0010_0110'
	SPBRG = 64;		// D'64'

/*
;BANK1を選択
	BANKSEL	TRISC
;PORTCのbit7は入力、それ以外は出力に設定
	MOVLW	B'10000000'
	MOVWF	TRISC
*/
// PORTCのbit7は入力、それ以外は出力に設定
	TRISC = 0x80;	// B'1000_0000'

/*
;割り込み設定
;PIE1
	BANKSEL	PIE1
	MOVLW	B'00100000'		;とりあえず受信割り込みだけ設定
;	MOVLW	B'00010000'		;送信割り込みだけ設定
;	MOVLW	B'00110000'		;送受信割り込みはこっち
	MOVWF	PIE1
;INTCON
	BANKSEL	INTCON
	MOVLW	B'11000000'		;割り込み許可(bit7は全体の割り込み許可)
	MOVWF	INTCON
*/
	PIE1 = 0x20;			// B'0010_0000'
	INTCON = 0xc0;			// B'1100_0000'

/*
;BANK0を選択
	BANKSEL	PORTA		;PORTA は BANK0
*/
}



//-------------------------------------------------------------
// (rxtmpに格納されている)1文字を送信
//
//  注意:アセンブリ言語ソースは w に格納されている文字を送信します
//-------------------------------------------------------------
void TxChar( void )
{
/*
;	MOVWF	PORTB		;そのままPORTBへ出力
*/
//	PORTB = rxtmp;

/*
	BTFSS	PIR1, TXIF
	GOTO	$-1
	MOVWF	TXREG
*/
	for(;;) {
		if( PIR1bits.TXIF ) {
			TXREG = rxtmp;
			break;
		}
	}
}



//-------------------------------------------------------------
// メインループ
//
// ※MPLAB X IDE ではmain関数はint型の戻り値を返すようですが、
// ここでは不要と思われるのでvoid型にしてあります。
//-------------------------------------------------------------
void main( void )
{
// 初期化
	init();

/*
main1:
	MOVF	RXTMP, W
	BTFSC	STATUS, Z	;RXTMPが0のときはmainに戻る
	GOTO	main1

	CLRF	RXTMP		;受信データを0に(重複送信を防ぐため)
	CALL	txchar		;Wの値(文字)を送信
	GOTO	main1
*/
	for(;;) {
		if( rxtmp ) {	// 受信データがあるとき
			TxChar();
			rxtmp = 0;
		}
	}
}




アセンブリ言語のソース(コメント行)を削除すればかなりすっきりと記述できていることに気づきます。 やはりPICでもC言語で書きたいですね。

最後に、このページからダウンロードできるソースプログラムに関してはフリーウェアとします。 よろしければご自由にお使い下さい。



ページのTOPへ

メニュー