Только для топикстартера похоже смысл так и остался непонятным...
Тут много было всего сказано. Смысл чего? Если смысл понятия "флаг", то это вполне мне понятно. Принцип-то не сложен, в зависимости от события сигнализирует какой-то части программы: можно - нельзя, есть - нет, произошло - не произошло и т.д. И что флагом может быть все подряд ))) Главное чтобы однозначно сигнализировало о наступившем событии. Если относительно прерываний, потери информации и как не попасть в просак. То здесь я еще не вполне понимаю как правильно с этим бороться. Толи флаги ставить, то ли запрет прерываний делать, то ли флаги на флаги ставить, чтобы первые флаги писать и в прерывании и в основном цикле, без опасений То ли стараться вообще не использовать прерывания и делать в основном цикле что-то похожее на псевдомногопоточность. ))
флаги - это не переменные. флаги для того и нужны, чтобы в одном мете поставить, а в другом проверить и если поставлен, выполнить действия и сбросить флаг.
Отчего же. Переменная, пусть однобитная и объединенная с другими. И под условие задачи попадает. Как бы то ни было, даже если флаги не переменные - как их реализовать-то?
Цитата:
Именно так работают неправильно написанные кольцевые буферы.
Не сработает. Для кольцевого буфера нужно две переменных - голова и хвост. Прерывание имеет право читать голову и писать хвост, основной цикл наоборот. Конфликта записи не возникает. Более того, размер буфера в 8-битках почти никогда не превышает 8 бит, так что даже прерывания можно не блокировать, доступ останется атомарным.
Цитата:
для меня в МК вообще не существует переменных - я пишу на ассемблере и у меня есть только регистры и ячейки памяти ОЗУ.
Если под данные отводится больше одной ячейки, такой подход только все усложняет. Но опять-таки, какая разница как это назвать. Допустим, нам нужно контролировать что прошла секунда (счет в прерывании TIM0), что передана вся строка по UART и что напряжение АЦП выше порога. Из соображений экономии вы отвели под это дело биты 0,1,2 ячейки 0x0084.
Если под данные отводится больше одной ячейки, такой подход только все усложняет.
ни чуть не усложняет.
COKPOWEHEU писал(а):
Но опять-таки, какая разница как это назвать.
совершенно верно сказано. лично мне нравится называть "параметр", и неважно, сколько ячеек он занимает. например, у меня параметр "время" - 3 ячейки памяти, часы, минуты и секунды, параметр "емкость" (аккумулятора) - 3 ячейки памяти. прочие параметры у меня все двухбайтные (2 ячейки), независимо от максимального значения параметра - для простоты доступа к ним. а на флаги я выделяю регистры, а не ячейки памяти. в одном моем проекте столько флагов, что пришлось выделить 3 регистра. выделять регистр под флаги очень удобно, так как есть команды работы с битами регистра.
_________________ Мудрость приходит вместе с импотенцией... Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Не сработает. Для кольцевого буфера нужно две переменных - голова и хвост. Прерывание имеет право читать голову и писать хвост, основной цикл наоборот. Конфликта записи не возникает. Более того, размер буфера в 8-битках почти никогда не превышает 8 бит, так что даже прерывания можно не блокировать, доступ останется атомарным.
У меня так и сделано, но тут нет записи переменной в основном коде и прерывании, а если добавлять данные в буфер могут несколько потоков, что типично для любой RTOS, где помимо этого хватает всяких мьютексов, или если размер сохраняется в отдельной переменной, что опять же типично, потому что значительная часть реализаций именно так и работает, то придется озаботиться атомарным доступом.
... Если относительно прерываний, потери информации и как не попасть в просак. То здесь я еще не вполне понимаю как правильно с этим бороться. Толи флаги ставить, то ли запрет прерываний делать, то ли флаги на флаги ставить, чтобы первые флаги писать и в прерывании и в основном цикле, без опасений То ли стараться вообще не использовать прерывания и делать в основном цикле что-то похожее на псевдомногопоточность. ))
Просто добавить в соответствующие места программ анализ флагов и соответствующую результатам анализа обработку данных. Для основной программы: Нет запроса - пробегаем мимо, есть что-то новое - исполняем (перемещая данные из промежуточного буфера в основной) и сбрасываем запрос. Для прерывания - контроль предыдущего запроса - если не обработан - пробегаем мимо если обработан - загружаем промежуточный буфер и выстваляем запрос обработки
parovoZZ, статью я понял так: если писать биты (значение) в переменную в основном цикле и также в прерывании, то может произойти потеря данных при ЗАПИСИ битов в прерывании. Автор статьи предлагает использовать запрет прерываний (атомарная операция) при записи переменной (флага) в основном цикле.
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, где помимо этого хватает всяких мьютексов
Ну, мбютексы ведь тоже как-то реализованы, и их реализация тоже попадает под наше обсуждение.
Цитата:
или если размер сохраняется в отдельной переменной
Хранить размер буфера в переменной? А какой смысл? Я очень надеюсь, что он у вас хотя бы не в динамической памяти находится... Или вы про количество данных в буфере? Так оно вычисляется из разности между головой и хвостом за два действия.
BOB51, Вы, собственно, об этом в начале и говорили. И идея меня зацепила! Но обсуждения в теме как-то в сторону увели )) Спасибо! В общем что я из этой темы для себя вынес: 1. Писать в одну и туже переменную в основном цикле и в прерывании неправильно (неграмотно). 2. Пишем только через буфер используя флаги (семафоры, мьютексы и т.п.). Причем в буфере можно выстроить очередь запросов (данных) от прерывания. Таким образом мы и код не будем блокировать от прерываний и все прерывания будут гарантированно обработаны в их первоначальном порядке (количестве). Все правильно?
Вобщем верно. Таким способом делается "квазиавтономная" динамическая индикация - для обслуживания развертки используется таймер и буфер видеопамяти. Это система крутится сама по себе на основе прерываний по таймеру (в фоне основной программы), а в основной программе второй буфер данных и флаг запроса перезаписи. Как толькоданные готовы - флаг для "контроллера дисплея", а тот уже дождавшись нужного момента перебрасывает данные в видеопамять и затем сбрасывает флаг запроса.
Можно и так, но тогда за ними надо следить чтобы разная периферия не пыталась занять одни и те же адреса.
а как ты себе представляешь, писать на ассемблере и не следить за использованием регистров? скажу даже больше - когда пишешь на ассемблере, следить нужно не только за регистрами флагов, следить нужно за всеми регистрами.
_________________ Мудрость приходит вместе с импотенцией... Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
1. Писать в одну и туже переменную в основном цикле и в прерывании неправильно (неграмотно). 2. Пишем только через буфер используя флаги (семафоры, мьютексы и т.п.). Причем в буфере можно выстроить очередь запросов (данных) от прерывания. Таким образом мы и код не будем блокировать от прерываний и все прерывания будут гарантированно обработаны в их первоначальном порядке (количестве). Все правильно?
Усложнять простые вещи - это, конечно, признак высшего класса.)
а как ты себе представляешь, писать на ассемблере и не следить за использованием регистров? скажу даже больше - когда пишешь на ассемблере, следить нужно не только за регистрами флагов, следить нужно за всеми регистрами.
Для кого конвенции придумали?.. Например, http://ww1.microchip.com/downloads/en/a ... c42055.pdf рекомендуют использовать r0 как временный, r1 как нулевой, r2-r17, r28, r29 как сохраняемые (при вызове функций они не меняются), r0, r18-r27, r31 как временные (подпрограмма имеет право делать с ними все что угодно). Вот и все, регистры предназначены для операций, а не для хранения данных. Разумеется, никто не мешает часть регистров зарезервировать под свои нужды. Для тех же флагов, например.
Ну, мбютексы ведь тоже как-то реализованы, и их реализация тоже попадает под наше обсуждение.
О чем и речь, в случае мьютексов тоже будет запись переменной из разных потоков и никто не говорит, что так делать нельзя.
COKPOWEHEU писал(а):
Хранить размер буфера в переменной? А какой смысл? Я очень надеюсь, что он у вас хотя бы не в динамической памяти находится... Или вы про количество данных в буфере? Так оно вычисляется из разности между головой и хвостом за два действия.
Емкость - константа, количество данных вычисляется как разность между головой и хвостом, но только если реализация именно такова, а может быть голова и размер или голова, хвост и размер, причем такие реализации можно легко найти в книгах непосредственно по эмбедду. И простой разностью можно обойтись только если емкость буфера равна степени двойки, плюс подход только с головой и хвостом чреват подводными камнями... Классический пример когда некоторые компиляторы сначала инкрементят и сохраняют tail, а потом уже пишут в buf:
И простой разностью можно обойтись только если емкость буфера равна степени двойки
Если не равняется, то вместо наложения маски будет сравнение и вычитание/сложение, вот и все.
Цитата:
а может быть голова и размер или голова, хвост и размер
Пока что я вижу только явный недостаток с тем, что к полю размера нужен доступ и при записи, и при чтении. Но вы говорите, что такие реализации есть. Чем именно они настолько хороши, что перевешивают этот недостаток?
Цитата:
плюс подход только с головой и хвостом чреват подводными камнями... Классический пример когда некоторые компиляторы сначала инкрементят и сохраняют tail, а потом уже пишут в buf
Ну так вариант с размером эту проблему не решает. И вообще, какой смысл сейчас рассматривать заведомо ошибочный код?
... Для кого конвенции придумали?.. Например, 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
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 46
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения