トピック関連記事
48時間以内の記事は New Mark で表示されます
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);
TR 2018/11/18(Sun) 07:08 No.1218
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の値がレジスタに残っているでしょうからそれが出力されます。
ちゃんと今回の値を返したいなら変換完了→値を取得という順板を守ってください。

今回の場合は気付かない程度に遅れるだけですけど、これが計算なら、計算をせずに結果を返すような事です。
猛牛ロック   2018/11/18(Sun) 12:54 No.1219
Re: ADRES
> MABOさんへ

> PICに比べると親切過ぎる感じですね。
そうですね。

基本的な思想としては、「なるべく低級な作業(レジスタの直接操作)はしない」というのがあるのだと思います。
analogReadやanalogWriteも、使い勝手のいいモデル(分周や周期)が採用されています。
で、そのモデルを外した使い方をするときは、本来は、レジスタを直接操作する方法をしなければならないわけなのですが、
私が見た処、そういった使い方は稀で、大抵は、外部のライブラリを使う、という方が多いです。
つまり、他人が作った、操作し易くなっているもの=ライブラリを多くの人が使っています。
それが見つからなくて最後の手段として、データシートをみて、レジスタを弄る、という感じでしょうか。
勿論、Arduino使いも色々な人がいるでしょうけど。

※低レベルで、きっちりと積み上げていけば、そのデバイスの能力を最大限に活かした、より良いものが出来るのは間違いありません。
けれども、低級な作業を学んでもデバイスを変えたら役に立ちません。
逆に言えば、中級でいろんなデバイスで共通の仕様になっているものを使った方が
覚えるのも優しいし、色んなデバイスでも使え、また、間違いも少なくなる、とも言えます。

そのデバイスを熟知している人でも、やはり多くの行を書いていけば間違いも起こります。
そういった人でさえ、ADC変換でレジスタ操作をするなら、データシートで確認する事は必須でしょう。
でも、analogRead(pin);を使えば5秒で安心して使うことが出来ます。
逆に言えば、仕事で「高度な事」をする人の方がデータシートを参照しなければならないレジスタ操作からは離れて
短時間で安全に操作できる方を優先させます。

実際の所、私も最初は、データシートを見て自分自身で書いていました。
でも、何度か同じような事をすると、前回の物を使いまわすようになります。
そうなってくると、レジスタの内容はちょっと忘れても、その作った関数だけが重要になります。
何度か使っているので、信頼性は多少高くなった、自分の関数です。
でも、Arduinoなら、ほとんど同じものが最初から提供されています。
そちらの方が、信頼性が高く、他の人にとっても判りやすい、と言う事は明白です。
プログラムの開発会社が、そのような中間的なものを使うのは必然だと思います。
(逆に、レジスタ操作なんて(極力)するな、というのが主流でしょう)

で、私としては、レジスタ操作(データシート)と悪戦苦闘して、無駄な時間を使ってしまうPICからは離れた方が良いと
思う次第です。(勿論、ベーシックな部分をおさえるのは重要です。)
ただ、そのデバイスの専門家になる目的ではないので、そのレジスタを覚えることは次の段階では役に立たなくなります。
なので、そのデバイス固有の事は、他の参考サイトからコピーすれば良いと思っています。
ただし、それ以上に安全で、信頼性の高いのが、中間的な処理をしてあるライブラリを使う事です。

※個人サイトは、間違いだらけです。「製作」サイトは仕方がないと思うのですけど、「説明」サイトならもう少し
ちゃんと(過去に書いたプログラムも)管理してもらいたいと思っています。
まぁ、結局の所、それだけマイコンプログラムや電子回路は「間違いが起こりやすい=見つけ辛い」ということなのでしょうけど。
猛牛ロック   2018/11/18(Sun) 14:20 No.1220
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);
}
TR   2018/11/18(Sun) 18:56 No.1221
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の値が幾つ位で、ボリューム位置がどの位置にあるのか、点滅を見て判断付きますか?
この位置なら、この位、というものを実証している動画には見えません。
猛牛ロック   2018/11/18(Sun) 19:23 No.1222
Re: ADRES
関数を無くし、意味不明な__delay_us(20); //アクィジョン時間 20usを無くし、
それと、読み取り値の変数宣言を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と思われる。


どうでしょうか??

TR   2018/11/19(Mon) 11:23 No.1223
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の点灯/消灯です。
猛牛ロック   2018/11/19(Mon) 15:37 No.1224
Re: ADRES
レジスタはともかく、PWMは出力です。目で見える状態としては点滅では無く点灯です。
LED照明などでの明るさ調整でも使われている手法です。
また、モータの速度調整にもよく使われています。

一方、ADC=アナログ/デジタルコンバータは入力したアナログ電圧を数値化するものです。
Lチカとは直接は関係ありません。
今回のように、ボリュームを付けた装置だと、ボリュームの位置が何処にあるのかを調べるために使います。
そして、そのボリューム位置が意味する出力?にします。
猛牛ロック   2018/11/19(Mon) 17:55 No.1225
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;
}
TR   2018/11/19(Mon) 18:43 No.1226
Re: ADRES
動作しました!
可変抵抗器で明るくなったり暗くなったりしました。
間違いを
MPLABが教えてくれました。

変更点
ANSELA →ANSEL
ANSELB → この行を削除

でも、ANSELにはAかBを付ける必要と思うのですが。
どうしてだろう?


と思って、、データシートを見ると、
88にはANSELはAがわの0〜7までしかありませんでした!!
TR   2018/11/19(Mon) 19:14 No.1227
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);も同じ意味で付けています。
猛牛ロック   2018/11/20(Tue) 02:50 No.1228
Re: ADRES
> while(1) { // アナログ値の変換と読込み処理
> 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にしたら、
調光の具合は良いです。

TR   2018/11/20(Tue) 07:44 No.1229
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になるのは変わらない、という事でした。
猛牛ロック   2018/11/20(Tue) 12:15 No.1230
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ビット)
この事ですか?


ともかく、ここまでプログラムを組めるようになったので嬉しいです。
TR   2018/11/20(Tue) 20:58 No.1231
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ビットは見えない部分で作られています。
猛牛ロック   2018/11/20(Tue) 22:56 No.1232
Re: ADRES
下の@〜Bのプログラム実行時間が分かりまし。
122μsだと出ました。

while(1) {
// アナログ値の変換と読込み処理
@ adconv();
Anum = adconv(); //RA0から電圧を読み込んだ値をtempをnumに代入
BCCPR1L = num/4 ; // 4で割って上位8ビットを取り出しアナログ値からのデータでデューティ値を設定
__delay_ms(10);


それで話しついでですが、
猛牛ロックさんが言われていることで、PICの欠点ですが、
少し分かってきました。

ADコンバーターには速さの設定が有ります。
合成写真右側です。
早い方が良いと思いますが、一番早いのは、2Toscですか?

単純に2Toscを選んでよいのでしょうか?

TR   2018/11/21(Wed) 15:39 No.1233
Re: ADRES
> 早い方が良いと思いますが、一番早いのは、2Toscですか?

勿論、早い方が良いと言えますけど、信頼性との兼ね合いもあります。
また、計測の回数が増えるほど、他の作業の邪魔になる場合もあります。
まぁ、今回の場合は、
> while(ADCON0bits.GO_DONE); //変換完了待ち
とウェイトしていますから、早い方が良いですね。
今回のようなウェイトする場合と、完了→割り込みをする場合があります。

で、今回(1MHz駆動)の場合、設定上一番早いのは、2Toscになります。
もし、4MHz駆動にした場合は8Toscです。
だけども、それよりも1つ2つ遅くした方が賢明だと思います。
私自身は測定出来るぎりぎりのラインは使いません。
猛牛ロック   2018/11/21(Wed) 18:01 No.1234
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の方を掛けるのですか?
TR   2018/11/21(Wed) 19:26 No.1235
Re: ADRES
御免なさい。間違った事を書いてしまいました。

基本は1.6us〜6.4usの間になるように選択する、と言う事みたいです。
なので、1MHz駆動(1us周期)の場合は、2TOSCか4TOSCのどちらかにする、と言う事になると思います。
より推奨されるのが4usになる4TOSCなのでしょうけど、2TOSCでも全く問題はないと思います。
※つまり、2TOSCの選択はギリギリという事ではなく、推奨される範囲の値、という事。

また、一番下のRCを選択しておくのが無難だとも感じました。これはおおよそ4usになるRCです。
これを選択しておけば、1MHz駆動のソースでも、4MHz駆動のソースでも共通で済みます。
また、sleepという機能を使った時にADC変換を行いたい時にはこのRCをを選択している必要があります。
※自分なりにデータシートを読み取った感想ですが、確証はありません。
猛牛ロック   2018/11/22(Thu) 00:00 No.1236
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が欲しい。
TR   2018/11/22(Thu) 07:19 No.1237
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になっています。
猛牛ロック   2018/11/22(Thu) 08:40 No.1238

処理 記事No 暗証キー

- JoyfulNote v6.02 -
++ Edited by Hamel ++