Below is our code for the project. To skip to the transmitter code, click here.
To access the original .c files, click click here for the main code and click here for the transmit code
// // Cornell 2005 // Edward Lo and Sihan Goi // ECE476 final project // Temperature sensor and fan controller // #include <mega32.h> #include <stdio.h> #include <string.h> #include <delay.h> //number of supported fans #define MAXFAN 2 //wireless packet definitions (maintain DC balance) #define start_packet 0b10100110 #define stop_packet 0b11010010 //timeout values for each task #define t1 20000 //Update LCD #define t2 20000 //Hyperterminal display #define t3 1000 //Hyperterminal input #define t4 500 //Read temperature #define t5 100 //Adjust fan speed #define t6 5000 //PWM pulse stretching //values for temperature calibration #define inittemp 0.76 #define roomtemp 72.0 #define opampGain 4.00 #define Arefvolt 5.09 //other thresholds #define t_lockedrotor 5 //when a fault occurs for this many cycles, the user is notified //conversion macros #define getTemp(input) ((float)(roomtemp +( ((float)((float)input-fproomtemp)/255.0)*Arefvolt/(0.01 * opampGain) ))) #define getRpm(input) ((unsigned int)(60000.0 / (((float)input/10.0) * tachdivider))) //I like these definitions #define begin { #define end } #define LCDwidth 16 //characters //lcd definitions #asm .equ __lcd_port=0x15 #endasm #include <lcd.h> // LCD driver routines enum { pwm, dac, off }; //fan mode enum { user, auto }; //opmode enum { wireless, computer }; //inputmode enum { start, data, stop }; //wireless data packets //variables unsigned char i; //local counter unsigned char j; //counter in ISR unsigned char opmode, inputmode, fanmode[MAXFAN]; //operation modes unsigned int counttime, time1, time2, time3, time4, time5, time6; //task scheduling timeout counters //wireless stuff unsigned char rstate; //receive state unsigned char datain; //data from serial in //lcd stuff unsigned char lcddispnum; //remembers which fan we are displaying status for char lcd_buffer[17]; //LCD display buffer //alarm stuff unsigned char f_alarm, f_alarmsound; //flags to determine if an alarm should light and sound //temperature sensing stuff unsigned char sensornum; //remembers which temperature sensor we are currently reading from unsigned char tempsensor[MAXFAN]; //temperature reading from ADC register unsigned char t_mintemp[MAXFAN]; //minimum temperature threshold when fan turns on unsigned char t_alarmtemp[MAXFAN]; //maximum temperature threshold when alarm sounds float fpcurrtemp[MAXFAN]; //current temperature float t_fpmintemp[MAXFAN]; //min threshold temperature float t_fpalarmtemp[MAXFAN]; //max threshold temperature float fproomtemp; //room temperature (for calibration) float fpmaxtemp; //temporary variable for calculating fan speed //fan tachometer detection stuff int tachhi[MAXFAN]; //counts time spent in high phase of tach pulse int tachlo[MAXFAN]; //counts time spent in low phase of tach pulse unsigned char rpmsensor[MAXFAN]; //holds current tach pulse value unsigned char f_readrpm[MAXFAN]; //flag to read rpm unsigned char f_tachphase[MAXFAN]; //flag to remember which phase of the tach pulse we are in unsigned char f_lockedrotor[MAXFAN]; //flag to detect a locked rotor unsigned int rpm[MAXFAN]; //holds the calculated rpm of a fan float tachdivider; //adjust for each fan - pulses per revolution //pwm and dac stuff unsigned char dacout; //holds dac value (0-15) unsigned char pwmspeed; //holds pwm duty cycle (0-255) unsigned char pwmcnt; //specifically for fan 2 rpm detection float fanspeed; //speed to drive fan at: 1.0 = 100% power, 0.0 = 0% power //keyboard input stuff char cmd; unsigned int val1; //RXC ISR variables unsigned char r_index; //current string index unsigned char r_buffer[90]; //input string unsigned char r_ready; //flag for receive done unsigned char r_char; //current character //TX empth ISR variables unsigned char t_index; //current string index unsigned char t_buffer[90]; //output string unsigned char t_ready; //flag for transmit done unsigned char t_char; //current character //the subroutines void gets_int(void); //starts getting a string from serial line void puts_int(void); //starts a send to serial line void initialize(void); //all the usual mcu stuff //scheduled tasks void DispScreen(void); void DispLCD(void); void ReadTemp(void); void Sysadmin(void); void AdjustFanSpeed(void); void PWMPulseStretch(void); //********************************************************** //timer 2 comparison ISR // This ISR ticks down the timers used to run scheduled tasks. // It generates the square wave for the piezo buzzer when an alarm should sound. // It reads the tachometer pulses from the fans and detects rpm. // It detects locked fan rotors based on the behaviour of the tach pulses interrupt [TIM2_COMP] void timer2_compare(void) begin //Decrement the scheduler times if they are not already zero if (time1>0) --time1; if (time2>0) --time2; if (time3>0) --time3; if (time4>0) --time4; if (time5>0) --time5; if (time6>0) --time6; //generates square wave for the alarm if( counttime-- == 0 ) { if( f_alarmsound ) PORTD.5 ^= 1; counttime = 10; } //turn off pwm mode pulse stretching after getting tachometer reading if( (fanmode[1] == pwm) && (pwmcnt == 3) ) { TCCR0 = 0b01101011; //resume normal pwm mode f_readrpm[1] = 0; //don't read rpm anymore } //cycle through each fan and get rpm data for( j = 0; j < MAXFAN; j++ ) { //get rpm sensor reading rpmsensor[j] = (PINA >> (j+4)) & 0x1; //sample tachometer pulses if( f_readrpm[j] == 1 ) { //tachometer pulse is HIGH if (rpmsensor[j] == 1) { f_tachphase[j] = 1; if (tachhi[j] > 600) f_lockedrotor[j] = t_lockedrotor; //rotor is locked in place tachhi[j]++; //counts in 0.1ms intervals } //tachnometer pulse is LOW else { //check for falling edge if (f_tachphase[j] == 1) { //check for locked rotor here! (check if time difference between phases is > factor of 3) if( ((tachhi[j]-tachlo[j]) > (3*tachlo[j])) && (f_lockedrotor[j] < t_lockedrotor) || ((tachlo[j]-tachhi[j]) > (3*tachhi[j])) && (f_lockedrotor[j] < t_lockedrotor) ) { f_lockedrotor[j]++; } else { //rotor operating normally f_lockedrotor[j] = 0; //pwm fan 2 if( fanmode[j] == pwm ) pwmcnt++; //for pwm pulse stretching, count number of cycles } if( fanmode[j] == pwm ) { if( pwmcnt == 3 ) rpm[j] = getRpm(tachlo[j]); //converts ms to rpm } else { rpm[j] = getRpm(tachlo[j]); //converts ms to rpm } tachhi[j] = 0; //reset for counting next cycle tachlo[j] = 0; //reset for counting next cycle } f_tachphase[j] = 0; if (tachlo[j] > 600) f_lockedrotor[j] = t_lockedrotor; //rotor is locked in place tachlo[j]++; //counts in 0.1ms intervals } } } //for loop end //********************************************************** //UART character-ready ISR interrupt [USART_RXC] void uart_rec(void) begin r_char=UDR; //get a char //computer input mode if( inputmode == computer ) { UDR=r_char; //then print it //build the input string if (r_char != '\r') r_buffer[r_index++]=r_char; else begin putchar('\n'); //use putchar to avoid overwrite r_buffer[r_index]=0x00; //zero terminate r_ready=1; //signal cmd processor UCSRB.7=0; //stop rec ISR end } //wireless input mode else { if( (rstate == start) && (r_char == start_packet) ) { rstate = data; } else if( rstate == data ) { datain = r_char; rstate = stop; } else if( (rstate == stop) && (r_char == stop_packet) ) { rstate = start; } else { rstate = start; } } end /**********************************************************/ //UART xmit-empty ISR interrupt [USART_DRE] void uart_send(void) begin t_char = t_buffer[++t_index]; if (t_char == 0) begin UCSRB.5 = 0; //kill isr t_ready = 1; //transmit done end else UDR = t_char; //send the char end //********************************************************** // -- non-blocking keyboard check initializes ISR-driven // receive. This routine merely sets up the ISR, which then // does all the work of getting a command. void gets_int(void) begin memset(r_buffer,0x00,90); r_ready = 0; r_index = 0; UCSRB.7 = 1; end //********************************************************** // -- nonblocking print: initializes ISR-driven // transmit. This routine merely sets up the ISR, then // send one character. The ISR does all the work. void puts_int(void) begin t_ready = 0; t_index = 0; if (t_buffer[0]>0) begin putchar(t_buffer[0]); UCSRB.5=1; end end //********************************************************** //Entry point and task scheduler loop // Handles mux selection, alarm and lights, and operation modes. // Calls various task timers based on the timeout counters. void main(void) begin initialize(); //main task scheduler loop -- never exits! while(1) begin if (time1==0) DispLCD(); //display system status on LCD if (time2==0) DispScreen(); //display information on terminal if (time3==0) Sysadmin(); //gets input from keyboard if (time4==0) ReadTemp(); //reads temperature sensors if (time5==0) AdjustFanSpeed(); //adjusts fan speeds if (time6==0) PWMPulseStretch(); //sets PWM to max for rpm detection //wireless or computer mode? inputmode = PINB.1; //mux output PORTB.0 = opmode; PORTB.2 = inputmode; //alarm system f_alarm = 0; for( i = 0; i < MAXFAN; i++ ) { if( (fanmode[i] != off) && ((fpcurrtemp[i] > t_fpalarmtemp[i]) || ((fpcurrtemp[i] > t_fpmintemp[i]) && (f_lockedrotor[i] == t_lockedrotor))) ) { f_alarm = 1; break; } } f_alarmsound = f_alarm; PORTD.4 = f_alarm; //alarm status light //status lights PORTD.2 = (opmode == auto); PORTD.3 = (inputmode == wireless); //manual mode if( opmode == user ) { TCCR0 = 0; PORTB.3 = 1; //drive PWM at full power to maintain rpm detection PORTA.2 = 1; //enable fan 2 so it will spin f_readrpm[0] = 1; //continue rpm detection f_readrpm[1] = 1; //continue rpm detection pwmcnt = 0; //so interrupt will always read rpm } //auto mode if( opmode == auto ) { //fan 2 (pwm fan) if( (fanmode[1] != off) && (fpcurrtemp[1] > t_fpmintemp[1]) ) { // turn on fan if( f_readrpm[1] == 0 ) { OCR0 = pwmspeed; //only set PWM speed if rpm is not being detected } TCCR0 = 0b01101011; //clear OC0 on upcount } else { // turn off fan OCR0 = 0; TCCR0 = 0; PORTB.3 = 0; //don't drive PWM rpm[1] = 0; f_lockedrotor[1] = 0; } //fan1 (dac fan) if( (fanmode[0] != off) && (fpcurrtemp[0] > t_fpmintemp[0]) ) { PORTA.2 = 1; //enable fan } else { PORTA.2 = 0; //disable fan rpm[0] = 0; f_lockedrotor[0] = 0; } //output dac value to port B PORTB = PINB & 0x0f | (dacout << 4); } //wireless input if( inputmode == wireless ) { switch( datain ) { case 0b11000010: t_fpmintemp[0] += 1.0; break; case 0b10100010: t_fpmintemp[0] -= 1.0; break; case 0b10010010: t_fpmintemp[1] += 1.0; break; case 0b10001010: t_fpmintemp[1] -= 1.0; break; case 0b10000110: opmode ^= 1; if( opmode == auto ) f_readrpm[1] = 0; break; } datain = 0; //clears received data } end end //********************************************************** //Adjusts fan speeds // This function uses the temperature detected from the sensors // and adjusts the fan speed accordingly. It assumes linear ramping. // The percentage of power to use is stored in 'fanspeed'. void AdjustFanSpeed(void) begin time5=t5; for( i = 0; i < MAXFAN; i++ ) { //get percentage of max speed to use fpmaxtemp = (t_fpalarmtemp[i] - t_fpmintemp[i]) * 0.75; fanspeed = (fpcurrtemp[i] - t_fpmintemp[i]) / fpmaxtemp; //pwm adjustment (fan 2) if( i == 1 ) { if( fanspeed >= 1.0 ) { pwmspeed = 255; } else if( fanspeed <= 0 ) { pwmspeed = 0; } else { pwmspeed = (unsigned char)( fanspeed * 255.0 ); } } //4 bit DAC adjustment (fan 1) if( i != 1 ) { if( fanspeed >= 1.0 ) { dacout = 15; } else if( fanspeed <= 0 ) { dacout = 0; } else { dacout = (unsigned char)( fanspeed * 15.0 ); } } } end //********************************************************** //Reads Temperature Sensors // This function reads the ADC result from the temperature sensors. // It continuously muxes between the different sensors and // alternatingly gets their readings and sets up for the next one. void ReadTemp(void) begin time4=t4; //reset the task timer // reads current sensor tempsensor[sensornum] = ADCH; fpcurrtemp[sensornum] = getTemp(tempsensor[sensornum]); //switch to next sensor and starts next conversion if( sensornum++ == (MAXFAN-1) ) sensornum = 0; ADMUX = 0b00100000 | sensornum; ADCSR.6 = 1; end //********************************************************** //PWM Pulse Stretching // If fan is running in PWM mode, then we need to run it at full power // periodically in order to get RPM information. It sets the f_readrpm // flag to 1 so the interrupt will gather RPM for the PWM fan. void PWMPulseStretch(void) begin time6=t6; //reset the task timer //use pulse stretching to handle pwm rpm monitoring if( (fanmode[1] == pwm) && (fpcurrtemp[1] > t_fpmintemp[1]) ) { OCR0 = 255; //drive PWM at full power TCCR0 = 0b01101011; //clear OC0 on upcount f_readrpm[1] = 1; //read rpm signals flag pwmcnt = 0; } end //********************************************************** //Displays to LCD // This function outputs the fan + temperature status to the LCD in // an alternating fashion. void DispLCD(void) begin time1=t1; //reset the task timer //toggle between different fan info if( lcddispnum++ == (MAXFAN-1) ) lcddispnum = 0; //current rpm + temperature lcd_clear(); sprintf( lcd_buffer, "%d: %4d rpm %2.0fF", lcddispnum+1, rpm[lcddispnum], fpcurrtemp[lcddispnum] ); lcd_gotoxy(0,0); lcd_puts( lcd_buffer ); end //********************************************************** //Displays fan controller information to the computer terminal void DispScreen(void) begin time2=t2; //prints out title and mode on the hyperterminal while ( !t_ready ); sprintf(t_buffer, "\f\n\rFan Controller ("); puts_int(); while ( !t_ready ); if( opmode == auto ) sprintf( t_buffer, "auto, " ); else sprintf( t_buffer, "user, " ); puts_int(); while ( !t_ready ); if( inputmode == wireless ) sprintf( t_buffer, "wireless)\n\r" ); else sprintf( t_buffer, "terminal)\n\r" ); puts_int(); //prints fan status for( i = 0; i < MAXFAN; i++ ) { while ( !t_ready ); if( (f_lockedrotor[i] == t_lockedrotor) && (fpcurrtemp[i] > t_fpmintemp[i]) && ((fanmode[i] != off) || (opmode == user)) ) sprintf(t_buffer, "Fan%d: %4drpm %4.1fF min=%3.0f max=%3.0f - FAULT detected!\n\r", i+1, rpm[i], fpcurrtemp[i], t_fpmintemp[i], t_fpalarmtemp[i]); else sprintf(t_buffer, "Fan%d: %4drpm %4.1fF min=%3.0f max=%3.0f\n\r", i+1, rpm[i], fpcurrtemp[i], t_fpmintemp[i], t_fpalarmtemp[i]); puts_int(); } //prints user prompt while ( !t_ready ); sprintf(t_buffer, "> %s", r_buffer); puts_int(); end //********************************************************** //Serial port input from the computer // Gets commands from the keyboard and parses them. void Sysadmin(void) begin time3=t3; //get user input if ( r_ready ) begin sscanf(r_buffer, "%c%d", &cmd, &val1); gets_int(); switch( cmd ) begin //set pwmspeed case 'a': t_fpmintemp[0] = val1; break; case 'b': t_fpalarmtemp[0] = val1; break; case 'c': t_fpmintemp[1] = val1; break; case 'd': t_fpalarmtemp[1] = val1; break; case 'o': opmode = opmode ^ 1; if( opmode == auto ) f_readrpm[1] = 0; break; case 'f': if( val1 == 1 ) { if( fanmode[0] == off ) fanmode[0] = dac; else fanmode[0] = off; } if( val1 == 2 ) { if( fanmode[1] == off ) fanmode[1] = pwm; else fanmode[1] = off; } break; case 't': tachdivider = val1; break; end end end //********************************************************** //Initialize program settings // Sets up port pins, timers, flags, and interrupts. void initialize(void) begin //temperature and rpm sensing DDRA = 0x84; //A.2 = enable DAC fan PORTA = 0xff; //dac, pwm, mode output and wireless toggle input DDRB = 0xfd; PORTB = 0x00; //lcd output DDRC = 0xff; PORTC = 0xff; //serial and status lights and alarm DDRD = 0xff; PORTD = 0xff; //serial setop for debugging using printf, etc. UCSRB = 0x18; UBRRL = 207; //4800 putsf("\r\nCornell Starting...\r\n"); //set up timer 0 for PWM TCNT0 = 0; OCR0 = 0; TCCR0 = 0b01001011; //prescalar to CLK, CTC, PWM, init with no OC0 operation //set up timer 2 for time based scheduler TCNT2 = 0; OCR2 = 24; //0.1 ms TIMSK = 1<<7; //turn on timer 2 cmp-match ISR TCCR2 = 0b00001011; //prescalar to CLK / 64 //init the task timers time1=t1; time2=t2; time3=t3; time4=t4; time5=t5; time6=t6; counttime = 10; //lcd stuff lcd_init(LCDwidth); //initialize the display lcd_clear(); //clear the display putsf("\r\nLCD init complete...\r\n"); //temperature reading stuff sensornum = 0; ADMUX = 0b00100100; //use port A.4 for input (left aligned) ADCSR = 0b11000111; //prescalar to 1/128*16Mhz = 125kHz, start conversion fproomtemp = (inittemp * opampGain / Arefvolt) * 255.0; //calibrate roomtemp with sensor 1 for( i = 0; i < MAXFAN; i++ ) { t_fpmintemp[i] = 72; //initial thresholds t_fpalarmtemp[i] = 80; t_mintemp[i] = 72; t_alarmtemp[i] = 80; } //fan speed and failure detection stuff for( i = 0; i < MAXFAN; i++ ) { tachhi[i] = 0; tachlo[i] = 0; f_readrpm[i] = 1; f_lockedrotor[i] = 0; } f_readrpm[1] = 0; //fan 2 is a pwm fan //selects fan modes opmode = auto; inputmode = computer; fanmode[0] = dac; fanmode[1] = pwm; //pwm and dac stuff dacout = 0; pwmspeed = 0; pwmcnt = 0; //transmit and receive flags r_ready = 0; t_ready = 1; rstate = start; datain = 0; //crank up the ISRs #asm sei #endasm //serial port input gets_int(); //our fan tachdivider = 2; end
Transmitter code
Below is the transmitter code:
// // Cornell 2005 // Edward Lo and Sihan Goi // ECE476 final project // Temperature sensor and fan wireless transmitter // #include <Mega32.h> #include <stdio.h> #include <stdlib.h> //I like these definitions #define begin { #define end } //task scheduling timers #define t1 30 //30 ms debounce state machine //wireless packet definitions (maintain DC balance) #define start_packet 0b10100110 #define stop_packet 0b11010010 enum { NoPush, MaybePush, Pushed, MaybeNoPush }; unsigned int time1; //task scheduling timeout counter unsigned char buttons, buttons2, inbutton; //saves the button press unsigned char butstate; //debounce state machine unsigned char cdata; //data byte to transmit void initialize(void); //set everything up void getInput(void); //debounce buttons //********************************************************** //Timer0 compare ISR // Simply ticks down the task timers. interrupt [TIM0_COMP] void timer0_compare(void) { if (time1>0) --time1; } //********************************************************** //Transmit the data byte // Encapsulates the data packet with a start and end packet. // Send 0xaa's to setup receiver gain. void senddata(void) { //setup the receiver for proper gain and sync putchar(0xaa); putchar(0xaa); putchar(0xaa); putchar(0xaa); putchar(0xaa); putchar(0xaa); putchar(0xaa); putchar(0xaa); putchar(0xaa); putchar(0xaa); putchar(0xaa); putchar(0xaa); putchar(0xaa); putchar(0xaa); putchar(0xaa); putchar(0xff); putchar(0x00); //send data putchar(start_packet); putchar(cdata); putchar(stop_packet); cdata = 0; } //********************************************************** //Button debouncing state machine // Gets the button presses into "inbutton" and then encodes it // into the "cdata" variable. This becomes the data packet. void getInput(void) { time1=t1; //button debounce switch( butstate ) begin case NoPush: buttons = PINA; if( ~buttons != 0 ) butstate = MaybePush; break; case MaybePush: buttons2 = PINA; if( buttons2 == buttons ) begin butstate = Pushed; inbutton = ~buttons; end else butstate = NoPush; break; case Pushed: buttons2 = PINA; if( buttons2 == buttons ) butstate = Pushed; else butstate = MaybeNoPush; break; case MaybeNoPush: buttons2 = PINA; if( buttons2 == buttons ) butstate = Pushed; else begin butstate = NoPush; inbutton = 0; end break; end //parse input switch( inbutton ) { case 0b10000000: case 0b01000000: case 0b00100000: case 0b00010000: cdata = 0b10000000 | (inbutton >> 1) | 0b10; inbutton = 0; break; case 0b00000001: cdata = 0b10000110; inbutton = 0; break; } } //********************************************************** //Main program // Debounce buttons and send presses to serial port. void main(void) { initialize(); while(1) { if( time1 == 0 ) getInput(); //get debounced input if( cdata != 0 ) senddata(); //transmits button press } } //********************************************************** //Get everyting started void initialize(void) { //setup ports DDRA = 0x00; // buttons PORTA = 0xff; // pull-up resistors DDRD = 0xff; // output for status light and transmitter PORTD = 0xff; // power status on //serial setop for debugging using printf, etc. UCSRB = 0x18; UBRRL = 207; //4800 //setup timer0 for 1ms TIMSK = 2; OCR0 = 249; TCCR0 = 0b00001011; //initialize timers time1 = t1; time2 = t2; //setup debounce states butstate = NoPush; inbutton = 0; cdata = 0; #asm ("sei"); //turns on LED PORTD.7 = 0; }