|
Передача данных, fifo, кольцевой буфер и т.д. |
|
|
|
Dec 12 2016, 15:43
|
Местный
  
Группа: Участник
Сообщений: 491
Регистрация: 18-05-11
Пользователь №: 65 102

|
Добрый день! Мне нужен некий буфер для обмена данными между прерываниями (в одном пишу в буфер, в другом читаю, передаю дальше) с условием, что при чтении данные должны лежать в памяти линейно. Суть в том, что сейчас я пишу в кольцевой буфер данные в прерывании I2S, потом в прерывании USB SOF смотрю есть ли данные, если есть - отправляю. Проблема в том, что библиотечная функция передачи данных по USB требует в качестве параметров адрес начала массива данных и количество данных. А буфер то у меня кольцевой! Подскажите, какие есть еще способы передачи данных? Я всегда кольцевым буфером пользовался, а тут надо чтобы данные лежали линейно в памяти.
|
|
|
|
|
Dec 12 2016, 18:01
|
Группа: Участник
Сообщений: 9
Регистрация: 12-12-16
Пользователь №: 94 601

|
Цитата(Atlantis- @ Dec 12 2016, 16:43)  Проблема в том, что библиотечная функция передачи данных по USB требует в качестве параметров адрес начала массива данных и количество данных. ... надо чтобы данные лежали линейно в памяти. Может, перед вызовом библиотечной функции, копировать данные из кольцевого буфера в линейный?
|
|
|
|
|
Dec 13 2016, 00:59
|
Участник

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

|
Цитата(Atlantis- @ Dec 12 2016, 18:43)  Добрый день! Мне нужен некий буфер для обмена данными между прерываниями (в одном пишу в буфер, в другом читаю, передаю дальше) с условием, что при чтении данные должны лежать в памяти линейно. Суть в том, что сейчас я пишу в кольцевой буфер данные в прерывании I2S, потом в прерывании USB SOF смотрю есть ли данные, если есть - отправляю. Проблема в том, что библиотечная функция передачи данных по USB требует в качестве параметров адрес начала массива данных и количество данных. А буфер то у меня кольцевой! Подскажите, какие есть еще способы передачи данных? Я всегда кольцевым буфером пользовался, а тут надо чтобы данные лежали линейно в памяти. Либо сперва скопировать в линейный буфер, и потом отдать в библиотечную функцию, либо вызывать библиотечную функцию 2 раза: первый -- для ранних данных, второй -- для последних (если организация кольц. буфера позволяет). Если все происходит от прерываний, то возникает вопрос повторной входимости функционала кольцевого буфера, который автоматически снимается, если вся работа с ним происходит в состоянии запрета прерываний
|
|
|
|
|
Dec 13 2016, 04:56
|

Гуру
     
Группа: Свой
Сообщений: 2 429
Регистрация: 30-11-05
Из: Ижевск
Пользователь №: 11 606

|
Два линейных буфера. Один для манипуляций с ним, второй готовый для передачи. Внутри прерывания USB меняете указатель. Примерно вот так: буферы и указатели на них Код volatile int16_t *rx_buf; volatile int16_t *tx_buf; volatile int16_t *tmp_rx_buf; volatile int16_t *tmp_tx_buf;
volatile int16_t rx_buf1[RESAMPELING_SCALE*(FRAME+BUFFER_RES)]; volatile int16_t tx_buf1[RESAMPELING_SCALE*(FRAME+BUFFER_RES)]; volatile int16_t rx_buf2[RESAMPELING_SCALE*(FRAME+BUFFER_RES)]; volatile int16_t tx_buf2[RESAMPELING_SCALE*(FRAME+BUFFER_RES)]; Внутри прерывания: Код if (rx_buf == rx_buf1) { rx_buf = rx_buf2; tx_buf = tx_buf2; tmp_rx_buf = rx_buf1; tmp_tx_buf = tx_buf1; } else { rx_buf = rx_buf1; tx_buf = tx_buf1; tmp_rx_buf = rx_buf2; tmp_tx_buf = tx_buf2; }
|
|
|
|
|
Dec 13 2016, 06:48
|
Местный
  
