реклама на сайте
подробности

 
 
4 страниц V   1 2 3 > »   
Reply to this topicStart new topic
> Работа с переменными в прерывании и основном теле, жутко неудобно выходит
Alexashka
сообщение Oct 3 2015, 19:40
Сообщение #1


Практикующий маг
******

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



Добрый вечер sm.gif
Что имеем: 8 битный проц, программа в прерывании читает из кольцевого буфера, указатель чтения -просто индекс массива, двухбайтное число. В основном теле мне приходит поток байт, который я контролирую (запускаю/останавливаю поток) и размещаю байты в этом же кольцевом буфере по другому указателю -указателю записи (тоже индекс массива). До этих пор все замечательно, но чтобы управлять потоком мне нужно знать сколько байт содержится в буфере, для этого я беру разницу между указателями записи и чтения и побитно умножаю ее на (размер_массива - 1). Эту разницу приходится брать в нескольких местах программы, при этом на время вычисления нужно блокировать прерывание, поскольку операция не атомарная, а одна из переменных модифицируется в прерывании. Если этого не делать происходит глюк, если делать -появляется задержка обработки прерывания, а это нехорошо, т.к в нем происходит формирования аудио сигнала. Да и некрасиво это и неудобно -следить какую перменную обрабатываю, нужно или нет блокировать прерывание, ну и поскольку кольцевой буфер используется в различных прерываниях нужно еще и учитывать какое именно прерывание блокировать biggrin.gif
Может это както по-другому можно делать, подскажите.
Go to the top of the page
 
+Quote Post
gerber
сообщение Oct 3 2015, 19:44
Сообщение #2


Знающий
****

Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088



Цитата(Alexashka @ Oct 3 2015, 22:40) *
Может это както по-другому можно делать, подскажите.

Заведите переменную "количество байт в буфере" и обновляйте её вместе с изменением указателей чтения и записи.


--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
Go to the top of the page
 
+Quote Post
Alexashka
сообщение Oct 3 2015, 19:49
Сообщение #3


Практикующий маг
******

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



Цитата(gerber @ Oct 3 2015, 22:44) *
Заведите переменную "количество байт в буфере" и обновляйте её вместе с изменением указателей чтения и записи.

неа, смотрите, эта переменная тоже двухбайтная выходит. Допустим если байт в буфере больше 2000 нужно остановить поток, для этого я сравниваю ее с числом 2000, при этом сначала сравнивается младшие байты, потом старшие, если между этими операциями произойдет вызов подпрограммы, которая изменит старший байт "количество байт в буфере", то в результате сравнения можно получить очень неверный результат.
Go to the top of the page
 
+Quote Post
gerber
сообщение Oct 3 2015, 20:05
Сообщение #4


Знающий
****

Группа: Участник
Сообщений: 750
Регистрация: 1-11-11
Пользователь №: 68 088



Значит, можно сделать порог кратным 256 и ограничиться сравнением одного старшего байта. rolleyes.gif
Да и вообще, логично начинать сравнение со старшего байта, а не с младшего, а младшие сравнивать только в случае равенства старших...

Сообщение отредактировал gerber - Oct 3 2015, 20:07


--------------------
"... часами я мог наблюдать, как люди работают." (М. Горький)
Go to the top of the page
 
+Quote Post
aaarrr
сообщение Oct 3 2015, 20:14
Сообщение #5


Гуру
******

Группа: Свой
Сообщений: 10 713
Регистрация: 11-12-04
Пользователь №: 1 448



Разделите свой большой буфер на N маленьких и оперируйте буферами, а не байтами.
Go to the top of the page
 
+Quote Post
smalcom
сообщение Oct 3 2015, 21:43
Сообщение #6


Профессионал
*****

Группа: Свой
Сообщений: 1 292
Регистрация: 26-06-07
Пользователь №: 28 718



Цитата
Разделите свой большой буфер на N маленьких и оперируйте буферами, а не байтами.

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

Примечик
Код
#define PLAYER_CONF_BUFSNDOUT_TIME_MS    20
#define PLAYER_CONF_BUFSNDOUT_SIZE        (PLAYER_CONF_BUFSNDOUT_TIME_MS * (44100 * 2 * 2 / 1000))
#define PLAYER_CONF_BUFSNDOUT_COUNT        3

template<uint32_t TBufSize> struct SSoundBuffer
{
    __attribute__((__aligned__(4))) uint8_t Data[TBufSize];
    volatile uint32_t Count;

    void Clear() { Count = 0; }
    bool IsEmpty() { return Count == 0; }
    uint32_t Size() { return TBufSize; }
    uint32_t Free() { return TBufSize - Count; }

    void AddData(const uint8_t* pData, const uint16_t pDataSize)
    {
        uint32_t toadd = pDataSize;

        if(pDataSize > Free()) toadd = Free();

        memcpy(&Data[Count], pData, toadd);
        Count += toadd;
    }
};

typedef SSoundBuffer<PLAYER_CONF_BUFSNDOUT_SIZE> BufferSndOut;

static BufferSndOut mBufferSndOut[PLAYER_CONF_BUFSNDOUT_COUNT];
Go to the top of the page
 
+Quote Post
Alexashka
сообщение Oct 3 2015, 22:28
Сообщение #7


Практикующий маг
******

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



Цитата
Значит, можно сделать порог кратным 256 и ограничиться сравнением одного старшего байта. rolleyes.gif


Цитата
Разделите свой большой буфер на N маленьких и оперируйте буферами, а не байтами.


Ну да, это получается примерно одно и тоже, старший байт по сути будет являться номером буфера.
Go to the top of the page
 
+Quote Post
jcxz
сообщение Oct 4 2015, 07:47
Сообщение #8


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(Alexashka @ Oct 4 2015, 01:49) *
неа, смотрите, эта переменная тоже двухбайтная выходит. Допустим если байт в буфере больше 2000 нужно остановить поток, для этого я сравниваю ее с числом 2000, при этом сначала сравнивается младшие байты, потом старшие, если между этими операциями произойдет вызов подпрограммы, которая изменит старший байт "количество байт в буфере", то в результате сравнения можно получить очень неверный результат.

Конечно не нужно никаких "количеств байт". Достаточно двух указателей (чтения/записи). И достаточно операции чтения/записи этих указателей сделать атомарными. И для вычисления кол-ва байт достаточно найти разность указателей (с учётом их цикличности), не нужно никаких "побитных умножений".
Неужто запрет прерывания на пару команд так влияет на выполнение алгоритма??? Тогда у Вас вероятно неправильно построен алгоритм.
Go to the top of the page
 
+Quote Post
_Pasha
сообщение Oct 4 2015, 11:27
Сообщение #9


;
******

Группа: Участник
Сообщений: 5 646
Регистрация: 1-08-07
Пользователь №: 29 509



Цитата(jcxz @ Oct 4 2015, 10:47) *
Неужто запрет прерывания на пару команд так влияет на выполнение алгоритма??? Тогда у Вас вероятно неправильно построен алгоритм.

можно проверку делать с учетом неатомарности, поскольку указатели в данном случае readonly
100500 раз об этом говорили, но давно, лет 6-7 назад. Пора обновлять rolleyes.gif
например.
Код
volatile char *r_pos, w_pos;
char * nonatomic_ptr(char ** dsc)
{
  char * res = *dsc;
  while(res != *dsc) res = *dsc;
  return res;
}

char *p  = nonatomic_ptr(&r_pos);

если оптимизатор выкидывает - dsc должен быть volatile, наверное

Сообщение отредактировал _Pasha - Oct 4 2015, 11:28
Go to the top of the page
 
+Quote Post
Alexashka
сообщение Oct 4 2015, 21:53
Сообщение #10


Практикующий маг
******

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



Цитата(jcxz @ Oct 4 2015, 10:47) *
Конечно не нужно никаких "количеств байт". Достаточно двух указателей (чтения/записи). И достаточно операции чтения/записи этих указателей сделать атомарными. И для вычисления кол-ва байт достаточно найти разность указателей (с учётом их цикличности), не нужно никаких "побитных умножений".
Неужто запрет прерывания на пару команд так влияет на выполнение алгоритма??? Тогда у Вас вероятно неправильно построен алгоритм.

Запрет прерывания на пару команд не повлияет я так думаю, но вычисление разницы двухбайтных чисел с последующим умножением на маску это всё же не 2 операции, с другой стороны как я уже писал, проблема не в этом, а в том, что при каждом таком обращении нужно добавлять запрет и разрешение прерывания, либо оборачивать это в функцию, плюс нужно следить -если изменил прерывание например с АЦП на UART, то нужно переписывать функции чтения переменной, а также чтения/записи в буфер.
А вот сделать чтение двухбайтного указателя атомарной операцией невозможно, поскольку ядро 8-разрядное sm.gif Ну и до кучи объясните как сделать это
Цитата
И для вычисления кол-ва байт достаточно найти разность указателей (с учётом их цикличности), не нужно никаких "побитных умножений".

Допустим есть буфер, он занимает пространство от 0 до 2047, указатель есно двухбайтный, если мы возьмем разницу двух указателей например как "0"-"1" (значит в буфере 2047 байт данных) получим 0xFFFF, хотя должно быть 2047.

Цитата(_Pasha @ Oct 4 2015, 14:27) *
можно проверку делать с учетом неатомарности, поскольку указатели в данном случае readonly

Поясните пожалуйста, что значит readonly если я как читаю указатель, так и пишу его (инкрементирую) wacko.gif
И что делает эта строчка
Код
while(res != *dsc) res = *dsc;
К сожалению не читал обсуждение темы, о которой Вы говорите, если есть ссылочка прошу направить в нужном направлении sm.gif
Go to the top of the page
 
+Quote Post
jcxz
сообщение Oct 5 2015, 04:12
Сообщение #11


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(Alexashka @ Oct 5 2015, 03:53) *
Запрет прерывания на пару команд не повлияет я так думаю, но вычисление разницы двухбайтных чисел с последующим умножением на маску это всё же не 2 операции, с другой стороны как я уже писал, проблема не в этом, а в том, что при каждом таком обращении нужно добавлять запрет и разрешение прерывания, либо оборачивать это в функцию, плюс нужно следить -если изменил прерывание например с АЦП на UART, то нужно переписывать функции чтения переменной, а также чтения/записи в буфер.

ЗАЧЕМ???? wacko.gif

Цитата(Alexashka @ Oct 5 2015, 03:53) *
А вот сделать чтение двухбайтного указателя атомарной операцией невозможно, поскольку ядро 8-разрядное sm.gif Ну и до кучи объясните как сделать это

Сделать чтение двухбайтового числа на 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 назад. Пора обновлять rolleyes.gif
например.

Это то же самое, что просто запрет прерывания на время чтения *dsc. Никакого преимущества не даёт по сранению с обычным запретом прерываний.
По скорости может быть как медленнее так и быстрее запрета - в зависимости от CPU.
Go to the top of the page
 
+Quote Post
Alexashka
сообщение Oct 5 2015, 12:25
Сообщение #12


Практикующий маг
******

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



Цитата(jcxz @ Oct 5 2015, 07:12) *
ЗАЧЕМ???? wacko.gif
... элементарно:
u16 i;
__disable_interrupt();
i = var;
__enable_interrupt();
всё!

<РУКАЛИЦО> Я так и делаю (см.первый пост) и уже дважды объяснил почему мне не нравится этот метод.

Цитата
Если чтение/запись указателей неатомарны, то необходимо сделать их таковыми с помощью например запретов прерываний. В процессе, вызывающем read(), запрет прерываний необходим
при чтении wpos и записи rpos. Для write() - наоборот. Для occupy() и free() - аналогично - запрет прерываний для чтения того указателя, который модифицируется в другом процессе.
Вот об этом и речь -прикиньте, сколько таких мест, где нужно локально блокирова/разрешать прерывание. Можно конечно проще -блокировать прерывание перед вызовом функции read/write/free, но тогда задержка обработки прерывания станет недопустимо большой. Есть у меня идея использовать теневые регистры, которые будут обновляться в прерывании в том случае, если в основном теле к ним не производится обращение, но это уже в другой раз, пока что обошелся локальной блокировкой прерываний.
Посмотрел Ваш код, в принципе у меня примерно также, только не понял зачем Вы копируете глобальные rpos, wpos в локальные ir,iw ведь можно работать непосредственно с глобальными?
Да, к слову, указатели не использую, слишком медленно они обрабатываются в 8051: сначала ф-я чтения по указателю должна определить какую область памяти адресует указатель, а затем перейти к соответствующей команде чтения (там вроде как 4 варианта обращения к памяти и соответственно 4 разные ассемблерные команды) , с массивом такой ерунды не происходит -компилятор уже на этапе компиляции знает к какому типу памяти мы обращаемся поэутому все делается очень быстро.
Go to the top of the page
 
+Quote Post
jcxz
сообщение Oct 5 2015, 12:51
Сообщение #13


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(Alexashka @ Oct 5 2015, 18:25) *
-прикиньте, сколько таких мест, где нужно локально блокирова/разрешать прерывание.

Прикинул. От одного до двух мест в каждой моей функции. Разве это критично?

Цитата(Alexashka @ Oct 5 2015, 18:25) *
Посмотрел Ваш код, в принципе у меня примерно также, только не понял зачем Вы копируете глобальные rpos, wpos в локальные ir,iw ведь можно работать непосредственно с глобальными?

Потому что rpos и wpos объявлены с volatile и если написать i = rpos - wpos, то компилятор выдаст варнинг и будет прав.
К тому-же в варианте с копированием легко сделать нужное чтение атомарным с помощью запрета прерывания или так как предложил _Pasha.

Цитата(Alexashka @ Oct 5 2015, 18:25) *
Да, к слову, указатели не использую, слишком медленно они обрабатываются в 8051:

У меня там и нет указателей.
Go to the top of the page
 
+Quote Post
Сергей Борщ
сообщение Oct 5 2015, 12:53
Сообщение #14


Гуру
******

Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095



Цитата(Alexashka @ Oct 5 2015, 15:25) *
слишком медленно они обрабатываются в 8051: сначала ф-я чтения по указателю должна определить какую область памяти адресует указатель, а затем перейти к соответствующей команде чтения (там вроде как 4 варианта обращения к памяти и соответственно 4 разные ассемблерные команды)
Все три известные мне компилятора для x51 (Кейл, Иар и SDCC) позволяли объявлять указатель на конкретный тип памяти. Такие указатели и меньше места занимают и работают без этого ветвления. Посмотрите документацию на свой компилятор, наверняка они там есть.


--------------------
На любой вопрос даю любой ответ
"Write code that is guaranteed to work, not code that doesn’t seem to break" (C++ FAQ)
Go to the top of the page
 
+Quote Post
jcxz
сообщение Oct 5 2015, 12:58
Сообщение #15


Гуру
******

Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713



Цитата(Alexashka @ Oct 5 2015, 18:25) *
Посмотрел Ваш код, в принципе у меня примерно также, только не понял зачем Вы копируете глобальные rpos, wpos в локальные ir,iw ведь можно работать непосредственно с глобальными?

И что значит "работать с глобальными"? Там где они модифицируются, можно делать только так, как я написал. А не напрямую глобальные.
Если Вы этого не поняли, значит не поняли ничего в моих функциях.
Go to the top of the page
 
+Quote Post

4 страниц V   1 2 3 > » 
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 25th June 2025 - 04:05
Рейтинг@Mail.ru


Страница сгенерированна за 0.01507 секунд с 7
ELECTRONIX ©2004-2016