Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.arch.embedded > #29293 > unrolled thread

Cortex-M: share an int between two tasks

Started bypozz <pozzugno@gmail.com>
First post2020-03-13 15:38 +0100
Last post2020-03-18 16:24 +0000
Articles 9 on this page of 49 — 12 participants

Back to article view | Back to comp.arch.embedded


Contents

  Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-13 15:38 +0100
    Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-13 17:49 +0100
      Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-18 09:09 +0100
        Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-18 09:17 +0100
        Re: Cortex-M: share an int between two tasks Hans-Bernhard Bröker <HBBroeker@t-online.de> - 2020-03-18 17:37 +0100
          Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-18 19:23 +0100
      Re: Cortex-M: share an int between two tasks Les Cargill <lcargill99@comcast.com> - 2020-03-22 13:42 -0500
        Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-22 21:45 +0100
        Re: Cortex-M: share an int between two tasks Richard Damon <Richard@Damon-Family.org> - 2020-03-22 20:50 -0400
          Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-23 09:09 +0100
            Re: Cortex-M: share an int between two tasks Richard Damon <Richard@Damon-Family.org> - 2020-03-23 08:00 -0400
              Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-23 14:21 +0100
                Re: Cortex-M: share an int between two tasks Clifford Heath <no.spam@please.net> - 2020-03-25 09:55 +1100
                  Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-30 16:07 +0200
                    Re: Cortex-M: share an int between two tasks Clifford Heath <no.spam@please.net> - 2020-03-31 09:02 +1100
                      Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-31 10:57 +0200
                        Re: Cortex-M: share an int between two tasks Clifford Heath <no.spam@please.net> - 2020-04-01 12:05 +1100
                  Re: Cortex-M: share an int between two tasks Tauno Voipio <tauno.voipio@notused.fi.invalid> - 2020-03-30 17:49 +0300
                  Re: Cortex-M: share an int between two tasks kegs@provalid.com (Kent Dickey) - 2020-05-19 00:34 -0500
                    Re: Cortex-M: share an int between two tasks Clifford Heath <no.spam@please.net> - 2020-05-19 17:40 +1000
                    Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-05-19 12:00 +0200
                      Re: Cortex-M: share an int between two tasks Tom Gardner <spamjunk@blueyonder.co.uk> - 2020-05-19 13:48 +0100
    Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-18 09:20 +0100
      Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-18 10:06 +0100
        Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-18 10:59 +0100
          Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-18 11:48 +0100
            Re: Cortex-M: share an int between two tasks upsidedown@downunder.com - 2020-03-18 20:37 +0200
              Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-18 21:24 +0100
                Re: Cortex-M: share an int between two tasks Tauno Voipio <tauno.voipio@notused.fi.invalid> - 2020-03-19 14:05 +0200
                  Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-19 13:16 +0100
                    Re: Cortex-M: share an int between two tasks Tauno Voipio <tauno.voipio@notused.fi.invalid> - 2020-03-19 14:27 +0200
                      Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-19 14:13 +0100
              Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-19 08:03 +0100
                Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-19 09:10 +0100
                Re: Cortex-M: share an int between two tasks upsidedown@downunder.com - 2020-03-19 12:51 +0200
            Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-19 07:40 +0100
              Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-19 09:38 +0100
                Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-19 11:26 +0100
                  Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-19 12:12 +0100
                    Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-19 15:31 +0100
                      Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-23 09:22 +0100
                      Re: Cortex-M: share an int between two tasks Richard Damon <Richard@Damon-Family.org> - 2020-03-23 08:10 -0400
                        Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-23 13:47 +0100
                          Re: Cortex-M: share an int between two tasks David Brown <david.brown@hesbynett.no> - 2020-03-23 14:31 +0100
                          Re: Cortex-M: share an int between two tasks Richard Damon <Richard@Damon-Family.org> - 2020-03-23 13:50 -0400
              Re: Cortex-M: share an int between two tasks Niklas Holsti <niklas.holsti@tidorum.invalid> - 2020-03-19 12:27 +0200
                Re: Cortex-M: share an int between two tasks pozz <pozzugno@gmail.com> - 2020-03-19 11:45 +0100
                  Re: Cortex-M: share an int between two tasks upsidedown@downunder.com - 2020-03-19 13:27 +0200
      Re: Cortex-M: share an int between two tasks antispam@math.uni.wroc.pl - 2020-03-18 16:24 +0000

