48時間以内の記事は
で表示されます

ADRES
上限を超えたので新しく投稿しました。
>RB3を2*tmp[ms]間HIGH、→RB3を2*tmp[ms]間LOWにする
済みません。
もう少し分かり易く教えて下さい。お願いします。
return文 有難うございます。
ADRESは、変数宣言で対処しましいた。
//変数宣言
unsigned int Value;
Value=(ADRESH<<8)+ ADRESL;
__delay_us(20); //アクィジョン時間 20us
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
return (Value);
>RB3を2*tmp[ms]間HIGH、→RB3を2*tmp[ms]間LOWにする
済みません。
もう少し分かり易く教えて下さい。お願いします。
return文 有難うございます。
ADRESは、変数宣言で対処しましいた。
//変数宣言
unsigned int Value;
Value=(ADRESH<<8)+ ADRESL;
__delay_us(20); //アクィジョン時間 20us
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
return (Value);
Re: ADRES
「行おうとしている事はボリュームに対応した点滅」なのですから、通常は
ボリューム(ADC)値×補正倍率を点灯/消灯時間、にしようとしますよね?
ボリューム値が1000なら2秒、100なら0.2秒、となるようにするなら補正倍率を2にします。
なので、通常は2msのディレイを1000回、或いは100回行って時間を合わせます。
RB3=1;
for(i=0;i<tmp;i++)__delay_ms(2);
だと、__delay_ms(2);がtmp回行われます。
> for(i = 0; i<tmp; i++){RB3=1;}
だと、tmp回、RB3=1;が行われます。(この処理時間をTとします。)
そして次行の
__delay_ms(2);
です。
つまり、RB3が1の時間は「T+2ms」ということになります。
Tはボリューム値に比例しますけど、意味のない2msが付きます。
for(i = 0; i<tmp*50; i++)RB3=1;
for(i = 0; i<tmp*50; i++)RB3=0;
のように、適当な倍率回数行う方がボリュームの設定値と点滅間隔の動きが一致します。
けれども、1ループで何クロックかかるかは、初心者には判断付きにくいと思います。
※実際、元のプログラムでは点滅時間が早すぎませんでしたか?
> Value=(ADRESH<<8)+ ADRESL;
> __delay_us(20); //アクィジョン時間 20us
> ADCON0bits.GO_DONE= 1; //AD変換開始
> while(ADCON0bits.GO_DONE); //変換完了待ち
> return (Value);
これは害が無いかもしれませんけど悪いプログラムです。
先頭で、まだADCを始めてさえいないのに
Value=(ADRESH<<8)+ ADRESL;
とADC結果レジスタの値を取得してからADC変換を開始しています。
実際の所、これは前回行ったADCの値がレジスタに残っているでしょうからそれが出力されます。
ちゃんと今回の値を返したいなら変換完了→値を取得という順板を守ってください。
今回の場合は気付かない程度に遅れるだけですけど、これが計算なら、計算をせずに結果を返すような事です。
ボリューム(ADC)値×補正倍率を点灯/消灯時間、にしようとしますよね?
ボリューム値が1000なら2秒、100なら0.2秒、となるようにするなら補正倍率を2にします。
なので、通常は2msのディレイを1000回、或いは100回行って時間を合わせます。
RB3=1;
for(i=0;i<tmp;i++)__delay_ms(2);
だと、__delay_ms(2);がtmp回行われます。
> for(i = 0; i<tmp; i++){RB3=1;}
だと、tmp回、RB3=1;が行われます。(この処理時間をTとします。)
そして次行の
__delay_ms(2);
です。
つまり、RB3が1の時間は「T+2ms」ということになります。
Tはボリューム値に比例しますけど、意味のない2msが付きます。
for(i = 0; i<tmp*50; i++)RB3=1;
for(i = 0; i<tmp*50; i++)RB3=0;
のように、適当な倍率回数行う方がボリュームの設定値と点滅間隔の動きが一致します。
けれども、1ループで何クロックかかるかは、初心者には判断付きにくいと思います。
※実際、元のプログラムでは点滅時間が早すぎませんでしたか?
> Value=(ADRESH<<8)+ ADRESL;
> __delay_us(20); //アクィジョン時間 20us
> ADCON0bits.GO_DONE= 1; //AD変換開始
> while(ADCON0bits.GO_DONE); //変換完了待ち
> return (Value);
これは害が無いかもしれませんけど悪いプログラムです。
先頭で、まだADCを始めてさえいないのに
Value=(ADRESH<<8)+ ADRESL;
とADC結果レジスタの値を取得してからADC変換を開始しています。
実際の所、これは前回行ったADCの値がレジスタに残っているでしょうからそれが出力されます。
ちゃんと今回の値を返したいなら変換完了→値を取得という順板を守ってください。
今回の場合は気付かない程度に遅れるだけですけど、これが計算なら、計算をせずに結果を返すような事です。
Re: ADRES
> MABOさんへ
> PICに比べると親切過ぎる感じですね。
そうですね。
基本的な思想としては、「なるべく低級な作業(レジスタの直接操作)はしない」というのがあるのだと思います。
analogReadやanalogWriteも、使い勝手のいいモデル(分周や周期)が採用されています。
で、そのモデルを外した使い方をするときは、本来は、レジスタを直接操作する方法をしなければならないわけなのですが、
私が見た処、そういった使い方は稀で、大抵は、外部のライブラリを使う、という方が多いです。
つまり、他人が作った、操作し易くなっているもの=ライブラリを多くの人が使っています。
それが見つからなくて最後の手段として、データシートをみて、レジスタを弄る、という感じでしょうか。
勿論、Arduino使いも色々な人がいるでしょうけど。
※低レベルで、きっちりと積み上げていけば、そのデバイスの能力を最大限に活かした、より良いものが出来るのは間違いありません。
けれども、低級な作業を学んでもデバイスを変えたら役に立ちません。
逆に言えば、中級でいろんなデバイスで共通の仕様になっているものを使った方が
覚えるのも優しいし、色んなデバイスでも使え、また、間違いも少なくなる、とも言えます。
そのデバイスを熟知している人でも、やはり多くの行を書いていけば間違いも起こります。
そういった人でさえ、ADC変換でレジスタ操作をするなら、データシートで確認する事は必須でしょう。
でも、analogRead(pin);を使えば5秒で安心して使うことが出来ます。
逆に言えば、仕事で「高度な事」をする人の方がデータシートを参照しなければならないレジスタ操作からは離れて
短時間で安全に操作できる方を優先させます。
実際の所、私も最初は、データシートを見て自分自身で書いていました。
でも、何度か同じような事をすると、前回の物を使いまわすようになります。
そうなってくると、レジスタの内容はちょっと忘れても、その作った関数だけが重要になります。
何度か使っているので、信頼性は多少高くなった、自分の関数です。
でも、Arduinoなら、ほとんど同じものが最初から提供されています。
そちらの方が、信頼性が高く、他の人にとっても判りやすい、と言う事は明白です。
プログラムの開発会社が、そのような中間的なものを使うのは必然だと思います。
(逆に、レジスタ操作なんて(極力)するな、というのが主流でしょう)
で、私としては、レジスタ操作(データシート)と悪戦苦闘して、無駄な時間を使ってしまうPICからは離れた方が良いと
思う次第です。(勿論、ベーシックな部分をおさえるのは重要です。)
ただ、そのデバイスの専門家になる目的ではないので、そのレジスタを覚えることは次の段階では役に立たなくなります。
なので、そのデバイス固有の事は、他の参考サイトからコピーすれば良いと思っています。
ただし、それ以上に安全で、信頼性の高いのが、中間的な処理をしてあるライブラリを使う事です。
※個人サイトは、間違いだらけです。「製作」サイトは仕方がないと思うのですけど、「説明」サイトならもう少し
ちゃんと(過去に書いたプログラムも)管理してもらいたいと思っています。
まぁ、結局の所、それだけマイコンプログラムや電子回路は「間違いが起こりやすい=見つけ辛い」ということなのでしょうけど。
> PICに比べると親切過ぎる感じですね。
そうですね。
基本的な思想としては、「なるべく低級な作業(レジスタの直接操作)はしない」というのがあるのだと思います。
analogReadやanalogWriteも、使い勝手のいいモデル(分周や周期)が採用されています。
で、そのモデルを外した使い方をするときは、本来は、レジスタを直接操作する方法をしなければならないわけなのですが、
私が見た処、そういった使い方は稀で、大抵は、外部のライブラリを使う、という方が多いです。
つまり、他人が作った、操作し易くなっているもの=ライブラリを多くの人が使っています。
それが見つからなくて最後の手段として、データシートをみて、レジスタを弄る、という感じでしょうか。
勿論、Arduino使いも色々な人がいるでしょうけど。
※低レベルで、きっちりと積み上げていけば、そのデバイスの能力を最大限に活かした、より良いものが出来るのは間違いありません。
けれども、低級な作業を学んでもデバイスを変えたら役に立ちません。
逆に言えば、中級でいろんなデバイスで共通の仕様になっているものを使った方が
覚えるのも優しいし、色んなデバイスでも使え、また、間違いも少なくなる、とも言えます。
そのデバイスを熟知している人でも、やはり多くの行を書いていけば間違いも起こります。
そういった人でさえ、ADC変換でレジスタ操作をするなら、データシートで確認する事は必須でしょう。
でも、analogRead(pin);を使えば5秒で安心して使うことが出来ます。
逆に言えば、仕事で「高度な事」をする人の方がデータシートを参照しなければならないレジスタ操作からは離れて
短時間で安全に操作できる方を優先させます。
実際の所、私も最初は、データシートを見て自分自身で書いていました。
でも、何度か同じような事をすると、前回の物を使いまわすようになります。
そうなってくると、レジスタの内容はちょっと忘れても、その作った関数だけが重要になります。
何度か使っているので、信頼性は多少高くなった、自分の関数です。
でも、Arduinoなら、ほとんど同じものが最初から提供されています。
そちらの方が、信頼性が高く、他の人にとっても判りやすい、と言う事は明白です。
プログラムの開発会社が、そのような中間的なものを使うのは必然だと思います。
(逆に、レジスタ操作なんて(極力)するな、というのが主流でしょう)
で、私としては、レジスタ操作(データシート)と悪戦苦闘して、無駄な時間を使ってしまうPICからは離れた方が良いと
思う次第です。(勿論、ベーシックな部分をおさえるのは重要です。)
ただ、そのデバイスの専門家になる目的ではないので、そのレジスタを覚えることは次の段階では役に立たなくなります。
なので、そのデバイス固有の事は、他の参考サイトからコピーすれば良いと思っています。
ただし、それ以上に安全で、信頼性の高いのが、中間的な処理をしてあるライブラリを使う事です。
※個人サイトは、間違いだらけです。「製作」サイトは仕方がないと思うのですけど、「説明」サイトならもう少し
ちゃんと(過去に書いたプログラムも)管理してもらいたいと思っています。
まぁ、結局の所、それだけマイコンプログラムや電子回路は「間違いが起こりやすい=見つけ辛い」ということなのでしょうけど。
Re: ADRES
1案
while (1)
{
tmp = adconv();
RB3=1;
for(i = 0; i<tmp; i++){__delay_ms(2);}
RB3=0;
for(i = 0; i<tmp; i++){__delay_ms(2);}
}
案1は、遅すぎです。
maboさんがご紹介下さったURLの動画を見て下さい。
>先頭で、まだADCを始めてさえいないのに
Value=(ADRESH<<8)+ ADRESL;
No1216でプロトタイプ宣言をしたのに、関数を入れ忘れていました。
今のところ、下記のとおりです。
記
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 4000000
//プロトタイプ宣言
unsigned int adconv();
//変数宣言
unsigned int tmp;
unsigned int i;
unsigned int Value;
void main()
{
OSCCON = 0b0110000; // 内蔵OSC 4MHz
// ADコンバータ設定
ADCON0 = 0b01000001; // RA0をADコンバータピンにし、ADコンバータ機能をEbableに
ADCON1 = 0b10000000; // 結果数値は右寄せ、基準電圧はVDD
ANSEL = 0b00000001; // RA0(AN0)はアナログで読み取りピン
TRISA = 0b00000001; // RA0(AN0)はアナログで読み取りピン(入力)
TRISB = 0x00; // すべて出力
PORTA = 0x00;
PORTB = 0x00;
unsigned int adconv();
while (1)
{
tmp = adconv();
for(i = 0; i<tmp; i++){RB3=1;}
__delay_ms(2);
for(i = 0; i<tmp; i++){RB3=0;}
__delay_ms(2);
}
}
unsigned int adconv()
{
Value=(ADRESH<<8)+ ADRESL;
__delay_us(20); //アクィジョン時間 20us
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
return (Value);
}
while (1)
{
tmp = adconv();
RB3=1;
for(i = 0; i<tmp; i++){__delay_ms(2);}
RB3=0;
for(i = 0; i<tmp; i++){__delay_ms(2);}
}
案1は、遅すぎです。
maboさんがご紹介下さったURLの動画を見て下さい。
>先頭で、まだADCを始めてさえいないのに
Value=(ADRESH<<8)+ ADRESL;
No1216でプロトタイプ宣言をしたのに、関数を入れ忘れていました。
今のところ、下記のとおりです。
記
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 4000000
//プロトタイプ宣言
unsigned int adconv();
//変数宣言
unsigned int tmp;
unsigned int i;
unsigned int Value;
void main()
{
OSCCON = 0b0110000; // 内蔵OSC 4MHz
// ADコンバータ設定
ADCON0 = 0b01000001; // RA0をADコンバータピンにし、ADコンバータ機能をEbableに
ADCON1 = 0b10000000; // 結果数値は右寄せ、基準電圧はVDD
ANSEL = 0b00000001; // RA0(AN0)はアナログで読み取りピン
TRISA = 0b00000001; // RA0(AN0)はアナログで読み取りピン(入力)
TRISB = 0x00; // すべて出力
PORTA = 0x00;
PORTB = 0x00;
unsigned int adconv();
while (1)
{
tmp = adconv();
for(i = 0; i<tmp; i++){RB3=1;}
__delay_ms(2);
for(i = 0; i<tmp; i++){RB3=0;}
__delay_ms(2);
}
}
unsigned int adconv()
{
Value=(ADRESH<<8)+ ADRESL;
__delay_us(20); //アクィジョン時間 20us
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
return (Value);
}
Re: ADRES
まず、行っている事の目的があります。
最初にも言ったように、プログラム的には「間違い」ではありません。
参考サイトは簡単に言えば、「A/D変換の結果を目視で確認する」というものです。
それなのに、プログラムが悪かったので、その動画のように訳の分からないものになってしまった、
と言う事です。つまり、プログラムも動画も目的を果たしていない例です。
TRさんは最初から勘違いしているようですが、ADRESはPWMとは関係ありません。
ADC変換の結果を格納するレジスタです。
PWMと連動させるなら全く別のプログラムになります。つまり点滅の速さではなく、
明るさを変えるプログラムです。
そして、内容をもっと考えてください。
点滅のスピードは単にADC値を何倍(x○秒)を変えるだけです。
意味が解っていれば
RB3=1;
for(i = 0; i<tmp; i++){__delay_ms(2);}
RB3=0;
for(i = 0; i<tmp; i++){__delay_ms(2);}
も
RB3=1;
for(i = 0; i<tmp; i++){__delay_us(20);}
RB3=0;
for(i = 0; i<tmp; i++){__delay_us(20);}
も同じ(意味)のスケッチだと判る筈です。
また、
>先頭で、まだADCを始めてさえいないのに
の部分は
while(ADCON0bits.GO_DONE); //変換完了待ち
return (Value);
の間で値を取得しないと意味がない、と言う事です。
追記しておきます。
ADCの結果を確認するプログラムなら、ます、ちゃんとその結果が大まかにでも確認できる、
というのが重要です。
つまり、ADCは10ビット、つまり、0〜1023までの結果になります。
RB3=1;
for(i = 0; i<tmp; i++){__delay_ms(2);}
と書いているのは最大値を取得した時に約2秒程度(点滅周期では4秒)を想定しています。
で、ボリュームを半分にした時が1秒、つまり、その程度なら時計などで、判断付きやすい、かつ、それほど時間がかからない、
というのを想定したものです。更にはマイコンが想定しているクロックで動作しているか確認できます。
それとも
> 案1は、遅すぎです。
は点滅間隔が20秒や30秒かかったということでしょうか?
もしそうなら他に原因があるのかも知れません。動作クロックが違ったとか。
動画をみてどの位の速さ、つまりADCの値が幾つ位で、ボリューム位置がどの位置にあるのか、点滅を見て判断付きますか?
この位置なら、この位、というものを実証している動画には見えません。
最初にも言ったように、プログラム的には「間違い」ではありません。
参考サイトは簡単に言えば、「A/D変換の結果を目視で確認する」というものです。
それなのに、プログラムが悪かったので、その動画のように訳の分からないものになってしまった、
と言う事です。つまり、プログラムも動画も目的を果たしていない例です。
TRさんは最初から勘違いしているようですが、ADRESはPWMとは関係ありません。
ADC変換の結果を格納するレジスタです。
PWMと連動させるなら全く別のプログラムになります。つまり点滅の速さではなく、
明るさを変えるプログラムです。
そして、内容をもっと考えてください。
点滅のスピードは単にADC値を何倍(x○秒)を変えるだけです。
意味が解っていれば
RB3=1;
for(i = 0; i<tmp; i++){__delay_ms(2);}
RB3=0;
for(i = 0; i<tmp; i++){__delay_ms(2);}
も
RB3=1;
for(i = 0; i<tmp; i++){__delay_us(20);}
RB3=0;
for(i = 0; i<tmp; i++){__delay_us(20);}
も同じ(意味)のスケッチだと判る筈です。
また、
>先頭で、まだADCを始めてさえいないのに
の部分は
while(ADCON0bits.GO_DONE); //変換完了待ち
return (Value);
の間で値を取得しないと意味がない、と言う事です。
追記しておきます。
ADCの結果を確認するプログラムなら、ます、ちゃんとその結果が大まかにでも確認できる、
というのが重要です。
つまり、ADCは10ビット、つまり、0〜1023までの結果になります。
RB3=1;
for(i = 0; i<tmp; i++){__delay_ms(2);}
と書いているのは最大値を取得した時に約2秒程度(点滅周期では4秒)を想定しています。
で、ボリュームを半分にした時が1秒、つまり、その程度なら時計などで、判断付きやすい、かつ、それほど時間がかからない、
というのを想定したものです。更にはマイコンが想定しているクロックで動作しているか確認できます。
それとも
> 案1は、遅すぎです。
は点滅間隔が20秒や30秒かかったということでしょうか?
もしそうなら他に原因があるのかも知れません。動作クロックが違ったとか。
動画をみてどの位の速さ、つまりADCの値が幾つ位で、ボリューム位置がどの位置にあるのか、点滅を見て判断付きますか?
この位置なら、この位、というものを実証している動画には見えません。
Re: ADRES

