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

Как все-таки правильно делать CDC на STM32?

Пытаюсь написать CDC ACM на STM32F103 (а потом и на F0x2 перенести). Основная проблема - обработка больших потоков входных данных. До этого делал эмулятор PL2303, он вполне работает на нескольких железяках, но при больших потоках входных данных тоже "умирает".

Каждый раз в обработке прерывания usb_lp_can_rx0_isr с флагом USB_ISTR_CTR вызывается функция, буферизующая данные:

static uint16_t EP23_Handler(ep_t ep){
    if(ep.rx_flag){
        int rd = ep.rx_cnt, rest = IDATASZ - idatalen;
        if(rd){
            if(rd <= rest){
                idatalen += EP_Read(2, (uint16_t*)&incoming_data[idatalen]);
                //incoming_data[idatalen++] = '$';
            }else{
                addbuflen = EP_Read(2, (uint16_t*)&addbuf);
                ep.status = SET_NAK_RX(ep.status);
                ovfl = 1;
                return ep.status;
            }
        }
    }else if (ep.tx_flag){
        tx_succesfull = 1;
    }
    ep.status = SET_VALID_RX(ep.status);
    // keep DTOG state
    ep.status = KEEP_DTOG_TX(ep.status);
    ep.status = KEEP_DTOG_RX(ep.status);
    // clear all RX/TX flags
    ep.status = CLEAR_CTR_RX_TX(ep.status);
    //ep.status = CLEAR_CTR_TX(ep.status);
    return ep.status;
}

т.е. в ней при переполнении буфера выполняется сохранение новых данных в отдельный буфер и выставляется NAK.

Для считывания этого буфера вызывается функция:

int USB_receive(char *buf, int bufsize){
    if(bufsize < 1 || !idatalen) return 0;
    uint32_t oldcntr = USB->CNTR;
    USB->CNTR = 0;
    int sz = (idatalen > bufsize) ? bufsize : idatalen, rest = idatalen - sz;
    for(int i = 0; i < sz; ++i) buf[i] = incoming_data[i];
    if(rest > 0){
        uint8_t *ptr = &incoming_data[sz];
        for(int i = 0; i < rest; ++i) incoming_data[i] = *ptr++;
        idatalen = rest;
    }else idatalen = 0;
    if(ovfl){
        //DBG("Got overflow!");
        if(idatalen == 0){
            ovfl = 0;
            //DBG("Clear overflow.");
            if(addbuflen){
                idatalen = addbuflen;
                for(int i = 0; i < addbuflen; ++i) incoming_data[i] = addbuf[i];
                addbuflen = 0;
            }
            uint16_t epstatus = USB->EPnR[2];
            epstatus = CLEAR_DTOG_RX(epstatus);
            epstatus = SET_VALID_RX(epstatus);
            USB->EPnR[2] = epstatus;
        }
    }
    USB->CNTR = oldcntr;
    return sz;
}

Однако, во-первых, входные данные при больших потоках приходят битыми. Во-вторых, после нескольких приемов микроконтроллер полностью зависает. Может, виной USB->CNTR = 0 в последней функции?  Тогда как обеспечивать целостность данных - мьютексами? А в STM32F0 мьютексы не сделать - там нет ldrex и strex..

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

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


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

9 minutes ago, Eddy_Em said:

USB->CNTR = 0

Зачем???

 

9 minutes ago, Eddy_Em said:

Тогда как обеспечивать целостность данных - мьютексами?

Данные класть в FIFO, по его уровню выставлять NAK. Без FIFO просто выставлять NAK до обработки принятых данных. Не надо уродливых костылей с дополнительным буфером.

 

13 minutes ago, Eddy_Em said:

А в STM32F0 мьютексы не сделать - там нет ldrex и strex.. 

Что мешает?

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


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

1 hour ago, aaarrr said:

Зачем???

Чтобы прерывание в это время не возникло.

1 hour ago, aaarrr said:

Данные класть в FIFO, по его уровню выставлять NAK

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

1 hour ago, aaarrr said:

Что мешает?

Отсутствие атомарных операций.

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


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

9 minutes ago, Eddy_Em said:

Отсутствие атомарных операций.

Стесняюсь спросить, сколько ядер у STM32F0?

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


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

При чем здесь ядра?

Выполняем что-то, тут бац - прерывание, где вызывается опять эта же функция. И привет…

Насчет USB: пробовал устанавливать в EPnR NAK до обработки - зависает. Если ничего не делать с EPnR - тоже зависает!

Какой же алгоритм-то работы?

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


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

44 minutes ago, Eddy_Em said:

При чем здесь ядра?

Потому что LDREX/STREX придуманы прежде всего как элемент синхронизации между ядрами.

 

45 minutes ago, Eddy_Em said:

Выполняем что-то, тут бац - прерывание, где вызывается опять эта же функция. И привет…

А  обернуть обращние к переменной запретом прерываний кто-то запрещает?

 

46 minutes ago, Eddy_Em said:

Какой же алгоритм-то работы?

Приняли данные. Если есть место в буфере еще на один пакет, сразу перебросили NAK->VALID. Нет - ничего не делаем до освобождения буфера, затем то же самое NAK->VALID.

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


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

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

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


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

Итак, что получается: в прерывании обязательно нужно выставить флаги. Но необязательно ставить ACK. Т.е. обработчик приема выглядит так:

static void receive_Handler(ep_t ep){ // EP2OUT
    ep.status = SET_NAK_RX(ep.status);
    ep.status = CLEAR_CTR_RX(ep.status);
    ep.status = KEEP_DTOG_TX(ep.status);
    ep.status = KEEP_DTOG_RX(ep.status);
    USB->EPnR[2] = ep.status;
}

Соответственно, хост затормаживает передачу. А вот когда МК готов принять данные, делаем:

int USB_receive(char *buf, int bufsize){
    if(!usbON) return 0;
    uint16_t epstatus = USB->EPnR[2];
    if(bufsize < 1 || !endpoints[2].rx_cnt) return 0;
    int sz = EP_Read(2, (uint16_t*)buf);
    epstatus = SET_VALID_RX(epstatus);
    epstatus = KEEP_DTOG_TX(epstatus);
    epstatus = KEEP_DTOG_RX(epstatus);
    USB->EPnR[2] = epstatus;
    endpoints[2].rx_cnt = 0;
    return sz;
}

В итоге оно на мелких потоках работает, да даже если 257 байт разом отправляю - проблем нет. А вот cat большойфайл > /dev/ttyACM0 приводит к подвисанию терминала, а МК выплевывает только первые 320 символов и успокаивается...

Ладно, буду дальше "методом Монте-Карло" разбираться.

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


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

В 09.02.2020 в 17:23, Eddy_Em сказал:

Как все-таки правильно делать CDC на STM32?

 

Коль уж разговор об этом в теме зашел, то помогите тогда и мне :). Будучи закоренелой AVR-овщицей, с мая прошлого года стала осваивать STM32, потеряв надежду на то, что Microchip продолжит дело Atmel'а в области AVR-контроллеров с повышенной производительностью (Тиньки и малоногие Меги нового образца не в счет - они погоды не делают). Ну, а поскольку по здешним понятиям я - гуру :), то освоение даётся мне легко, за исключением ... USB CDC девайса. Не хочет он у меня работать. Причем, я хочу от него совсем малого - пусть в девайс-менеджере хотя бы желтый треугольничек покажет после подключения к компьютеру - но и этого нет, не чувствует компьютер мой девайс. Гуглить гуглила и советам этим следовала, но всё без толку.  Проекты с GitHub так и вовсе не компилятся - чего-то из среды не хватает. Как назло примеров для моего STM32F407 в CubeMX нет, хотя для USB HID девайса они есть. Примеры для других STM32F4 сплошь на HS (high spеed), а для FS (full speed) их нет, а в переделанном виде они у меня не работают.

 

С железом у меня всё в порядке, т.к. в случае USB HID девайса (FS) USB-порт работает нормально.

 

Может кто поделится примером проектика, чтобы USB CDC девайс определялся? Мне неважно, что передает он и что принимает, лишь бы компьютер его узнавал, как Virtual COM port.

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


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

Ксения, на каком из контроллеров (HS/FS) в режиме FS у Вас припаян USB разъем? Ккой кварц? Или это готовая плата?

Пробовали не пример искать а сгенерировать приложение из того же куба?

 

Изменено пользователем GenaSPB

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


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

37 минут назад, GenaSPB сказал:

Ксения, на каком из контроллеров (HS/FS) в режиме FS у Вас припаян USB разъем?

OTG_FS (PA11, PA12)

 

37 минут назад, GenaSPB сказал:

Какой кварц?

8 MHz, но после множителей и делителей выдает на USB частоту 48 MHz.

 

37 минут назад, GenaSPB сказал:

Или это готовая плата?

У меня исключительно готовые, т.к. сама я их делать не умею :).

Это - любимая на STM32F407ZET6:

https://aliexpress.ru/item/32878749012.html

но пыталась и на плате

STM32F429I-DISCO (там STM32F429ZIT6 стоит).

 

37 минут назад, GenaSPB сказал:

Пробовали не пример искать а сгенерировать приложение из того же куба?

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

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


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

STM32F429I-DISCO  - там USB подключен к OTG_HS в FS режиме. Вы это учитывали при создании проекта? Спрашиваю потому, что это работает с гарантией...

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


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

57 minutes ago, Eddy_Em said:

SET_NAK_RX(ep.status)

Read-modify-write cycles on these registers should be avoided because between the read
and the write operations some bits could be set by the hardware and the next write would
modify them before the CPU has the time to detect the change.

 

 

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


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

17 минут назад, GenaSPB сказал:

STM32F429I-DISCO  - там USB подключен к OTG_HS в FS режиме. Вы это учитывали при создании проекта? Спрашиваю потому, что это работает с гарантией...

Я только на FS и пишу, а о HS даже не думала, полагая, что частоту 480 MHz мне не создать, да и контролер не потянет. Я в первом же своем сообщении ясно сказала, что мне на FS проект нужен и даже посетовала, что в примерах есть только CDC для HS, который я пыталась переделать на FS, но безрезультатно.

Но, положим, вы правы, и у меня USB-разъем не туда припаян,  но  тогда почему у меня проект HID-девайса работает (на обеих платах) и тех же 48 MHz на USB. Ведь не может же при заливке другой прошивки USB-разъем отпаяться и к другому контролеру сам припаяться?

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


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

USB_OTG_HS это второй контроллер. Для FS режима встроенный интерфейсный блок (как и в USB_OTG_FS). Требуется те же 48 МГц. Выберите ему Internal FS Phy: Device_Only. VBUS если есть соединение на плате... Если не выяснить - не ставьте.

зы: я про STM32F429I-DISCO

Изменено пользователем GenaSPB

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


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

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

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

Гость
Ответить в этой теме...

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

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

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

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

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

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