реклама на сайте
подробности

 
 
> Вариант реализации атомарного счетчика, Укажите на проблемы, ежели таковые есть
turnon
сообщение Aug 30 2015, 09:58
Сообщение #1


Местный
***

Группа: Свой
Сообщений: 340
Регистрация: 17-10-14
Пользователь №: 83 207



Нужен счетчик количества микросекунд, прошедших с момента старта МК (STM32).

Насколько мне известно, переменные длинее байта обновляются не за одну команду МК. И надо чтобы в момент обновления была уверенность, что переменную в это время не читают, потому как она скорее всего будет иметь некорректное значение (1 байт обновился, второй еще и нет и т.д.)

Вот набросал такой счетчик, для проверки атомарности используется флаг _busy. Имеет право на жизнь такая конструкция или чего-то не учел?

Код
class AtomicUint64
{
  private:
    volatile uint8_t _busy;
    volatile uint64_t _counterBackup;
    volatile uint64_t _counterValue;
  public:
    AtomicUint64(){
      _busy = 0;
      _counterBackup = 0;
      _counterValue = 0;
    }
    
    inline void inc(uint8_t value = 1){
      _counterBackup = _counterValue + value;
      _busy = 1;
      _counterValue += value;
      _busy = 0;
    }
    
    inline uint64_t get(){
      if (_busy)
        return _counterBackup;
      else    
        return _counterValue;
    }
};

extern AtomicUint64 gTick1MsecCounter;


Как используется. В прерывании 1 мsec вызывается gTick1MsecCounter.inc(), в остальном коде где нужно знать текущее значение счетчика - gTick1MsecCounter.get()

Сообщение отредактировал turnon - Aug 30 2015, 10:11
Go to the top of the page
 
+Quote Post
 
Start new topic
Ответов
menzoda
сообщение Aug 30 2015, 10:20
Сообщение #2


Участник
*

Группа: Участник
Сообщений: 55
Регистрация: 13-09-12
Пользователь №: 73 530



Цитата
Насколько мне известно, переменные длинее байта обновляются не за одну команду МК.

Нет, зависит от архитектуры.

Цитата
Имеет право на жизнь такая конструкция или чего-то не учел?

Не учел. Представь такую ситуацию: функция get вызывается из фона, проверяется флаг busy. Так как он равен нулю, то осуществляется переход к return. Чтобы возвратить 64-битное значение нужно скопировать его из памяти в регистры R0 и R1. Копирование происходит по 32 бита. Одна часть скопировалась. Тут происходит прерывание в котором вызывается inc, которая увеличивает значение счетчика. Прерывание завершается и управление возвращается get, которая копирует оставшиеся 32 бита, потенциально изменившиеся в прерывании. В итоге получается хрень.

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

Код
static uint64_t counter;

void inc(uint32_t value)
{
    __disable_interrupt();
    counter += value;
    __enable_interrupt();
}

uint64_t get(void)
{
    __disable_interrupt();
    uint64_t copy = counter;
    __enable_interrupt();  

    return copy;
}
Go to the top of the page
 
+Quote Post



Reply to this topicStart new topic
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 29th July 2025 - 04:30
Рейтинг@Mail.ru


Страница сгенерированна за 0.01303 секунд с 7
ELECTRONIX ©2004-2016