Friday, February 26, 2016

Atmega8 thermocouple thermostat, C code source


This code is from project: "The" PCB Laminator

Files: pid.h and pid.c was downloaded from: AVR221 - PID controller Documentation

pid.h

/*This file has been prepared for Doxygen automatic documentation generation.*/
#ifndef PID_H
#define PID_H

// #include "stdint.h"
#include <stdint.h>

#define SCALING_FACTOR  128

typedef struct PID_DATA{
  int16_t lastProcessValue;
  int32_t sumError;
  int16_t P_Factor;
  int16_t I_Factor;
  int16_t D_Factor;
  int16_t maxError;
  int32_t maxSumError;
} pidData_t;

// Maximum value of variables
#define MAX_INT         INT16_MAX
#define MAX_LONG        INT32_MAX
#define MAX_I_TERM      (MAX_LONG / 2)

// Boolean values
#define FALSE           0
#define TRUE            1

void pid_Init(int16_t p_factor, int16_t i_factor, int16_t d_factor, struct PID_DATA *pid);
int16_t pid_Controller(int16_t setPoint, int16_t processValue, struct PID_DATA *pid_st);
void pid_Reset_Integrator(pidData_t *pid_st);

#endif

pid.c

/*This file has been prepared for Doxygen automatic documentation generation.*/
#include "pid.h"
// #include "stdint.h"

void pid_Init(int16_t p_factor, int16_t i_factor, int16_t d_factor, struct PID_DATA *pid)
// Set up PID controller parameters
{
  // Start values for PID controller
  pid->sumError = 0;
  pid->lastProcessValue = 0;
  // Tuning constants for PID loop
  pid->P_Factor = p_factor;
  pid->I_Factor = i_factor;
  pid->D_Factor = d_factor;
  // Limits to avoid overflow
  pid->maxError = MAX_INT / (pid->P_Factor + 1);
  pid->maxSumError = MAX_I_TERM / (pid->I_Factor + 1);
}


int16_t pid_Controller(int16_t setPoint, int16_t processValue, struct PID_DATA *pid_st)
{
  int16_t error, p_term, d_term;
  int32_t i_term, ret, temp;

  error = setPoint - processValue;

  // Calculate Pterm and limit error overflow
  if (error > pid_st->maxError){
    p_term = MAX_INT;
  }
  else if (error < -pid_st->maxError){
    p_term = -MAX_INT;
  }
  else{
    p_term = pid_st->P_Factor * error;
  }

  // Calculate Iterm and limit integral runaway
  temp = pid_st->sumError + error;
  if(temp > pid_st->maxSumError){
    i_term = MAX_I_TERM;
    pid_st->sumError = pid_st->maxSumError;
  }
  else if(temp < -pid_st->maxSumError){
    i_term = -MAX_I_TERM;
    pid_st->sumError = -pid_st->maxSumError;
  }
  else{
    pid_st->sumError = temp;
    i_term = pid_st->I_Factor * pid_st->sumError;
  }

  // Calculate Dterm
  d_term = pid_st->D_Factor * (pid_st->lastProcessValue - processValue);

  pid_st->lastProcessValue = processValue;

  ret = (p_term + i_term + d_term) / SCALING_FACTOR;
  if(ret > MAX_INT){
    ret = MAX_INT;
  }
  else if(ret < -MAX_INT){
    ret = -MAX_INT;
  }

  return((int16_t)ret);
}

void pid_Reset_Integrator(pidData_t *pid_st)
{
  pid_st->sumError = 0;
}

main.h

#ifndef _H_MAIN_
#define _H_MAIN_

#define  F_CPU 1000000L

#define H(a,b) a |=(1<<(b))
#define L(a,b) a &=~(1<<(b))
#define IS(a,b) bit_is_set(a,b)
#define BS(a,b) (a & (1<<(b)))

#endif

#ifndef F_CPU
   #error CPU speed unknown
#endif


main.c

#include "main.h"
#include "delay.h"
#include "pid.h"

#include <avr/interrupt.h>

#define Dec2Bcd(dec) (((dec / 10) << 4) + (dec % 10))

uint8_t heat_off_flag = 0;
uint8_t desired_temp = 90;
uint8_t show_desired_temp = 0;
uint8_t reset = 1;