Page 3 of 3 — ← Prev page 1 2 [3]


#29334

Frompozz <pozzugno@gmail.com>
Date2020-03-23 09:22 +0100
Message-ID<r59rki$pjq$1@dont-email.me>
In reply to#29320
Il 19/03/2020 15:31, pozz ha scritto:
> Il 19/03/2020 12:12, David Brown ha scritto:
 > [...]
>> One possible arrangement could be:
>>
>>
>> static volatile bool sensor_stopped;
>> static SemaphoreHandle_t sensor_semaphore;
>>
>> // TaskA
>> static const TickType_t delay_ticks = pdMS_TO_TICKS(10);
>>
>> while (true) {
>>      // Get sample and process
>>      if (sensor_stopped) {
>>          // Wait for semaphore to be ready
>>          xSemaphoreTake(sensor_semaphore, portMAX_DELAY);
>>          // Then release it again
>>          xSemaphoreGive(sensor_semaphore);
>>      }
>>      vTaskDelay(delay_ticks);    // DelayUntil
>> }
>>
>>
>> // TaskB
>> static void turn_off_sensor(void) {
>>      sensor_stopped = True;
>>      xSemaphoreTake(sensor_semaphore, 0);
>> }
>>
>> static void turn_on_sensor(void) {
>>      sensor_stopped = False;
>>      xSemaphoreGive(sensor_semaphore);
>> }
>>
>> The "sensor_stopped" flag is really just an optimisation, so that 
>> TaskA doesn't have to keep checking the semaphore.  It can be dropped 
>> if you want.
> 
> I'm not sure about xSemaphoreTake(sensor_semaphore, 0) in 
> turn_off_sensor(). You pass a zero timeout. Are you sure you will be 
> able to take the semaphore in *every* situation? What happens if 
> turn_off_sensor() is run immediately after xSemaphoreTake(..., 
> portMAX_DELAY) returns?
> In this case, I think taskB wouldn't be able to take the semaphore and 
> so taskA doesn't really block. You will have sensor_stopped true, but 
> the semaphore released.

David I hope you can answer to this. This topic is quite confusing to 
me. Thank you.

[toc] | [prev] | [next] | [standalone]


#29336

FromRichard Damon <Richard@Damon-Family.org>
Date2020-03-23 08:10 -0400
Message-ID<4H1eG.131641$xv4.107395@fx18.iad>
In reply to#29320
On 3/19/20 10:31 AM, pozz wrote:
> Il 19/03/2020 12:12, David Brown ha scritto:
> ...
>>>> It would be possible, but it does not look good to me.  I'd be
>>>> looking at an atomic flag (volatile bool, pre-C11) to indicate task
>>>> A should be shutting down after the current sample, then wait on a
>>>> semaphore. But I'd need a clearer picture of the whole system and
>>>> interaction between threads.
>>>
>>> This is what I thought, but I'm not able to implement it without any
>>> race conditions.
>>>
>>> // TaskA
>>> static volatile bool stopped;
>>> static volatile unsigned int delay_ms;
>>>
>>> stopped = false;
>>> while(1) {
>>>    // get sample and process
>>>    if (stopped) {                             (1)
>>>      vTaskDelay(portMAX_DELAY);               (2)
>>
>> This is a stop forever!
> 
> Yes, but in my example I used vTaskDelayAbort(), so it isn't really a
> stop forever.
> 
> 
>>>    } else {
>>>      vTaskDelay(pdMS_TO_TICKS(delay_ms));
>>>    }
>>> }
>>>
>>> void set_delay(unsigned int new_delay) {
>>>    // This is TaskB
>>>    if (new_delay == 0) {
>>>      stopped = true;
>>>    } else {
>>>      stopped = false;
>>>      delay_ms = new_delay;
>>>      vTaskDelayAbort(taskA);
>>>    }
>>> }
>>>
>>> This code has some race conditions if both tasks can be interrupted
>>> in any point by the other.
>>> For example, if stopped is true and taskB enters immediately after
>>> (1) and before (2) with set_delay(3), I will have taskA blocked
>>> forever!!
>>>
>>> Could you suggest a solution with a simple volatile bool flag?
>>>
>>
>> You use a flag to indicate that you should be stopping, and a
>> semaphore to handle the wakeup (semaphores are intended for one task
>> to give them, and another to take them).
>>
>> Stop messing around with varying delays - that is an irrelevancy, and
>> is complicating your code.  Either your sensor task is running with
>> regular samples, or it is stopped.
> 
> Sincerely taskB should change the sampling interval too. Anyway the big
> issue is stopping and restarting, not changing the interval.
> 
> 
>> One possible arrangement could be:
>>
>>
>> static volatile bool sensor_stopped;
>> static SemaphoreHandle_t sensor_semaphore;
>>
>> // TaskA
>> static const TickType_t delay_ticks = pdMS_TO_TICKS(10);
>>
>> while (true) {
>>      // Get sample and process
>>      if (sensor_stopped) {
>>          // Wait for semaphore to be ready
>>          xSemaphoreTake(sensor_semaphore, portMAX_DELAY);
>>          // Then release it again
>>          xSemaphoreGive(sensor_semaphore);
>>      }
>>      vTaskDelay(delay_ticks);    // DelayUntil
>> }
>>
>>
>> // TaskB
>> static void turn_off_sensor(void) {
>>      sensor_stopped = True;
>>      xSemaphoreTake(sensor_semaphore, 0);
>> }
>>
>> static void turn_on_sensor(void) {
>>      sensor_stopped = False;
>>      xSemaphoreGive(sensor_semaphore);
>> }
>>
>> The "sensor_stopped" flag is really just an optimisation, so that
>> TaskA doesn't have to keep checking the semaphore.  It can be dropped
>> if you want.
> 
> I'm not sure about xSemaphoreTake(sensor_semaphore, 0) in
> turn_off_sensor(). You pass a zero timeout. Are you sure you will be
> able to take the semaphore in *every* situation? What happens if
> turn_off_sensor() is run immediately after xSemaphoreTake(...,
> portMAX_DELAY) returns?
> In this case, I think taskB wouldn't be able to take the semaphore and
> so taskA doesn't really block. You will have sensor_stopped true, but
> the semaphore released.
> 
A Take with a zero timeout will lower the semaphore if it happens to be
raised, and continue if is was lowered. This will cause task A to go and
block on the Semaphore until task B gives it again. The take operation
returns a flag as to if it succeeded, and Task a should really use a:
  if(xSemaphoreTake(sensor_semaphore, portMAX_DELAY)) {
     xSemaphoreGive(sensor_semaphore);
  }

Otherwise if something causes the Take to abort, the the sampling will
start back up.

I would probably do the take before setting the flag, so if TaskA runs
between the statements it doesn't do its take, and pass.

> 
>> A really simple solution would be:
>>
>> static volatile bool sensor_stopped;
>>
>> // TaskA
>> static const TickType_t delay_ticks = pdMS_TO_TICKS(10);
>>
>> while (true) {
>>      if (!sensor_stopped) {
>>          // Get sample and process
>>      }
>>      vTaskDelay(delay_ticks);    // DelayUntil
>> }
>>
>>
>> // TaskB
>> static void turn_off_sensor(void) {
>>      sensor_stopped = True;
>> }
>>
>> static void turn_on_sensor(void) {
>>      sensor_stopped = False;
>> }
>>
>>
>> This all depends on why you want to stop the sensor task - if you are
>> going into low power modes, for example, then it may be best to block
>> the task.  If you don't mind a regular task doing nothing useful, then
>> the second choice is fine.
> 
> Yes, this is another solution that doesn't really stop the task, but
> only the sampling process.
> 
> 

[toc] | [prev] | [next] | [standalone]


#29337

