|
Работа с переменными в прерывании и основном теле, жутко неудобно выходит |
|
|
|
Oct 3 2015, 19:40
|

Практикующий маг
     
Группа: Свой
Сообщений: 3 634
Регистрация: 28-04-05
Из: Дубна, Моск.обл
Пользователь №: 4 576

|
Добрый вечер Что имеем: 8 битный проц, программа в прерывании читает из кольцевого буфера, указатель чтения -просто индекс массива, двухбайтное число. В основном теле мне приходит поток байт, который я контролирую (запускаю/останавливаю поток) и размещаю байты в этом же кольцевом буфере по другому указателю -указателю записи (тоже индекс массива). До этих пор все замечательно, но чтобы управлять потоком мне нужно знать сколько байт содержится в буфере, для этого я беру разницу между указателями записи и чтения и побитно умножаю ее на (размер_массива - 1). Эту разницу приходится брать в нескольких местах программы, при этом на время вычисления нужно блокировать прерывание, поскольку операция не атомарная, а одна из переменных модифицируется в прерывании. Если этого не делать происходит глюк, если делать -появляется задержка обработки прерывания, а это нехорошо, т.к в нем происходит формирования аудио сигнала. Да и некрасиво это и неудобно -следить какую перменную обрабатываю, нужно или нет блокировать прерывание, ну и поскольку кольцевой буфер используется в различных прерываниях нужно еще и учитывать какое именно прерывание блокировать Может это както по-другому можно делать, подскажите.
|
|
|
|
|
 |
Ответов
|
Oct 3 2015, 19:44
|
Знающий
   
Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088

|
Цитата(Alexashka @ Oct 3 2015, 22:40)  Может это както по-другому можно делать, подскажите. Заведите переменную "количество байт в буфере" и обновляйте её вместе с изменением указателей чтения и записи.
--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
|
|
|
|
|
Oct 3 2015, 19:49
|

Практикующий маг
     
Группа: Свой
Сообщений: 3 634
Регистрация: 28-04-05
Из: Дубна, Моск.обл
Пользователь №: 4 576

|
Цитата(gerber @ Oct 3 2015, 22:44)  Заведите переменную "количество байт в буфере" и обновляйте её вместе с изменением указателей чтения и записи. неа, смотрите, эта переменная тоже двухбайтная выходит. Допустим если байт в буфере больше 2000 нужно остановить поток, для этого я сравниваю ее с числом 2000, при этом сначала сравнивается младшие байты, потом старшие, если между этими операциями произойдет вызов подпрограммы, которая изменит старший байт "количество байт в буфере", то в результате сравнения можно получить очень неверный результат.
|
|
|
|
|
Oct 4 2015, 07:47
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Alexashka @ Oct 4 2015, 01:49)  неа, смотрите, эта переменная тоже двухбайтная выходит. Допустим если байт в буфере больше 2000 нужно остановить поток, для этого я сравниваю ее с числом 2000, при этом сначала сравнивается младшие байты, потом старшие, если между этими операциями произойдет вызов подпрограммы, которая изменит старший байт "количество байт в буфере", то в результате сравнения можно получить очень неверный результат. Конечно не нужно никаких "количеств байт". Достаточно двух указателей (чтения/записи). И достаточно операции чтения/записи этих указателей сделать атомарными. И для вычисления кол-ва байт достаточно найти разность указателей (с учётом их цикличности), не нужно никаких "побитных умножений". Неужто запрет прерывания на пару команд так влияет на выполнение алгоритма??? Тогда у Вас вероятно неправильно построен алгоритм.
|
|
|
|
|
Oct 4 2015, 21:53
|

Практикующий маг
     
Группа: Свой
Сообщений: 3 634
Регистрация: 28-04-05
Из: Дубна, Моск.обл
Пользователь №: 4 576

