jcxz 243 10 апреля, 2023 Опубликовано 10 апреля, 2023 · Жалоба 54 минуты назад, artemkad сказал: Вас смущает дробность делителя аппаратного UART? Ну пусть будет 0.92МГц тактовой или скорость 128000. Вы искусственно придумываете крайние условия и заведомо предполагаете кривую реализацию системы. Да - умеючи можно какую угодно работу завалить. При таких жёстких условиях (80 тактов на символ), очевидно что в системе разгребающей такой поток должно быть хотя бы одно из: 1. FIFO в UART. 2. UART читается через DMA. 3. ISR читающий из UART и ISR парсящий строку - разные ISR, на разных уровнях приоритета. 54 минуты назад, artemkad сказал: Не уложится. Только организация цикла со счетчиком это примерно в 5 операций или иначе говоря 1мс хватит тупо на 200 проходов пустого цикла. На сравнение и остальную логику - хорошо если поиск за 10мс управится. Про какое именно ядро речь? Если про Cortex-M, то "сравнение и остальная логика" на >= CM3 - это ещё 3 такта. А если у программиста есть голова на плечах и условия работы реально настолько жёсткие, он сделает N сравнений за проход цикла. И размажет ваши 5 тактов на организацию цикла на несколько сравниваемых образцов. Получив 4 такта на сравнение с одним образцом (его первыми 4 байтами). А если голова у него реально варит, то он например: одновременно с приёмом символов в строку посчитает их хеш (CRC32 например) и искать будет по таблице хешей (таблица хешей всех возможных лексем). Тратя всего чуть больше 3 тактов на сравнение с одним хешем из таблицы. Итого: округлим с запасом 3 такта с хвостиком до 4 тактов, получим: 4*200=800 тактов на поиск по всей таблице. Т.е. - вполне укладываемся в 1000 тактов. Ещё и время на перекур остаётся. А если программёр настолько крут, что знает не только про линейный поиск, но способен и к другим алгоритмам быстрого поиска, то возможно время перекура будет ещё больше. ADR R0, hashTable LDMIA R0!, {R2-R12,LR} CMP R1, R2 BEQ found_00 CMP R1, R3 BEQ found_01 ... CMP R1, LR BEQ found_11 LDMIA R0!, {R2-R12,LR} CMP R1, R2 BEQ found_00 CMP R1, R3 BEQ found_01 ... CMP R1, LR BEQ found_11 ... B not_found found_00: SUBS R0, R0, #4 found_01: SUBS R0, R0, #4 ... found_10: SUBS R0, R0, #4 found_11: ADR R1, hashTable + 4 SUBS R0, R0, R1 ;R0 = результат Как видно - всего чуть больше 3 тактов на сравнение с одним хешем. И это тупой линейный поиск. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 10 апреля, 2023 Опубликовано 10 апреля, 2023 · Жалоба 1 час назад, artemkad сказал: Это вообще не к нормальной организации алгоритма, а требование к аппаратной части МК. Куча контроллеров на подобное не способны. Это требование не к аппаратной части МК, а к выбору аппаратной платформы для решения поставленной задачи. PS: И выбор программиста, способного решить поставленную задачу - тоже из этой же оперы. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xvr 12 10 апреля, 2023 Опубликовано 10 апреля, 2023 · Жалоба 18 hours ago, MementoMori said: Там ведь как - если приходит 0x00, то это считается концом строки. А если в структуре есть байт 0x00? Как быть? Как синхронизировать начало посылки? А вот и парсинг подоспел. Пошёл я за попкорном Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
artemkad 92 10 апреля, 2023 Опубликовано 10 апреля, 2023 · Жалоба 2 часа назад, jcxz сказал: 1. FIFO в UART. Почти нигде аппаратного нет или он маленький 1-8 байт. 2 часа назад, jcxz сказал: 2. UART читается через DMA. И что это даст при приеме изначально неизвестного числа байт? Правильно - ничего. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 86 10 апреля, 2023 Опубликовано 10 апреля, 2023 · Жалоба Я раньше писал о том, что даже состояние Idle может быть разграничением пакетов. Не говоря уже о продвинутом УАРТе с распознаванием брейк-фреймов и символов. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
artemkad 92 10 апреля, 2023 Опубликовано 10 апреля, 2023 · Жалоба 2 часа назад, jcxz сказал: Про какое именно ядро речь? Если про Cortex-M Про любое и любую платформу. На STM32 свет клином не сошелся. 2 часа назад, jcxz сказал: и условия работы реально настолько жёсткие Там нет жестких условий. Жесткими они становятся только от того, что парсинг пытаешься засунуть в прерывание. 2 часа назад, jcxz сказал: то он например: одновременно с приёмом символов в строку посчитает их хеш (CRC32 например) и искать будет по таблице хешей Что считать если неизвестна длина лексемы, неизвестно их число в строке, неизвестно длина строки и неизвестно их положение? Отсюда хеш в потоке не посчитаешь. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 10 апреля, 2023 Опубликовано 10 апреля, 2023 · Жалоба 3 часа назад, artemkad сказал: 5 часов назад, jcxz сказал: 1. FIFO в UART. Почти нигде аппаратного нет Да ладно??? Смотрю свои коммерческие проекты - везде он есть. И даже 8 байт FIFO увеличивает время парсинга для вашего экстремального примера до 8000 тактов. За которые можно успеть что угодно. 3 часа назад, artemkad сказал: И что это даст при приеме изначально неизвестного числа байт? Правильно - ничего. Почему-то никому кроме вас "неизвестное число байт" не мешает использовать DMA с UART. 3 часа назад, artemkad сказал: Что считать если неизвестна длина лексемы, неизвестно их число в строке, неизвестно длина строки и неизвестно их положение? Как же вы их парсить собрались, если ничего не известно? Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 86 10 апреля, 2023 Опубликовано 10 апреля, 2023 · Жалоба Подсказка: DMA можно остановить, даже если счетчик его транзакций не дошел до 0 😄 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
jcxz 243 10 апреля, 2023 Опубликовано 10 апреля, 2023 · Жалоба Подсказка: DMA не нужно останавливать. 1 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
EdgeAligned 86 10 апреля, 2023 Опубликовано 10 апреля, 2023 · Жалоба В циклическом режиме - не нужно. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Сергей Борщ 143 10 апреля, 2023 Опубликовано 10 апреля, 2023 · Жалоба В любом режиме не нужно. 2 Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
artemkad 92 10 апреля, 2023 Опубликовано 10 апреля, 2023 · Жалоба 4 часа назад, jcxz сказал: Да ладно??? Смотрю свои коммерческие проекты - везде он есть. Точно? Попробуй, к примеру, его обнаружить в stm32f030f4 ? 4 часа назад, jcxz сказал: Почему-то никому кроме вас "неизвестное число байт" не мешает использовать DMA с UART. наверно потому "все доступные" обзоры примеров использования DMA с UART заканчиваются использованием DMA для передачи посылок. Задача как-бы не для этого раздела... 4 часа назад, jcxz сказал: Как же вы их парсить собрались, если ничего не известно? Сами лексемы известны - неизвестны которые из них и где располагаются в принятой строке. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Arlleex 190 11 апреля, 2023 Опубликовано 11 апреля, 2023 · Жалоба 9 часов назад, artemkad сказал: наверно потому "все доступные" обзоры примеров использования DMA с UART заканчиваются использованием DMA для передачи посылок... Позвольте спросить, а какие примеры? В интернет-барахолке которые, по типу народстрим и т.д.? Ну а чего на них смотреть то? Взять SLIP. У него заранее неизвестная длина кадров сырого потока (да и не сырого для приемника - тоже). Разве что-то мешает принимать этот поток через DMA? Ниже привожу драйвер-прослойку организации FIFO на UART-модуле для указанного Вами STM32F030 (может, только, буквы на конце другие). uart.hpp Спойлер #ifndef _UART_HPP_ #define _UART_HPP_ #include "macros.h" namespace nsUART { void init(); u32 readRxFIFO (u8 dst[], u32 len); s32 writeTxFIFO(u8 src[], u32 len); } #endif uart.cpp Спойлер #include <string.h> #include "uart.hpp" #include "stm32f0xx.h" #define FIFO_RX_SIZE 256 #define FIFO_TX_SIZE 256 namespace nsUART { static struct { u32 volatile rpos, wpos; u8 buf[FIFO_RX_SIZE]; } RxFIFO; static struct { u32 volatile rpos, wpos; u8 buf[FIFO_TX_SIZE]; bool volatile isStart; u32 volatile reqSize; } TxFIFO; static void reqTxDMA(u8 *mem, u32 len) { len &= 0xFFFF; TxFIFO.reqSize = len; DMA1_Channel4->CNDTR = len; DMA1_Channel4->CMAR = (u32)mem; DMA1_Channel4->CCR |= DMA_CCR_EN; } extern "C" void DMA1_Channel4_5_IRQHandler(void) { u32 const isr = DMA1->ISR & (DMA_ISR_TEIF5 | DMA_ISR_HTIF5 | DMA_ISR_TCIF5 | DMA_ISR_TEIF4 | DMA_ISR_HTIF4 | DMA_ISR_TCIF4); DMA1->IFCR = isr; if(isr & DMA_ISR_TCIF4) { DMA1_Channel4->CCR &= ~DMA_CCR_EN; u32 const bufSize = FIFO_TX_SIZE, endReqSize = TxFIFO.reqSize; u32 nxtRPos = TxFIFO.rpos + endReqSize; if(nxtRPos >= bufSize) nxtRPos -= bufSize; TxFIFO.rpos = nxtRPos; u32 newReqSize = TxFIFO.wpos - nxtRPos; if((s32)newReqSize < 0) newReqSize = bufSize - nxtRPos; if(newReqSize > 0) reqTxDMA(&TxFIFO.buf[nxtRPos], newReqSize); else TxFIFO.reqSize = 0, TxFIFO.isStart = false; } } extern "C" void TIM16_IRQHandler(void) { TIM16->SR = ~TIM_SR_UIF; RxFIFO.wpos = FIFO_RX_SIZE - DMA1_Channel5->CNDTR; } void init() { RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_DMAEN; RCC->APB2ENR |= RCC_APB2ENR_TIM16EN | RCC_APB2ENR_USART1EN | RCC_APB2ENR_SYSCFGEN; __DSB(); SYSCFG->CFGR1 |= SYSCFG_CFGR1_USART1RX_DMA_RMP | SYSCFG_CFGR1_USART1TX_DMA_RMP; DMA1_Channel5->CCR = DMA_CCR_MINC | DMA_CCR_CIRC; DMA1_Channel5->CNDTR = FIFO_RX_SIZE; DMA1_Channel5->CPAR = (u32)&USART1->RDR; DMA1_Channel5->CMAR = (u32) RxFIFO.buf; DMA1_Channel4->CCR = DMA_CCR_MINC | DMA_CCR_DIR | DMA_CCR_TCIE; DMA1_Channel4->CPAR = (u32)&USART1->TDR; #define BRTOFIX(apbfreq, br) divrnd((u64)(apbfreq) << 3, (br) * 8) #define BRRCALC(apbfreq, br) (BRTOFIX(apbfreq, br) >> 3 << 4 | (BRTOFIX(apbfreq, br) & 0x7)) USART1->BRR = BRRCALC(48000000, 115200); USART1->CR1 = USART_CR1_OVER8 | USART_CR1_TE | USART_CR1_RE; USART1->CR2 = 0; USART1->CR3 = USART_CR3_DMAT | USART_CR3_DMAR; TIM16->PSC = 48 - 1; TIM16->ARR = 1000 - 1; TIM16->DIER = TIM_DIER_UIE; static u32 constexpr moder = 0x2 * expfld(B10 | B9, 2), otyper = 0x0 * expfld(B9, 1), ospeedr = 0x1 * expfld(B9, 2), pupdr = 0x1 * expfld(B10, 2) | 0x0 * expfld(B9, 2), afrh = 0x1 * expfld((u64)B2 | B1, 4); updbit(GPIOA->PUPDR, rst(0x3 * expfld(B10 | B9, 2)), set(pupdr), inv(0)); updbit(GPIOA->OTYPER, rst(0x1 * expfld(B9, 1)), set(otyper), inv(0)); updbit(GPIOA->OSPEEDR, rst(0x3 * expfld(B9, 2)), set(ospeedr), inv(0)); updbit(GPIOA->AFR[1], rst(0xF * expfld((u64)B2 | B1, 4)), set(afrh), inv(0)); updbit(GPIOA->MODER, rst(0x3 * expfld(B10 | B9, 2)), set(moder), inv(0)); NVIC_SetPriority(DMA1_Channel4_5_IRQn, 2); NVIC_EnableIRQ(DMA1_Channel4_5_IRQn); NVIC_SetPriority(TIM16_IRQn, 1); NVIC_EnableIRQ(TIM16_IRQn); DMA1_Channel5->CCR |= DMA_CCR_EN; USART1->CR1 |= USART_CR1_UE; TIM16->CR1 = TIM_CR1_CEN; } u32 readRxFIFO(u8 dst[], u32 len) { u32 const bufSize = FIFO_RX_SIZE, curRPos = RxFIFO.rpos; u32 busyLen = RxFIFO.wpos - curRPos; if((s32)busyLen < 0) busyLen += bufSize; if(len > busyLen) len = busyLen; if(len > 0) { u32 nxtRPos = curRPos + len; if(nxtRPos >= bufSize) nxtRPos -= bufSize; if(dst != NULL) { u32 llen = 0, rlen = len; if(nxtRPos < curRPos) llen = nxtRPos, rlen -= llen; memcpy(dst, RxFIFO.buf + curRPos, rlen); if(llen > 0) memcpy(dst + rlen, RxFIFO.buf, llen); } RxFIFO.rpos = nxtRPos; } return len; } s32 writeTxFIFO(u8 src[], u32 len) { if(len > 0) { u32 curRPos, curReqSize, curReqNDTR; asm volatile ( "cpsid i \n\t" "ldr %0, [%3] \n\t" "ldr %1, [%4] \n\t" "ldr %2, [%5] \n\t" "cpsie i \n\t" : "=&r" (curRPos), "=&r" (curReqSize), "=&r" (curReqNDTR) : "r" (&TxFIFO.rpos), "r" (&TxFIFO.reqSize), "r" (&DMA1_Channel4->CNDTR) : "memory" ); u32 const bufSize = FIFO_TX_SIZE; u32 nxtRPos = curRPos + curReqSize - curReqNDTR; if(nxtRPos >= bufSize) nxtRPos -= bufSize; u32 const curWPos = TxFIFO.wpos; u32 freeLen = nxtRPos; if((s32)(freeLen -= curWPos) <= 0) freeLen += bufSize; if(freeLen - 1 >= len) { u32 llen = 0, rlen = len, nxtWPos = curWPos + len; if(nxtWPos >= bufSize) nxtWPos -= bufSize, llen = nxtWPos, rlen -= llen; memcpy(&TxFIFO.buf[curWPos], src, rlen); if(llen > 0) memcpy(TxFIFO.buf, &src[rlen], llen); TxFIFO.wpos = nxtWPos; if(!TxFIFO.isStart) TxFIFO.isStart = true, reqTxDMA(&TxFIFO.buf[curWPos], rlen); } else return -1; } return 0; } } Можете пользоваться, заодно, может, какие-то баги найдете (я не нашел), буду только рад поправить. Писал как раз недавно, без всяких классов/наследований/мета-мыла в глазах. FIFO классический, один читатель, один писатель. С точки зрения API readRxFIFO(buf, len) тупо считывает из FIFO не более len байт, writeTxFIFO(buf, len) запишет в FIFO и отправит; если места нет - вернет -1. Передатчик не блокирующий, т.е. как в индусских примерах не надо ожидать завершения передачи предыдущего кусочка (от предыдущих вызовов writeTxFIFO()). Тупо вызвали и это гарантированно отправится, если место есть. Все! Этот драйвер самодостаточен. Реализация лишь требует, чтобы readRxFIFO() читался с интенсивностью, достаточной для однозначно гарантируемого не переполнения входящего буфера. А дальше можно писать следующий драйвер канального уровня (или какой он там будет) - хоть SLIP, хоть что. Вызывая readRxFIFO() читаем сколько-то байт и тут же их скармливаем декодеру, а он уже разбирает на лексемы. Небольшое дополнение по коду. В фукции записи в FIFO критическая секция реализована исходя из предположения, что writeTxFIFO() не может быть вызван из места, где прерывания запрещены. Второй момент - не обрабатываются ошибки UART. Мне это было не нужно, ибо я точно знал, что протокол верхнего уровня после декодера сообщений точно будет проверять их целостность. "Битые" байты контроллер UART, судя по описанию в RM, DMA не отдаст. Но все это поправить под себя дело минутное. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
tonyk_av 45 11 апреля, 2023 Опубликовано 11 апреля, 2023 · Жалоба 9 hours ago, artemkad said: Сами лексемы известны - неизвестны которые из них и где располагаются в принятой строке. А вы вообще представляете, как выполняется синтаксический разбор? Я ведь не зря говорил про кольцевой буфер и stdio. В stdio есть все функции для разбора, в том числе самые главные, getc() и ungetc(). При разборе идёт посимвольное, никакие строки не_нужны. Теорию можно почитать у Вирта или Страуструпа, где доходчиво и с примерами показан синтаксический разбор и даже кодогенерация. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
artemkad 92 11 апреля, 2023 Опубликовано 11 апреля, 2023 · Жалоба 1 час назад, Arlleex сказал: Позвольте спросить, а какие примеры? В интернет-барахолке которые, по типу народстрим и т.д.? Ну а чего на них смотреть то? Гайды для начинающих с Ютуба. Мы ведь в разделе для начинающих или как? 1 час назад, Arlleex сказал: Ниже привожу драйвер-прослойку Забавный код... Забавно использование аппаратного таймера в виде костыля который каждую миллисекунду обновляет текущую позицию в очереди. Меня-бы жаба задавила тратить на это помимо DMA еще и таймер... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться