• ベストアンサー

pic12f675の待ち時間関数について

初めて質問します。 現在pic12f675で遊んでいるのですが、 http://homepage3.nifty.com/mitt/pic/picc/picc675_04_1.html にある、 // =================== 50uSウェイトルーチン =============== static void wait_50uS() { unsigned int cnt; unsigned int i; cnt = 2; for (i=0 ; i<cnt ; i++) { NOP(); } } の意味がわかりません。 特にfor (i=0 ; i<cnt ; i++) { NOP(); } の箇所です。 これでなぜ、50μsの待ち時間関数になるのでしょうか? どなたか答えてくれませんか?

質問者が選んだベストアンサー

  • ベストアンサー
  • tadys
  • ベストアンサー率40% (856/2135)
回答No.3

PIC12シリーズでは4クロックで機械語の1命令を実行します。 (ジャンプする命令では8クロック) この事は例示されたソースリストに // clock 4MHz // 命令 1μS と書いてあることからも分かります。 C言語のソースをコンパイラが機械語に変換した時に何命令になるかは 最適化のレベルによって変わってきます。 例示されたソースリストについて最高の最適化がなされた場合は   nop   nop   return の3命令に変換されます。この場合は3usの時間待ちになります。 まったく最適化をなされない場合にどうなるかを考えて見ると以下のようになります。 static void wait_50uS() { // 関数の入り口での処理 // WREGをセーブする 1CYC // statusをWREGに入れて、RAMにセーブする 2CYC unsigned int cnt;    // 0CYC unsigned int i;     // 0CYC cnt = 2;        // WREGに2を入れ、cntにセーブ 2CYC for (i=0 ; i<cnt ; i++) {         // 1回目のループ         // i=0 ; WREGに0を入れ、iにセーブ 2CYC         // i<cnt ; iをWREGにいれ、cntと比較、i<cntなのでNOP();を実行 4CYC NOP();     // nop; 1CYC         // i++ 1cyc         // ループ開始へジャンプ 1CYC }         // 2回目のループ         // i<cnt ; iをWREGにいれ、cntと比較、i<cntなのでNOP();を実行 4CYC NOP();     // nop; 1CYC         // i++ 1cyc         // ループ開始へジャンプ 1CYC         // 3回目のループ         // i<cnt ; iをWREGにいれ、cntと比較、i<cntでないのでループを抜ける 4CYC } // 関数の出口での処理 // RAMにセーブした statusを元に戻す 2CYC // セーブしたWREGを戻す 1CYC // リターンする 1CYC トータル29CYCなので実行時間は29usになります。 コンパイラがこのように変換しているとは限りませんが50usにはならないでしょう。 MPLABを使用しているのであればストップウォッチ機能で実行時間を測ってみてください。 なお、このようなコードはコンパイラのバージョンや最適化のレベル指定で実行時間が変わるのでアセンブラで記述するほうがいいです。 実行中に割り込みがかかると時間が延びるのでそれが不都合な場合には割り込みを禁止するかタイマーを利用するのがいいです。

hidebubu
質問者

補足

御返答いただきありがとうございます。 御察しの通りMPLABのv8.4を使っています。 break pointをはりつけるところまでは、調べてできたのですが、 デバッガーをクリックしても、select toolとclear memoryしか出てきません(・_・?) もし、ご存知でしたらストップウォッチ機能の使い方を教えていただけないでしょうか?

その他の回答 (3)

  • tadys
  • ベストアンサー率40% (856/2135)
回答No.4

下記のようにセッティングしてください Debugger -> Select Tool -> MPLAB SIM Debugger -> Settings を指定して      Osc/Trace タブを選んで Processor Frequencyに4MHzを設定 Debugger -> Stopwatch  を選択 ブレークポイントのところでStopwatchウインドーの「ZERO」をクリック ステップ実行、または次のブレークポイントまでの時間を計る。 なお、View -> Disassembly List を選択すればコンパイラがどのように機械語に変換したかが分かります。

hidebubu
質問者

お礼

