質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

マイコン

マイクロコンピュータの略で、CPUにマイクロプロセッサを用いたコンピュータのこと。家電製品、電磁機器などの制御に用いられています。単体でコンピュータとしての機能を一通り備えています。 現代のパーソナルコンピュータに近く、同時期のメインフレームやミニコンピュータと比べ、小さいことが特徴です。

Q&A

解決済

5回答

1245閲覧

LED点滅制御によるカウント不定

GM-DS

総合スコア3

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

プログラミング言語

プログラミング言語はパソコン上で実行することができるソースコードを記述する為に扱う言語の総称です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

マイコン

マイクロコンピュータの略で、CPUにマイクロプロセッサを用いたコンピュータのこと。家電製品、電磁機器などの制御に用いられています。単体でコンピュータとしての機能を一通り備えています。 現代のパーソナルコンピュータに近く、同時期のメインフレームやミニコンピュータと比べ、小さいことが特徴です。

0グッド

0クリップ

投稿2020/04/30 00:24

編集2020/04/30 23:30

前提・実現したいこと

初めて質問させて頂きます。
PICの初心者で、様々な所からご教示頂き勉強中です。
よろしくお願いいたします。

PIC16F1503を使用して下記動作のプログラミングを製作しています。
①PB1を押すとLED1が10秒間点滅しBZがON、LED1が点滅中にPB2を押すとキャンセルされ最初に戻る。
②10秒後LED1が消灯、BZがOFF、LED2が点滅
③PB2を押すとすべてキャンセルされ①に戻る

発生している問題・エラーメッセージ

動作はしているが
①PB1を押した後の、10秒カウントが17秒位ある。
②LED2の点滅が不安定で点滅周期が長かったり、短かったりする。
また、PB2を押さない限りLED2はずっと点滅するはずが、途中から点灯に代わってしまう。
③そもそもこのコードは最適なのか?

該当のソースコード

#define _XTAL_FREQ 2000000 void main(void) { //マイコン設定 OSCCON = 0b01100000; //内部クロック周波数を2MHzに設定 ANSELA = 0b00000000; //PortA全てのピンをデジタルモードに設定 ANSELC = 0b00000000; //PortC全てのピンをデジタルモードに設定 TRISA = 0b00000000; //PortA全てのピンを入力モードに設定 TRISC = 0b00000000; //PortC全てのピンを出力モードに設定 //初期処理:電源ON後、全ての出力を一度OFFにする。 LATC0=0; //LED1 LATC1=0; //LED2 LATC2=0; //BZ //RA5:PB1、RA4:PB2 while(1){ if(RA5==1){ int cnt=0; //PB1がONの時、最初にカウンタに0を代入 while(cnt<10000 && RA4==0){ //カウント値が30秒以下、PB2 OFFの時、下を実行 LATC2=1; //BZ ON if(cnt%500<250)LATC0=1; //LED1 ON else LATC0=0; //LED1 OFF __delay_ms(1); cnt++; } while(cnt==10000){ //カウント値が10秒に達したら下を実行 LATC0=0; //LED1 OFF LATC2=0; //BZ OFF while(RA4==0){ //PB2を押さない限り下を実行 if(cnt%1000<500)LATC1=1; //LED2 ON else LATC1=0; //LED2 OFF __delay_ms(1); cnt++; } } LATC0=0; //LED1 OFF LATC1=0; //LED2 OFF LATC2=0; //BZ OFF } } return; } ```### 試したこと ①の10秒カウントについて ・点滅コードが原因かと思い、LED1の点滅をやめて10間点灯に変更すると正常に10後LED2が点滅した。 ・点滅コードを ```ここに言語を入力 for(timer=0;teimer<10;timer++){ LATC0=1; __delay_ms(500); LATC1=0; __delay_ms(500); ```にすると、PB2を押してもキャンセルされない時がある。 ②の点滅が点灯に代わってしまう事について カウント上限?になってしまい、計算結果が真のままになってしまって点灯しっぱなしになっているのが原因かと思い、cnt++の下に if(cnt==2000){ cnt=0; } を追加したが関係なかった。 ### 補足情報(FW/ツールのバージョンなど) MPLABX IDE v5.30を使用

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

dodox86

2020/04/30 01:08

見づらいですので、プログラムのソースコードはマークダウン記法で記載してください。(読みづらいと、それだけで回答を得る機会が減ります) https://teratail.com/help#about-markdown C言語のソースコードであれば ```C int main() { printf("hello\n"); return 0; } ``` などと書きます。
ikadzuchi

2020/04/30 01:18

文中にPB1・PB2・LED1・LED2とありますが、コード中には作動PB・停止PB・作動灯・不具合灯とあります。 合わせてください。 また、LEDがHiとLowのどちらで点灯するか分かりません。
GM-DS

2020/04/30 01:56

コードを見やすく、コメントを統一致しました。 ご確認の程、よろしくお願いいたします。
thkana

2020/05/12 11:26

> ②LED2の点滅が不安定で点滅周期が長かったり、短かったりする。 > また、PB2を押さない限りLED2はずっと点滅するはずが、途中から点灯に代わってしまう。 こっちの方は解決してたんでしたっけ?
GM-DS

2020/05/13 04:13

thkana様 おはようございます。 LED2の不安定点滅については、実機で内部クロックを変えたコードで確認した所、改善されました。 すごく大きな誤差ではありませんが、改善され気にならない程度になりました。 途中から点灯については、cnt を unsigned intにする事により改善されました。勉強不足でした。 お気に留めて頂きありがとうございます。
guest

回答5

0

__delay_ms(500);

にすると、PB2を押してもキャンセルされない時がある。

delay中に押して離すと、押していないと判定されるからでしょう。

cnt++の下に

if(cnt==2000){
cnt=0;
}
を追加したが関係なかった。

cnt10000を超えているのでcnt==2000が成立することはありません。
while(RA4==0)の前でcnt0にしておけばそのコードでも動くでしょう。

PB2を押さない限りLED2はずっと点滅するはずが、途中から点灯に代わってしまう

cntint(16bit)なので32767を超えるとオーバーフローしてマイナス値になり、マイナス値の間はcnt%1000<500が成立します。

(追記)
時間が合わない件について書き忘れていました。
y_waiwaiさんの回答にもある通り、delay以外の時間も加算されるため細かく刻むほどdelay以外で消費した時間が蓄積され表面化します。
LED点滅と時間カウントにはタイマの使用を検討されてみては?
時間の正確性にあまりこだわらないなら100ms間隔程度にすれば気にならない程度に収まると思いますが、その場合はdelayを伸ばした分ボタンはゆっくり操作する必要があります。

C

1__delay_ms(100); 2cnt+=100;

ループが回るたびにLATC0,LATC1,LATC2を設定する必要もないですね…


250msや500msに一度しかLED設定していないので、以下のようにしてLED設定回数を減らし除算もなくすとどうなります?

C

1 while(1){ 2 if(RA5==1){ 3 int cnt=0; //PB1がONの時、最初にカウンタに0を代入 4 int led=0; 5 LATC2=1; //BZ ON 6 int ra4 = RA4; 7 while(cnt<10000 && ra4==0){ //カウント値が10秒以下、PB2 OFFの時、下を実行 8 LATC0=led=1-led; 9 for(int i = 0; i < 250 && ra4==0; ++i){ 10 __delay_ms(1); 11 cnt++; 12 ra4 = RA4; 13 } 14 } 15 if(cnt==10000){ //カウント値が10秒に達したら下を実行 16 LATC0=0; //LED1 OFF 17 LATC2=0; //BZ OFF 18 led=1; 19 ra4 = RA4; 20 while(ra4==0){ //PB2を押さない限り下を実行 21 LATC1=led=1-led; 22 for(int i = 0; i < 500 && ra4==0; ++i){ 23 __delay_ms(1); 24 ra4 = RA4; 25 } 26 } 27 } 28 LATC0=0; //LED1 OFF 29 LATC1=0; //LED2 OFF 30 LATC2=0; //BZ OFF 31 } 32 }

投稿2020/04/30 00:55

編集2020/05/03 09:48
SHOMI

総合スコア4079

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

SHOMI

2020/04/30 01:16

時間が合わない件について書き忘れていましたが、y_waiwaiさんの回答の通りです。
GM-DS

2020/04/30 06:28

SHOMI様 お忙しい中、回答頂きありがとうございます。 cnt==2000の件、確かにその通りだと思いました。 そこに関してはもう一度作り直しテストしたいと思います。 また、オーバーフローになってしまう件もきちんと理解していればと思いました。 できればPB2の押し方に関係なくリセットをしたいと思っております。 引き続き何か考えてみたいと思います。 コードではなく、考え方のヒントがあればご教示願います。 よろしくお願いいたします。
退会済みユーザー

退会済みユーザー

2020/04/30 06:47

PIC16F1503は触った事が無いですが、時間計測に使えそうなカウンター値みたいなものは存在しないのでしょうか?そういったものがあれば、カウンター値の押した直後の値と現在の差分で時間計測した方がよさそうな気がします。
SHOMI

2020/05/09 08:10

他の回答には5/4以降コメントがついているけれど、これは見てもらえてないのかな…?
GM-DS

2020/05/12 00:21

SHOMI様 おはようございます。 回答が遅くなり申し訳ございません。 また、ご不快な思いをさせてしまって申し訳ございません。 一度にたくさんの内容を理解するのができず、1つ自分なりに理解してからほかのコードも理解しようとしていました。また、実機に入れる事ができるのが昨日からでした。 全て言い訳になってしまいますが申し訳ございませんでした。
GM-DS

2020/05/12 04:30

SHOUMI様 ご教示頂きましたコードで何点か質問が有ります。 ①LATC0=led=1-ledについてです。 上段でint led=0;と宣言しています、LATC0=0=1-0になるのでしょうか? 「0=1-0」は偽になりLATC0は0になるのでしょうか? 10000カウントする間に250回のループを抜ける毎に処理をすると解釈してしまっています。 どのような形で点滅をするのでしょうか? ②ra4 = RA4;についてです。 この命令はどのような役割をするのでしょうか? お忙しい中、大変申し訳ございませんがご教示願います。
SHOMI

2020/05/13 02:47 編集

① よく見てください。LATC0=led==1-led;ではなくLATC0=led=1-led;です。 led=1-led; LATC0=led; と書いたのと同じで、0と1を交互に繰り返すようにしているだけです。 ② 一時変数ra4 にRA4の値を読み取っておくことで、 forで判定した時と同じ値をその外側のwhileでも使用させています。 forを抜けてからwhileの条件式までの間にRA4の値が変化した場合に止まらない事へ対策です。
GM-DS

2020/05/13 02:33

SHOMI様 おはようございます。 回答ありがとうございます。 ご教示頂きましたコードで試した所、10秒カウントは正常でした。 しっかり点滅もしていました。 そこで大変申し訳ありませんが①の点滅コードが理解できません。 これは前置演算と同様な考え方でしょうか? led=1-led;→ledは1 LATC0=led;→LATC0は1 と言う事でしょうか? そうなると、最初LATC0が1になり点灯し、次に250ms後に0になる原理がわかりません。 本来、初歩的な事なのでしょうが、わかりません。 申し訳ございませんがご教示願います。
SHOMI

2020/05/13 02:45

250ms後はledが1になっているので led=1-led;→ledは0 LATC0=led;→LATC0は0 です。
GM-DS

2020/05/13 04:16

SHOMI様 大変申し訳ございません。 そうです。おっしゃる通りです。考えが及びませんでした。
guest

0

PICには手を出していないので実験は出来ませんが...

各命令の実行に時間がかかるのは事実ですが、しかし

C

1 while (cnt < 10000 && RA4 == 0) { //カウント値が30秒以下、PB2 OFFの時、下を実行 2 LATC2 = 1; // BZ ON 3 if (cnt % 500 < 250) 4 LATC0 = 1; // LED1 ON 5 else LATC0 = 0; // LED1 OFF 6 __delay_ms(1); 7 cnt++; 8 }

で、__delay_ms()以外の部分の実行に0.7ms掛かっている、というのは単純に「ああそうですか」と受け入れていいレベルを超えていると思います。
点滅の周期も1.7秒くらいなのですか? 同じことですが、17秒の間に10回点滅しているのですか?

・点滅コードが原因かと思い、LED1の点滅をやめて10間点灯に変更すると正常に10後LED2が点滅した。

このときのコードはどのようなものだったのでしょう。

コンパイラの性能がすごく悪くて、割り算がものすごく遅い、とかだと可能性はあるかしら。
if (cnt % 500 < 250)
if( (cnt & 0x100)!=0 )
とかしたら(点滅周期は12msほどずれるけど)どうなるでしょうか。

なお、この部分だけでも元の質問ではLATC2 = 1;のセミコロンがいわゆる全角だったり、cnt1なんていう変数はなかったりします。コンパイルを通るわけがないので、これはあなたが実際にコンパイルして実行しているプログラムではあり得ません。あなたが実際にコンパイルして実行しているプログラム「そのもの」を提示して下さい。そうでないと、実は間違っているのを手で打ち直しているうちに「直して」しまっていて、いくら質問のプログラムを見ても原因が見つからない、なんていう可能性を否定出来なくなってしまいます。

もう一つ気になるのですが、スイッチの接続はどのような回路になっていますか? スイッチの一方をVDD(電源), もう一方をマイコンの入力ピンに繋いだだけ...なんていう回路になっていないでしょうか。(初心者と称する人が「スイッチONで1」としているとしばしばその回路になっているので...)
その場合、スイッチがOFFの場合マイコンの入力ピンは何も繋がっていない状態になりますがそれは「0(Lowレベル)」ではありません。たまたま0になるかも知れませんが、ノイズやちょっとした回路の都合で簡単に1になるかも知れません。不安定...問題になっている症状の1つじゃありませんでしたっけ。
マイコンの入力端子に何も繋がないという使い方は原則として「やってはいけない」と思って下さい。プルアップ/プルダウン回路を内蔵していてそれを使っている、とかいうのなら問題はないのですが、そうでなければマイコン外部にプルアップやプルダウン抵抗を接続して、マイコンの入力ピンの電位が確実に決まっているようにする必要があります。

コメントに関して追記 5/1

●スイッチの配線について

スイッチの配線は色々なサイトを見て勉強し、一応プルダウン?抵抗を接続しGNDに接続しています。

不安定という事象に対しては一番疑われる部分です。配線の間違い、部品の間違い(抵抗値等)、、接触不良、近隣とのショート、極端に長い配線等もありませんね? 「一応」ではなく「大丈夫」といい切れるようにして下さい。

●コンパイラの性能がすごく悪くて、割り算がものすごく遅い、とかだと可能性はあるかしら。

⇒そのような事があるんですか?想像がつきませんでした。

小学校で計算を習う順番を考えてみて下さい。足し算、引き算、掛け算、割り算の順で習うでしょう。その順で「難しい」のです。コンピュータの場合も、特別な計算回路を持っていないマイコンではその順番で計算が面倒になり、時間がかかることは考えられます。

volatile int i,j,result=0;//volatileはどこかで値が変更されているかも知れない、というコンパイラへの指示。これをつけないと計算を端折ることがあるので //ここでLED ON for(i=0;i<10000;i++){//ループの回数は違いが判断できて結果が出るまで我慢できるくらいの時間に適当に設定 for(j=0;j<100;j++) result=i+j; //ここを result=i-j;とかresult=i*jとかresult=i/jとかresult=i%jに替えてみる } } //ここでLED OFFしてLEDが点灯していた時間を計る while(1);//停止

みたいな実験をして調べてみるといいかも。
手元のArduino UNO(クロック16MHz)では+,-が約2秒 *が2.5秒に対し/と%は15.7秒かかりました。
PICで質問のプログラムの条件に置き換えると
・クロック2MHzで速度1/8(かかる時間は8倍)
・繰り返し回数10000で1/100
・CPUの作りも違ってAVRは1クロック1命令、PICは4クロック1命令が基本で4倍時間がかかる
とすると単純に換算して(相当いい加減な見積もりですが)5秒くらい。つまり10秒ループのはずが15秒とかの実行時間はあり得る、ということになりそうです。(意外と大きいなぁ)

だとすると、

●実験したコードは単純に

if(cnt%500<250)
LATC0=0;
else LATC0=1;
を抜いただけです。

これについては、そこを抜くのではなく

C

1if(cnt%500<250) 2LATC0=1; 3else LATC0=1;

による点きっぱなしも試してみた方がよさそうですね。「点滅」の影響なのか、割り算が遅いのか。

それはそれとして

●17秒の間に10回点滅しているのですか?

⇒10回以上点滅していました。

これは重要な事実です。点滅周期の情報がありませんが、点滅が10回を超えるということがあるなら、単純にループに時間がかかっているのとは別の現象ですから。

投稿2020/04/30 13:05

編集2020/05/01 02:05
thkana

総合スコア7610

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

GM-DS

2020/04/30 23:48

thkana様 お忙しい中、返信ありがとうございます。 また、codeの載せ方のご教示ありがとうございました。 ●17秒の間に10回点滅しているのですか? ⇒10回以上点滅していました。 ●実験したコードは単純に if(cnt%500<250) LATC0=0; else LATC0=1; を抜いただけです。 ●コンパイラの性能がすごく悪くて、割り算がものすごく遅い、とかだと可能性はあるかしら。 ⇒そのような事があるんですか?想像がつきませんでした。 ●コード入力間違いについて おっしゃる通り間違えていました。申し訳ございません。 ●スイッチの配線について スイッチの配線は色々なサイトを見て勉強し、一応プルダウン?抵抗を接続しGNDに接続しています。 初心者と言う事を言い訳にして大変申し訳ございませんが、本当にわからず独学で勉強しています。せっかく頂いた回答が理解できず失礼な返答をしてしまうかもしれませんがよろしくお願いいたします。
ikadzuchi

2020/05/01 00:47

私は除算に0.7msは「ああそうですか」なレベルですね。 PICに手を出されていないということでご存知ないかもしれませんが、PICは1命令に4クロック掛かりますので、0.7msは2MHzで350命令。 8bit CPUで16bit int同士の除算をしていればそんなものかなと思います。 折角なので暇を見て試してみようか…。
thkana

2020/05/01 02:06

> お忙しい中 いや...このご時勢でstay homeな(特にとりたくもない)GW休暇中なので。 > 本当にわからず Q&Aの掲示板なんで、質問者がわからないこと、知らないことがあるのは当然です。ただ、一応「エンジニア」向けということのようなので、問題解決への考え方とかは論理的によろしく。それと、コンピュータにいくら「初心者だから」と言っても優しく言うことを聞いてくれたりはしないので、やることはきっちりやるしかありません。 とりあえず回答に追記しました。
thkana

2020/05/01 02:25 編集

ikadzuchiさん PICは持ってないのでArduinoで試してみました。回答に書きましたが、0.7msも「あり得る」ラインですね。 (最近ARMばっかり使っていたので感覚が鈍っていたようです。そもそも割り算なんて本能的に避けていたし)
ikadzuchi

2020/05/01 05:06

試してみました。 PIC16F1503、コンパイラがXC8(v1.44)で、「if (cnt % 500 < 250)」の実行は270サイクル程度(=1100クロック程度)掛かりました。(cntの数値にはあまり影響されない) 350サイクルとはだいぶ差があるのがちょっと不可解で、他に何か問題が残っていそう(点滅が10回を超えるあたり怪しい)ではありますが、とりあえずこの程度は掛かるものだということで。
GM-DS

2020/05/04 23:36

thkana様 お忙しい中、回答ありがとうございます。 また、返信が遅くなり大変申し訳ございません。 実験については是非やります。 現在、それができる環境ではないので、来週の月曜日に行います。 それについての報告をさせていただきます。 ありがとうございます。 また、報告させてください。
SHOMI

2020/05/09 08:07 編集

>●17秒の間に10回点滅しているのですか? >⇒10回以上点滅していました。 250ループ毎にON/OFF切り替えなので20回までは問題ないのでは?
thkana

2020/05/09 09:23

確かに...10秒カウントが頭の中で10回点滅に置き換わってしまっていたようです。放置しておいて規定回数点滅しているのか、ということを言いたかったのですが、混乱させてしまったでしょうか。失礼しました。
GM-DS

2020/05/11 04:57

thkana様 <if( (cnt & 0x100)!=0 )を試してみました。 周期は約11秒になりました。 これについて独自で調べましたが、2点質問が有ります。 ①これは左側が0以外の時、LEDは点灯と言う意味になるかと思います。ビットANDで0x100(1 0000 0000)だと255(1111 1111)までは0と言う事でしょうか? ②点滅周期が12msほどずれるというのはどれをどのように考えれば算出されるのでしょうか? <volatile int のコード実験はまだ理解ができていないのでやっていませんが、よく耳にする繰り返し回数とはどのような事なのでしょうか?繰り返し行っていると、何かか変化してしまう事なのでしょうか? <付きっぱなしの実験は約16秒かかりました。 これは割り算が遅いと考えてよいものなのでしょうか? 質問ばかりで大変申し訳ございません。
GM-DS

2020/05/11 05:04

ikadzuchi様 回答ありがとうございます。 <PICに手を出されていないということでご存知ないかもしれませんが、PICは1命令に4クロック掛かりますので、0.7msは2MHzで350命令。 とありますが、おっしゃる通りわかりませんでした。独自で調べましたら4クロックの意味は分かりましが「0.7msは2MHzで350命令」と言うのはどのような計算なのでしょうか?350と言うのは1サイクルで350命令処理しているという事でしょうか? 質問ばかりで大変申し訳ございません。
thkana

2020/05/11 11:47

「350命令」は私じゃありませんが、 2MHzクロック(2000000クロック/秒)、1命令に4クロックが費やされるなら、0.7msの間には350個の命令が実行できる/350命令が必要な動作には0.7msかかる、ということです。
GM-DS

2020/05/11 23:55

thkana様 おはようございます。 回答ありがとうございます。 350命令の計算わかりました。 ありがとうございました。
guest

0

ベストアンサー

誰も突っ込まないようなので

●スイッチの配線について
スイッチの配線は色々なサイトを見て勉強し、一応プルダウン?抵抗を接続しGNDに接続しています。

プルダウン(マイコンピンとGNDを抵抗で繋ぐ)すると、その電圧はLOWレベル(GND)側に引っ張りますから、LOWと判断されます。
スイッチの先は、押した時にそれとは識別できるようにしなければ意味がありませんから、VCC(5V)に繋ぎます。
※と言っても、ある程度動いているようですから、実際は正しく配線してあるのだと想像します。

①PB1を押した後の、10秒カウントが17秒位ある。

作業時間が蓄積されて誤差の7秒となっています。
8bitのPICはかなり非力なマイコンです。
現在流通されているマイコンの中でも最弱だと思った方が良いです。
(乗算器が無い&4クロックで1命令を行う)
2MHz駆動にすると、実質(他の多くのマイコンに換算すると)500kHzで駆動している事になります。
なので、内部クロックはもっと高くした方が良いです。例えば2MHz→16MHzに変更しただけで、誤差の7秒が
1秒程度になる事が期待されます。

そして、点滅のループもやはり、「このマイコンは処理能力が低い」という事を意識して書くべきです。
つまり、10000回もループさせたら誤差が大きくなります。
例えば10msを単位として、1000回のループにすれば、誤差は1/10程度になると期待されます。

上記の2つの事を考慮して書けば、7秒の誤差が1/80、つまり、0.1秒程度になるので、全く気になる事が無い範囲になります。

②LED2の点滅が不安定で点滅周期が長かったり、短かったりする。

また、PB2を押さない限りLED2はずっと点滅するはずが、途中から点灯に代わってしまう。

プログラムでは、if(cnt%1000<500)ですけど、変数cntはintです。
その場合、変数範囲の半分はマイナス値です。そうなると余り(%)もマイナス値になります。
よって、cnt++で、マイナス値に切り替わったあたりから暫く(約33秒)は(cnt%1000<500)は真になります。
cntをunsigned intで宣言すれば直ると思います。(とはいえ、数値が一周した瞬間だけ、ちょっと乱れます。)

③そもそもこのコードは最適なのか?

想定したように動いていない時点で・・・
細かい事は、他の方の指摘とダブりそうなので、控えます。
(一番最初なら一通り指摘したと思いますけど)

その代わり、一通り書き換えておいたものを置いておきます。
コンパイルまでは試しましたけど、レジスタを確認した訳でもないし、検証した訳でもありません。

c

1//接続を定義しておくと解り易い 2#define LED1 LATC0 3#define LED2 LATC1 4#define BZ LATC2 5#define PB1 RA5 6#define PB2 RA4 7 8#define _XTAL_FREQ 16000000 9#include <xc.h> 10 11void main(void) { 12 OSCCON = 0b01111000; //内部クロック周波数を16MHzに設定 13 ANSELA = 0b00000000; //PortA全てのピンをデジタルモードに設定 14 ANSELC = 0b00000000; //PortC全てのピンをデジタルモードに設定 15 TRISA = 0b11111111; //PortA全てのピンを入力モードに設定 16 TRISC = 0b00000000; //PortC全てのピンを出力モードに設定 17 18 //初期処理:電源ON後、全ての出力を一度OFFにする。 19 LED1 = 0; 20 LED2 = 0; 21 BZ = 0; 22 23 int cnt = 0; //カウンタ 24 int mode = 0;//modeは0:全停止 1:LED1点滅、BZオン 2:LED2点滅 25 26 while (1) { 27 //入力(sw)処理 28 if (PB1 && mode == 0) { mode = 1;cnt = 0;} 29 //modeが0の状態でPB1押されたら、modeを1にしてカウンタも初期化 30 if (PB2) {mode = 0;} 31 //PB2が押されたら、どんな状態からでもmodeを0 32 33 34 //出力処理 35 if (mode == 0) {//mode0は全オフ 36 LED1 = 0; 37 LED2 = 0; 38 BZ = 0; 39 } else if (mode == 1) {//mode1はLED1点滅、BZオン 40 LED1 = (cnt % 50 < 25); 41 LED2 = 0; 42 BZ = 1; 43 __delay_ms(10); 44 if (++cnt == 1000){mode = 2;cnt=0;}//10秒になったらmodeを2に 45 } else if (mode == 2) {//mode2はLED2のみ点滅 46 LED1 = 0; 47 LED2 = (cnt < 50); 48 BZ = 0; 49 __delay_ms(10); 50 if(++cnt==100)cnt=0; 51 } 52 } 53} 54

追記
よく見たら、②は、とっくにSHOMIさんが指摘していましたね。
それなら、後からでしゃばる必要も無かったなぁ・・・

投稿2020/05/03 02:30

編集2020/05/06 09:33
nac_tnk

総合スコア463

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nac_tnk

2020/05/03 02:59

ゲッ! これって、修正するとポイント下がっちゃうの? 多分、13→11になっちゃった(>_<) ポイントマイナスになるようだと書き込み(修正)出来ないの? 親切な方教えてください(._.)
thkana

2020/05/03 05:51

Scoreであれば別に修正では減ることはないです。減るのは低評価とか高評価の取り消し、フォローの取り消し、かな。プラットフォームページのscoreの項で確認してみては。( https://teratail.com/users/nac_tnk#score ) でも、nac_tnkさんのscoreは現在22だけど...13とか11とかってのは何の数字でしょう?
nac_tnk

2020/05/03 08:14

あつ、先生すみません。 確かに、score 22となっていますね。 上部のバーの右側の名前の横に目立つように数字(赤□に白抜き文字)がありますけど、これがライフポイントだと思っていました。 で、書き込んだ時に13に増えて、修正した直後に11になりました。 そして今・・・ これなんだろう?とクリックしてたら0?になって消えてしまいました。 どうやら、未読の類だったようです。 お騒がせしました(._.)
thkana

2020/05/24 01:57 編集

先生? いや、そんなたいしたもんじゃ... 名前のところは、スコアとかバッジその他ステータスの増分値の未確認分みたいです。クリックしてみればだいたい想像つくと思いますけれど。詳細内容を閲覧すれば消えます。増減詳細はそちらで確認できます。
nac_tnk

2020/05/04 14:22

チョコチョコ触ってみましたけど、良くできたシステムですね。 どうでも良い機能も多いようですけど。 ※(たまーに)先生を付けて呼ぶのは今の所、一人だけです。 (回答を読んでいればバレバレです)
GM-DS

2020/05/04 23:25

nac_tnk様 お忙しい中ご回答ありがとうございます。 また、返信が遅くなり申し訳ございませんでした。 ①について 内部クロックについては自分で調べてその値にしました。 理由は消費電流が増えると書いてあったので特にこだわりがなく、何も考えず調べたコードより少し早くしてしまいました。 内部クロックというのは早くすることによって消費電流以外にデメリットはないのでしょうか? デメリットがなければ単純に最速が一番いいと思ってしまいます。 ②について int, unsigned int調べました。恥ずかしながら知りませんでした。 調べたコードを少しいじって動いてしまったので・・・ 基本的なことをさぼってしまっていました。 ③について おっしゃる通りです。 想像通り動いていない時点でダメですね。 defineについて知りませんでした。最初に定義すれば後がわかりやすくなります。今後はこれを活用したいと思います。 またコードについてです。コードありがとうございます。 nac_tnk様にとっては簡単かと思われますが、私にとっては初めてみる考え方です。 しっかりと意味を理解したいので少しお時間をください。 意味を理解してから、もう一度質問がしたいと思っております。 ありがとうございました。 もう一度、質問をさせていただきます。
nac_tnk

2020/05/05 03:14

マイコンは通常、一番速い速度で使います。 でも、確かに速度により、消費電流(電力)に違いが出ます。 どの位の速度なら、このスケッチで誤差を感じないのか?というのも考えた方が良いです。 また、速い速度で計算させて、あとはスリープさせる、という考え方もあります。 そして、より正確に行いたい時にはタイマーを利用します。 (koujikuuさんのプログラムがその一例です。ただし、ちょっとだけ難しくなります。) 他人のコードは読みにくいものです。↑は解り易いように書いたつもりですけど、そう感じないかもしれませんね。 GM-DSさんのコードをちゃんと動くように?主要部を書き換えると以下です。 2MHz駆動でも殆ど誤差は感じないと思います。 if (RA5 == 1) { int cnt = 0; LATC2 = 1; while (cnt < 1000 && RA4 == 0) { LATC0 = (cnt%50 < 25); __delay_ms(10); cnt++; } LATC0 = 0; LATC2 = 0; cnt = 0; while (RA4 == 0) { LATC1 = (cnt < 50); __delay_ms(10); if (++cnt == 100)cnt = 0; } LATC0 = 0; LATC1 = 0; LATC2 = 0; }
thkana

2020/05/05 03:51

ちょっと横から口を出しますけど、 > 理由は消費電流が増えると書いてあったので PICのデータシート http://ww1.microchip.com/downloads/en/DeviceDoc/40001607D.pdf を見ると、消費電流は内蔵発振器500KHzで620μA, 8MHzで1200μA, 16MHzで1700μA(@5V, Max値)となっています。2MHzだと、800μAというところでしょうか。8MHzに比べて約1mA消費電流が減りました。電池駆動の機器だと、電池の持ちに「大きな」影響が出かねない値ではありますけれど...どういう使い方をしていますか?(ちなみに、USBの電源供給能力はUSB2.0までは500mAだったかと) 本当に消費電流を気にするのなら、それこそタイマーを仕掛けてマイコンはスリープにするべきところです。また、LEDに流す電流にも同じくらい気を配っていますか? 言ってしまうと、「気を使うところを間違っている」と思います。
GM-DS

2020/05/05 22:20

nac_tnk様 おはようございます。 ご回答ありがとうございます。 nac_tnk様に教えていただいたコードを自分なりに理解してみました。 私が言うのもなんですが、確かにとてもわかりやすかったです。 そこで何点か質問があります。 ①基本的にはあのコードの様に入力処理、出力処理と分けるものなのでしょうか?私が調べたサイトだと入力処理すぐ下に出力処理があったのでそういうもんだと思っていました。 ②例えばmode2の出力処理をしている時に上の段にあるmode0にする入力処理は処理されるのでしょうか?できると思いますが、入力処理のifやwhileに出力と考えていたのでこのような考え方はなかったです。 ③点滅処理について わざわざelse if で消灯しなくても比較の結果によって真か偽になると言う事でしょうか? 以上、よろしくお願いいたします。
GM-DS

2020/05/05 22:54

thkana様 おはようございます。 ご回答ありがとうございました。 おっしゃる通りです。 他に気にするところがあります。無駄にLEDに電流を流しています。 もっと暗くて大丈夫です。 駆動は電池を使用しています。 消費電流と言っている割にしっかりデータシートを見ていませんでした。 16MHzで1700μAデータシートに書いてありました。LEDの電流を絞る方が良いです。
nac_tnk

2020/05/06 04:14

①私の場合は、一人でやっているので基本的にはテキトウです。  仕事(複数人)でやっているのなら、仕様があるでしょうけど。  今回程度の物(と言っても何を作っているのか不明ですけど)なら、何となく書くだけです。  添付画像は今作っているものですけど、タクトスイッチ8個、セレクトスイッチはそれぞれADC入力です。  そして、押釦は単独でピンに繋いでいます。これと、LCD、SSR(エアシリンダやMC用)7個、ステッパー2台、センサー入力5  位まで繋げられえる仕様です。そうなってくると、どこで(タイマ割り込みやADC割り込みやメインループ等)何を処理するかは  それなりに考えてから始めます。(と言っても、決まりがある訳では無いです)   ②出力処理の場所ではif文だけです。forやwhileなどならそこで留まりますから、入力処理の↑には行きませんけど、  約10msで、while(1)以下をループさせているので大丈夫です。  多分、GM-DSさんの次の段階はタイマー割り込みでの処理になると思います。 ③LED2 = (cnt < 50);あたりの書き方の事でしょうか?  右辺はC言語では真の場合は「0以外」が返るのでしょうけど、実際には1です。  (このあたりは先生に怒られてしまうかも。ただし、問題無く動きます)  私は面倒くさがりなので、簡単に書ける方を好みますけど、実際には他の条件も入れたい場合も多いです。  今回の場合はそのLEDの操作だけをしている意味合いも込めて、単純に1行で済むようにしています。  ※実際にはどんな書き方でも良いです。自分なりに自信のある書き方で書いてください。 基本的に、プログラミングは何でもありだと思っています。 __delay_ms(1);を__delay_us(300);で時間を合わせても良いでしょうし、或いは駆動クロックをさらに下げるのも可能だと思います。 ただし、動作(プログラムの動き)は理解している=プログラマがコントロールしている必要はあると思います。 ※後で横にある物の写真撮って、付けようと思って書いたんだけど、コメント欄に画像を付けられない??のに気づきました(*'▽')
thkana

2020/05/06 05:38

Cの規格を紐解けば、比較の演算子は条件が成立するときint型の1を、そうでなければ0を返す、と明記されています。安心して1/0として扱って下さい。 例えば、aが1, bが2, cが3であれば (a!=b)+(b<c)+(a&&c) という式は3になります。 ついでに。 > ①基本的にはあのコードの様に入力処理、出力処理と分けるものなのでしょうか? このくらいの簡単なプログラムならいいですけど、ちょっと複雑な(というか普通の)装置になってくると、あるLEDの点灯状態は本体スイッチとリモコンによる操作と機能Aの動作状態によって決まる...なんてことになってきます。 そうなると、ある入力を調べたらその場で出力しちゃえ...というやり方は簡単に破綻します。「入力」して諸々「処理」した結果を一点集中で「出力」、という処理手順になるのが自然でしょう。
nac_tnk

2020/05/06 06:49

int型の1ですか。覚えておきます。(._.)
GM-DS

2020/05/06 22:49

nac_tnk様 おはようございます。 回答ありがとうございました。 私もゆくゆくはアナログ入力をしてみたいと思っております。 まだまだですが・・・ ②について 例えば、一番最後のLED2の点滅です。上段で10秒経過し、mode2が2になりelse if内の命令をぐるぐる回ると思います。この時、ぐるぐる回って処理中なのにif(PB2){mode=0;}は受け付けてもらえるものなのでしょうか?私の今までの考え方だと、きっとelse if(++cnt==1000 && PB2==0)と作っていたと思います。 nac_tnk様が作っているものを参考までに是非みたいです。 そこまで行くのは全然まだまだとはわかっています・・・
GM-DS

2020/05/07 00:01

thkana様 おはようございます。 回答ありがとうございます。 (a!=b)+(b<c)+(a&&c)の(a&&c)はaが1で真、cが3で真、aとcが共に真だから真=1ということでしょうか? >「入力」して諸々「処理」した結果を一点集中で「出力」、という処理手順になるのが自然でしょう。 おっしゃる通りです。確かにこちらの方が自然な流れで見やすいです。まだ、初期段階ですがこのような癖をつけていきたいと思いました。
nac_tnk

2020/05/07 04:46 編集

> mode2が2になりelse if内の命令をぐるぐる回る いいえ、else ifなので、そこに入って通り抜けます。ぐるぐる回りません。 whileやforは何度も行う(一度も行わない場合もあります)時に使いますけど、if elseは多くても1回切りです。 while(1)のループをぐるぐる回ります。 そこには入力処理部と出力処理部が入っています。 > 私もゆくゆくはアナログ入力を あぁ、私の製作物の事でしたか。タイマー割り込みの事を言ったのに???と思ってしまいました。 どちらも、直ぐにやる事になると思います。 コントローラ部なので、単にタカチのケースに操作部が付いているだけですよ。 それにまだ何も書いていません。SW類が並んでいるだけです。 (別にマグネットスイッチとSSR+電磁弁がある大きなBOXやステッパードライバ基板に繋ぎます。) http://get.secret.jp/pt/file/1588818802.jpg (中はPICでは無く、STM32です。)
GM-DS

2020/05/07 12:19

nak_tnk様 こんばんは。回答ありがとうございます。 else ifもぐるぐる回ると思っていました。 すみません。言葉足らずでした。最終目標がアナログでタイマー割り込みもそこに含んでいるつもりでした・・・ これは個人で作っているのですか?
nac_tnk

2020/05/07 13:19

仕事です。けど、作っているのは私一人です。 基本的に、目立たないようにコソコソ作っています。そういった部署はありません。 電気系を担っていた人が退職されて、機械が動かなくなると私が頼られるようになって・・・ 後は、相談する相手もいないので、マイコン(電子系)も勝手に始めました。 なので、この類は私の本職ではありません。でも、年々、割合が増えてますね。
GM-DS

2020/05/07 22:07

nac_tnk様 おはようございます。 目立たないようコソコソって面白いですね^^ しかも独学ですか!?すごすぎです。 月曜日か火曜日には実験ができると思います! ここでベストアンサーに決めてしまうとこのスレは閉じてしまうのでしょうか? できた時の報告をしたいと思っています。
nac_tnk

2020/05/07 22:46

GM-DSさんも、当然気付いているでしょうけど、私はここのシステムは詳しくないですけど・・・ ・ベストアンサーはかなり長い期間、付けなくても大丈夫 ・ベストアンサーを付けた後でもさらに回答やコメントは付けられる となっていると思います。
thkana

2020/05/07 23:22

補足するなら、ベストアンサーの取り消し、他の回答への付け替えも可能 質問/回答の編集も無期限で可能(質問/回答本体は編集履歴が残る。コメント部分は編集済のフラグだけで履歴は残らない) といったあたりは知っておいてもいいかも。
GM-DS

2020/05/11 02:05

nac_tnk様 thkana様 おはようございます。 回答ありがとうございます。 ただ今、皆様にご教示頂いた内容を実機で確認しております。
GM-DS

2020/05/11 05:29

nac_tnk様 内部クロックを2MHzから16MHzに変更して実験をしました。 結果は約10~11秒になりました。 これについては2MHzだと剰余計算に1サイクル0.7msかかるという事でしょうか? また、10000回もループさせたら誤差が大きくなるというのは、1サイクル辺り0.7msが10000回=7秒と言う考え方でよろしいんでしょうか? cnt を unsigned intにすることにより、点灯しっぱなしは改善されました。 お手数おかけいたしました。
nac_tnk

2020/05/11 09:09

10秒の予定が17秒かかったというのは実際に処理している時間(delay以外の時間)が7秒かかっています。 10000回のループですから、1回あたり7/10000=0.7msですけど、それはwhile処理の時間、cnt<1000の時間、RA4==0等の計算も含めたものです。 「%」の計算も含めてwhileの処理全てです。おおよそ、書いた事は全て時間がかかります。 > 1サイクル辺り0.7msが10000回=7秒と言う考え方でよろしいんでしょうか? その考えでOKです。while処理、whileの条件、while内のdelay以外の処理で、それだけ掛かっています。 そして、delay関数を基準に時間を測るプログラムだと、空いているプログラムでなければより誤差が出ます。 駆動クロックに準じた速度(時間)にするためには、内部タイマーを基準にプログラミングする必要があります。
GM-DS

2020/05/12 00:08

nac_tnk様 おはようございます。 回答ありがとうございます。 意味を理解することができました。 また、ご教示頂いたコードを試してみました。 正常に動きました。 そこで、内部クロックやdelayの桁を変えたりしてみた所、16MHzの10msが最適値でした。 内部タイマを基準にするプログラムはkoujikuu様にご教示いただいたコードの事ですよね。 直ぐに理解はできなさそうなので、自分なりにしっかり読んでから改めて質問させて下さい。
nac_tnk

2020/05/12 03:24

内蔵タイマを使う手法も、大きく見て2つに分かれます。 1つはタイマを回し、横目で見て、処理をする方法、もう一つはタイマ割り込みをかけて、一定周期で起こる割り込み関数で入った回数で時間測ります。 (koujikuuさんのプログラムは前者です。タイマ割り込みを使う方がより一般的です。多分、割り込み関数では元のプログラムから大きく変わるのでこの例を示したのだと推測します。) 特に、タイマー割り込みは必須だと思うので、トライしてみてください。
GM-DS

2020/05/12 04:14

nac_tnk様 割り込みですね・・・ まだまだ知識が足らずたどり着くまでに時間がかかりそうですがトライしたいと思います。 お忙しい中、長々とお付き合い頂きましてありがとうございました。 またの機会が有りましたら是非よろしくお願いいたします。
guest

0

__delay_ms() ではなく 内蔵タイマを基準とします
PIC16F84 でテスト PIC16F1503 でも基本的な考え方は同じです (除算は使いません)
SW , LED , BZ の ON/OFF (o,1) は #define で設定しています

#include <xc.h> #pragma config WDTE=OFF,PWRTE=OFF,CP=OFF,FOSC=HS #define _XTAL_FREQ 10000000 // 10MHz #define FOSC4 ( _XTAL_FREQ / 4 ) // PIC CLOCK #define _500ms ( FOSC4 / 2 ) #define _10s ( FOSC4 * 10 ) #define LED1 RA2 #define LED2 RA3 #define LED_ON 1 #define LED_OFF 0 #define SW1 RB0 #define SW2 RB1 #define SW_ON 0 #define BZ RA1 #define BZ_ON 0 #define BZ_OFF 1 unsigned short long tmr0_cnt3 = 0 ; // 24bit unsigned long tmr0_cnt4 = 0 ; // 32bit unsigned char blink_flag = 0 ; void main(void){ TRISA = 0 ; // OUTPUT TRISB = 0xFF; // INPUT OPTION_REG = 0b01010111; // TMR0_ON Prescaler 1:256 , PORTB pullup LED1=LED_OFF ; LED2=LED_OFF ; BZ=BZ_OFF ; while(1){ if ( SW1 == SW_ON && blink_flag==0 ){ blink_flag=1 ; tmr0_cnt3=0 ; tmr0_cnt4=0 ; } if ( SW2 == SW_ON ){ LED1=LED_OFF ; LED2=LED_OFF ; BZ=BZ_OFF ; blink_flag=0 ; } if ( T0IF==1 && blink_flag ==1 ){ // tmr0 over flow T0IF=0; tmr0_cnt3 += 65536; if ( tmr0_cnt4 < _10s ){ tmr0_cnt4 += 65536; } if ( tmr0_cnt3 >= _500ms ){ tmr0_cnt3 -= _500ms; if ( tmr0_cnt4 < _10s ){ LED1 ^= 1 ; LED2=LED_OFF ; BZ=BZ_ON ; } else { LED2 ^= 1 ; LED1=LED_OFF ; BZ=BZ_OFF ; } } } } // while }

投稿2020/05/02 04:11

koujikuu

総合スコア401

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

GM-DS

2020/05/04 23:28

koujikuu様 お忙しい中、ご回答ありがとうございました。 また、返信遅くなり申し訳ございませんでした。 コードありがとうございます。 すぐにコードを理解できません。 私なりにしっかりと理解をしたいと思っております。 理解するまで少しお時間をください。 理解をしてから再度質問をさせていただきたいと思っております。 ありがとうございました。また質問させていただきます。
guest

0

そもそも、時間のカウントに__delay_msを使ってますが、たとえこの関数で1msきっちりのディレイが得られたとしても、その他の命令の実行時間がそれに加わりますんで、10000回まわすと10秒きっちり、というわけにはいかなくなります

そもそもディレイとかスリープ系の関数というのは、なにもしないでひたすらその時間がすぎるのを待ってるだけ、です。
ですんで、それを前提にコードの動作を追いかけてみてはどうでしょう

投稿2020/04/30 01:05

y_waiwai

総合スコア87719

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

GM-DS

2020/04/30 06:20

y_waiwai様 お忙しい中、回答ありがとうございます。 カウントの計算をしてdelayで1ms待ってカウントしてとやっていると、カウントは10000しているが点滅用カウントの計算をしているが為に、正確な10秒が得られないと言う事でしょうか?
y_waiwai

2020/04/30 09:38

for のループを実行する時間、変数をカウントする実行時間、何らかの命令を実行する場合には必ず実行する時間が経過しますね
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問