Группа: Участник
Сообщений: 491
Регистрация: 18-05-11
Пользователь №: 65 102

|
Цитата(G.Simenon @ Dec 12 2016, 21:01)  Может, перед вызовом библиотечной функции, копировать данные из кольцевого буфера в линейный? я в данный момент так и делаю, но хочется какой то более красивый вариант
|
|
|
|
|
Dec 13 2016, 08:32
|
Местный
  
Группа: Участник
Сообщений: 491
Регистрация: 18-05-11
Пользователь №: 65 102

|
Цитата(Valentine Loginov @ Dec 13 2016, 10:19)  Вам предложили работать с несколькими линейными массивами и оперировать индексами массива. я понял, в один массив писать, из другого читать. только тут есть нюанс, что теоретически (да и практически бывает) функция отправки по USB может не вернуть OK, то есть надо данные отправить в следующий раз. в случае с двумя массивами, как минимум перепутается последовательность сообщений, хотя наверно можно не переключать массивы в случае если данные не были отправлены... Цитата(Valentine Loginov @ Dec 13 2016, 10:19)  Более конкретно про различные методики можно почитать у Таненбаума в "Современные операционные системы", раздел 5.3.3 (Программное обеспечение ввода-вывода, не зависящее от конкретных устройств). Спасибо
|
|
|
|
|
Dec 14 2016, 12:40
|
Местный
  
Группа: Участник
Сообщений: 491
Регистрация: 18-05-11
Пользователь №: 65 102

|
Цитата(uriy @ Dec 14 2016, 07:50)  Мютексы. Не совсем понял, как их использовать в моем случае?
Сообщение отредактировал Atlantis- - Dec 14 2016, 13:35
|
|
|
|
|
Dec 14 2016, 18:19
|

Гуру
     
Группа: Свой
Сообщений: 2 957
Регистрация: 19-09-06
Из: Москва
Пользователь №: 20 514

|
Цитата(Atlantis- @ Dec 14 2016, 15:40)  Не совсем понял, как их использовать в моем случае? то есть как? функция, которая пишет в буфер, все используют только ее в начале ждем мютекс, в конце отпускаем если функция вызывается и мютекс занят (кто-то уже вызвал функцию), ОС будет держать вызывающую функцию в ожидании, пока мютекс не освободится (ранее вызвавший функцию записи в буфер закончил работу) типа так Код int write_buf(uint8_t *buf, int len) { os_mut_wait(bufwr_mutex, 0xFFFF); // тут пишем в буфер
os_mut_release(bufwr_mutex); return len; }
|
|
|
|
|
Dec 14 2016, 22:44
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Atlantis- @ Dec 12 2016, 17:43)  Мне нужен некий буфер для обмена данными между прерываниями (в одном пишу в буфер, в другом читаю, передаю дальше) с условием, что при чтении данные должны лежать в памяти линейно. Элементарно. Организовать кольцевой буфер не из байтов, а из блоков данных типового размера.Если блоки данных произвольного размера, то добавить дополнительный указатель позволяющий помечать и соответственно пропускать в конце блока памяти выделенного под кольцевой буфер блок, размер которого недостаточен для размещения полученного блока данных.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Dec 15 2016, 07:35
|
Местный
  
Группа: Участник
Сообщений: 491
Регистрация: 18-05-11
Пользователь №: 65 102

