Обсуждаем контроллеры компании Atmel.
Ответить

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 08:45:40

BOB51 писал(а):Только для топикстартера похоже смысл так и остался непонятным...

Тут много было всего сказано. :) Смысл чего? Если смысл понятия "флаг", то это вполне мне понятно. Принцип-то не сложен, в зависимости от события сигнализирует какой-то части программы: можно - нельзя, есть - нет, произошло - не произошло и т.д. И что флагом может быть все подряд ))) Главное чтобы однозначно сигнализировало о наступившем событии.
Если относительно прерываний, потери информации и как не попасть в просак. То здесь я еще не вполне понимаю как правильно с этим бороться. Толи флаги ставить, то ли запрет прерываний делать, то ли флаги на флаги ставить, чтобы первые флаги писать и в прерывании и в основном цикле, без опасений :))) То ли стараться вообще не использовать прерывания и делать в основном цикле что-то похожее на псевдомногопоточность. ))

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 08:54:12

флаги - это не переменные. флаги для того и нужны, чтобы в одном мете поставить, а в другом проверить и если поставлен, выполнить действия и сбросить флаг.

Отчего же. Переменная, пусть однобитная и объединенная с другими. И под условие задачи попадает.
Как бы то ни было, даже если флаги не переменные - как их реализовать-то?
Именно так работают неправильно написанные кольцевые буферы.

Не сработает. Для кольцевого буфера нужно две переменных - голова и хвост. Прерывание имеет право читать голову и писать хвост, основной цикл наоборот. Конфликта записи не возникает. Более того, размер буфера в 8-битках почти никогда не превышает 8 бит, так что даже прерывания можно не блокировать, доступ останется атомарным.
для меня в МК вообще не существует переменных - я пишу на ассемблере и у меня есть только регистры и ячейки памяти ОЗУ.

Если под данные отводится больше одной ячейки, такой подход только все усложняет.
Но опять-таки, какая разница как это назвать. Допустим, нам нужно контролировать что прошла секунда (счет в прерывании TIM0), что передана вся строка по UART и что напряжение АЦП выше порога. Из соображений экономии вы отвели под это дело биты 0,1,2 ячейки 0x0084.

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 09:26:21

COKPOWEHEU писал(а):Из соображений экономии вы отвели под это дело биты 0,1,2 ячейки 0x0084.
из соображений экономии сишному компилятору как-то больше нравятся целые переменные в качестве флагов, но иногда и битовые поля
Последний раз редактировалось slav0n Чт окт 07, 2021 09:38:29, всего редактировалось 1 раз.

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 09:30:53

slav0n писал(а):из соображений экономии сишному компилятору нравятся
Да компилятору с высокой горки на...., все зависит от программиста.

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 09:44:14

COKPOWEHEU писал(а):Если под данные отводится больше одной ячейки, такой подход только все усложняет.
ни чуть не усложняет.
COKPOWEHEU писал(а):Но опять-таки, какая разница как это назвать.
совершенно верно сказано. лично мне нравится называть "параметр", и неважно, сколько ячеек он занимает.
например, у меня
параметр "время" - 3 ячейки памяти, часы, минуты и секунды,
параметр "емкость" (аккумулятора) - 3 ячейки памяти.
прочие параметры у меня все двухбайтные (2 ячейки), независимо от максимального значения параметра - для простоты доступа к ним.
а на флаги я выделяю регистры, а не ячейки памяти. в одном моем проекте столько флагов, что пришлось выделить 3 регистра.
выделять регистр под флаги очень удобно, так как есть команды работы с битами регистра.

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 09:52:44

Не сработает. Для кольцевого буфера нужно две переменных - голова и хвост. Прерывание имеет право читать голову и писать хвост, основной цикл наоборот. Конфликта записи не возникает. Более того, размер буфера в 8-битках почти никогда не превышает 8 бит, так что даже прерывания можно не блокировать, доступ останется атомарным.

У меня так и сделано, но тут нет записи переменной в основном коде и прерывании, а если добавлять данные в буфер могут несколько потоков, что типично для любой RTOS, где помимо этого хватает всяких мьютексов, или если размер сохраняется в отдельной переменной, что опять же типично, потому что значительная часть реализаций именно так и работает, то придется озаботиться атомарным доступом.

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 09:59:13

а зачем запрещать прерывания на запись переменной, разрядность которой совпадает с разрядностью шины?

В начале темы я дал ссылку на статью, там описаны причины.

Я вроде написал по-русски: ЗАПИСЬ. В статье совершенно про другое.

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 10:16:19

...
Если относительно прерываний, потери информации и как не попасть в просак. То здесь я еще не вполне понимаю как правильно с этим бороться. Толи флаги ставить, то ли запрет прерываний делать, то ли флаги на флаги ставить, чтобы первые флаги писать и в прерывании и в основном цикле, без опасений :))) То ли стараться вообще не использовать прерывания и делать в основном цикле что-то похожее на псевдомногопоточность. ))

Просто добавить в соответствующие места программ анализ флагов и соответствующую результатам анализа обработку данных.
Для основной программы:
Нет запроса - пробегаем мимо, есть что-то новое - исполняем (перемещая данные из промежуточного буфера в основной) и сбрасываем запрос.
Для прерывания -
контроль предыдущего запроса - если не обработан - пробегаем мимо
если обработан - загружаем промежуточный буфер и выстваляем запрос обработки
8)

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 10:27:30

parovoZZ, статью я понял так: если писать биты (значение) в переменную в основном цикле и также в прерывании, то может произойти потеря данных при ЗАПИСИ битов в прерывании. Автор статьи предлагает использовать запрет прерываний (атомарная операция) при записи переменной (флага) в основном цикле.

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 11:05:01

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

Смотря на что оптимизировать - на скорость или на память. Впрочем, по скорости выигрыша тоже нет:
Код:
lds r16, FLAGS_REG
sbrs r16, FLAG_TIM
  rjmp TIM_CLEARED
cbr r16, FLAG_TIM
sts FLAGS_REG, r16
;флаг был поднят, обрабатываем
TIM_CLEARED: ;возвращаемся к выполнению

Код:
lds r16, TIM_FLAG
tst r16
  brne TIM_CLEARED
clr r16
sts TIM_FLAG, r16
;флаг был поднят, обрабатываем
TIM_CLEARED: ;возвращаемся к выполнению

ни чуть не усложняет.

Вот есть значение таймера на 32 бита, есть АЦП на 16 бит, есть даже какой-то коэффициент, пришедший снаружи в формате IEE754 (float). Каждый байт в отдельности не значит ничего, значение имеют группы по 4, 2 и снова 4 байта. Еще хуже когда все это упаковано в массив структур.
а на флаги я выделяю регистры

Можно и так, но тогда за ними надо следить чтобы разная периферия не пыталась занять одни и те же адреса. Тем более что флаги подразумевают, что быстрой реакции не нужно, и обойтись ячейкой вполне можно. Ну там одну под события UART, вторую под SPI, третью под I2C (там одной может и не хватить).
для любой RTOS, где помимо этого хватает всяких мьютексов

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

Хранить размер буфера в переменной? А какой смысл? Я очень надеюсь, что он у вас хотя бы не в динамической памяти находится...
Или вы про количество данных в буфере? Так оно вычисляется из разности между головой и хвостом за два действия.

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 11:09:36

BOB51, Вы, собственно, об этом в начале и говорили. И идея меня зацепила! Но обсуждения в теме как-то в сторону увели )) Спасибо! :) В общем что я из этой темы для себя вынес:
1. Писать в одну и туже переменную в основном цикле и в прерывании неправильно (неграмотно).
2. Пишем только через буфер используя флаги (семафоры, мьютексы и т.п.). Причем в буфере можно выстроить очередь запросов (данных) от прерывания. Таким образом мы и код не будем блокировать от прерываний и все прерывания будут гарантированно обработаны в их первоначальном порядке (количестве). Все правильно?

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 11:53:03

