void GrillPid::setOutputDevice(unsigned char outputDevice) { TIMSK1 = 0; // Fast PWM Timer with TOP=ICR1 with no OC1x output TCCR1A = bit(WGM11); switch (outputDevice) { case PIDOUTPUT_Default: case PIDOUTPUT_Fan: _outputDevice = PIDOUTPUT_Fan; // 64 prescaler TCCR1B = bit(WGM13) | bit(WGM12) | bit(CS11) | bit(CS10); // 511 TOP to be close to original 488Hz and allows OCR1B to be set to // 512 to prevent COMB_vect from firing (which means no LOW when at 100%) ICR1 = 511; break; case PIDOUTPUT_Servo: _outputDevice = PIDOUTPUT_Servo; // 8 prescaler gives us down to 30Hz with 0.5usec resolution TCCR1B = bit(WGM13) | bit(WGM12) | bit(CS11); // TOP = servo refresh ICR1 = uSecToTicks(SERVO_REFRESH); break; } // Interrupt on overflow, and OCB TIMSK1 = bit(OCIE1B) | bit(TOIE1); }
inline void GrillPid::commitServoOutput(void) { #if defined(GRILLPID_SERVO_ENABLED) unsigned char output; if (bit_is_set(_outputFlags, PIDFLAG_SERVO_ANY_MAX) && _pidOutput > 0) output = 100; else output = _pidOutput; if (bit_is_set(_outputFlags, PIDFLAG_INVERT_SERVO)) output = 100 - output; // Get the output speed in 10x usec by LERPing between min and max output = mappct(output, _minServoPos, _maxServoPos); // Servo output is actually set on the next interrupt cycle _servoOutput = uSecToTicks(10U * output); #endif }
inline void GrillPid::commitFanSpeed(void) { /* Long PWM period is 10 sec */ const unsigned int LONG_PWM_PERIOD = 10000; const unsigned int PERIOD_SCALE = (LONG_PWM_PERIOD / TEMP_MEASURE_PERIOD); calcExpMovingAverage(FANSPEED_AVG_SMOOTH, &FanSpeedAvg, _fanSpeed); if (_outputDevice == PIDOUTPUT_Fan) { /* For anything above _minFanSpeed, do a nomal PWM write. For below _minFanSpeed we use a "long pulse PWM", where the pulse is 10 seconds in length. For each percent we are emulating, run the fan for one interval. */ unsigned char output; if (_fanSpeed >= _minFanSpeed) { output = _fanSpeed; _longPwmTmr = 0; } else { // Simple PWM, ON for first [FanSpeed] intervals then OFF // for the remainder of the period if (((PERIOD_SCALE * _fanSpeed / _minFanSpeed) > _longPwmTmr)) output = _minFanSpeed; else output = 0; if (++_longPwmTmr > (PERIOD_SCALE - 1)) _longPwmTmr = 0; } /* long PWM */ if (_invertPwm) output = _maxFanSpeed - output; OCR1B = (unsigned int)output * 512 / 100; } else { // GrillPidOutput::Servo unsigned char output; if (_invertPwm) output = 100 - _fanSpeed; else output = _fanSpeed; // Get the output speed in 10x usec by LERPing between min and max output = ((_maxFanSpeed - _minFanSpeed) * (unsigned int)output / 100) + _minFanSpeed; OCR1B = uSecToTicks(10U * output); } #if defined(TIMER1_DEBUG) SerialX.print("HMLG,0,"); SerialX.print(" ICR1="); SerialX.print(ICR1, DEC); SerialX.print(" OCR1B="); SerialX.print(OCR1B, DEC); SerialX.print(" TCCR1A=x"); SerialX.print(TCCR1A, HEX); SerialX.print(" TCCR1B=x"); SerialX.print(TCCR1B, HEX); SerialX.print(" TCCR1C=x"); SerialX.print(TCCR1C, HEX); SerialX.print(" TIMSK1=x"); SerialX.print(TIMSK1, HEX); if (ovf) { ovf = false; SerialX.print(" ovf"); } if (rst) { SerialX.print(' '); SerialX.print(rst, DEC); rst = 0; } if (bit_is_set(TIFR1, TOV1)) { SerialX.print(" TOV1"); TIFR1 = bit(TOV1); } SerialX.nl(); #endif }