Homepage GitHub

Safeguard Parameters structure for multiple threads useage

Hello,

How are You Pavel ? I have one question maybe You or someone could help me. Just finishing my small gimbal motor control project and left to do publisher and subsciber services. Problem are how to nicely safeguard parameters service, where parameters must be read and write by multiple freertos threads. At some point code crashes.
At this time I am using legacy libuavcan library with stm32 drivers.

  1. So, first of all I have created Parameter service which work normally.
  2. I am reading parameters to the structure from EEPROM, which executes before param service initialization.
uint8_t pos[31]={0};
	myEEPROM.readData(pos, 0x04, 31);
    if (osSemaphoreWait(Param_completeID, 1000) == osOK)
    {
		configuration.PowerOn = pos[0];
		configuration.ControlType = pos[1];
		num[0].bytes[0] = pos[2];
		num[0].bytes[1] = pos[3];
		num[0].bytes[2] = pos[4];
		num[0].bytes[3] = pos[5];
		configuration.ControlValue = num[0].number;
		configuration.Calibrate = pos[6];
		configuration.PoleNumber = pos[7];
		configuration.FOCModulation = pos[8];
		num[1].bytes[0] = pos[9];
		num[1].bytes[1] = pos[10];
		num[1].bytes[2] = pos[11];
		num[1].bytes[3] = pos[12];
		configuration.VEL_P = num[1].number ;
		num[2].bytes[0] = pos[13];
		num[2].bytes[1] = pos[14];
		num[2].bytes[2] = pos[15];
		num[2].bytes[3] = pos[16];
		configuration.VEL_I = num[2].number;
		num[3].bytes[0] = pos[17];
		num[3].bytes[1] = pos[18];
		num[3].bytes[2] = pos[19];
		num[3].bytes[3] = pos[20] ;
		configuration.VEL_U_RAMP = num[3].number;
		num[4].bytes[0] = pos[21];
		num[4].bytes[1] = pos[22];
		num[4].bytes[2] = pos[23];
		num[4].bytes[3] = pos[24];
		configuration.VEL_FILTER_Tf = num[4].number;
		num[5].bytes[0] = pos[25];
		num[5].bytes[1] = pos[26];
		num[5].bytes[2] = pos[27];
		num[5].bytes[3] = pos[28];
		configuration.Angle = num[5].number;
		configuration.Orientation = pos[29];
		configuration.NodeID = pos[30];
		osSemaphoreRelease(Param_completeID);
    }
  1. Reading param values in param class without safe guards
        void readParamValue(const Name& name, Value& out_value) const override {
        	if (name == "Power On/Off")
        		out_value.to<uavcan::protocol::param::Value::Tag::boolean_value>() = configuration.PowerOn;
        	else if (name == "Control Type")
        		out_value.to<uavcan::protocol::param::Value::Tag::integer_value>() = configuration.ControlType;
        	else if (name == "Control Value")
        		out_value.to<uavcan::protocol::param::Value::Tag::real_value>() = configuration.ControlValue;
        	else if (name == "Calibrate On/Off")
            	out_value.to<uavcan::protocol::param::Value::Tag::boolean_value>() = configuration.Calibrate;
          	else if (name == "Pole Number")
          		out_value.to<uavcan::protocol::param::Value::Tag::integer_value>() = configuration.PoleNumber;
          	else if (name == "FOC Modulation")
          		out_value.to<uavcan::protocol::param::Value::Tag::integer_value>() = configuration.FOCModulation;
          	else if (name == "Velocity PID P")
          		out_value.to<uavcan::protocol::param::Value::Tag::real_value>() = configuration.VEL_P;
          	else if (name == "Velocity PID I")
          		out_value.to<uavcan::protocol::param::Value::Tag::real_value>() = configuration.VEL_I;
          	else if (name == "Velocity U Ramp")
          		out_value.to<uavcan::protocol::param::Value::Tag::real_value>() = configuration.VEL_U_RAMP;
          	else if (name == "Velocity Filter Tf")
          		out_value.to<uavcan::protocol::param::Value::Tag::real_value>() = configuration.VEL_FILTER_Tf;
          	else if (name == "Primary angle")
          		out_value.to<uavcan::protocol::param::Value::Tag::real_value>() = configuration.Angle;
          	else if (name == "Motor Pitch/Roll/Yaw")
          		out_value.to<uavcan::protocol::param::Value::Tag::integer_value>() = configuration.Orientation;
          	else if (name == "Node ID")
          		out_value.to<uavcan::protocol::param::Value::Tag::integer_value>() = configuration.NodeID;
        	else
        		if (DEBUG_MAIN == 1) printf("Can't read parameter. Check if type is correct\r\n");
        }

