モーターの回転数とかで使いたい所があって
20KHz辺りまである程度正確に測りたいのです。
いつものように右往左往してしまい内容に関しては、
多分にマーティーにしかわからない記録になっております(-_-;)
さて、Arduinoを調べると、pulseIn() 命令で
LOW期間、HIGH期間を計測することができるようです(解説はここ)
たぶん、割り込みでタイマーを制御するだろうから
Timer Clockで精度はほぼ決まるのでしょうが...
まあ、調べればいいのですが^^;
PICが頭に浮かんだので、久しぶりに使ってみようかなと^^;
しか~し、もう数年使ってないのです。
少なくともブログ始めてから使ってません(T_T)
早速、当時、随分お世話になった
「きむ茶工房ガレージハウス」さんのブログから探します。
Arduinoに出会う前は、随分お世話になりましたm(_ _)m
ブログを移設されて昔と異なるアドレスになってますね~
「ソフトウエアについて
当サイトのソフトウエアは特に記述が無い限りPDS(Public Domain Software)とします。
作権表示義務無し。商用利用、改造、自由。連絡不要とします。
役立ててやって下さい。」
とあり、誠にありがたいことであります。
活用させていただきますm(_ _)m
サイト内を探すと、やはり、
「PICのキャプチャ機能でパルスを計測してみる」ってのが見つかりました。
PIC 16F1829のCCP(Capture/Compare/PWM Modules)モジュールの
Capture機能を使って、周波数とDUTY比を測定するというものです。
ドンピシャであります^^;
CCPモジュールは、全てのPICに装備されているわけではなく、
マーティーの宝箱を探すと、16F1827が1個だけありました!
CCPモジールに関しては、16F1829と同じでした。
マーティー愛用の12F1822は、CCPが1個なので今回は不採用です。
1個しかないので16F1827をポチっておきます。
ここが最安値かな? US$ 13.35/10個(11% OFF)
さて、PICは、5年以上前から使ってないので、すっかり忘れております。
過去に動いたプログラムで、コンパイラや書込の復習が必要です。
先の「PICのキャプチャ機能でパルスを計測してみる」のプログラムでは、
表示の部分が
「ここでご自由に表示処理を行いましょう」になってるのです。
マーティーは、PICの表示には、
秋月電子の「SC1602」というキャラクタLCDを使っています。半角16文字、2行です。
端子位置が横並びの「SD1602」も持ってます。
というのは、これも「きむ茶工房ガレージハウス」さんの
「キャラクタLCDモジュールに表示を行う」を使わせていただいてるからです。
幸い、16F1827でサンプルプログラムが書かれています。
そのサンプルプログラムで昔~し温度計を作ってたので
そのプログラムでコンパイル・ビルド・書込の復習することにします。
書込機は、秋月電子で買った「PIC KIT2」です。
検索するとここにまだ現役でありますね~!
価格も同じ5300円のままで、発売日 2008/10/09 になってます。
PIC KIT2に書き込みの手順は、
こんな事を予期して、マーティーがボードに貼ってました^^;
プログラムソースの日付は、2014年9月6日で6年前でした^^;
ちょっと新規プロジェクトの作成とかで手こずりましたが(-_-;)
コンパイル、ビルドは、成功です。開発環境は、MPLAB X IDE v2.10、Compilerは、XC8 v1.32 です。
ソフトは、WindowsXPマシンに入れてて
Windows10のマーティーPCには、まだ入れてないのです。
画面はこんな感じです。
昔のプロジェクトを沢山開いたままでした。
PIC KIT2に16F1827を載せて
WindowsXPにUSBで接続して
書き込みソフトは、「PICKit2 v2.61」です。
DEVICE TYPEを設定すると、16F1827が検出されました。
hexファイルを開くのですが、
MPLAB X IDE v2.10のhexファイルは、デフォルトなので深い所にできます。
[Documents and Settings]の下の方なのです。
[Write]!
無事[Programming Successful]がでました!
[Verify]も成功を確認して、
昔~し作って動作してたブレッドボードをお菓子の箱に保存してたので
そのまま、PICを差し替えて、電源ON!
無事動きました!
PIC始めて間もない頃は、とても感動したものです。
左下のTO-92の黒いのが温度センサIC LM35DZです。
10.0 mV/℃の出力電圧をPICのADCで読んでます。
LCDの配線が面倒なので、温度センサーだけ外して
このブレッドボードをそのまま使うことにします。
「PICのキャプチャ機能でパルスを計測してみる」から
サンプルプログラムをダウンロードして
「Capture.zip」を解凍するとファイルが3つあります。
周波数計測するプログラムは、この2つです。
CPU Clock:32MHzで
・Capture.c・・・・・クロックソース 1μsec
・HICapture.c・・・クロックソース 125nsec
「Capture.c」でも25KHz辺りまで使えるらしいですが、
1μsecを単純に200倍して±2%と考えると、20KHz
「HICapture.c」の方、125nsecを単純200倍すると、
・クロックソース 1μsec・・・20KHz
・クロックソース 125nsec・・・160KHz
辺りまでは、そこそこ測定できそうと考えていいのかな?
で、「HICapture.c」を使おうと眺めていると
これは、周波数だけでDUTY測定がないですね~
「Capture.c」が、周波数とDUTYを測定していました。
なので、これを元にクロックソースを125nsecに変更した方がラクそうです^^;
さて、16F1829 ⇒ 16F1827に移植しないといけません。
20pin ⇒ 18pin でI/O PIN番号やPORT数も異なるので
定義とかを変更しないといけないのです。
Arduinoの用に簡単にはいかないのです。
まずは、ピンアサインと使用しているモジュールを調べます。
16F1827/16F1829共通
PORT-A、PORT-B、PORT-C、わりと差がありますね~
では、「Capture.c」を眺めていきます。
まず、コンフィグレーション・・・1827と1829で共通なので変更不要です。
と思って進めてたのですが
最初、LCDに何も表示されなくて、気づくまで随分と時間を消費したのですが
先のLCD表示プログラムが32MHzでは、動かなかったのです(T_T)
で、1箇所変更しました。
*********
// コンフィギュレーション2の設定
#pragma config WRT = OFF // Flashメモリーを保護しない(OFF)
// #pragma config PLLEN = ON // 動作クロックを32MHzで動作させる(ON)
#pragma config PLLEN = OFF // 動作クロックを8MHzで動作させる(OFF)
*********
「割り込みの処理」で[Timer1]と[CCP4]を使われてますが
これも16F1827と16F1829で同じです。
CCPのCaptureで使うTimerもTimer1で16F1827/16F1829は、共通なので
この「割り込みの処理」は変更不要です。
ここは、各PORTの設定ですが、
Datasheetを見比べると
16F1827と16F1829でだいぶ異なっています。
CCP4の入力も、1829・・・RC6、1827・・・RA4、と異なっています。
ということで変更します。
コメントだけ変更してる所もあります。
*********
// I/O情報の設定
// OSCCON = 0b01110000 ; // 内部クロックは8MHzx4(32MHz)とする
OSCCON = 0b01110000 ; // 内部クロックは8MHzとする(config PLLENで決まる)
// ANSELA = 0b00000000 ; // AN0-AN3は使用しない全てデジタルI/Oとする for 1829
ANSELA = 0b00000000 ; // AN0-AN4は使用しない全てデジタルI/Oとする for 1827
// ANSELB = 0b00000000 ; // AN10-AN11は使用しない全てデジタルI/Oとする for 1829
ANSELB = 0b00000000 ; // AN5-AN11は使用しない全てデジタルI/Oとする for 1827
// ANSELC = 0b00000000 ; // AN4-AN9は使用しない全てデジタルI/Oとする 1829 only
// TRISA = 0b00000000 ; // ピン(RA)は全て出力に割当てる(RA3は入力専用)
TRISA = 0b00110000 ; // RA4(CCP4)入力、ピン(RA)は全て出力に割当てる(RA5は入力専用)
TRISB = 0b00000000 ; // ピン(RB)そのは全て出力に割当てる
// TRISC = 0b01000000 ; // RC6(CCP4)入力、そのは全て出力に割当てる 1829 only
// PORTA = 0b00000000 ; // RA0-RA5出力ピンの初期化(全てLOWにする) for 1829
PORTA = 0b00000000 ; // RA0-RA7出力ピンの初期化(全てLOWにする) for 1827
// PORTB = 0b00000000 ; // RB4-RB7出力ピンの初期化(全てLOWにする) for 1829
PORTB = 0b00000000 ; // RB0-RB7出力ピンの初期化(全てLOWにする) for 1827
// PORTC = 0b00000000 ; // RC出力ピンの初期化(全てLOWにする) 1829 only
*********
後は、変更しなくていいハズです。
が、肝心のLCD表示は、各自で考えてね。という事になってるので
表示部分を考えます。
キャラクタLCD「SC1602」は、半角16文字2行なので
ちょっと欲張って少数2位まで、こんな感じにしようと思います。
先の「キャラクタLCDモジュールに表示を行う」の
サンプルプログラムを解凍すると、3つファイルがありますが
skSD1602LCD.c と skSD1602LCD.h を使ってます。
LCD「SC1602」は、4bitパラレルで、6本のI/Oを使います。
I/Oの定義は、「skSD1602LCD.h」に記述されていて
RB2~RB7になっているので、
「Capture.c」で使用するI/Oとカブリはなく
RB2~RB7は、全てデジタル出力として使うので、そのまま使えます^^;
skSD1602LCD.h |
skSD1602LCD.h |
main()を変更・追加していきます。(★1が該当箇所)
*****Capture.c*****
#include <xc.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "skSD1602LCD.h"
~
~中略~
~
PORTB = 0b00000000 ; // RB0-RB7出力ピンの初期化(全てLOWにする) for 1827
// PORTC = 0b00000000 ; // RC出力ピンの初期化(全てLOWにする) 1829 only
/* LCDの初期化処理を行う */
lcd_init() ; //★1
PEIE = 1 ; // 周辺装置割り込み有効
GIE = 1 ; // 全体の割込み処理を許可する
~
~中略~
~
CaptureSet() ; // キャプチャ機能の初期設定を行う
while(1) {
// 周波数計測の終了を待ちます(MAX25KHz程までは計測出来るでしょう)
f = PulseCount(1,1) ; // PlusCount(↑,↑)
//★1 fl = 1000000L / f ; // 周波数の計算[Hz]、1000000LONG÷PlusCount(↑,↑)
fl = 1000L / f ; //★1 周波数の計算[KHz]、1000LONG÷PlusCount(↑,↑)
//★1 sprintf(buf,"%ldHz",fl) ;
sprintf(buf," Freq %6.2f KHz",fl) ; //★1 " Freq xxx.xx KHz"をbufへ
// ここでご自由に表示処理を行いましょう。
lcd_setCursor(0,0) ; //★1 カーソルを1行目の先頭に移動
lcd_puts(buf); //★1 LCD1行目に " Freq xxx.xx KHz"表示
// 1サイクルのON部分の計測の終了を待ちます
d = PulseCount(1,0) ; // PlusCount(↑,↓)
df = ((float)d / (float)f) * 100 ; // デュティ比の計算
//★1 sprintf(buf,"%5.1f%%",df) ;
sprintf(buf," Duty %6.2f%%",df) ; //★1 " Duty xxx.xx%"をbufへ
// ここでご自由に表示処理を行いましょう。
lcd_setCursor(0,1) ; //★1 カーソルを2行目の先頭に移動
lcd_puts(buf); //★1 LCD2行目に" Duty xxx.xx%"表示
//★1 __delay_ms(1000) ;
__delay_ms(100) ; //★1 表示頻度UP
}
*********
ひとまず、1カウント 1μsecでの周波数とDuty計測のプログラムができました。
いざ、コンパイル!
ありゃ~!ERROR一杯であります(T_T)
「largest unused contiguous range 0x」なるerrorが一杯(T_T)
原因が判明するのにかなりの時間を要しました(-_-;)
「sprintf(buf," Duty %6.2f%%",df) ;」の行が原因でしたが
オリジナルの「sprintf(buf,"%5.1f%%",df) ;」でもダメなのです。
どうやら、sprintf()で浮動小数点変数float(上のdf)を指定すると
オーバーフローを起こすようです。
引数(f)と書式設定の中もint型、だとコンパイルは通ります。
sprintf(buf,"%6.2u",f) ;
きむ茶工房さんは、MPLAB X v4.10、XC8 v1.40ですね~
マーティーのは、MPLAB X v2.10、XC8 v1.32です(-_-;)
古いからでしょうね~
バージョアップするとお作法が変わるので、したくないしな~sprintf()を使うことにします。
で、手っ取り早く整数部と小数部を分離しようと思って
使った関数が、
C = modf(A, &B);
・A:分離したい浮動小数点変数
・C:Aの整数部を格納
・&B:Aの小数部を格納する変数Bのアドレス
・#include <math.h> とセットです。
なのですが、
これがまたマーティーを悩ませることに(-_-;)
A、B、C ともfloat型で十分だと思ってやってたら、通らない(T_T)
double型にしないとダメなんですね~(-_-;)
これもわかるまで、あれやこれや時間がかかってしまいました~
分かり難いですが、周波数の表示だけでこんなことになってます(-_-;)
*********
char buf[16] ; // 表示用のバッファ
long f ; // 周波数の生値[Hz]
double fl ; // fl:周波数[KHz]
double fl_i , fl_d ; // fl_i:flの整数部、fl_d:flの小数部
int fl_i_int ; // 浮動小数点flの整数部分fl_iを整数として格納
int fl_d_100 ; // 浮動小数点flの小数部分の100倍を整数として格納
f = PulseCount(1,1) ; // PlusCount(↑,↑)
fl = 1000L / f ; // 周波数の計算[KHz]
fl = fl + 0.005 ; // 0.01KHzの位で四捨五入するため
fl_d = modf(fl, &fl_i) ; // 小数部 = modf(引数, 整数部を格納するアドレス)
fl_i_int = (int)fl_i ; // flの整数部をint型へキャストして小数部を切捨てる
fl_d_100 = fl_d * 100 ; // flの小数部x100して整数として格納
sprintf(buf," Freq %3d.%02dKHz ",fl_i_int,fl_d_100) ; //bufに" Freq xxx.xx KHz "を格納
// ここでご自由に表示処理を行いましょう。
lcd_setCursor(0,0) ; // カーソルを1行目の先頭に移動
lcd_puts(buf); // LCD1行目に bufの内容を表示
*********
で、やっとコンパイルが通ったわけですが、
実は、最初LCD表示が全くでなかったので、真っ青(T_T)
Clock 32MHz⇒8MHzに落としてやっとLCD表示が出ました。
いつの間にか、Program Space:70.9% になってます。
Compiler XC8 v1.32ですが、Free版なので
生成コードがオプティマイズされないのです。
有償版のPRO modeだと一般に40%削減できると書いてあります。
もうちょっと、処理を追加する予定なのですが生成コードがオプティマイズされないのです。
有償版のPRO modeだと一般に40%削減できると書いてあります。
この時点でProgram Space:70.9% じゃ足りなくなりそう(T_T)
sprintf() や modf() を使うのを止めるしかなさそうです^^;
表示レイアウトもちょっと変えます。
まあ、小数点付けるのが面倒だからなのですが(-_-;)
表示の辺りだけ抜粋すると
とても分かり難いですが、バラバラに分解して表示しました。
更にオリジナルの1カウント 1μsecから
Timer Clock=Fosc/4(8MHz Prescale 1:1)にしたので0.5μsecです。
なのでfl=2000000L/f; で周波数をHzで算出してます。
*********
~
char buf[16] ;
long f , d ;
float fl ; //fl(周波数)は、小数まで表示する、modf()使わないのでfloatに戻す
int fl_int ; //周波数はHz表示で整数のみにする。その格納用
float df ; //df(Duty)は、小数まで表示する、modf()使わないのでfloatに戻す
int df_i_int ; //dfx100(%)の整数部分
int df_d_10 ; //浮動小数点dfx100(%)の小数部分の10倍を整数として格納
char freq[7] , duty_i[4] , duty_d[2] ; // LCD表示用に数値をcharに変換用
int lcd_i = 0 ; //LCD表示クリヤのカウント用
~中略~
while(1) {
// 周波数計測の終了を待ちます
f = PulseCount(1,1) ; // PlusCount(↑,↑)
fl = 2000000L / f ; //周波数の計算[Hz]、2000000LONG÷PlusCount(↑,↑) 1count 500nsec
fl = fl + 0.5 ; //0.1Hzの位で四捨五入するため
fl_int = (int)fl ; //fl(Hz)の整数部をint型へキャストして小数部を切捨てる
lcd_setCursor(0,0) ; //カーソルを1行目の先頭に移動
lcd_i = ++lcd_i ;
lcd_puts(" Freq ") ;
itoa(freq,fl_int,10) ; //整数"fl_int"を10進のchar"freq"に変換
lcd_puts(freq) ;
lcd_puts(" Hz") ;
// 1サイクルのON部分の計測の終了を待ちます
d = PulseCount(1,0) ; // PlusCount(↑,↓)
df = ((float)d / (float)f) * 100 ; // デュティ比の計算[%]
df = df + 0.05 ; //0.1%の位で四捨五入するため
df_i_int = (int)df ; //df(%)をintにキャストして整数部をduty_iに格納
df_d_10 = (df - (float)df_i_int) * 10 ;
//df(%)の小数部にx10して、小数部の第1位だけを整数として duty_dに格納
lcd_setCursor(0,1) ; //カーソルを2行目の先頭に移動
lcd_puts(" Duty ") ;
itoa(duty_i,df_i_int,10) ; //整数"df_i_int"を10進のchar"duty_i"に変換
lcd_puts(duty_i) ;
lcd_puts(".") ;
itoa(duty_d,df_d_10,10) ; //整数"df_i_int"を10進のchar"duty_i"に変換
lcd_puts(duty_d) ;
lcd_puts(" %") ;
__delay_ms(100) ; //表示頻度UP
}
*********
Program Space:59.0% になりました!
まあこれくらいだったらよさそうです。
後日、printf()の書式設定は大飯食らいってことを知ったのであります(-_-;)
動いたショットは、これ!
リビングのテーブルにシリコンマット敷いた臨時作業台であります^^;
中央下のは、ATtiny85の方形波発振器です。
DUTYが小さい時。
1KHzですが、DUTYも正しく表示されています。
もしかして、プログラム一式「16F1827_FreqDuty_v6.zip」を見たい方は、
こちらからどうぞ(^_^)
Timer1のClockを1カウント0.5μsecにしたので
最初と同じ様に単純に200倍して±2%と考えると
・クロックソース 0.5μsec・・・40KHz
辺りまで測定できそうです。
上手く動き出したので、更にClockを16MHzに上げて
周波数をどこまで計測できるかやろうとして、
一旦外して書き込んだら、16F1827が死にました_| ̄|○
書き込みソフトでは、WriteもVerifyも「Successful」になってるので、
出力回路が死んでると思われます。
AliExpressでポチってた10個が到着するのを待つしかありません(T_T)
死んでる出力を別PORTに変更すれば、まだ使えそうですが...
それにしてもよく考えると、というか途中で気づいてはいたのですが(-_-;)
Duty100%、つまりDCの時は、
エッジがないので周波数もDUTY比も計測できません。
対策を考えるか別の方法にしないといけないですね~
まあ、これはこれで簡易周波数カウンタとして活躍できるかな^^;
この程度ならArduino+I2C I/FのLCDで作り直した方がいいんでしょうね~
マーティーのプログラミング能力が劣化してるな~(-_-;)
2 件のコメント:
最近は、Arduino IDEばかりで、PICもHEXファイルの提供でなくコンパイルが必要だったりすると、再勉強に時間がかかります。(勉強にはなりますが)
コロナ騒ぎでAlieからの商品到着がえらく時間がかかっていますね。
Arduinoを始める前にやってたので、できたんだと思います。
勉強する順序が逆だったらやってない可能性大ですね~(-_-;)
Aliからは手のひらサイズの封筒の小物は通常っぽいですが
ちょっと大きくなると遅いですね~
それよりも送料が高騰してきてる気がします(T_T)
コメントを投稿