/* * File: main.c * Author: idzero * * Created on 2017/07/28, 16:24 * * PWMファンコントローラ * * 機能説明 * 1. PWMによる回転数制御 * 2. ファンON/OFF機能 *  ファンのPWM信号では完全に停止できないので、4端子のファンでもファンの電源を直接PWM制御すること。 * 3. STOPタイマ機能(1時間単位) * * 動作説明 * 1. DC電源接続後、スタンバイ(ST_STANDBY)になる。 * 2. 無操作の場合、SLEEP_TOUT_VALで指定された時間経過後、スリープ(ST_SLEEP)する。 * 3. 電源ボタンが押された場合、1.5秒以下の短押しは、STOPタイマ設定(ST_SPTIMER)になる。 * 4. 長押しの場合は、電源ON(ST_PWRON)となる。最初に高速回転し、その後、EEPROMに保存された回転数する。 * 5. 電源ONで、ロータリエンコーダの右回転(時計回り)でファン用PWM DUTY比を上げる。左回転でDUTY比を下げる。 * 6. 電源ONで、STOPタイマが設定されていた場合、指定時間が経過するとファン停止して、スタンバイになる。 * 7. 電源ONで、電源ボタンの長押しで、スリープになる。このとき、回転数設定をEEPROMに保存する。 * 8. スリープ時、電源ボタンで一旦スタンバイになり、短押しならば、そのままSTOPタイマ設定へ、長押しならば、電源ONする。 * 9. STOPタイマ設定は、右回転1クリックで1時間、左回転で-1時間。ゆっくり廻すこと。電源ボタン短押しで終了する。 * 10.ロータリエンコーダでチャタリングが酷い場合、A端子-GND間、B端子?GND間に0.01uF程度のコンデンサを入れノイズ吸収させる。 *   ただしその場合、ICSP機能が使用できなくなる。 */ #include // CONFIG1 #pragma config FOSC = INTOSC // Oscillator Selection bits #pragma config WDTE = OFF // Watchdog Timer Enable bit #pragma config PWRTE = ON // Active Low, Power-up Timer Enable bit #pragma config MCLRE = OFF // RA3/MCLR pin function select bit //#pragma config MCLRE = ON // RA3/MCLR pin function select bit デバッグ時、ONする #pragma config BOREN = ON // Brown Out Reset Selection bits #pragma config IESO = ON // Internal External Switchover bit #pragma config FCMEN = ON // Fail-Safe Clock Monitor Enabled bit #pragma config LVP = OFF // Low Voltage Programming Enable bit #define _XTAL_FREQ 8000000 /* STOPタイマ設定最大時間 */ #define STOP_TIMER_MAX 8 /* 時間 */ /* SLEEPまでの時間 */ #define SLEEP_TOUT_VAL 10 /* 秒 */ /* FAN印加電圧 PWM Duty(ここで調整する) */ #define PWM_DUTY_STEP 5 /* 変更ステップ */ #define PWM_DUTY_MIN 5 /* 低速回転 */ #define PWM_DUTY_MAX 100 /* 高速回転 */ /* 変更不可 */ #define PWM_MAX ((PWM_DUTY_MAX * 2)+4) #define PWM_MIN ((PWM_DUTY_MIN * 2)+4) #define PWM_STEP PWM_DUTY_STEP * 2 #define PWM_INIT 100 /* * I/O port assign * RA4 : Rotary Encoder A * RA5 : Rotary Encoder B * RA2 : FAN control PWM out * RA3 : Push switch * RA0 : Blue LED * RA1 : Red LED */ #define LED_BLUE PORTAbits.RA0 #define LED_RED PORTAbits.RA1 #define PWR_BTN PORTAbits.RA3 #define A_pin PORTAbits.RA4 #define B_pin PORTAbits.RA5 #define LED_ON 1 #define LED_OFF 0 /* pwr_state ステート値 */ #define ST_STANDBY 0 #define ST_PWRON 1 #define ST_SPTIMER 2 #define ST_SLEEP 3 char sp_tup; /* Sleep Time Up */ char stop_enb; /* STOP TIMER Enable */ unsigned char pwm; /* 200 = 100% = 100us */ unsigned int t10ms, t10ms_dec, t100ms, t100ms2, t1s, t1s_dec; unsigned char stop_timer; /* STOP Timer Value */ unsigned char sleep_tout; /* Time Out Value to Sleep */ /* PWM値保管用EEPROM */ #define PWM_EEP 0 __EEPROM_DATA(PWM_INIT,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF); void interrupt intr(void){ /* 10ms Timer1 処理 */ if (TMR1IE == 1 && TMR1IF == 1){ /* Every 10ms */ TMR1 = 63035; TMR1IF = 0; t10ms++; --t10ms_dec; /* 100msカウント */ if(t10ms >= 10){ t10ms = 0; t100ms++; t100ms2++; } /* 1秒カウント */ if(t100ms >= 10){ /* 100ms x 10 -> 1sec */ t100ms = 0; t1s++; --t1s_dec; sleep_tout++; } /* 1時間カウント (Stop Timer) */ if(t1s >= 3600){ t1s = 0; if(stop_enb){ if(--stop_timer == 0) sp_tup = 1; } } } /* 電源ボタン(RA3) でSleepから復帰 */ if(IOCIF && IOCAF3){ IOCAF3 = 0; } } /* * iwamotoさん「はじめてのPIC」のロータリエンコーダ取得関数 (感謝!) * http://sky.geocities.jp/home_iwamoto/page/Psoft/Psoft_B02.htm */ char readRE(void){ /* ロータリーエンコーダのAB出力組み合わせでチャタリングの少ない個所を選択 */ /* Lowレベルのときにチャタリングが出やすく、Highは間違いの可能性がある。 */ /* 出来るだけLowレベルが多いパターンを選択する。 */ /* また信号が切り替わった直後が出やすい。 */ static char RE_states[] = {0,1,-1,0,0,0,0,0,0,0,0,0,0,0,0,0}; // static char RE_states[] = {0,0,0,0,0,0,0,1,0,0,0,-1,0,0,0,0}; static char RE_old = 0; // 共通変数(値は保存) char RE_now; RE_now = B_pin * 2 + A_pin; // 今回情報の読取 RE_old <<= 2; // 前回の読取値と RE_old |= ( RE_now & 0x03 ); // 今回の読取値を組合わせる return (RE_states[(RE_old & 0x0F)]); // 変化分を戻り値とする } void main(void) { char n; char pwr_state; /* CPU Clock 設定 */ OSCCON = 0b01110010; /* Clock 8MHz internal */ /* PORT A 入出力設定 (1b:Input 0b:Output) */ ANSELA = 0; /* 全てデジタル I/O */ TRISA = 0b11111100; /* bit0,1 出力 */ PORTA = 0b00000000; WPUA = 0b00111011; /* Port A Weak Pull Up */ /* IOC 設定 */ IOCAF = 0; IOCAN3 = 1; IOCIE = 1; /* OPTION REG, TIMER0 設定 */ OPTION_REG = 0b00000010; /* Prescaler -> 1:8 (4us) */ /* TIMER1 設定(未使用) */ T1CON = 0b00110001; /* 16bit mode RD16:1 */ /* Prescaler -> 1:8 */ /* Clock Source -> Internal */ /* Timer1 -> Start */ TMR1IE = 0; /* Timer1 Interrupt Disable */ TMR1 = 63035; /* Timer1 Value Set */ TMR1IF = 0; /* Timer1 Interrupt Flag Clear */ /* PWM 設定 */ PR2 = 50; /* PWM Cycle 10KHz = 100us */ CCP1CON = 0b00001100; /* PWM Mode & Duty 2LSB(bit5,4) */ CCPR1 = 0x0; /* Init Zero , 8MSB */ TMR2IE = 0; /* Timer2 Interrupt Disable */ TMR2IF = 0; /* Timer2 Interrupt Flag Clear */ T2CON = 0b00000001; /* Prescaler = 1:4 */ TMR2ON = 1; /* Timer 2 Enable */ TRISAbits.TRISA2 = 0; /* Enable CCP1 out */ t10ms = t100ms = t1s = 0; pwr_state = ST_STANDBY; stop_timer = 0; sp_tup = 0; stop_enb = 0; sleep_tout = 0; TMR1IE = 1; PEIE = 1; GIE = 1; while(1){ switch(pwr_state){ /* スタンバイ状態 */ case ST_STANDBY: /* 無操作タイムアウト検出 */ if(sleep_tout > SLEEP_TOUT_VAL){ pwr_state = ST_SLEEP; break; } /* 電源ボタンを押す長さ確認 */ t100ms2 = 0; while(!PWR_BTN && t100ms2 <= 30); /* ボタンが離されるまで待つ */ if(t100ms2 < 1){ /* 押されていない */ break; }else if(t100ms2 > 1 && t100ms2 < 15){ /* 短押し */ LED_BLUE = LED_ON; LED_RED = LED_ON; __delay_ms(1000); LED_BLUE = LED_OFF; LED_RED = LED_OFF; pwr_state = ST_SPTIMER; break; }else if(t100ms2 > 30){ /* 長押し */ /* 電源ON処理 */ pwm = PWM_MAX; CCPR1 = pwm>>2; /* FAN 初期回転待ち */ t1s_dec = 3; while(t1s_dec){ LED_BLUE =~ LED_BLUE; __delay_ms(300); } pwm = eeprom_read(PWM_EEP); if(pwm < PWM_MIN) pwm = PWM_MIN; CCPR1 = pwm>>2; LED_BLUE = LED_ON; pwr_state = ST_PWRON; } break; /* 電源ON状態 */ case ST_PWRON: sleep_tout = 0; /* STOP タイマー処理 */ if(stop_enb && sp_tup){ sp_tup = 0; pwr_state = ST_STANDBY; sleep_tout = 0; break; } /* ロータリエンコーダ 処理 */ n = readRE(); switch(n){ case 1: /* 右回転 → 高速 */ if(pwm < PWM_MAX){ pwm = pwm + PWM_STEP; CCPR1 = pwm>>2; } break; case -1: /* 左回転 → 低速 */ if(pwm > PWM_MIN){ pwm = pwm - PWM_STEP; CCPR1 = pwm>>2; } break; default: break; } /* 最高速では 赤色LED点灯 */ if(pwm >= PWM_MAX) LED_RED = LED_ON; else LED_RED = LED_OFF; /* 電源OFF(ボタン長押し)チェック */ t1s_dec = 3; while(!PWR_BTN){ if(!t1s_dec){ pwr_state = ST_SLEEP; break; } } break; /* Stop Timer 設定 */ case ST_SPTIMER: sleep_tout = 0; /* ロータリエンコーダ 処理 */ n = readRE(); switch(n){ case 1: /* 右回転 → 時間増加 */ if(stop_timer < STOP_TIMER_MAX){ stop_timer++; LED_BLUE = LED_ON; __delay_ms(200); LED_BLUE = LED_OFF; __delay_ms(200); } break; case -1: /* 左回転 → 時間減少 */ if(stop_timer > 0){ --stop_timer; LED_RED = LED_ON; __delay_ms(200); LED_RED = LED_OFF; __delay_ms(200); } break; default: break; } /* Stop Timer LED確認 */ if(!PWR_BTN){ if(stop_timer){ for(n=0; n