Subversion Repositories HomeAutomation

Rev

Rev 1382 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | SVN | RSS feed

  1.  
  2. #include "act_PID_eeprom.h"
  3.  
  4. #include "act_PID.h"
  5. #include <drivers/misc/pid.h>
  6.  
  7. #define PID_ON 1
  8. #define PID_OFF 0
  9. #define PID_AUTO 2
  10.  
  11. //! Parameters for regulator
  12. struct PID_DATA pidData;
  13. struct PID_DEBUG_DATA pidDebugData;
  14.  
  15. uint8_t sensorModuleType, sensorModuleId,sensorId;
  16. uint8_t PID_Status;
  17. uint8_t calculatePID_flag,sendPID_flag = 0;
  18. uint16_t pwmValue=0;
  19. float referenceValue, measurementValue, inputValue;
  20.  
  21. struct eeprom_act_PID EEMEM eeprom_act_PID =
  22. {
  23.     {
  24.         ///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.
  25.         0x0200, // referenceValue
  26.         CAN_MODULE_TYPE_SNS_DS18X20,    //SensorModuleType
  27.         0x00,   //SensorModuleId
  28.         0x00,   //SensorId
  29.         0x0000, //uint16_t K_P;
  30.         0x0000, //uint16_t K_I;
  31.         0x0000, //uint16_t K_D;
  32.         0x00,   //uint8_t TimeMsOrS;
  33.         0x0000, //uint16_t Time;
  34.         CAN_MODULE_TYPE_ACT_SOFTPWM,    //ActuatorModuleType
  35.         0x00,   //ActuatorModuleId
  36.         0x00,   //ActuatorId
  37.         10, //SendPeriod
  38.  
  39.     },
  40.     0   // crc, must be a correct value, but this will also be handled by the EEPROM module or make scripts
  41. };
  42.  
  43. void calculatePID(void) {
  44.     if (PID_Status == PID_OFF){
  45.         ;// use current PWMvalue
  46.     } else {
  47. //printf("PWM: %d\n",pwmValue);
  48. uint16_t tempPwm = pwmValue;
  49.         tempPwm += (int16_t) pid_Controller(referenceValue, measurementValue, &pidData, &pidDebugData);
  50. //printf("InOut: ref: %d, meas: %d\n",(int16_t) (referenceValue*10), (int16_t) (measurementValue*10));
  51. //printf("PWM: %d\n",pwmValue);
  52.  
  53.         if (tempPwm < MIN_PWM_VALUE) {
  54.             tempPwm = MIN_PWM_VALUE;
  55.         }
  56.         else if (tempPwm > MAX_PWM_VALUE) {
  57.             tempPwm = MAX_PWM_VALUE-1;
  58.         }
  59.         cli();
  60.         pwmValue = tempPwm;
  61.         sei();
  62. //printf("PWM: %d\n",pwmValue);
  63.         //send current PWM value as soon as possible
  64.         sendPID_flag=1;
  65.     }
  66.  
  67. }
  68.  
  69.  
  70. void calculatePID_callback(uint8_t timer)
  71. {
  72.  
  73.     if (eeprom_read_byte(EEDATA.TimeMsOrS) == CAN_MODULE_ENUM_PID_CONFIG_PARAMETER_TIMEUNIT_S) {
  74.         static uint8_t seconds = 0;
  75.         seconds++;
  76.         if (seconds >= eeprom_read_word(EEDATA16.Time))
  77.         {
  78.             seconds = 0;
  79.             calculatePID_flag = 1;
  80.         }
  81.     } else {
  82.         calculatePID_flag = 1;
  83.     }
  84. }
  85.  
  86. void sendPID(void)
  87. {
  88.     if (eeprom_read_byte(EEDATA.actuatorModuleType) != 0) {
  89.         StdCan_Msg_t txMsg;
  90.         StdCan_Set_class(txMsg.Header, CAN_MODULE_CLASS_ACT); ///TODO: Change this to the actual class type
  91.         StdCan_Set_direction(txMsg.Header, DIRECTIONFLAG_TO_OWNER);
  92.         txMsg.Header.ModuleType = eeprom_read_byte(EEDATA.actuatorModuleType); ///TODO: Change this to the actual module type
  93.         txMsg.Header.ModuleId = eeprom_read_byte(EEDATA.actuatorModuleId);
  94.         txMsg.Header.Command = CAN_MODULE_CMD_PHYSICAL_PWM;
  95.         txMsg.Length = 3;
  96.         txMsg.Data[0] = eeprom_read_byte(EEDATA.actuatorId);
  97.         //uint16_t tempPWM =(uint16_t) (pwmValue*10000);
  98.         txMsg.Data[1] = ( (pwmValue)>>8)&0xff;
  99.         txMsg.Data[2] = ( (pwmValue))&0xff;
  100.         while (StdCan_Put(&txMsg) != StdCan_Ret_OK);
  101.     }
  102.     StdCan_Msg_t txMsg;
  103.     StdCan_Set_class(txMsg.Header, CAN_MODULE_CLASS_ACT); ///TODO: Change this to the actual class type
  104.     StdCan_Set_direction(txMsg.Header, DIRECTIONFLAG_FROM_OWNER);
  105.     txMsg.Header.ModuleType = CAN_MODULE_TYPE_ACT_PID; ///TODO: Change this to the actual module type
  106.     txMsg.Header.ModuleId = act_PID_ID;
  107.     txMsg.Header.Command = CAN_MODULE_CMD_PID_PID_STATUS;
  108.     txMsg.Length = 8;
  109.     txMsg.Data[0] = (uint8_t)0x00ff & (((uint32_t)(measurementValue*64))>>8);
  110.     txMsg.Data[1] = (uint8_t)0x00ff & ((uint32_t)(measurementValue*64));
  111.     txMsg.Data[2] = (uint8_t)0x00ff & (((uint32_t)(referenceValue*64))>>8);
  112.     txMsg.Data[3] = (uint8_t)0x00ff & ((uint32_t)(referenceValue*64));
  113.     //uint16_t tempPWM =(uint16_t) (pwmValue*10000);
  114.         txMsg.Data[4] = ( (pwmValue)>>8)&0xff;
  115.         txMsg.Data[5] = ( (pwmValue))&0xff;
  116.     txMsg.Data[6] = ((int16_t) (pidDebugData.Sum)>>8)&0xff;
  117.     txMsg.Data[7] = ((int16_t) (pidDebugData.Sum))&0xff;
  118.     while (StdCan_Put(&txMsg) != StdCan_Ret_OK);
  119. }
  120.  
  121. void sendPID_callback(uint8_t timer) {
  122.   sendPID_flag=1;
  123. }
  124.  
  125. #ifdef act_PID_SEND_DEBUG_TIMER
  126. void sendPID_debug_callback(uint8_t timer)
  127. {
  128.     StdCan_Msg_t txMsg;
  129.     StdCan_Set_class(txMsg.Header, CAN_MODULE_CLASS_ACT); ///TODO: Change this to the actual class type
  130.     StdCan_Set_direction(txMsg.Header, DIRECTIONFLAG_FROM_OWNER);
  131.     txMsg.Header.ModuleType = CAN_MODULE_TYPE_ACT_PID; ///TODO: Change this to the actual module type
  132.     txMsg.Header.ModuleId = act_PID_ID;
  133.     txMsg.Header.Command = CAN_MODULE_CMD_PID_DEBUG;
  134.     txMsg.Length = 8;
  135.  
  136.     txMsg.Data[0] = ((int16_t) (pidDebugData.P_term)>>8)&0xff;
  137.     txMsg.Data[1] = ((int16_t) (pidDebugData.P_term))&0xff;
  138.     txMsg.Data[2] = ((int16_t) (pidDebugData.I_term)>>8)&0xff;
  139.     txMsg.Data[3] = ((int16_t) (pidDebugData.I_term))&0xff;
  140.     txMsg.Data[4] = ((int16_t) (pidDebugData.D_term)>>8)&0xff;
  141.     txMsg.Data[5] = ((int16_t) (pidDebugData.D_term))&0xff;
  142.     txMsg.Data[6] = ((int16_t) (pidDebugData.Sum)>>8)&0xff;
  143.     txMsg.Data[7] = ((int16_t) (pidDebugData.Sum))&0xff;
  144.     while (StdCan_Put(&txMsg) != StdCan_Ret_OK);
  145. //printf("PID: P:%d, I:%d, D:%d, S:%d\n",(int16_t)pidDebugData.P_term,(int16_t)pidDebugData.I_term,(int16_t)pidDebugData.D_term,(int16_t)pidDebugData.Sum);
  146. }
  147. #endif
  148.  
  149. void act_PID_Init(void)
  150. {
  151.     if (EEDATA_OK)
  152.     {
  153.     } else
  154.     {   //The CRC of the EEPROM is not correct, store default values and update CRC
  155.       eeprom_write_word_crc(EEDATA16.referenceValue, 0x0520, WITHOUT_CRC);
  156.       eeprom_write_byte_crc(EEDATA.sensorModuleType, PID_TEMPERATURE_SENSOR_MODULE_TYPE, WITHOUT_CRC);
  157.       eeprom_write_byte_crc(EEDATA.sensorModuleId, PID_TEMPERATURE_SENSOR_MODULE_ID, WITHOUT_CRC);
  158.       eeprom_write_byte_crc(EEDATA.sensorId, PID_TEMPERATURE_SENSOR, WITHOUT_CRC);
  159.       eeprom_write_word_crc(EEDATA16.K_P, (int16_t)(DEFAULT_K_P*64), WITHOUT_CRC);
  160.       eeprom_write_word_crc(EEDATA16.K_I, (int16_t)(DEFAULT_K_I*64), WITHOUT_CRC);
  161.       eeprom_write_word_crc(EEDATA16.K_D, (int16_t)(DEFAULT_K_D*64), WITHOUT_CRC);
  162.       eeprom_write_byte_crc(EEDATA.TimeMsOrS, DEFAULT_PID_CALC_PERIOD_UNIT, WITHOUT_CRC);
  163.       eeprom_write_word_crc(EEDATA16.Time, DEFAULT_PID_CALC_PERIOD, WITHOUT_CRC);
  164.       eeprom_write_byte_crc(EEDATA.actuatorModuleType, PID_PWM_ACTUATOR_MODULE_TYPE, WITHOUT_CRC);
  165.       eeprom_write_byte_crc(EEDATA.actuatorModuleId, PID_PWM_ACTUATOR_MODULE_ID, WITHOUT_CRC);
  166.       eeprom_write_byte_crc(EEDATA.actuatorId, PID_PWM_ACTUATOR, WITHOUT_CRC);
  167.       eeprom_write_byte_crc(EEDATA.sendPeriod, 10, WITH_CRC);
  168.     }
  169.     referenceValue = (float) eeprom_read_word(EEDATA16.referenceValue)/64;
  170.     sensorModuleType = eeprom_read_byte(EEDATA.sensorModuleType);
  171.     sensorModuleId = eeprom_read_byte(EEDATA.sensorModuleId);
  172.     sensorId = eeprom_read_byte(EEDATA.sensorId);
  173.  
  174.  
  175.     if (eeprom_read_byte(EEDATA.TimeMsOrS) == CAN_MODULE_ENUM_PID_CONFIG_PARAMETER_TIMEUNIT_S) {
  176.         Timer_SetTimeout(act_PID_TIMER, 1000, TimerTypeFreeRunning, &calculatePID_callback);
  177.     } else {
  178.         Timer_SetTimeout(act_PID_TIMER, eeprom_read_word(EEDATA16.Time), TimerTypeFreeRunning, &calculatePID_callback);
  179.     }
  180.  
  181.     pid_Init((float) eeprom_read_word(EEDATA16.K_P)/64 * SCALING_FACTOR, (float) eeprom_read_word(EEDATA16.K_I)/64 * SCALING_FACTOR , (float) eeprom_read_word(EEDATA16.K_D)/64 * SCALING_FACTOR , &pidData);
  182.     pwmValue += (int16_t) pid_Controller(referenceValue, measurementValue, &pidData, &pidDebugData);
  183.     PID_Status = PID_ON;
  184.     pwmValue = DEFAULT_PWM_VALUE;
  185.     Timer_SetTimeout(act_PID_SEND_TIMER, eeprom_read_byte(EEDATA.sendPeriod)*1000, TimerTypeFreeRunning, &sendPID_callback);
  186.     #ifdef act_PID_SEND_DEBUG_TIMER
  187.       Timer_SetTimeout(act_PID_SEND_DEBUG_TIMER, PID_SEND_DEBUG_PERIOD, TimerTypeFreeRunning, &sendPID_debug_callback);
  188.     #endif
  189. }
  190.  
  191. void act_PID_Process(void)
  192. {
  193.     if (calculatePID_flag) {
  194.         calculatePID();
  195.         calculatePID_flag = 0;
  196.     }
  197.     if (sendPID_flag) {
  198.         sendPID();
  199.         sendPID_flag= 0;
  200.     }
  201. }
  202.  
  203. void act_PID_HandleMessage(StdCan_Msg_t *rxMsg)
  204. {
  205.  
  206.     if (    StdCan_Ret_class(rxMsg->Header) == CAN_MODULE_CLASS_ACT &&
  207.             StdCan_Ret_direction(rxMsg->Header) == DIRECTIONFLAG_TO_OWNER &&
  208.             rxMsg->Header.ModuleType == CAN_MODULE_TYPE_ACT_PID &&
  209.             rxMsg->Header.ModuleId == act_PID_ID)
  210.     {
  211.         switch (rxMsg->Header.Command)
  212.         {
  213.         case CAN_MODULE_CMD_PHYSICAL_TEMPERATURE_CELSIUS:
  214.             if (rxMsg->Data[0]==0)  //sensor id shall be zero
  215.             {
  216.                 if (rxMsg->Length == 3)
  217.                 {
  218.                     if (0x80 == rxMsg->Data[1] && 0x00 == rxMsg->Data[2])
  219.                     {
  220.                         pid_Reset_Integrator(&pidData);
  221.                         PID_Status = PID_AUTO;
  222.                     }
  223.                     else
  224.                     {
  225.                         pid_Reset_Integrator(&pidData);
  226.                         eeprom_write_word_crc(EEDATA16.referenceValue, ((rxMsg->Data[1]<<8) + rxMsg->Data[2]), WITH_CRC);
  227.                         referenceValue = ((rxMsg->Data[1]<<8) + rxMsg->Data[2])/64;
  228.                     }
  229.                 } else
  230.                 {
  231.                     rxMsg->Data[1] = (uint8_t)0x00ff & (((uint32_t)(referenceValue*64))>>8);
  232.                     rxMsg->Data[2] = (uint8_t)0x00ff & ((uint32_t)referenceValue*64);
  233.                     StdCan_Set_direction(rxMsg->Header, DIRECTIONFLAG_FROM_OWNER);
  234.                     rxMsg->Length = 3;
  235.                     while (StdCan_Put(rxMsg) != StdCan_Ret_OK);
  236.                 }
  237.  
  238.             }
  239.         break;
  240.         case CAN_MODULE_CMD_PID_CONFIG_SENSOR:
  241.             if (rxMsg->Length == 3)
  242.             {
  243.                 eeprom_write_byte_crc(EEDATA.sensorModuleType, rxMsg->Data[0] , WITHOUT_CRC);
  244.                 eeprom_write_byte_crc(EEDATA.sensorModuleId, rxMsg->Data[1] , WITHOUT_CRC);
  245.                 eeprom_write_byte_crc(EEDATA.sensorId, rxMsg->Data[2] , WITH_CRC);
  246.                 sensorModuleType = eeprom_read_byte(EEDATA.sensorModuleType);
  247.                 sensorModuleId = eeprom_read_byte(EEDATA.sensorModuleId);
  248.                 sensorId = eeprom_read_byte(EEDATA.sensorId);
  249.             }
  250.             rxMsg->Data[0] = eeprom_read_byte(EEDATA.sensorModuleType);
  251.             rxMsg->Data[1] = eeprom_read_byte(EEDATA.sensorModuleId);
  252.             rxMsg->Data[2] = eeprom_read_byte(EEDATA.sensorId);
  253.             StdCan_Set_direction(rxMsg->Header, DIRECTIONFLAG_FROM_OWNER);
  254.             rxMsg->Length = 3;
  255.             while (StdCan_Put(rxMsg) != StdCan_Ret_OK);
  256.         break;
  257.         case CAN_MODULE_CMD_PID_CONFIG_ACTUATOR:
  258.             if (rxMsg->Length == 3)
  259.             {
  260.                 eeprom_write_byte_crc(EEDATA.actuatorModuleType, rxMsg->Data[0] , WITHOUT_CRC);
  261.                 eeprom_write_byte_crc(EEDATA.actuatorModuleId, rxMsg->Data[1] , WITHOUT_CRC);
  262.                 eeprom_write_byte_crc(EEDATA.actuatorId, rxMsg->Data[2] , WITH_CRC);
  263.             }
  264.             rxMsg->Data[0] = eeprom_read_byte(EEDATA.actuatorModuleType);
  265.             rxMsg->Data[1] = eeprom_read_byte(EEDATA.actuatorModuleId);
  266.             rxMsg->Data[2] = eeprom_read_byte(EEDATA.actuatorId);
  267.             StdCan_Set_direction(rxMsg->Header, DIRECTIONFLAG_FROM_OWNER);
  268.             rxMsg->Length = 3;
  269.             while (StdCan_Put(rxMsg) != StdCan_Ret_OK);
  270.         break;
  271.         case CAN_MODULE_CMD_GLOBAL_REPORT_INTERVAL:
  272.             if (rxMsg->Length == 1) {
  273.                 if (65 <= rxMsg->Data[0])
  274.                     rxMsg->Data[0]=65;
  275.                 if (0 == rxMsg->Data[0])
  276.                     rxMsg->Data[0]=1;
  277.                 eeprom_write_byte_crc(EEDATA.sendPeriod, rxMsg->Data[0], WITH_CRC);
  278.                 Timer_SetTimeout(act_PID_SEND_TIMER, rxMsg->Data[0]*1000, TimerTypeFreeRunning, &sendPID_callback);
  279.                 StdCan_Set_direction(rxMsg->Header, DIRECTIONFLAG_FROM_OWNER);
  280.                 while (StdCan_Put(rxMsg) != StdCan_Ret_OK);
  281.             }
  282.         break;
  283.  
  284.         case CAN_MODULE_CMD_PID_CONFIG_PARAMETER:
  285.             if (rxMsg->Length == 8)
  286.             {
  287.                 eeprom_write_word_crc(EEDATA16.K_P, (uint16_t)rxMsg->Data[1]+(rxMsg->Data[0]<<8), WITHOUT_CRC);
  288.                 eeprom_write_word_crc(EEDATA16.K_I, (uint16_t)rxMsg->Data[3]+(rxMsg->Data[2]<<8), WITHOUT_CRC);
  289.                 eeprom_write_word_crc(EEDATA16.K_D, (uint16_t)rxMsg->Data[5]+(rxMsg->Data[4]<<8), WITHOUT_CRC);
  290.                 eeprom_write_byte_crc(EEDATA.TimeMsOrS, ((rxMsg->Data[6]&0x80)>>7), WITHOUT_CRC);
  291.                 eeprom_write_word_crc(EEDATA16.Time, (uint16_t)rxMsg->Data[7]+((rxMsg->Data[6]&0x7f)<<8), WITH_CRC);
  292.                 if (eeprom_read_byte(EEDATA.TimeMsOrS) == 0) {
  293.                     Timer_SetTimeout(act_PID_TIMER, 1000, TimerTypeFreeRunning, &calculatePID_callback);
  294.                 } else {
  295.                     Timer_SetTimeout(act_PID_TIMER, eeprom_read_word(EEDATA16.Time), TimerTypeFreeRunning, &calculatePID_callback);
  296.                 }
  297.  
  298.                 pid_Init((float) eeprom_read_word(EEDATA16.K_P)/64 * SCALING_FACTOR, (float) eeprom_read_word(EEDATA16.K_I)/64 * SCALING_FACTOR , (float) eeprom_read_word(EEDATA16.K_D)/64 * SCALING_FACTOR , &pidData);
  299.                 pwmValue += (int16_t) pid_Controller(referenceValue, measurementValue, &pidData, &pidDebugData);
  300.                 PID_Status = PID_ON;
  301.                 pwmValue = DEFAULT_PWM_VALUE;
  302.  
  303.                 rxMsg->Data[0] = (0xff&(eeprom_read_word(EEDATA16.K_P)>>8));
  304.                 rxMsg->Data[1] = (0xff&(eeprom_read_word(EEDATA16.K_P)));
  305.                 rxMsg->Data[2] = (0xff&(eeprom_read_word(EEDATA16.K_I)>>8));
  306.                 rxMsg->Data[3] = (0xff&(eeprom_read_word(EEDATA16.K_I)));
  307.                 rxMsg->Data[4] = (0xff&(eeprom_read_word(EEDATA16.K_D)>>8));
  308.                 rxMsg->Data[5] = (0xff&(eeprom_read_word(EEDATA16.K_D)));
  309.                 rxMsg->Data[6] = (0xff&(eeprom_read_word(EEDATA16.Time)>>8));
  310.                 rxMsg->Data[7] = (0xff&(eeprom_read_word(EEDATA16.Time)));
  311.                 rxMsg->Data[6] |= (0x80&(eeprom_read_byte(EEDATA.TimeMsOrS))<<7);
  312.                 StdCan_Set_direction(rxMsg->Header, DIRECTIONFLAG_FROM_OWNER);
  313.                 rxMsg->Length = 8;
  314.                 while (StdCan_Put(rxMsg) != StdCan_Ret_OK);
  315.             } else
  316.             {
  317.                 rxMsg->Data[0] = (0xff&(eeprom_read_word(EEDATA16.K_P)>>8));
  318.                 rxMsg->Data[1] = (0xff&(eeprom_read_word(EEDATA16.K_P)));
  319.                 rxMsg->Data[2] = (0xff&(eeprom_read_word(EEDATA16.K_I)>>8));
  320.                 rxMsg->Data[3] = (0xff&(eeprom_read_word(EEDATA16.K_I)));
  321.                 rxMsg->Data[4] = (0xff&(eeprom_read_word(EEDATA16.K_D)>>8));
  322.                 rxMsg->Data[5] = (0xff&(eeprom_read_word(EEDATA16.K_D)));
  323.                 rxMsg->Data[6] = (0xff&(eeprom_read_word(EEDATA16.Time)>>8));
  324.                 rxMsg->Data[7] = (0xff&(eeprom_read_word(EEDATA16.Time)));
  325.                 rxMsg->Data[6] |= (0x80&(eeprom_read_byte(EEDATA.TimeMsOrS))<<7);
  326.                 StdCan_Set_direction(rxMsg->Header, DIRECTIONFLAG_FROM_OWNER);
  327.                 rxMsg->Length = 8;
  328.                 while (StdCan_Put(rxMsg) != StdCan_Ret_OK);
  329.             }
  330.         break;
  331.         }
  332.     }
  333.  
  334.  
  335.     if (    StdCan_Ret_class(rxMsg->Header) == CAN_MODULE_CLASS_SNS &&
  336.                 StdCan_Ret_direction(rxMsg->Header) == DIRECTIONFLAG_FROM_OWNER &&
  337.                 rxMsg->Header.ModuleType == sensorModuleType &&
  338.                 rxMsg->Header.ModuleId == sensorModuleId &&
  339.                 rxMsg->Header.Command == CAN_MODULE_CMD_PHYSICAL_TEMPERATURE_CELSIUS &&
  340.                 rxMsg->Data[0] == sensorId)
  341.         {
  342.             if (0x80 == rxMsg->Data[1] && 0x00 == rxMsg->Data[2])
  343.             {
  344.                 //Error on the temperature signal, do something
  345.             }
  346.             else
  347.             {
  348.                 measurementValue = ((float)((rxMsg->Data[1]<<8) + rxMsg->Data[2]))/64;
  349.             }
  350.  
  351.         }
  352.  
  353. }
  354.  
  355. void act_PID_List(uint8_t ModuleSequenceNumber)
  356. {
  357.     StdCan_Msg_t txMsg;
  358.    
  359.     StdCan_Set_class(txMsg.Header, CAN_MODULE_CLASS_ACT); ///TODO: Change this to the actual class type
  360.     StdCan_Set_direction(txMsg.Header, DIRECTIONFLAG_FROM_OWNER);
  361.     txMsg.Header.ModuleType = CAN_MODULE_TYPE_ACT_PID; ///TODO: Change this to the actual module type
  362.     txMsg.Header.ModuleId = act_PID_ID;
  363.     txMsg.Header.Command = CAN_MODULE_CMD_GLOBAL_LIST;
  364.     txMsg.Length = 6;
  365.  
  366.     txMsg.Data[0] = NODE_HW_ID_BYTE0;
  367.     txMsg.Data[1] = NODE_HW_ID_BYTE1;
  368.     txMsg.Data[2] = NODE_HW_ID_BYTE2;
  369.     txMsg.Data[3] = NODE_HW_ID_BYTE3;
  370.    
  371.     txMsg.Data[4] = NUMBER_OF_MODULES;
  372.     txMsg.Data[5] = ModuleSequenceNumber;
  373.    
  374.     while (StdCan_Put(&txMsg) != StdCan_Ret_OK);
  375. }
  376.