Subversion Repositories HomeAutomation

Rev

Rev 1910 | Blame | Compare with Previous | Last modification | View Log | SVN | RSS feed

  1. /*
  2.     linearAct started by dberg 2011.
  3.    
  4.     This module is written to control an old linear actuator from Landis & Gyr, but should work
  5.     with a broad range of linear actuators and other stuff that conforms to its simple protocol:
  6.    
  7.     Movement is controlled by two signals, typically controlling two relays. Feedback of
  8.     position is sensed by pulses connected to the hardware counter.
  9.    
  10.     The two control signals are ON/OFF and DIRECTION - one relay to control actuator engine on/off
  11.     and the other one to change the polarity.
  12.    
  13.     The position is always relative and must be calibrated manually. Use CALIBRATION command to
  14.     give the module full moving space.
  15.    
  16.     Posible features to add:
  17.     - automatic recalibration by external switch (one or perhaps two)
  18.     - PWM speedcontrol
  19. */
  20.  
  21.  
  22. #define USE_STIMULI 1                   // Stimuli can be used to simulate a pulse-source. Hardware
  23.                                                 //  connection also needed, see config.inc.template for more info
  24.  
  25.  
  26. #include "act_linearAct.h"
  27.  
  28. #if act_linearAct_USEEEPROM==1
  29. #include "act_linearAct_eeprom.h"
  30. struct eeprom_act_linearAct EEMEM eeprom_act_linearAct =
  31. {
  32.     {
  33.         ///TODO: Define initialization values on the EEPROM variables here, this will generate a *.eep file that can be used to store this values to the node, can in future be done with a EEPROM module and the make-scrips. Write the values in the exact same order as the struct is defined in the *.h file.
  34.         1200,       // pulses
  35.         300,        // min
  36.         815,        // low
  37.         5600,       // high
  38.         6100        // max
  39.     },
  40.     0   // crc, must be a correct value, but this will also be handled by the EEPROM module or make scripts
  41. };
  42. #endif
  43.  
  44. #define NOT_MOVING 0
  45. #define UP      1
  46. #define DOWN    2
  47. #define stopTimeout 1500        // ms (timerquanta)
  48.  
  49. volatile uint16_t pulses;
  50. uint16_t min;
  51. uint16_t low;
  52. uint16_t high;
  53. uint16_t max;
  54.  
  55. volatile uint8_t stopfactor = 0;
  56. volatile uint8_t direction = 0;
  57. volatile uint8_t latest_stop_pulses_up = 0;
  58. volatile uint8_t latest_stop_pulses_down = 0;
  59.  
  60. uint8_t act_linearAct_ReportInterval = (uint8_t)act_linearAct_SEND_PERIOD;
  61.  
  62. void linearAct_sendPosition(uint8_t dummy);
  63. void linearAct_sendLimits (uint8_t dummy);
  64. uint16_t linearAct_getPosition (void);
  65. uint8_t linearAct_setPosition (uint16_t new_position);
  66. void linearAct_stop(void);
  67. void linearAct_cleanUp (uint8_t dummy);
  68.  
  69.  
  70.  
  71.  
  72. ISR (TIMER1_COMPA_vect)
  73. {
  74.     linearAct_stop();                           // stoppa maskineriet
  75. }  
  76.  
  77.  
  78.  
  79.  
  80. void act_linearAct_Init(void)
  81. {
  82. #if act_linearAct_USEEEPROM==1
  83.     if (EEDATA_OK)
  84.     {
  85.       ///TODO: Use stored data to set initial values for the module
  86.       pulses = eeprom_read_word(EEDATA16.pulses_ee);
  87.       min = eeprom_read_word(EEDATA16.min_ee);
  88.       low = eeprom_read_word(EEDATA16.low_ee);
  89.       high = eeprom_read_word(EEDATA16.high_ee);
  90.       max = eeprom_read_word(EEDATA16.max_ee);
  91.     } else
  92.     {   //The CRC of the EEPROM is not correct, store default values and update CRC
  93.       eeprom_write_word_crc(EEDATA16.pulses_ee, 1200, WITHOUT_CRC);
  94.       eeprom_write_word_crc(EEDATA16.min_ee, 300, WITHOUT_CRC);
  95.       eeprom_write_word_crc(EEDATA16.low_ee, 815, WITHOUT_CRC);
  96.       eeprom_write_word_crc(EEDATA16.high_ee, 5600, WITHOUT_CRC);
  97.       eeprom_write_word_crc(EEDATA16.max_ee, 6100, WITHOUT_CRC);
  98.       EEDATA_UPDATE_CRC;
  99.     }
  100. #else  
  101.     // Only for testing/debugging...
  102.     pulses = 1200;
  103.     min = 300;
  104.     low = 815;
  105.     high = 5600;
  106.     max = 6100;
  107. #endif
  108.    
  109.     // PD5 (EXP_G) is used as Timer1 input. Hardwired, no move possible!
  110.     gpio_set_in(EXP_G);
  111.     gpio_set_pullup(EXP_G);
  112.    
  113.     //PORTD &= ~(_BV(DIR_RELAY) | _BV(ON_RELAY));
  114.     gpio_clr_pin(RELAY_ON);
  115.     gpio_clr_pin(RELAY_DIR);
  116.    
  117.     // initiera utgĆ„ngar
  118.     //DDRD = (_BV(DIR_RELAY) | _BV(ON_RELAY));
  119.     gpio_set_out(RELAY_ON);
  120.     gpio_set_out(RELAY_DIR);
  121.  
  122.     // Start report timer
  123.     Timer_SetTimeout(act_linearAct_TIMER_report, act_linearAct_ReportInterval*1000 , TimerTypeFreeRunning, 0);
  124. #if (USE_STIMULI == 1) 
  125.     // Set stimuli as an output
  126.     gpio_set_out(STIMULI);
  127.     Timer_SetTimeout(act_linearAct_TIMER_stimuli, 10, TimerTypeFreeRunning,0);
  128. #endif
  129. }
  130.  
  131. void act_linearAct_Process(void)
  132. {
  133.     if (Timer_Expired(act_linearAct_TIMER_report)) {
  134.         linearAct_sendPosition(0);
  135.     }
  136. #if (USE_STIMULI == 1)
  137.     if (Timer_Expired(act_linearAct_TIMER_stimuli)) {
  138.         gpio_toggle_pin(STIMULI);
  139.     }
  140. #endif
  141. }
  142.  
  143. void act_linearAct_HandleMessage(StdCan_Msg_t *rxMsg)
  144. {
  145.     uint16_t i;
  146.    
  147.     if (    StdCan_Ret_class(rxMsg->Header) == CAN_MODULE_CLASS_ACT &&
  148.         StdCan_Ret_direction(rxMsg->Header) == DIRECTIONFLAG_TO_OWNER &&         
  149.         rxMsg->Header.ModuleType == CAN_MODULE_TYPE_ACT_LINEARACT &&
  150.         rxMsg->Header.ModuleId == act_linearAct_ID)
  151.     {
  152.         switch (rxMsg->Header.Command) {
  153.         case CAN_MODULE_CMD_GLOBAL_REPORT_INTERVAL:
  154.             act_linearAct_ReportInterval = rxMsg->Data[0];
  155.             Timer_SetTimeout(act_linearAct_TIMER_report, act_linearAct_ReportInterval*1000 , TimerTypeFreeRunning, 0);
  156.             break;
  157.         case CAN_MODULE_CMD_LINEARACT_POSITION:
  158.             if (rxMsg->Length >= 3) {       // remain forward compatible by allowing longer messages, but not shorter
  159.                 if (rxMsg->Data[2] != 0) {
  160.                     Timer_SetTimeout(act_linearAct_TIMER_report, 750 , TimerTypeFreeRunning, 0);
  161.                     linearAct_setPosition(((uint16_t)rxMsg->Data[0] << 8) + rxMsg->Data[1]);
  162.                 } else {    // Data[2] signals moving speed, where zero stands for stop and also triggers transmission of limits
  163.                     linearAct_stop();
  164.                     // wait until complete stop (with timeout)
  165.                     i = 0;
  166.                     do {
  167.                         if (direction == 0) break;
  168.                         _delay_ms(10);
  169.                         i++;   
  170.                     } while (i < 200);
  171.                    
  172.                     linearAct_sendPosition(0);
  173.                     linearAct_sendLimits(0);
  174.                 }
  175.             }
  176.             break;
  177.         case CAN_MODULE_CMD_LINEARACT_LIMITS:
  178.             if (rxMsg->Length == 8) {
  179.            
  180.                 min = (rxMsg->Data[0] << 8) + rxMsg->Data[1];      
  181.                 low = (rxMsg->Data[2] << 8) + rxMsg->Data[3];
  182.                 high = (rxMsg->Data[4] << 8) + rxMsg->Data[5];
  183.                 max = (rxMsg->Data[6] << 8) + rxMsg->Data[7];
  184.            
  185.                 // pulses are already saved. eeprom_write_word_crc(EEDATA16.pulses_ee, 1200, WITHOUT_CRC);
  186.                 eeprom_write_word_crc(EEDATA16.min_ee, min, WITHOUT_CRC);
  187.                 eeprom_write_word_crc(EEDATA16.low_ee, low, WITHOUT_CRC);
  188.                 eeprom_write_word_crc(EEDATA16.high_ee, high, WITHOUT_CRC);
  189.                 eeprom_write_word_crc(EEDATA16.max_ee, max, WITHOUT_CRC);
  190.                 EEDATA_UPDATE_CRC;         
  191.                
  192.             }
  193.             break;
  194.         case CAN_MODULE_CMD_LINEARACT_CALIBRATION:      // set limits to absolute maximum to allow full movement
  195.            min = 0;
  196.            low = 0;
  197.            high = 0xFFFF;
  198.            max = 0xFFFF;
  199.            if (rxMsg->Length >= 2) {        // optional: set start position. Note! This will definitly ruin the old calibration.
  200.             pulses = (rxMsg->Data[0] << 8) + rxMsg->Data[1];
  201.            }
  202.            break;
  203.         default:
  204.            break;
  205.         }
  206.     }
  207. }
  208.  
  209. void act_linearAct_List(uint8_t ModuleSequenceNumber)
  210. {
  211.     StdCan_Msg_t txMsg;
  212.  
  213.     StdCan_Set_class(txMsg.Header, CAN_MODULE_CLASS_ACT);
  214.     StdCan_Set_direction(txMsg.Header, DIRECTIONFLAG_FROM_OWNER);
  215.     txMsg.Header.ModuleType = CAN_MODULE_TYPE_ACT_LINEARACT;
  216.     txMsg.Header.ModuleId = act_linearAct_ID;
  217.     txMsg.Header.Command = CAN_MODULE_CMD_GLOBAL_LIST;         
  218.     txMsg.Length = 6;
  219.  
  220.     uint32_t HwId=BIOS_GetHwId();
  221.     txMsg.Data[0] = HwId&0xff;
  222.     txMsg.Data[1] = (HwId>>8)&0xff;
  223.     txMsg.Data[2] = (HwId>>16)&0xff;
  224.     txMsg.Data[3] = (HwId>>24)&0xff;
  225.  
  226.     txMsg.Data[4] = NUMBER_OF_MODULES;
  227.     txMsg.Data[5] = ModuleSequenceNumber;
  228.  
  229.     while (StdCan_Put(&txMsg) != StdCan_Ret_OK);
  230. }
  231.  
  232.  
  233. void linearAct_sendPosition(uint8_t dummy) {
  234.     StdCan_Msg_t txMsg;
  235.     uint16_t w_tmp;
  236.    
  237.     StdCan_Set_class(txMsg.Header, CAN_MODULE_CLASS_ACT);
  238.     StdCan_Set_direction(txMsg.Header, DIRECTIONFLAG_FROM_OWNER);
  239.     txMsg.Header.ModuleType = CAN_MODULE_TYPE_ACT_LINEARACT;
  240.     txMsg.Header.ModuleId = act_linearAct_ID;
  241.     txMsg.Header.Command = CAN_MODULE_CMD_LINEARACT_POSITION;  
  242.     txMsg.Length = 3;
  243.    
  244.     w_tmp = linearAct_getPosition();
  245.    
  246.     txMsg.Data[0] = w_tmp >> 8;
  247.     txMsg.Data[1] = w_tmp;
  248.    
  249.     txMsg.Data[2] = direction;                                 
  250.  
  251.     //while (StdCan_Put(&txMsg) != StdCan_Ret_OK);
  252.     StdCan_Put(&txMsg);                                     // No problem if we miss one   
  253. }
  254.  
  255.  
  256. void linearAct_sendLimits (uint8_t dummy) {
  257.     StdCan_Msg_t txMsg;
  258.  
  259.     StdCan_Set_class(txMsg.Header, CAN_MODULE_CLASS_ACT);
  260.     StdCan_Set_direction(txMsg.Header, DIRECTIONFLAG_FROM_OWNER);
  261.     txMsg.Header.ModuleType = CAN_MODULE_TYPE_ACT_LINEARACT;
  262.     txMsg.Header.ModuleId = act_linearAct_ID;
  263.     txMsg.Header.Command = CAN_MODULE_CMD_LINEARACT_LIMITS;
  264.     txMsg.Length = 8;
  265.    
  266.     txMsg.Data[0] = min >> 8;
  267.     txMsg.Data[1] = min;
  268.        
  269.     txMsg.Data[2] = low >> 8;
  270.     txMsg.Data[3] = low;
  271.            
  272.     txMsg.Data[4] = high >> 8;
  273.     txMsg.Data[5] = high;
  274.  
  275.     txMsg.Data[6] = max >> 8;
  276.     txMsg.Data[7] = max;
  277.        
  278.     while (StdCan_Put(&txMsg) != StdCan_Ret_OK);
  279. }
  280.  
  281.  
  282. /**
  283.     Return current position
  284. */
  285. uint16_t linearAct_getPosition (void) {
  286. uint16_t total_pulses;
  287.     // Check if timer1 is running, if so, we have to take a snapshot
  288.     if ((stopfactor) || (TCCR1B != 0)) {                                           
  289.         if (direction == UP) total_pulses = pulses + TCNT1;
  290.         else total_pulses = pulses - TCNT1;
  291.     }
  292.     else total_pulses = pulses;
  293.    
  294.     return total_pulses;
  295. }
  296.  
  297.  
  298. /**
  299.     Move to position
  300.     Position is a 16 bits integer between min and max.
  301.     Not degrees, nor percent.
  302.     */
  303. uint8_t linearAct_setPosition (uint16_t new_position) {
  304.  
  305.     /* Check if position is within limits */
  306.     if (new_position > max) new_position = max;
  307.     if (new_position < min) new_position = min;
  308.    
  309.     // First check if we need to move at all
  310.     if (new_position != pulses) {      
  311.        
  312.         // Check if we already are in movement, then exit with value 1
  313.         if (direction != 0) {
  314.             //stop();
  315.             //while (in_motion());
  316.             return 1;                   // no autostop, let the user stop movement first. This may be improved.
  317.         }
  318.        
  319.         if (new_position > pulses) {
  320.            if (latest_stop_pulses_up > (new_position - pulses)) return 1;           // We will move past target, so don't move
  321.             OCR1A = new_position - pulses - latest_stop_pulses_up;  // set Output Compare target
  322.             TIMSK1 = _BV(OCIE1A);                       // activate interrupts for output compare!
  323.             direction = UP;                             // Adjust direction for software
  324.             gpio_set_pin(RELAY_DIR);                    // Adjust direction physically
  325.         }
  326.         else  {
  327.             if (latest_stop_pulses_up > (pulses - new_position)) return 1;          // We will move past target, so don't move
  328.             OCR1A = pulses - new_position - latest_stop_pulses_down;   
  329.             TIMSK1 = _BV(OCIE1A);                      
  330.             direction = DOWN;
  331.             gpio_clr_pin(RELAY_DIR);
  332.         }
  333.         TCCR1B = _BV(CS12) | _BV(CS11);             // Activate counter
  334.         _delay_ms(10);                                  // wait until direction relay is ready  
  335.         gpio_clr_pin(RELAY_ON);                     // Start moving!
  336.     }
  337.     return 0;
  338. }
  339.  
  340. void linearAct_stop(void) {
  341.     gpio_clr_pin(RELAY_ON);
  342.     _delay_ms(10);
  343.     gpio_clr_pin(RELAY_DIR);
  344.     stopfactor = 1;
  345.    
  346.     // start stop-timer
  347.     Timer_SetTimeout(act_linearAct_TIMER_stop, stopTimeout , TimerTypeOneShot, linearAct_cleanUp);
  348.     //printf("In linearAct_stop\n");
  349. }
  350.  
  351. void linearAct_cleanUp (uint8_t dummy) {
  352.     if (stopfactor) {
  353.         stopfactor = 0;
  354.         // Add number of pulses
  355.         if (direction == UP) {
  356.             pulses += TCNT1;        // adjust official counter
  357.             // keep track on how far beyond target we have moved
  358.             if (TCNT1 > OCR1A) latest_stop_pulses_up = (uint8_t) TCNT1 - OCR1A;
  359.             else latest_stop_pulses_up = 0;
  360.         }
  361.         else {
  362.             pulses -= TCNT1;
  363.             // keep track on how far beyond target we have moved
  364.             if (TCNT1 > OCR1A) latest_stop_pulses_down = (uint8_t) TCNT1 - OCR1A;
  365.             else latest_stop_pulses_down = 0;
  366.         }
  367.         TCCR1B = 0;
  368.         TCNT1 = 0;                          // reset counter before next run
  369.         direction = 0;
  370.         eeprom_write_word_crc(EEDATA16.pulses_ee, pulses, WITH_CRC);
  371.         //printf("In linearAct_cleanUp\n");
  372.         Timer_SetTimeout(act_linearAct_TIMER_report, act_linearAct_ReportInterval*1000 , TimerTypeFreeRunning, 0);
  373.         linearAct_sendPosition(0);
  374.     }
  375. }
  376.  
  377.  
  378.  
  379.