それと、読み取り値の変数宣言をValueからADRESにしました。
簡単にしました。
本当は、ADRESが変わったらといったif文も付けるのでしょうけど。
案1
for(i = 0; i<ADRES; i++){RB3=1;}
__delay_ms(2);
for(i = 0; i<ADRES; i++){RB3=0;}
__delay_ms(2);
案2
for(i = 0; i<ADRES; i++){__delay_ms(2);}
RB3=1;
for(i = 0; i<ADRES; i++){__delay_ms(2);}
RB3=0;
案1と案2の違い
案1は、iを0からADRESの値になったら、RBを点灯し、2msおいて、またiが0からADRESまでRBが点灯
案2は、iを0からADRESの値になったら、2msおいて、RBを点灯(またiが0からADRESになり、2ms経過するまでの間)し、次に、RBが消灯
どちらも同じに思えるが、周期は案1の方が早い。
結局、何故、案1が早くなるのか分かりませんでした。
while (1)
{
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
ADRES=(ADRESH<<8)+ ADRESL;
//※ここから案1と案2を入れ替えます。
for(i = 0; i<ADRES; i++){RB3=1;}
__delay_ms(2);
for(i = 0; i<ADRES; i++){RB3=0;}
__delay_ms(2);
}
追記
PWM機能とADC機能 よく理解せずに、今回、続けて読んできてしまった。
どちらも10ビット操作という点で似ていた。
これが、、、、間違えてしまった原因。
どちらも、Lチカなので、より一層困惑してしまった。
PWMは、CCPR1の上位8ビットとCCP1CONのDC1Bの2ビット
ADCは、上位8バイトがADRESH (ADRES High byte)、下位2バイトがADRESL
Lチカしたい場合、ADC変換時間を利用するのではなく、PWM機能でする。
while(ADCON0bits.GO_DONE); //ADCの変換完了待ち 、これを使うのではなく、
ADRESH + ADRESLのビットの値 =CCP1LとCCP1CONのDC1B
にすれば、OKと思われる。
どうでしょうか??
Re: ADRES
まず、聞きたいのはTRさんは、いったい何をしようとしているのでしょうか?
案1も案2もプログラム的には間違いではありません。
なので、両方ともにかかれたように動きます。
重要な事は、目的と合致しているかどうかです。
> 読み取り値の変数宣言をValueからADRESにしました。
それはあまりいい変更ではありません。
というのはレジスタ名とダブル可能性があるからです。
(つまり、自分で作る変数に今回無いレジスタでもPORTDとかを付けるのはどうでしょうか?)
小文字にしておくとか区別した方が良いです。
> 何故、案1が早くなるのか分かりませんでした
orz 時間の計測の仕方をちゃんと覚えた方が良いです。
重要なのは「早くなる/遅くなる」ではなくて、それが○秒になる、と把握する事です。
Arduinoなら、時間をそのままPCに送って表示出来ますけど、
PIC使いならLEDの点滅の速さで識別するような使い方も覚える必要があります。
※でなければ、LCDや7セグを繋いで数値を表示させます。
ADCが5V、つまり、1023の時の時間を付けておきます。
なぜ、その時間になるのかを考えてください。
(説明は複数回しています)
for(i = 0; i<ADRES; i++){RB3=1;}//18.7ms
__delay_ms(2);//2ms
for(i = 0; i<ADRES; i++){RB3=0;}//18.7ms
__delay_ms(2);//2ms
これだと、約21msの点灯/消灯です。
実際の所、__delay_ms(2);は付けないで、ADRES*30とかの方が、まだ、比例関係になります。
for(i = 0; i<ADRES; i++) {__delay_ms(2);}//2.06s
RB3 = 1;//3us
for(i = 0; i<ADRES; i++) {__delay_ms(2);}//2.06s
RB3 = 0;//3us
こっちだと約2.06sの点灯/消灯です。
案1も案2もプログラム的には間違いではありません。
なので、両方ともにかかれたように動きます。
重要な事は、目的と合致しているかどうかです。
> 読み取り値の変数宣言をValueからADRESにしました。
それはあまりいい変更ではありません。
というのはレジスタ名とダブル可能性があるからです。
(つまり、自分で作る変数に今回無いレジスタでもPORTDとかを付けるのはどうでしょうか?)
小文字にしておくとか区別した方が良いです。
> 何故、案1が早くなるのか分かりませんでした
orz 時間の計測の仕方をちゃんと覚えた方が良いです。
重要なのは「早くなる/遅くなる」ではなくて、それが○秒になる、と把握する事です。
Arduinoなら、時間をそのままPCに送って表示出来ますけど、
PIC使いならLEDの点滅の速さで識別するような使い方も覚える必要があります。
※でなければ、LCDや7セグを繋いで数値を表示させます。
ADCが5V、つまり、1023の時の時間を付けておきます。
なぜ、その時間になるのかを考えてください。
(説明は複数回しています)
for(i = 0; i<ADRES; i++){RB3=1;}//18.7ms
__delay_ms(2);//2ms
for(i = 0; i<ADRES; i++){RB3=0;}//18.7ms
__delay_ms(2);//2ms
これだと、約21msの点灯/消灯です。
実際の所、__delay_ms(2);は付けないで、ADRES*30とかの方が、まだ、比例関係になります。
for(i = 0; i<ADRES; i++) {__delay_ms(2);}//2.06s
RB3 = 1;//3us
for(i = 0; i<ADRES; i++) {__delay_ms(2);}//2.06s
RB3 = 0;//3us
こっちだと約2.06sの点灯/消灯です。
Re: ADRES
レジスタはともかく、PWMは出力です。目で見える状態としては点滅では無く点灯です。
LED照明などでの明るさ調整でも使われている手法です。
また、モータの速度調整にもよく使われています。
一方、ADC=アナログ/デジタルコンバータは入力したアナログ電圧を数値化するものです。
Lチカとは直接は関係ありません。
今回のように、ボリュームを付けた装置だと、ボリュームの位置が何処にあるのかを調べるために使います。
そして、そのボリューム位置が意味する出力?にします。
LED照明などでの明るさ調整でも使われている手法です。
また、モータの速度調整にもよく使われています。
一方、ADC=アナログ/デジタルコンバータは入力したアナログ電圧を数値化するものです。
Lチカとは直接は関係ありません。
今回のように、ボリュームを付けた装置だと、ボリュームの位置が何処にあるのかを調べるために使います。
そして、そのボリューム位置が意味する出力?にします。
Re: ADRES
>まず、聞きたいのはTRさんは、いったい何をしようとしているのでしょうか?
有難うございます。
教科書でのPWMについては、半固定抵抗器を使っての明るさ調整がありませんので、
学習したいと思っています。
>というのはレジスタ名とダブル可能性があるからです。
止めます。
>重要なのは「早くなる/遅くなる」ではなくて、それが○秒になる、と把握する事です。
これからの、学習テーマの1つにしたいと思います。
自分で、PWM機能により可変抵抗器を使いLEDを滑らかに明るくしたり暗くしたりを考えましたが、
コンパイル不可
出来ましたら、ご指摘願いたいと思います。
/*PWM機能によりRB3のLEDで滑らかに調光。可変抵抗で制御*/
// PIC16F88 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 1000000
// プロトタイプ宣言
void InitPWM (void);
void InitTimer2 (void);
unsigned int adconv(void);
// メイン関数
void main (void)
{
OSCCON = 0x40; //クロック周波数を1MHzに設定
// 1,2,17,18端子を入出力端子に設定
CMCON = 0x07;
// 電圧レベルの初期設定
PORTA = 0xFF;
PORTB = 0xFF;
// 入出力設定
TRISA =0b00000001; // RA0(AN0)はアナログで読み取りピン(入力)
TRISB =0x00;
ANSELA=0b00000001;//RA0をアナログピンに設定
ANSELB=0x00; //
// ADコンバータ設定
ADCON0 = 0b00000001; // RA0をADコンバータピンにし、ADコンバータ機能をEbableに
ADCON1 =0b10000000; // 結果数値は右寄せ、ADコンバータクロックはFOSC/2、基準電圧はVDD
//変数宣言
unsigned int num ;
// PWMモード設定関数の呼び出し
InitPWM();
// タイマ2設定関数の呼び出し
InitTimer2();
while(1) {
// アナログ値の変換と読込み処理
adconv();
num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
}
// アナログ値の変換と読込み処理
unsigned int adconv()
{
unsigned int temp;
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
temp = ADRESH ;//
temp = ( temp << 8 ) + ADRESL ;//RA0で読み込んだ値をtempに代入
return temp ;//読み込んだtempをnumに返す
}
// PWMモード設定関数
void InitPWM (void)
{
// RB3端子を出力端子に設定
TRISBbits.TRISB3 = 0;
// CCPのモードをPWMモードに設定
CCP1CONbits.CCP1M3=1;
CCP1CONbits.CCP1M2=1;
CCP1CONbits.CCP1M1=0;
CCP1CONbits.CCP1M0=0;
// 周期を1μ秒に設定(99 + 1μ秒)
PR2 =100;
// デューティーサイクルを0msで初期化
CCPR1L = 0;
CCP1CONbits.CCP1X=0;
CCP1CONbits.CCP1Y=0;
}
// タイマ2設定関数
void InitTimer2 (void)
{
// プリスケーラ値を1に設定
T2CONbits.T2CKPS1=0;
T2CONbits.T2CKPS0=0;
// TMR2レジスタをクリア
TMR2 = 0;
// タイマ2起動
T2CONbits.TMR2ON= 1;
}
有難うございます。
教科書でのPWMについては、半固定抵抗器を使っての明るさ調整がありませんので、
学習したいと思っています。
>というのはレジスタ名とダブル可能性があるからです。
止めます。
>重要なのは「早くなる/遅くなる」ではなくて、それが○秒になる、と把握する事です。
これからの、学習テーマの1つにしたいと思います。
自分で、PWM機能により可変抵抗器を使いLEDを滑らかに明るくしたり暗くしたりを考えましたが、
コンパイル不可
出来ましたら、ご指摘願いたいと思います。
/*PWM機能によりRB3のLEDで滑らかに調光。可変抵抗で制御*/
// PIC16F88 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 1000000
// プロトタイプ宣言
void InitPWM (void);
void InitTimer2 (void);
unsigned int adconv(void);
// メイン関数
void main (void)
{
OSCCON = 0x40; //クロック周波数を1MHzに設定
// 1,2,17,18端子を入出力端子に設定
CMCON = 0x07;
// 電圧レベルの初期設定
PORTA = 0xFF;
PORTB = 0xFF;
// 入出力設定
TRISA =0b00000001; // RA0(AN0)はアナログで読み取りピン(入力)
TRISB =0x00;
ANSELA=0b00000001;//RA0をアナログピンに設定
ANSELB=0x00; //
// ADコンバータ設定
ADCON0 = 0b00000001; // RA0をADコンバータピンにし、ADコンバータ機能をEbableに
ADCON1 =0b10000000; // 結果数値は右寄せ、ADコンバータクロックはFOSC/2、基準電圧はVDD
//変数宣言
unsigned int num ;
// PWMモード設定関数の呼び出し
InitPWM();
// タイマ2設定関数の呼び出し
InitTimer2();
while(1) {
// アナログ値の変換と読込み処理
adconv();
num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
}
// アナログ値の変換と読込み処理
unsigned int adconv()
{
unsigned int temp;
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
temp = ADRESH ;//
temp = ( temp << 8 ) + ADRESL ;//RA0で読み込んだ値をtempに代入
return temp ;//読み込んだtempをnumに返す
}
// PWMモード設定関数
void InitPWM (void)
{
// RB3端子を出力端子に設定
TRISBbits.TRISB3 = 0;
// CCPのモードをPWMモードに設定
CCP1CONbits.CCP1M3=1;
CCP1CONbits.CCP1M2=1;
CCP1CONbits.CCP1M1=0;
CCP1CONbits.CCP1M0=0;
// 周期を1μ秒に設定(99 + 1μ秒)
PR2 =100;
// デューティーサイクルを0msで初期化
CCPR1L = 0;
CCP1CONbits.CCP1X=0;
CCP1CONbits.CCP1Y=0;
}
// タイマ2設定関数
void InitTimer2 (void)
{
// プリスケーラ値を1に設定
T2CONbits.T2CKPS1=0;
T2CONbits.T2CKPS0=0;
// TMR2レジスタをクリア
TMR2 = 0;
// タイマ2起動
T2CONbits.TMR2ON= 1;
}
Re: ADRES
動作しました!
可変抵抗器で明るくなったり暗くなったりしました。
間違いを
MPLABが教えてくれました。
変更点
ANSELA →ANSEL
ANSELB → この行を削除
でも、ANSELにはAかBを付ける必要と思うのですが。
どうしてだろう?
と思って、、データシートを見ると、
88にはANSELはAがわの0〜7までしかありませんでした!!
可変抵抗器で明るくなったり暗くなったりしました。
間違いを
MPLABが教えてくれました。
変更点
ANSELA →ANSEL
ANSELB → この行を削除
でも、ANSELにはAかBを付ける必要と思うのですが。
どうしてだろう?
と思って、、データシートを見ると、
88にはANSELはAがわの0〜7までしかありませんでした!!
Re: ADRES
> while(1) { // アナログ値の変換と読込み処理
> adconv();
> num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
> CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
> }
の部分ですけど、adconv();を2回しています。
これ自体、間違いで残ったのだと思うのですけど、実際に複数のアナログ入力ピンを切り替えて使う時に
この手法を取ったりします。(1回を読み捨てる)
つまり、切り替えてすぐに取得すると間違った値になりやすいからです。
で、前に、
> 意味不明な__delay_us(20); //アクィジョン時間 20usを無くし
と言っていますけど、これもチャンネルを切り替えた後に、その電圧が安定するまで少し待っている、という意味だと思います。
今回はチャンネルを切り替える訳では無いので、無くても大丈夫かな?と思っています。
> // 周期を1μ秒に設定(99 + 1μ秒)
> PR2 =100;
以前もおかしな記述(コメント)があったのですけど、取りあえず放っておきました。
それは
> // 周期を1μ秒に設定(249 + 1μ秒)
> PR2 =249;
というものです。
この時の周期は(249+1)*4[us]=1000[us]=1msです。
で、今回の場合は
(100+1)*4=404usです。
あと、PWMやADCのメカニズム?をちゃんと押さえておいた方が良いです。
PWMだと、
PR2が周期、つまりこの値が上限に当たります。
duty値がHIGH→LOWに切り替わる値です。(場合によっては逆側に切り変える設定があったりもします)
タイマーが0で出力がHIGHになり、タイマーがduty値でLOWに切り替わります。
「249」と、250に1を引いた数にするのは249をカウントして、次の0になった時にHIGHになるので1を引きます。
そうした仕組みなので、PR2を101(101/255)にして、ADCの値をそのまま入れてもボリュームの半分以下の部分しか
反応しない(つまり、101/255以上になると常にHIGH)になっていると想像します。
感覚的にはどうだったでしょうか?
ボリュームのフルレンジを使って、明るさを調節したいのなら、PR2は255が適当でしょう。
勿論、ADCの値に101/255をかけたものをduty値に設定しても良いですけど。
で、
while(1) { // アナログ値の変換と読込み処理
num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
の部分だと1回で、280us程度の時間しかかかりません。つまり、PWMの1回の点滅よりも多く取得して、
切り替え値を操作しています。
そう言った時は適度なディレイを入れた方が良いと思います。(そのままで駄目という訳では無いですけど)
つまり、
while(1) {
num = adconv();
CCPR1L = num/4 ;
__delay_ms(10);
}
程度にするのが適当かな、と思う次第です。
で、これを書き換えると(まぁ、書き換えなくても良いのですけど)
while(1) {
CCPR1L = adconv()/4 ;
__delay_ms(10);
}
で、同じ意味です。
それが、No.1213で書いたArduinoの
void loop() {
analogWrite(3,analogRead(A0)/4);
delay(10);
}
です。こっちのdelay(10);も同じ意味で付けています。
> adconv();
> num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
> CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
> }
の部分ですけど、adconv();を2回しています。
これ自体、間違いで残ったのだと思うのですけど、実際に複数のアナログ入力ピンを切り替えて使う時に
この手法を取ったりします。(1回を読み捨てる)
つまり、切り替えてすぐに取得すると間違った値になりやすいからです。
で、前に、
> 意味不明な__delay_us(20); //アクィジョン時間 20usを無くし
と言っていますけど、これもチャンネルを切り替えた後に、その電圧が安定するまで少し待っている、という意味だと思います。
今回はチャンネルを切り替える訳では無いので、無くても大丈夫かな?と思っています。
> // 周期を1μ秒に設定(99 + 1μ秒)
> PR2 =100;
以前もおかしな記述(コメント)があったのですけど、取りあえず放っておきました。
それは
> // 周期を1μ秒に設定(249 + 1μ秒)
> PR2 =249;
というものです。
この時の周期は(249+1)*4[us]=1000[us]=1msです。
で、今回の場合は
(100+1)*4=404usです。
あと、PWMやADCのメカニズム?をちゃんと押さえておいた方が良いです。
PWMだと、
PR2が周期、つまりこの値が上限に当たります。
duty値がHIGH→LOWに切り替わる値です。(場合によっては逆側に切り変える設定があったりもします)
タイマーが0で出力がHIGHになり、タイマーがduty値でLOWに切り替わります。
「249」と、250に1を引いた数にするのは249をカウントして、次の0になった時にHIGHになるので1を引きます。
そうした仕組みなので、PR2を101(101/255)にして、ADCの値をそのまま入れてもボリュームの半分以下の部分しか
反応しない(つまり、101/255以上になると常にHIGH)になっていると想像します。
感覚的にはどうだったでしょうか?
ボリュームのフルレンジを使って、明るさを調節したいのなら、PR2は255が適当でしょう。
勿論、ADCの値に101/255をかけたものをduty値に設定しても良いですけど。
で、
while(1) { // アナログ値の変換と読込み処理
num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
の部分だと1回で、280us程度の時間しかかかりません。つまり、PWMの1回の点滅よりも多く取得して、
切り替え値を操作しています。
そう言った時は適度なディレイを入れた方が良いと思います。(そのままで駄目という訳では無いですけど)
つまり、
while(1) {
num = adconv();
CCPR1L = num/4 ;
__delay_ms(10);
}
程度にするのが適当かな、と思う次第です。
で、これを書き換えると(まぁ、書き換えなくても良いのですけど)
while(1) {
CCPR1L = adconv()/4 ;
__delay_ms(10);
}
で、同じ意味です。
それが、No.1213で書いたArduinoの
void loop() {
analogWrite(3,analogRead(A0)/4);
delay(10);
}
です。こっちのdelay(10);も同じ意味で付けています。
Re: ADRES

> adconv();
> num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
> CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
> }
の部分ですけど、adconv();を2回しています。
これ自体、間違いで残ったのだと思うのですけど、実際に複数のアナログ入力ピンを切り替えて使う時に
この手法を取ったりします。(1回を読み捨てる)
そのとおりです。1回を読み捨てるという内容です。
この意味合いで使いました。
であれば、OKですよね。
以前、間欠プログラムでも似たような以下や、あちこち見ていて、このアイデアを思いつきました。
可決プログラムから抜粋 ↓
// 永久ループ
while(1){
MODE=PORTA&0b11;
__delay_ms(50);
while(MODE!=(PORTA&0b11)){ //変化があればウェイト、無ければ確定
MODE=PORTA&0b11;
> 意味不明な__delay_us(20); //アクィジョン時間 20usを無くし
と言っていますけど、これもチャンネルを切り替えた後に、その電圧が安定するまで少し待っている、という意味だと思います。
今回はチャンネルを切り替える訳では無いので、無くても大丈夫かな?と思っています。
そうでしたか。チャタリング防止ですね。
> // 周期を1μ秒に設定(99 + 1μ秒)
> PR2 =100;
以前もおかしな記述(コメント)があったのですけど、取りあえず放っておきました。
この方法は、キットで遊ぼうの教科書にありました。
写真参照願います。
教科書では、周期を先に100μsとして、PR2を逆算しています。
ただ、プログラムの方は「周期を1μ・・・」となってしまっていましたので、間違えです。教科書では100μsです。
今回のように可変抵抗器で調光する場合、周期やPR2はどのように考えて決めるのでしょうか?
自分なりに思うと、
写真の公式によれば、
周期=(PR2+1)1/(動作処理速度÷4)
となっているので、
動作処理速度 1MHz(とりあえずにした値です)
100μs=(PR2+1)1/(1MHz÷4)
100×4/1-1=PR2
従って PR2=309μs
なので、動作を先に確認したかったので、PR2=100 としましたが、
本当は、309μsでした。
で、今直しました。
今のところ、LEDの状況を見ると、不満は、
VRを絞りると暗くなるけど、
明けきらない内に、頭打ちになって明るさが伴っていない感じです。
解消方法は有りますか?
以下に説明されていますが、文字だけでは、自分には良く分かりません。
もう少し、おいおい、なんとかしたいと思ってはいます。キットで遊ぼうの教科書を、今回のご指摘を踏まえて再度読み込んでみます。
1週間前まではチンプンカンプンでしたから、自分でもよくここまで来たかと思っているところです。
申し訳ありません。
ところで、250という数字が出てきますね。
255が適当でしょうとの事ですね。
周期を255にするPR2を逆算してプログに反映してみます。
> // 周期を1μ秒に設定(249 + 1μ秒)
> PR2 =249;
というものです。
この時の周期は(249+1)*4[us]=1000[us]=1msです。
で、今回の場合は
(100+1)*4=404usです。
>あと、PWMやADCのメカニズム?をちゃんと押さえておいた方が良いです。
PWMだと、
PR2が周期、つまりこの値が上限に当たります。
duty値がHIGH→LOWに切り替わる値です。(場合によっては逆側に切り変える設定があったりもします)
タイマーが0で出力がHIGHになり、タイマーがduty値でLOWに切り替わります。
「249」と、250に1を引いた数にするのは249をカウントして、次の0になった時にHIGHになるので1を引きます。
そうした仕組みなので、PR2を101(101/255)にして、ADCの値をそのまま入れてもボリュームの半分以下の部分しか
反応しない(つまり、101/255以上になると常にHIGH)になっていると想像します。
感覚的にはどうだったでしょうか?
ボリュームのフルレンジを使って、明るさを調節したいのなら、PR2は255が適当でしょう。
勿論、ADCの値に101/255をかけたものをduty値に設定しても良いですけど。
>で、
while(1) { // アナログ値の変換と読込み処理
num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
の部分だと1回で、280us程度の時間しかかかりません。つまり、PWMの1回の点滅よりも多く取得して、
切り替え値を操作しています。
そう言った時は適度なディレイを入れた方が良いと思います。(そのままで駄目という訳では無いですけど)
つまり、
while(1) {
num = adconv();
CCPR1L = num/4 ;
__delay_ms(10);
}
程度にするのが適当かな、と思う次第です。
で、これを書き換えると(まぁ、書き換えなくても良いのですけど)
分かりました。
>while(1) {
CCPR1L = adconv()/4 ;
__delay_ms(10);
}
で、同じ意味です。
それが、No.1213で書いたArduinoの
void loop() {
analogWrite(3,analogRead(A0)/4);
delay(10);
}
です。こっちのdelay(10);も同じ意味で付けています。
分かりました。
追記
周期255にしてPR2を1019にしたら、
調光の具合は良いです。
Re: ADRES
> そうでしたか。チャタリング防止ですね。
いいえ、そうではありません。
大抵のマイコンは、ADCの装置は1つで、それを複数のピンにADC装置の外側で、マルチプレクサで切り替えて使います。
それで複数のピンでADCを使えるようにしています。
で、接続ピンを切り替えるという事は異なる電圧に繋がる、という事です。その際の誤計測の防止の為に。1回読み飛ばしたり、ちょっと間を空けたりします。
そのテキストでは4MHz駆動=1MHzサイクル=1us/カウントでPR2が99なので
(99+1)*1us=100usです。
最初の例では1Mhz駆動=250kHzサイクル=4us/カウントで、PR2が249です。なので、
(249+1)*4=1000us=1msです。
今回のものも1Mhz駆動=250kHzサイクル=4us/カウントで、PR2は100です。なので
(100+1)*4=404us
となります。マイコンの速度は元になる駆動クロックから求まります。
※カウント数と1サイクル(カウント)時間をかけた方が分かりやすいと思います。
> 可変抵抗器で調光する場合、周期やPR2
まず、ボリュームによるADC入力と、PWM出力の間に関連性はありません。
「スイッチを押したらLEDを点灯させる」というものが、元々スイッチとLEDという無関係の物を
連動させているのと同じです。
LEDの適正周期に関してはマイコンとはあまり関係ありません。
人間にとって100Hz=10msよりも周期が長いとちらつきを感じると言われています。
なので、7セグ等はそれよりも短いものにします。
照明で利用するなら更に周期を短くする必要があるでしょう。
※周波数が高すぎて問題が起こることは殆どありません。ただし、周波数が高いほどノイズを誘発し易くなる事、
波形が乱れやすくなることがあります。
で、ボリュームの指している値、つまり、抵抗値の割合とPWMのduty比を一致させる、という考えもあります。
そうするとボリュームとLEDの電力が比例関係になります。
上で説明した通り、まず、カウンタの動作を考えてください。
カウンタがPR2の値になると0に戻ります。つまり、0→PR2を繰り返すカウンタです。
そして、duty値とカウント値を比較して、どちらが大きいかで、HIGH/LOW出力が決まります。。
duty値が大きすぎてPR2以上なら何も変化せずに、HIGH出力です。
時間や表示を最優先にしたいなら、周期(PR2+1)は10進数できりが良い値でしょうね。
100や250が適当だと思います。例えば%表示をしたい場合などはそのまま100でいいでしょう。
ADCが最大1023なので、
<ADC>*100/1023の計算をしてduty値のレジスタCCPR1Lに入れて、表示させることになります。
ただし、余りがでるのでちょっと嫌な感じがします。
※ADCが0〜10が0、11〜20が1、…と殆どの範囲が10になるのですけど、MAX100の範囲は1023のみになります。
そうなるのを避けるために、<ADC+50>*100/1023とする手法もあります。
ボリュームとPWMを同期させるなら0xFFが良いです。
ADC値を4で割ればそのまま使えます。つまり、割り切れます。
> 周期255にしてPR2を1019にしたら、
意味が判りません。PR2レジスタは8ビットなので、そのまま
PR2=0xFF;//又はPR2=255;
です。
PR2=1019;
と、入りきらない値を書くと上位桁が消されて
PR2=251;//1019%256=251
と同じ意味になります。
※ちなみに、PWMは10ビットですけど、下位2ビットはプリスケーラを使います。
なので、プリスケーラが1:1の今回の場合は実質的に切り捨てられるような気がします。
詳しくはデータシートで確認して下さい。
上記はプリスケーラが1:1のときはプリスケーラではなく、駆動クロックが代わりになるようです。
なので、10ビットのPWMになるのは変わらない、という事でした。
いいえ、そうではありません。
大抵のマイコンは、ADCの装置は1つで、それを複数のピンにADC装置の外側で、マルチプレクサで切り替えて使います。
それで複数のピンでADCを使えるようにしています。
で、接続ピンを切り替えるという事は異なる電圧に繋がる、という事です。その際の誤計測の防止の為に。1回読み飛ばしたり、ちょっと間を空けたりします。
そのテキストでは4MHz駆動=1MHzサイクル=1us/カウントでPR2が99なので
(99+1)*1us=100usです。
最初の例では1Mhz駆動=250kHzサイクル=4us/カウントで、PR2が249です。なので、
(249+1)*4=1000us=1msです。
今回のものも1Mhz駆動=250kHzサイクル=4us/カウントで、PR2は100です。なので
(100+1)*4=404us
となります。マイコンの速度は元になる駆動クロックから求まります。
※カウント数と1サイクル(カウント)時間をかけた方が分かりやすいと思います。
> 可変抵抗器で調光する場合、周期やPR2
まず、ボリュームによるADC入力と、PWM出力の間に関連性はありません。
「スイッチを押したらLEDを点灯させる」というものが、元々スイッチとLEDという無関係の物を
連動させているのと同じです。
LEDの適正周期に関してはマイコンとはあまり関係ありません。
人間にとって100Hz=10msよりも周期が長いとちらつきを感じると言われています。
なので、7セグ等はそれよりも短いものにします。
照明で利用するなら更に周期を短くする必要があるでしょう。
※周波数が高すぎて問題が起こることは殆どありません。ただし、周波数が高いほどノイズを誘発し易くなる事、
波形が乱れやすくなることがあります。
で、ボリュームの指している値、つまり、抵抗値の割合とPWMのduty比を一致させる、という考えもあります。
そうするとボリュームとLEDの電力が比例関係になります。
上で説明した通り、まず、カウンタの動作を考えてください。
カウンタがPR2の値になると0に戻ります。つまり、0→PR2を繰り返すカウンタです。
そして、duty値とカウント値を比較して、どちらが大きいかで、HIGH/LOW出力が決まります。。
duty値が大きすぎてPR2以上なら何も変化せずに、HIGH出力です。
時間や表示を最優先にしたいなら、周期(PR2+1)は10進数できりが良い値でしょうね。
100や250が適当だと思います。例えば%表示をしたい場合などはそのまま100でいいでしょう。
ADCが最大1023なので、
<ADC>*100/1023の計算をしてduty値のレジスタCCPR1Lに入れて、表示させることになります。
ただし、余りがでるのでちょっと嫌な感じがします。
※ADCが0〜10が0、11〜20が1、…と殆どの範囲が10になるのですけど、MAX100の範囲は1023のみになります。
そうなるのを避けるために、<ADC+50>*100/1023とする手法もあります。
ボリュームとPWMを同期させるなら0xFFが良いです。
ADC値を4で割ればそのまま使えます。つまり、割り切れます。
> 周期255にしてPR2を1019にしたら、
意味が判りません。PR2レジスタは8ビットなので、そのまま
PR2=0xFF;//又はPR2=255;
です。
PR2=1019;
と、入りきらない値を書くと上位桁が消されて
PR2=251;//1019%256=251
と同じ意味になります。
※ちなみに、PWMは10ビットですけど、下位2ビットはプリスケーラを使います。
なので、プリスケーラが1:1の今回の場合は実質的に切り捨てられるような気がします。
詳しくはデータシートで確認して下さい。
上記はプリスケーラが1:1のときはプリスケーラではなく、駆動クロックが代わりになるようです。
なので、10ビットのPWMになるのは変わらない、という事でした。
Re: ADRES
こんばんは。
今日も、MPLABさんのPWMとADコンバーターの記事を読んできました。
少しは分かったような感じがします。
No1229
>で、
@ while(1) { // アナログ値の変換と読込み処理
A num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
B CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
の部分だと1回で、280us程度の時間しかかかりません。
280μsとは、@+A+Bのプログラム内容をPICがこなす時間ですか?
MPLABIDXのシュミレーター機能を使えば、プログラムの作業時間が分かるのかな?
やってみたけど、難しい。
尚、__delay_ms(10);は追加しました。
No1230
>※ADCが0〜10が0、11〜20が1、…と殆どの範囲が10になるのですけど、MAX100の範囲は1023のみになります。
そうなるのを避けるために、<ADC+50>*100/1023とする手法もあります。
良く分からないので、PR2は249μsにしました。
PWMの操作とは、内蔵クロックなどを基本に、端子から読み取ったアナログ電圧とを比べての操作になるんでしょう。
今は、これくらいにします。
No1230
>※ちなみに、PWMは10ビットですけど、下位2ビットはプリスケーラを使います。
なので、プリスケーラが1:1の今回の場合は実質的に切り捨てられるような気がします。
詳しくはデータシートで確認して下さい。
DutyCycleを決める10ビットの内、下位2ビット(CCP1CONレジスタのDC1Bビット)
この事ですか?
ともかく、ここまでプログラムを組めるようになったので嬉しいです。
今日も、MPLABさんのPWMとADコンバーターの記事を読んできました。
少しは分かったような感じがします。
No1229
>で、
@ while(1) { // アナログ値の変換と読込み処理
A num = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
B CCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
}
の部分だと1回で、280us程度の時間しかかかりません。
280μsとは、@+A+Bのプログラム内容をPICがこなす時間ですか?
MPLABIDXのシュミレーター機能を使えば、プログラムの作業時間が分かるのかな?
やってみたけど、難しい。
尚、__delay_ms(10);は追加しました。
No1230
>※ADCが0〜10が0、11〜20が1、…と殆どの範囲が10になるのですけど、MAX100の範囲は1023のみになります。
そうなるのを避けるために、<ADC+50>*100/1023とする手法もあります。
良く分からないので、PR2は249μsにしました。
PWMの操作とは、内蔵クロックなどを基本に、端子から読み取ったアナログ電圧とを比べての操作になるんでしょう。
今は、これくらいにします。
No1230
>※ちなみに、PWMは10ビットですけど、下位2ビットはプリスケーラを使います。
なので、プリスケーラが1:1の今回の場合は実質的に切り捨てられるような気がします。
詳しくはデータシートで確認して下さい。
DutyCycleを決める10ビットの内、下位2ビット(CCP1CONレジスタのDC1Bビット)
この事ですか?
ともかく、ここまでプログラムを組めるようになったので嬉しいです。
Re: ADRES
「1回で」、というのはAとBという意味です。
勿論それは駆動クロックによって大きく変動します。
シミュレータで以前調べたのを利用して換算しました。
今回、直接調べた訳では無いです。
以前調べた時は4MHz動作の時にADC変換が68usだったと思います。
その4倍位です。Bは2サイクル=8us程度でしょう。
> PWMの操作とは、内蔵クロックなどを基本に、端子から読み取ったアナログ電圧とを比べての操作になるんでしょう。
だから、PWMとADCは、全くの別物です。ADCは電圧計です。PWMは高速の点滅です。
今回はそれを組み合わせただけです。まぁ、そういった使い方もよくあるとは思いますけど。
ただ、今まで言いませんでしたけど、ADCや時間と比例関係で動作させるのではなく、累乗?のようにすることもあります。
つまり、8段階の明るさとして、均等の32、64、96、128…256ではなく、2,4,8,16…256のように進行させたりします。
均等割りだと急に明るくなり、明るい部分ではあまり変化が感じません。逆に暗い部分は急に変化しているように感じます。
100分割(%)の計算「<ADC>*100/1023」だと、判り難いのでこれを4分割の式にしてみると「<ADC>*4/1023」になります。
でもこの計算式だとADCの値が0〜255だと0です。256〜511が1です。512〜767が2です。768〜1022が3です。そして、1023だけが4になります。
まぁ、今回の事とはあまり関係ない話です。
> DutyCycleを決める10ビットの内、下位2ビット(CCP1CONレジスタのDC1Bビット)
> この事ですか?
そうです。その比較対象のタイマーの方です。
タイマーのその部分はTIMER2のカウント外です。なので、カウントしている数値は読み出せません。
プリスケーラのカウンタ部分が使われている、というのをうろ覚えしていたのですが、
1:1ということはプリスケーラが使われていません。
なので、TIMER2のみで比較しているのでは?と思ったのですが、駆動クロックが利用されている、という記述を見つけました。
duty値の下位2bitも有効です。
上限(周期)が上位8ビット指定で、比較(duty値)は10ビット指定、という点は判り難い構成ですね。
そして、本体となるタイマーも下位2ビットは見えない部分で作られています。
勿論それは駆動クロックによって大きく変動します。
シミュレータで以前調べたのを利用して換算しました。
今回、直接調べた訳では無いです。
以前調べた時は4MHz動作の時にADC変換が68usだったと思います。
その4倍位です。Bは2サイクル=8us程度でしょう。
> PWMの操作とは、内蔵クロックなどを基本に、端子から読み取ったアナログ電圧とを比べての操作になるんでしょう。
だから、PWMとADCは、全くの別物です。ADCは電圧計です。PWMは高速の点滅です。
今回はそれを組み合わせただけです。まぁ、そういった使い方もよくあるとは思いますけど。
ただ、今まで言いませんでしたけど、ADCや時間と比例関係で動作させるのではなく、累乗?のようにすることもあります。
つまり、8段階の明るさとして、均等の32、64、96、128…256ではなく、2,4,8,16…256のように進行させたりします。
均等割りだと急に明るくなり、明るい部分ではあまり変化が感じません。逆に暗い部分は急に変化しているように感じます。
100分割(%)の計算「<ADC>*100/1023」だと、判り難いのでこれを4分割の式にしてみると「<ADC>*4/1023」になります。
でもこの計算式だとADCの値が0〜255だと0です。256〜511が1です。512〜767が2です。768〜1022が3です。そして、1023だけが4になります。
まぁ、今回の事とはあまり関係ない話です。
> DutyCycleを決める10ビットの内、下位2ビット(CCP1CONレジスタのDC1Bビット)
> この事ですか?
そうです。その比較対象のタイマーの方です。
タイマーのその部分はTIMER2のカウント外です。なので、カウントしている数値は読み出せません。
プリスケーラのカウンタ部分が使われている、というのをうろ覚えしていたのですが、
1:1ということはプリスケーラが使われていません。
なので、TIMER2のみで比較しているのでは?と思ったのですが、駆動クロックが利用されている、という記述を見つけました。
duty値の下位2bitも有効です。
上限(周期)が上位8ビット指定で、比較(duty値)は10ビット指定、という点は判り難い構成ですね。
そして、本体となるタイマーも下位2ビットは見えない部分で作られています。
Re: ADRES

122μsだと出ました。
while(1) {
// アナログ値の変換と読込み処理
@ adconv();
Anum = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
BCCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
__delay_ms(10);
それで話しついでですが、
猛牛ロックさんが言われていることで、PICの欠点ですが、
少し分かってきました。
ADコンバーターには速さの設定が有ります。
合成写真右側です。
早い方が良いと思いますが、一番早いのは、2Toscですか?
単純に2Toscを選んでよいのでしょうか?
Re: ADRES
> 早い方が良いと思いますが、一番早いのは、2Toscですか?
勿論、早い方が良いと言えますけど、信頼性との兼ね合いもあります。
また、計測の回数が増えるほど、他の作業の邪魔になる場合もあります。
まぁ、今回の場合は、
> while(ADCON0bits.GO_DONE); //変換完了待ち
とウェイトしていますから、早い方が良いですね。
今回のようなウェイトする場合と、完了→割り込みをする場合があります。
で、今回(1MHz駆動)の場合、設定上一番早いのは、2Toscになります。
もし、4MHz駆動にした場合は8Toscです。
だけども、それよりも1つ2つ遅くした方が賢明だと思います。
私自身は測定出来るぎりぎりのラインは使いません。
勿論、早い方が良いと言えますけど、信頼性との兼ね合いもあります。
また、計測の回数が増えるほど、他の作業の邪魔になる場合もあります。
まぁ、今回の場合は、
> while(ADCON0bits.GO_DONE); //変換完了待ち
とウェイトしていますから、早い方が良いですね。
今回のようなウェイトする場合と、完了→割り込みをする場合があります。
で、今回(1MHz駆動)の場合、設定上一番早いのは、2Toscになります。
もし、4MHz駆動にした場合は8Toscです。
だけども、それよりも1つ2つ遅くした方が賢明だと思います。
私自身は測定出来るぎりぎりのラインは使いません。
Re: ADRES
>だけども、それよりも1つ2つ遅くした方が賢明だと思います。
私自身は測定出来るぎりぎりのラインは使いません。
分かりました。
今は、内部クロックが1Mhzです、
これをもっと早くすれば、全体の速度は早くなるのですか?
それと、No1223の写真にあるデータシートの注意書きをグーグル翻訳をしたら ↓
注1:RCソースの標準TAD時間は4μsですが、2~6μsの間で変化する可能性があります。
あるサイトで見つけました。
TAD=(PICクロックの周期)×(Tosc)
今回は、OSCCON = 0x40; //クロック周波数を1MHzに設定 →1μs
Toscは、現在、2Toscなので、No1223写真のデータシートにより
1.25Mhz → 0.8μs
Tosc=1μs × 0.8μs
Tosc=0.8 となってOkでしょうか?
とおもったら、
Operationの方を掛けるのですか?
私自身は測定出来るぎりぎりのラインは使いません。
分かりました。
今は、内部クロックが1Mhzです、
これをもっと早くすれば、全体の速度は早くなるのですか?
それと、No1223の写真にあるデータシートの注意書きをグーグル翻訳をしたら ↓
注1:RCソースの標準TAD時間は4μsですが、2~6μsの間で変化する可能性があります。
あるサイトで見つけました。
TAD=(PICクロックの周期)×(Tosc)
今回は、OSCCON = 0x40; //クロック周波数を1MHzに設定 →1μs
Toscは、現在、2Toscなので、No1223写真のデータシートにより
1.25Mhz → 0.8μs
Tosc=1μs × 0.8μs
Tosc=0.8 となってOkでしょうか?
とおもったら、
Operationの方を掛けるのですか?
Re: ADRES
御免なさい。間違った事を書いてしまいました。
基本は1.6us〜6.4usの間になるように選択する、と言う事みたいです。
なので、1MHz駆動(1us周期)の場合は、2TOSCか4TOSCのどちらかにする、と言う事になると思います。
より推奨されるのが4usになる4TOSCなのでしょうけど、2TOSCでも全く問題はないと思います。
※つまり、2TOSCの選択はギリギリという事ではなく、推奨される範囲の値、という事。
また、一番下のRCを選択しておくのが無難だとも感じました。これはおおよそ4usになるRCです。
これを選択しておけば、1MHz駆動のソースでも、4MHz駆動のソースでも共通で済みます。
また、sleepという機能を使った時にADC変換を行いたい時にはこのRCをを選択している必要があります。
※自分なりにデータシートを読み取った感想ですが、確証はありません。
基本は1.6us〜6.4usの間になるように選択する、と言う事みたいです。
なので、1MHz駆動(1us周期)の場合は、2TOSCか4TOSCのどちらかにする、と言う事になると思います。
より推奨されるのが4usになる4TOSCなのでしょうけど、2TOSCでも全く問題はないと思います。
※つまり、2TOSCの選択はギリギリという事ではなく、推奨される範囲の値、という事。
また、一番下のRCを選択しておくのが無難だとも感じました。これはおおよそ4usになるRCです。
これを選択しておけば、1MHz駆動のソースでも、4MHz駆動のソースでも共通で済みます。
また、sleepという機能を使った時にADC変換を行いたい時にはこのRCをを選択している必要があります。
※自分なりにデータシートを読み取った感想ですが、確証はありません。
Re: ADRES
パソコンでも、クロック数は、大きい方、2Mhより4Mhzといった具合。
PICも内蔵クロック数を大きく設定した方が、PICの動作速度が速くなるのでしょうか?
今は、1Mhzに設定。
TAD=(PICクロックの周期)×(Tosc)
この式は合っていたようですね。
追記
時期PICはやはり、日本語のデータシートがあった方が良いですね。
16F1823 これに近い奴16F1827
http://akizukidenshi.com/catalog/g/gI-04430/
であれば、
TAD=1μs×2Tosc
TAD=2μs
でもRCといった選択肢があったわけですね。
有難うございます。
日本語のデータシートがあるPIC16F1823が欲しい。
PICも内蔵クロック数を大きく設定した方が、PICの動作速度が速くなるのでしょうか?
今は、1Mhzに設定。
TAD=(PICクロックの周期)×(Tosc)
この式は合っていたようですね。
追記
時期PICはやはり、日本語のデータシートがあった方が良いですね。
16F1823 これに近い奴16F1827
http://akizukidenshi.com/catalog/g/gI-04430/
であれば、
TAD=1μs×2Tosc
TAD=2μs
でもRCといった選択肢があったわけですね。
有難うございます。
日本語のデータシートがあるPIC16F1823が欲しい。
Re: ADRES
速度の考え方は、PCと同じです。
通常、4MHz駆動なら1MHz駆動の4倍速く動いていますから、1MHz→4MHzとすると
動作時間が1/4になります。
ただし、今回のようにADCは、ADCの変換速度はある範囲内、つまり一定にしてほしい、と言う事です。
> TAD=(PICクロックの周期)×(Tosc)
> この式は合っていたようですね。
いいえ、
(PICクロックの周期)と(Tosc)は同一です。
Fosc「周波数」=1/Tosc です。
で、
TADを「1.6us〜6.4usの間になるように」倍率を選択する、と言う事です。
1MHz駆動ならToscは1usです。そして設定できる倍率は、1,2,4,8、…なので、
2か4を選択する事になります。
RCはただ選択するだけです。計算はありません。おおよそ4usが標準値になるように作られているけども、
2〜6usのアバウトの範囲としか保証しない、と言う事です。
追記
16F1827だと、ちょっと性能が上がって、推奨範囲が広がっています。
数値は見つけられなかったですけど、例えば、0.8us〜6.4usのように速い速度のほうが広がったのだと思います。
なので、TADが1usも推奨範囲になっていますね。
そして、RCの標準値も1.6usになっています。
通常、4MHz駆動なら1MHz駆動の4倍速く動いていますから、1MHz→4MHzとすると
動作時間が1/4になります。
ただし、今回のようにADCは、ADCの変換速度はある範囲内、つまり一定にしてほしい、と言う事です。
> TAD=(PICクロックの周期)×(Tosc)
> この式は合っていたようですね。
いいえ、
(PICクロックの周期)と(Tosc)は同一です。
Fosc「周波数」=1/Tosc です。
で、
TADを「1.6us〜6.4usの間になるように」倍率を選択する、と言う事です。
1MHz駆動ならToscは1usです。そして設定できる倍率は、1,2,4,8、…なので、
2か4を選択する事になります。
RCはただ選択するだけです。計算はありません。おおよそ4usが標準値になるように作られているけども、
2〜6usのアバウトの範囲としか保証しない、と言う事です。
追記
16F1827だと、ちょっと性能が上がって、推奨範囲が広がっています。
数値は見つけられなかったですけど、例えば、0.8us〜6.4usのように速い速度のほうが広がったのだと思います。
なので、TADが1usも推奨範囲になっていますね。
そして、RCの標準値も1.6usになっています。
Duty比を変えるプログラム
以下のプログラムでLEDが暗くなる、明るくなるを繰り返すと思ったのですが、
点灯したままに見えます。
何処が悪いのでしょうか?
dutyを変える数値の50と1000はいい加減です。
動作確認したかったので、適当な数値です。
// TMR2レジスタをクリア
TMR2 = 0;
// タイマ2起動
T2CONbits.TMR2ON= 1;
// LEDをPWM制御
// デューテー比は10%100%の制御にする
while(1) {
// 5% -> 100%の制御
for(duty=50; duty<=1000; duty++) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(100);
}
// 100% -> 5%の制御
for(duty=1000; duty>=50; duty--) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(100);
}
}
点灯したままに見えます。
何処が悪いのでしょうか?
dutyを変える数値の50と1000はいい加減です。
動作確認したかったので、適当な数値です。
// TMR2レジスタをクリア
TMR2 = 0;
// タイマ2起動
T2CONbits.TMR2ON= 1;
// LEDをPWM制御
// デューテー比は10%100%の制御にする
while(1) {
// 5% -> 100%の制御
for(duty=50; duty<=1000; duty++) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(100);
}
// 100% -> 5%の制御
for(duty=1000; duty>=50; duty--) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(100);
}
}
Re: Duty比を変えるプログラム
上手くいきました。
動作しなかった理由が分かりました。
それは、Configでした。
色々と調べたので、関数の使い方など、ちょっとは分かるようになりました。
// PIC16F88 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 1000000
// プロトタイプ宣言
void InitPWM (void);
void InitTimer2 (void);
// メイン関数
void main (void)
{
// 1,2,17,18端子を入出力端子に設定
CMCON = 0x07;
// 電圧レベルの初期設定
PORTA = 0xFF;
PORTB = 0xFF;
// 入出力設定
TRISA = 0x20;
TRISB = 0x00;
OSCCON = 0x40; //クロック周波数を1MHzに設定
ANSEL = 0x00; //すべてのピンをデジタルモードに設定
// PWMモード設定関数の呼び出し
InitPWM();
// タイマ2設定関数の呼び出し
InitTimer2();
unsigned short duty;// PWMのデューティーサイクル
while(1){
// 5% -> 100%の制御
for(duty=50; duty<=1000;++duty) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(10);
}
// 100% -> 5%の制御
for(duty=1000; duty>=50; duty--) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(10);
}
}
}
// PWMモード設定関数
void InitPWM (void)
{
// RB3端子を出力端子に設定
TRISBbits.TRISB3 = 0;
// CCPのモードをPWMモードに設定
CCP1CONbits.CCP1M3=1;
CCP1CONbits.CCP1M2=1;
CCP1CONbits.CCP1M1=0;
CCP1CONbits.CCP1M0=0;
// 周期を1μ秒に設定(249 + 1μ秒)
PR2 =249;
// デューティーサイクルを0.5ms
CCPR1L = 500/4;
CCP1CONbits.CCP1X=0;
CCP1CONbits.CCP1Y=0;
}
// タイマ2設定関数
void InitTimer2 (void)
{
// プリスケーラ値を1に設定
T2CONbits.T2CKPS1=0;
T2CONbits.T2CKPS0=0;
// TMR2レジスタをクリア
TMR2 = 0;
// タイマ2起動
T2CONbits.TMR2ON= 1;
}
動作しなかった理由が分かりました。
それは、Configでした。
色々と調べたので、関数の使い方など、ちょっとは分かるようになりました。
// PIC16F88 Configuration Bit Settings
// 'C' source line config statements
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 1000000
// プロトタイプ宣言
void InitPWM (void);
void InitTimer2 (void);
// メイン関数
void main (void)
{
// 1,2,17,18端子を入出力端子に設定
CMCON = 0x07;
// 電圧レベルの初期設定
PORTA = 0xFF;
PORTB = 0xFF;
// 入出力設定
TRISA = 0x20;
TRISB = 0x00;
OSCCON = 0x40; //クロック周波数を1MHzに設定
ANSEL = 0x00; //すべてのピンをデジタルモードに設定
// PWMモード設定関数の呼び出し
InitPWM();
// タイマ2設定関数の呼び出し
InitTimer2();
unsigned short duty;// PWMのデューティーサイクル
while(1){
// 5% -> 100%の制御
for(duty=50; duty<=1000;++duty) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(10);
}
// 100% -> 5%の制御
for(duty=1000; duty>=50; duty--) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(10);
}
}
}
// PWMモード設定関数
void InitPWM (void)
{
// RB3端子を出力端子に設定
TRISBbits.TRISB3 = 0;
// CCPのモードをPWMモードに設定
CCP1CONbits.CCP1M3=1;
CCP1CONbits.CCP1M2=1;
CCP1CONbits.CCP1M1=0;
CCP1CONbits.CCP1M0=0;
// 周期を1μ秒に設定(249 + 1μ秒)
PR2 =249;
// デューティーサイクルを0.5ms
CCPR1L = 500/4;
CCP1CONbits.CCP1X=0;
CCP1CONbits.CCP1Y=0;
}
// タイマ2設定関数
void InitTimer2 (void)
{
// プリスケーラ値を1に設定
T2CONbits.T2CKPS1=0;
T2CONbits.T2CKPS0=0;
// TMR2レジスタをクリア
TMR2 = 0;
// タイマ2起動
T2CONbits.TMR2ON= 1;
}
Re: Duty比を変えるプログラム
TRさん,猛牛ロックさん,こんばんは。
TRさん,頑張ってますね。
PWMモード復習してみました。
TRさんの書かれたプログラムですが,
for(duty=50; duty<=1000;++duty) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(10);
}
の
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビッ
部分ですが,ビットの操作なので,0か1がはいるのですが,
10進数を代入するとどうなるのでしょうね。
ちゃんと,下位の1ビット目と2ビットめがそれぞれ入る
のでしょうか。
ちょっと分からないところです。
もしかして,
CCP1CONbits.CCP1X=((duty>>1)&1);// dutyの1ビット目代入
CCP1CONbits.CCP1Y=(duty&1);// dutyの0ビット目を代入
みたいにしないとだめかもしれません。
TRさん,頑張ってますね。
PWMモード復習してみました。
TRさんの書かれたプログラムですが,
for(duty=50; duty<=1000;++duty) {
CCPR1L = duty / 4; // 上位8ビット
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビット
__delay_ms(10);
}
の
CCP1CONbits.CCP1X=duty;// CCP1Xビット
CCP1CONbits.CCP1Y=duty;// CCP1Yビッ
部分ですが,ビットの操作なので,0か1がはいるのですが,
10進数を代入するとどうなるのでしょうね。
ちゃんと,下位の1ビット目と2ビットめがそれぞれ入る
のでしょうか。
ちょっと分からないところです。
もしかして,
CCP1CONbits.CCP1X=((duty>>1)&1);// dutyの1ビット目代入
CCP1CONbits.CCP1Y=(duty&1);// dutyの0ビット目を代入
みたいにしないとだめかもしれません。
Re: Duty比を変えるプログラム
maboさん、レス有難うございます。
私も、10進数を=にしてよいものかと感じていますが、
ツールラボさんの解説を貼り付けます。
さて、500をわざわざ2進数に変換して、その上位8ビットや下位2ビットを求めるのは面倒ですので、楽な方法でプログラムを書くことにしましょう。
まず、上位8ビットの求め方です。2進数表現では、2で割ると1桁右に移動します。一番右の桁は無くなります。
Pic app 4 binary calculation
さらに2で割ればもう1桁右に移動します。ということは、500を4で割れば上位8ビットを取り出すことができます。
ということで、CCPR1Lに上位8ビットを設定するには、
CCPR1L = 500/4;
と書けばよいことになります。
私も、10進数を=にしてよいものかと感じていますが、
ツールラボさんの解説を貼り付けます。
さて、500をわざわざ2進数に変換して、その上位8ビットや下位2ビットを求めるのは面倒ですので、楽な方法でプログラムを書くことにしましょう。
まず、上位8ビットの求め方です。2進数表現では、2で割ると1桁右に移動します。一番右の桁は無くなります。
Pic app 4 binary calculation
さらに2で割ればもう1桁右に移動します。ということは、500を4で割れば上位8ビットを取り出すことができます。
ということで、CCPR1Lに上位8ビットを設定するには、
CCPR1L = 500/4;
と書けばよいことになります。
Re: Duty比を変えるプログラム
TRさん,猛牛ロックさん,おはようございます。
>上位8ビット
を取り出すにには,右に,2ビット分シフトですから,
意味的には,4で割ることと同意ですよね。
このへんの操作が面倒ですよね。
下位ビットの設定には,
CCP1CONbits.DC1B
がTRさんのおっしゃるツールラボでは使われてましたね。
CCP1CONbits.DC1B = 500
あるいは,
CCP1CONbits.DC1B = 500 && 0b11;
のように書いてありました。
CCP1CONbits.DC1B
だと,
CCP1CONbits.CCP1X
CCP1CONbits.CCP1Y
を使わなくても一度ですむみたいですね。
PWMはすっかり忘れていました。
>上位8ビット
を取り出すにには,右に,2ビット分シフトですから,
意味的には,4で割ることと同意ですよね。
このへんの操作が面倒ですよね。
下位ビットの設定には,
CCP1CONbits.DC1B
がTRさんのおっしゃるツールラボでは使われてましたね。
CCP1CONbits.DC1B = 500
あるいは,
CCP1CONbits.DC1B = 500 && 0b11;
のように書いてありました。
CCP1CONbits.DC1B
だと,
CCP1CONbits.CCP1X
CCP1CONbits.CCP1Y
を使わなくても一度ですむみたいですね。
PWMはすっかり忘れていました。
Re: Duty比を変えるプログラム
10進数をレジスタに=にしてもOKみたいですね!
CCP1CONbits.DC1B 
これですが、16F88の場合、無いようでしたので、1バイトづつとじました。
本当に忘れるほうが早い (笑)
CCP1CONbits.DC1B 
これですが、16F88の場合、無いようでしたので、1バイトづつとじました。
本当に忘れるほうが早い (笑)
Re: Duty比を変えるプログラム
ツールラボの話は別のマイコンの話です。
CCP1CONbits.DC1Bは2bitの器です。
なので、4で割った余りが入ります。
CCP1CONbits.CCP1X=500;
CCP1CONbits.CCP1Y=500;
なら、当然、両方とも0です。
1bitの器(が2つ)しかない所にそれよりも多い数を入れると、繰り上がった分は消えます。
簡単に言えば2で割った余りです。
duty比が1なら判るでしょう?
CCP1CONbits.CCP1X=1;
CCP1CONbits.CCP1Y=1;
と書いて、Xの方が0になる筈ないですよね?
MABOさんの言う通りの処理が必要です。
まぁ、端数なので気付かない可能性が大きいですけど。
…と思ったら
> CCP1CONbits.DC1B = 500 && 0b11;
これは駄目です。そのサイトに書いてあったんですか?
これでは500の部分が0以外なら、CCP1CONbits.DC1Bには0b01が入ります。
0の場合は0です。
(本当は「&&」では無く「&」)
CCP1CONbits.DC1Bは2bitの器です。
なので、4で割った余りが入ります。
CCP1CONbits.CCP1X=500;
CCP1CONbits.CCP1Y=500;
なら、当然、両方とも0です。
1bitの器(が2つ)しかない所にそれよりも多い数を入れると、繰り上がった分は消えます。
簡単に言えば2で割った余りです。
duty比が1なら判るでしょう?
CCP1CONbits.CCP1X=1;
CCP1CONbits.CCP1Y=1;
と書いて、Xの方が0になる筈ないですよね?
MABOさんの言う通りの処理が必要です。
まぁ、端数なので気付かない可能性が大きいですけど。
…と思ったら
> CCP1CONbits.DC1B = 500 && 0b11;
これは駄目です。そのサイトに書いてあったんですか?
これでは500の部分が0以外なら、CCP1CONbits.DC1Bには0b01が入ります。
0の場合は0です。
(本当は「&&」では無く「&」)
Re: Duty比を変えるプログラム
猛牛ロックさんへ
>簡単に言えば2で割った余りです。
なんだろうと思って、検索したら、 2で割った、余りの1を下から並べると、
2進数表現になるとありました。
でも、10進数に対応する2進数の上位8ビットを取り出すために
10進数÷4 とすればOKというプログラムは、、、
何か不思議な感じです。
オシログラフで確認できたらいいのでしょうけど、自分のレベルでは持っていません。
ただ、実験してみると、次第に明るくなって、次第に暗くなりました。
・10進数÷4 とすればOKというプログラム
CCPR1L = 500/4;
・参考したURL
https://arigato-lucky.at.webry.info/200710/article_17.html
※
今回も、一つ覚えたので、良かったです。
でなんですが、
PWM機能を使うために、ADRESというレジスタ
があるようなのですが、
これは、16F88にはあるのでしょうか?
自分としては見つかりませんでした。
良かったら、教えて下さい。
>簡単に言えば2で割った余りです。
なんだろうと思って、検索したら、 2で割った、余りの1を下から並べると、
2進数表現になるとありました。
でも、10進数に対応する2進数の上位8ビットを取り出すために
10進数÷4 とすればOKというプログラムは、、、
何か不思議な感じです。
オシログラフで確認できたらいいのでしょうけど、自分のレベルでは持っていません。
ただ、実験してみると、次第に明るくなって、次第に暗くなりました。
・10進数÷4 とすればOKというプログラム
CCPR1L = 500/4;
・参考したURL
https://arigato-lucky.at.webry.info/200710/article_17.html
※
今回も、一つ覚えたので、良かったです。
でなんですが、
PWM機能を使うために、ADRESというレジスタ
があるようなのですが、
これは、16F88にはあるのでしょうか?
自分としては見つかりませんでした。
良かったら、教えて下さい。
Re: Duty比を変えるプログラム
10進数、2進数、16進数などは単に人が識別するためのものです。
値としてはどれも同じです。
2進数「表現」とあるように、人にとって意味がある区別の仕方です。
> 10進数÷4 とすればOKというプログラムは、、、
10進数は隣の位が10倍、或いは1/10です。2進数の隣の位は同じ様に2倍、或いは1/2です。
○進数は○で、位が上がる、と言う事です。
> 実験してみると、次第に明るくなって、次第に暗くなりました。
10桁ある中で、下2桁ですから、ほとんど感じないでしょうね。
確認するならレジスタを読み込めば判りますよ。
> 自分としては見つかりませんでした。
検索すればすぐ判りますよ。
値としてはどれも同じです。
2進数「表現」とあるように、人にとって意味がある区別の仕方です。
> 10進数÷4 とすればOKというプログラムは、、、
10進数は隣の位が10倍、或いは1/10です。2進数の隣の位は同じ様に2倍、或いは1/2です。
○進数は○で、位が上がる、と言う事です。
> 実験してみると、次第に明るくなって、次第に暗くなりました。
10桁ある中で、下2桁ですから、ほとんど感じないでしょうね。
確認するならレジスタを読み込めば判りますよ。
> 自分としては見つかりませんでした。
検索すればすぐ判りますよ。
Re: Duty比を変えるプログラム

>CCP1CONbits.DC1B = 500 && 0b11;
マスク処理ですから,猛牛ロックさんのおっしゃるように,
&一つですね。
HPに書いてある記事は,なんの疑いもなく信じてしまうところがあるので,
気をつけないとだめですね。
https://tool-lab.com/make/pic-app-4/
2進数→←10進数の変換をよくエクセルを使ってやります。
ただ,今までは,小さな数でしたので,問題なくできましたが,
512だと備え付けの関数ではだめでした。
=DEC2BIN(N,n)
という関数ですが,
−513<N<512
1<=n<11
でした。符号も絡んでくるので,ちょっと頭痛くなります。
Re: Duty比を変えるプログラム
ADRESレジスターですが、
最初、ADRESで検索しても無かったので、
次に「A/D Result register」でも検索しましたが、
ADRESの表記は見当たりませんでした。
というのも、MPLAB IDEにADRESと書き込むと
エラーを起こしたので、
16F88の場合は、レジスターの名称が違うと思ったので、
探し始めました。
ADRESH、とADRESLの表記はあるのですが、
ADRESに対応する16F88版レジスターが見つかりません。
maboさん
2進数変換ですが、自分は下記サイトを利用しています。
https://hogehoge.tk/tool/number.html
最初、ADRESで検索しても無かったので、
次に「A/D Result register」でも検索しましたが、
ADRESの表記は見当たりませんでした。
というのも、MPLAB IDEにADRESと書き込むと
エラーを起こしたので、
16F88の場合は、レジスターの名称が違うと思ったので、
探し始めました。
ADRESH、とADRESLの表記はあるのですが、
ADRESに対応する16F88版レジスターが見つかりません。
maboさん
2進数変換ですが、自分は下記サイトを利用しています。
https://hogehoge.tk/tool/number.html
Re: Duty比を変えるプログラム

