Перейти к содержанию
    

Как правильно объявить extern для typedef?

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 тактов. Ещё и время на перекур остаётся. :smoke:

А если программёр настолько крут, что знает не только про линейный поиск, но способен и к другим алгоритмам быстрого поиска, то возможно время перекура будет ещё больше.  :wizard:

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 тактов на сравнение с одним хешем. И это тупой линейный поиск.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, artemkad сказал:

Это вообще не к нормальной организации алгоритма, а требование к аппаратной части МК. Куча контроллеров на подобное не способны.

Это требование не к аппаратной части МК, а к выбору аппаратной платформы для решения поставленной задачи.

 

PS: И выбор программиста, способного решить поставленную задачу - тоже из этой же оперы.  :sarcastic:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

18 hours ago, MementoMori said:

Там ведь как - если приходит 0x00, то это считается концом строки.  А если в структуре есть байт 0x00? Как быть? Как синхронизировать начало посылки?

А вот и парсинг подоспел. Пошёл я за попкорном :popcorm2:

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, jcxz сказал:

1. FIFO в UART.

Почти нигде аппаратного нет или он маленький 1-8 байт.

2 часа назад, jcxz сказал:

2. UART читается через DMA.

И что это даст при приеме изначально неизвестного числа байт? Правильно - ничего.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Я раньше писал о том, что даже состояние Idle может быть разграничением пакетов. Не говоря уже о продвинутом УАРТе с распознаванием брейк-фреймов и символов.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 часа назад, jcxz сказал:

Про какое именно ядро речь? Если про Cortex-M

Про любое и любую платформу.  На STM32 свет клином не сошелся.

2 часа назад, jcxz сказал:

и условия работы реально настолько жёсткие

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

2 часа назад, jcxz сказал:

то он например: одновременно с приёмом символов в строку посчитает их хеш (CRC32 например) и искать будет по таблице хешей

Что считать если неизвестна длина лексемы, неизвестно их число в строке, неизвестно длина строки  и неизвестно их положение? Отсюда хеш в потоке не посчитаешь.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 часа назад, artemkad сказал:
5 часов назад, jcxz сказал:

1. FIFO в UART.

Почти нигде аппаратного нет

Да ладно??? Смотрю свои коммерческие проекты - везде он есть. 

И даже 8 байт FIFO увеличивает время парсинга для вашего экстремального примера до 8000 тактов. За которые можно успеть что угодно.

3 часа назад, artemkad сказал:

И что это даст при приеме изначально неизвестного числа байт? Правильно - ничего.

Почему-то никому кроме вас "неизвестное число байт" не мешает использовать DMA с UART.

3 часа назад, artemkad сказал:

Что считать если неизвестна длина лексемы, неизвестно их число в строке, неизвестно длина строки  и неизвестно их положение?

Как же вы их парсить собрались, если ничего не известно?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Подсказка: DMA можно остановить, даже если счетчик его транзакций не дошел до 0 😄

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 часа назад, jcxz сказал:

Да ладно??? Смотрю свои коммерческие проекты - везде он есть. 

Точно? Попробуй, к примеру, его обнаружить в stm32f030f4 ?

4 часа назад, jcxz сказал:

Почему-то никому кроме вас "неизвестное число байт" не мешает использовать DMA с UART.

наверно потому "все доступные" обзоры примеров использования DMA с UART заканчиваются использованием DMA для передачи посылок. Задача как-бы не для этого раздела...

 

4 часа назад, jcxz сказал:

Как же вы их парсить собрались, если ничего не известно?

Сами лексемы известны - неизвестны которые из них и где располагаются в принятой строке.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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 не отдаст. Но все это поправить под себя дело минутное.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 hours ago, artemkad said:

Сами лексемы известны - неизвестны которые из них и где располагаются в принятой строке.

А вы вообще представляете, как выполняется синтаксический разбор? Я ведь не зря говорил про кольцевой буфер и stdio. В stdio есть все функции для разбора, в том числе самые главные, getc() и ungetc(). При разборе идёт посимвольное, никакие строки не_нужны. Теорию можно почитать у Вирта или Страуструпа, где доходчиво и с примерами показан синтаксический разбор и даже кодогенерация.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Arlleex сказал:

Позвольте спросить, а какие примеры? В интернет-барахолке которые, по типу народстрим и т.д.? Ну а чего на них смотреть то?

Гайды для начинающих с Ютуба. Мы ведь в разделе для начинающих или как?

 

1 час назад, Arlleex сказал:

Ниже привожу драйвер-прослойку

Забавный код... Забавно использование аппаратного таймера в виде костыля который каждую миллисекунду обновляет текущую  позицию в очереди. Меня-бы жаба задавила тратить на это помимо DMA еще и таймер...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...