|
Цитата(zltigo @ Dec 15 2016, 01:44)  Элементарно. Организовать кольцевой буфер не из байтов, а из блоков данных типового размера. Эмм...и что будет? У меня сейчас кольцевой буфер 64 байта, а блоки данных 8 байт. Это не мешает одному блоку данных находиться в конце буфера, а второму - в начале. Цитата(Сергей Борщ @ Dec 15 2016, 09:46)  Простите, а что мешает при переходе через конец буфера первой посылкой отправлять данные от текущего положения до конца буфера, а по следующему запросу отдавать уже от начала буфера? Я так в УАСПП через ПДП отправляю и VCP у меня так же работает. Я сначала отмел этот вариант потому что скорость передачи данных по Interrupt каналу и так всего 64 байта за 1 мс. Потом прикинул, вроде должно хватать, получается это более выгодный вариант, чем копировать в линейный буфер. Но меня сейчас больше волнует, как писать в кольцевой буфер из основного потока и из прерывания одновременно. Цитата(toweroff @ Dec 14 2016, 21:19)  то есть как? функция, которая пишет в буфер, все используют только ее
в начале ждем мютекс, в конце отпускаем
если функция вызывается и мютекс занят (кто-то уже вызвал функцию), ОС будет держать вызывающую функцию в ожидании, пока мютекс не освободится (ранее вызвавший функцию записи в буфер закончил работу) Это подразумевает, что пишущий поток должен отложить свою работу на потом. У меня предполагается, что в кольцевой буфер будут писаться сообщения из прерывания I2S о начале/конце выдаваемого блока данных и сообщения из основного потока о нажатии кнопок. То есть очень вероятен вариант, что я буду писать сообщение в буфер из основного потока, в этот момент вылетит прерывание и данные смешаются. Тут я вроде бы должен заблокировать запись в буфер из прерывания, но когда прерывание снова сработает снова - мне надо будет посылать еще одно сообщение (первое - о конце одного блока данных, второе - о начале другого блока). То есть получается надо внутри прерывания I2S буферизировать сообщения...
Сообщение отредактировал Atlantis- - Dec 15 2016, 07:57
|
|
|
|
|
Dec 15 2016, 08:28
|
Частый гость
 
Группа: Участник
Сообщений: 78
Регистрация: 7-04-10
Из: Пушкино
Пользователь №: 56 462

|
Цитата(Atlantis- @ Dec 15 2016, 10:35)  как писать в кольцевой буфер из основного потока и из прерывания одновременно. Запрещать прерывание во время работы с буфером из основного приложения. Но это некрасиво. Цитата(Atlantis- @ Dec 15 2016, 10:35)  То есть получается надо внутри прерывания I2S буферизировать сообщения... Разделите свой драйвер I2S на две части: обработчик прерывания и фоновый обработчик на уровне приложения. Фоновый обработчик драйвера I2S будет через небольшой буфер получать данные из обработчика прерывания, а затем формировать и передавать сообщения в вашу общую очередь, доступ к которой вы осуществляете мьютексом. Это позволит вам не блокировать прерывания (не терять данные) и разграничить доступ. Однако следует корректно рассчитать размеры буферов и очередей, а также времена блокировки обработчиков во время обращения за доступом к мьютексу.
|
|
|
|
|
Dec 15 2016, 11:52
|
Местный
  
Группа: Участник
Сообщений: 491
Регистрация: 18-05-11
Пользователь №: 65 102

|
Цитата(Valentine Loginov @ Dec 15 2016, 11:28)  Запрещать прерывание во время работы с буфером из основного приложения. Но это некрасиво. Джиттер будет если надолго запрещать Цитата(Valentine Loginov @ Dec 15 2016, 11:28)  Разделите свой драйвер I2S на две части: обработчик прерывания и фоновый обработчик на уровне приложения. Фоновый обработчик драйвера I2S будет через небольшой буфер получать данные из обработчика прерывания, а затем формировать и передавать сообщения в вашу общую очередь, доступ к которой вы осуществляете мьютексом. Это позволит вам не блокировать прерывания (не терять данные) и разграничить доступ. Однако следует корректно рассчитать размеры буферов и очередей, а также времена блокировки обработчиков во время обращения за доступом к мьютексу. Я хотел так сначала разделить - чтобы прерывание I2S только отправляло данные и обрабатывало флаги, а расчет следующих данных для отправки и всего остального вести в специальной задаче, которую вызывать из прерывания I2S. Но I2S у меня работает на частоте 48 кГц, то есть период 21 мкс, прерывание I2S обрабатывается у меня за 3 мкс, а вызов задачи FreeRTOS занимает 4 мкс! Поэтому я решил все эти дела делать в прерывании, даже для сообщений кольцевой буфер сделал, хотя мог просто очередь создать и писать в нее хоть из прерываний, хоть из основного потока одновременно. Но писать в очередь в прерывании I2S это плюс 4 мкс к времени его обработки, то есть в сумме 7 мкс, а это уже треть от 21...в общем показалось мне многовато, поэтому и пытаюсь что то изобрести.
|
|
|
|
|
Dec 15 2016, 13:08
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Atlantis- @ Dec 15 2016, 09:35)  Эмм...и что будет? У меня сейчас кольцевой буфер 64 байта, а блоки данных 8 байт. Это не мешает одному блоку данных находиться в конце буфера, а второму - в начале. Не понял о чем Вы написали, и в чем тогда проблема, но 8 байтовые блоки укладываются в 64 байта без перехода за границу блока. Если хотите оперировать (например передавать) большими блоками, то тогда и буфер должен состоять из блоков такого размера
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Dec 15 2016, 13:26
|
Местный
  
Группа: Участник
Сообщений: 491
Регистрация: 18-05-11
Пользователь №: 65 102

|
Цитата(zltigo @ Dec 15 2016, 16:08)  Не понял о чем Вы написали, и в чем тогда проблема, но 8 байтовые блоки укладываются в 64 байта без перехода за границу блока. Если хотите оперировать (например передавать) большими блоками, то тогда и буфер должен состоять из блоков такого размера Допустим я в этот буфер записал и считал 7 таких сообщений, указатель записи у меня равен 56. И вот пишу я одно сообщение (указатель записи = 0), а потом второе (указатель записи равен 8). После этого возникает прерывание на чтение данных, а данные в памяти расположены прерывисто. И я не могу после передачи данных сбрасывать указатель записи в ноль, потому что у прерывания, которое пишет в буфер приоритет выше, чем у прерывания, которое из буфера читает.
|
|
|
|
|
Dec 15 2016, 13:30
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Atlantis- @ Dec 15 2016, 15:26)  Допустим я в этот буфер записал и считал 7 таких сообщений, указатель записи у меня равен 56. И вот пишу я одно сообщение (указатель записи = 0), а потом второе (указатель записи равен 8). После этого возникает прерывание на чтение данных, а данные в памяти расположены прерывисто. И я не могу после передачи данных сбрасывать указатель записи в ноль, потому что у прерывания, которое пишет в буфер приоритет выше, чем у прерывания, которое из буфера читает. Читаем внимательно: Если хотите оперировать (например передавать) большими блоками, то тогда и буфер должен состоять из блоков такого размера
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Dec 15 2016, 14:03
|
Местный
  
Группа: Участник
Сообщений: 491
Регистрация: 18-05-11
Пользователь №: 65 102

|
Цитата(AlexandrY @ Dec 15 2016, 16:24)  Выглядит не очень разумно каждый сэмпл на I2S делать прерывание. Согласен, но я не нашел другого способа. Мне нужно выдавать в I2S данные из памяти, а потом делать паузу (слать нули). Данные я могу через DMA слать, а паузу уже нет - она может быть большой, памяти не хватит. Цитата(AlexandrY @ Dec 15 2016, 16:24)  Но даже так, 3 мкс на прерывание в котором только пересылка это подозрительно много. Может прерывание RTOS используете? Почему много? У меня там куча условных операторов, которые контролируют разные счетчики и данные читаются из внешней памяти SDRAM. Хотя я измерил сколько времени идет на запись 8 байт в буфер и инкремент счетчика записи - получилось 1,3 мкс. Прерывание RTOS использую, его я тоже замерил - обрабатывается за 2,5 мкс. Но его приоритет - самый низкий. Цитата(zltigo @ Dec 15 2016, 16:30)  Читаем внимательно: Если хотите оперировать (например передавать) большими блоками, то тогда и буфер должен состоять из блоков такого размера Не понял, что Вы имеете ввиду? Если я хочу передавать блоками максимум по 64 байта, значит мне сделать буфер состоящий из блоков по 64 байта каждый? Не очень представляю как с ним работать.
Сообщение отредактировал Atlantis- - Dec 15 2016, 14:07
|
|
|
|
|
Dec 16 2016, 08:23
|
Частый гость
 