TRさん,2進数変換のHP紹介ありがとうございます。
>ADRESレジスターですが、
データシート見てみると,A/D変換の結果の格納で使われてるようですね。
ADRESH
ADRESL
の記述がデータしとの下の方にありました。
A/D変換の結果の格納に使うみたいですね。
http://physics.cocolog-nifty.com/weblog/2012/07/post-2147.html
にプログラムでてます。
Re: Duty比を変えるプログラム
maboさん有り難うございます
やはり、F88にはADRESレジスタが無かったようですね。
紹介先の管理人は、対処として、
unsigned int adconv() 関数を作っています。
でも、プログラムで
return (ADRESH<<8) + ADRESL
このように、+として10バイトを取り出せるのは、驚きました!?
16F88は面倒くさい。
もっと簡単なPICが欲しいです
やはり、F88にはADRESレジスタが無かったようですね。
紹介先の管理人は、対処として、
unsigned int adconv() 関数を作っています。
でも、プログラムで
return (ADRESH<<8) + ADRESL
このように、+として10バイトを取り出せるのは、驚きました!?
16F88は面倒くさい。
もっと簡単なPICが欲しいです
Re: Duty比を変えるプログラム
maboさん、こんばんは。
ご紹介くださった、頁のLチカを早速検証しました。
IDXの今バージョンに変更し、
「<」を半角にしたら、動作しました。
で、以下のプログラムですが、
今までと、ちと違う書き方をしています。
For文です。
{}内に、今までは、処理する内容を書いてきましたが、
空欄の場合は、単に++の足し算をするだけ、
つまり、時間を稼ぐだけのことになるのでしょうか?
http://physics.cocolog-nifty.com/weblog/2012/07/post-2147.html
RB3 = 1;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
RB3 = 0;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
}
ご紹介くださった、頁のLチカを早速検証しました。
IDXの今バージョンに変更し、
「<」を半角にしたら、動作しました。
で、以下のプログラムですが、
今までと、ちと違う書き方をしています。
For文です。
{}内に、今までは、処理する内容を書いてきましたが、
空欄の場合は、単に++の足し算をするだけ、
つまり、時間を稼ぐだけのことになるのでしょうか?
http://physics.cocolog-nifty.com/weblog/2012/07/post-2147.html
RB3 = 1;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
RB3 = 0;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
}
Re: Duty比を変えるプログラム
TRさん,こんばんは。
>空欄の場合は、単に++の足し算をするだけ、
>つまり、時間を稼ぐだけのことになるのでしょうか?
そうだと思います。
意味的には,
__delay_ms();
と同じような処理をすることになると思いますが,
確か,__delay_ms() では,括弧の中に変数は入れられなかった,ような気がします。
それで,こんな風にしたのかなと思います。
変数が入れられれば __delay_ms() 使えばいいのでしょうけど。
>空欄の場合は、単に++の足し算をするだけ、
>つまり、時間を稼ぐだけのことになるのでしょうか?
そうだと思います。
意味的には,
__delay_ms();
と同じような処理をすることになると思いますが,
確か,__delay_ms() では,括弧の中に変数は入れられなかった,ような気がします。
それで,こんな風にしたのかなと思います。
変数が入れられれば __delay_ms() 使えばいいのでしょうけど。
Re: Duty比を変えるプログラム
> F88にはADRESレジスタが無かったようですね。
え?まぁ、無いとも言えますけど、あるとも言えます。
ADRESHとADRESLがADRESです。
10ビット性能のADCなので、必然的に2つに分かれます。
> 16F88は面倒くさい。
いやいや、16F88は使いやすさで生き残っているマイコンですよ。
ウィンドウズで言えばXPみたいなものかなぁ。
最近のものは覚えることが2〜3倍程度あります。16F19155のデータシートなんて735ページまであります。
頭に来るのは、それ以外にエラッタのデータシートがあったり、データシートのバージョンで重要な事項が
載っていなかったりすることです。
RB3 = 1;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
RB3 = 0;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
}
はあまり参考にしない方が良いです。
動かしてみないとどうなるか判らないコードです。
本人も判っていなかったのでは?
やりたかったのは
while (1){
tmp = adconv();
RB3 = 1;
__delay_ms(tmp);
RB3 = 0;
__delay_ms(tmp);
}
のような事だと思いますけど、
(__delay_ms()の引数に変数を入れては駄目ですけど)
for(i = 0; i<tmp; i++){}
の部分は
volatile unsigned int i;
とiを宣言していないと省略される場合があります。
※内容が{}と空だからです。
そして、その次の
;
は単独です。for文とは関係ありません。
そして、
__delay_ms(2);
も単独です。
つまり、3つの何もしない命令を並べています。
通常は
RB3 = 1;
for(i = 0; i<tmp; i++)__delay_ms(1);
RB3 = 0;
for(i = 0; i<tmp; i++)__delay_ms(1);
です。
え?まぁ、無いとも言えますけど、あるとも言えます。
ADRESHとADRESLがADRESです。
10ビット性能のADCなので、必然的に2つに分かれます。
> 16F88は面倒くさい。
いやいや、16F88は使いやすさで生き残っているマイコンですよ。
ウィンドウズで言えばXPみたいなものかなぁ。
最近のものは覚えることが2〜3倍程度あります。16F19155のデータシートなんて735ページまであります。
頭に来るのは、それ以外にエラッタのデータシートがあったり、データシートのバージョンで重要な事項が
載っていなかったりすることです。
RB3 = 1;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
RB3 = 0;
for(i = 0; i<tmp; i++){};
__delay_ms(2);
}
はあまり参考にしない方が良いです。
動かしてみないとどうなるか判らないコードです。
本人も判っていなかったのでは?
やりたかったのは
while (1){
tmp = adconv();
RB3 = 1;
__delay_ms(tmp);
RB3 = 0;
__delay_ms(tmp);
}
のような事だと思いますけど、
(__delay_ms()の引数に変数を入れては駄目ですけど)
for(i = 0; i<tmp; i++){}
の部分は
volatile unsigned int i;
とiを宣言していないと省略される場合があります。
※内容が{}と空だからです。
そして、その次の
;
は単独です。for文とは関係ありません。
そして、
__delay_ms(2);
も単独です。
つまり、3つの何もしない命令を並べています。
通常は
RB3 = 1;
for(i = 0; i<tmp; i++)__delay_ms(1);
RB3 = 0;
for(i = 0; i<tmp; i++)__delay_ms(1);
です。
Re: Duty比を変えるプログラム
因みに、そのサイトのプログラムをArduinoでやると
void setup() {
pinMode(13,OUTPUT);
}
void loop() {
word tmp=analogRead(A0)/4;
digitalWrite(13,HIGH);
delay(tmp);
digitalWrite(13,LOW);
delay(tmp);
}
だけです。データシートを見る必要はありません。
初心者はリファレンスのページは見る必要あるでしょうけど。
回路もLEDは13番ピンに付いているので、
ボリュームの配線だけ5V、A0ピン、GNDと繋げば直ぐに書き込んで試せます。
そう言えば、そのサイトの人も、今はArduinoに鞍替えした?のかも知れません。
http://physics.cocolog-nifty.com/weblog/2018/06/arduino14-392d.html
マルチボートも試していたようです。
ついでに、ボリュームに合わせて明るさを変えるスケッチだと更に簡単です。
void setup() {
}
void loop() {
analogWrite(3,analogRead(A0)/4);
delay(10);
}
3番ピン-LED、A0ピン-ボリュームです。
void setup() {
pinMode(13,OUTPUT);
}
void loop() {
word tmp=analogRead(A0)/4;
digitalWrite(13,HIGH);
delay(tmp);
digitalWrite(13,LOW);
delay(tmp);
}
だけです。データシートを見る必要はありません。
初心者はリファレンスのページは見る必要あるでしょうけど。
回路もLEDは13番ピンに付いているので、
ボリュームの配線だけ5V、A0ピン、GNDと繋げば直ぐに書き込んで試せます。
そう言えば、そのサイトの人も、今はArduinoに鞍替えした?のかも知れません。
http://physics.cocolog-nifty.com/weblog/2018/06/arduino14-392d.html
マルチボートも試していたようです。
ついでに、ボリュームに合わせて明るさを変えるスケッチだと更に簡単です。
void setup() {
}
void loop() {
analogWrite(3,analogRead(A0)/4);
delay(10);
}
3番ピン-LED、A0ピン-ボリュームです。
Re: Duty比を変えるプログラム
TRさん,猛牛ロックさん,こんにちは。
猛牛ロックさん,いろいろありがとうございます。
私もちょっとだけ,Arduinoいじりましたが,
PICに比べると親切過ぎる感じですね。
それから,
analogRead(A0)/4
ですが,たんに,4で割るのか,それとも上位の4ビットを
とりだすのでしょうか。
この辺の仕様がちょっと分からないかなと思います。
猛牛ロックさん,いろいろありがとうございます。
私もちょっとだけ,Arduinoいじりましたが,
PICに比べると親切過ぎる感じですね。
それから,
analogRead(A0)/4
ですが,たんに,4で割るのか,それとも上位の4ビットを
とりだすのでしょうか。
この辺の仕様がちょっと分からないかなと思います。
Re: Duty比を変えるプログラム
> たんに,4で割るのか,それとも上位の4ビットを
> とりだすのでしょうか。
単に4で割っています。元が10ビットなので、上位8ビットを取り出しています。
※analogWriteは8ビット指定、analogReadは10ビット読出し
> とりだすのでしょうか。
単に4で割っています。元が10ビットなので、上位8ビットを取り出しています。
※analogWriteは8ビット指定、analogReadは10ビット読出し
Re: Duty比を変えるプログラム
みなさんこんばんは。
ADRESですが、
ようやく分かりました。
やはりレジスタとしては存在していないそうです。
でも、
12F1822には、ADRESとすれば、10ビットとして取り扱うことが出来るそうです。
PIC12F1822にはある機能ですが、88や627Aにはなさそうです。
PIC12F1822日本語バージョンのデータシート149頁上の4行目に
ADRES<9:2>と表記されています。
この独特の表記方法からすると、
9から2ビットまでをひとくくりにする、、、←自分的理解ですが。
それから、maboさんのご紹介下さったプログラムですが、
以下のように変えました。
動作しています。
気になった点は、
char str[7]; ← 配列、プログラムの何処にもないので不要。
RB3 = 1;
for(i = 0; i<tmp; i++){};
上の2行です。RB3を{}の中に入れました
後、猛牛ロックさんの言われるように ; も不要ですね。
話は戻りますが、この管理人さんも88を使っていますが、
12F1822にすれば、電圧読み取りの下記プログラム
記
return (ADRESH<<8) + ADRESL;
上記を
return(ADRES); と出来るはずです。
拘る理由
教科書では
return(○○)となっているからです。
本当に上記の記載方法で上位8ビットと下位2ビットがきちんと並んでいるのだろうかと??
変更後
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 4000000
//プロトタイプ宣言
unsigned int adconv();
//変数宣言
unsigned int tmp;
unsigned int i;
void main()
{
OSCCON = 0b0110000; // 内蔵OSC 4MHz
// ADコンバータ設定
ADCON0 = 0b01000001; // RA0をADコンバータピンにし、ADコンバータ機能をEbableに
ADCON1 = 0b10000000; // 結果数値は右寄せ、基準電圧はVDD
ANSEL = 0b00000001; // RA0(AN0)はアナログで読み取りピン
TRISA = 0b00000001; // RA0(AN0)はアナログで読み取りピン(入力)
TRISB = 0x00; // すべて出力
PORTA = 0x00;
PORTB = 0x00;
while (1)
{
tmp = adconv();
for(i = 0; i<tmp; i++){RB3=1;}
__delay_ms(2);
for(i = 0; i<tmp; i++){RB3=0;};
__delay_ms(2);
}
}
unsigned int adconv()
{
__delay_us(20); //アクィジョン時間 20us
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
return (ADRESH<<8)+ ADRESL;
}
ADRESですが、
ようやく分かりました。
やはりレジスタとしては存在していないそうです。
でも、
12F1822には、ADRESとすれば、10ビットとして取り扱うことが出来るそうです。
PIC12F1822にはある機能ですが、88や627Aにはなさそうです。
PIC12F1822日本語バージョンのデータシート149頁上の4行目に
ADRES<9:2>と表記されています。
この独特の表記方法からすると、
9から2ビットまでをひとくくりにする、、、←自分的理解ですが。
それから、maboさんのご紹介下さったプログラムですが、
以下のように変えました。
動作しています。
気になった点は、
char str[7]; ← 配列、プログラムの何処にもないので不要。
RB3 = 1;
for(i = 0; i<tmp; i++){};
上の2行です。RB3を{}の中に入れました
後、猛牛ロックさんの言われるように ; も不要ですね。
話は戻りますが、この管理人さんも88を使っていますが、
12F1822にすれば、電圧読み取りの下記プログラム
記
return (ADRESH<<8) + ADRESL;
上記を
return(ADRES); と出来るはずです。
拘る理由
教科書では
return(○○)となっているからです。
本当に上記の記載方法で上位8ビットと下位2ビットがきちんと並んでいるのだろうかと??
変更後
// CONFIG1
#pragma config FOSC = INTOSCIO // Oscillator Selection bits (INTRC oscillator; port I/O function on both RA6/OSC2/CLKO pin and RA7/OSC1/CLKI pin)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF // Power-up Timer Enable bit (PWRT disabled)
#pragma config MCLRE = ON // RA5/MCLR/VPP Pin Function Select bit (RA5/MCLR/VPP pin function is MCLR)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF // Data EE Memory Code Protection bit (Code protection off)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off)
#pragma config CCPMX = RB3 // CCP1 Pin Selection bit (CCP1 function on RB3)
#pragma config CP = OFF // Flash Program Memory Code Protection bit (Code protection off)
// CONFIG2
#pragma config FCMEN = OFF // Fail-Safe Clock Monitor Enable bit (Fail-Safe Clock Monitor enabled)
#pragma config IESO = OFF // Internal External Switchover bit (Internal External Switchover mode enabled)
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// クロック周波数指定
// (__delay_ms()関数が必要としているため)
#define _XTAL_FREQ 4000000
//プロトタイプ宣言
unsigned int adconv();
//変数宣言
unsigned int tmp;
unsigned int i;
void main()
{
OSCCON = 0b0110000; // 内蔵OSC 4MHz
// ADコンバータ設定
ADCON0 = 0b01000001; // RA0をADコンバータピンにし、ADコンバータ機能をEbableに
ADCON1 = 0b10000000; // 結果数値は右寄せ、基準電圧はVDD
ANSEL = 0b00000001; // RA0(AN0)はアナログで読み取りピン
TRISA = 0b00000001; // RA0(AN0)はアナログで読み取りピン(入力)
TRISB = 0x00; // すべて出力
PORTA = 0x00;
PORTB = 0x00;
while (1)
{
tmp = adconv();
for(i = 0; i<tmp; i++){RB3=1;}
__delay_ms(2);
for(i = 0; i<tmp; i++){RB3=0;};
__delay_ms(2);
}
}
unsigned int adconv()
{
__delay_us(20); //アクィジョン時間 20us
ADCON0bits.GO_DONE= 1; //AD変換開始
while(ADCON0bits.GO_DONE); //変換完了待ち
return (ADRESH<<8)+ ADRESL;
}
Re: Duty比を変えるプログラム
10ビットのADRESが「ADRES」で通じるかどうかはコンパイラの仕様です。
ハードとは関係ありません。
※実際にはハードには何の名前もありません。ADRESLやPORTAも単にそのアドレスにあるレジスタの説明上付けた名前です。
その名前で、そのアドレスの内容を制御できるようにコンパイラが関連付けています。
なので、データシートに載っているレジスタ名がそのコンパイラで通じるかさえも実際は判りません。
そのコンパイラが通じる仕様で、提供してくれれば使えばいいですし、
そうでなければ自分で処理する事になります。
勿論、コンパイラの仕様ですから、コンパイラバージョンでも変わるかもしれません。
> ADRES<9:2>
これは、ADRESの第9ビット〜第2ビットまで、と言う事です。
逆に言えば、ADRESの第0、第1ビットは別の所にある、と言う事です。
普通、8ビットしか使わない場合は左詰め(でHIGHしか読まない)、10ビット使う場合は右詰めにします。
> for(i = 0; i<tmp; i++){RB3=1;}
> __delay_ms(2);
> for(i = 0; i<tmp; i++){RB3=0;};
> __delay_ms(2);
> }
これ自体は間違いでは無いです。勿論、元もプログラムでも間違いではありません。
ただ、思っている動作を記述しているのかどうか?です。
普通、この時にやりたいことは
RB3を2*tmp[ms]間HIGH、→RB3を2*tmp[ms]間LOWにする
という考えで作りますよね?
※なぜそうするかというと、__delay_ms(tmp*2);と__delay_ms関数では変数を引数に出来ない仕様だからです。
でも今回のだと、
最初のfor文ではtmp回RB3=1が実行されます。(それでfor文終わり)
次に__delay_ms(2);が1回行われます。
次のfor文でtmp回RB3=1が実行されます。(それでfor文終わり)
(取り忘れでしょうけど「;」が1回行われる?)
で、次に__delay_ms(2);が1回行われます。
つまり、時間の増減に__delay_ms(2);は関わっていません。
{}内にRB3=が入っているので、コンパイラに省略される事は無いでしょう。
> 教科書では
> return(○○)となっているからです。
> 本当に上記の記載方法で上位8ビットと下位2ビットがきちんと並んでいるのだろうかと??
return文には()は要りません。つまり、関数で使うmain「()」の意味とは違います。こちらは引数を入れる場所、という役割があります。
return文に付いているのは、単純に計算時に使う()の意味です。必要以上に付けてもエラーではありません。
return (1+2+3)/4;
と書いても、6/4=「1」が返ります。
「unsigned int」 adconv()
と戻り値が負号なし16ビット整数なので、
return (ADRESH<<8)+ ADRESL;
で、ちゃんと16ビットの整数が返ります。
※勿論、変換結果を右詰めした場合です。
後は、その受け入れ先もそれが入るだけの大きさが必要です。
もし入らない大きさなら、あふれ出た桁は消えます。
ハードとは関係ありません。
※実際にはハードには何の名前もありません。ADRESLやPORTAも単にそのアドレスにあるレジスタの説明上付けた名前です。
その名前で、そのアドレスの内容を制御できるようにコンパイラが関連付けています。
なので、データシートに載っているレジスタ名がそのコンパイラで通じるかさえも実際は判りません。
そのコンパイラが通じる仕様で、提供してくれれば使えばいいですし、
そうでなければ自分で処理する事になります。
勿論、コンパイラの仕様ですから、コンパイラバージョンでも変わるかもしれません。
> ADRES<9:2>
これは、ADRESの第9ビット〜第2ビットまで、と言う事です。
逆に言えば、ADRESの第0、第1ビットは別の所にある、と言う事です。
普通、8ビットしか使わない場合は左詰め(でHIGHしか読まない)、10ビット使う場合は右詰めにします。
> for(i = 0; i<tmp; i++){RB3=1;}
> __delay_ms(2);
> for(i = 0; i<tmp; i++){RB3=0;};
> __delay_ms(2);
> }
これ自体は間違いでは無いです。勿論、元もプログラムでも間違いではありません。
ただ、思っている動作を記述しているのかどうか?です。
普通、この時にやりたいことは
RB3を2*tmp[ms]間HIGH、→RB3を2*tmp[ms]間LOWにする
という考えで作りますよね?
※なぜそうするかというと、__delay_ms(tmp*2);と__delay_ms関数では変数を引数に出来ない仕様だからです。
でも今回のだと、
最初のfor文ではtmp回RB3=1が実行されます。(それでfor文終わり)
次に__delay_ms(2);が1回行われます。
次のfor文でtmp回RB3=1が実行されます。(それでfor文終わり)
(取り忘れでしょうけど「;」が1回行われる?)
で、次に__delay_ms(2);が1回行われます。
つまり、時間の増減に__delay_ms(2);は関わっていません。
{}内にRB3=が入っているので、コンパイラに省略される事は無いでしょう。
> 教科書では
> return(○○)となっているからです。
> 本当に上記の記載方法で上位8ビットと下位2ビットがきちんと並んでいるのだろうかと??
return文には()は要りません。つまり、関数で使うmain「()」の意味とは違います。こちらは引数を入れる場所、という役割があります。
return文に付いているのは、単純に計算時に使う()の意味です。必要以上に付けてもエラーではありません。
return (1+2+3)/4;
と書いても、6/4=「1」が返ります。
「unsigned int」 adconv()
と戻り値が負号なし16ビット整数なので、
return (ADRESH<<8)+ ADRESL;
で、ちゃんと16ビットの整数が返ります。
※勿論、変換結果を右詰めした場合です。
後は、その受け入れ先もそれが入るだけの大きさが必要です。
もし入らない大きさなら、あふれ出た桁は消えます。
PIC書き込み自作電源

