⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 tinyplc.c

📁 单片PLC,AT2581实现梯形图功能,可作为参考
💻 C
📖 第 1 页 / 共 3 页
字号:
/*
 *  Released under the GNU GPL.  See http://www.gnu.org/licenses/gpl.txt
 *
 *  This program is part of TinyPLC
 *
 *  TinyPLC is free software; you can redistribute it and/or modify it
 *  under the terms of the GNU General Public License as published by the
 *  Free Software Foundatation; version 2 of the License.
 *
 *  TinyPLC is distributed in the hope that it will be useful, but WITHOUT
 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 *  for more details.
 */
/*
 *  My thanks to my employer, Athena Controls (www.athenacontrols.com)
 *  for allowing me to open source this project.
 */
/*
 *  TinyPLC kernel
 *  Processor:   Atmel MEGA1281 or Atmel MEGA2561
 *  Compiler:    Codevision AVR C compiler, 1.24.8e
 *
 *  TinyPLC editor/compiler
 *  Processor:   PC-clone, VESA video (mode 0x105, 1024x768x256)
 *  Compiler:    DeSmet C, version 2.51 (PCC version 1.2)
 *
 *  Revision history:
 *  Programmer        Date       Comments
 *  ----------------  ---------  ---------------------------------------------
 *  William Couture   3/19/2006  Original code
 *  William Couture   9/13/2006  Modify for GPL release
 */

#pragma regalloc-
    /* automatic register allocation OFF for now */
#include <mega1281.h>
#include <ctype.h>

#include "tinyplc.h"

REGISTER BYTE linetrue;
REGISTER BYTE tempvar;
REGISTER BYTE r1;
REGISTER BYTE r2;
REGISTER BYTE runflag;
REGISTER BYTE debugflag;
REGISTER WORD program_ip;
REGISTER BYTE program_valid;
REGISTER WORD program_len;
REGISTER BYTE hardrun;
REGISTER BYTE lasthardrun;
#pragma regalloc+
   /* now that we have our variables allocated to regs, let the compiler use the rest */

BYTE softrun;

BYTE program[MAX_PROGRAM + 2];
  /* +2 for paranoia about R1 and R2 and checksum length */

BYTE message[MAX_MSG_SIZE];
WORD message_ptr;
WORD p_len;
WORD incoming_len;
BYTE message_status;

WORD run_nodecount;  /* for debugging */

WORD program_timer;
BYTE program_abort;

eeprom WORD sacrificial_goat;  /* do not use EEPROM location 0 due to powerup bug on some chips */
eeprom WORD eeprom_program_len;
eeprom BYTE eeprom_program[MAX_PROGRAM];
eeprom WORD eeprom_program_checksum;

struct bytes_t
   {
   BYTE b0;
   BYTE b1;
   BYTE b2;
   BYTE b3;
   };
struct words_t
   {
   WORD w0;
   WORD w1;
   };
struct timercounter_t
   {
   BYTE b0;
   BYTE b1;
   WORD w;
   };

union
   {
   struct bytes_t b;
   struct words_t w;
   struct timercounter_t t;
   DWORD v;
   BYTE bytes[4];
   } dataregs[PLC_MEMORY_LOCS], valdest, val1, val2;
                 /* make valdest, val1, val2 global to avoid passing */


BYTE initstack[STACKSIZE];
BYTE initstackptr;
BYTE resultstack[STACKSIZE];
BYTE resultstackptr;

volatile DWORD tick_counter;

volatile BYTE status_led_pwm;
         BYTE status_led_counter;
volatile WORD seconds_counter;
volatile BYTE seconds_tick;
volatile BYTE newtick;
volatile BYTE xmittick;

#if TOTAL_ANALOGS > 0
   enum
      {
      ADC_SETTLING_1,
      ADC_SETTLING_2,
      ADC_VALID
      };
   BYTE adc_state;
   BYTE adc_channel;
   BYTE adc_tick_counter;
   WORD adc_filter[TOTAL_ANALOGS];
#endif

