/* Firmware for the Temperaure fan controller */ /* Version 1.2, March, 2006 */ /* Jos van Eijndhoven (jos@vaneijndhoven.net) */ /* */ /* Intended for compilation with the (freeware) */ /* CC5X compiler, version 3.2 */ /* see: http://www.bknd.com/cc5x/index.shtml */ /********************************************************************/ /* Note: before programming (erasing) a 16F676 device, first read */ /* its CONFIG register and its last (0x3ff) program word! */ /* These contain factory-programmed calibration values, */ /* that become lost by (re-)programming! */ /* 1st sample: read last program word=343C config:'bandgap midhigh' */ /* */ /* Operation: */ /* TMR0 is used to count up, driven by the internal oscillator. */ /* In 'set_fans()' the counter value is compared with target values */ /* to create PWM output signals. */ /* Four analog inputs are sampled: three LM60 temperature sensors */ /* and one 'minimum fan speed' setting. */ /* At TMR0 wrap-around all PWM outputs are activated and a limited */ /* amount of computation is done. During the TMR0 loop, the PWM */ /* outputs are reset one-by-one. */ /* The PWM frequency can be adjusted with the TMR0 prescaler, */ /* typically between 64 and 512 Hz. */ /* A global 'time' is maintained for a slow reduction of fan speed, */ /* avoiding control loop instability. Note that this notion of time */ /* floats with the TMR0 prescaler. */ /********************************************************************/ #include <16F676.h> /* config 13,12 have bandgap calibration value */ #pragma config =0x21c5 /* INTOSC oscillator, with clkout, do BOD, PWRT, no WDT, no MCLR */ #pragma config ID=0xfa01 // PORTA bit icspdat @ PORTA.0; // p13: ICSPDAT bit icspclk @ PORTA.1; // p12: ICSPCLK, and reference at half vdd (2.5V) bit minrpm @ PORTA.2; // p11: AN2 bit mclr @ PORTA.3; // p04: bit clkout @ PORTA.4; // p03: bit leda @ PORTA.5; // p02: // PORTC bit temp1 @ PORTC.0; // p10: AN4 bit temp2 @ PORTC.1; // p09: AN5 bit temp3 @ PORTC.2; // p08: AN6 bit fan3 @ PORTC.3; // p07: bit fan2 @ PORTC.4; // p06: bit fan1 @ PORTC.5; // p05: bit ADCgo @ ADCON0.1; // PWMFREQ is 0,1,2,3, for a PWM frequency of resp. 512Hz, 256Hz, 128Hz, 64Hz #define PWMFREQ 3 uns8 fanspeed[4]; // [0] is used for LED output, showing current max temperature uns8 fansample[4]; // [0] is used for sampling min_speed uns8 calibrate_clk( void); void pic_init( void) { /* PORTA */ TRISA = 0x07; // A.0 to A.2 are inputs PORTA = 0; ADCON0= 0x41; // left-justified, ext Vref, AD power-up ADCON1= 0x50; // rate Fosc/16 ANSEL = 0x74; // AN6,5,4,2 are analog /* PORTC */ TRISC = 0x07; /* portC 0,1,2 are input */ PORTC = 0x3f; /* fans on */ /* TMR0 will wrap at about 500 Hz (or 250, or 125, or 60Hz) */ OPTION_REG = 0x82 + PWMFREQ; /* no pull-up portA, TMR0 on clk, prescale=8 */ OSCCAL = calibrate_clk(); /* calibrate on-chip oscillator */ T1CON = 0x00; GIE = 0; /* no interrupts allowed */ } void ad_input( uns8 sel) { switch (sel) { case 1: ADCON0 = 0xd1; break; // AN4, Vref=extern, Right-justified case 2: ADCON0 = 0xd5; break; // AN5, Vref=extern, Right-justified case 3: ADCON0 = 0xd9; break; // AN6, Vref=extern, Right-justified default: ADCON0 = 0x09; // minRPM: AN2, Vref=Vdd, Left justified } /* after setting ADCON, we need some time to stabilize the analog input */ } void set_fans( void) { while (!T0IF) { mclr = 0; if (TMR0 > fanspeed[0]) leda = 0; // off if (TMR0 > fanspeed[1]) fan1 = 0; // off if (TMR0 > fanspeed[2]) fan2 = 0; if (TMR0 > fanspeed[3]) fan3 = 0; } T0IF = 0; leda = 1; mclr = 1; PORTC = 0xff; // all fans on } void scale( uns8 sel) { /* AD full scale (Vref) is 1.6V, 64 LSB counts is 100mV */ /* 580mV -> fan off, 680mV -> fan full on */ /* 580mV/1.6mV = 362; mod 256 = 106 counts */ uns8 base; if (sel == 0) // global min fan speed, at ADC full scale { fansample[0] = ADRESH; // was left-justified } else { // LM60 temp sensor input, right-justified if ((ADRESH == 0) || ((ADRESH == 1) && (ADRESL < 220))) fansample[sel] = 0; else if ((ADRESH >= 3) || ((ADRESH == 2) && (ADRESL >= 28))) fansample[sel] = 255; else { base = ADRESL - 220; // min 0 && max 63 fansample[sel] = base << 2; } } } uns8 time; void damp( uns8 sel) { uns8 minspeed, mysample; if (sel == 0) // set led to highest current temp { // led-color (green-red) is created by fanspeed[0] value fanspeed[0] = fansample[1]; if (fansample[2] > fanspeed[0]) fanspeed[0] = fansample[2]; if (fansample[3] > fanspeed[0]) fanspeed[0] = fansample[3]; return; } // else, sel=[1,2,3], denotes actual fan minspeed = fansample[0]; // [0] refers here to analog input 'minspeed' setting mysample = fansample[sel]; // scaled temp sensor value for this fan if (minspeed >= mysample) mysample = minspeed; // higher temp? then increase fan speed immediatly if (mysample >= fanspeed[sel]) { fanspeed[sel] = mysample; return; } // else, decrease fanspeed slowly: full-to-zero (256 cnts) in 500sec if (fanspeed[sel] == 0) // fan stands still already return; if (time == 0) fanspeed[sel] -= 1; } void main( void) { uns8 sel; pic_init(); time = 0; while (1) { time += (4<