回答編集履歴

2 追記

nac_tnk

nac_tnk score 303

2020/05/06 18:32  投稿

誰も突っ込まないようなので
> ●スイッチの配線について
> スイッチの配線は色々なサイトを見て勉強し、一応プルダウン?抵抗を接続し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
//接続を定義しておくと解り易い
#define   LED1     LATC0
#define   LED2     LATC1
#define   BZ       LATC2
#define   PB1       RA5
#define   PB2       RA4
#define _XTAL_FREQ 16000000
#include <xc.h>
void main(void) {
   OSCCON = 0b01111000; //内部クロック周波数を16MHzに設定
   ANSELA = 0b00000000; //PortA全てのピンをデジタルモードに設定
   ANSELC = 0b00000000; //PortC全てのピンをデジタルモードに設定
   TRISA = 0b11111111; //PortA全てのピンを入力モードに設定
   TRISC = 0b00000000; //PortC全てのピンを出力モードに設定
   //初期処理:電源ON後、全ての出力を一度OFFにする。   
   LED1 = 0;
   LED2 = 0;
   BZ = 0;
   int cnt = 0; //カウンタ
   int mode = 0;//modeは0:全停止 1:LED1点滅、BZオン 2:LED2点滅
   
   while (1) {
   //入力(sw)処理
       if (PB1 && mode == 0) {   mode = 1;cnt = 0;}
       //modeが0の状態でPB1押されたら、modeを1にしてカウンタも初期化
       if (PB2) {mode = 0;}
       //PB2が押されたら、どんな状態からでもmodeを0
       
       
   //出力処理   
       if (mode == 0) {//mode0は全オフ
           LED1 = 0;
           LED2 = 0;
           BZ = 0;
       } else if (mode == 1) {//mode1はLED1点滅、BZオン
           LED1 = (cnt % 50 < 25);
           LED2 = 0;
           BZ = 1;
           __delay_ms(10);
           if (++cnt == 1000){mode = 2;cnt=0;}//10秒になったらmodeを2に
       } else if (mode == 2) {//mode2はLED2のみ点滅
           LED1 = 0;
           LED2 = (cnt < 50);
           BZ = 0;
           __delay_ms(10);
           if(++cnt==100)cnt=0;
       }
   }
}
```
```
追記
よく見たら、②は、とっくにSHOMIさんが指摘していましたね。
それなら、後からでしゃばる必要も無かったなぁ・・・
1 些事(「真」と「偽」)を間違えていたため1文字修正

nac_tnk

nac_tnk score 303

2020/05/03 11:44  投稿

誰も突っ込まないようなので
> ●スイッチの配線について
> スイッチの配線は色々なサイトを見て勉強し、一応プルダウン?抵抗を接続し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++で、マイナス値に切り替わったあたりから暫く(約33秒)は(cnt%1000<500)はになります。
cntをunsigned intで宣言すれば直ると思います。(とはいえ、数値が一周した瞬間だけ、ちょっと乱れます。)
> ③そもそもこのコードは最適なのか?
想定したように動いていない時点で・・・
細かい事は、他の方の指摘とダブりそうなので、控えます。
(一番最初なら一通り指摘したと思いますけど)
その代わり、一通り書き換えておいたものを置いておきます。
コンパイルまでは試しましたけど、レジスタを確認した訳でもないし、検証した訳でもありません。
```c
//接続を定義しておくと解り易い
#define   LED1     LATC0
#define   LED2     LATC1
#define   BZ       LATC2
#define   PB1       RA5
#define   PB2       RA4
#define _XTAL_FREQ 16000000
#include <xc.h>
void main(void) {
   OSCCON = 0b01111000; //内部クロック周波数を16MHzに設定
   ANSELA = 0b00000000; //PortA全てのピンをデジタルモードに設定
   ANSELC = 0b00000000; //PortC全てのピンをデジタルモードに設定
   TRISA = 0b11111111; //PortA全てのピンを入力モードに設定
   TRISC = 0b00000000; //PortC全てのピンを出力モードに設定
   //初期処理:電源ON後、全ての出力を一度OFFにする。   
   LED1 = 0;
   LED2 = 0;
   BZ = 0;
   int cnt = 0; //カウンタ
   int mode = 0;//modeは0:全停止 1:LED1点滅、BZオン 2:LED2点滅
   
   while (1) {
   //入力(sw)処理
       if (PB1 && mode == 0) {   mode = 1;cnt = 0;}
       //modeが0の状態でPB1押されたら、modeを1にしてカウンタも初期化
       if (PB2) {mode = 0;}
       //PB2が押されたら、どんな状態からでもmodeを0
       
       
   //出力処理   
       if (mode == 0) {//mode0は全オフ
           LED1 = 0;
           LED2 = 0;
           BZ = 0;
       } else if (mode == 1) {//mode1はLED1点滅、BZオン
           LED1 = (cnt % 50 < 25);
           LED2 = 0;
           BZ = 1;
           __delay_ms(10);
           if (++cnt == 1000){mode = 2;cnt=0;}//10秒になったらmodeを2に
       } else if (mode == 2) {//mode2はLED2のみ点滅
           LED1 = 0;
           LED2 = (cnt < 50);
           BZ = 0;
           __delay_ms(10);
           if(++cnt==100)cnt=0;
       }
   }
}
```

思考するエンジニアのためのQ&Aサイト「teratail」について詳しく知る