uint8_t digits[3];
void led_set(uint8_t n) {

   digits[0] = (n / 100);
   n-= (digits[0] * 100);
   digits[1] = (n / 10);
   n-= (digits[1] * 10);
   digits[2] = n;

   digits[0] = Dec2Bcd(digits[0]);
   digits[1] = Dec2Bcd(digits[1]);
   digits[2] = Dec2Bcd(digits[2]);
}


void led_off() {
   L(PORTB, PB7);
   L(PORTB, PB6);
   L(PORTD, PD4);
}


// led driver
SIGNAL(TIMER0_OVF_vect) {
   static uint8_t i;

   switch(i) {
      case 0:
         H(PORTD, PD4);
         L(PORTB, PB6);
         L(PORTB, PB7);
         break;
      case 1:
         L(PORTD, PD4);
         H(PORTB, PB6);
         L(PORTB, PB7);
         break;
      case 2:
         L(PORTD, PD4);
         L(PORTB, PB6);
         H(PORTB, PB7);
         break;
      case 3:
         L(PORTD, PD4);
         L(PORTB, PB6);
         L(PORTB, PB7);
         i = 0;
         return;
      default: break;
   };

   if IS(digits[i], 0)
      H(PORTC, PC2);
   else
      L(PORTC, PC2);

   if IS(digits[i], 1)
      H(PORTC, PC5);
   else
      L(PORTC, PC5);

   if IS(digits[i], 2)
      H(PORTC, PC4);
   else
      L(PORTC, PC4);

   if IS(digits[i], 3)
      H(PORTC, PC3);
   else
      L(PORTC, PC3);

   i++;
}


void spi_enable() {
   L(PORTB, PB2); 
}


void spi_disable() {
   H(PORTB, PB2); 
}


void spi_init() {
   spi_disable();
   SPSR = (1<<SPI2X);
   SPCR = (1<<SPE) | (1<<MSTR);
}


uint8_t spi_write(uint8_t data) {
   SPDR = data;
   while(!(SPSR & (1 << SPIF)));
   return SPDR;
}


uint16_t get_temp() {

   uint16_t t = 0;
   uint8_t data[4]; 

   data[3] = 0;
   data[2] = 0;
   data[1] = 0;
   data[0] = 0;

   spi_enable();

   data[3] = spi_write(0);
   data[2] = spi_write(0);
   data[1] = spi_write(0);
   data[0] = spi_write(0);

//    31       23       15           
//    xxxxxxxx xxxxxxxx xxxxxxxx 
   t = ((data[3] << 8) | data[2]) ;

   if (!(IS(data[2], 0))) {
      t = t >> 4;
   }
   else {
      t = 255;
   }

   spi_disable();

   return t;
}


// heater
int16_t cycles = 0;
SIGNAL(TIMER2_OVF_vect) {
   static uint16_t f;
   
   if(f == 0 && cycles != 0) {
      L(PORTD, PD0);
   }

   if(f >= cycles * 10) {
      H(PORTD, PD0);
   }

   if(++f == (38 * 10))
      f = 0;
}



SIGNAL(INT0_vect) { 
   L(GICR, INT0);
   led_off();
   heat_off_flag = 1;
}


SIGNAL(INT1_vect) { 
   H(GICR, INT0);
   L(GICR, INT1);
   led_off();
   show_desired_temp = 1; 
   reset = 1;

   if (heat_off_flag) {
      H(TIMSK, TOIE0);
      heat_off_flag = 0;
   } else
      switch(desired_temp) {
         case 90:
            desired_temp = 125;
            break;

         case 125:
            desired_temp = 150;
            break;

         case 150:
            desired_temp = 160;
            break;

         case 160:
            desired_temp = 170;
            break;

         case 170:
            desired_temp = 175;
            break;

         case 175:
            desired_temp = 180;
            break;

         case 180:
            desired_temp = 90;
            break;
      };

   led_set(desired_temp);
}