御返答ありがとうございます。 18μsとなりました。 いろいろ勉強させてもらい、感謝至極です。 アセンブリ言語も勉強したいと思います(;^_^A アセアセ・・・ お二人とも、ほんとにありがとうございました!

回答No.2

 まず、CPUの1クロックで1行実行出来るわけではありません。  Cプログラムの1行はコンパイラで数命令から下手すると20命令以上に 変換されます。  さらに、SH2のようにパイプライン処理で1クロック1命令を実行出来る マイコンの場合でも、外部 DRAMのように4 waitが入ってしまうと1クロ ック1命令も怪しくなります。  PIC16 がパイプライン処理をしているか知りませんが、こうゆうループは 実機で確認して合わせるしかないのが実情でしょう。  とゆうわけで、4MHzなら4M行/s ではなく数倍処理速度が落ちると 考えた方が実用的です。  こんな説明でいかがでしょう。

hidebubu
質問者

お礼

御返答ありがとうございます。 上のtadvsさんのストップウォッチ機能で確認してみます。 お付き合いいただき、ありがとうございましたm(_ _)m

回答No.1

 この例題で使われているCPU CLOCK周波数の場合 for ループを2回 実行すると、約50マイクロ秒になるとゆう事です。  ループの実行回数はCPUの種類、クロック周波数で調整する必要が 有ります。

hidebubu
質問者

補足

御返答ありがとうございます。 このマイコンのクロックは4MHzですよね? ということは 1/4000000=0.00000025=0.25μsの処理能力をもつので、必要なループの回数は200回なのではないでしょうか? 調整しているとしたら、プログラムのどこでしているのでしょうか? 回答していただければありがたいです。

関連するQ&A

  • PICのプログラムについて質問です

    使用するPICは16F84Aで、MPLAB IDEv8.88を使ってこのようなプログラムを作りました。 #include"pic.h" static void pic_init(); static void Delay_ms(unsigned char ms); static void Delay_1ms(); void main(){ pic_init(); while(1) { RB0 = 1 ; Delay_ms(250); Delay_ms(250); Delay_ms(250); RB0 = 0 ; Delay_ms(250); Delay_ms(250); Delay_ms(250); } static void pic_init() { // GPIO = 0b00000000; TRISA = 0xFF ; TRISB = 0x00 } static void Delay_ms(unsigned char ms) { unsigned char c; for (c=ms ; c>0 ; c--) { Delay_1ms(); } } static void Delay_1ms() { unsigned int cnt; unsigned int i; cnt = 76; for (i=0 ; i<cnt ; i++) { NOP(); } } ポートB0の出力を0から1にするプログラムなのですが、実行すると Error [314] C:\Users\moriwaki\Desktop\PIC program\step_test2.c; 52.24 ";" expected Error [254] C:\Users\moriwaki\Desktop\PIC program\step_test2.c; 77.0 undefined variable: "pic_init" ********** Build failed! ********** というエラーが出ます。このようなエラーが出る原因を教えて頂けないでしょうか。お願いします。

  • PICプログラミング

    テストプログラムとして以下のようなプログラムを作ってみました。 RB0を一定時間だけHIGHにした後に、LOWに戻すプログラムなのですが、テスターで測定したところRB0は結果的にHIGHのままでした。wait関数が間違っているのかと思いましたが、どう考えても文法的な間違いはないと思います。 原因がまったく分からないので、分かる方がいましたらヒントだけでもいいので是非教えてください。よろしくお願いします。 PIC16F819 セラロック20MHz #include <pic.h> __CONFIG(DEBUGEN&WDTDIS&LVPDIS&HS&PWRTEN); #define voltage 5.0 void wait(int a){ int i,j; for(i=0;i<=30000;i++) for(j=0;j<=2000*a;j++); } void init_a2d(void){ ADCON0=0x80; ADCON1=0x00; ADON=1; } unsigned char read_a2d(unsigned char channel){ channel&=0x07; ADCON0&=0xC5; ADCON0|=(channel<<3); // GODONE=1; // while(GODONE)continue; ADCON0|=0x04; while(ADCON0&0x04)continue; return(ADRESH); } void main(void){ int i,j; unsigned char x; double y; init_a2d(); GIE=0; TRISB=0x00; PORTB=0x00; RB0=1;    wait(5); RB0=0; }

  • PIC16F88に書き込んだプログラムについて

    以前、直流電圧と電流を測定する回路を製作し、その時書いたプログラムでは正常にLCDに表示されていたのですが、最近になって測定した電圧および電流を1秒間に8回更新するように書き替えました。しかしLCDに正常に表示されなくなりました。 プログラムと回路図を掲載しますのでどなたか、どうすればいいか教えてください。 よろしくお願い致します。 ////////////////////以下プログラム/////////////////////////////////////////////////// <lcd.c> #include <pic.h> //#include "lcd.h" //#include "delay.h" #define _XTAL_FREQ 4000000 #define LCD_RS RA0 #define LCD_RW RA7 #define LCD_EN RA6 #define LCD_DATA PORTB #define LCD_STROBE() ((LCD_EN = 1),(LCD_EN=0)) /* write a byte to the LCD in 4 bit mode */ void lcd_write(unsigned char c) { //DelayUs(40); __delay_us(40); LCD_DATA = ( ( c >> 4 ) & 0x0F ); LCD_STROBE(); LCD_DATA = ( c & 0x0F ); LCD_STROBE(); } /* * Clear and home the LCD */ void lcd_clear(void) { LCD_RS = 0; lcd_write(0x1); //DelayMs(2); __delay_ms(2); } /* write a string of chars to the LCD */ void lcd_puts(const char * s) { LCD_RS = 1; // write characters while(*s) lcd_write(*s++); } /* write one character to the LCD */ void lcd_putch(char c) { LCD_RS = 1; // write characters lcd_write( c ); } /* * Go to the specified position */ void lcd_goto(unsigned char pos) { LCD_RS = 0; lcd_write(0x80+pos); } /* initialise the LCD - put into 4 bit mode */ void lcd_init() { char init_value; //ADCON1 = 0x06; // Disable analog pins on PORTA init_value = 0x3; //TRISA=0; //TRISB=0; LCD_RS = 0; LCD_EN = 0; LCD_RW = 0; //DelayMs(15); // wait 15mSec after power applied, __delay_ms(15); LCD_DATA = init_value; LCD_STROBE(); //DelayMs(5); __delay_ms(5); LCD_STROBE(); //DelayUs(200); __delay_us(200); LCD_STROBE(); //DelayUs(200); __delay_us(200); LCD_DATA = 2; // Four bit mode LCD_STROBE(); lcd_write(0x28); // Set interface length //lcd_write(0xF); // Display On, Cursor On, Cursor Blink lcd_write(0x0C); lcd_clear(); // Clear screen //lcd_write(0x6); // Set entry Mode lcd_write(0x07); } <main.c> #include <pic.h> #include <stdlib.h> #include "lcd.h" #define _LEGACY_HEADERS #define _XTAL_FREQ 4000000 __CONFIG(FOSC_INTOSCIO & WDTE_OFF & PWRTE_ON & MCLRE_OFF & BOREN_OFF & LVP_OFF & CPD_OFF & WRT_OFF & CCPMX_RB0 & CP_OFF); __CONFIG(FCMEN_OFF & IESO_OFF); unsigned int ADConv(unsigned char ch) { ADCON0=(ch<<3)&0x38; ADCS2=0; ADCS1=0; ADCS0=1; ADON=1; ADIF=0; __delay_us(20); GO_DONE=1; while(GO_DONE); return (ADRESH<<8) | ADRESL; } unsigned short Code2mV(unsigned char ch) { unsigned short ret; ret=0; ret += ADConv(ch)<<1; ret += ADConv(ch)>>1; ret += ADConv(ch)>>3; ret += ADConv(ch)>>4; ret += ADConv(ch)>>5; return ret; } void ioport(void) { CMCON=0x07; ANSEL= 0b00001110; TRISA = 0b00001110; PORTA=0x00; TRISB = 0b00000000; PORTB=0x00; } void main(void) { static double data, offset; static unsigned int v ,i,n1,n0; static unsigned char buf[8], cnt,tmp1[2],tmp2[2]; OSCCON = 0b01100000; // �N���b�N��8Mhz ioport(); ADCON1=0b11100000; lcd_init(); lcd_write(0x0D); lcd_goto(0); lcd_puts("Starting"); lcd_goto(0x40); lcd_puts("DC Meter"); __delay_ms(2500); lcd_clear(); offset=0.0; while (1) { data=0.0; for(cnt=0; cnt<10; cnt++) { data += Code2mV(1); } data /= 10.0; data *= 2.4287109375; data *= 11.0; data -= offset; v=(unsigned int)(data); n1=(unsigned int)(v/1000); n0=v-1000*n1; itoa(tmp2,n1,10); itoa(tmp1,n0,10); lcd_goto(1); lcd_puts(tmp2); lcd_goto(3); lcd_putch('.'); lcd_puts(tmp1); lcd_goto(6); lcd_puts("V"); data =0.0; for(cnt=0; cnt<10; cnt++) { data += Code2mV(2); } data /= 10.0; data *= 2.4287109375; data /= 11.0; offset = data; i=(unsigned int)(10.0 * data); itoa(buf,i,10); lcd_goto(9); lcd_puts(buf); lcd_goto(13); lcd_puts("mA"); __delay_ms(125); } }

  • PICマイコンのI/Oについて教えてください。

    PIC16F1936を使っています。 各ポートをデジタル出力にしLEDを順に点灯させていますが RB0に接続したLEDのみうっすらとしか光りません。 (プログラムで意図したとおり点滅はします。) テスターで電流を計測したところRB0のみほとんど流れていませんでした。 また電源投入時にRB0に接続したLEDだけ一瞬光ります。 PICは初期状態でI/Oは入力になっているので光らないと思うんですが・・・ MPLABX+XC8で開発しています。 以下の切り分けを行いました。 ・LED単体での点灯:正常点灯 ・他のLEDをRB0に接続:現象再現 ・他のブレットボードで作動:現象再現 データシートとかなり睨めっこしたんですがそれっぽい部分がみつかりません ご教示いただけると助かります。 ソースは以下のとおりです。 #include <xc.h> #pragma config CLKOUTEN = OFF,\ WDTE = OFF,\ PWRTE = ON,\ CP = OFF,\ BOREN = OFF,\ FCMEN = OFF,\ MCLRE = ON,\ CPD = OFF,\ IESO = OFF,\ FOSC = INTOSC,\ STVREN = OFF,\ BORV = LO,\ LVP = OFF,\ VCAPEN = OFF,\ WRT = OFF,\ PLLEN = ON #define uchar unsigned char #define uint unsigned int void init(void){ //ポート初期化 PORTA = 0b00000000; PORTB = 0b00000000; PORTC = 0b00000000; PORTE = 0b00000000; TRISA = 0b00000000; TRISB = 0b00000000; TRISC = 0b00000000; TRISE = 0b00000000; //全てデジタル ANSELA = 0b00000000; ANSELB = 0b00000000; //8mhz OSCCON = 0b11110000; OPTION_REG = 0b00000000; APFCON = 0; WPUE = 0; } void Wait(unsigned int num){ for (int i=0 ; i<num ; ++i) { for( int j = 0; j < 100; ++j){ NOP(); } } } void main(void){ init(); while(1){ PORTC = 0b00000100; Wait(1000); PORTC = 0b00000000; PORTB = 0b00001000; Wait(1000); PORTB = 0b00100000; Wait(1000); PORTB = 0b00000001; Wait(1000); PORTB = 0b00000000; } }

  • PICマイコン 動作せず、教えて下さい。

    PICマイコン初心者です。 PICマイコン(PIC16F877)をHI-TEC社のPICCでコンパイルして、Buildしたものを 焼き込んで電源印可しますが全然動作せず。  出力に設定しているピン(PORTB)の電圧をみると全て3.8Vぐらいになっています。(不定??)  ちなみに出力PinはVdd(5V) => 発光D => 抵抗 => PORTBと接続されています。  どこが悪いか教えて頂きたく。  *** ソース ファイル ****   #include"pic.h"   void main(void){ unsigned long i; TRISA = 0x00; TRISB = 0x00;   while(1){ PORTB = 0b11111111; //RB0をLowに設定 for(i=0;i<20000000;i++); PORTB = 0b11111110; //RB1をLowに設定 for(i=0;i<40000000;i++); PORTB = 0b11111101; //RB2をLowに設定 for(i=0;i<15000000;i++);          }        }

  • PIC16F819のADC機能について

     PIC16F819のA/D変換プログラムをC言語で作りたいのですが、参考になるサイトや本がないのでどこから手をつけていいのかまったく分かりません。  試しにPICC Compilerの期間限定版をダウンロードして、その中にあったサンプルプログラムを見てみました。PIC16F87X用のADCのサンプルプログラムがあったので、とりあえずMPLABでコンパイルしてみましたが、ADGOの部分でエラーが出てしまいました。どうもPIC16F819ではADGOが使用できないようですが、どうすれば改善されるのでしょうか?  分かる方がいましたら、御教授よろしくおねがいします。 サンプルプログラム #include <pic.h> __CONFIG(DEBUGEN&WDTDIS&LVPDIS); /***Sample code to set up the A2D module********/ void init_a2d(void){ ADCON0=0; ADCON1=0; ADON=1; } /***Return an 8 bit result**********************/ unsigned char read_a2d(unsigned char channel){ channel&=0x07; ADCON0&=0xC5; ADCON0|=(channel<<3); ADGO=1; while(ADGO)continue; return(ADRESH); } /***Main関数*************************************/ void main(void){ unsigned char x; init_a2d(); GIE=0; TRISB=0xF0; for(;;){ x=read_a2d(1); PORTB=(8>>(x>>6)); } }

  • PICのコンフィグレーションワードの書き方

    MPLAB + PICC-Lite + PICkit2 で PIC16F877(20MHz)を使用しようとしています。 今まで、16F627Aを使用していて、今回が初めての16F877です。最初にDポートのLED4つを点滅させるプログラムを実行しようとしました。 最初、ディパッガーでも動かなかったのですが、MPLAB IDEのConfigureメニューのConfiguration BitsをHSに変更したら動きました。 ところがPICkitを外して、16F877単体で動きません。 おそらく、コンフィグレーションワードで、HSを書き込まなければだめらしいということは、わかったのですが、その書き方がわかりません。 現在のソースファイルは以下の通りです。 ----------------------- #include <pic.h> void delay(void) { unsigned char i, j; for (i = 0; i < 254; i++) { for (j = 0; j < 254; j++) { } } } void main(void) { TRISD = 0; unsigned char led = 0x01; do { PORTD = led; if (led == 0x08) { led = 0x01; } else { led <<= 1; } delay(); } while (1); }

  • PICマイコンで時間測定の方法を教えて!

    PICマイコン初心者です。時間を正確に測定して表示させたいのですが、時間を正確に測定する方法が分からず苦慮しています。教えてください。  デバイスはPIC16F877、プログラミング言語はMPLABに付属していた  Cコンパイラ(HI-TECH PICC Lite)です。 現在、考えてうまく動作していない記述を下記に示します。ご指摘頂きたく。  Buildは成功するのですが動作せず???? 私が考えていたのはRC0がLOWのときにdelay(100ms)をカウントしていき、 それをバイナリでPORTBに表示する方法です。 測定レンジは0.1 - 60secぐらいまでです。 ***************************************************************************** #include <pic.h> #define _XTAL_FREQ 16000000 __CONFIG(HS & DEBUGDIS & LVPDIS & BORDIS & PWRTEN & WDTDIS ); int main(void){ unsigned long T1 = 0 ; // iの変数設定 TRISC = 0b00000001 ; // RC0入力 その他は出力設定 TRISB = 0x00 ; // PORT B 出力 PORTC = 0xff    ; // PORTCをall 1を設定 PORTB = 0x00 ; // PORTBを"0"Resetする if ( RC0 == 1 ) { } else { __delay_ms(100) ; // 100ms wait T1++ ; // T1 = T1 + 1 PORTB = T1 ; } }

  • PIC16F1823の割り込み処理について

    PIC初心者です。よろしくお願いします。 ※HI-TECH Cコンパイラを使用しています。 最近まで、PIC16F676を使用していましたが、 今回、PIC16F1823に変更することにしました。 プルグラムをPIC16F1823用に変更し、実行したところ思ったように動いてくれません。 いろいろ調べたところ割り込み処理がうまいこと出来ていないような気がします。 割り込みルーチンに飛んだ後、なぜか戻ってこないような気がします。 下記のコードは、簡単に書き直しました。 どなたか分かる方、お願いしますm(_ _)m --------------------------------------------- #include <htc.h> __CONFIG(FOSC_INTOSC & WDTE_OFF & PWRTE_ON & MCLRE_OFF & BOREN_ON & CP_OFF); unsigned int i,Time; void main(void) { OSCCON = 0b01101010; //おそらく4MHz? //ポートA ANSELA = 0x00; //全てデジタル CM1CON0 = 0b00000111; //コンパレート無効化 CM1CON1 = 0b00000111; TRISA = 0b00111110; //ポートA入力出力設定 WPUA = 0b00110110; //ポートAプルアップビット指定 PORTA = 0b00000100; //各ピンデフォルト値 IOCAP = 0b00100000; //割り込みピン指定 //ポートC ANSELC = 0x00; //全てデジタル TRISC = 0b00000000; //ポートC入力出力設定 nWPUEN = 0; //プルアップ有効化 IOCIE = 1; IOCIF = 0; GIE = 1; i = 0; while(1){  if(i){   RC5 =1;   RC0 =0;  }else{   RC5 =0;   RC0 =1;  } } } void interrupt ISR(void){ //割込み if(IOCIF && !RA5){  if(i) i=0;else i=1; } IOCIF = 0; }

  • PICの割り込み機能

    現在電子工作をしており、それにPIC16F84Aを使っています。 正直アセンブリはよくわからないので、C言語でプログラムを作っています。 コンパイラはPICCLITEで、LEDの点灯・消灯に関するプログラムです。 機能としては ・RA0に入力があったときRB0につないであるLEDが消灯しているならLEDを点灯。 ・RA0に入力があったときRB0につないであるLEDが点灯しているならLEDを消灯。 ・RA1に入力があったときLEDが点灯しているなら5秒後にLEDを消灯。 というものを目指しています。 1つ目、2つ目の項目はif文で簡単に実現できましたが、割り込みがうまくいかず、 消えている状態でRA1に入力を入れたときなぜか5秒後に点灯してしまいます。 だからRB0の出力を逆にしてみたんですがうまくいかず・・・。 ハード的には、RB0には反対側から5Vをかけ、 RB0=0の時は点灯 RB0=1の時は消灯 としています。 またセラロックは10MHzのものを使用しています。 現在のプログラムとしては #include "pic.h" #define XTAL_FREQ 10MHZ #define MHZ*1000 void DelayUs(unsigned char cnt){ //時間待ち関数  unsigned char i;  i=(cnt)/(12MHZ/(XTAL_FREQ))|1;  while(--i!=0) continue; } void DelayMs(unsigned int cnt){ //時間待ち関数  unsigned char i;  do{   i=4;   do{    DelayUs(250);   }while(--i);  }while(--cnt); } int cnt,SW; //SWが0なら消灯 //SWが1なら点灯 void interrupt isr(void){ //割り込み関数  if(T0IF==1){   T0IF=0;   cnt--;  }  if(cnt==0){   RB0=1; //消灯   SW=0;   cnt=190;   T0IE=0;   GIE=0;  } } main() {  TRISA=0xFF; //入出力設定  TRISB=0x00;  PORTA=0x00;  PORTB=0x00;  SW=0;  OPTION=0x87; //プリスケーラの設定  TMR0=0x00;  T0IF=0;  T0IE=1;  cnt=190;  while(1){   if(RA0==1){    DelayMs(60); //チャタリング防止    if(RA0==1){     if(SW==0){      RB0=0;      SW=1;     }    else{     RB0=1;     SW=0;    }   }  }  if(RA1==1){   DelayMs(60); //チャタリング防止    if(RA1==1 && SW==1){     TMR0=0;     T0IF=0;     T0IE=1; //割り込み許可     GIE=1; //全体割り込み許可    }   }  } } インターネット上での割り込みのプログラムをいくつか見てみたのですが、 どれもすべて動作を終えて、あとはwhile(1)で割り込みを待つだけ というプログラムばかりでした。 僕のは基本的にRA0の入力によってLEDを点灯・消灯させつつ RA1の入力で割り込みを開始。 5秒後に消えたら割り込みを禁止する。 という仕様にしたいのですが・・・・・・。 1ヶ月ぐらいやってますがうまくいきません。 良ければご回答をお願いします。

専門家に質問してみよう