Jump to content

    

jeka

Свой
  • Content Count

    463
  • Joined

  • Last visited

Community Reputation

0 Обычный

About jeka

  • Rank
    Administrator
  • Birthday 06/14/1980

Контакты

  • ICQ
    Array

Recent Profile Visitors

5768 profile views
  1. Я получил без трансивера ~10(+-1)мбайт/сек записи, примерно столько же чтения. Без low-voltage трансивера это близко к пределу пропускной способности интерфейса sd карты. Загрузка cpu H743 порядка 10% получилась (реально меньше, ибо в загрузку еще получение по интерфейсу и предобработка данных включается). Без кеширования скорость записи была порядка 2-3МБ, но главная неприятность в том что при записи были регулярные фризы на время около 1 секунды (предполагаю, контроллер sd карты при многократной перезаписи одной области искусственно вносит задержку чтоб не истратить ресурс быстро). При кешировании ни одного фриза не заметил. В моем случае при потоковой записи такие фризы - очень неприятная штука.
  2. Разобрался. Код этот рабочий, с размером блока напортачил. Плюс IDE в дампе EEPROM не обновляет дамп в реальном времени (обновляет только при запуске отладчика) и затирает EEPROM по умолчанию при программировании.
  3. Не пойму в чем дело. Пытаюсь писать в eeprom, и никаких признаков что пишется не наблюдаю. В гугле есть аналогичный вопрос, без ответа торчит. void WriteFlash(const void* Src, void* Dst, int Len) { while (NVMCTRL.STATUS & 3) {}; uint8_t* SrcB = (uint8_t*)Src; volatile uint8_t* DstB = (uint8_t*)Dst; CPU_CCP=0x9D;// unlock for next 4 cycles. ints skipped for 4 cycles NVMCTRL.CTRLA=NVMCTRL_CMD_PAGEBUFCLR_gc; while (NVMCTRL.STATUS & 3) {}; for (int i=0; i<Len; i++) { *(DstB++) = *(SrcB++); } CPU_CCP=0x9D;// unlock for next 4 cycles. ints skipped for 4 cycles NVMCTRL.CTRLA=NVMCTRL_CMD_PAGEERASEWRITE_gc; while (NVMCTRL.STATUS & 3) {}; } #define SETTINGS_ADDR 0x1400 WriteFlash ((void*)&settings, (void*)SETTINGS_ADDR, sizeof (struct SETTINGS)); // пробовал и штатную функцию вызывать. эффект аналогичный: #include <avr/eeprom.h> eeprom_write_block ((void*)&settings, (void*)0/*SETTINGS_ADDR*/, sizeof (struct SETTINGS)); смотрел дамп в mplab x 5.50. везде ff ff...
  4. Увы, это не так. Совсем. Во-первых, модуль кеширования как раз занимается всем этим: и группировкой секторов в блоки, и умным префетчем, и отсеканием ненужных/пустых операций, и выравниванием операций по границе. Просто увеличив размер сектора до 4к мы как минимум половину из этих проблем не решаем, ибо я детально изучал какие обращения делает fatfs на каких операциях, там много избыточных обращений, которые можно вылечить либо серьезным усложнением самого fatfs, либо на порядок более простым модулем кеширования. К тому же, размер блока SD карты обычно больше 4к, со всеми вытекающими. А при работающем кеше 4к блоки у fatfs не имеют никакого практического смысла. Еще приятный бонус кеширования - можно операции чтения-записи к fatfs делать любыми удобными кусками, хоть по 5 байт, можно бустро и часто открывать-закрывать файлы без перечитывания цепочек fat и каталогов. Модуль кеширования всё это выстраивает и оптимизирует.
  5. Я с SD карточкой тоже боролся. Тоже смотрел в сторону увеличить блок данных (точнее, почему блок указан длинный, но пишется-читается все равно по 512 байт). В процессе разбора кода понял что лучше сделать модуль кеширования. В первую очередь чтобы ресурс флешки продлить - fatfs бестолково пишет некоторые сектора по многу раз в одно и то же место. В итоге немного допилил сам fatfs, ибо модулю кеширования для правильного выбора метода кеширования нужно знать какой это сектор - fat, каталога или кластер данных. Т.е. при кешировании данных модуль кеширования использует разные стратегии кеширования для кластеров, fat и каталогов. Ну и умеет буферизировать чтение и запись.
  6. После патча косяки исчезли. Вот подправленный варинат: static ETH_BufferTypeDef RxBuff[ETH_RX_DESC_CNT];// вынес в статику, чтоб при каждом пакете не инициализировать их заново. static struct pbuf * low_level_input(struct netif *netif) { struct pbuf *p = NULL; uint32_t framelength = 0; struct pbuf_custom* custom_pbuf; if (HAL_ETH_GetRxDataBuffer(&heth, RxBuff) == HAL_OK) { HAL_ETH_GetRxDataLength(&heth, &framelength); #if !defined(DUAL_CORE) || defined(CORE_CM7) // SCB_InvalidateDCache_by_Addr((uint32_t *)RxBuff->buffer, framelength); __DSB();// если кеш принимаемых пакетов отключен #endif lan_stats.rx_frames++; custom_pbuf = (struct pbuf_custom*)LWIP_MEMPOOL_ALLOC(RX_POOL); if(custom_pbuf != NULL) { custom_pbuf->custom_free_function = pbuf_free_custom; p = pbuf_alloced_custom(PBUF_RAW, framelength, PBUF_REF, custom_pbuf, RxBuff->buffer, framelength); } } return p; } extern SemaphoreHandle_t ipSemaphore; void ethernetif_input(void* argument) { struct pbuf *p; struct netif *netif = (struct netif *) argument; memset(RxBuff, 0 , ETH_RX_DESC_CNT*sizeof(ETH_BufferTypeDef)); for(int i = 0; i < ETH_RX_DESC_CNT -1; i++) RxBuff[i].next=&RxBuff[i+1]; while (ipSemaphore==NULL) osDelay(1); for( ;; ) { if (osSemaphoreAcquire(RxPktSemaphore, TIME_WAITING_FOR_INPUT) == osOK) { do { p = low_level_input( netif ); if (p != NULL) { // if (netif->input( p, netif) != ERR_OK ) if( xSemaphoreTake( ipSemaphore, ( TickType_t ) 0 ) ) { ethernet_input(p, netif); xSemaphoreGive( ipSemaphore ); } else pbuf_free(p); /* Build Rx descriptor to be ready for next data reception */ HAL_ETH_BuildRxDescriptors(&heth); } } while(p!=NULL); } } } И в tcpip.c семафор вставил: SemaphoreHandle_t ipSemaphore = NULL; static void tcpip_thread(void *arg) { struct tcpip_msg *msg; LWIP_UNUSED_ARG(arg); ipSemaphore = xSemaphoreCreateMutex(); xSemaphoreGive( ipSemaphore ); LWIP_MARK_TCPIP_THREAD(); LOCK_TCPIP_CORE(); if (tcpip_init_done != NULL) { tcpip_init_done(tcpip_init_done_arg); } while (1) { /* MAIN Loop */ LWIP_TCPIP_THREAD_ALIVE(); /* wait for a message, timeouts are processed while waiting */ TCPIP_MBOX_FETCH(&tcpip_mbox, (void **)&msg); if (msg == NULL) { LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n")); LWIP_ASSERT("tcpip_thread: invalid message", 0); continue; } if( xSemaphoreTake( ipSemaphore, ( TickType_t ) 0 ) ) { tcpip_thread_handle_msg(msg); xSemaphoreGive( ipSemaphore ); } } }
  7. Поразбирался. Самое веселое, что куда ни ставь HAL_ETH_BuildRxDescriptors, глюк не исчезает по банальной причине - пакет обрабатывается в другом потоке... Пришел пакет -> вызвалось Ethernet IRQ -> RxPktSemaphore -> завершение IRQ далее отрабатывает Ethif task: ethernetif_input -> tcpip_inpkt -> sys_mbox_trypost -> osMessageQueuePut -> xQueueSendToBack -> и в конце вызывает HAL_ETH_BuildRxDescriptors. Причем если вызов HAL_ETH_BuildRxDescriptors перенести в tcpip_thread, HAL перестанет сдвигать буфера и будет бесконечно один и тот же пакет пытаться отправить. далее, всё это принимает поток tcpip_thread. И в ней уже обрабатывается пакет, который был освобожден предыдущим потоком, со всеми вытекающими: tcpip_thread -> ethernet_input Вообщем, сделали крайне бестолково. Думаю, весь Ethif task надо убирать ибо он не нужен, а пакет из ethernetif_input сразу отдавать на обработку в ethernet_input.
  8. Как раз хотелось бы дескрипторы поменьше, например 128 байт, но общий объем буферов около 20кб. Иначе при маленьких пакетах и больших буферах эффективность использования RAM низкая, а packet rate может быть весьма высокий. И получается, если система задумалась при обработке жирного пакета, на буферизацию мелких пакетов может памяти не хватить (что собственно и происходит на практике). Сейчас смотрю реализацию езернета в порте микропитона под STM... Код явно в разы шустрей чем hal от stm. Видимо придется взять его за основу и немного доработать, ибо он на мелкие дескрипторы не заточен. Потрассировал HAL - честно разочаровывают программисты от ST. такие жирные функции крутить на каждом пакете, которые по факту ничего не делают. Вероятно, у Вас в поллинг режиме езернет работает. Мне же нужно с IRQ и RTOS.
  9. Еще не понял. HAL_ETH_GetRxDataLength(&heth, &framelength); - это размер всего пакета же, а не одного куска в дескрипторе? т.е. далее по тексту сброс DCACHE подразумевает целый пакет одним куском. Но... ведь пакет может быть в нескольких дескрипторах и не подряд идущих (переход по кругу от последнего буфера к первому), и получается хвост может потерять, если пакет не влезает в один дескриптор?
  10. При работе езернета натолкнулся на интересный глюк - содержимое пакета во время обработки может перезаписаться (на другой пакет). Причем долго работало нормально, стало проявляться только когда много коротких пакетов после задержки в роутере пачкой выплёвываются. Стал копать. Смотрю, куб из коробки генерирует такой вот код (специально обновил куб, свежий то же самое генерит): static struct pbuf * low_level_input(struct netif *netif) { struct pbuf *p = NULL; uint32_t framelength = 0, i = 0; struct pbuf_custom* custom_pbuf; ETH_BufferTypeDef RxBuff[ETH_RX_DESC_CNT]; memset(RxBuff, 0 , ETH_RX_DESC_CNT*sizeof(ETH_BufferTypeDef)); for(i = 0; i < ETH_RX_DESC_CNT -1; i++) { RxBuff[i].next=&RxBuff[i+1]; } if (HAL_ETH_GetRxDataBuffer(&heth, RxBuff) == HAL_OK) { HAL_ETH_GetRxDataLength(&heth, &framelength); HAL_ETH_BuildRxDescriptors(&heth); #if !defined(DUAL_CORE) || defined(CORE_CM7) /* Invalidate data cache for ETH Rx Buffers */ SCB_InvalidateDCache_by_Addr((uint32_t *)RxBuff->buffer, framelength); #endif lan_stats.rx_frames++; custom_pbuf = (struct pbuf_custom*)LWIP_MEMPOOL_ALLOC(RX_POOL); if(custom_pbuf != NULL) { custom_pbuf->custom_free_function = pbuf_free_custom; p = pbuf_alloced_custom(PBUF_RAW, framelength, PBUF_REF, custom_pbuf, RxBuff->buffer, framelength); } } return p; } забавно, что HAL_ETH_BuildRxDescriptors (как я понял это функция, которая помечает буфера приема как "свободно") вызывается не дожидаясь обработки пакета (которая в традиционно в процессе RTOS крутится). Подебажил - действительно, после вызова HAL_ETH_BuildRxDescriptors буфер перезаписывается свежими пакетами. Вот думаю, что разработчики имели в виду написав так... и как проще исправить.
  11. Да, lvgl уже подключил. На первый взгляд очень понравилось - документация адекватная, архитетура сделана по уму. openvg интересная наработка, будет время попробую. Откликов как-то не густо. Сложностью испугал или ценник не озвучил? платить готовы достойно, главное получить результат. Лучше конечно в перспективе рассматривать как длительное сотрудничество, а не разовую задачу.
  12. Есть дисплей 480x272 с тачскрином, на stm32h745. Есть базовые графические библиотеки (по большей части самописные) и текущий юзер-интерфейс (он умеет рисовать графики, приборную панель, гистограмму, бегать по менюшкам). Библиотека - отрисовывать текстуры, накладывать шрифты, все с субпиксельной точностью и прозрачностью). Есть базовые графические примитивы (привет Брезенхему), но их нужно будет доработать до субпиксельного обсчета или взять что-то готовое, подходящее под наши задачи. Нужно в этот юзер-интерфейс добавить продвинутый просмотрищик графиков (примерно функционал осциллографа, но каналов на 60 и с дополнительными фишками), и другие вещи. Все должно быть красиво, с субпиксельной отрисовкий, сплайнами/кривыми безье, каналом прозрачности при наложении. Реакция на тач тоже продвинутая - с прокруткой по инерции, просчетом ускорений, замедлений) Графические элементы есть кому рисовать. Нужно чувство эстетики, внимание к деталям и к мелким недочетам. Т.е. задача - программирование интерфейса, добавление в библиотеки недостающего функционала. И работа с данными, которые собираем-отображаем-меняем (приходящие в реальном времени данные буферизируются, пишутся на sd карту, возможно в разных масштабах для ускорения загрузки при большим масштабировании; просмотрищиком графиков нужный кусок собирается из буферов ram и/или sd карты, масштабируется и отображается). По срокам - хорошо бы в пределах месяца на минимальный функионал по отрисовке графиков (чтоб как-то можно было пользоваться), потом добавление фишек, доведение и вылизывание эстетики (несколько месяцев) Территориально - Москва, м. калужская. jeka (а) 2x4 тчк ru или личка, тут можно задат вопросы.
  13. Еще немного про кеш (тестировалось на DRAM памяти, кеш включен на чтение и запись) Заметил, что при любом обращении на чтение незакешированных данных, читается _вся_ строка кеша (32 байта), а не только тот элемент что вы запросили. Т.е. достаточно одной операции (например PLD) чтоб предварительно загрузить всю строку кеша. Предположительно, при чтении на границе будут читаться 2 строки кеша (будет полезно чтоб командой PLD грузить сразу 64 байта)
  14. В итоге, сделал программный профайлер, который замеряет время блокировки прерываний у freertos. И сразу все стало ясно - мой косяк оказался. Со стеком всё нормально. как я понял, этой штукой точно замерить время не получится. Как-то пробовал запустить таймлайн через SWO, но всё наглухо подвисало, ощущение что клоки swo неверные были. Надеюсь, скоро будет j-trace pro, с ним должно быть повеселее такие штуки искать.