简体   繁体   中英

Fastest way to signal USB Device 2 if USB Device 1 fires event (both via serial port) - C#

So following setup: I have 2 USB devices.

  • (1) is a very accurate contact sensor, down to mikrometer
  • (2) is a microcontroller which communicates with a stepper motor which moves a sled to which the sensor is connected

I communicate with those 2 devices via serial port by sending a specific command to either the controller or the sensor and in return I get a value from (1) or (2) perfoms an action (ie move up, down or stop).

Now as the motor moves down, I want to send the STOP signal as soon as the sensor value changes. Lets say it reads 12,0000mm all the the time and as soon as it hits a possible obstacle, it is pushed in and it reads 11,9997mm.

For that I had 2 ideas so far, which kind of worked but not really as I wanted it too.

  • Have a background thread where I send the sensor read signal in a do-while loop and checking the if the value changed in regard to the 12,0000mm. This is to keep the UI responsive. This worked but the motor stopped to late for my taste, which gave me very inconsitent measuring results ranging between 9-11mm
  • Have a timer (10ms) where on each tick I read the sensor data and send back the result. This worked a bit better but blocks the UI thread and from what I read a timer within a background thread is not a good thing to do.

The motor only need to be signaled once in order to start or stop an action, the sensor needs to be signaled constantly for an updated readout. I understand that in my "solutions" it depends on where the programm is currently. If it is at the end of the while loop, it needs a few milliseconds in order to start again and read out the sensor.

Is there a way to get consitent sensor data between 11.9960 and 12.0000, or similar, meaning to send the stop signal really fast or that at least I can control better when the motor will stop? The faster the speed X is of my motor (ROR_Speed(X)) the less accurate the results got. I could set a very low speed but then it would take forever which is not an option. I hope the information is enough for some tipps or improvements.

For important code (the whole code is quite big with multiple classes)

    private void Read_sensor()                                   
    {
        Sensor.SendCommand();
        Sensor_height = Sensor.GetDecimalOutput();
    }

    private void Sensor_Read()                                         
    {
        System.Threading.Thread t = new System.Threading.Thread(() => Sensor_read());
        t.Start();
    }

    private void Sensor_read()                                         
    {
        do
        {
            Read_sensor();
            if (Sensor_height > Sensor_height_Check + 3000)  //the sensor answer is between
            {                                                //0 and 120000, for readout
                Motor.Instance.MST(0);  //stop signal        //needs to be divided by 10000
                    Thread.Sleep(500);
                    Threadbreak = 1;
                    Read_sensor();
                    break;
            }
        } while (Threadbreak == 0);
    }
private void button3_Click(object sender, EventArgs e)
    {
        Threadbreak = 0;
        Sensor.SendCommand();           //gets intial value
        Sensor_height_Check = Sensor.GetDecimalOutput();
        Sensor_Read();                  //start background thread
        Motor.Instance.ROR_Speed(1000); //motor moves sled down
        do                              //wait until backgroundtrhead is stopped
        {                              //not really good pracice I know maybe
            counter += 1;              //someone could point me in the right
        } while (Threadbreak != 1);    //direction here as well
        MessageBox.Show("Check: " + Convert.ToString(Sensor_height_Check) + "Actual: " + Convert.ToString(Sensor_height));
        Motor.Instance.Move_steps_Schlitten(-80000);

    }

If you really want to keep two threads and block one until something happens in another, then you can use Monitor methods Pulse / Wait (make sure to read this great book ):

object sync = new object();

// thread 1 - block
lock(sync)
    Monitor.Wait(sync);

// thread 2 - signal
lock(sync)
    Monitor.Pulse(sync);

In your case it would be more efficient (easy) to use async/await and just one thread:

async void button1_Click(object sender, EventArgs e)
{
    button1.Enabled = false;
    await RotateMotorUntilSensor();
    button1.Enabled = true;
}

Task RotateMotorUntilSensor(someParameters) => Task.Run(() =>
{
    Invoke(new Action(() => Text = "Start"));

    // .. start motor here

    while (true)
    {

        // .. read sensor here

        if (someCondition)
        {

            // ... stop motor here

            Invoke(new Action(() => Text = "stop"));
            break;
        }
        Thread.Sleep(0);
    }
});

You want to read sensor as often as possible. Polling time introduces the lag. Taking your code as example: if you have read the sensor and its value is changed 1 ms after - you will only be able to read new value 500 ms later, delaying motor stop by the same amount at worst.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM