Страница 4 из 5

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

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

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

Добавлено: Чт окт 07, 2021 08:54:12
COKPOWEHEU
флаги - это не переменные. флаги для того и нужны, чтобы в одном мете поставить, а в другом проверить и если поставлен, выполнить действия и сбросить флаг.
Отчего же. Переменная, пусть однобитная и объединенная с другими. И под условие задачи попадает.
Как бы то ни было, даже если флаги не переменные - как их реализовать-то?
Именно так работают неправильно написанные кольцевые буферы.
Не сработает. Для кольцевого буфера нужно две переменных - голова и хвост. Прерывание имеет право читать голову и писать хвост, основной цикл наоборот. Конфликта записи не возникает. Более того, размер буфера в 8-битках почти никогда не превышает 8 бит, так что даже прерывания можно не блокировать, доступ останется атомарным.
для меня в МК вообще не существует переменных - я пишу на ассемблере и у меня есть только регистры и ячейки памяти ОЗУ.
Если под данные отводится больше одной ячейки, такой подход только все усложняет.
Но опять-таки, какая разница как это назвать. Допустим, нам нужно контролировать что прошла секунда (счет в прерывании TIM0), что передана вся строка по UART и что напряжение АЦП выше порога. Из соображений экономии вы отвели под это дело биты 0,1,2 ячейки 0x0084.

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

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

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

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

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

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

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

Добавлено: Чт окт 07, 2021 09:52:44
Reflector
[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4102089#p4102089"]Не сработает. Для кольцевого буфера нужно две переменных - голова и хвост. Прерывание имеет право читать голову и писать хвост, основной цикл наоборот. Конфликта записи не возникает. Более того, размер буфера в 8-битках почти никогда не превышает 8 бит, так что даже прерывания можно не блокировать, доступ останется атомарным.[/uquote]
У меня так и сделано, но тут нет записи переменной в основном коде и прерывании, а если добавлять данные в буфер могут несколько потоков, что типично для любой RTOS, где помимо этого хватает всяких мьютексов, или если размер сохраняется в отдельной переменной, что опять же типично, потому что значительная часть реализаций именно так и работает, то придется озаботиться атомарным доступом.

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

Добавлено: Чт окт 07, 2021 09:59:13
parovoZZ
[uquote="Kalisnik",url="/forum/viewtopic.php?p=4101526#p4101526"][uquote="parovoZZ",url="/forum/viewtopic.php?p=4101459#p4101459"]а зачем запрещать прерывания на запись переменной, разрядность которой совпадает с разрядностью шины?[/uquote]
В начале темы я дал ссылку на статью, там описаны причины.[/uquote]
Я вроде написал по-русски: ЗАПИСЬ. В статье совершенно про другое.

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

Добавлено: Чт окт 07, 2021 10:16:19
BOB51
[uquote="Kalisnik",url="/forum/viewtopic.php?p=4102086#p4102086"]...
Если относительно прерываний, потери информации и как не попасть в просак. То здесь я еще не вполне понимаю как правильно с этим бороться. Толи флаги ставить, то ли запрет прерываний делать, то ли флаги на флаги ставить, чтобы первые флаги писать и в прерывании и в основном цикле, без опасений :))) То ли стараться вообще не использовать прерывания и делать в основном цикле что-то похожее на псевдомногопоточность. ))[/uquote]
Просто добавить в соответствующие места программ анализ флагов и соответствующую результатам анализа обработку данных.
Для основной программы:
Нет запроса - пробегаем мимо, есть что-то новое - исполняем (перемещая данные из промежуточного буфера в основной) и сбрасываем запрос.
Для прерывания -
контроль предыдущего запроса - если не обработан - пробегаем мимо
если обработан - загружаем промежуточный буфер и выстваляем запрос обработки
8)

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

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

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

Добавлено: Чт окт 07, 2021 11:05:01
COKPOWEHEU
из соображений экономии сишному компилятору как-то больше нравятся целые переменные в качестве флагов, но иногда и битовые поля
Смотря на что оптимизировать - на скорость или на память. Впрочем, по скорости выигрыша тоже нет:

Код: Выделить всё

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

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

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

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

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

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

Добавлено: Чт окт 07, 2021 12:03:28
slav0n
Dimon456 писал(а):все зависит от программиста.
правильно
лично я пишу прямо в теле функции:
static u8 flg;
и не парюсь

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

Добавлено: Чт окт 07, 2021 13:11:18
OKF
[uquote="Kalisnik",url="/forum/viewtopic.php?p=4102147#p4102147"]1. Писать в одну и туже переменную в основном цикле и в прерывании неправильно (неграмотно).
2. Пишем только через буфер используя флаги (семафоры, мьютексы и т.п.). Причем в буфере можно выстроить очередь запросов (данных) от прерывания. Таким образом мы и код не будем блокировать от прерываний и все прерывания будут гарантированно обработаны в их первоначальном порядке (количестве). Все правильно?[/uquote]
Усложнять простые вещи - это, конечно, признак высшего класса.)

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

Добавлено: Чт окт 07, 2021 13:22:06
COKPOWEHEU
а как ты себе представляешь, писать на ассемблере и не следить за использованием регистров?
скажу даже больше - когда пишешь на ассемблере, следить нужно не только за регистрами флагов, следить нужно за всеми регистрами.
Для кого конвенции придумали?..
Например, 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
Reflector
[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4102145#p4102145"]Ну, мбютексы ведь тоже как-то реализованы, и их реализация тоже попадает под наше обсуждение.[/uquote]
О чем и речь, в случае мьютексов тоже будет запись переменной из разных потоков и никто не говорит, что так делать нельзя.
COKPOWEHEU писал(а):Хранить размер буфера в переменной? А какой смысл? Я очень надеюсь, что он у вас хотя бы не в динамической памяти находится...
Или вы про количество данных в буфере? Так оно вычисляется из разности между головой и хвостом за два действия.
Емкость - константа, количество данных вычисляется как разность между головой и хвостом, но только если реализация именно такова, а может быть голова и размер или голова, хвост и размер, причем такие реализации можно легко найти в книгах непосредственно по эмбедду. И простой разностью можно обойтись только если емкость буфера равна степени двойки, плюс подход только с головой и хвостом чреват подводными камнями... Классический пример когда некоторые компиляторы сначала инкрементят и сохраняют tail, а потом уже пишут в buf:

Код: Выделить всё

buf[tail++ & mask] = value;

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

Добавлено: Чт окт 07, 2021 14:35:01
Kalisnik
OKF, предложите свой вариант :)

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

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

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

Добавлено: Чт окт 07, 2021 14:57:09
BOB51
[uquote="COKPOWEHEU",url="/forum/viewtopic.php?p=4102228#p4102228"]...
Для кого конвенции придумали?..
Например, http://ww1.microchip.com/downloads/en/a ... c42055.pdf рекомендуют использовать r0 как временный, r1 как нулевой, r2-r17, r28, r29 как сохраняемые (при вызове функций они не меняются), r0, r18-r27, r31 как временные (подпрограмма имеет право делать с ними все что угодно). Вот и все, регистры предназначены для операций, а не для хранения данных.
Разумеется, никто не мешает часть регистров зарезервировать под свои нужды. Для тех же флагов, например.[/uquote]
Регистровая модель для АВРки может быть любой - хоть под 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)