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

ilkz

Участник
  • Постов

    132
  • Зарегистрирован

  • Посещение

Сообщения, опубликованные ilkz


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

     

    Ну тут три мысли зарыто:

    1. Размерность размера в 8 бит позволяет задать для порта ширину до 255 бит. Вряд ли в реальной жизни потребуется больше.

    2.Синтезатор (ква) не умеет делать конкатенацию без явного указания разрядности ее элементов, что правильно. Попробуйте - и получите еррор.

    3. Уже не раз напарывался на забавные "сюрпрайзы", когда в коде пишешь число без указания разрядности, например в каком-нибудь суммировании или умножении, а синтезатор его подрезает как ему удобно, но не так как надо мне, из-за чего рушится логика работы. Не один день можно убить на отладку.

  2. GAYVER, это макрос, который позволяет упростить определение параметра port_list (см. пример использования в самом первом посте). Без этого макроса было бы

        localparam port_list   =  port_sizes[0 +: 8] +  port_sizes[8 +: 8] +  port_sizes[16 +: 8];

    соответственно, надо помнить про индексы, высчитывать их. Это муторно.

    Макрос позволяет это действо скрыть:

        localparam port_list   = `register_port(0) + `register_port(1) + `register_port(2);

     

    Кроме того, в последней версии кода этот параметр вообще ушел и никаких дополнительных макросов не требуется. Там использование гораздо проще:

    data_packer dp
        (
                .reset          (reset),
                .clk            (sys_clk),
    
                .mode           (mode),
    
                .datain         ({port2, port1, port0}),
                .port_ready     (rdy_port),
    
                .byte           (),
                .byte_ready     (),
                .data_packed    ()
        )
             dp.nports = 3,                                         // задаем количество используемых портов
             dp.port_sizes = {8'd3, 8'd8, 8'd6},            // задаем разрядности каждого порта
             dp.max_port_size = 8;                             // разрядность самого толстого используемого порта (в нашем случае 8, но можно задать с запасом)

     

     

     

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

  3. Потому что StdLib - ад и ужас.

     

    Клок на шине выставляет мастер. Чтобы он это сделал, ему надо сказать "передавай". Этот StdLib так не делает, хотя, если судить только по названиям функций, должен.

    Т.е. берём и делаем ОДНУ функцию

     

    Т.е., чтобы что-то прочитать - надо что-то записать. Люююто. А как быть, если нужно прочитать много байт, а слейв не допускает dummy-записей? Ну, то есть, во время записи dummy-слов слейв может на них как-то отреагировать, т.к. для него это будут команды какие-нибудь.

  4. Ладно, с этм разберусь когда железяку запущу хоть как-то. Идея в целом понятна. По результатам дам знать. Спасибо за разжевывание )))

     

    Пока что возник следующий проблем:

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

    void mcp_read(uint8_t addr) {
        GPIO_ResetBits(GPIOB, GPIO_Pin_11);
        spi2_send(0x41);
        spi2_send(addr);
        SPI_I2S_ReceiveData(SPI2);
        GPIO_SetBits(GPIOB, GPIO_Pin_11);
    }
    
    ...
    
    mcp_write(0x05, 0x20); // IOCON
    mcp_write(0x00, 0x00); // IODIR
    mcp_write(0x01, 0xA5); // IPOL
    mcp_read(0x01); // IPOL

     

    Я вижу что слейв что-то пытается выдать на свой SDO, но т.к. нет клока, то слейв обламывается и мой мастер ничего не принимает... Что за очередной бред? (Мы щас пока не трогаем всякие прерывания и прочие DMA).

    Соответственно, никакие прерывания не дергаются.

  5. Какой-то странный флаг у ST-шников получился, да.

     

    Они это и сами признают:

     

    Ок, допустим. Но рекомендуемый ими флаг TXE имеет не совсем то поведение что нужно: он единичится, когда данные попадают в сдвиговый регистр, а не когда они из него улетели на улицу. Т.е. для управления собственным сигналом CS испоьзование флага TXE не подходит.

     

    Как вы выходите из положения?

  6. Друзья, не могу понять в чем дело.

    Завожу SPI в режиме мастера, сигналом CS управляю как отдельным пином.

    Ожидаю поведения, когда он (CS) нулем подчеркивает все данные, но он почему-то этого не делает (см картинку).

    Самое интересное, что если в начало тела while(1) дописать что-то типа printf("spi send %X\n", 0xC1);, то CS начинает формироваться правильно.

     

    Код привожу ниже.

     

    void spi2_init(void){
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_SPI2, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
    
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource13, GPIO_AF_SPI2); // sck
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource15, GPIO_AF_SPI2); // mosi
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource14, GPIO_AF_SPI2); // miso
    
    GPIO.GPIO_Mode = GPIO_Mode_AF;
    GPIO.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO.GPIO_OType = GPIO_OType_PP;
    GPIO.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
    GPIO_Init(GPIOB, &GPIO);
    
    SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
    SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
    SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
    SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
    SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
    SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
    SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_8;
    SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
    SPI_Init(SPI2, &SPI_InitStructure);
    //SPI_I2S_ITConfig(SPI2,SPI_I2S_IT_RXNE,ENABLE);
    SPI_Cmd(SPI2, ENABLE);
    SPI_NSSInternalSoftwareConfig(SPI2, SPI_NSSInternalSoft_Set);
    
    GPIO.GPIO_Pin = GPIO_Pin_11 | GPIO_Pin_12;
    GPIO.GPIO_Mode = GPIO_Mode_OUT;
    GPIO.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init (GPIOB, &GPIO);
    }
    
    void spi2_send(uint8_t value) {
    SPI_I2S_SendData(SPI2, value);
    while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET);
    }
    
    void ad_send(uint8_t value) {
    GPIO_ResetBits(GPIOB, GPIO_Pin_12);
    spi2_send(value);
    GPIO_SetBits(GPIOB, GPIO_Pin_12);
    }
    
    int main(void) {
    SystemInit();
    TM_DELAY_Init();
    TM_DISCO_LedInit();
    spi2_init();
    while (1) {
    	//printf("spi send %X\n", 0xC1);
    	ad_send(0xC1);
    	Delayms(1);
    }
    }

     

    post-67084-1441792417_thumb.png

     

     

     

     

    UPD:

    Опытным путем выяснилось, что если вставить задержку между SPI_I2S_SendData() и ожиданием флага SPI_I2S_FLAG_BSY, то все начинает работать:

     

    void spi2_send(uint8_t value) {
        int i;
        SPI_I2S_SendData(SPI2, value);
        for(i=0; i<63; i++);
        while(SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_BSY) == SET);
    }

     

    Получается что нельзя проверяь флаг SPI_I2S_FLAG_BSY сразу после отправки? Он что, не успевает встать?

  7. Думаю, это единственное логичное объяснение. Но мне не удалось найти где делается эта привязка. Но очень хочется разобраться.

    Выкладываю проект, посмотрите по возможности незамыленным взглядом (сабж сидит в usbd_hid_core.c).

    __1.rar

     

    UPD: И еще, почему-то после первого приема данных буфер - пустой (это видно по мемори дампу). Начиная со второго приема - он заполняется валидными данными.

    Он был не готов принимать после инициализации. Проблема решилась добавлением DCD_EP_PrepareRx(pdev, HID_OUT_EP, rxbuf, MAX_DATA_LENGTH); в USBD_HID_Init() в место после открытия ендпоинтов.

     

    UPD2: Возможно, становится понятно поведение с "магическим" запонением приемного буфера. Насколько реален такой сценарий:

    1. Объявлена переменная-буфер buf.

    2. Т.к., во время инициализации не делается DCD_EP_PrepareRx(..., buf, ...), то буфер не трогается и он по дефолту равен нулю.

    3. Возникает событие приема: буфер равен нулю (т.к. его никто не трогал).

    4. Что-то делается в обработчике.

    5. Последняя строчка - это как раз DCD_EP_PrepareRx(..., buf, ...), т.е. тут уже нормально передается ссылка на этот несчастный буфер.

    6. Ну и на последующих приемах он уже получается прилинкован к приемнику, который заполняет его валидными данными.

     

    Я прав или не?

  8. Видимо как-то отсюда:

    Тут же вы передаете адрес этого буфера стеку. Копайте эту функцию.

     

    Весь цимес в том, что если убрать строки

      TM_USB_HIDDEVICE_SendCustom(USB_Rx_Buffer, 65);
      DCD_EP_PrepareRx(pdev, HID_OUT_EP, USB_Rx_Buffer, MAX_DATA_LENGTH);

     

    ,то волшебный буфер все равно оказывается заполнен принятыми данными )))

  9. Искал - пусто.

     

    Сергей Борщ, хотите прикол?

    Было (это единственное объявление данной переменной на весь проект, сидит в том же файле что и код ниже):

    __ALIGN_BEGIN uint8_t USB_Rx_Buffer   [MAX_DATA_LENGTH] __ALIGN_END;

     

    Меняю на:

    __ALIGN_BEGIN uint8_t SuperMegaBuffer   [MAX_DATA_LENGTH] __ALIGN_END;

     

    , соответственно, в коллбеке приема:

    static uint8_t  USBD_HID_DataOut (void  *pdev, uint8_t epnum) {
      uint16_t USB_Rx_Cnt;
      uint8_t i = 0;
      
      USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count; // pointer to usb packet
    
      USB_Rx_Buffer[0] = 0x01;
      TM_USB_HIDDEVICE_SendCustom(USB_Rx_Buffer, 65);
    
      DCD_EP_PrepareRx(pdev, HID_OUT_EP, USB_Rx_Buffer, MAX_DATA_LENGTH);
      return USBD_OK;
    }

     

    меняю на:

    static uint8_t  USBD_HID_DataOut (void  *pdev, uint8_t epnum) {
      uint16_t USB_Rx_Cnt;
      uint8_t i = 0;
      
      USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count; // pointer to usb packet
      
      SuperMegaBuffer[0] = 0x01;
      TM_USB_HIDDEVICE_SendCustom(SuperMegaBuffer, 65);
      
      DCD_EP_PrepareRx(pdev, HID_OUT_EP, SuperMegaBuffer, MAX_DATA_LENGTH);
      return USBD_OK;
    }

     

    и все, больше ничего и нигде не трогаю. И что же я вижу? А то, что чудесным, мать его, образом - в SuperMegaBuffer оказываются принятые данные. КАААК?

  10. Сергей Борщ, ясно.

     

    Последний пока что вопрос: кто же все-таки заполняет USB_Rx_Buffer?

    Пока что единственное что я нашел связанное с буфером приема/передачи - это указатель xferr_buff в структуре USB_OTG_hc (файл usb_core.h). Но как-то оно не то...

  11. Вы поняли неправильно. Конечная точка имеет...

     

    REPORT ID - это совсем другая опера. Если вы в дескрипторах сообщили, что будете отсылать несколько разных типов сообщений (reports), то вы должны в начале каждого передавать дополнительный байт с идентификатором. Если вы анонсировали только один вид сообщений, то второй стороне не нужно отделять его от других и этот байт с идентификатором не передается.

     

     

    С первым понятно, спасибо за внятное разъяснение.

    Со вторым: если в репорте используется один IN и один OUT, то это считается за два вида сообщений или за один?

  12. Опа-ля, йа креведко розобралсо.

     

    Дело было вот в чем:

    1. Если мы хотим отправить N Байт, то реально надо отправить на 1 байт больше, т.е. N+1.

    2. В дескрипторе репорта должно быть указано количество отправляемых элементов (REPORT_COUNT) равное N.

    3. Первым байтом в отправляемых данных должен быть REPORT ID.

    4. И усё.

     

    Теперь понятны слова Golikov A. про 65 байт.

     

    Осталось понять кто заполняет USB_Rx_Buffer.

  13. 64 байта - граница пакета, должна быть снабжена пустым пакетом с 0 данными, может дело в этом?

     

    Не совсем понял: т.е., чтобы отослать 64 байта, надо отослать 65 с последним нулевым? Или в 64 байтах последний болжен быть нулевой?

     

    Хорошо, давайте разбираться детально :)

     

    Вот есть такой код отправки ответа:

    #define buf_size  8
    uint8_t buf[buf_size];
    
    static uint8_t  USBD_HID_DataOut (void  *pdev, uint8_t epnum) {
      uint16_t USB_Rx_Cnt;
      uint8_t i = 0;
      
      /* Get the received data buffer and update the counter */
      USB_Rx_Cnt = ((USB_OTG_CORE_HANDLE*)pdev)->dev.out_ep[epnum].xfer_count; // pointer to usb packet
    
      if(USB_Rx_Buffer[1] == 0x01) {
        TM_DISCO_LedOn(LED_GREEN);
      }
      else if(USB_Rx_Buffer[1] == 0x00) {
        TM_DISCO_LedOff(LED_GREEN);
      }
      
      for(i = 0; i<buf_size; i++) buf[i] = i;
      TM_USB_HIDDEVICE_SendCustom(buf, buf_size);
      
      /* Prepare Out endpoint to receive next packet */
      DCD_EP_PrepareRx(pdev, HID_OUT_EP, USB_Rx_Buffer, MAX_DATA_LENGTH);
      
      return USBD_OK;
    }

     

    Т.е., в ответ на прием пакета я отправляю 8 байт со счетчиком внутри.

    TM_USB_HIDDEVICE_SendCustom() - это просто обертка над USBD_HID_SendReport(&USB_OTG_dev, buff, count).

     

    Я ожидаю, что на пк примется этот 8-байтный ответ, однако ничего не принимается. Смотрим сниффером что получается:

    post-67084-1440650779_thumb.png

    Ответы приходят (правда почему-то только с 3-го раза после включения девайса), длина 8 байт, вроде все как заказывали.

     

     

    Смотрим како-нить пакетик:

    post-67084-1440650783_thumb.png

    Сниффер говорит что пакет принят успешно.

     

    Однако, принимающая прога на компе в упор ничего не видит.

    Вот репу уже сломал, прям руки опускаются. И что-то мне подсказывает что дело, скорее всего, в дескрипторах. Вроде я их пилил-пилил, но может чего не допилил. Посмотрите опытным взглядом:

    post-67084-1440651195_thumb.png

    Там по идее заложено 64 байта туда, 64 байта обратно и все.

  14. 1. Очевидно её заполняет обработчик прерывний USB который не был найден.

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

    3. Могут быть разные причины.

     

    1. Так ведь обращение к ней нигде не производится. Я даже указателя нигде на нее не нашел. Может кто знает сей фокус?

    2. Более-менее понятно.

    3. Пока тоже неясно, разбираюсь.

     

    Появился еще один вопрос:

    Для отправки данных используется функция USBD_HID_SendReport(&USB_OTG_dev, buff, count).

    И для того, чтобы послать буфер в 64 байта надо указывать count равным 65, а не 64 как казалось бы... Никто с таким не сталкивался?

  15. Добрый день!

     

    Понадобилось организовать обмен данными стмки с пк на основе USB_HID.

    За основу взял темплейт от TM с его либами.

     

    В целом, туда-сюда пакетики бегают, но есть несколько крайне непонятных для меня моментов:

    1. Коллбек USBD_HID_DataOut. Насколько я понял, прием данных происходит именно в нем. Но откуда достать сами данные? Во всех примерах используют переменную USB_Rx_Buffer, которая объявляется в том же файле. Но она нигде не заполняется, а при попадании в коллбек данные в ней чудесным образом оказываются. Как так?

     

    2. Непонятно поведение: если буфер объявить внутри коллбека, то вместо значений из буфера отсылается мусор. Если же объявление сделать глобальным, то отсылаются верные значения. Почему так?

     

    3. В коллбек вписана отправка ответного пакета. Но иногда (в основном после включения) ответный пакет отсылается лишь с 3-го раза. Почему такое может возникать?

     

    Спасибо.

  16. Хотелось бы начать переход на систем верилог, но перед этим хочется понять какие фичи языка поддерживает квартус, а какие - нет. Чтобы не попасть в просак, написав код, который придется переписывать. Можете написать как-нибудь тезисно, в духе "вот это поддерживает, а вот это нет, а вот это частично в таком-то объеме". Думаю, это будет многим полезно.

  17. Интересную тему поднял оказывается ))

     

    Постараюсь на все вопросы ответить:

    1. Один пакет имеет размер около 2 КБ (плюс-минус).

    2. Памяти на устройстве есть 128 МБ (можно расширить до 256, DDR2).

    3. Прокачивать пакеты через эту систему надо будет как разово понемногу (с большим запасом - до 100.000 пакетов), так и, условно говоря, неделями непрерывно (т.е. память должна быть циклической).

    4. Каждый пакет может отправиться несколько раз (и если пришло подтверждение приема либо исчерпано количество посылок, то пакет помечается как успешно доставленный, а на его место теперь можно класть новый).

    5. Чтобы понимать где есть пустые пакеты, а где нет - система использует счетчик количества посылок (0 - пакет пустой, >0 - пакет еще живой).

     

    Сейчас пока придумал такой алгоритм, который позволяет обойтись без дорогостоящего поиска:

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

    2. Получатель при подтверждении ретранслирует этот адрес обратно мне - по нему можно однозначно достать из памяти именно этот отправленный пакет.

    3. Для записи новых пакетов в сдрам в плисине используется небольшое фифо, в которое складываются адреса свободных в данный момент пакетов.

     

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

  18. из сдрам будет медленно и печально.

     

    На пальцах : по сути КАМ это быстрый массив компараторов. Положим хотим быстро искать слово 32-бита. Итого нужно хранить само слово + логику сравнения. На сыклоне 64 плитки минимум. Итого 1024 плитки на 16 слов, остальное будет стыковая логика. Итого сможете сделать что-то вроде CAM16x32 (глубина 16, разрядность слова 32). Можно сделать на блочной памяти, но там память всего 13 бит по адресу (M9k), итого потребуется минимум 3 блока RAM, и это на одно вхождение слова, или 4 блока на 8 вхождений (память в режиме 1024х8). Т.е. 4 блока памяти на КАМ8х32 (глубина 8, разрядность слова 32).

     

    ЗЫ. если ваше поле небольшое, то можно сделать CAM только для этого поля. Если больше 32-х бит, то лучше считать хеш.

     

    Ага, идея понятна. Спасибо.

     

    К сожалению, данных для поиска очень много (по моим оценкам - в районе 100.000 записей).

     

    Размер ключа поиска пока не определен, но думаю будет что-то от 16 до 32 бит (это таймер, искать надо будет самый старый пакет с момента последнего обновления). А что если виртуально разбить память на блоки, и в ПЛИС сделать кэш, в котором хранить адреса самых старых пакетов в блоках. Тогда по идее можно быстро (небольшим САМ-мом) найти блок с самым старым пакетом, а уже в нем найти требуемый пакет недолгим последовательным перебором (я так понимаю, это какое-то подобие поиска по хэшам?).

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