Группа: Участник
Сообщений: 78
Регистрация: 7-04-10
Из: Пушкино
Пользователь №: 56 462

|
Цитата(Atlantis- @ Dec 15 2016, 14:52)  Я хотел так сначала разделить - чтобы прерывание I2S только отправляло данные и обрабатывало флаги, а расчет следующих данных для отправки и всего остального вести в специальной задаче, которую вызывать из прерывания I2S. Но I2S у меня работает на частоте 48 кГц, то есть период 21 мкс, прерывание I2S обрабатывается у меня за 3 мкс, а вызов задачи FreeRTOS занимает 4 мкс! Поэтому я решил все эти дела делать в прерывании, даже для сообщений кольцевой буфер сделал, хотя мог просто очередь создать и писать в нее хоть из прерываний, хоть из основного потока одновременно. Но писать в очередь в прерывании I2S это плюс 4 мкс к времени его обработки, то есть в сумме 7 мкс, а это уже треть от 21...в общем показалось мне многовато, поэтому и пытаюсь что то изобрести. Тут вам в помощь счётный семафор. Не обязательно же чтобы на каждый вызов прерывания немедленно запускалась задача обработки, главное чтобы данные не потерялись. Подозрительно большие числа по временам, особенно на переключение контекста FreeRTOS, это сколько же у вас там задач? Было бы легче, если бы была информация о камне и его тактировании. Те же прерывания нужно оптимизировать грамотными аттрибутами на инлайнинг, оптимизацию и пр.
|
|
|
|
|
Dec 16 2016, 08:45
|

Гуру
     
Группа: Свой
Сообщений: 13 372
Регистрация: 27-11-04
Из: Riga, Latvia
Пользователь №: 1 244

|
Цитата(Atlantis- @ Dec 15 2016, 16:03)  Не понял, что Вы имеете ввиду? Если я хочу передавать блоками максимум по 64 байта, значит мне сделать буфер состоящий из блоков по 64 байта каждый? Не очень представляю как с ним работать. Так же, как и с буфером из какого то количества байт, только вместо одного байта - 64 и указатель указывает не на байт в буфере, а на блок.
--------------------
Feci, quod potui, faciant meliora potentes
|
|
|
|
|
Dec 16 2016, 09:58
|

Ally
     
Группа: Модераторы
Сообщений: 6 232
Регистрация: 19-01-05
Пользователь №: 2 050

|
Цитата(Atlantis- @ Dec 15 2016, 16:03)  Согласен, но я не нашел другого способа. Мне нужно выдавать в I2S данные из памяти, а потом делать паузу (слать нули). Данные я могу через DMA слать, а паузу уже нет - она может быть большой, памяти не хватит. Для этого в STM32 есть Double buffer mode у DMA, в Kinetis это еще красивей можно сделать. Проверенно. Параллельно еще есть уйма времени читать SD карту и поддерживать несколько потоков по USB. Для пауз буфера заполняются нулями за время на порядки меньше чем время воспроизведения буфера. Эстеты и нулями могут по DMA заполнять. Короче, в прерываниях от I2S я ни разу не видел необходимости. Т.е. тема прерываний I2S вами ни разу не раскрыта.
|
|
|
|
|
Dec 16 2016, 14:31
|
Местный
  
Группа: Участник
Сообщений: 491
Регистрация: 18-05-11
Пользователь №: 65 102

|
Цитата(Valentine Loginov @ Dec 16 2016, 11:23)  Тут вам в помощь счётный семафор. Не обязательно же чтобы на каждый вызов прерывания немедленно запускалась задача обработки, главное чтобы данные не потерялись.
Подозрительно большие числа по временам, особенно на переключение контекста FreeRTOS, это сколько же у вас там задач? Было бы легче, если бы была информация о камне и его тактировании. Те же прерывания нужно оптимизировать грамотными аттрибутами на инлайнинг, оптимизацию и пр. 6 задач, но 5 обычно в ждущем состоянии (ждут сообщений в своих очередях). Может я как то не так меряю, я просто вначале обработчика прерывания выдаю на тестовой ноге единичку, а в конце - ноль. И измеряю осциллографом.
|
|
|
|
|
Dec 17 2016, 10:51
|
Гуру
     
Группа: Свой
Сообщений: 5 228
Регистрация: 3-07-08
Из: Омск
Пользователь №: 38 713

|
Цитата(AlexandrY @ Dec 14 2016, 11:47)  Вы наверно перепутали с Linked List DMA или Linked DMA как в Gecko. Нет, в Kinetis это нечто иное. Смотрите мануал. Я имел в виду именно "Linked List" - свЯзные списки. В LPC. В Tiva тоже подобный есть. С EFM32 не имею опыта. Цитата(Сергей Борщ @ Dec 15 2016, 09:46)  Простите, а что мешает при переходе через конец буфера первой посылкой отправлять данные от текущего положения до конца буфера, а по следующему запросу отдавать уже от начала буфера? Я так в УАСПП через ПДП отправляю и VCP у меня так же работает. Я так тоже делаю. Но тут надо умнее рассчитывать размер очередной передачи. Чтобы не оказалось, что в очередной передаче нужно передать всего один байт.  Цитата(Valentine Loginov @ Dec 15 2016, 11:28)  Запрещать прерывание во время работы с буфером из основного приложения. Но это некрасиво. Если хочется, то можно и без запрета прерываний. Как тут уже предложил zltigo: иметь кольцевой буфер не в виде просто массива байт, а в виде массива указателей на блоки с массивами байт. Функция, которая хочет записать данные в такой буфер, из некоего пула свободных блоков получает блок, записывает свои данные туда, формирует заголовок блока и помещает этот блок в кольцевую очередь при помощи LDREX/STREX. Я обычно так строю очереди для межпоточных передач данных под виндой. Без семафоров и критических секций, на семействе функций InterlockedExchange...(). Цитата(AlexandrY @ Dec 15 2016, 16:24)  Выглядит не очень разумно каждый сэмпл на I2S делать прерывание. Но даже так, 3 мкс на прерывание в котором только пересылка это подозрительно много. Может прерывание RTOS используете? К гадалке не ходи - на каждое прерывание дёргает отправку сообщений какому-то объекту синхронизации ОС . Совершенно безграмотная организация работы! На пустом месте тратится огромная часть времени процессора. Какой смысл дёргать ОС так часто???
|
|
|
|
|
Dec 26 2016, 07:18
|
Местный
  
Группа: Участник
Сообщений: 491
Регистрация: 18-05-11
Пользователь №: 65 102

|
Цитата(jcxz @ Dec 17 2016, 13:51)  Совершенно безграмотная организация работы! На пустом месте тратится огромная часть времени процессора. Какой смысл дёргать ОС так часто??? Например, чтобы поймать в коде ноль и переключить в этот момент громкость. Чтобы в нужный момент (нужный USB фрейм и смещение в отсчетах ЦАП) запустить выдачу массива данных Иногда чтобы посчитать фильтрованные значения
|
|
|
|
|
Dec 26 2016, 11:16
|
Местный
  
Группа: Участник
Сообщений: 491
Регистрация: 18-05-11
Пользователь №: 65 102

|
Цитата(AlexandrY @ Dec 26 2016, 12:39)  Смысл переключать громкость так оперативно? Это уже модуляция получается. а чего ждать? паузы? можно долго ждать, памяти много, секунд на 20 Цитата(AlexandrY @ Dec 26 2016, 12:39)  Ну и остально все надуманно. Все это прекрасно делается на буферах. может быть, я не думал над этим вариантом
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|