Subversion Repositories HomeAutomation

Rev

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

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