interrupt [TIM0_COMPA] void heartbeat_isr(void)
   {
   status_led_pwm++;
   tick_counter++;
   newtick = TRUE;
   if (++seconds_counter == HEARTBEAT_FREQUENCY)
      {
      seconds_counter = 0;
      seconds_tick++;
      if (program_timer)
         if (--program_timer == 0)
            program_abort = TRUE;
      }

   if (RUNSTOP_SWITCH == RUNSTOP_RUN)
      hardrun = RUNSTOP_RUN;
   else
      hardrun = RUNSTOP_STOP;
   if (hardrun == RUNSTOP_STOP)
      runflag = FALSE;

   if (xmittick)
      xmittick--;

#if TOTAL_ANALOGS > 0
   if (++adc_tick_counter == ADC_CONVERSION_TIME)  /* ADC should be ready */
      {
      adc_tick_counter = 0;
      if (ADCSRA & ADCSRA_ADSC)  /* DONE bit != 0, not finished */
         {
         /* worry about ADC internal errors later */
         }
      else
         {
         if (adc_state == ADC_VALID)
            {
            adc_filter[adc_channel] -= adc_filter[adc_channel] / ADC_FILTER_STRENGTH;
            adc_filter[adc_channel] += ADCW;
            }
         }
      if (++adc_state > ADC_VALID)
         {
         adc_state = ADC_SETTLING_1;
         if (++adc_channel >= TOTAL_ANALOGS)
            adc_channel = 0;
         ADMUX = ADMUX_VALUE | adc_channel;
         }
      ADCSRA = ADCSRA_GO;  /* start next ADC conversion */
      }
#endif  /* TOTAL_ANALOGS */
   }

BYTE tx_buf[TXBUF_SIZE];
BYTE tx_buf_in;  /* index to next byte to be written into tx_buf[] */
volatile BYTE tx_buf_out; /* index to next byte to be read from tx_buf[] */

BYTE rx_buf[RXBUF_SIZE];
BYTE rx_buf_in;  /* index to next byte to be written into rx_buf[] */
BYTE rx_buf_out; /* index to next byte to be read from rx_buf[] */
BYTE rx_byte, rx_temp;
static BYTE stopmsg[5] = { MSG_BYTE0, MSG_BYTE1, MSG_STOP, 0, 0 };

interrupt [USART0_RXC] void usart0_receive(void)
   {
   BYTE i, j, k;

   rx_temp = UCSR0A;
   rx_byte = UDR0;
   if (!(rx_temp & FRAMING_ERROR))
      {
      rx_buf[rx_buf_in] = rx_byte;
      if (rx_byte == 0)
         {
         for (i = 0, j = 4, k = rx_buf_in; i < 5; i++, j--)
            {
            if (rx_buf[k] != stopmsg[j])
               break;
            k = (k == 0) ? RXBUF_SIZE - 1 : k - 1;
            }
         if (i == 5)
            {
            runflag = FALSE;
            softrun = FALSE;
            }
         }
      rx_temp = (rx_buf_in == RXBUF_SIZE - 1) ? 0 : rx_buf_in + 1;
      if (rx_temp != rx_buf_out)
         rx_buf_in = rx_temp;
      }
   }

interrupt [USART0_TXC] void usart0_tx_complete(void)
   {
   UCSR0B = UCSR0B_INIT;  /* turn off TX, stuff in buffer will finish xmiting */
   }

interrupt [USART0_UDRE] void usart0_datareg_empty(void)
   {
   if (tx_buf_out == tx_buf_in)  /* buffer empty */
      {
      UCSR0B = UCSR0B_FINISH;  /* turn off UDRIE0, stuff in buffer will finish xmiting */
      }
   else
      {
      UDR0 = tx_buf[tx_buf_out];
      tx_buf_out = (tx_buf_out == TXBUF_SIZE - 1) ? 0 : tx_buf_out + 1;
      }
   }

void xmit_char(BYTE ch)
   {
   BYTE temp;

   DI();
   UCSR0B = UCSR0B_XMIT;  /* enable transmitter */
   temp = (tx_buf_in == TXBUF_SIZE - 1) ? 0 : tx_buf_in + 1;
   if (temp != tx_buf_out)  /* if buffer not full */
      {
      tx_buf[tx_buf_in] = ch;
      tx_buf_in = temp;
      }
   EI();
   }

