Serial Wombat
a general-purpose digital interface device for hobbyists, engineers and students

 


Home
Overview
Protocol
Documentation
Channel Modes
Direct Control
Controlled Pin
Raw A/D
A/D Averaging
A/D 1st Order Filtering
Matrix Keypad
Servo Control
Analog Direct
Analog Follow
Rotary Encoder
Debouncing
Counter
Hysteresis
Morse Code
Pulse
Unipolar Stepper
LCD Driver 1
LCD Driver 2
HW Pulse Meas.
2D Lookup
SPI Master
HD44780 Generic
Remote Control
DataLogger
Min-Max
Public Data
Timed IO
Getting Started
Connectivity
Pin Mode SDK Beta
Sample Projects
Downloads
Contact Us
Purchase
Forum

Did you know...

 

Hysteresis Control

The hysteresis mode is used for controling a digital output based on a high and low limit. Hysteresis control is when a controller changes output states only when a gvien input goes below the low limit, or goes above the high limit. This method of control is frequently used when the input is highly responsive to the output, or when the input may fluxuate rapidly above and below a single setpoint.
The best known use of hysteresis control is the common household thermostat. A furnace is turned on when the room temperature drops below a set point. It is turned off when the room temperature rises some fixed number of degrees above the set point.

Hysteresis Example


If a simpler control is used (turn the furnace on below the set point, and off above the setpoint) then the furnace may rapidly turn itself on and off (short cycling) as the temperature in the room varies between slightly above and slightly below the set point. This sort of operation tends to be inefficient, hard on equipment, and irritating to humans. This operation can be caused when desired by setting the high and low limits of the hysteresis controller right next to each other.

The hysteresis mode outputs a 0 or a 1 in its public data to represent whether the output is in the low limit state or the high limit state. The public data does not indicate the value of the output pin.

The initialization of the hysteresis mode requires two packets::

Byte # Byte Value Description
0 200 Configure pin, First Message
1 pin# Physical Pin number
2 5 Channel mode = CHANNEL_MODE_HYSTERESIS
3 Low Limit MSB (Low Limit) / 256
4 Low Limit LSB (Low Limit) modulo 256
5 Hysteresis Mode 0: High and low limits are absolute values
1: High and low limits are releative to Low Limit Pin
6 Low Limit Pin Pin from which to read low limit (if not equal to 255)
7 High Limit Pin Pin from which to read high limit (if not equal to 255)


Byte # Byte Value Description
0 201 Configure pin, Second Message
1 pin# Physical Pin number
2 5 Channel mode = CHANNEL_MODE_HYSTERESIS
3 High Limit MSB (High Limit) / 256
4 High Limit LSB (High Limit) modulo 256
5 Pin Output Settings This field is a bit field:

Bits 0x03
Action to take when the input is <= the low limit
0 = pin low, 1 = pin high, 2 = pin High-Z
Bits 0x0C
Action to take when the input is >= the high limit
0 = pin low, 1 = pin high, 2 = pin High-Z
Bit 0x10
State to assume if limit pins are invalid or if low limit >= high limit
0 = assume the low state (bits 0x03)
1 = assume the high state (bits 0xC0)
Bit 0x20
State to assume at initialization if the input is between the low and high limits
0 = assume the low state (bits 0x03)
1 = assume the high state (bits 0xC0)

6 Input pin Pin whose output will be compared against the limits
7 unused  

Hysteresis Example 1:
*************
Problem:

A temperature dependent IC needs to be heated to its operating temperature when a product is used in cold environment. A small power resisor is attached to the IC, along with a temperature sensor on its opposite side. The power resistor is controlled by a transistor attached to pin 21 which turns on when the pin is high, and off when the pin is low. The desired temperature range from the sensor, attached to the A/D at pin 2, is between 0x3500 and 0x4000. Assume that the temperature gradient between the heater and the sensor is zero.

Solution:
This is a very straightforward example of hysteresis. Turn the heater on when the temperature is equal or below 0x3500, and turn it off when the temperature isequal or above 0x4000.

Initializaton commands:

First Command:
200 : Set pin mode
21 : of pin 21
5 : to Hysteresis
0x35 : with a low limit of 0x3500
0x00
0 : using absolute values
255 : Which are fixed (255 means don't use a pin input for
255 : High or Low limits).

Second Command:
201 : Set pin mode
21 : of pin 21
5 : to Hysteresis
0x40 : with a high limit of 0x4000
0x00
0x31 : Low state ( 0x03 bits) is = 1 (on)
: High state ( 0x0C bits) is 0= off
: Set default state to high-limit (1) to turn off heater if
: the limits appear invalid (0x10 bit is set)
: And start in high-limit state so we don't
: turn on the heater at start unless
: we start below minimum temperature (0x20 bit is set)
0x02 : Pin 2 is the temperature measuring pin
0x00 : The last byte of the second message is reserved. Set it to
: zero.



Hysteresis Example 2:
********************
Problem:

The producer creating a TV show about a bunch of people lost on a mysterious island after a plane crash decides that a running jet engine in the background of the crash site scene would be really cool. In order to be extra disturbing, the jet engine should spool up and slow down periodically. Since real jet engines are very expensive, an aluminum mock-up powered by an electric motor will be used. A controller is to be created. This controller will provide two knobs which will control the minimum and maximum speeds of the jet engine.

The shaft on the motor has an encoder which provides pulses on pin number 35 each time the engine spins some number of degrees. Two potentiometers have been wired to act as voltage dividers and are attached to A/D's on pins 3 and 4. A reed relay's coil has been attached to +5v and pin 34. The reed relay then controls the coil of a larger relay which drives the motor. Energizing the reed relay causes the jet engine to accelerate.

Assume pin 35 has been configured to provide the time between pulses as its public data, and that its values compare reasonably with those of the potentiometers.

Soultion:

Configure pin 34 to go low when the number of ticks provided by pin 35 is larger than the public data of pin 3, and to go input (high impedence) when the number of ticks is smaller than the public data of pin 4. This will allow the director to control the low speed limit with the pot on pin 3, and the high speed limit with the pot on pin 4.