|
Цитата(jcxz @ Oct 4 2015, 10:47)  Конечно не нужно никаких "количеств байт". Достаточно двух указателей (чтения/записи). И достаточно операции чтения/записи этих указателей сделать атомарными. И для вычисления кол-ва байт достаточно найти разность указателей (с учётом их цикличности), не нужно никаких "побитных умножений". Неужто запрет прерывания на пару команд так влияет на выполнение алгоритма??? Тогда у Вас вероятно неправильно построен алгоритм. Запрет прерывания на пару команд не повлияет я так думаю, но вычисление разницы двухбайтных чисел с последующим умножением на маску это всё же не 2 операции, с другой стороны как я уже писал, проблема не в этом, а в том, что при каждом таком обращении нужно добавлять запрет и разрешение прерывания, либо оборачивать это в функцию, плюс нужно следить -если изменил прерывание например с АЦП на UART, то нужно переписывать функции чтения переменной, а также чтения/записи в буфер. А вот сделать чтение двухбайтного указателя атомарной операцией невозможно, поскольку ядро 8-разрядное  Ну и до кучи объясните как сделать это Цитата И для вычисления кол-ва байт достаточно найти разность указателей (с учётом их цикличности), не нужно никаких "побитных умножений". Допустим есть буфер, он занимает пространство от 0 до 2047, указатель есно двухбайтный, если мы возьмем разницу двух указателей например как "0"-"1" (значит в буфере 2047 байт данных) получим 0xFFFF, хотя должно быть 2047. Цитата(_Pasha @ Oct 4 2015, 14:27)  можно проверку делать с учетом неатомарности, поскольку указатели в данном случае readonly Поясните пожалуйста, что значит readonly если я как читаю указатель, так и пишу его (инкрементирую)  И что делает эта строчка Код while(res != *dsc) res = *dsc; К сожалению не читал обсуждение темы, о которой Вы говорите, если есть ссылочка прошу направить в нужном направлении
|
|
|
|
|
Oct 5 2015, 04:12
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(Alexashka @ Oct 5 2015, 03:53)  Запрет прерывания на пару команд не повлияет я так думаю, но вычисление разницы двухбайтных чисел с последующим умножением на маску это всё же не 2 операции, с другой стороны как я уже писал, проблема не в этом, а в том, что при каждом таком обращении нужно добавлять запрет и разрешение прерывания, либо оборачивать это в функцию, плюс нужно следить -если изменил прерывание например с АЦП на UART, то нужно переписывать функции чтения переменной, а также чтения/записи в буфер. ЗАЧЕМ???? Цитата(Alexashka @ Oct 5 2015, 03:53)  А вот сделать чтение двухбайтного указателя атомарной операцией невозможно, поскольку ядро 8-разрядное  Ну и до кучи объясните как сделать это Сделать чтение двухбайтового числа на 8-разрядном процессоре атомарным (при отсутствии в нём команды чтения 16-разрядных чисел), элементарно: u16 i; __disable_interrupt(); i = var; __enable_interrupt(); всё! Цитата(Alexashka @ Oct 5 2015, 03:53)  Допустим есть буфер, он занимает пространство от 0 до 2047, указатель есно двухбайтный, если мы возьмем разницу двух указателей например как "0"-"1" (значит в буфере 2047 байт данных) получим 0xFFFF, хотя должно быть 2047. Код //предполагаем размерность int >=16бит int volatile rpos = 0, wpos = 0; u8 buf[N];
int read() { int ir = rpos; int iw = wpos; if (ir == iw) return -1; if (--ir < 0) ir = sizeof(buf) - 1; iw = buf[ir]; rpos = ir; return iw; }
int write(int data) { int ir = rpos; int iw = wpos; if (--iw < 0) iw = sizeof(buf) - 1; if (ir == iw) return -1; buf[iw] = data; wpos = iw; return 0; }
//кол-во данных в кольц. буфере int occupy() { int ir = rpos; int iw = wpos, i; if ((i = ir - iw) < 0) i += sizeof(buf); return i; }
//свободное место в кольц. буфере int free() { int ir = rpos; int iw = wpos, i; if ((i = iw - ir - 1) < 0) i += sizeof(buf); return i; } Это классика. Если операции чтения/записи wpos, rpos в данном CPU атомарны, то можно использовать read(), write() в разных процессах/ISR как есть, без запретов прерываний. Только обязательно read() и write() каждая должна вызываться только из одного процесса! Если чтение/запись указателей неатомарны, то необходимо сделать их таковыми с помощью например запретов прерываний. В процессе, вызывающем read(), запрет прерываний необходим при чтении wpos и записи rpos. Для write() - наоборот. Для occupy() и free() - аналогично - запрет прерываний для чтения того указателя, который модифицируется в другом процессе. Цитата(_Pasha @ Oct 4 2015, 17:27)  можно проверку делать с учетом неатомарности, поскольку указатели в данном случае readonly 100500 раз об этом говорили, но давно, лет 6-7 назад. Пора обновлять например. Это то же самое, что просто запрет прерывания на время чтения *dsc. Никакого преимущества не даёт по сранению с обычным запретом прерываний. По скорости может быть как медленнее так и быстрее запрета - в зависимости от CPU.
|
|
|
|
|
Oct 5 2015, 12:25
|

Практикующий маг
     
Группа: Свой
Сообщений: 3 634
Регистрация: 28-04-05
Из: Дубна, Моск.обл
Пользователь №: 4 576

|
Цитата(jcxz @ Oct 5 2015, 07:12)  ЗАЧЕМ???? ... элементарно: u16 i; __disable_interrupt(); i = var; __enable_interrupt(); всё! <РУКАЛИЦО> Я так и делаю (см.первый пост) и уже дважды объяснил почему мне не нравится этот метод. Цитата Если чтение/запись указателей неатомарны, то необходимо сделать их таковыми с помощью например запретов прерываний. В процессе, вызывающем read(), запрет прерываний необходим при чтении wpos и записи rpos. Для write() - наоборот. Для occupy() и free() - аналогично - запрет прерываний для чтения того указателя, который модифицируется в другом процессе. Вот об этом и речь -прикиньте, сколько таких мест, где нужно локально блокирова/разрешать прерывание. Можно конечно проще -блокировать прерывание перед вызовом функции read/write/free, но тогда задержка обработки прерывания станет недопустимо большой. Есть у меня идея использовать теневые регистры, которые будут обновляться в прерывании в том случае, если в основном теле к ним не производится обращение, но это уже в другой раз, пока что обошелся локальной блокировкой прерываний. Посмотрел Ваш код, в принципе у меня примерно также, только не понял зачем Вы копируете глобальные rpos, wpos в локальные ir,iw ведь можно работать непосредственно с глобальными? Да, к слову, указатели не использую, слишком медленно они обрабатываются в 8051: сначала ф-я чтения по указателю должна определить какую область памяти адресует указатель, а затем перейти к соответствующей команде чтения (там вроде как 4 варианта обращения к памяти и соответственно 4 разные ассемблерные команды) , с массивом такой ерунды не происходит -компилятор уже на этапе компиляции знает к какому типу памяти мы обращаемся поэутому все делается очень быстро.
|
|
|
|
|
Oct 6 2015, 07:29
|

Практикующий маг
     
Группа: Свой
Сообщений: 3 634
Регистрация: 28-04-05
Из: Дубна, Моск.обл
Пользователь №: 4 576

|
Цитата(jcxz @ Oct 5 2015, 15:58)  Там где они модифицируются, можно делать только так, как я написал. А не напрямую глобальные. Если Вы этого не поняли, значит не поняли ничего в моих функциях. Да вроде понятно всЁ. Ну я например работаю непосредственно с глобальными и вроде все работает (блокирую прерывание непосредственно перед сравнением двух глобальных переменных). С копированием получается наглядней в том смысле что имеем только 2 места где нужно ставить блокировку прерывания, и к локальной переменной можно обращаться сколько угодно раз. Я правильно понимаю идею? Цитата(Сергей Борщ @ Oct 5 2015, 15:53)  Все три известные мне компилятора для x51 (Кейл, Иар и SDCC) позволяли объявлять указатель на конкретный тип памяти. Такие указатели и меньше места занимают и работают без этого ветвления. Посмотрите документацию на свой компилятор, наверняка они там есть. Проверил- и действительно: добавил спецификатор памяти в объявление указателя и всё лишнее в коде исчезло. Вот оно как оказывается, Михалыч (с)  Спасибо за информацию, Сергей! Цитата(_Pasha @ Oct 5 2015, 16:36)  Мы читаем указатель, но не запрещаем прерывания. Указатель считается валидным, если за два чтения подряд он не был изменен процессом более высокого приоритета. Это и есть неатомарное чтение. Быстро ли, медленно ли? Наверное, если это имеет значение - пора утяжелять прерывание. Понял идею, но это ведь можно провернуть не только с указателем, но и с любой переменной? Добавлено: попробовал Вашу функцию, вроде работает, только выдает предупреждение Цитата Pointer truncation A spaced pointer has been assigned some constant value which exceeds the range covered by the pointers memory space. For example: char idata *p1 = 0x1234; /* result is 0x34 */
|
|
|
|
|
Oct 6 2015, 16:36
|

Практикующий маг
     
Группа: Свой
Сообщений: 3 634
Регистрация: 28-04-05
Из: Дубна, Моск.обл
Пользователь №: 4 576

|
Цитата(_Pasha @ Oct 6 2015, 14:51)  Ну я как бы тоже "из головы" писал, не с реального примера. У меня не случались неатомарные чтения по указателю. Мог и что-то упустить. Да нет все ок, разобрался  Заменил Код char *p = nonatomic_ptr(&r_pos); на Код char *p; p = nonatomic_ptr(&r_pos); и ашИпка ушла
|
|
|
|
Сообщений в этой теме
Alexashka Работа с переменными в прерывании и основном теле Oct 3 2015, 19:40   _Pasha Цитата(jcxz @ Oct 4 2015, 10:47) Неужто з... Oct 4 2015, 11:27    ViKo Цитата(_Pasha @ Oct 4 2015, 14:27) Кодvol... Oct 8 2015, 09:31     _Pasha Цитата(ViKo @ Oct 8 2015, 12:31) Читать и... Oct 8 2015, 16:55      jcxz Цитата(Alexashka @ Oct 5 2015, 18:25) -пр... Oct 5 2015, 12:51      Сергей Борщ Цитата(Alexashka @ Oct 5 2015, 15:25) сли... Oct 5 2015, 12:53        jcxz Цитата(Alexashka @ Oct 6 2015, 13:29) Да ... Oct 6 2015, 08:10         Alexashka Цитата(jcxz @ Oct 6 2015, 11:10) просто т... Oct 6 2015, 08:41     MrYuran Цитата(jcxz @ Oct 5 2015, 07:12) Это то ж... Oct 6 2015, 10:49      jcxz Цитата(MrYuran @ Oct 6 2015, 16:49) Преим... Oct 7 2015, 02:41    _Pasha Цитата(Alexashka @ Oct 5 2015, 00:53) Поя... Oct 5 2015, 13:36 gerber Значит, можно сделать порог кратным 256 и ограничи... Oct 3 2015, 20:05 Alexashka ЦитатаЗначит, можно сделать порог кратным 256 и ог... Oct 3 2015, 22:28 aaarrr Разделите свой большой буфер на N маленьких и опер... Oct 3 2015, 20:14 smalcom ЦитатаРазделите свой большой буфер на N маленьких ... Oct 3 2015, 21:43 Valentine Loginov Вариант с так называемым "nonatomic" по-... Oct 7 2015, 07:13 zltigo QUOTE (Valentine Loginov @ Oct 7 2015, 10... Oct 7 2015, 07:25 Valentine Loginov Идея в том, что атомарный флаг делается тоже через... Oct 8 2015, 07:35 Alexashka Цитата(Valentine Loginov @ Oct 8 2015, 11... Oct 8 2015, 07:58  jcxz Цитата(Alexashka @ Oct 8 2015, 13:58) Ест... Oct 8 2015, 09:09   Alexashka Цитата(jcxz @ Oct 8 2015, 13:09) Есть вер... Oct 8 2015, 18:17    jcxz Цитата(Alexashka @ Oct 9 2015, 00:17) Да ... Oct 9 2015, 03:18    ASN Alexashka
Так и не понял, почему запрет прерываний... Oct 9 2015, 06:05 zltigo QUOTE (Valentine Loginov @ Oct 8 2015, 10... Oct 8 2015, 09:13 Valentine Loginov Вообще долгая работа с данными в прерывании плохо.... Oct 9 2015, 06:51 Alexashka Цитата(Valentine Loginov @ Oct 9 2015, 10... Oct 9 2015, 07:40  zltigo QUOTE (Alexashka @ Oct 9 2015, 10:40) Я х... Oct 9 2015, 08:33   Alexashka Цитата(zltigo @ Oct 9 2015, 12:33) Лучше ... Oct 9 2015, 13:09    zltigo QUOTE (Alexashka @ Oct 9 2015, 16:09) А к... Oct 9 2015, 13:42 rudy_b Цитата(Alexashka @ Oct 3 2015, 22:40) Доб... Oct 9 2015, 13:19 Alexashka Цитата(rudy_b @ Oct 9 2015, 17:19) Как-то... Oct 9 2015, 17:09 ViKo Так зачем "чутка освободится..."? Сделай... Oct 9 2015, 18:19 rudy_b Цитата(ViKo @ Oct 9 2015, 21:19) Так заче... Oct 9 2015, 23:08  Dog Pawlowa Какие компоненты в систему заложишь, так она и поп... Oct 10 2015, 03:51 Alexashka Цитата(ViKo @ Oct 9 2015, 22:19) Так заче... Oct 10 2015, 11:19  ViKo Цитата(Alexashka @ Oct 10 2015, 14:19) Эт... Oct 10 2015, 11:39   Alexashka Цитата(ViKo @ Oct 10 2015, 15:39) Красиво... Oct 10 2015, 12:09    Tanya Цитата(Alexashka @ Oct 10 2015, 15:09) Жа... Oct 10 2015, 12:19     Alexashka Цитата(Tanya @ Oct 10 2015, 16:19) Понима... Oct 10 2015, 15:31      _Pasha Цитата(Alexashka @ Oct 10 2015, 18:31) Ду... Oct 10 2015, 18:06  Tanya Цитата(Alexashka @ Oct 10 2015, 14:19) И ... Oct 10 2015, 11:47 ViKo Можно и одним буфером обойтись, если разбить его н... Oct 10 2015, 13:57 smalcom тогда будет ограничение на размер буфера. Oct 10 2015, 18:56 zltigo QUOTE (smalcom @ Oct 10 2015, 21:56) тогд... Oct 10 2015, 19:04  Alexashka Цитата(zltigo @ Oct 10 2015, 23:04) Вам у... Oct 11 2015, 20:56   zltigo QUOTE (Alexashka @ Oct 11 2015, 23:56) Вы... Oct 11 2015, 21:01    Alexashka Цитата(zltigo @ Oct 12 2015, 01:01) Если ... Oct 12 2015, 06:38 smalcom а? Oct 10 2015, 20:09 ViKo Да, для 8-битников 16 256-байтовых буферов будет л... Oct 12 2015, 05:45 smalcom для этого используйте 3, 4 и т.д. буферов. Oct 12 2015, 08:00
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|