Frompozz <pozzugno@gmail.com>
Date2020-03-23 13:47 +0100
Message-ID<r5ab4n$jag$1@dont-email.me>
In reply to#29336
Il 23/03/2020 13:10, Richard Damon ha scritto:
> On 3/19/20 10:31 AM, pozz wrote:
>> Il 19/03/2020 12:12, David Brown ha scritto:

>>> One possible arrangement could be:
>>>
>>>
>>> static volatile bool sensor_stopped;
>>> static SemaphoreHandle_t sensor_semaphore;
>>>
>>> // TaskA
>>> static const TickType_t delay_ticks = pdMS_TO_TICKS(10);
>>>
>>> while (true) {
>>>       // Get sample and process
>>>       if (sensor_stopped) {
>>>           // Wait for semaphore to be ready
>>>           xSemaphoreTake(sensor_semaphore, portMAX_DELAY);
>>>           // Then release it again
>>>           xSemaphoreGive(sensor_semaphore);
>>>       }
>>>       vTaskDelay(delay_ticks);    // DelayUntil
>>> }
>>>
>>>
>>> // TaskB
>>> static void turn_off_sensor(void) {
>>>       sensor_stopped = True;
>>>       xSemaphoreTake(sensor_semaphore, 0);
>>> }
>>>
>>> static void turn_on_sensor(void) {
>>>       sensor_stopped = False;
>>>       xSemaphoreGive(sensor_semaphore);
>>> }
>>>
>>> The "sensor_stopped" flag is really just an optimisation, so that
>>> TaskA doesn't have to keep checking the semaphore.  It can be dropped
>>> if you want.
>>
>> I'm not sure about xSemaphoreTake(sensor_semaphore, 0) in
>> turn_off_sensor(). You pass a zero timeout. Are you sure you will be
>> able to take the semaphore in *every* situation? What happens if
>> turn_off_sensor() is run immediately after xSemaphoreTake(...,
>> portMAX_DELAY) returns?
>> In this case, I think taskB wouldn't be able to take the semaphore and
>> so taskA doesn't really block. You will have sensor_stopped true, but
>> the semaphore released.
>>
> A Take with a zero timeout will lower the semaphore if it happens to be
> raised, and continue if is was lowered. This will cause task A to go and
> block on the Semaphore until task B gives it again. The take operation
> returns a flag as to if it succeeded, and Task a should really use a:
>    if(xSemaphoreTake(sensor_semaphore, portMAX_DELAY)) {
>       xSemaphoreGive(sensor_semaphore);
>    }

If portMAX_DELAY is used in xSemaphoreTake(), I think the function 
returns only if it was able to take the semaphore (if we don't use 
functions that abruptly let one task out of blocked state).

> Otherwise if something causes the Take to abort, the the sampling will
> start back up.
> 
> I would probably do the take before setting the flag, so if TaskA runs
> between the statements it doesn't do its take, and pass.

Anyway my objection is still there. If turn_off_sensor() is executed 
immediately after xSemaphoreTake(sensor_semaphore, portMAX_DELAY) 
returns, taskB wouldn't be able to take the semaphore, so taskA will 
wrongly continue doing its work.

I think we can use portMAX_DELAY in turn_off_sensor() too. In the worst 
case, taskB would wait for a very short time to take the semaphore, but 
usually it takes the semaphore immediately.

[toc] | [prev] | [next] | [standalone]


#29339

FromDavid Brown <david.brown@hesbynett.no>
Date2020-03-23 14:31 +0100
Message-ID<r5admv$9ho$1@dont-email.me>
In reply to#29337
On 23/03/2020 13:47, pozz wrote:
> Il 23/03/2020 13:10, Richard Damon ha scritto:
>> On 3/19/20 10:31 AM, pozz wrote:
>>> Il 19/03/2020 12:12, David Brown ha scritto:
> 
>>>> One possible arrangement could be:
>>>>
>>>>
>>>> static volatile bool sensor_stopped;
>>>> static SemaphoreHandle_t sensor_semaphore;
>>>>
>>>> // TaskA
>>>> static const TickType_t delay_ticks = pdMS_TO_TICKS(10);
>>>>
>>>> while (true) {
>>>>       // Get sample and process
>>>>       if (sensor_stopped) {
>>>>           // Wait for semaphore to be ready
>>>>           xSemaphoreTake(sensor_semaphore, portMAX_DELAY);
>>>>           // Then release it again
>>>>           xSemaphoreGive(sensor_semaphore);
>>>>       }
>>>>       vTaskDelay(delay_ticks);    // DelayUntil
>>>> }
>>>>
>>>>
>>>> // TaskB
>>>> static void turn_off_sensor(void) {
>>>>       sensor_stopped = True;
>>>>       xSemaphoreTake(sensor_semaphore, 0);
>>>> }
>>>>
>>>> static void turn_on_sensor(void) {
>>>>       sensor_stopped = False;
>>>>       xSemaphoreGive(sensor_semaphore);
>>>> }
>>>>
>>>> The "sensor_stopped" flag is really just an optimisation, so that
>>>> TaskA doesn't have to keep checking the semaphore.  It can be dropped
>>>> if you want.
>>>
>>> I'm not sure about xSemaphoreTake(sensor_semaphore, 0) in
>>> turn_off_sensor(). You pass a zero timeout. Are you sure you will be
>>> able to take the semaphore in *every* situation? What happens if
>>> turn_off_sensor() is run immediately after xSemaphoreTake(...,
>>> portMAX_DELAY) returns?
>>> In this case, I think taskB wouldn't be able to take the semaphore and
>>> so taskA doesn't really block. You will have sensor_stopped true, but
>>> the semaphore released.
>>>
>> A Take with a zero timeout will lower the semaphore if it happens to be
>> raised, and continue if is was lowered. This will cause task A to go and
>> block on the Semaphore until task B gives it again. The take operation
>> returns a flag as to if it succeeded, and Task a should really use a:
>>    if(xSemaphoreTake(sensor_semaphore, portMAX_DELAY)) {
>>       xSemaphoreGive(sensor_semaphore);
>>    }
> 
> If portMAX_DELAY is used in xSemaphoreTake(), I think the function 
> returns only if it was able to take the semaphore (if we don't use 
> functions that abruptly let one task out of blocked state).
> 
>> Otherwise if something causes the Take to abort, the the sampling will
>> start back up.
>>
>> I would probably do the take before setting the flag, so if TaskA runs
>> between the statements it doesn't do its take, and pass.
> 
> Anyway my objection is still there. If turn_off_sensor() is executed 
> immediately after xSemaphoreTake(sensor_semaphore, portMAX_DELAY) 
> returns, taskB wouldn't be able to take the semaphore, so taskA will 
> wrongly continue doing its work.

Yes, I think the code is not quite right.  I am not happy about it 
anyway - it doesn't feel good.  But as I said before, I think you would 
be better with a re-think of the design at a higher level.

> 
> I think we can use portMAX_DELAY in turn_off_sensor() too. In the worst 
> case, taskB would wait for a very short time to take the semaphore, but 
> usually it takes the semaphore immediately.
> 

That depends on the timing requirements, but could work.

[toc] | [prev] | [next] | [standalone]


#29340

FromRichard Damon <Richard@Damon-Family.org>
Date2020-03-23 13:50 -0400
Message-ID<BF6eG.18908$ZT6.17858@fx01.iad>
In reply to#29337
On 3/23/20 8:47 AM, pozz wrote:
> 
> Anyway my objection is still there. If turn_off_sensor() is executed
> immediately after xSemaphoreTake(sensor_semaphore, portMAX_DELAY)
> returns, taskB wouldn't be able to take the semaphore, so taskA will
> wrongly continue doing its work.

Yes, if TaskB has priority greater than or equal to TaskA, then it could
come in between the Take and the Give. One solution would be to make the
Give back conditional on not stopped, as well as the getting and
processing of the sample. Something like:

while (true) {
    if(sensor_stopped) {
        xSemaphoreTake(sensor_semaphore, portMAX_DELAY);
        if(!sensor_stopped) {
            xSemaphoreGive(sensor_semaphore);
        }
    } else {
       // Get sample and process
       vTaskDelay(delay_ticks;
    }
}

With this, if TaskB just sets the stopped flag, TaskA will take the
semaphore, not give it, then loop around and take it again and wait. If
TaskB does a 0 timeout take, it can prevent that extra loop.

If TaskA gets accidentally woken up, then it still doesn't give the
semaphore and blocks on the next loop.

[toc] | [prev] | [next] | [standalone]


#29311

FromNiklas Holsti <niklas.holsti@tidorum.invalid>
Date2020-03-19 12:27 +0200
Message-ID<hdgvpeF4ktdU1@mid.individual.net>
In reply to#29306
On 2020-03-19 8:40, pozz wrote:

    [snip long discussion]

> I simply wanted a management taskB to stop and restart sensor sampling 
> done by taskA. taskA uses vTaskDelay() for sampling at precise timings.

    [snip]

> After many years using cooperative schedulers, it appears to me 
> incredible how difficult, error prone and resource consuming is to 
> implement even simple mechanisms like this one with a preemptive scheduler.

It's not so hard once you absorb a set of high-level solution patterns 
and apply them consistently and with a high-level design approach.

I realize that this response may not be helpful, but here is how it can 
be done in Ada, as an example of a high-level approach.

The SW will have a subsystem that does "sampling", and you want some 
other "managing" subsystem to be able to set the sampling period and to 
start/stop sampling. The sampling subsystem interface (API) is then:

    package Sampling is
       procedure Set_Period (To : in Duration);
       procedure Start;
       procedure Stop;
    end Sampling;

That Ada text is put in its own file (by current convention named 
sampling.ads).

The body (implementation) of the sampling subsystem is put in its own 
file (conventionally named sampling.adb) and will start with

    package body Sampling is

The rest of this post will add the parts of the package body one by one.

The sampling package will have a concurrent task, called Sampler for 
example, and declared as:

    task Sampler;

The task body is shown later below.

The data to be communicated to the Sampler task will be the sampling 
period and whether sampling should be active. These shared data are 
placed in a "protected object" to ensure mutually exclusive access. The 
actions on these shared data are the following:

- Actions from the "manager" (exterior to the Sampling package):

    -- set the sampling period
    -- start sampling
    -- stop sampling.

- Actions from the Sampler task:

    -- get the current sampling period-

Moreover, the Sampler task should be suspended when sampling is stopped, 
which is convenient to implement with an Ada "entry" operation that 
suspends the caller if its "guard" expression is false. The declaration 
of the protected object is then:

    protected Sampling_Control is
       procedure Set_Period (To : in Duration);
       procedure Start;
       procedure Stop;
       entry Get_Period (Giving : out Duration);
    private
       Running : Boolean := False;
       Period : Duration := 1.0;
    end Sampling_Control;

The shared but protected data are thus the Running flag, which will be 
the guard for the Get_Period entry, and the current sampling Period.
Note that sampling is initially stopped (Running is initially False) and 
the initial sampling period is one second.

The implementation is rather trivial:

    protected body Sampling_Control is

       procedure Set_Period (To : in Duration) is
       begin
          Period := To;
       end Set_Period;

       procedure Start is
       begin
          Running := True;
       end Start;

       procedure Stop is
       begin
          Running := False;
       end Stop;

       entry Get_Period (Giving : out Duration)
       when Running
       is
       begin
          Giving := Period;
       end Get_Period;

    end Sampling_Control;

Note the guard "when Running" on the Get_Period entry. This means that a 
task that calls Get_Period when Running is False is suspended, at the 
point of the call, until Running becomes True. If Running is True, the 
call succeeds at once.

Note that Set_Period does not automatically start sampling. If that is 
desired, add the statement "Running := True" in Set_Period.

The Sampler task body can now be implemented simply:

    task body Sampler is
       Period : Duration;
    begin
       loop
          Sampling_Control.Get_Period (Giving => Period);
          -- Take the sample, etc.
          delay Period;
        end loop;
    end Sampler;

(This code is a bit sloppy, because the use of a "relative" delay 
statement brings lag and jitter into sample timing. It is better to use 
the "absolute" delay statement ("delay until"), but this complication is 
not relevant to the task interactions so I skipped it.)

The public (API) operations of the Sampling package just call the 
corresponding operations of Sampling_Control (this circumlocation can of 
course be avoided by making Sampling_Control itself public, but that 
would harm encapsulation and increase inter-module coupling):

    procedure Set_Period (To : in Duration) is
    begin
       Sampling_Control.Set_Period (To);
    end Set_Period;

    procedure Start is
    begin
       Sampling_Control.Start;
    end Start;

    procedure Stop is
    begin
       Sampling_Control.Stop;
    end Stop;

The implementation of the Sampling subsystem is now complete, and the 
file sampling.adb ends with:

    end Sampling;

The "manager" part of the SW accesses the Sampling subsystem by saying

    with Sampling;

and can then use the Sampling interface, for example as

    Sampling.Set_Period (To => 0.1);  -- 10 Hz
    Sampling.Start;
    ...
    Sampling.Set_Period (To => 2.5);
    ...
    Sampling.Stop;

and so on.

To map this Ada code into some C+RTOS combination, one should use a 
mutex to make the executions of the protected operations 
(Sampling_Control.Set_Period, .Start, .Stop, .Get_Period) mutually 
exclusive, and use some "condition" variable or semaphore to implement 
the guard (caller suspension) of Sampling_Control.Get_Period.

That last point (caller suspension) has some subtleties that depend on 
the actual set of RTOS primitives available. But I recommend a simpler 
solution: instead of suspending/resuming the sampling task, just let it 
run normally at the sampling period, but make the actual sampling (and 
sample processing, I assume) conditional on a "Running" flag (by 
returning this flag, too, from Get_Period). Usually the processor effort 
saved by actually suspending tasks that normally run periodically is not 
significant.

-- 
Niklas Holsti
Tidorum Ltd
niklas holsti tidorum fi
       .      @       .

[toc] | [prev] | [next] | [standalone]


#29312

Frompozz <pozzugno@gmail.com>
Date2020-03-19 11:45 +0100
Message-ID<r4vigh$k06$1@dont-email.me>
In reply to#29311
Il 19/03/2020 11:27, Niklas Holsti ha scritto:
> On 2020-03-19 8:40, pozz wrote:
> 
>     [snip long discussion]
> 
>> I simply wanted a management taskB to stop and restart sensor sampling 
>> done by taskA. taskA uses vTaskDelay() for sampling at precise timings.
> 
>     [snip]
> 
>> After many years using cooperative schedulers, it appears to me 
>> incredible how difficult, error prone and resource consuming is to 
>> implement even simple mechanisms like this one with a preemptive 
>> scheduler.
> 
> It's not so hard once you absorb a set of high-level solution patterns 
> and apply them consistently and with a high-level design approach.

Ok, where do I study those "high-level solution patterns"? Because I 
didn't find good docs that explain non-trivial real examples.


> I realize that this response may not be helpful, but here is how it can 
> be done in Ada, as an example of a high-level approach.
 > [...]

Unfortunatley I don't know Ada.

> To map this Ada code into some C+RTOS combination, one should use a 
> mutex to make the executions of the protected operations 
> (Sampling_Control.Set_Period, .Start, .Stop, .Get_Period) mutually 
> exclusive, and use some "condition" variable or semaphore to implement 
> the guard (caller suspension) of Sampling_Control.Get_Period.
> 
> That last point (caller suspension) has some subtleties that depend on 
> the actual set of RTOS primitives available. But I recommend a simpler 
> solution: instead of suspending/resuming the sampling task, just let it 
> run normally at the sampling period, but make the actual sampling (and 
> sample processing, I assume) conditional on a "Running" flag (by 
> returning this flag, too, from Get_Period). Usually the processor effort 
> saved by actually suspending tasks that normally run periodically is not 
> significant.

Yes, I didn't imagine that controlling a task from another task was so 
difficult with a full-features RTOS like FreeRTOS.
So your suggestion to let the sampling task always run even when it 
should be suspended is good. It's a pity, because I have many sensors, 
but only a few of them are active at any time.

[toc] | [prev] | [next] | [standalone]


#29315

Fromupsidedown@downunder.com
Date2020-03-19 13:27 +0200
Message-ID<ltk67fp1jsimrd0k14hcnjcphkqad5b9ae@4ax.com>
In reply to#29312
On Thu, 19 Mar 2020 11:45:37 +0100, pozz <pozzugno@gmail.com> wrote:

>
>Unfortunatley I don't know Ada.
>
>> To map this Ada code into some C+RTOS combination, one should use a 
>> mutex to make the executions of the protected operations 
>> (Sampling_Control.Set_Period, .Start, .Stop, .Get_Period) mutually 
>> exclusive, and use some "condition" variable or semaphore to implement 
>> the guard (caller suspension) of Sampling_Control.Get_Period.
>> 
>> That last point (caller suspension) has some subtleties that depend on 
>> the actual set of RTOS primitives available. But I recommend a simpler 
>> solution: instead of suspending/resuming the sampling task, just let it 
>> run normally at the sampling period, but make the actual sampling (and 
>> sample processing, I assume) conditional on a "Running" flag (by 
>> returning this flag, too, from Get_Period). Usually the processor effort 
>> saved by actually suspending tasks that normally run periodically is not 
>> significant.
>
>Yes, I didn't imagine that controlling a task from another task was so 
>difficult with a full-features RTOS like FreeRTOS.
>So your suggestion to let the sampling task always run even when it 
>should be suspended is good. It's a pity, because I have many sensors, 
>but only a few of them are active at any time.

Why don't you simply make each sampling task like

loop
    wait_for_event()
   do_the_sampling
end loop

Then have a separate timing task, which runs at say every millisecond
and increment a single integer counter.

For each task, calculate (Counter modulo NN) and send a message to a
specific sampler task, when modulo is zero. NN is the number of clock
ticks before a specific sampling is done.

If you want to do sampling say every 10 ms, but want to spread out the
sampling times, add a constant 1, 2, 3 to a copy of the counter before
performing the module and one task will sample at xx7, the other at
xx8 and third at xx9 milliseconds every 10 ms.
 
This way, you can control the sampling times and periods from a single
point in the timing task and no need to spread out this to individual
sampler tasks.
 

[toc] | [prev] | [next] | [standalone]


#29301

Fromantispam@math.uni.wroc.pl
Date2020-03-18 16:24 +0000
Message-ID<r4ti0d$h14$1@z-news.wcss.wroc.pl>
In reply to#29297
pozz <pozzugno@gmail.com> wrote:
> 
> Ok the example was extremely simple. If I have something a little more 
> complex:
> 
> ---
> volatile uint32_t wait_period;
> 
> while(1) {
>   // make some things
>   if (wait_period > 0) {
>     vTaskDelay(pdMS_TO_TICKS(wait_period));
>   } else {
>     vTaskSuspend(myTask);
>   }
> }
> 
> void set_waiting_period(uint32_t new_period) {
>   wait_period = new_period;
>   if (wait_period > 0) {
>     vTaskResume(myTask);
>   }
> }
> ---
> 
> I think this doesn't work in all the cases. For example, if 
> wait_period=0 and set_waiting_period(3) is executed from another higher 
> priority task immediately after
> 
>   if(wait_period > 0)   // the if in the endless loop
> 
> wait_period is changed to 3, but vTaskSuspended() is executed in the 
> else branch, so leaving the task suspended with a not-null wait_period.

To me the above looks like very natural anti-pattern.  Namely,
code testing 'wait_period' _and_ execution of 'vTaskSuspend'
logically form a single ciritical section.  AFAICS any placement
of locks above leads to races.

My solution to the above is have _whole thing_ in scheduler,
so that testing flags and switching process is atomic.
In your case messages look like step to solution, but
you still need to think about messaging protocol...

-- 
                              Waldek Hebisch

[toc] | [prev] | [standalone]


Page 3 of 3 — ← Prev page 1 2 [3]

Back to top | Article view | comp.arch.embedded


csiph-web