Skip to content

Instantly share code, notes, and snippets.

@PhirePhly
Created June 12, 2012 04:42
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save PhirePhly/2915052 to your computer and use it in GitHub Desktop.
Save PhirePhly/2915052 to your computer and use it in GitHub Desktop.
A simple Integral fan controller driving three PC fans.
#include "msp430G2452.h"
#define FAN_CNT (3)
#define PWM_FREQ (32)
#define PWM_RESS (128)
// Port 1
#define LED1 (1<<0)
#define TMP_RX (1<<1)
static const int TACH[] = { (1<<3), (1<<4), (1<<5) };
// Port 2
static const int FAN[] = { (1<<0), (1<<1), (1<<2) };
#define LED1_ON() (P1OUT &= ~LED1)
#define LED1_OFF() (P1OUT |= LED1)
volatile struct {
int second;
int fracofsec;
unsigned int rpmdata[2][FAN_CNT];
unsigned int maxrpm[FAN_CNT];
unsigned int fanpwr[FAN_CNT];
unsigned int fanrpm[FAN_CNT];
} fs;
void tempCTLAlg(void);
int bound(int low, int mid, int high);
int takeADCReading(int channel);
void main(void) {
// Kill Boris - we don't need a watchdog
WDTCTL = WDTPW + WDTHOLD;
// Clock system
DCOCTL = CALDCO_16MHZ;
BCSCTL1 = CALBC1_16MHZ;
BCSCTL2 = SELS; // DCO slave clock
// Timer A
TA0CTL = TASSEL_2 | MC_1; // SCLK, UP
TA0CCR0 = (32768 / PWM_FREQ / PWM_RESS) - 1;
TA0CCTL0 = CCIE;
// ADC10
ADC10CTL0 = SREF_0 | ADC10ON; // Vcc reference
ADC10CTL1 = INCH0 | ADC10SSEL1; // Channel A1, MCLK
ADC10AE0 = (1<<1);
// IO Port 1
P1DIR = LED1;
P1IE = TMP_RX | TACH[0] | TACH[1] | TACH[2]; // Enable ints
P1IES = 0xFF; // H-L transition int
P1IFG = 0x00; // Clear noise interrupts
// IO Port 2
P2DIR = FAN[0] | FAN[1] | FAN[2];
// Start interrupts
_BIS_SR(GIE);
int i;
// Three second startup at full power
for (i=0; i<FAN_CNT; i++)
fs.fanpwr[i] = PWM_RESS;
while (fs.second <3) ;
for (i=0; i<FAN_CNT; i++) {
fs.maxrpm[i] = fs.rpmdata[1][i];
fs.fanrpm[i] = 2 * fs.maxrpm[i] / 3;
}
fs.fanrpm[0] = fs.fanrpm[0] / 2;
while (1) {
if (fs.second) {
fs.second--;
tempCTLAlg();
}
}
}
// Temperature Control Algorithm
// Do the 1Hz work to try and keep the temperature on target
void tempCTLAlg(void) {
int tempsense = takeADCReading(1);
int lightvote = 0;
int i;
int tempDelta = tempsense - 280;
for (i=0; i<FAN_CNT; i++) {
fs.fanrpm[i] = bound(fs.maxrpm[i]>>2, fs.fanrpm[i] + tempDelta, fs.maxrpm[i]);
int rpmDelta = fs.rpmdata[1][i] - fs.fanrpm[i];
fs.fanpwr[i] = bound(0, fs.fanpwr[i] - (rpmDelta/250), PWM_RESS);
if (rpmDelta < 0) { // too slow
lightvote++;
} else {// too fast
lightvote--;
}
}
if (tempDelta > 0) {
LED1_ON();
} else {
LED1_OFF();
}
}
int bound(int low, int mid, int high) {
if (low > mid)
mid = low;
if (high < mid)
mid = high;
return mid;
}
int takeADCReading(int channel) {
ADC10CTL1 = (ADC10CTL1 & 0x0FFF) | (channel << 12);
ADC10CTL0 |= ENC | ADC10SC;
while (ADC10CTL1 & ADC10BUSY) ;
return ADC10MEM;
}
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_A00 (void) {
int i, fracofpwm;
fs.fracofsec = (fs.fracofsec + 1) % (PWM_FREQ * PWM_RESS);
if (fs.fracofsec == 0) {
for (i=0; i<FAN_CNT; i++) {
fs.rpmdata[1][i] = 30 * fs.rpmdata[0][i];
fs.rpmdata[0][i] = 0;
}
fs.second++;
}
fracofpwm = fs.fracofsec%PWM_RESS;
if (fracofpwm == 0) {
// Turn on enabled fans
P2OUT |= FAN[0] | FAN[1] | FAN[2];
}
for (i=0; i<FAN_CNT; i++) {
if (fs.fanpwr[i] < fracofpwm) {
P2OUT &= ~(FAN[i]);
}
}
}
#pragma vector=PORT1_VECTOR
__interrupt void Port1 (void) {
unsigned int pins = P1IFG;
P1IFG = 0x00;
unsigned int i;
for (i=0; i<FAN_CNT; i++) {
if (pins & TACH[i]) {
fs.rpmdata[0][i]++;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment