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

 
 
2 страниц V   1 2 >  
Reply to this topicStart new topic
> Закольцевать данные, программа на Си
Dubov
сообщение Jun 3 2014, 05:09
Сообщение #1


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Имеется буфер длины N
имеется подпрограмма, принимающая на вход указатель на блок данных длины N.

Буфер заполняется циклически (по заполнении буфера, новый отсчёт поступает в начало буфера).

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

Как закольцевать адресацию массива?

Например, может получится ситуация когда указатель находится в произвольном месте массива:

| (a) ( b ) (PTR) (...) (N) |

тогда подпрограмма должна "знать", что она оперирует с массивом | (PTR) (...) (N) (a) ( b ) |

Сообщение отредактировал Dubov - Jun 3 2014, 05:10
Go to the top of the page
 
+Quote Post
menzoda
сообщение Jun 3 2014, 05:59
Сообщение #2


Участник
*

Группа: Участник
Сообщений: 55
Регистрация: 13-09-12
Пользователь №: 73 530



Вместе с указателем на данные передавать указатель на начало и конец массива?
Go to the top of the page
 
+Quote Post
mempfis_
сообщение Jun 3 2014, 06:02
Сообщение #3


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

Группа: Свой
Сообщений: 1 001
Регистрация: 27-06-06
Пользователь №: 18 409



Цитата(Dubov @ Jun 3 2014, 12:19) *
Имеется буфер длины N
имеется подпрограмма, принимающая на вход указатель на блок данных длины N.
Буфер заполняется циклически (по заполнении буфера, новый отсчёт поступает в начало буфера).
с каждым новым вызовом подпрограммы, получаемый указатель увеличивается на единицу.
Как закольцевать адресацию массива?


Может быть Вам стоит поступить немного иначе - организовать 2 функции putArray(data) и getarray()
putArray(data) закидывает данные в буффер, контролируя при этом переход через границу буффера и смещая позицию head (позицию куда необходимо закидывать следующие данные)
Код
void putarray(short data)
{
buff[head] = data;
head++;
if(head >= BUFF_SIZE) head = 0; //переход через границу буффера
}


Функция getArray() возвращает или-1 если нет данных, или данные
Код
signed int getArray(void)
{
signed int res = -1;
if(head == tail) return res; //нет данных - возвращаем -1
res = buff[tail];
tail++;
if(tail >= BUFF_SIZE) tail = 0;
return res; //возвращаем данные

}


Эти 2 процедуры позволяют организовать FIFO данных без проверки переполнения буффера.
Если Вам критично отслеживать переполнение, то всегда можно ввести переменную status, которой можно присваивать статусы empty/not_empty/full
Также довольно легко вместо данных, возвращать указатели на данные.
Go to the top of the page
 
+Quote Post
Dubov
сообщение Jun 3 2014, 06:38
Сообщение #4


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Цитата(menzoda @ Jun 3 2014, 14:09) *
Вместе с указателем на данные передавать указатель на начало и конец массива?

допустим, функция кроме указателя на начало данных получила указатель на начало и конец массива. Как это обработать на Си, чтобы за концом массива, брались данные из начала?

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

Как то так:

j = count;

for(i = 0; i <= N-1; i++)
{
array_dest[i] = array_src[j];
j++;
if(j == N)
j = 0;
}



где count = это положение самого "свежего отсчёта" в массиве array_src. таким образом, мы получим в массиве array_dest всегда данные начиная с самого свежего элемента исходного массива.

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

Цитата(mempfis_ @ Jun 3 2014, 14:12) *
Функция getArray() возвращает или-1 если нет данных, или данные
Код
signed int getArray(void)
{
signed int res = -1;
if(head == tail) return res; //нет данных - возвращаем -1
res = buff[tail];
tail++;
if(tail >= BUFF_SIZE) tail = 0;
return res; //возвращаем данные

}


Эти 2 процедуры позволяют организовать FIFO данных без проверки переполнения буффера.
Если Вам критично отслеживать переполнение, то всегда можно ввести переменную status, которой можно присваивать статусы empty/not_empty/full
Также довольно легко вместо данных, возвращать указатели на данные.


не понимаю, ну получил я указатель на данные в буфере. Функция обработки берёт по указателю N байт (по сути весь массив с началом в указателе) и если указатель пришёлся на конец буфера, то функция возьмёт данные где-то пределами массива.
В идеале мне нужна циклическая адресация, чтобы за концом массива шло начало в адресном пространстве.

rolleyes.gif

Сообщение отредактировал Dubov - Jun 3 2014, 07:23
Go to the top of the page
 
+Quote Post
megajohn
сообщение Jun 3 2014, 06:42
Сообщение #5


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

Группа: Свой
Сообщений: 1 080
Регистрация: 16-11-04
Из: СПб
Пользователь №: 1 143



Цитата(Dubov @ Jun 3 2014, 14:48) *
В идеале мне нужна циклическая адресация, чтобы за концом массива шло начало в адресном пространстве.


ну дык расположите буфер к примеру по адресу 0x1000, размер буфера кратный 2^n и обращайтесь по маске


--------------------
Марс - единственная планета, полностью населенная роботами (около 7 штук).
Go to the top of the page
 
+Quote Post
Dubov
сообщение Jun 3 2014, 06:47
Сообщение #6


Местный
***

Группа: Участник
Сообщений: 408
Регистрация: 28-05-12
Пользователь №: 72 052



Цитата(megajohn @ Jun 3 2014, 14:52) *
ну дык расположите буфер к примеру по адресу 0x1000, размер буфера кратный 2^n и обращайтесь по маске

прошу прощения, можно по-подробнее об этом методе. А лучше ссылку на код (совсем наглость с моей стороны) rolleyes.gif rolleyes.gif

Сообщение отредактировал Dubov - Jun 3 2014, 06:54
Go to the top of the page
 
+Quote Post
MaxiMuz
сообщение Jun 3 2014, 07:30
Сообщение #7


Местный
***

Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658



Цитата(mempfis_ @ Jun 3 2014, 13:12) *
Функция getArray() возвращает или-1 если нет данных, или данные
Код
signed int getArray(void)
{
signed int res = -1;
if(head == tail) return res; //нет данных - возвращаем -1
res = buff[tail];
tail++;
if(tail >= BUFF_SIZE) tail = 0;
return res; //возвращаем данные
}

Только в этой функции, я думаю , не увеличение tail а набоборот:
Код
signed int getArray(void)
{
signed int res = -1;
if(head == 0) return res; //нет данных - возвращаем -1
res = buff[tail];
tail--;
return res; //возвращаем данные
}
Go to the top of the page
 
+Quote Post
Voldemari4
сообщение Jun 3 2014, 07:43
Сообщение #8


Участник
*

Группа: Участник
Сообщений: 66
Регистрация: 5-05-14
Из: Минск
Пользователь №: 81 582



Можно сделать указатель типом BYTE. Если размер массива 256 байт, то после значения указателя 255 автоматически следующим будет 0.
Go to the top of the page
 
+Quote Post
_pv
сообщение Jun 3 2014, 07:43
Сообщение #9


Гуру
******

Группа: Свой
Сообщений: 2 563
Регистрация: 8-04-05
Из: Nsk
Пользователь №: 3 954



Цитата(Dubov @ Jun 3 2014, 17:57) *
прошу прощения, можно по-подробнее об этом методе. А лучше ссылку на код (совсем наглость с моей стороны) rolleyes.gif rolleyes.gif


data[i & (size-1)] = 0;
size дожен быть степенью двойки. при занулении старших разрядов автоматически получается закольцовывание массива.


но ничем это не лучше дополнительных проверок на выходы за границы массива.

Код
void func(double * data, int size, int i0, int num){
  while (num--){
    while (i0 >= size) i0 -= size;
    process(data[i0++]);
  }
}

num может быть даже в несколько раз больше size, при этом оно пройдётся по массиву несколько раз.
Go to the top of the page
 
+Quote Post
megajohn
сообщение Jun 3 2014, 07:50
Сообщение #10


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

Группа: Свой
Сообщений: 1 080
Регистрация: 16-11-04
Из: СПб
Пользователь №: 1 143



Цитата(Dubov @ Jun 3 2014, 14:57) *
прошу прощения, можно по-подробнее об этом методе. А лучше ссылку на код (совсем наглость с моей стороны) rolleyes.gif rolleyes.gif


к примеру ваш буфер размещен по адресу #define BUFF_START 0x1000 и занимает #define BUFF_SIZE 512

void buff_add( u8* ptr )
{
*ptr++ = get_data();
ptr = (u8*) ( (int)ptr & ( BUFF_SIZE - 1 ) + BUFF_START ); // получится типо ptr = (u8*) ( (int)ptr & 0x1FF + BUFF_START );
}


p.s. на скорую руку набросал.


--------------------
Марс - единственная планета, полностью населенная роботами (около 7 штук).
Go to the top of the page
 
+Quote Post
mempfis_
сообщение Jun 3 2014, 08:13
Сообщение #11


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

Группа: Свой
Сообщений: 1 001
Регистрация: 27-06-06
Пользователь №: 18 409



Цитата(MaxiMuz @ Jun 3 2014, 14:40) *
Только в этой функции, я думаю , не увеличение tail а набоборот:
Код
signed int getArray(void)
{
signed int res = -1;
if(head == 0) return res; //нет данных - возвращаем -1
res = buff[tail];
tail++;
return res; //возвращаем данные
}


Исходный код правильный - это кольцевой буффер FIFO.
По позици head ложим данные с инкрементом head.
Забираем данные по позиции tail с инкрементом tail.
При закидывании данных голова убегает от хвоста.
При извлечении - хвост догоняет голову.

Если Т.С. нужен кольцевой буффер с доступом по указателю, то megajohn уже подсказал решение.


Позволю себе также пояснить то, что предложил megajohn

Предположим у Вас есть буффер char-элементов buff расположенный по адресу 0x1000 и размер BUFF_SIZE = 256 байт
Тогда Ваш буффер будет занимать диапазон адресов 0xFF1000 - 0x001000
Маска для этого диапазона адресов ADDR_MSK = 0xFF<<12

Теперь мы хотим считать с любой позиции буффера через указатель 256 байт, но так, чтобы при достижении границы буффера адрес автоматически перешёл в начало
Код
unsigned char* pbuff = &buff[255];
for(unsigned int i=0; i<BUFF_SIZE; i++)
{
*((pbuff+i)&ADDR_MSK);
}


Сначала адрес pbuff+i = 0xff1000+0
Читаем с этого адреса значение из ячейки buff[255]
Далее инкремен i и адрес становится pbuff+i=0xff1000+1=0x1001000
Но мы накладываем маску адресов 0xff<<12 и получаем адрес 0x100010000&(0xff<<12) = 0x001000;
Т.е. новый адрес соответсвует ячейке buff[0];
при i == 1 адрес будет 0x1011000&(0xff<<12) = 0x001000
и так далее.
Go to the top of the page
 
+Quote Post
menzoda
сообщение Jun 3 2014, 08:18
Сообщение #12


Участник
*

Группа: Участник
Сообщений: 55
Регистрация: 13-09-12
Пользователь №: 73 530



Цитата(Dubov @ Jun 3 2014, 14:48) *
допустим, функция кроме указателя на начало данных получила указатель на начало и конец массива. Как это обработать на Си, чтобы за концом массива, брались данные из начала?

Так и обрабатывать. В цикле проверять не дошел ли текущий указатель до конца массива, если да, то сдвигаем его в начало массива.
Код
void foo(int *ptr, int count, int *start, int *end)
{
    while(count-- > 0)
    {
        if (ptr >= end)
        {
            ptr = start;
        }

        do_something_with(ptr);    
        ptr++;            
    }
}


Цитата(Dubov @ Jun 3 2014, 14:48) *
Такой подход самый тупой и, что называется, в лоб. Мне думается, что он будет самым затратным из возможных, и для реалтаймовых задач нерациональным.

Собственно других волшебных методов нет. Разве что использовать размер массива равный степени двойки. Тогда можно будет заменить условие на побитовую операцию и присвоение, но даже если это и даст выигрыш, то всего в пару тактов, что будет каплей в море.
Go to the top of the page
 
+Quote Post
mdmitry
сообщение Jun 3 2014, 08:29
Сообщение #13


Начинающий профессионал
*****

Группа: Свой
Сообщений: 1 215
Регистрация: 25-10-06
Из: СПб
Пользователь №: 21 648



Цитата(Dubov @ Jun 3 2014, 14:57) *
прошу прощения, можно по-подробнее об этом методе. А лучше ссылку на код (совсем наглость с моей стороны) rolleyes.gif rolleyes.gif

В коде scmrtos есть кольцевой буфер (файл usrlib.h), на писан на c++, легко адаптируется.


--------------------
Наука изощряет ум; ученье вострит память. Козьма Прутков
Go to the top of the page
 
+Quote Post
MaxiMuz
сообщение Jun 5 2014, 08:15
Сообщение #14


Местный
***

Группа: Участник
Сообщений: 253
Регистрация: 15-04-10
Из: Волгоград
Пользователь №: 56 658



Цитата(mempfis_ @ Jun 3 2014, 15:23) *
Тогда Ваш буффер будет занимать диапазон адресов 0xFF1000 - 0x001000
Маска для этого диапазона адресов ADDR_MSK = 0xFF<<12

Принцип понял, но непонятен механизм.
Цитата(mempfis_ @ Jun 3 2014, 15:23) *
Сначала адрес pbuff+i = 0xff1000+0
Читаем с этого адреса значение из ячейки buff[255]
Далее инкремен i и адрес становится pbuff+i=0xff1000+1=0x1001000
Но мы накладываем маску адресов 0xff<<12 и получаем адрес 0x100010000&(0xff<<12) = 0x001000;
Т.е. новый адрес соответсвует ячейке buff[0];
при i == 1 адрес будет 0x1011000&(0xff<<12) = 0x001000
и так далее.

i=0 ,получаем адрес ((pbuff+i)&ADDR_MSK) = (0х00001000+0)&0x000FF000 = 0х00001000
i=1 : ((pbuff+i)&ADDR_MSK) = (0х00001000+1)&0x000FF000 = 0х00001000
тот же самый адрес, или я чего то не так понял ?
Go to the top of the page
 
+Quote Post
megajohn
сообщение Jun 5 2014, 08:24
Сообщение #15


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

Группа: Свой
Сообщений: 1 080
Регистрация: 16-11-04
Из: СПб
Пользователь №: 1 143



Цитата(MaxiMuz @ Jun 5 2014, 16:25) *
тот же самый адрес, или я чего то не так понял ?


автар перепутал байт
вместо "Тогда Ваш буффер будет занимать диапазон адресов 0xFF1000 - 0x001000"
читать "Тогда Ваш буффер будет занимать диапазон адресов 0x0010FF - 0x001000"

и далее, сам же запутался (или HumanEndian путает с HardwareEndian ) =)


--------------------
Марс - единственная планета, полностью населенная роботами (около 7 штук).
Go to the top of the page
 
+Quote Post

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

 


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


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