Rasberry PI PICO (RP2040) продолжение темы

На одном ядре - DOS, на втором супервайзер. :slight_smile:

суперкластер ага)

тогда уж вин95

начать с win 3.1

если по честному тогда уж с win 2.0

Вопрос к поднаторевшим: Корректно ли использовать в качестве семафора использовать булеву volatile переменную в многопоточной обработке во избежания коллизий при доступе к переменной. Пример в коде:

/// Осциллоскоп ¯\_(ツ)_/¯ 
#include <GyverOLED.h>

GyverOLED<SSH1106_128x64> oled;

volatile uint16_t sensorValue;
volatile bool fBuzy = false;

void setup() {
  Serial.begin(115200);
}

void loop() {
  uint16_t tempVal;
  tempVal = analogRead( A0);
  fBuzy^=true;  // установить флаг занятости
  sensorValue = tempVal;
  fBuzy^=true;  // сбросить флаг занятости
  Serial.println(sensorValue);
  delay(1);
}

void setup1() {
  oled.init();
  Wire.setClock( 1200000L);
}

void loop1() {
  uint16_t tempVal;
  uint16_t prevVal;
  
  oled.clear();
  prevVal = 0;
  for (byte x = 0; x < 128; x++) {
    while ( fBuzy);
    tempVal = sensorValue;
    tempVal = (tempVal - 200)<<1;
    oled.line( x, prevVal, x+1, tempVal, x?1:0);
    //oled.dot( x, tempVal, 1);
    prevVal = tempVal;
    delay(1);
  }  
  oled.update();
}

А переводить volatile не пробовал перед тем как задать вопрос вопрос?

Идея состояла в использовании не оптимизированной булевой переменной в качестве семафора с изменением её атомарной операцией XOR…
В общем то, работать это будет, если судить по обсуждению на c++ - When to use volatile with multi threading? - Stack Overflow. Но не рекомендуется…

Кем?

bool, сдается мне, тут излишен. Может из-за этого и не работает :0)

С++ сгенерирует 2 переменные, setSDA = false и setSCL = true. Это то, что нужно?

Проблема вот какая: цикл работы с переменной обычно выглядит так:

  1. добыть переменную из памяти в регистр
  2. совершить ту или иную операцию (например ++)
  3. сохранить новое значение в память.

Если у тебя многопоточность, то будет происходить следующее:
Thread1: достал переменную, прибавил единицу, и…
Случился Context Switch
Thread2: достал переменную, прибавил единицу, сохранил, и…
Случился Context Switch
Thread1 сохранил свой вариант.

Как видно, один из инкрементов потеряется. Дела будут еще хуже на многоядерной системе: два ядра одновременно будут писать, например, в 32-битную переменную, и там будет совсем мусор.

ЗЫЖ
Посмотрел повнмательнее: твой вариант пойдет. У тебя только одна задача пишет в переменную.

Есть такие хрени, как семафор, ATOMIC_BLOCK и еще одна, на LOCK… начинается, забыл.

Upd. CRITICAL_SECTION

Это все в полноценных ОС есть, а тут все ручками надо придумывать на PICO.
Хотя давно это было, но вроде в PICO в даташите что то было про корректную работу в многпоточности :thinking:

Разве FreeRTOS на него не портирован?

Без понятия. Для меня Free RTOS это какашка. Впрочем дело вкуса.

1 лайк

Тащемта мы об \том и говорим.. Вопрос был - “можно ли использовать volatile для того, чтобы работало, как семафор.”

Короткий ответ: в общем случае -нет.

Частный случай, как в посте автора - ответ: да, скорее всего это сработает.

spinlock.

на его основе, в принципе, все остальные примитивы (семафоры, мутексы) и строятся.

PS: не знаю насчет Пико, но думаю,что там все с этим хорошо..
С этим - atomic_compare_xchange.

В Интеле есть отдельная ассемблерная инструкция cmpxchg. Очень удобно делать всякие синхронизационные примитивы.

А в ESP32 этого нет.
Может быть плохо искал.
Но там, честно говоря, чорт ногу сломит: более отвратительного даташита, чем Xtensa ISA я в жизни не видывал. Это такая эзотерика,что атас.

можно стырить из RTEMS или FreeRTOS. Мне первая как-то ближе. Ну или из linux/arch/какой-там-процессор-в-пико

почитайте как работает например семафоры или распределенная память :slightly_smiling_face:
обязательно почитать еще стетейку как это все использовать в Linux своих программах на Си.

и подумайте можно ли это перетащить в Pico…

во нашел в документации PICO, наверное проще программе точно узнавать на каком ядре она конкретно сейчас работает и уже логику написать для корректного обмена с общими переменными или областью памяти.

Спойлер
 
 
#include <stdio.h>

 
2 #include "pico/stdlib.h"
 3 #include "pico/multicore.h"

 
4 

 
5 #define FLAG_VALUE 123

 
6 

 
7 void core1_entry() {
 8 

 
9 multicore_fifo_push_blocking(FLAG_VALUE);
10 
11 uint32_t g = multicore_fifo_pop_blocking();
12 
13 if (g != FLAG_VALUE)
14 printf("Hmm, that's not right on core 1!\n");
15 else
16 printf("Its all gone well on core 1!");
17 
18 while (1)
19 tight_loop_contents();
20 }
21 
22 int main() {
23 stdio_init_all();
24 printf("Hello, multicore!\n");
25 
26 
27 multicore_launch_core1(core1_entry);
28 
29 // Wait for it to start up
30 
31 uint32_t g = multicore_fifo_pop_blocking();
32 
33 if (g != FLAG_VALUE)
34 printf("Hmm, that's not right on core 0!\n");
35 else {
36 multicore_fifo_push_blocking(FLAG_VALUE);
37 printf("It's all gone well on core 0!");
38 }
39 
40 }

Там-то все просто: pthreads, POSIX стандарт.