トビラの絵は今回の話とあまり関係ないJD-Sterで壊れたサイドカバーの修復。
もともと同じプラリペアで修復していたのだが、破断面の処理が甘くプラリペアごと外れてしまった模様。破壊した人達から外れた部品無かったよとその報告があったので、今回は破断面をノコギリ状に処理して、ダイソーで買っておいたプラスチック粘土を型取り君の代用に使い。せっせと修復した。今度は指でおしても外れそうもないくらいガッチリ修復できている
昨年、たまたま見つけて購入したプラスチック粘土「おゆまる」ダイソー

急に寒くなりましたねぇ。3Dプリンタの話の予定でしたが部材が揃わず、待ち状態です。
LM2917の使い方が、完全理解とはならなくてそのジレンマから、逃げ出した1週間。
何をしたかというとPICに舞い戻ってしまったというお話。
今回の純正タコメータの最大入力が5V以下だったのでそこをヒントとして捉えました。
何をしてるかというと0-14000rpmの範囲のパルスを受けてそれに前回の表に見合う電圧を出力しているというものです。でも落とし穴が・・
ステッピングモータのキャプチャ部分を分捕れば、半分完成したようなものですが、それじゃ面白く無いし、自分の糧とならないので最初から書き直していますが、あれから時間が経っているのでいろいろ間違っていました。まず出力が出ません。インターフェース指定を間違っていたのでちゃんとでるようになったのですが、入力キャプチャが美味く働きません。以前は出来てたのに何を間違ってるのか。。
ヒントでも貰おうかと自分の作ったものを見直すとまた定義を間違ってるという情けなさでした。
MMCを使えない当方が悪いのです。
ステッピングタコメータの揺らぎを思ったように制御出来ず、デジタル+アナログのハイブリッドタコメータが爆誕するといいなぁ。純正タコメータの電子回路部分の代替にはなってるので当初の目的にはギリセーフなんて作ってましたが・・
以下プログラム
#include <xc.h>
#include <stdint.h>
#include <stdbool.h>
// CONFIG設定 12F1822
#pragma config FOSC = INTOSC // 内部クロック
#pragma config WDTE = OFF // ウォッチドッグ無効
#pragma config MCLRE = OFF // MCLR無効(RA3はI/O)
#pragma config CP = OFF, BOREN = OFF, PWRTE = OFF
#define _XTAL_FREQ 8000000 // 8MHz内部クロック
#define MAX_DAC_VALUE_FOR_4V3 28
#define MAX_RPM 14000
// 2000 RPMの周期(15000 us)のカウント値。
// TMR1 Prescale 1:8, Fosc/4 = 2MHz (1 count = 4 us) -> 15000 us / 4 us/count = 3750 counts
#define RECIPROCAL_THRESHOLD_COUNT 3750
// 60,000,000 (us/min) / 2 (pulses/rev) = 30,000,000
#define RPM_PERIOD_FACTOR 30000000
#define TMR1_PRESCALE_FACTOR 8 // 1:8 プリスケーラを使用
#define RPM_MIN_THRESHOLD 500
#define PERIOD_COUNT_FOR_500RPM 15000 // 60ms周期 → 15000カウント(4us/カウント)
volatile uint16_t last_capture = 0;
volatile uint16_t pulse_period_count = 0;
volatile bool reciprocal_ready = false;
volatile uint16_t pulse_count = 0;
// 割り込みサービスルーチン
void __interrupt() isr(void) {
// CCP1キャプチャ割り込み処理(RA5)
if (PIE1bits.CCP1IE && PIR1bits.CCP1IF) {
PIR1bits.CCP1IF = 0;
uint16_t current_capture = ((uint16_t)CCPR1H << 8) | CCPR1L;
// オーバーフローを考慮して周期を計算(wrap-around対応)
pulse_period_count = (uint16_t)(current_capture - last_capture);
last_capture = current_capture;
reciprocal_ready = true;
pulse_count++;
}
}
void system_init(void) {
// クロック設定 (8MHz)
OSCCON = 0b01110010;
// I/O設定
ANSELA = 0x00; // すべてデジタル
TRISA = 0b00101000; // RA5(入力)それ以外は出力
// CCP1入力ピン設定(RA5)
APFCON0bits.CCP1SEL = 1;
// DAC設定(VDD基準、RA0出力)
DACCON0 = 0b11100000 ; // VDD/VSSを使用、DACOUTピン(RA0)出力する
DACCON1 = 0;
// Timer1設定(Fosc/4, 1:8プリスケーラ)
TMR1H = 0; TMR1L = 0; //Timer1初期化
T1CONbits.TMR1CS = 0; // クロック = Fosc/4
T1CONbits.T1CKPS = 0b11; // プリスケーラ 1:8
T1CONbits.TMR1ON = 1; // Timer1 ON
// CCP1設定:立ち上がりエッジでキャプチャ
CCP1CON = 0b00000101;
PIE1bits.CCP1IE = 1; // CCP1割り込み初期化
PIE1bits.TMR1IE = 0; // Timer1オーバーフロー無効
INTCONbits.PEIE = 1; // 周辺割り込み許可
INTCONbits.GIE = 1; // 全体割り込み許可
}
// レシプロカル方式でRPM測定
uint8_t calculate_dac_value(uint16_t rpm) {
if (rpm >= MAX_RPM) return MAX_DAC_VALUE_FOR_4V3;
uint32_t temp = (uint32_t)rpm * MAX_DAC_VALUE_FOR_4V3;
return (uint8_t)(temp / MAX_RPM);
}
uint16_t measure_rpm_by_period(void) {
if (!reciprocal_ready || pulse_period_count == 0) return 0;
reciprocal_ready = false;
if (pulse_period_count > PERIOD_COUNT_FOR_500RPM) {
return 0; // 500RPM未満とみなして無視
}
uint32_t rpm_calc = RPM_PERIOD_FACTOR / TMR1_PRESCALE_FACTOR;
rpm_calc = rpm_calc / pulse_period_count;
if (rpm_calc > MAX_RPM) return MAX_RPM;
return (uint16_t)rpm_calc;
}
// ダイレクト方式でRPM測定
uint16_t measure_rpm_by_count(void) {
INTCONbits.GIE = 0;
uint16_t count = pulse_count;
pulse_count = 0;
INTCONbits.GIE = 1;
uint32_t temp_rpm = (uint32_t)count * 300;
if (temp_rpm < RPM_MIN_THRESHOLD) return 0;
if (temp_rpm > MAX_RPM) return MAX_RPM;
return (uint16_t)temp_rpm;
}
// RPMに応じたDAC出力処理(ハイブリッド方式)
void process_rpm_hybrid(void) {
static uint8_t timeout_counter = 0;
uint16_t rpm;
if (pulse_period_count > RECIPROCAL_THRESHOLD_COUNT) {
rpm = measure_rpm_by_period();
} else {
rpm = measure_rpm_by_count();
}
if (rpm == 0) {
timeout_counter++;
if (timeout_counter >= 5) {
DACCON1 = 0; // 無信号状態として0V出力
timeout_counter = 5; // 上限保持
}
} else {
timeout_counter = 0;
DACCON1 = calculate_dac_value(rpm);
}
}
/*
// DEMO1:RPMスイープデモ(0→14000→0 RPM)2000ms
void demo_rpm_sweep(void) {
// 上昇フェーズ(910ms)
for (uint16_t rpm = 0; rpm <= MAX_RPM; rpm += 200) {
DACCON1 = calculate_dac_value(rpm);
__delay_ms(13);
}
DACCON1 = MAX_DAC_VALUE_FOR_4V3;
__delay_ms(180);
// 下降フェーズ(910ms)
for (uint16_t rpm = MAX_RPM; rpm >= 200; rpm -= 200) {
DACCON1 = calculate_dac_value(rpm);
__delay_ms(13);
}
DACCON1 = 0;
}
// 90秒間 4.3V 出力デモ(テスト参考用)
void demo_output_90s(void) {
DACCON1 = MAX_DAC_VALUE_FOR_4V3;
for (int i = 0; i < 90; i++) {
__delay_ms(1000);
}
DACCON1 = 0;
}
*/
void main(void) {
system_init();
//demo_rpm_sweep();
//demo_output_90s(); // 必要なら使用
while (1) {
__delay_ms(100);
process_rpm_hybrid();
}
}
// Mooncat 2025/10/25
以下が未完成な回路図(MCP6002がこの通りで良いか謎)
回路図は最初に想定した12F1840ですが、プログラム領域はそんなに要らなかったので以前のモデルの12F1822を使用しており、回路図は少しだけ実際と使うものと異なります。
この回路出力のバッファー前はサーキットテスターであれば変化を拾えますが電圧計を動かすような電流値は出力出来ません。後段(MCP6002など)のバッファが必要になります。
トランジスタ回りが怪しい回路図。

で、結果はと言うと爆誕しませんでした。また、半分作って失敗だなと思う。高インピーダンスのテスターでは面白いほど機能してるのに低インピーダンスの電圧計であるタコメータに繋ぐとちゃんと動かない。
やっぱりLM2917しかないかと最初に戻るという訳です。
PIC→ESP32-C3→アナログに戻っていたので良い勉強になったという感じの悪い顛末でした。

コメント