Notes:
1. Remember that as the motor goes faster, the public data on pin 35 gets smaller, not bigger.
2. Remember that a low side driver (like we're using on the reed relay) is set to 0 volts to be active.
3. The values for Low Limit and High Limit in the commands don't matter, since pins 3 and 4 will be providing these limits.
4. Pin 3 is actually the high limit, and pin 4 is the low limit, since we're measuring rotational period, not rotational velocity.

Initializaton commands:

First Command:
200 : Set pin mode
34 : of pin 34
5 : to Hysteresis
0x00 : Low limit doesn't matter since the limit will be provided by a
0x00 : pin
0 : use absolute values
4 : Pin 4 specifies the minimum period
3 : pin 3 specifies the maximum period

Second Command:
201 : Set pin mode
34 : of pin 34
5 : to Hysteresis
0x00 : High limit doesn't matter since the limit will be provided
0x00 : by a pin
0x22 : Low state ( 0x03 bits) is = 2 (High impedence)
: High state ( 0x0C bits) is = 0 ( Low side driver)
: Set default state to Low Limit to turn off the jet
: if the director makes the low limit higher than the
: high limit (0x10 is clear)
: Set the initial state to high-limit (1) to get
: the jet moving right at the start (0x20 is set)
35 : Pin 35 is providing the timing
0x00 : The last byte of the second message is reserved. Set it to
: zero.


Hysteresis Example 3:
************

Problem:

A compressed air tank needs to be pressurized. A pressure sensor is attached to pin 8. Safe pressures for the tank are between 0 and 0x9000. For proper operation of the tools which use air from the tank, a pressure of at least 0x7000 must be maintained in the tank. A compressor can be turned on by setting pin 20 high, or turned off by setting pin 20 low. When the compressor is on it will cause the pressure in the tank to rise. A valve is installed which can release pressure from the tank. This valve is open (allows air to flow out of the tank) when no current is applied to it. Current can be applied in order to close the valve by setting pin 21 high. Current can be removed by setting pin 21 low. Due to ambient pressure changes, it is possible for the pressure in the tank to rise even if the compressor is turned off.

Provide a configuration to keep the pressure in the tank in acceptable limits.

Solution:

There are two outputs which need to be controlled here. The first is the compressor. We will set it to turn on when the pressure drops to 0x8000, and turn off when the pressure rises to 0x8800. This will leave a sufficient margin to make sure we supply necessary pressure to the tools without exceeding the specifications of the tank. The pressure release valve should be activated before the tank's maximum pressure is met. We'll open the valve at 0x8C00, and close it again after pressure drops to 0x8B00. It is important that the release valve high limit be above the compressor high limit, or the system would cycle all the time, the compressor running trying to reach a pressure which causes the valve to pop open, releasing pressure.

Initializaton commands for compressor:

First Command:
200 : Set pin mode
20 : of pin 20
5 : to Hysteresis
0x80 : with a low limit of 0x8000
0x00
0 : using absolute values
255 : Which are fixed (255 means don't use a pin input for
255 : High or Low limits).

Second Command:
201 : Set pin mode
20 : of pin 20
5 : to Hysteresis
0x88 : with a high limit of 0x8800
0x00
0x11 : Low state ( 0x03 bits) is = 1 (on)
: High state ( 0x0C bits) is 0= off
: Set default state to high-limit (1) to turn off compressor
: the limits appear invalid (0x10 bit is set)
: And start in low-limit state so we build up spare pressure
: when we start (0x20 bit is clear)
8 : Pin 8 is the pressure measuring pin
0x00 : The last byte of the second message is reserved. Set it to
: zero.

Initializaton commands for valve:

First Command:
200 : Set pin mode
21 : of pin 21
5 : to Hysteresis
0x8B : with a low limit of 0x8B00
0x00
0 : using absolute values
255 : Which are fixed (255 means don't use a pin input for
255 : High or Low limits).

Second Command:
201 : Set pin mode
21 : of pin 21
5 : to Hysteresis
0x8C : with a high limit of 0x8C00
0x00
0x11 : Low state ( 0x03 bits) is = 1 (on) (valve closed)
: High state ( 0x0C bits) is 0= off (valve open)
: Set default state to high-limit (1) to open valve if
: the limits appear invalid (0x10 bit is set)
: And start in low-limit state so we build up pressure
: when we start (0x20 bit is clear) = valve closed
8 : Pin 8 is the pressure measuring pin
0x00 : The last byte of the second message is reserved. Set it to
: zero.

 


Pin Mode Source Code

The following code is what creates the pin mode for this function inside of the Wombat. You don't need to know this to use the pin mode, but some people find it interesting. See the SDK for some insight into how pin modes work. Hold your mouse over various words and variables for definitions. #include "types.h"
#include "utilities.h"
#include "global_data.h"
#pragma code APPLICATION
#pragma romdata APPLICATION_DATA

#define HYSTERESIS_MODE_LIMITS  0
#define HYSTERESIS_MODE_MOVING_TARGET  1



uint8 hysteresis_set_pin(void);

void init_hysteresis(void)
{
        uint8 temp;

        if (rxbuffer[0] == CONFIGURE_CHANNEL_MODE_0)
        {
              tp.hysteresis.lowlimit = RXBUFFER16(3);
              tp.hysteresis.mode = rxbuffer[5] & 0x03;
              tp.hysteresis.lowcommandpin = map_pin(rxbuffer[6]);
              tp.hysteresis.highcommandpin = map_pin(rxbuffer[7]);
              tp.hysteresis.commandpin = 255;
          }
        else if (rxbuffer[0] == CONFIGURE_CHANNEL_MODE_1)
        {
              tp.hysteresis.highlimit = RXBUFFER16(3);
              tp.hysteresis.lowaction = rxbuffer[5] & 0x03;
              rxbuffer[5] >>= 2;
              tp.hysteresis.highaction = rxbuffer[5] & 0x03;
              rxbuffer[5] >>= 2;
              tp.hysteresis.defaultaction = rxbuffer[5] & 0x01;
              rxbuffer[5] >>= 1;
              tp.hysteresis.commandpin = map_pin(rxbuffer[6]);

              temp = hysteresis_set_pin();

              //printf ("Set pin result %d\n",temp);
              if ( temp == 2)
              {
                      if (rxbuffer[5] & 0x01)
                      {
                              //printf ("Initialize high action, inbetween\n");
                              vpin_set(tp.hysteresis.highaction);
                              tp.generic.buffer = 1;
                              tp.hysteresis.currentstate = 1;
                      }
                      else
                      {
                              //printf ("Initialize low action, inbetween\n");
                              vpin_set(tp.hysteresis.lowaction);
                              tp.generic.buffer = 0;
                              tp.hysteresis.currentstate = 0;
                      }
             }
             else if (temp == 1)
             {
                             //printf("Initialize active below low\n");
                              vpin_set(tp.hysteresis.highaction);
                              tp.generic.buffer = 1;
                              tp.hysteresis.currentstate = 1;
             }
              else
             {
                             //printf("Initialize active above high\n");
                 vpin_set(tp.hysteresis.lowaction);
                 tp.generic.buffer = 0;
                 tp.hysteresis.currentstate = 0;
             }

         }


}

uint8 hysteresis_set_pin(void)
{
              if (tp.hysteresis.commandpin == 255)
              {
                      //printf ("Hysteresis command pin undefined.\n");
                  return 2;
              }
              tp2.hysteresis.input = get_buffer(tp.hysteresis.commandpin);
              //printf ("Hysteresis input %d\n", tp2.hysteresis.input);
              //Calculate low and high limits
              if (tp.hysteresis.mode == HYSTERESIS_MODE_LIMITS)
              {
                  if (tp.hysteresis.highcommandpin == 255 )
                  {
                          tp2.hysteresis.highlimit = tp.hysteresis.highlimit;                }
                  else
                  {
                          tp2.hysteresis.highlimit = get_buffer(tp.hysteresis.highcommandpin);                
                  }
                          
                  if (tp.hysteresis.lowcommandpin == 255 )
                  {
                          tp2.hysteresis.lowlimit = tp.hysteresis.lowlimit;                  }
                  else
                  {
                          tp2.hysteresis.lowlimit = get_buffer(tp.hysteresis.lowcommandpin);                
                  }

              }
              else if (tp.hysteresis.mode = HYSTERESIS_MODE_MOVING_TARGET)
              {
                    if (tp.hysteresis.lowcommandpin != 255)
                    {

                            //printf ("Using relative limits...\n");
                            //printf ("Low limit is...%d\n",get_buffer(tp.hysteresis.lowcommandpin));

                         tp2.hysteresis.highlimit =
                          tp2.hysteresis.lowlimit =
                           get_buffer(tp.hysteresis.lowcommandpin);
                         tp2.hysteresis.temp16 = tp2.hysteresis.highlimit + tp.hysteresis.highlimit;
                         if (tp2.hysteresis.temp16 < tp2.hysteresis.highlimit)
                         {
                                 tp2.hysteresis.highlimit = 0xFFFF;
                         }
                         else 
                         {
                                 tp2.hysteresis.highlimit = tp2.hysteresis.temp16;
                         }

                         tp2.hysteresis.temp16 = tp2.hysteresis.lowlimit - tp.hysteresis.lowlimit;
                         if (tp2.hysteresis.temp16 > tp2.hysteresis.lowlimit)
                         {
                                 tp2.hysteresis.lowlimit = 0x0000;
                         }
                         else 
                         {
                                 tp2.hysteresis.lowlimit = tp2.hysteresis.temp16;
                         }
                    }
                    else 
                    {
                            tp2.hysteresis.lowlimit = 0x0000;
                            tp2.hysteresis.highlimit = 0x0000;
                    }
              }
              //printf ("Hysteresis low limit %d, Hysteresis high limit %d\n", tp2.hysteresis.lowlimit, tp2.hysteresis.highlimit);

              //Set the pin

              if (tp2.hysteresis.lowlimit >= tp2.hysteresis.highlimit)
              {
                      if(tp.hysteresis.defaultaction )
                      {
                              vpin_set(tp.hysteresis.highaction);
                              tp.generic.buffer = 1;
                              tp.hysteresis.currentstate = 1;
                              executive_settings.buffer_dirty = 1;
                              return (1);
                      }
                      else
                      {
                              vpin_set(tp.hysteresis.lowaction);
                              tp.generic.buffer = 0;
                              tp.hysteresis.currentstate = 0;
                              executive_settings.buffer_dirty = 1;
                              return (0);
                      }
              }
              else if (tp2.hysteresis.input >= tp2.hysteresis.highlimit && tp.hysteresis.currentstate == 0)
              {
                      vpin_set(tp.hysteresis.highaction);
                      tp.generic.buffer = 1;
                      tp.hysteresis.currentstate = 1;
                      executive_settings.buffer_dirty = 1;
                      return (1);
              }
              else if (tp2.hysteresis.input <= tp2.hysteresis.lowlimit && tp.hysteresis.currentstate == 1)
              {
                      vpin_set(tp.hysteresis.lowaction);
                      tp.generic.buffer = 0;
                      tp.hysteresis.currentstate = 0;
                      executive_settings.buffer_dirty = 1;
                       return (0);
              }
                       return (2);
}


void update_hysteresis(void)
{
       hysteresis_set_pin();
       //printf ("Pin setting %d before update\n",vpin_read());
       //printf ("Set pin result %d\n",hysteresis_set_pin());
       //printf ("Pin setting %d after update\n",vpin_read());
}


#ifndef COMPILING_FIRMWARE

#ifdef TEST_HYSTERESIS
int main(void)
{
        int final_result = 0;

        int test = 0;
        int pin = 20;
        int input_pin = 21;
        int low_limit_pin = 255;
        int high_limit_pin = 255;

        int low;
        int high;

        system_init();

        printf("Test %d:  Normal mode, absolute low 2000, absolute high 3000\n",test);
        
        low = 2000;
        high = 3000;

        set_buffer(map_pin(input_pin),2500);
        
        rxbuffer[0] = CONFIGURE_CHANNEL_MODE_0;
        rxbuffer[1] = pin;
        rxbuffer[2] = PIN_MODE_HYSTERESIS;
        rxbuffer[3] = low / 256;
        rxbuffer[4] = low % 256;
        rxbuffer[5] = 0;  // 0 - Absolute value  1- High relative to low
        rxbuffer[6] = low_limit_pin; // Low not based on pin
        rxbuffer[7] = low_limit_pin; // High not based on pin

        process_rxbuffer();

        rxbuffer[0] = CONFIGURE_CHANNEL_MODE_1;
        rxbuffer[1] = pin;
        rxbuffer[2] = PIN_MODE_HYSTERESIS;
        rxbuffer[3] = high / 256;
        rxbuffer[4] = high % 256;
        rxbuffer[5] = 1 + (0 << 2) + ( 0 << 4 ) + ( 0 << 5);
                // Low = go high, High = go low, Low if illegal, start with
                // low action
        rxbuffer[6] = input_pin;
        rxbuffer[7] = 0x55; //unused

        process_rxbuffer();
        if (!read_pin(map_pin(pin)))
        {
                //Should be on
           printf("Error, test %d.  Pin did not initialize on\n",test);
          final_result = 1;
        }

        set_buffer(map_pin(input_pin),2500);
        
        process_pins();
        if (!read_pin(map_pin(pin)))
        {
                //Should be on
           printf("Error, test %d.  Pin shoudl be on (1)\n",test);
        }
        test++;
        printf("Test %d:  Normal mode, absolute low 2000, absolute high 3000, input 2000 from 2500\n",test);
        set_buffer(map_pin(input_pin),2000);
        process_pins();
        if (!read_pin(map_pin(pin)))
        {
                //Should be on
           printf("Error, test %d.  Pin should be on (2)\n",test);
        }


        test++;
        printf("Test %d:  Normal mode, absolute low 2000, absolute high 3000, input 3000 from 2000\n",test);
        set_buffer(map_pin(input_pin),3000);
        process_pins();
        if (read_pin(map_pin(pin)))
        {
                //Should be off
           printf("Error, test %d.  Pin should be off (3)\n",test);
        }


        ++test;
        printf("Test %d: repeat in relative mode, 2000, + 1000\n",test);
        low = 0 ;
        high = 1000;
        low_limit_pin = 3;

        set_mode(map_pin(low_limit_pin),PIN_MODE_CONTROLLED);

        set_buffer(map_pin(input_pin),2500);
        set_buffer(map_pin(low_limit_pin),2000);
        
        rxbuffer[0] = CONFIGURE_CHANNEL_MODE_0;
        rxbuffer[1] = pin;
        rxbuffer[2] = PIN_MODE_HYSTERESIS;
        rxbuffer[3] = low / 256;
        rxbuffer[4] = low % 256;
        rxbuffer[5] = 1;  // 0 - Absolute value  1- High relative to low
        rxbuffer[6] = low_limit_pin; // Low not based on pin
        rxbuffer[7] = high_limit_pin; // High not based on pin

        process_rxbuffer();

        rxbuffer[0] = CONFIGURE_CHANNEL_MODE_1;
        rxbuffer[1] = pin;
        rxbuffer[2] = PIN_MODE_HYSTERESIS;
        rxbuffer[3] = high / 256;
        rxbuffer[4] = high % 256;
        rxbuffer[5] = 1 + (0 << 2) + ( 0 << 4 ) + ( 0 << 5);
                // Low = go high, High = go low, Low if illegal, start low action
        rxbuffer[6] = input_pin;
        rxbuffer[7] = 0x55; //unused

        process_rxbuffer();
        if (!read_pin(map_pin(pin)))
        {
                //Should be on
           printf("Error, test %d.  Pin did not initialize on\n",test);
          final_result = 1;
        }

        
        process_pins();
        if (!read_pin(map_pin(pin)))
        {
                //Should be on
           printf("Error, test %d.  Pin shoudl be on (1)\n",test);
        }
        test++;
        printf("Test %d:  Normal mode, absolute low 2000, absolute high 3000, input 2000 from 2500\n",test);
        set_buffer(map_pin(input_pin),2000);
        process_pins();
        if (!read_pin(map_pin(pin)))
        {
                //Should be on
           printf("Error, test %d.  Pin should be on (2)\n",test);
        }


        test++;
        printf("Test %d:  Normal mode, absolute low 2000, absolute high 3000, input 3000 from 2000\n",test);
        set_buffer(map_pin(input_pin),3000);
        process_pins();
        if (read_pin(map_pin(pin)))
        {
                //Should be off
           printf("Error, test %d.  Pin should be off (3)\n",test);
        }

        return(final_result);
}

#endif

#endif

 

Copyright Wombat Interface Products, 2005-2008. All Rights Reserved.