/****************************************************************************/
/*                                                                          */
/****************************************************************************/
int main()
{
   // BCD driver
   H(DDRC, PC2);
   H(DDRC, PC3);
   H(DDRC, PC4);
   H(DDRC, PC5);

   H(PORTC, PC2);
   H(PORTC, PC3);
   H(PORTC, PC4);
   H(PORTC, PC5);

   // Miultiplexer
   H(DDRB, PB6);
   H(DDRB, PB7);
   H(DDRD, PD4);

   L(PORTB, PB6);
   L(PORTB, PB7);
   L(PORTD, PD4);

   H(TCCR0, CS01);
   H(TIMSK, TOIE0);

   // Buttons
   L(DDRD, PD2);
   L(DDRD, PD3);

   H(PORTD, PD2);
   H(PORTD, PD3);

   L(MCUCR, ISC01);
   L(MCUCR, ISC00);

   L(MCUCR, ISC11);
   L(MCUCR, ISC10);

   H(GICR, INT0);
   H(GICR, INT1);

   // Heater
   H(DDRD, PD0);
   H(PORTD, PD0); 

   H(TCCR2, CS21);
   H(TIMSK, TOIE2);

   sei();

   // SPI
   H(DDRB, PB5);  // sck
   H(DDRB, PB2);  // ss
   L(DDRB, PB4);  // so

   spi_init();
   led_set(0);

   uint16_t pid_ret = 0;
   uint8_t current_temp = 0;

   struct PID_DATA pd;
   pid_Init(150, 1, 0, &pd);

   while(1) {
      current_temp = get_temp();

      if (show_desired_temp) {
         show_desired_temp = 0; 
         delay_ms(400);
         H(GICR, INT1);
         continue;
      }
      else
         led_set(current_temp);

      if (heat_off_flag) {
         cycles = 0;
         L(TIMSK, TOIE0);
         led_off();
         delay_ms(500);
         H(TIMSK, TOIE0);
         delay_ms(500);
         continue;
      } else
         H(TIMSK, TOIE0);

      if ((current_temp > desired_temp - 20) && reset) {
         reset = 0;
         pid_Reset_Integrator(&pd);
      }

      pid_ret = pid_Controller(desired_temp, current_temp, &pd);

      if (pid_ret >= 0) {
         if (pid_ret > 38)
            pid_ret = 38;
         cycles = pid_ret;
      } else {
        cycles = 0;
        while(pid_ret++)
           delay_ms(100);
      }
      delay_ms(500);
   }
}

delay.h

#ifndef _H_DELAY_H
#define _H_DELAY_H

#include "main.h"
#include <util/delay.h>
#define nop()  __asm__ __volatile__("nop")

void delay_ms(unsigned short ms);
void delay_us(unsigned short us);

#endif

delay.c

#include "delay.h"

void delay_ms(unsigned short ms) {
    while ( ms ) {
      ms--;
      _delay_ms(1);
   }
}

void delay_us(unsigned short us) {
    while ( us ) {
      us--;
      _delay_us(1);
   }
}

Makefile

CPU=atmega8

GCC=avr-gcc
CFLAGS= -Os -mmcu=$(CPU) -Wall -fpack-struct -fshort-enums -funsigned-bitfields -Wl,--relax -fno-move-loop-invariants -funsigned-char -fno-inline-small-functions -fdata-sections -fno-tree-loop-optimize -lprintf_min 

INCLUDES=
LIBS=

OBJCPY=avr-objcopy
OBJECTS=main.o delay.o pid.o

PROJECT_NAME=mega8
HEX_FILE=$(PROJECT_NAME).hex
HEX_FILE_DUMP=$(PROJECT_NAME)_dump.hex

PROG=avrdude
PROG_FLAGS=-V -c usbasp -p $(CPU)

all: 
  $(MAKE) cls
  $(MAKE) $(PROJECT_NAME)
  $(MAKE) obj
  $(MAKE) size #upload

$(PROJECT_NAME): $(OBJECTS)
  $(GCC) -o $(PROJECT_NAME) $(OBJECTS) $(CFLAGS) $(LIBS) $(INCLUDES)

main.o: main.c main.h
  $(GCC) $(CFLAGS) $(INCLUDES) -c main.c

delay.o: delay.c delay.h
  $(GCC) $(CFLAGS) $(INCLUDES) -c delay.c

pid.o: pid.c pid.h
  $(GCC) $(CFLAGS) $(INCLUDES) -c pid.c

obj: $(OBJECTS)
  $(OBJCPY) -O ihex $(PROJECT_NAME) $(HEX_FILE)

clean:
  rm -f $(PROJECT_NAME) $(OBJECTS) $(HEX_FILE)

cls:
  clear

size:
  du -b $(HEX_FILE) 

upload: 
  $(MAKE) all
    $(MAKE) $(HEX_FILE)
  $(PROG) $(PROG_FLAGS) -e -U flash:w:$(HEX_FILE)

download:
  $(PROG) $(PROG_FLAGS) -U flash:r:$(HEX_FILE_DUMP)

No comments:

Post a Comment