So first question do I need to have additional safe guards in parameter class ?

  1. Saving new parametrs. Here without safeguards
 int saveAllParams() override {
        	if (DEBUG_MAIN == 1) printf("Save - this implementation does not require any action\r\n");

			uint8_t pos[31]={0};
 			pos[0] = configuration.PowerOn;
			pos[1] = configuration.ControlType;
			num[0].number = configuration.ControlValue;
			pos[2] =  num[0].bytes[0];
			pos[3] =  num[0].bytes[1];
			pos[4] =  num[0].bytes[2];
			pos[5] =  num[0].bytes[3];
			pos[6] = configuration.Calibrate;
			pos[7] = configuration.PoleNumber;
			pos[8] = configuration.FOCModulation;
			num[1].number = configuration.VEL_P;
			pos[9] =  num[1].bytes[0];
			pos[10] =  num[1].bytes[1];
			pos[11] =  num[1].bytes[2];
			pos[12] =  num[1].bytes[3];
			num[2].number = configuration.VEL_I;
			pos[13] =  num[2].bytes[0];
			pos[14] =  num[2].bytes[1];
			pos[15] =  num[2].bytes[2];
			pos[16] =  num[2].bytes[3];
			num[3].number = configuration.VEL_U_RAMP;
			pos[17] =  num[3].bytes[0];
			pos[18] =  num[3].bytes[1];
			pos[19] =  num[3].bytes[2];
			pos[20] =  num[3].bytes[3];
			num[4].number = configuration.VEL_FILTER_Tf;
			pos[21] =  num[4].bytes[0];
			pos[22] =  num[4].bytes[1];
			pos[23] =  num[4].bytes[2];
			pos[24] =  num[4].bytes[3];
			num[5].number = configuration.Angle;
			pos[25] =  num[5].bytes[0];
			pos[26] =  num[5].bytes[1];
			pos[27] =  num[5].bytes[2];
			pos[28] =  num[5].bytes[3];
			pos[29] = configuration.Orientation;
			pos[30] = configuration.NodeID;

      	  /**
      	   * @brief Write data from EEPROM
      	   * @param pos   - array or variable where readed data will be pushed
      	   * @param start - start address in the momory in HEX format
      	   * @param size  - Size of data to read sequentally
      	   */
        	myEEPROM.writeData(pos, 0x04, 31);
        	osDelay(100);
        	return 0;     // Zero means that everything is fine.
        }

  1. And now I want to access in some thread one of the parameters
foo(configuration.value);

if I put here safeguard practical it crashes imidiatelly. If using without safeguard it could crash in different time period.

Maybe soem ideas how to solve it ?

How variables is handeld by param service and how it sfaeguard and how correctlly safeguard this variables in structures from different threads and do not influence param service handler ?

full code
https://github.com/raimapo/GMM_BIG_V2_Firmware

Hey Raimondas, great to see you back.

I’ve looked at your problem and I’m afraid it’s way outside the scope of this forum. Somebody might offer you advice here but I am personally not able to, sorry. The only thing that immediately comes to mind is that you should use the lock not only when writing but also when reading to avoid reading half-updated state if there is a concurrent write, but I’m not sure if it directly relates to your problem.

You could consider bringing your question elsewhere (unless someone else helps you here). Electronics StackExchange worked for me in the past; also there is a major Russian-languaged forum (I recall you speak Russian very well) https://electronix.ru which was helpful to me on more than one occasion.

Hi,

I though about out of scopus :slight_smile: Thank You for some references. a little bit smaller question.

So I understand that everything in "class : public uavcan::IParamManager " also must be safeguarded for thread/task safe access ?

Yes, except pure functions (without side effects) like getParamNameByIndex().

understood, thanks