Вобщем верно.
Таким способом делается "квазиавтономная" динамическая индикация - для обслуживания развертки используется таймер и буфер видеопамяти. Это система крутится сама по себе на основе прерываний по таймеру (в фоне основной программы), а в основной программе второй буфер данных и флаг запроса перезаписи.
Как толькоданные готовы - флаг для "контроллера дисплея", а тот уже дождавшись нужного момента перебрасывает данные в видеопамять и затем сбрасывает флаг запроса.
8)

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 11:57:17

COKPOWEHEU писал(а):Можно и так, но тогда за ними надо следить чтобы разная периферия не пыталась занять одни и те же адреса.
а как ты себе представляешь, писать на ассемблере и не следить за использованием регистров?
скажу даже больше - когда пишешь на ассемблере, следить нужно не только за регистрами флагов, следить нужно за всеми регистрами.

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 12:03:28

Dimon456 писал(а):все зависит от программиста.
правильно
лично я пишу прямо в теле функции:
static u8 flg;
и не парюсь

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 13:11:18

1. Писать в одну и туже переменную в основном цикле и в прерывании неправильно (неграмотно).
2. Пишем только через буфер используя флаги (семафоры, мьютексы и т.п.). Причем в буфере можно выстроить очередь запросов (данных) от прерывания. Таким образом мы и код не будем блокировать от прерываний и все прерывания будут гарантированно обработаны в их первоначальном порядке (количестве). Все правильно?

Усложнять простые вещи - это, конечно, признак высшего класса.)

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 13:22:06

а как ты себе представляешь, писать на ассемблере и не следить за использованием регистров?
скажу даже больше - когда пишешь на ассемблере, следить нужно не только за регистрами флагов, следить нужно за всеми регистрами.

Для кого конвенции придумали?..
Например, http://ww1.microchip.com/downloads/en/a ... c42055.pdf рекомендуют использовать r0 как временный, r1 как нулевой, r2-r17, r28, r29 как сохраняемые (при вызове функций они не меняются), r0, r18-r27, r31 как временные (подпрограмма имеет право делать с ними все что угодно). Вот и все, регистры предназначены для операций, а не для хранения данных.
Разумеется, никто не мешает часть регистров зарезервировать под свои нужды. Для тех же флагов, например.

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 14:33:16

Ну, мбютексы ведь тоже как-то реализованы, и их реализация тоже попадает под наше обсуждение.

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

COKPOWEHEU писал(а):Хранить размер буфера в переменной? А какой смысл? Я очень надеюсь, что он у вас хотя бы не в динамической памяти находится...
Или вы про количество данных в буфере? Так оно вычисляется из разности между головой и хвостом за два действия.

Емкость - константа, количество данных вычисляется как разность между головой и хвостом, но только если реализация именно такова, а может быть голова и размер или голова, хвост и размер, причем такие реализации можно легко найти в книгах непосредственно по эмбедду. И простой разностью можно обойтись только если емкость буфера равна степени двойки, плюс подход только с головой и хвостом чреват подводными камнями... Классический пример когда некоторые компиляторы сначала инкрементят и сохраняют tail, а потом уже пишут в buf:
Код:
buf[tail++ & mask] = value;

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 14:35:01

OKF, предложите свой вариант :)

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 14:52:32

И простой разностью можно обойтись только если емкость буфера равна степени двойки
Если не равняется, то вместо наложения маски будет сравнение и вычитание/сложение, вот и все.
а может быть голова и размер или голова, хвост и размер
Пока что я вижу только явный недостаток с тем, что к полю размера нужен доступ и при записи, и при чтении. Но вы говорите, что такие реализации есть. Чем именно они настолько хороши, что перевешивают этот недостаток?
плюс подход только с головой и хвостом чреват подводными камнями... Классический пример когда некоторые компиляторы сначала инкрементят и сохраняют tail, а потом уже пишут в buf
Ну так вариант с размером эту проблему не решает. И вообще, какой смысл сейчас рассматривать заведомо ошибочный код?

Re: Правильная обработка переменных в контексте прерываний

Чт окт 07, 2021 14:57:09

...
Для кого конвенции придумали?..
Например, http://ww1.microchip.com/downloads/en/a ... c42055.pdf рекомендуют использовать r0 как временный, r1 как нулевой, r2-r17, r28, r29 как сохраняемые (при вызове функций они не меняются), r0, r18-r27, r31 как временные (подпрограмма имеет право делать с ними все что угодно). Вот и все, регистры предназначены для операций, а не для хранения данных.
Разумеется, никто не мешает часть регистров зарезервировать под свои нужды. Для тех же флагов, например.

Регистровая модель для АВРки может быть любой - хоть под I8080 хоть под Z80 или что иное.
Ограничения по "вольному" использованию возможны только для регистровых пар x, y, z и R0/R1 как потенциально используемых в командах умножения.
Как вариант вот такая "заготовка" (мой "слэнг" из проектов котуинко):
Спойлер
Код:
;             принята базовая модель: AVR
; область ограниченного функционала
; .def  = r0 ; (математика и обмен с ПЗУ/самопрограммирование)
; .def  = r1 ; (математика и обмен с ПЗУ/самопрограммирование)
; .def  = r2 ; зеркало SREG (ограниченный функционал)
; .def  = r3  ; зеркало SPL (ограниченный функционал)
; .def  = r4  ; зеркало SPH (ограниченный функционал)
; .def  = r5  ; оперативные флаги (ограниченный функционал)
; .def  = r6  ; оперативные флаги (ограниченный функционал)
; .def  = r7 ;(ограниченный функционал)
; .def  = r8 ;(ограниченный функционал)
; .def  = r9 ;(ограниченный функционал)
; .def  = r10 ;(ограниченный функционал)
; .def  = r11 ;(ограниченный функционал)
; .def  = r12 ;(ограниченный функционал)
; .def  = r13 ;(ограниченный функционал)
; .def  = r14 ;(ограниченный функционал)
; .def  = r15 ;(ограниченный функционал)
; область полного функционала
 .def tmpr0 = r16 ; рабочий регистр (полный функционал)
 .def tmpr1 = r17 ; рабочий регистр (полный функционал)
 .def tmpr2 = r18 ; рабочий регистр (полный функционал)
 .def tmpr3 = r19 ; рабочий регистр (полный функционал)
 .def tmpr4 = r20 ; рабочий регистр (полный функционал)
 .def tmpr5 = r21 ; рабочий регистр (полный функционал)
 .def tmpr6 = r22 ; рабочий регистр (полный функционал)
 .def tmpr7 = r23 ; рабочий регистр (полный функционал)
 .def bpl = r24 ; "указатель базы" (полный функционал)
 .def bph = r25 ; "указатель базы" (полный функционал)
;     Xl = r26 ; адрес сегмента Х (полный функционал)
;     Xh = r27 ; адрес сегмента Х (полный функционал)
;     Yl = r28 ; адрес сегмента Y (полный функционал)
;     Yh = r29 ; адрес сегмента Y (полный функционал)
;     Zl = r30 ; адрес сегмента Z (полный функционал ПЗУ/самопрограммирование)
;     Zh = r31 ; адрес сегмента Z (полный функционал ПЗУ/самопрограммирование)
; регистры Xh:Xl, Yh:Yl, Zh:Zl определены в дефайне изготовителя и в системе команд
; изменение их имени хотя и возможно, но нежелательно -
; возникает путаница с интегрированной абревиатурой системы команд
; регистры имеют также отображение в пространстве ОЗУ 0х0000 - 0х001F

8)
Ответить