|
Атомарная работа с данными в Cortex-M3 |
|
|
|
Aug 8 2013, 18:17
|
Участник

Группа: Участник
Сообщений: 23
Регистрация: 3-05-13
Пользователь №: 76 718

|
Привет! Почитал разные материалы про атомарные операции, никак не могу прийти к выводу в чем опасность чтения? При модификации все понятно - произошло прерывание или переключение задач в ОСРВ, которое вклинилось между инструкциями, данные испортятся. А вот при чтении? Возьмем функцию на Cortex-M3: long long a; void store(void) { a=0xDDCCFFEE12345678; } Ассемблерный код после компиляции такой: 0x00001314 493A LDR r1,[pc,#232] ; @0x00001400 0x00001316 483B LDR r0,[pc,#236] ; @0x00001404 0x00001318 4A3B LDR r2,[pc,#236] ; @0x00001408 0x0000131A E9C21000 STRD r1,r0,[r2,#0] Если я располагаю этот код в одной задаче ОСРВ, а читаю допустим в другой, то ведь никаких проблем не будет т.к. переменная a меняется по последней инструкции? В чем подвох?
|
|
|
|
|
 |
Ответов
(1 - 9)
|
Aug 9 2013, 03:36
|
Участник

Группа: Участник
Сообщений: 23
Регистрация: 3-05-13
Пользователь №: 76 718

|
Понятно, спасибо. А каким образом избегать ошибок неатомарного доступа при работе с ОСРВ на архитектуре Cortex-M3, какие механизмы ОСРВ использовать? Допустим, у меня есть задача ОСРВ, которая вычитывает данные из АЦП. Далее эти данные как-то обрабатываются (по неким формулам) и затем доступ к этим данным (чтение) должны получить другие задачи ОСРВ. Как это лучше организовать? При этом, программируя на Си, получается неэффективно проверять все генерируемые компилятором ассемблерные инструкции, есть какой-то универсальный способ, чтобы не лазить в ассемблерный код?
|
|
|
|
|
Aug 12 2013, 07:07
|
Участник

Группа: Участник
Сообщений: 23
Регистрация: 3-05-13
Пользователь №: 76 718

|
Хорошо. Мне просто интересен опыт как делаете вы в своих проектах, если в проекте используется RTOS. Вот у меня проект средней сложности (не знаю как описать) - как думаю будет несколько задач RTOS (7-8), принимаемые данные от АЦП (всего их 3) имеет смысл разделить и обрабатывать в разных задачах, часть из них независимые часть будет пересекаться. Как бы вы сделали? 1. С маилбоксами все хорошо, можно не плодить глобальные переменные, проект красиво будет выглядеть. 2. С глобальными переменными вроде контроллеру посвободнее будет.
|
|
|
|
|
Aug 12 2013, 08:28
|
Знающий
   
Группа: Участник
Сообщений: 837
Регистрация: 8-02-07
Пользователь №: 25 163

|
Я бы, для работы с АЦП, выделил отдельную задачу. Результаты бы складывал в локальный буфер: static adc_measure_t adc_results[ADC_CHANNELS]. Для обращения к полученным значениям ADC сделал бы функцию геттер: adc_task_get_adc_value(int channel). Внутри этой функции я бы делал блокировку, копирование результата в локальную переменную (если много, то memcpy) и разблокировку: Код lock(mutex) int result = adc_results[channel]; unlock(mutex) return result; Да, задача ADC Task при складировании результатов от ацп тоже должна выполнять блокировку-разблокировку.
|
|
|
|
|
Aug 13 2013, 12:25
|
Участник

Группа: Участник
Сообщений: 23
Регистрация: 3-05-13
Пользователь №: 76 718

|
andrewlekar, не совсем понял вашу идею. Результат АЦП обычно копируется в буфер прямо в прерывании, какой тогда смысл в вышеприведенной блокировке внутри задачи?
static int adc_buf[2];/* статический массив в модуле */ void ADC_isr(void) { ... adc_buf[0] = ADC_REG0; adc_buf[1] = ADC_REG1; ... }
а далее уже, наверное, можно сделать эту функцию-геттер (глобальную), которая будет возвращать результат в задаче: int ADC_get_result(unsigned char channel) { int result;
result = adc_buf[channel]; return result; } Это я к тому что в общем случае ни ваш вариант ни мой не защищают изменение массива adc_buf в прерывании, когда какая-либо задача уже начала его читать, а атомарность обеспечивается лишь 32-битной архитектурой и соответствующими ассеблерными инструкциями. В общем случае: int ADC_get_result(unsigned char channel) { int result;
disable_interrupt(); result = adc_buf[channel]; enable_interrupt(); return result; } А если делать через мьютексы, то это нас не защитит т.к. на прерывания они не распространяются. Или я ошибаюсь?
Сообщение отредактировал Rev0.0 - Aug 13 2013, 12:26
|
|
|
|
|
Aug 13 2013, 14:27
|
Местный
  
Группа: Участник
Сообщений: 421
Регистрация: 2-01-08
Пользователь №: 33 778

|
Есть lock-free способы. Например как-то так, для одного писателя и одного читателя, Код struct data d[3], *w, *r;
writer () { struct data *p;
for (i = 0; i < 3; ++i) { p = d + i; if (p != r && p != w) break; }
p->adc1 = ADC1; p->adc2 = ADC2; ...
w = p; }
read () { r = w;
return r; }
r = read ();
foo(r->adc1); bar(r->adc2); ... Но, - Нужно время на отладку своей реализации, надо не забывать о соблюдении порядка доступа к памяти, как на уровне генерации кода так и при работе конвеера вычислителя - Надежность блокировок может быть важнее производительности - Неизвестно ещё, что будет быстрее
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|