PIC書き込み電源の記事を見かけて、
自分もやってみました。
書き込みが成功
16F88でLチカもできました。(*^^*)
猛牛ロックさん
>No.1181の添付画像はF88のものでもF627Aの物でもないですよね?
違うデバイスのデータシートは意味がありません。
そうです。
似たようなものだろうと思って、やってしまいました。
16F88のデータシートの日本語があればよいのですが。
グーグルの翻訳で
やっと、PIC3の端子に対応する16F88のピンを見つけまして、
表題の自作電源が上手くいきました。
写真がいい物が取れなくて、つぎはぎになってしまいました。
修正だらけで済みません。
Re: PIC書き込み自作電源
Re: PIC書き込み自作電源
TRさん,猛牛ロックさん,こんばんは。
ちょっと出番がなかったので,ひたすらROMってました。
>どうも、MPLAB 純正PIC3の方は、電源が弱いんですかね〜。
私もそんな気がします。
書き込み失敗するのは,PIC3の電源関係の設定を忘れたり,
USBポートからの電源供給が足りなかったりのような気がします。
以前,電源関係の改良の記事をみたような気がしてますが,
該当のHP探せませんでした。
ちょっと出番がなかったので,ひたすらROMってました。
>どうも、MPLAB 純正PIC3の方は、電源が弱いんですかね〜。
私もそんな気がします。
書き込み失敗するのは,PIC3の電源関係の設定を忘れたり,
USBポートからの電源供給が足りなかったりのような気がします。
以前,電源関係の改良の記事をみたような気がしてますが,
該当のHP探せませんでした。
Re: PIC書き込み自作電源
プチ縁覚
笑われるような話なのです。
猛牛ロックさんとのやり取りを通じて、
今の自分は、やはり、arduinoには及ばないというのが良く分かりました。
表題のプチ縁覚ですが、六道の世界で使われる単語なんですが、あまり気にしないで下さい。
C言語と言っても色々あるんだな〜という感じです。
でなんですが、
自分的には、 PICのプログラミングで以下の事が分かりました。
動作は、何もしないで続けるです。
while(RA5==1){} 左の動作{}内を空欄にすれば、何もしないという意味になって、
RB=1 の動作を続けることになる。
つまり、PICに電圧がかかったという状態表示がLEDが点灯することによって把握できるという事です。
// LEDのピンを1にして、LEDを点灯する
RB7=1;
//SWがONにならなければそのまま
while(RA5==1){}
それから、maboさんも、PIC3の自作電源をやってみて下さい。
安定して書き込みが出来る様になります。
追記
間欠プログラムを一部修正
修正内容
PICに通電をお知らせ用にRA4を点灯させ、
MODEが変わったRA4を消灯させる。
// メイン関数
void main (void){
OSCCON = 0b01110010; // 内蔵 8MHz
ANSEL = 0; // 全アナログOFF
unsigned int DousaTime,KanketuTime; //宣言。#defineからこちらに変更
char change;
PORTA=0;//出力のピンを初期設定。
// 入出力設定
TRISA = 0b01000011;
TRISB = 0b11111111;//負論理ロータリースイッチ2個。RB0-3が動作時間、RB4-7が間欠時間
OPTION_REG=0b00000000;//ポートBプルアップ
unsigned int TIME[]={60,100,150,200,300,600,900,1200,1800,2400,3000,6000,9000,12000,15000,180};//unsigned intで65000まで
//PICに電源が入ったお知らせRA4を点灯
RA4=0;
//MODEが変化なければRA4はそのまま点灯
while(RA0==1&&RA1==1){}
//MODEが変わったらRA4は消灯
RA4=1;
// 永久ループ
while(1){
MODE=PORTA&0b11;
__delay_ms(50);
while(MODE!=(PORTA&0b11)){ //変化があればウェイト、無ければ確定
MODE=PORTA&0b11;
__delay_ms(50);
以下 略
プチハッピー
笑われるような話なのです。
猛牛ロックさんとのやり取りを通じて、
今の自分は、やはり、arduinoには及ばないというのが良く分かりました。
表題のプチ縁覚ですが、六道の世界で使われる単語なんですが、あまり気にしないで下さい。
C言語と言っても色々あるんだな〜という感じです。
でなんですが、
自分的には、 PICのプログラミングで以下の事が分かりました。
動作は、何もしないで続けるです。
while(RA5==1){} 左の動作{}内を空欄にすれば、何もしないという意味になって、
RB=1 の動作を続けることになる。
つまり、PICに電圧がかかったという状態表示がLEDが点灯することによって把握できるという事です。
// LEDのピンを1にして、LEDを点灯する
RB7=1;
//SWがONにならなければそのまま
while(RA5==1){}
それから、maboさんも、PIC3の自作電源をやってみて下さい。
安定して書き込みが出来る様になります。
追記
間欠プログラムを一部修正
修正内容
PICに通電をお知らせ用にRA4を点灯させ、
MODEが変わったRA4を消灯させる。
// メイン関数
void main (void){
OSCCON = 0b01110010; // 内蔵 8MHz
ANSEL = 0; // 全アナログOFF
unsigned int DousaTime,KanketuTime; //宣言。#defineからこちらに変更
char change;
PORTA=0;//出力のピンを初期設定。
// 入出力設定
TRISA = 0b01000011;
TRISB = 0b11111111;//負論理ロータリースイッチ2個。RB0-3が動作時間、RB4-7が間欠時間
OPTION_REG=0b00000000;//ポートBプルアップ
unsigned int TIME[]={60,100,150,200,300,600,900,1200,1800,2400,3000,6000,9000,12000,15000,180};//unsigned intで65000まで
//PICに電源が入ったお知らせRA4を点灯
RA4=0;
//MODEが変化なければRA4はそのまま点灯
while(RA0==1&&RA1==1){}
//MODEが変わったらRA4は消灯
RA4=1;
// 永久ループ
while(1){
MODE=PORTA&0b11;
__delay_ms(50);
while(MODE!=(PORTA&0b11)){ //変化があればウェイト、無ければ確定
MODE=PORTA&0b11;
__delay_ms(50);
以下 略
プチハッピー
Re: PIC書き込み自作電源
TRさん,猛牛ロックさん,こんばんは。
TRさん,PICKIT3の情報ありがとうございます。
私は,電源供給のUSBポートを使って,書き込みが
できたので,それ使おうかなと思います。
arduino私もちょっとだけいじってみましたが,Cライクな言語
みたいなので,大丈夫なような気がしますが,APIというか,
覚えるのが結構あるようなきがしました。
ただ,周辺機器は,いろんなものがあるので,その点は便利かなと思いました。
それから,TRも新しいPCを物色中のゆですが,私も,悪い虫が起きて,
MACがほしくなりました。
iPhoneのアプリを使って遊びたくなりました。
公開しなくて自分の実機だけで確認するのには,費用はかからないようなので。
公開すると年間約1万円ぐらいかかるようです。
TRさん,PICKIT3の情報ありがとうございます。
私は,電源供給のUSBポートを使って,書き込みが
できたので,それ使おうかなと思います。
arduino私もちょっとだけいじってみましたが,Cライクな言語
みたいなので,大丈夫なような気がしますが,APIというか,
覚えるのが結構あるようなきがしました。
ただ,周辺機器は,いろんなものがあるので,その点は便利かなと思いました。
それから,TRも新しいPCを物色中のゆですが,私も,悪い虫が起きて,
MACがほしくなりました。
iPhoneのアプリを使って遊びたくなりました。
公開しなくて自分の実機だけで確認するのには,費用はかからないようなので。
公開すると年間約1万円ぐらいかかるようです。
Re: PIC書き込み自作電源
>arduino私もちょっとだけいじってみましたが,Cライクな言語
みたいなので,大丈夫なような気がしますが,APIというか,
覚えるのが結構あるようなきがしました。
それなんですよ。
自分の場合、C言語初心者なので、
先ずは、キットで遊ぼうの教本をマスターしたいと思っています。
現在は、PWMを読み始めました。
ただ、教本のPICは16F627Aなので、16F88には適合しない部分もあります。
なので、データーシートが日本語でならばならと、つくずく思います。
教本を基に88のデータシートを見ていますが難しいです(-_-;)
デスクトップですが、ついにポチット行きました。
DELLです。
自分の程度は知れていますので、
ロースペックです。
でも、楽しみです。
24インチなので、爺の自分にも優しいです。
それに、これが最後のPCでしょう。(笑い
みたいなので,大丈夫なような気がしますが,APIというか,
覚えるのが結構あるようなきがしました。
それなんですよ。
自分の場合、C言語初心者なので、
先ずは、キットで遊ぼうの教本をマスターしたいと思っています。
現在は、PWMを読み始めました。
ただ、教本のPICは16F627Aなので、16F88には適合しない部分もあります。
なので、データーシートが日本語でならばならと、つくずく思います。
教本を基に88のデータシートを見ていますが難しいです(-_-;)
デスクトップですが、ついにポチット行きました。
DELLです。
自分の程度は知れていますので、
ロースペックです。
でも、楽しみです。
24インチなので、爺の自分にも優しいです。
それに、これが最後のPCでしょう。(笑い
Re: PIC書き込み自作電源
TRさん,猛牛ロックさん,おはようございます。
>デスクトップですが、ついにポチット行きました。
>DELLです。
ポチりましたか・・・・。
>それに、これが最後のPCでしょう。(笑い
最後の最初かもしれませんよ。(笑)先は,ながいですよ。(笑)
最近は,デスクトップは使っていませんが,
しばらく前は,デスクトップは自分で組み立ててました。
ただ,XPになって,インストールに制限が出て,
一つのソフトでインストールできる台数に限りがでて,
それで,自作は,下火になっりました。
デスクトップは,拡張製がいいですよね。
グラフィックボードを増設したり,その他,自分なりに拡張できると思います。
何よりも,シリアルポートの安定性がいいのが魅力ですね。
ノートには,ほとんどシリアルポートはついていませんからね,最近のものは。
PICですが,16F627Aは確か16F88の上位互換機だったような気がします。
細かい部分では,いろいろ違いがあるのでしょうが,そんなに気にしなくてもいいようなかんじですが。
もっとも,CONFIG等の記載は,若干違うのかもしれませんね。
私も昨日ポチりましたが,ポチったお店が,改装中で,
購入の手続きできませんでした。
>デスクトップですが、ついにポチット行きました。
>DELLです。
ポチりましたか・・・・。
>それに、これが最後のPCでしょう。(笑い
最後の最初かもしれませんよ。(笑)先は,ながいですよ。(笑)
最近は,デスクトップは使っていませんが,
しばらく前は,デスクトップは自分で組み立ててました。
ただ,XPになって,インストールに制限が出て,
一つのソフトでインストールできる台数に限りがでて,
それで,自作は,下火になっりました。
デスクトップは,拡張製がいいですよね。
グラフィックボードを増設したり,その他,自分なりに拡張できると思います。
何よりも,シリアルポートの安定性がいいのが魅力ですね。
ノートには,ほとんどシリアルポートはついていませんからね,最近のものは。
PICですが,16F627Aは確か16F88の上位互換機だったような気がします。
細かい部分では,いろいろ違いがあるのでしょうが,そんなに気にしなくてもいいようなかんじですが。
もっとも,CONFIG等の記載は,若干違うのかもしれませんね。
私も昨日ポチりましたが,ポチったお店が,改装中で,
購入の手続きできませんでした。
config
https://tool-lab.com/make/pic-basic-21/
このサイトを良く見たいと思います。
このサイトを良く見たいと思います。
Re: config

http://rikei60.web.fc2.com/toybox/sokkyo/16F88conf.htm
間欠プログラムでの制御として間欠をLEDでモニターしているのですが、
下記のように変更の結果結果、物凄くLEDが点滅するようになりました。
点滅の速度を16F627と同じようにするにはどうしたらよいか教えて頂けませんか?
宜しくお願いします。
記
88の場合、内部クロックが627Aと違って、内部クロックを8MHzにしました。
それと、
627Aの時 OSCF=1;//内蔵発振器 4MHz使用に設定 したのを
88 用に、OSCCON = 0x70; このようにしました。
写真ですが、
IDXの設定ですが、627Aから、簡単に88に出来ました。
Re: config
駆動速度の分(2倍)だけ早くなっているのか、「物凄くLEDが点滅」では
判断できません。
通常、delay関数を用いた時間軸なら_XTAL_FREQの値を変えれば
コンパイラがその時間に変えてくれます。
それ以外の調整法なら、駆動周波数を4MHzにすることで解消するかもしれません。
ただし、周辺機能を使っている場合は、レジスタの内容が違いますから確認する必要があります。
(勿論、8MHz駆動で、それに合わせて書き換えても良いです)
「物凄く」と言う事なら、もしかしたら、再起動を繰り返してるのかもしれません。
まずは、まっさらものにdelayを使った点滅プログラムを書いて、期待した周波数で
起動しているか確認した方が良いです。
判断できません。
通常、delay関数を用いた時間軸なら_XTAL_FREQの値を変えれば
コンパイラがその時間に変えてくれます。
それ以外の調整法なら、駆動周波数を4MHzにすることで解消するかもしれません。
ただし、周辺機能を使っている場合は、レジスタの内容が違いますから確認する必要があります。
(勿論、8MHz駆動で、それに合わせて書き換えても良いです)
「物凄く」と言う事なら、もしかしたら、再起動を繰り返してるのかもしれません。
まずは、まっさらものにdelayを使った点滅プログラムを書いて、期待した周波数で
起動しているか確認した方が良いです。
Re: config
以下のとおりに再度、コンパイルしたら、
上手くいきました。
猛牛ロックさんの言われた意味かどうか分かりませんが、
IDXのプログラム書き込み画面を一度クリアーにして、
コピペし直しました。
#include <xc.h>
#pragma config WDTE=OFF,PWRTE=OFF,CP=OFF,BOREN=OFF,DEBUG=OFF
#pragma config LVP=OFF,MCLRE=ON,CPD=OFF,WRT=OFF,FOSC=INTOSCIO
#pragma config FCMEN=ON,IESO=OFF
#define _XTAL_FREQ 8000000 // delay用
char Second(unsigned int sec);
#define On 1
#define Off 0
#define MACH_sig RA6 //machからの信号入力プルダウン
#define burowa RA2=RA7 //RA2:ブロワ出力 RA7:LED出力
#define vacuum RA3 //掃除機出力
unsigned char PORTB_save; //ポートBの保存用。(変更に対応するため)
unsigned char MODE; //セレクタ保存用
// メイン関数
void main (void){
OSCCON = 0x70; // 内蔵 8MHz
ANSEL = 0; // 全アナログOFF
unsigned int DousaTime,KanketuTime; //宣言。#defineからこちらに変更
char change;
PORTA=0;//出力のピンを初期設定。
// 入出力設定
TRISA = 0b01000011;
TRISB = 0b11111111;//負論理ロータリースイッチ2個。RB0-3が動作時間、RB4-7が間欠時間
OPTION_REG=0b00000000;//ポートBプルアップ
unsigned int TIME[]={60,100,150,200,300,600,900,1200,1800,2400,3000,6000,9000,12000,15000,180};//unsigned intで65000まで
// 永久ループ
while(1){
MODE=PORTA&0b11;
__delay_ms(50);
while(MODE!=(PORTA&0b11)){ //変化があればウェイト、無ければ確定
MODE=PORTA&0b11;
__delay_ms(50);
}
if(MODE==0||(MODE==3&&MACH_sig==0)){//OFF
burowa=Off;
vacuum=Off;
}
else if(MODE==1){//掃除機のみオン
burowa=Off;
vacuum=On;
}
else if(MODE==2||(MODE==3&&MACH_sig==1)){ //間欠
PORTB_save=PORTB;
DousaTime=TIME[PORTB_save&0b1111];
KanketuTime=TIME[PORTB_save>>4];
vacuum=On;__delay_ms(700);burowa=On;
change=Second(DousaTime); //動作時間分ウェイト
if(change==0){
burowa=Off;__delay_ms(500);vacuum=Off;
change=Second(KanketuTime); //間欠時間分ウェイト
}
}
}
}
// 待ち時間関数
char Second(unsigned int sec){//1S→0.1Sに変更。int secに1を入れると0.01秒ウェイト
while(sec >0){
if((MODE!=(PORTA&0b11))||(MODE==3&&MACH_sig==0)||(PORTB_save!=PORTB)) return 1;
//セレクタスイッチが変更された場合とMACH制御でのオフ信号、間欠時間の変更があった場合はwhileから抜ける
sec--;
__delay_ms(10); //この部分を1000→10に書き換えたので1s→0.01s
}
return 0;
}
これで、88でも動作したので、嬉しいです。
configもちょっとは分かりました。
C言語の場合
#pragma これがPICに仕事をさせるうえで、仕事のやり方を決めるようなものなんですね。
上手くいきました。
猛牛ロックさんの言われた意味かどうか分かりませんが、
IDXのプログラム書き込み画面を一度クリアーにして、
コピペし直しました。
#include <xc.h>
#pragma config WDTE=OFF,PWRTE=OFF,CP=OFF,BOREN=OFF,DEBUG=OFF
#pragma config LVP=OFF,MCLRE=ON,CPD=OFF,WRT=OFF,FOSC=INTOSCIO
#pragma config FCMEN=ON,IESO=OFF
#define _XTAL_FREQ 8000000 // delay用
char Second(unsigned int sec);
#define On 1
#define Off 0
#define MACH_sig RA6 //machからの信号入力プルダウン
#define burowa RA2=RA7 //RA2:ブロワ出力 RA7:LED出力
#define vacuum RA3 //掃除機出力
unsigned char PORTB_save; //ポートBの保存用。(変更に対応するため)
unsigned char MODE; //セレクタ保存用
// メイン関数
void main (void){
OSCCON = 0x70; // 内蔵 8MHz
ANSEL = 0; // 全アナログOFF
unsigned int DousaTime,KanketuTime; //宣言。#defineからこちらに変更
char change;
PORTA=0;//出力のピンを初期設定。
// 入出力設定
TRISA = 0b01000011;
TRISB = 0b11111111;//負論理ロータリースイッチ2個。RB0-3が動作時間、RB4-7が間欠時間
OPTION_REG=0b00000000;//ポートBプルアップ
unsigned int TIME[]={60,100,150,200,300,600,900,1200,1800,2400,3000,6000,9000,12000,15000,180};//unsigned intで65000まで
// 永久ループ
while(1){
MODE=PORTA&0b11;
__delay_ms(50);
while(MODE!=(PORTA&0b11)){ //変化があればウェイト、無ければ確定
MODE=PORTA&0b11;
__delay_ms(50);
}
if(MODE==0||(MODE==3&&MACH_sig==0)){//OFF
burowa=Off;
vacuum=Off;
}
else if(MODE==1){//掃除機のみオン
burowa=Off;
vacuum=On;
}
else if(MODE==2||(MODE==3&&MACH_sig==1)){ //間欠
PORTB_save=PORTB;
DousaTime=TIME[PORTB_save&0b1111];
KanketuTime=TIME[PORTB_save>>4];
vacuum=On;__delay_ms(700);burowa=On;
change=Second(DousaTime); //動作時間分ウェイト
if(change==0){
burowa=Off;__delay_ms(500);vacuum=Off;
change=Second(KanketuTime); //間欠時間分ウェイト
}
}
}
}
// 待ち時間関数
char Second(unsigned int sec){//1S→0.1Sに変更。int secに1を入れると0.01秒ウェイト
while(sec >0){
if((MODE!=(PORTA&0b11))||(MODE==3&&MACH_sig==0)||(PORTB_save!=PORTB)) return 1;
//セレクタスイッチが変更された場合とMACH制御でのオフ信号、間欠時間の変更があった場合はwhileから抜ける
sec--;
__delay_ms(10); //この部分を1000→10に書き換えたので1s→0.01s
}
return 0;
}
これで、88でも動作したので、嬉しいです。
configもちょっとは分かりました。
C言語の場合
#pragma これがPICに仕事をさせるうえで、仕事のやり方を決めるようなものなんですね。
Re: config
私が指摘したこととは、全く別の要因があったようです。
動いたという事は、それで大丈夫だと思います。
config自体は、プログラムとは無関係です。configの内容とプログラムの内容の整合性はチェックしません。(多分)
使う用途にあわせて、スイッチを設定するだけです。但し、マイコン毎に持っている機能が違うのでconfigの内容は異なります。
それはレジスタも同じです。
どのマイコン(PIC16F)でも
OSCCON = 0x70;
で内蔵8MHzを意味するのでもありませんし、
ANSEL = 0;
で、全アナログOFFを意味するのでもありません。
PICの仕様として、configはプログラムに記述して、プログラムの書き込み時に両方とも書き込みます。
#pragmaは、コンパイラの特別指令、という意味合いです。
動いたという事は、それで大丈夫だと思います。
config自体は、プログラムとは無関係です。configの内容とプログラムの内容の整合性はチェックしません。(多分)
使う用途にあわせて、スイッチを設定するだけです。但し、マイコン毎に持っている機能が違うのでconfigの内容は異なります。
それはレジスタも同じです。
どのマイコン(PIC16F)でも
OSCCON = 0x70;
で内蔵8MHzを意味するのでもありませんし、
ANSEL = 0;
で、全アナログOFFを意味するのでもありません。
PICの仕様として、configはプログラムに記述して、プログラムの書き込み時に両方とも書き込みます。
#pragmaは、コンパイラの特別指令、という意味合いです。
Re: config

#pragma config FOSC=INTOSCIO
この意味がやっと分かりました。
それと、C言語の場合、main関数の中でOSCCON = 0x70; // 内蔵 8MHz と書き込みますが
これとの関係もあるんですね。
クロック設定は、#pragmaで、PIC内部のクロックを使うと指示し、
main関数で、内部クロックの周波数を設定する、
といった具合に2段階で設定されているんですよね。
そして、OSCCON = 0x70;
これの意味ですが、
OSCCONは1バイト、つまり8ビットのレジスタで、写真の様に構成されているんですね。
つまり、OSCCONの8bitを設定してやらないと駄目。
具体的には、8MHzの場合は、
ビットを 01110000 このように設定する
16進数なら0x70となるわけですよね。
後の、ANSEL これは、PICをアナログ運転にするか、それともデジタル運転するかを決めるためのものです。
今回の間欠運転は、デジタル運転です。半固定抵抗を使うときにA/Dのアナログということなんでしょうね。
設定方法は、やはり、OSCCONとおなじで、
8ビットのレジスタに0と1を書き込んでやるようになります。
ANSEL = 0x00;
でなんですが、main関数内で、
OSCCONやANSELは、PICの初期化ということで、main関数内の最初に設定するようですね。
追記
分からいのが、猛牛ロックさんは、ANSELを627Aでは使わなかったけど88で使いましたよね
また、キットで遊ぼうの教本でもANSELを使っていません。
どのようにして使い分けの判断をするのでしょうか?
データシートなのでしょうか?
若しそうであれば、、、
Re: config
ANSELの設定の方法が分かりました。
データシートに有りました。
88は、デフォルトで1
627Aはデータシートに記載なし。
データシートに有りました。
88は、デフォルトで1
627Aはデータシートに記載なし。
Re: config
configのみで決まるもの、プログラミング出来るもの、或いは両者の組み合わせで決まるもの、と
色々な場合があります。
No.1181の添付画像はF88のものでもF627Aの物でもないですよね?
違うデバイスのデータシートは意味がありません。
偶然一致して、動作する事はあるでしょうけど。
OSCCONはおおよそ、オシレータ関係制御レジスタを纏めたもの、というだけの意味で、
多くのデバイスにありますけど、内容は異なります。
627A自体にアナログ・デジタルコンバータ(ADC)はありません。
なので、ANSELというレジスタは存在しません。
ADCはアナログ出力のセンサーだったり、ボリュームの読み取り等で使います。
間欠で、コードスイッチを使って、16ch?を読み取ったと思うのですけど、
その位なら、ADC1ピンで済ませることが出来ます。
PICの場合は通常、アナログ入力になっています。
GPIOとして使う場合はその処理が必要になります。
色々な場合があります。
No.1181の添付画像はF88のものでもF627Aの物でもないですよね?
違うデバイスのデータシートは意味がありません。
偶然一致して、動作する事はあるでしょうけど。
OSCCONはおおよそ、オシレータ関係制御レジスタを纏めたもの、というだけの意味で、
多くのデバイスにありますけど、内容は異なります。
627A自体にアナログ・デジタルコンバータ(ADC)はありません。
なので、ANSELというレジスタは存在しません。
ADCはアナログ出力のセンサーだったり、ボリュームの読み取り等で使います。
間欠で、コードスイッチを使って、16ch?を読み取ったと思うのですけど、
その位なら、ADC1ピンで済ませることが出来ます。
PICの場合は通常、アナログ入力になっています。
GPIOとして使う場合はその処理が必要になります。
Re: config
Re: config
それは当然F88のデータシートに載っているものなのでF88用です。
そこよりも、P40のOSCCONの説明ページを見てください。
そこに説明してある通りです。
凡そ、内部クロックの設定はIRCFとPLLの組み合わせで決まります。
ただし、OSCCONの中に、IRCFが3ビットで構成されているとか、4〜6bitの位置にあるとか、100にすれば1MHzになるとか、
そういった決まりはありません。
場合によってはOSCCONではない所にIRCFでは無い名前で内蔵クロックの設定が存在するかもしれません。
そこよりも、P40のOSCCONの説明ページを見てください。
そこに説明してある通りです。
凡そ、内部クロックの設定はIRCFとPLLの組み合わせで決まります。
ただし、OSCCONの中に、IRCFが3ビットで構成されているとか、4〜6bitの位置にあるとか、100にすれば1MHzになるとか、
そういった決まりはありません。
場合によってはOSCCONではない所にIRCFでは無い名前で内蔵クロックの設定が存在するかもしれません。
Re: config
Re: config
そうです。そのあたりの項目をよく読む必要があります。
特にNoteと書いてある欄にも重要事項が書かれていたりもします。
F88の場合はIRCF以外にも、OSCCONのbit 1-0 SCSの所でもちゃんと選択している必要があり、
更にCONFIGのFOSCの設定も必要です。
その3つがちゃんと選ばれて、内蔵1MHzになります。
特にNoteと書いてある欄にも重要事項が書かれていたりもします。
F88の場合はIRCF以外にも、OSCCONのbit 1-0 SCSの所でもちゃんと選択している必要があり、
更にCONFIGのFOSCの設定も必要です。
その3つがちゃんと選ばれて、内蔵1MHzになります。
Re: config
40頁の以下については分かりませんでした。
00 = Oscillator mode defined by FOSC<2:0>
01 = T1OSC is used for system clock
10 = Internal RC is used for system clock
11 = Reserved
参りました。
00 = Oscillator mode defined by FOSC<2:0>
01 = T1OSC is used for system clock
10 = Internal RC is used for system clock
11 = Reserved
参りました。
Re: config
そこに書いてある事も当然重要なのですけど、P39の説明がまた重要です。
つまり、P40の部分だけを見ても失敗しがちです。(例外とかが書いてあったりします)
PICを使いこなすにはデータシートを熟読する必要があります。
私の印象ではAVRはどちらかというと、ユーザーが使いやすい形に仕上げてありますけど、
PICは、全て操作できるように出しておくから、後は勝手にやってくれ、という感じです。
その説明自体は簡単です。
00だとFOSC(config)で設定した通りに起動します。
01だとT1OSCがシステムクロックとして使われます。
10だと内蔵RCがシステムクロックとして使われます。
11はリザーブ(ここは使わない)
です。
つまり、P40の部分だけを見ても失敗しがちです。(例外とかが書いてあったりします)
PICを使いこなすにはデータシートを熟読する必要があります。
私の印象ではAVRはどちらかというと、ユーザーが使いやすい形に仕上げてありますけど、
PICは、全て操作できるように出しておくから、後は勝手にやってくれ、という感じです。
その説明自体は簡単です。
00だとFOSC(config)で設定した通りに起動します。
01だとT1OSCがシステムクロックとして使われます。
10だと内蔵RCがシステムクロックとして使われます。
11はリザーブ(ここは使わない)
です。
マルチボード 例題3

実際にはビット操作の方が安全です。
というのはポート操作はちゃんとマスク処理をしないと、「その他のピン」にも影響を及ぼします。
その点、ビット操作の方は、既に関数自体に、「その他のピン」には影響を及ぼさない様になっているので、
初心者向け、と言えると思います。
で、今回の例題ですけど、似たようなものを3つ考えました。
回答はまだ考え中(1つ2つなら、直ぐにできるのですけど)です。
今回はLEDのバラバラな制御です。まぁ、通常、課題1や2のように4つを同じ様に
点滅させることなんてありませんから、こちらがより普通の制御になります。
8つ位LEDがあればもっと面白い例があるのでしょうけど、思いつきませんでした。
「こんな形をプログラムしたい」というのがあれば言ってください。
重要な事は、頭に浮かんだものをプログラム出来るようになることです。
例題
3-1
添付図左のように、1方向にスクロールさせる。
3-2
添付図中のように、往復させる。
3-3
添付図右のように、2進法で、0→15のカウント(繰り返し)
です。間隔は0.5sにして下さい。
Re: マルチボード 例題3
本当にすみませんが、自分はarduinoはやりません。
PICとarduinoは自分にとって違いすぎます。
買ってみてそれが分かりました。
PICの理解を深めたいと思います。
プログラムもそうだし。
PIC自体も良く分かっていません。
出力端子や入力端子です。
例として、
つい最近分かりました。
SEC基板の出力端子 1番、
これに間欠プログラムのPICのRA6を繋いでいますが、
これは出力端子の設定です。
素人的に考えると、
出力端子と出力端子が繋がっていること自体、不思議な感じです。
CMOSのトーテムポールというそうですが。
PICの端子の構造は、NチャンPチャンは繋がっているそうですね。
PICとarduinoは自分にとって違いすぎます。
買ってみてそれが分かりました。
PICの理解を深めたいと思います。
プログラムもそうだし。
PIC自体も良く分かっていません。
出力端子や入力端子です。
例として、
つい最近分かりました。
SEC基板の出力端子 1番、
これに間欠プログラムのPICのRA6を繋いでいますが、
これは出力端子の設定です。
素人的に考えると、
出力端子と出力端子が繋がっていること自体、不思議な感じです。
CMOSのトーテムポールというそうですが。
PICの端子の構造は、NチャンPチャンは繋がっているそうですね。
Re: マルチボード 例題3
3−1の回答です。
// PIC16F627A Configuration Bit Settings
// 'C' source line config statements
#include <xc.h>
#pragma config FOSC = INTOSCIO
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = ON
#pragma config BOREN = ON
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config CP = OFF
#define _XTAL_FREQ 4000000 //delay用
void main(void) {
CMCON = 0x07;//コンパレータ不使用
// 電圧レベルの初期設定
PORTA = 0xFF;
PORTB = 0xFF;
// 入出力設定
TRISA = 0x20;
TRISB = 0x00;
// 永久ループ
while(1) {
PORTB = 0b11111110; //LED1のみ点灯
__delay_ms(1000);
PORTB = 0b11111101; //LED2のみ点灯
__delay_ms(1000);
PORTB = 0b11111011; //LED3のみ点灯
__delay_ms(1000);
PORTB = 0b11110111; //LED4のみ点灯
__delay_ms(1000);
}
}
回答に当たっては、教科書を参考にしました。
// PIC16F627A Configuration Bit Settings
// 'C' source line config statements
#include <xc.h>
#pragma config FOSC = INTOSCIO
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = ON
#pragma config BOREN = ON
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config CP = OFF
#define _XTAL_FREQ 4000000 //delay用
void main(void) {
CMCON = 0x07;//コンパレータ不使用
// 電圧レベルの初期設定
PORTA = 0xFF;
PORTB = 0xFF;
// 入出力設定
TRISA = 0x20;
TRISB = 0x00;
// 永久ループ
while(1) {
PORTB = 0b11111110; //LED1のみ点灯
__delay_ms(1000);
PORTB = 0b11111101; //LED2のみ点灯
__delay_ms(1000);
PORTB = 0b11111011; //LED3のみ点灯
__delay_ms(1000);
PORTB = 0b11110111; //LED4のみ点灯
__delay_ms(1000);
}
}
回答に当たっては、教科書を参考にしました。
Re: マルチボード 例題3
そうですか。それは残念です。
本質的には、PICもArduinoもAVRもSTMも同じなので、PICを使えるようになれば
Arduinoも使えるようになると思います。
で、回答ですけど、ほぼ合ってはいますけど、修正点もあります。
、
1.接続ピンが違います。その学習ボードのRA2〜RA5にLEDを付けて試してください。
2.↑のNo.1149でも書いたように、その書き方だと今回使用していないピンまで、
変更してしまいます。なので、ちゃんとマスク処理をした方が良いです。
3.使わないピンは入力にしておくべきです。何か不慮の事、つまり、
他のラインとの短絡があった時に PICを守ります。
本質的には、PICもArduinoもAVRもSTMも同じなので、PICを使えるようになれば
Arduinoも使えるようになると思います。
で、回答ですけど、ほぼ合ってはいますけど、修正点もあります。
、
1.接続ピンが違います。その学習ボードのRA2〜RA5にLEDを付けて試してください。
2.↑のNo.1149でも書いたように、その書き方だと今回使用していないピンまで、
変更してしまいます。なので、ちゃんとマスク処理をした方が良いです。
3.使わないピンは入力にしておくべきです。何か不慮の事、つまり、
他のラインとの短絡があった時に PICを守ります。
Re: マルチボード 例題3
回答3-1-@ TRさんの修正版
#include <xc.h>
#pragma config FOSC = INTOSCIO
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = ON
#pragma config BOREN = ON
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config CP = OFF
#define _XTAL_FREQ 4000000 //delay用
void main(void) {
CMCON = 0x07;//コンパレータ不使用
PORTB = 0xFF;
TRISA = 0xFF;
TRISB = 0xC3;
while(1) {
PORTB = (PORTB&0xC3)|(0b1110<<2); //LED1のみ点灯
__delay_ms(500);
PORTB = (PORTB&0xC3)|(0b1101<<2); //LED2のみ点灯
__delay_ms(500);
PORTB = (PORTB&0xC3)|(0b1011<<2); //LED3のみ点灯
__delay_ms(500);
PORTB = (PORTB&0xC3)|(0b0111<<2); //LED4のみ点灯
__delay_ms(500);
}
}
回答3-1-A @を纏めたもの。ノーマル?
//config略
unsigned char fig[4]={0b1110,0b1101,0b1011,0b0111};
void main(void) {
CMCON = 0x07;//コンパレータ不使用
PORTB = 0xFF;
TRISA = 0xFF
TRISB = 0xC3;
while(1) {
for(char i=0,i<4;i++){
PORTB = (PORTB&0xC3)|(fig[i]<<2); //LED1のみ点灯
__delay_ms(500);
}
}
}
回答3-1-B 初心者用
//config略
void main(void) {
CMCON = 0x07;//コンパレータ不使用
PORTB = 0xFF;
TRISA = 0xFF
TRISB = 0xC3;
while(1) {
RB5=1;
RB2=0;
__delay_ms(500);
RB2=1;
RB3=0;
__delay_ms(500);
RB3=1;
RB4=0;
__delay_ms(500);
RB4=1;
RB5=0;
__delay_ms(500);
}
}
解説
@
ディレイ時間も0.5sだったので修正しました。
ピンの初期設定は、出力設定にするのものはちゃんと入れておきます。
ポートAは全て入力なので、接続状態により自由に変化するレジスタに何を入れても意味がありません。
PORTB&0xC3は0b□□0000□□となります。※□部はPORTBの元の値です。つまり、代入する部分を0でくり抜いています。
0b1110<<2は0b00「1110」00です。つまり、表示形を当てはめる位置に移動させています。
それを「|」をする事で、0b□□1110□□が完成します。
このマスク処理も初心者には難しい所でしょうけど、覚えなければ、1.ポート全部を犠牲にするか、2.ビット操作のみでやり過ごすか、の
2択になります。※ビット操作のみの回答が3-1-Bになります。
A
@を纏めたもの。勿論4つ位ではそのまま書いても気になる範囲ではありませんけど、
これが8個、16個となると、並べるのも恥ずかしくなります。
点灯形を別に用意すると、見やすくなります。
B
これが最も初心者用のプログラムです。
ビット操作にしているので、マスク処理は必要ありません。
この程度のプログラムなら、こちらも判りやすいですね。
#include <xc.h>
#pragma config FOSC = INTOSCIO
#pragma config WDTE = OFF
#pragma config PWRTE = ON
#pragma config MCLRE = ON
#pragma config BOREN = ON
#pragma config LVP = OFF
#pragma config CPD = OFF
#pragma config CP = OFF
#define _XTAL_FREQ 4000000 //delay用
void main(void) {
CMCON = 0x07;//コンパレータ不使用
PORTB = 0xFF;
TRISA = 0xFF;
TRISB = 0xC3;
while(1) {
PORTB = (PORTB&0xC3)|(0b1110<<2); //LED1のみ点灯
__delay_ms(500);
PORTB = (PORTB&0xC3)|(0b1101<<2); //LED2のみ点灯
__delay_ms(500);
PORTB = (PORTB&0xC3)|(0b1011<<2); //LED3のみ点灯
__delay_ms(500);
PORTB = (PORTB&0xC3)|(0b0111<<2); //LED4のみ点灯
__delay_ms(500);
}
}
回答3-1-A @を纏めたもの。ノーマル?
//config略
unsigned char fig[4]={0b1110,0b1101,0b1011,0b0111};
void main(void) {
CMCON = 0x07;//コンパレータ不使用
PORTB = 0xFF;
TRISA = 0xFF
TRISB = 0xC3;
while(1) {
for(char i=0,i<4;i++){
PORTB = (PORTB&0xC3)|(fig[i]<<2); //LED1のみ点灯
__delay_ms(500);
}
}
}
回答3-1-B 初心者用
//config略
void main(void) {
CMCON = 0x07;//コンパレータ不使用
PORTB = 0xFF;
TRISA = 0xFF
TRISB = 0xC3;
while(1) {
RB5=1;
RB2=0;
__delay_ms(500);
RB2=1;
RB3=0;
__delay_ms(500);
RB3=1;
RB4=0;
__delay_ms(500);
RB4=1;
RB5=0;
__delay_ms(500);
}
}
解説
@
ディレイ時間も0.5sだったので修正しました。
ピンの初期設定は、出力設定にするのものはちゃんと入れておきます。
ポートAは全て入力なので、接続状態により自由に変化するレジスタに何を入れても意味がありません。
PORTB&0xC3は0b□□0000□□となります。※□部はPORTBの元の値です。つまり、代入する部分を0でくり抜いています。
0b1110<<2は0b00「1110」00です。つまり、表示形を当てはめる位置に移動させています。
それを「|」をする事で、0b□□1110□□が完成します。
このマスク処理も初心者には難しい所でしょうけど、覚えなければ、1.ポート全部を犠牲にするか、2.ビット操作のみでやり過ごすか、の
2択になります。※ビット操作のみの回答が3-1-Bになります。
A
@を纏めたもの。勿論4つ位ではそのまま書いても気になる範囲ではありませんけど、
これが8個、16個となると、並べるのも恥ずかしくなります。
点灯形を別に用意すると、見やすくなります。
B
これが最も初心者用のプログラムです。
ビット操作にしているので、マスク処理は必要ありません。
この程度のプログラムなら、こちらも判りやすいですね。
Re: マルチボード 例題3
0b1110とはRB=00001110ということですか??
回答3-1-@はエラーが出ます。
追記
エラーの理由が分かりました。
TRISA = 0xFF の後に「;」がありませんでした。
ボードのLEDは、中4個のLED]がずれながら、点灯していきます。
回答3-1-@はエラーが出ます。
追記
エラーの理由が分かりました。
TRISA = 0xFF の後に「;」がありませんでした。
ボードのLEDは、中4個のLED]がずれながら、点灯していきます。
Re: マルチボード 例題3
3-2の回答案です。
// CONFIG 省略
#define _XTAL_FREQ 4000000 //delay用
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// メイン関数
void main (void)
{
unsigned char fig[7]={0b1110,0b1101,0b1011,0b0111,0b1011,0b1101,0b1110};
unsigned char i = 0;// 点灯パターン配列の添え字用変数の初期値
// 1,2,17,18端子を入出力端子に設定
CMCON = 0x07;
// 電圧レベルの初期設定
PORTA = 0xFF;
PORTB = 0xFF;
// 入出力設定
TRISA = 0xFF;
TRISB = 0xc3;
// 永久ループ
while(1)
{
PORTB = (PORTB&0xC3)|(fig[i]<<2);//LED1のみ点灯
__delay_ms(500);
// 添え字が6よりも小さい場合
if(i < 6)
{
// 添え字に1プラスする
i++;
}
// その他の場合(添え字が7の場合)
else
{
// 添え字を0にする
i = 0;
}
}
}
// CONFIG 省略
#define _XTAL_FREQ 4000000 //delay用
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
#include <xc.h>
// メイン関数
void main (void)
{
unsigned char fig[7]={0b1110,0b1101,0b1011,0b0111,0b1011,0b1101,0b1110};
unsigned char i = 0;// 点灯パターン配列の添え字用変数の初期値
// 1,2,17,18端子を入出力端子に設定
CMCON = 0x07;
// 電圧レベルの初期設定
PORTA = 0xFF;
PORTB = 0xFF;
// 入出力設定
TRISA = 0xFF;
TRISB = 0xc3;
// 永久ループ
while(1)
{
PORTB = (PORTB&0xC3)|(fig[i]<<2);//LED1のみ点灯
__delay_ms(500);
// 添え字が6よりも小さい場合
if(i < 6)
{
// 添え字に1プラスする
i++;
}
// その他の場合(添え字が7の場合)
else
{
// 添え字を0にする
i = 0;
}
}
}
Re: マルチボード 例題3
番の問題はお手上げです。
マスク処理という意味を調べました。
意味は、
必要なビット以外をOFF(0)にする処理(マスクといいます)に使用されます。
わざわざ演算子を使って「0」にしなければならないので、
3番はお手上げです。
ただ、ループの関数ですが、便利なのを見つけました。
従来
cnt ii;//変数の定義
cnt =00//初期値の設定
処理
if(ii<3){
ii++;
else(
i=0
)
}
for文の利用
for(ii=0;ii<3;ii++){
処理
}
マスク処理という意味を調べました。
意味は、
必要なビット以外をOFF(0)にする処理(マスクといいます)に使用されます。
わざわざ演算子を使って「0」にしなければならないので、
3番はお手上げです。
ただ、ループの関数ですが、便利なのを見つけました。
従来
cnt ii;//変数の定義
cnt =00//初期値の設定
処理
if(ii<3){
ii++;
else(
i=0
)
}
for文の利用
for(ii=0;ii<3;ii++){
処理
}
Re: マルチボード 例題3
> TRISA = 0xFF の後に「;」がありませんでした。 No.1156
あっ、忘れていました。
後で修正しておきます。
> No.1157
おぉ、いい感じじゃないですか。
修正点としては
unsigned char fig[7]={0b1110,0b1101,0b1011,0b0111,0b1011,0b1101,0b1110};
としていますけど、実際は
unsigned char fig[6]={0b1110,0b1101,0b1011,0b0111,0b1011,0b1101};
と1つ少なくて良いです。
if(i < 6) i++;
else i = 0;
の部分は例えば
i++;
if(i==6)i=0;
とか、それよりもよく使われるのが
i=(i+1)%6;
です。
> No.1157
マスク処理は初心者には難しいです。それはビット演算子を使わないと出来ないからです。
AVR派は日常的にビット演算子を使います。PIC派はピン操作をRA=2;みたいに書けるので
ビット演算子に不慣れな人が多いような気がします。
マスク処理自体は重要です。ポートで弄る時には必須です。勿論、ポート全て使うなら必要ありませんけど。
> cnt ii;//変数の定義
> cnt =00//初期値の設定
これは
char ii=0;
という事ですよね?
ループ関数は正確には、その2つの意味が違います。
従来の方は
・処理を1回行います。
・次にif文で、iiが1になります。
それで終了です。
for文の方は
iiが0になり、比較文で真なので、処理が行われる。
iiが1になり、比較文で真なので、処理が行われる。
iiが2になり、比較文で真なので、処理が行われる。
iiが3になり、比較文で偽なので、for文を出る
と3回行われます。そして、出て来たときにはiiの内容は3です。
あっ、忘れていました。
後で修正しておきます。
> No.1157
おぉ、いい感じじゃないですか。
修正点としては
unsigned char fig[7]={0b1110,0b1101,0b1011,0b0111,0b1011,0b1101,0b1110};
としていますけど、実際は
unsigned char fig[6]={0b1110,0b1101,0b1011,0b0111,0b1011,0b1101};
と1つ少なくて良いです。
if(i < 6) i++;
else i = 0;
の部分は例えば
i++;
if(i==6)i=0;
とか、それよりもよく使われるのが
i=(i+1)%6;
です。
> No.1157
マスク処理は初心者には難しいです。それはビット演算子を使わないと出来ないからです。
AVR派は日常的にビット演算子を使います。PIC派はピン操作をRA=2;みたいに書けるので
ビット演算子に不慣れな人が多いような気がします。
マスク処理自体は重要です。ポートで弄る時には必須です。勿論、ポート全て使うなら必要ありませんけど。
> cnt ii;//変数の定義
> cnt =00//初期値の設定
これは
char ii=0;
という事ですよね?
ループ関数は正確には、その2つの意味が違います。
従来の方は
・処理を1回行います。
・次にif文で、iiが1になります。
それで終了です。
for文の方は
iiが0になり、比較文で真なので、処理が行われる。
iiが1になり、比較文で真なので、処理が行われる。
iiが2になり、比較文で真なので、処理が行われる。
iiが3になり、比較文で偽なので、for文を出る
と3回行われます。そして、出て来たときにはiiの内容は3です。
Re: マルチボード 例題3
>従来の方は
・処理を1回行います。
・次にif文で、iiが1になります。
それで終了です。
上記の意味は、下記の事ですよね。
記
{
処理;
if(i < 6)
{
// 添え字に1プラスする
i++;
}
}
どうしてですか?
iは、最初が0
i++なので、0+1=1 つまり、配列の添え字が1
その1に対応する値で次の処理をし、
処理をしたら、
またI++なので、1+1=2 配列の添え字が2の値で処理をするので、
iは、どんどん増えて5まで増えるのではないでしょうか??
・処理を1回行います。
・次にif文で、iiが1になります。
それで終了です。
上記の意味は、下記の事ですよね。
記
{
処理;
if(i < 6)
{
// 添え字に1プラスする
i++;
}
}
どうしてですか?
iは、最初が0
i++なので、0+1=1 つまり、配列の添え字が1
その1に対応する値で次の処理をし、
処理をしたら、
またI++なので、1+1=2 配列の添え字が2の値で処理をするので、
iは、どんどん増えて5まで増えるのではないでしょうか??
Re: マルチボード 例題3
あぁ、それはwhileですね。
i=0;
while(i<6){
//処理
i++;
}
と
for(i=0;i<6;i++){
//処理
}
が同じ意味になります。
if文は「もし〜なら」、だけの一度だけの処理です。
繰り返しは多分、for文とwhile文、あとはdo 〜 while文の3つ位だと思います
i=0;
while(i<6){
//処理
i++;
}
と
for(i=0;i<6;i++){
//処理
}
が同じ意味になります。
if文は「もし〜なら」、だけの一度だけの処理です。
繰り返しは多分、for文とwhile文、あとはdo 〜 while文の3つ位だと思います
Re: マルチボード 例題3
違う解き方を2つ。
//3-2方向の切り替え
//略
void main (void) {
PORTB = 0xFF;
TRISA = 0xFF;
TRISB = 0xc3;
char dir=1;//方向
char fig_num=0;//何番目が光るか(0-3)
while(1) {
PORTB=(PORTB&0xC3)|(~(1<<fig_num)&0x3C);
fig_num+=dir;
if(fig_num==0)dir=1;//下まで来たら+
else if(fig_num==3)dir=-1;//上まで来たら-
__delay_ms(500);
}
}
※より多くのLEDの場合はこのように方向を切り替えてやる方式の方が適しています。
8個ぐらいあればこっちの方にするかな?
//3-2PIC初心者向
//略
unsigned char fig[6][4]= {
{0,1,1,1},
{1,0,1,1},
{1,1,0,1},
{1,1,1,0},
{1,1,0,1},
{1,0,1,1}
};
void main (void) {
PORTB = 0xFF;
TRISA = 0xFF;
TRISB = 0xc3;
char num=0;
while(1) {
RB2=fig[num][0];
RB3=fig[num][1];
RB4=fig[num][2];
RB5=fig[num][3];
num=(num+1)%6;
__delay_ms(500);
}
}
※Arduinoでも、この書き方がベーシックだと思います。
RB○にchar型を入れるので、場合によってはエラー、若しくは警告が付くかもしれません。
その場合は
RB2=!!fig[num][0];
RB3=!!fig[num][1];
RB4=!!fig[num][2];
RB5=!!fig[num][3];
とすれば、間違いなく通る筈です。
マスク処理が要らないので、初心者にも判りやすいです。
(とはいえ、2次元配列になってしまいましたけど)
//3-2方向の切り替え
//略
void main (void) {
PORTB = 0xFF;
TRISA = 0xFF;
TRISB = 0xc3;
char dir=1;//方向
char fig_num=0;//何番目が光るか(0-3)
while(1) {
PORTB=(PORTB&0xC3)|(~(1<<fig_num)&0x3C);
fig_num+=dir;
if(fig_num==0)dir=1;//下まで来たら+
else if(fig_num==3)dir=-1;//上まで来たら-
__delay_ms(500);
}
}
※より多くのLEDの場合はこのように方向を切り替えてやる方式の方が適しています。
8個ぐらいあればこっちの方にするかな?
//3-2PIC初心者向
//略
unsigned char fig[6][4]= {
{0,1,1,1},
{1,0,1,1},
{1,1,0,1},
{1,1,1,0},
{1,1,0,1},
{1,0,1,1}
};
void main (void) {
PORTB = 0xFF;
TRISA = 0xFF;
TRISB = 0xc3;
char num=0;
while(1) {
RB2=fig[num][0];
RB3=fig[num][1];
RB4=fig[num][2];
RB5=fig[num][3];
num=(num+1)%6;
__delay_ms(500);
}
}
※Arduinoでも、この書き方がベーシックだと思います。
RB○にchar型を入れるので、場合によってはエラー、若しくは警告が付くかもしれません。
その場合は
RB2=!!fig[num][0];
RB3=!!fig[num][1];
RB4=!!fig[num][2];
RB5=!!fig[num][3];
とすれば、間違いなく通る筈です。
マスク処理が要らないので、初心者にも判りやすいです。
(とはいえ、2次元配列になってしまいましたけど)
Re: マルチボード 例題3
これは難しいので無理です。
どうも、縦方向に発光するLEDを選んでいるようですが。
猛牛ロックさん、素晴らしいです。
自分は、それより、
今日は、以前、教わった事を自分流に出来たので、良かったです。
それは、SWによって、永久ループから、抜け出る方法です。
unsigned char MODE; //スイッチ保存用
// メイン関数
void main (void){
OSCF=1;//内蔵発振器 4MHz使用に設定
CMCON = 0x07;// 1,2,17,18端子を入出力端子に設定すること
//宣言
PORTA=0;//出力のピンを初期設定。
// 入出力設定
TRISA = 0b00000001;//RA0スイッチ RA7LED
TRISB = 0b11111111;//タイマースイッチ
OPTION_REG=0b00000000;//ポートBプルアップ
// 永久ループ
while(1){
MODE=RA0;
__delay_ms(50);
while(MODE!=RA0){ //変化なければそのまま
MODE=RA0;
__delay_ms(50);
}
if(MODE==1){//RA0スイッチOFF
RA7=1;//LED8ロー
}
else if(MODE==0){//RA0スイッチオン
RA7=1;__delay_ms(700);
RA7=0;__delay_ms(700);
}
}
}
どうも、縦方向に発光するLEDを選んでいるようですが。
猛牛ロックさん、素晴らしいです。
自分は、それより、
今日は、以前、教わった事を自分流に出来たので、良かったです。
それは、SWによって、永久ループから、抜け出る方法です。
unsigned char MODE; //スイッチ保存用
// メイン関数
void main (void){
OSCF=1;//内蔵発振器 4MHz使用に設定
CMCON = 0x07;// 1,2,17,18端子を入出力端子に設定すること
//宣言
PORTA=0;//出力のピンを初期設定。
// 入出力設定
TRISA = 0b00000001;//RA0スイッチ RA7LED
TRISB = 0b11111111;//タイマースイッチ
OPTION_REG=0b00000000;//ポートBプルアップ
// 永久ループ
while(1){
MODE=RA0;
__delay_ms(50);
while(MODE!=RA0){ //変化なければそのまま
MODE=RA0;
__delay_ms(50);
}
if(MODE==1){//RA0スイッチOFF
RA7=1;//LED8ロー
}
else if(MODE==0){//RA0スイッチオン
RA7=1;__delay_ms(700);
RA7=0;__delay_ms(700);
}
}
}
Re: マルチボード 例題3
永久ループ(無限ループ)とは条件が無条件=常に真になっているループを指します。
つまり、
while(1)
や
for(;;)
です。勿論、for(;1;)でも同じです。
で、ここから出るのは、break,return,gotoの3つ位しか方法は無いと思います。
TRさんの提示されたプログラムでは永久ループを抜けた形跡がありません。
※永久ループを抜けたら再起動するのかな?
つまり、
while(1)
や
for(;;)
です。勿論、for(;1;)でも同じです。
で、ここから出るのは、break,return,gotoの3つ位しか方法は無いと思います。
TRさんの提示されたプログラムでは永久ループを抜けた形跡がありません。
※永久ループを抜けたら再起動するのかな?
Re: マルチボード 例題3
>TRさんの提示されたプログラムでは永久ループを抜けた形跡がありません。
そうでした。私の表現がまずかったです。
気持としては、
スイッチにより、動作を切り替えるでした。
MODO 1(RA0がスイッチOFFで、LED8がロー)
MODE==0でRA0スイッチオン となり、
RA7=が点滅する。
しかし、Windows10とMPLAB IDEV5.05と相性が悪い。
何度も、ファイルを作っては消しての繰り返しです。
そうでした。私の表現がまずかったです。
気持としては、
スイッチにより、動作を切り替えるでした。
MODO 1(RA0がスイッチOFFで、LED8がロー)
MODE==0でRA0スイッチオン となり、
RA7=が点滅する。
しかし、Windows10とMPLAB IDEV5.05と相性が悪い。
何度も、ファイルを作っては消しての繰り返しです。
Re: マルチボード 例題3
一応、3-3の回答も書いておきます。
でも、重要な事は「その時に頭に浮かんだものをプログラミング出来る」ようになる事です。
その意味では、この課題はあまり現実的ではありません。
まぁ、軽く流しておいてください。
3-3はある意味一番簡単です。
条件が変わって、LEDがアクティブハイ、LEDがRB0からRB3についている場合は
while(1){
PORTB++;
__delay_ms(500);
}
で、目的の動作になります。※勿論主要部のみの記述です。
これをアクティブローに変える事と、ピン位置を合わせる事、そして、マスク処理の3つが加わります。
回答3-3-@ 判りやすいやつ?
//前略
void main(void){
PORTB = 0xFF;
TRISA = 0xFF;
TRISB = 0xc3;
unsigned char num=0;
while(1){
RA2=!(num>>0&1);
RA3=!(num>>1&1);
RA4=!(num>>2&1);
RA5=!(num>>3&1);
}
num++;
__delay_ms(500);
}
※対応するビットを「!」を付ける事で反転=アクティブローにしています。
numはわざと、0〜3にしないで、num++;にしました。
回答3-3-A
//前略
void main(void){
PORTB = 0xFF;
TRISA = 0xFF;
TRISB = 0xc3;
unsigned char num=0;
while(1){
PORTB=(PORTB&0xc3)|((~num<<2)&0x3c);
num++;
__delay_ms(500);
}
※変わった所は、PORTB=(PORTB&0xc3)|((~num<<2)&0x3c);だけです。
(PORTB&0xc3)は入れる場所を0でくりぬいた形。
((~num<<2)&0x3c)はnumを反転(~)させて、2ビット左シフトさせて位置合わせして(<<2)、最後に要らない部分を切り捨てています(&0x3c)。
でも、重要な事は「その時に頭に浮かんだものをプログラミング出来る」ようになる事です。
その意味では、この課題はあまり現実的ではありません。
まぁ、軽く流しておいてください。
3-3はある意味一番簡単です。
条件が変わって、LEDがアクティブハイ、LEDがRB0からRB3についている場合は
while(1){
PORTB++;
__delay_ms(500);
}
で、目的の動作になります。※勿論主要部のみの記述です。
これをアクティブローに変える事と、ピン位置を合わせる事、そして、マスク処理の3つが加わります。
回答3-3-@ 判りやすいやつ?
//前略
void main(void){
PORTB = 0xFF;
TRISA = 0xFF;
TRISB = 0xc3;
unsigned char num=0;
while(1){
RA2=!(num>>0&1);
RA3=!(num>>1&1);
RA4=!(num>>2&1);
RA5=!(num>>3&1);
}
num++;
__delay_ms(500);
}
※対応するビットを「!」を付ける事で反転=アクティブローにしています。
numはわざと、0〜3にしないで、num++;にしました。
回答3-3-A
//前略
void main(void){
PORTB = 0xFF;
TRISA = 0xFF;
TRISB = 0xc3;
unsigned char num=0;
while(1){
PORTB=(PORTB&0xc3)|((~num<<2)&0x3c);
num++;
__delay_ms(500);
}
※変わった所は、PORTB=(PORTB&0xc3)|((~num<<2)&0x3c);だけです。
(PORTB&0xc3)は入れる場所を0でくりぬいた形。
((~num<<2)&0x3c)はnumを反転(~)させて、2ビット左シフトさせて位置合わせして(<<2)、最後に要らない部分を切り捨てています(&0x3c)。
Re: マルチボード 例題3
>、重要な事は「その時に頭に浮かんだものをプログラミング出来る」ようになる事です。
それはそうですよね。
なので、間欠タイマーを今でも解明する努力をしています。
ついでで申し訳ないのですが、間欠タイマーでまだ不明な点があるので教えて下さい。
抜粋になります。
// 永久ループ
while(1){
MODE=PORTA&0b11;
略
else if(MODE==2||(MODE==3&&MACH_sig==1)){ //間欠
PORTB_save=PORTB;
DousaTime=TIME[PORTB_save&0b1111];
KanketuTime=TIME[PORTB_save>>4];
vacuum=On;__delay_ms(700);burowa=On;
change=Second(DousaTime); //動作時間分ウェイト
if文A if(change==0){
burowa=Off;__delay_ms(500);vacuum=Off;
change=Second(KanketuTime); //間欠時間分ウェイト
}
}
}
}
// 待ち時間関数
char Second(unsigned int sec){
while(sec >0){
1f文@→if((MODE!=(PORTA&0b11))||(MODE==3&&MACH_sig==0)||(PORTB_save!=PORTB)) return 1;
sec--;
__delay_ms(10);
}
return 0;
}
※ 永久ループwhileと待ち時間関数char Secondの関係です。
永久ループ内の「change=Second(DousaTime); //動作時間分ウェイト」
上記のカギかっこの指令によりSecond関数が呼び出されて、
while(sec >0){
if((MODE!=(PORTA&0b11))||(MODE==3&&MACH_sig==0)||(PORTB_save!=PORTB)) return 1;
ここへ来ると、
secは0以上なら、
処理if文@をしますよね。
if文@は、MODEが変わった、または、MODE 3且つMach信号無し 又は、PORTBの間欠時間が変わった場合にreturn 1を
change=Second(DousaTime); //動作時間分ウェイ 左のchangeに1を返しますよね。
でも、if文@がNOの場合(MODE等変化なし)は、
if文@{}内の、sec--;に移り、
PORTBの値をマイナスする。しかも、secが0より大きい間中は、マイナス1の計算をすると思います。
次に、
sec--;の計算を終えたら、
return 0; これにより、changeに0が入るので、if文A に移り、
間欠時間の動作をする。
つまり、burowaをOff、vacuumをOFF そして、そのOFFの時間は、
change=Second(KanketuTime); //間欠時間分ウェイト 左により、またまた、待ち時間関数が呼び出されて
PORTBで読み取った時間として、待ち時間関数のsec--;をするわけですよね。
この動作時間と間欠時間の繰り返しを繰り返しになる。
で、
if文@は、MODEが変わった、または、MODE 3且つMach信号無し 又は、PORTBの間欠時間が変わった場合にreturn 1を
change=Second(DousaTime); //動作時間分ウェイ 左のchangeに1を返しますよね。
次の動作は、if文Aになる。
つまり、停止時間をし、次に、MODE等変化なければ、運転時間に移る
以上の解釈で宜しいでしょうか?
それはそうですよね。
なので、間欠タイマーを今でも解明する努力をしています。
ついでで申し訳ないのですが、間欠タイマーでまだ不明な点があるので教えて下さい。
抜粋になります。
// 永久ループ
while(1){
MODE=PORTA&0b11;
略
else if(MODE==2||(MODE==3&&MACH_sig==1)){ //間欠
PORTB_save=PORTB;
DousaTime=TIME[PORTB_save&0b1111];
KanketuTime=TIME[PORTB_save>>4];
vacuum=On;__delay_ms(700);burowa=On;
change=Second(DousaTime); //動作時間分ウェイト
if文A if(change==0){
burowa=Off;__delay_ms(500);vacuum=Off;
change=Second(KanketuTime); //間欠時間分ウェイト
}
}
}
}
// 待ち時間関数
char Second(unsigned int sec){
while(sec >0){
1f文@→if((MODE!=(PORTA&0b11))||(MODE==3&&MACH_sig==0)||(PORTB_save!=PORTB)) return 1;
sec--;
__delay_ms(10);
}
return 0;
}
※ 永久ループwhileと待ち時間関数char Secondの関係です。
永久ループ内の「change=Second(DousaTime); //動作時間分ウェイト」
上記のカギかっこの指令によりSecond関数が呼び出されて、
while(sec >0){
if((MODE!=(PORTA&0b11))||(MODE==3&&MACH_sig==0)||(PORTB_save!=PORTB)) return 1;
ここへ来ると、
secは0以上なら、
処理if文@をしますよね。
if文@は、MODEが変わった、または、MODE 3且つMach信号無し 又は、PORTBの間欠時間が変わった場合にreturn 1を
change=Second(DousaTime); //動作時間分ウェイ 左のchangeに1を返しますよね。
でも、if文@がNOの場合(MODE等変化なし)は、
if文@{}内の、sec--;に移り、
PORTBの値をマイナスする。しかも、secが0より大きい間中は、マイナス1の計算をすると思います。
次に、
sec--;の計算を終えたら、
return 0; これにより、changeに0が入るので、if文A に移り、
間欠時間の動作をする。
つまり、burowaをOff、vacuumをOFF そして、そのOFFの時間は、
change=Second(KanketuTime); //間欠時間分ウェイト 左により、またまた、待ち時間関数が呼び出されて
PORTBで読み取った時間として、待ち時間関数のsec--;をするわけですよね。
この動作時間と間欠時間の繰り返しを繰り返しになる。
で、
if文@は、MODEが変わった、または、MODE 3且つMach信号無し 又は、PORTBの間欠時間が変わった場合にreturn 1を
change=Second(DousaTime); //動作時間分ウェイ 左のchangeに1を返しますよね。
次の動作は、if文Aになる。
つまり、停止時間をし、次に、MODE等変化なければ、運転時間に移る
以上の解釈で宜しいでしょうか?
Re: マルチボード 例題3
質問の最後4行が?です。
Sedond関数で、異常、若しくは変化があった時は、戻り値1です。つまり、changeは1になります。(そこまでは良いです)
で、次の動作はif文Aですけど、if(change==0)は「偽」になるので、その後の{}内は実行されません。
なので、上にある「else if」文のカッコも終わって、while(1)の次の行に戻ります。
Sedond関数で、異常、若しくは変化があった時は、戻り値1です。つまり、changeは1になります。(そこまでは良いです)
で、次の動作はif文Aですけど、if(change==0)は「偽」になるので、その後の{}内は実行されません。
なので、上にある「else if」文のカッコも終わって、while(1)の次の行に戻ります。
Re: マルチボード 例題3
>なので、上にある「else if」文のカッコも終わって、while(1)の次の行に戻ります。
次の行とは下記のBの行ですか?
記
// 永久ループ
while(1){
B MODE=PORTA&0b11;
略
else if(MODE==2||(MODE==3&&MACH_sig==1)){ //間欠
PORTB_save=PORTB;
DousaTime=TIME[PORTB_save&0b1111];
KanketuTime=TIME[PORTB_save>>4];
vacuum=On;__delay_ms(700);burowa=On;
change=Second(DousaTime); //動作時間分ウェイト
if文A if(change==0){
burowa=Off;__delay_ms(500);vacuum=Off;
change=Second(KanketuTime); //間欠時間分ウェイト
}
}
}
}
// 待ち時間関数
char Second(unsigned int sec){
while(sec >0){
1f文@→if((MODE!=(PORTA&0b11))||(MODE==3&&MACH_sig==0)||(PORTB_save!=PORTB)) return 1;
sec--;
__delay_ms(10);
}
return 0;
}
次の行とは下記のBの行ですか?
記
// 永久ループ
while(1){
B MODE=PORTA&0b11;
略
else if(MODE==2||(MODE==3&&MACH_sig==1)){ //間欠
PORTB_save=PORTB;
DousaTime=TIME[PORTB_save&0b1111];
KanketuTime=TIME[PORTB_save>>4];
vacuum=On;__delay_ms(700);burowa=On;
change=Second(DousaTime); //動作時間分ウェイト
if文A if(change==0){
burowa=Off;__delay_ms(500);vacuum=Off;
change=Second(KanketuTime); //間欠時間分ウェイト
}
}
}
}
// 待ち時間関数
char Second(unsigned int sec){
while(sec >0){
1f文@→if((MODE!=(PORTA&0b11))||(MODE==3&&MACH_sig==0)||(PORTB_save!=PORTB)) return 1;
sec--;
__delay_ms(10);
}
return 0;
}
- JoyfulNote v6.02 -
++ Edited by Hamel ++