void xmit_wait(BYTE ch)
   {
   BYTE temp;

   temp = (tx_buf_in == TXBUF_SIZE - 1) ? 0 : tx_buf_in + 1;
   xmittick = 2;
   while ((temp == tx_buf_out) && (xmittick))  /* if buffer not full */
      ;
   if (xmittick)  /* if not timeout */
      xmit_char(ch);
   }

void xmit_str(char flash *s)
   {
   while (*s)
      xmit_char(*s++);
   }

void system_init(void)
   {
   PET_WATCHDOG();

   WDTCSR = WDTCSR_UNLOCK;   /* unlock WDTCSR register to update Watchdog params */
   WDTCSR = WDTCSR_SET_WDT_PERIOD;  /* set WDT period to 2 seconds */

   DDRA = PORTA_DIRECTIONS;
   PORTA = PORTA_DEFAULT;

   DDRB = PORTB_DIRECTIONS;
   PORTB = PORTB_DEFAULT;

   DDRC = PORTC_DIRECTIONS;
   PORTC = PORTC_DEFAULT;

   DDRD = PORTD_DIRECTIONS;
   PORTD = PORTD_DEFAULT;

   DDRE = PORTE_DIRECTIONS;
   PORTE = PORTE_DEFAULT;

   DDRF = PORTF_DIRECTIONS;
   PORTF = PORTF_DEFAULT;

   DDRG = PORTG_DIRECTIONS;
   PORTG = PORTG_DEFAULT;

   TCCR0A = TCCR0A_INIT;  /* init TMR0 for "heartbeat" */
   TIMSK0 = TIMSK0_INIT;
   OCR0A = TMR0_PERIOD;
   TCCR0B = TCCR0B_INIT;

   UCSR0C = UCSR0C_INIT;  /* init USART 0 */
   UCSR0B = UCSR0B_INIT;
   UBRR0H = UBRR_VALUE >> 8;
   UBRR0L = (UBRR_VALUE & 0xff);

#if TOTAL_ANALOGS > 0
   ADCSRA = ADCSRA_INIT;  /* init ADC */
   DIDR0 = DIDR0_VALUE;   /* set pins for analog input */
   ADMUX = ADMUX_VALUE | adc_channel;
   ADCSRA = ADCSRA_GO;    /* start first ADC conversion */
   adc_tick_counter = 0;
#endif

   EI();

   program_timer = 0;    /* get ready for incoming message */
   program_abort = FALSE;
   message_ptr = 0;
   message_status = MSG_STATUS_WAITING;
   incoming_len = 0xffff;  /* invalid! */
   }


/* set the real outputs */
void service_outputs(void)
   {
      /* NOTE: this could be done as a for() loop, such as
       *            for (i = 0; i < TOTAL_OUTPUTS; i++)
       *               {
       *               val = dataregs[FIRST_OUTPUT + i].v ? OUTPUT_ON : OUTPUT_OFF;
       *               switch (i)
       *                  {
       *         #if TOTAL_OUTPUTS > 0
       *                  case 0:
       *                     DIG_OUT_0  = val;
       *                     break;
       *         #endif
       *                  { etc }
       *                  }
       *               }
       *       but the CASE statement would still need be setup for the
       *       number of outputs.
       */
#if TOTAL_OUTPUTS > 0
   DIG_OUT_0  = dataregs[FIRST_OUTPUT +  0].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 1
   DIG_OUT_1  = dataregs[FIRST_OUTPUT +  1].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 2
   DIG_OUT_2  = dataregs[FIRST_OUTPUT +  2].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 3
   DIG_OUT_3  = dataregs[FIRST_OUTPUT +  3].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 4
   DIG_OUT_4  = dataregs[FIRST_OUTPUT +  4].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 5
   DIG_OUT_5  = dataregs[FIRST_OUTPUT +  5].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 6
   DIG_OUT_6  = dataregs[FIRST_OUTPUT +  6].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 7
   DIG_OUT_7  = dataregs[FIRST_OUTPUT +  7].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 8
   DIG_OUT_8  = dataregs[FIRST_OUTPUT +  8].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 9
   DIG_OUT_9  = dataregs[FIRST_OUTPUT +  9].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 10
   DIG_OUT_10 = dataregs[FIRST_OUTPUT + 10].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 11
   DIG_OUT_11 = dataregs[FIRST_OUTPUT + 11].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 12
   DIG_OUT_12 = dataregs[FIRST_OUTPUT + 12].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 13
   DIG_OUT_13 = dataregs[FIRST_OUTPUT + 13].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 14
   DIG_OUT_14 = dataregs[FIRST_OUTPUT + 14].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 15
   DIG_OUT_15 = dataregs[FIRST_OUTPUT + 15].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 16
   DIG_OUT_16 = dataregs[FIRST_OUTPUT + 16].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 17
   DIG_OUT_17 = dataregs[FIRST_OUTPUT + 17].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 18
   DIG_OUT_18 = dataregs[FIRST_OUTPUT + 18].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 19
   DIG_OUT_19 = dataregs[FIRST_OUTPUT + 19].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 20
   DIG_OUT_20 = dataregs[FIRST_OUTPUT + 20].v ? OUTPUT_ON : OUTPUT_OFF;
#endif
#if TOTAL_OUTPUTS > 21
   #error This program is only setup to handle up to 21 outputs
#endif
   }

void gather_inputs()
   {
   BYTE i;

      /* NOTE: this could be done as a for() loop, such as
       *            for (i = 0; i < TOTAL_INPUTS; i++)
       *               {
       *               switch (i)
       *                  {
       *         #if TOTAL_INPUTS > 0
       *                  case 0:
       *                     val = (DIG_IN_0  == INPUT_ON) ? ON : OFF;
       *                     break;
       *         #endif
       *                  { etc }
       *                  }
       *               dataregs[FIRST_INPUT + i] = val;
       *               }
       *       but the CASE statement would still need be setup for the
       *       number of inputs.
       */
#if TOTAL_INPUTS > 0
   dataregs[FIRST_INPUT +  0].v = (DIG_IN_0  == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 1
   dataregs[FIRST_INPUT +  1].v = (DIG_IN_1  == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 2
   dataregs[FIRST_INPUT +  2].v = (DIG_IN_2  == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 3
   dataregs[FIRST_INPUT +  3].v = (DIG_IN_3  == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 4
   dataregs[FIRST_INPUT +  4].v = (DIG_IN_4  == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 5
   dataregs[FIRST_INPUT +  5].v = (DIG_IN_5  == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 6
   dataregs[FIRST_INPUT +  6].v = (DIG_IN_6  == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 7
   dataregs[FIRST_INPUT +  7].v = (DIG_IN_7  == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 8
   dataregs[FIRST_INPUT +  8].v = (DIG_IN_8  == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 9
   dataregs[FIRST_INPUT +  9].v = (DIG_IN_9  == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 10
   dataregs[FIRST_INPUT + 10].v = (DIG_IN_10 == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 11
   dataregs[FIRST_INPUT + 11].v = (DIG_IN_11 == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 12
   dataregs[FIRST_INPUT + 12].v = (DIG_IN_12 == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 13
   dataregs[FIRST_INPUT + 13].v = (DIG_IN_13 == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 14
   dataregs[FIRST_INPUT + 14].v = (DIG_IN_14 == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 15
   dataregs[FIRST_INPUT + 15].v = (DIG_IN_15 == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 16
   dataregs[FIRST_INPUT + 16].v = (DIG_IN_16 == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 17
   dataregs[FIRST_INPUT + 17].v = (DIG_IN_17 == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 18
   dataregs[FIRST_INPUT + 18].v = (DIG_IN_18 == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 18
   dataregs[FIRST_INPUT + 19].v = (DIG_IN_19 == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 20
   dataregs[FIRST_INPUT + 20].v = (DIG_IN_20 == INPUT_ON) ? ON : OFF;
#endif
#if TOTAL_INPUTS > 21
   #error This program is only setup to handle up to 21 inputs
#endif
#if TOTAL_ANALOGS > 0
   for (i = 0; i < TOTAL_ANALOGS; i++)
      dataregs[FIRST_ANALOG + i].v = (adc_filter[i] + ADC_FILTER_ROUNDING) / ADC_FILTER_STRENGTH;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -