STM32 - организация виртуального COM-порта

Раз у камня есть аппаратный USB, то грех им не пользоваться. Один из способов плюнуть байтом в компьютер и чтобы он при этом не очень обиделся — это организация виртуального COM-порта.
Все в железе пробовалось на камне STM32F103ZE, на аналогах тоже должно взлететь.


Забиваем.


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

STM предоставила нам библиотеку STM32F10x and STM32L1xx USB full-speed device library, версия 3.3.0
Описание к ней: документ CD00158241 — UM0424 User manual

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

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

Также нам пригодятся следующие стандарты
USB 2.0 Specification
USB CDC

В составе библиотеки есть пример как раз организации виртуального COM-порта. Правда там он сделан как мост USART-USB, но мы же не боимся трудностей?

Цель этой статьи — разобрать приложение, делающее следующее:


Поджигаем


Собираем проект из нужных нам библиотек: CMSIS, SPD, USB. Дерево проекта прилагаю.

Как обычно, самое интересное в папочке /src. Вот её-то и будем разбирать.

Неторопливо курим


Начнем с раскуривания библиотеки от STM



Эта библиотека предназначена и для connectivity-line устройств (это STM32F105/107), у них не просто USB FS, а USB OTG. Поскольку камней с OTG у меня пока нет, сконцентрируемся на простом USB FS.
В примере, который мы взяли за основу, есть куча дефайнов, как раз на случай отличить connectivity-line от других демоплат. Я эти дефайны повырезал, чтобы глаза не мозолили.

Cо всей картинки нас интересует High Layer — синенькие квадратики, которые и составляют, собственно, пользовательскую часть библиотеки. Их мы меняем под свои нужды, все остальное остается неизменным. Если заглянуть в папочку /src, то видно, что все эти файлики там и собраны.

Первая затяжка — usb_conf.h


Самые общие настройки библиотеки.
Указано сколько у нас будет endpoints (а нам их надо 4 штуки — нулевой для начального конфигурирования устройства, один для команд, один для приема и один для передачи).
Также расписаны, какие нам будут нужны коллбэки. Все взаимодействие библиотеки и пользовательской программы построено на обратных вызовах: в случае возникновения какого-либо события вызывается функция с заданным названием. А уже содержание этой функции — проблема разработчика.
Нам будет нужен SOF_CALLBACK – сигнал начала фрейма (Start Of Frame). По нему будем выплевывать то, что накопилось в буфере передачи.
Также резервируем еще два коллбэка — на прием и передачу. По приему символа будем мигать светодиодами, чтобы уж как-нибудь задействовать канал приема.

Упс, кончился файл. Короткая получилась затяжка.

Файл берем из примера, ничего не меняем.

Вторая затяжка — usb_desc.h / usb_desc.c


В этих файлах находятся дескрипторы устройства и эндпоинтов.
Информация по стандартным дескрипторам есть в стандарте USB, в разделе 9.6 Standard USB Descriptor Definitions
Специфические дескрипторы описаны в USB CDC, раздел 5 (так и называется Descriptors),

Эти все тонны текста в стандартах для того, чтобы USB стала действительно Universal. Поэтому тщательно выведена классификация устройств и интерфейсов — чтобы глянув на дескриптор хост сразу понял, что с этим делать.

Особо подробно разбирать смысла не вижу — это не характерно для STM32, это общая боль разработчиков USB устройств.

Файл берем из примеров, ничего не меняем.

Продолжение дескрипторов — usb_prop.h / usb_prop.c


В этих файлах описана таблица реакции на запросы. Все запросы к устройству пронумерованы и стандартизованы. В этих файлах определяется массивы функций Device_Property, User_Standard_Requests и просто массивы String_Descriptor, например. Это все используется ядром. Когда в устройство приходит определенный запрос, например «Выдай мне дескриптор устройства», то ядро посмотрит в таблице, какая функция обрабатывает этот запрос, вызовет эту функцию, а оно уже отработает:
uint8_t *Virtual_Com_Port_GetDeviceDescriptor(uint16_t Length)
{
  return Standard_GetDescriptorData(Length, &Device_Descriptor);
}


Опять же, берем файл из примеров.

Прерываемся — usb_istr.h / usb_istr.c


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

Прерывание будет настраиваться в файле hw_config.c, обработчик выглядит очень просто:

void USB_LP_CAN1_RX0_IRQHandler(void)
{
USB_Istr();
}

его можно разместить в файлах stm32f10x_it.*, а в данном проекте, чтобы не растекаться мысью по древу, я его включил в файл main.c

И тут особо ничего не меняем, все как в примере.

Питание — usb_pwr.h / usb_pwr.c


Здесь все, что касается питания. По стандарту, устройство может быть переведено в режим спячки, тогда, если оно питается от порта, нужно позаботиться об отключении жрущей периферии. Поскольку у нас плата питается отдельно, то особого смысла в управлении питанием нет. Просто устройство формально переводится в состояние SUSPENDED.

Ничего не меняем, файл из примеров.

Коллбэки — usb_endp.c


Этот файл я переписал, поскольку у нас не будет работы с USART, как это было в примере. Приведу код с комментариями:


/* Сколько фреймов будет между отсылкой наших пакетов. */
#define VCOMPORT_IN_FRAME_INTERVAL             5

// буфер приема данных из USB, исходя из этих данных будем мигать светодиодами
uint8_t USB_Rx_Buffer[VIRTUAL_COM_PORT_DATA_SIZE];

// для нас отправка, для хоста IN. Путает немного название, но справимся
// выполняем отправку данных, накопленных в буфере USART_Rx_Buffer
// название буфера не менял, так и осталось историческое
void EP1_IN_Callback (void)
{
	Handle_USBAsynchXfer();
}

// коллбэк при приеме данных от хоста. Анализируем посимвольно и мигаем светодиодами
void EP3_OUT_Callback(void)
{
  uint16_t USB_Rx_Cnt;
  uint16_t i;
  
  /* Get the received data buffer and update the counter */
  USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, USB_Rx_Buffer);

  for (i=0; i<USB_Rx_Cnt; i++) {
	  USB_SetLeds(USB_Rx_Buffer[i]);
  }
  
  /* Enable the receive of data on EP3 */
  SetEPRxValid(ENDP3);
}

// коллбэк по началу фрейма. 
// Через определенное количество фреймов сбрасываем накопившийся буфер.
void SOF_Callback(void)
{
  static uint32_t FrameCount = 0;
  
  if(bDeviceState == CONFIGURED)
  {
    if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)
    {
      /* Reset the frame counter */
      FrameCount = 0;
      
      /* Check the data to be sent through IN pipe */

      Handle_USBAsynchXfer();

    }
  }  
}


Работа с железом — hw_config.h / hw_config.c



В этом файле собраны функции инициализации периферии, прерываний, функции управления светодиодами и работы с буфером отправки.

USB-порт и подтяжки на моей плате сделаны по такой схеме:


И USB_EN заведен на пин PF11. Значит надо не забыть его проинициализировать и дернуть вовремя.


void Set_System(void);
void Set_USBClock(void);
void Enter_LowPowerMode(void);
void Leave_LowPowerMode(void);
void USB_Interrupts_Config(void);
void USB_Cable_Config (FunctionalState NewState);
void Get_SerialNum(void);


void Handle_USBAsynchXfer (void);
void USB_Send_Data(uint8_t data);
void USB_SetLeds(uint8_t LED_Command);


По сравнению с примером, выкинуты функции инициализации и работы с USART, добавлена функция мигания светодиодом.


void USB_SetLeds(uint8_t LED_Command) {
	switch (LED_Command) {
	case 'A': {
	    GPIO_SetBits(GPIOF, GPIO_Pin_6);
	    break;
	}
	case 'B': {
	    GPIO_SetBits(GPIOF, GPIO_Pin_7);
	    break;
	}
	case 'C': {
	    GPIO_SetBits(GPIOF, GPIO_Pin_8);
	    break;
	}
	case 'D': {
	    GPIO_SetBits(GPIOF, GPIO_Pin_9);
	    break;
	}
	case 'a': {
	    GPIO_ResetBits(GPIOF, GPIO_Pin_6);
	    break;
	}
	case 'b': {
	    GPIO_ResetBits(GPIOF, GPIO_Pin_7);
	    break;
	}
	case 'c': {
	    GPIO_ResetBits(GPIOF, GPIO_Pin_8);
	    break;
	}
	case 'd': {
	    GPIO_ResetBits(GPIOF, GPIO_Pin_9);
	    break;
	}
	case '1': {
	    GPIO_SetBits(GPIOF, GPIO_Pin_6 | GPIO_Pin_7 |GPIO_Pin_8 |GPIO_Pin_9);
	    break;
	}
	case '0': {
	    GPIO_ResetBits(GPIOF, GPIO_Pin_6 | GPIO_Pin_7 |GPIO_Pin_8 |GPIO_Pin_9);
	    break;
	}
	}
}


По приему символов 'A', 'B', 'C', 'D' – зажигаются соответствующие светодиоды, 'a', 'b', 'c', 'd' – гасятся.
'1' и '0' — соответственно зажигают и гасят все светодиоды.

Ну и все, что будет передано функции USB_Send_Data() попадает в буфер, а затем и через USB – в комп.

Уфф. С USB вроде закончили.

Теперь в головной программе можно просто вызывать USB_Send_Data() и посимвольно передавать данные в комп.

Если мы на этом остановимся, то размер кода будет порядка 11 кБ:

arm-none-eabi-size  --format=berkeley test03.elf
   text	   data	    bss	    dec	    hex	filename
  11660	    236	   2744	  14640	   3930	test03.elf


Более двух килобайт bss – это буферы приема и передачи. Понятное дело, их можно уменьшить.

Выдыхаем — printf()


Но мы же хотим, чтобы вывод функции printf() перенаправлялся в наш свежесозданный COM-порт. Подключаем <stdio.h> и офигеваем от количества ошибок линковки:


sbrkr.c:(.text+0x12): undefined reference to `_sbrk'
writer.c:(.text+0x16): undefined reference to `_write'
closer.c:(.text+0x12): undefined reference to `_close'
lseekr.c:(.text+0x16): undefined reference to `_lseek'
readr.c:(.text+0x16): undefined reference to `_read'
fstatr.c:(.text+0x14): undefined reference to `_fstat'
isattyr.c:(.text+0x12): undefined reference to `_isatty'
collect2: ld returned 1 exit status
cs-make: *** [test03.elf] Error 1


А тут все просто — стандартная библиотека ввода-вывода подразумевает работу с файлами, но в больших системах есть ОС, которая собственно и занимается организацией файлов для программ пользователя. А поскольку у нас нет никакой ОС, то библиотека вполне справедливо недоумевает «А что же мне делать-то?»

Вот, чтобы это обойти, в проект включается файл newlib_stubs.c
В нем находятся функции-заглушки ко всем вышеперечисленным ошибкам. Но и кроме этого, есть одна функция, которую нам и надо переписать, чтобы весь вывод шел в USB:


int _write(int file, char *ptr, int len)
{
    int n;
    switch (file)
    {
    case STDOUT_FILENO: /*stdout*/
        for (n = 0; n < len; n++)
        {
        	USB_Send_Data((uint8_t) * ptr++);
        }
        break;
    case STDERR_FILENO: /* stderr */
        for (n = 0; n < len; n++)
        {
        	USB_Send_Data((uint8_t) * ptr++);
        }
        break;
    default:
        errno = EBADF;
        return -1;
    }
    return len;
}


Видим, что как раз и вызывается наша функция USB_Send_Data()

Все, компилим, собираем, запускаем.


arm-none-eabi-size  --format=berkeley test03.elf
   text	   data	    bss	    dec	    hex	filename
  19744	   1520	   2808	  24072	   5e08	test03.elf
Finished building: test03.siz


Архив с проектом прилагаю — это на случай, если кто не заметит маленькие буковки внизу :)

  • +6
  • 01 февраля 2012, 15:41
  • steel_ne
  • 1
Файлы в топике: test03.zip

Комментарии (104)

RSS свернуть / развернуть
Все, я в печали )
0
А какой IDE ты пользуешься? Вроде редактор кода выглядит симпатично, на кейл или иар не похоже. Больше похоже на разновидность эклипса.
0
  • avatar
  • Vga
  • 01 февраля 2012, 18:34
Это и есть чистейший эклипс. Я в прошлых статьях писал, решил не повторяться )
0
Я в нем и под AVR, и под STM32 пишу. Удобно, привычно
0
Собственная сборка, не что-нить вроде кокоса?
0
Скачал с сайта оригинальный, поставил плагин AVR и CDT и все. Сборкой это назвать язык не поворачивается )

Где-то в сообществе была статья как установить эклипс и отладчик для STM. Все по ней делал — получилось
0
А отладку AVR из-под эклипса пробовал?
0
AVR — нет. У меня только программатор под них был, не заморачивался отладкой.
0
Добрый день скачал пример настроил эклипс пытаюсь комиплить
компиляция доходит до
Config\startup_stm32f10x_hd.o
и начинает сыпать типа такого
src\usb_pwr.o: In function `_init':
usb_pwr.c:(.text+0x30): multiple definition of `_init'
c:/program files/codesourcery/sourcery g++ lite/bin/../lib/gcc/arm-none-eabi/4.5.2/thumb2/crti.o:(.init+0x0): first defined here
src\usb_pwr.o: In function `_fini':
usb_pwr.c:(.text+0x3c): multiple definition of `_fini'
c:/program files/codesourcery/sourcery g++ lite/bin/../lib/gcc/arm-none-eabi/4.5.2/thumb2/crti.o:(.fini+0x0): first defined here
src\usb_prop.o: In function `__exidx_start':
usb_prop.c:(.text+0x48): multiple definition of `__exidx_end'
src\usb_pwr.o:usb_pwr.c:(.text+0x48): first defined here
src\usb_prop.o: In function `__exidx_start':
usb_prop.c:(.text+0x48): multiple definition of `_etext'
src\usb_pwr.o:usb_pwr.c:(.text+0x48): first defined here
src\usb_prop.o: In function `__bss_start__':
уважаемый автор не подскажешь в чем может быть дело?
0
Проверьте, чтобы ld-скрипт был подключен правильно. Я его указал в абсолютных путях, может не находит?
0
в файл hw_config.h
были добавлены строки
#include «stm32f10x_usart.h»
#include «misc.h»
#include «stm32f10x_gpio.h»
#include «stm32f10x_rcc.h»
так как лезли ошибки

..\src\hw_config.c:33:19: error: expected '=', ',', ';', 'asm' or '__attribute__' before 'USART_InitStructure'
..\src\hw_config.c: In function 'Set_System':
..\src\hw_config.c:54:3: error: 'GPIO_InitTypeDef' undeclared (first use in this function)
..\src\hw_config.c:54:3: note: each undeclared identifier is reported only once for each function it appears in
..\src\hw_config.c:54:21: error: expected ';' before 'GPIO_InitStructure'
..\src\hw_config.c:65:3: warning: implicit declaration of function 'RCC_APB2PeriphClockCmd'
и тп
0
надо добавить только #include «stm32f10x.h»

но странно, у меня и без него собралось…
0
а можно мыло или аську для проверки настроек эклипса?
0
ушло в личку
0
А есть пример такого же, но с перламутровыми пуговицами? (Композитное устройство, состоящее из двух и более виртуальных COM портов). Хотя бы в общих чертах, что в проекте изменить надо?
0
  • avatar
  • Lyrri
  • 02 февраля 2012, 23:05
изменить дескрипторы, дописать обработчики ендпоинтов. Но тут уже как драйвера воспримут это композитное устройство. Информации к дровам подробной нет, только методом тыка.
0
Десрипторов то там больше одного ))). Склоняюсь к тому, что необходимо добавлять количество интерфейсов и изменять дескриптор конфигурации под количество интерфейсов. Драйвера должны воспринять, CDC вроде как стандартное устройство. Из VCP_V1.3.1_Setup.exe достаточно выудить .inf файл. Хотя, возможно, я ошибаюсь.
0
Просто сейчас, в примере, он организован с двумя интерфейсами. Где-то на форумах читал, что если составное устройство, то не более одного интерфейса на рыло. А то типа дрова криво встанут. Правда там аудио обсуждали, но фиг его знает…
0
Спасибо, отличная статья.
0
  • avatar
  • Ras2
  • 06 февраля 2012, 11:28
А теперь можете рассказать как встроить данный функционал в предидущий проэкт с FreeRTOS?
0
  • avatar
  • Roman
  • 09 февраля 2012, 09:07
Огромное спасибо за статью! То, что надо.
0
  • avatar
  • _YS_
  • 09 февраля 2012, 22:10
делал все как в этой статье. немного переделал пример: при нажатии на кнопку клавиатуры — мигание диодом. НО в терминале нету ничего. подскажите как решить проблему. грешу на функцию printf().
0
Так чуть короче и нету дублирования кода:
void USB_SetLeds(char LED_Command) {
        switch (LED_Command) {
        case 'A':
        case 'a':
            GPIO_SetBits(GPIOF, GPIO_Pin_6);
            break;
        case 'B':
        case 'b':
            GPIO_SetBits(GPIOF, GPIO_Pin_7);
            break;
        case 'C':
        case 'c':
            GPIO_SetBits(GPIOF, GPIO_Pin_8);
            break;
        case 'D':
        case 'd':
            GPIO_SetBits(GPIOF, GPIO_Pin_9);
            break;
        case '1':
            GPIO_SetBits(GPIOF, GPIO_Pin_6 | GPIO_Pin_7 |GPIO_Pin_8 |GPIO_Pin_9);
            break;
        case '0':
            GPIO_ResetBits(GPIOF, GPIO_Pin_6 | GPIO_Pin_7 |GPIO_Pin_8 |GPIO_Pin_9);
            break;
        }
}

Ещё лучше сравнивать объекты одного типа ('a' — имеет тип char, а не uint8_t).
0
  • avatar
  • amx
  • 21 февраля 2012, 14:21
Возможно тогда уже лучше что-то типа такого:

typedef struct {
	char key;
	uint16_t bits;
	char reset;
} action_t;
action_t actions[] =
{
		{'A', GPIO_Pin_6},
		{'a', GPIO_Pin_6},
		{'B', GPIO_Pin_7},
		{'b', GPIO_Pin_7},
		{'C', GPIO_Pin_8},
		{'c', GPIO_Pin_8},
		{'D', GPIO_Pin_9},
		{'d', GPIO_Pin_9},
		{'1', GPIO_Pin_6 | GPIO_Pin_7 |GPIO_Pin_8 |GPIO_Pin_9},
		{'0', GPIO_Pin_6 | GPIO_Pin_7 |GPIO_Pin_8 |GPIO_Pin_9, RESET_BITS},
};

void USB_SetLeds(char LED_Command) {
	for(int i = 0; i < sizeof(actions)/sizeof(action_t); i++) {
		if (LED_Command == actions[i].key) {
			actions[i].reset ? GPIO_ResetBits(GPIOF, actions[i].bits) : GPIO_SetBits(GPIOF, actions[i].bits);
		}
	}
}

Тут дублирования кода нет совсем, независимо от того, сколько вариантов надо обработать.
0
Сорри, при копировании потерял вот это:

#define RESET_BITS	1
0
Опс, извиняюсь, не уследил различия между 'A' и 'a'
#define RESET_BITS      1
typedef struct {
        char key;
        uint16_t bits;
        char reset;
} action_t;
const action_t actions[] =
{
                {'A', GPIO_Pin_6},
                {'a', GPIO_Pin_6, RESET_BITS},
                {'B', GPIO_Pin_7},
                {'b', GPIO_Pin_7, RESET_BITS},
                {'C', GPIO_Pin_8},
                {'c', GPIO_Pin_8, RESET_BITS},
                {'D', GPIO_Pin_9},
                {'d', GPIO_Pin_9, RESET_BITS},
                {'1', GPIO_Pin_6 | GPIO_Pin_7 |GPIO_Pin_8 |GPIO_Pin_9},
                {'0', GPIO_Pin_6 | GPIO_Pin_7 |GPIO_Pin_8 |GPIO_Pin_9, RESET_BITS},
                {'\0', 0}
};

void USB_SetLeds(char LED_Command) {
    int i = 0;
    while (actions[i].key != LED_Command && actions[i].key != '\0' )
    {
        i++;
    }
    if (LED_Command == actions[i].key) {
        (actions[i].reset ? GPIO_ResetBits : GPIO_SetBits)(GPIOF, actions[i].bits);
    }
}
0
Да, проглядел.

P.S. а вот вешать в конце маркер в виде 0 я принципиально не хотел. да и врядли модифицированный вами поиск проще или лучше читается.
0
В вашем варианте можно сделать обратный порядок обхода массива, сравнение на отрицательность после декремента скорее всего будет быстрее, чем сравнение с константой.

Прошу прощения, что-то увлёкся :)
0
Это мелкая оптимизация, при достаточном числе команд табличку можно упорядочить по кодам и сделать бинарный поиск. Это, кстати, еще одна причина не делать маркер в конце, тем более, что структура статическая.
0
Зачем два раза сравнивать?
while(actions[i].key)
{
if(LED_Command=actions[i].key)
(actions[i].reset? GPIO_ResetBits(GPIOF, actions[i].bits): GPIO_SetBits(GPIOF, actions[i].bits);
}
0
Забыл в конце if (LED_Command == actions[i].key) поставить return.
0
Свитч я думаю оптимальней при малом числе команд.
0
Согласен. Но когда дело доходит до внешних команд — их мало почему-то не бывает. А вот таблица куда проще в сопровождении.
0
Да и читать такой код проще, IMHO. Наконец, если команд будет много, можно их упорядочить по алфавиту и перейти к бинарному поиску, с кейсами этот номер не пройдет.
0
Можно прикинуть на пальцах: свич на один кейс это одна проверка+переход+положить два параметра+вызов+переход в конец свичаю. Если очень грубо оценивать каждый шаг как 2 байта, то это 10байт. Одна запись в actions занимает (если паковать структуру) 4 байта ну и общий код (~равен двум кейсам, один на сравнение ключа и вызов и один на внешний цикл). Получается, что уже с тремя командами может быть выиграш по размеру. Понятно, что оценка очень грубая, но врядли реальное число кейсов, которые выгоднее таблички будет больше 4-5.
0
Ну не по размеру, так по скорости. В реальном проекте впрочем и правда лучше таблица. Кроме того, есть еще оптимизатор. Мож заметит, что кейсы отличаются тока параметрами и переместит вызов функции на выход из свитча. Да и поиск в свитче афайк бинарный. В реальном проекте впрочем и правда лучше таблица.
0
В реальном проекте команды вряд-ли будут однобитные. Либо жесткая бинарная структура, либо (если требуется человекочитаемость) какая-либо грамматика, которую без анализатора с наскока и не разберешь.
0
Я бы так не сказал. Тот же бутлоадер AVR109 использует однобайтные команды. Мой программатор для AT89C2051 тоже использует однобайтные команды, хотя изначально предполагалось что работа с ним будет вестись через терминал (и кстати, там именно кейс по командам).
0
Ну с бутлоадером я через терминал ни разу не общался )

А вот, например, реализация АТ-команд — уже достаточно нетривиальное решение, там просто кейсом не обойтись.
0
Твой вариант работать не будет — «a» и «A» разные команды.
0
Да, уже сам увидел. Чуть выше исправил.
0
А что ето за адреса указаны в функции Get_SerialNum файла hw_config.c?
Device_Serial0 = *(__IO uint32_t*)(0x1FFFF7E8);
Device_Serial1 = *(__IO uint32_t*)(0x1FFFF7EC);
Device_Serial2 = *(__IO uint32_t*)(0x1FFFF7F0);
у меня в кейле при дебаге на них пишет *** error 65: access violation at 0x1FFFF7E8: no 'read' permission
0
Читаем в Reference manual (RM0008):

28.2 Unique device ID register (96 bits)
The unique device identifier is ideally suited:
● for use as serial numbers (for example USB string serial numbers or other end
applications)
● for use as security keys in order to increase the security of code in Flash memory while
using and combining this unique ID with software cryptographic primitives and
protocols before programming the internal Flash memory
● to activate secure boot processes, etc.
The 96-bit unique device identifier provides a reference number which is unique for any
device and in any context. These bits can never be altered by the user.
The 96-bit unique device identifier can also be read in single bytes/half-words/words in
different ways and then be concatenated using a custom algorithm.
Base address: 0x1FFF F7E8
0
У кого-нибудь были проблемы с драйвером? У меня 64х битная 7ка и нехочет распознавать девайс.
Отладочная плата — HY-Mini STM32V с STM32А103VCT6 на борту, второстепенный USB подключен как и в примере только USB_EN на другом пине. Проэкт собрался после пары добавленных '#include' так как импользовал КуКокс и смены LED пинов на собственные.
0
Linux его распознаёт? Ядро поддерживает? Провобал кто-нибудь?
0
Посмотрел по именам модулей. В ядре Linux версии 3.0 нет модудей с поддержкой виртуального последовательного порта для STM32. Дистрибутив Ubuntu 11.10.
0
Спасибо огромное ресурсу. Очень помог!!!

Внимание!!!
Использовал этот пример. После долгих ночных побайтных ковыряний выяснилась неприятность.
При транзакциях передачи.
а Именно.
Передачу надо делать в функции SOF_Calback. а не в EP3_OUT_Callback.
связано скорей всего Handle_USBAsynchXfer.
Ошибка вылитала только когда много данных с ПК кидал.

Теперь как часы.
Реально без потерь на 72 мгц с кодированием в эскейп последовательности и crc32.
150-200 килобайт в сек больше не пробовал.
И сам правленый код.

void EP3_OUT_Callback(void)
{
  unsigned int USB_Rx_Cnt;
  unsigned int i,j;
  //	Get the received data buffer and update the counter
  USB_Rx_Cnt = USB_SIL_Read(EP3_OUT, USB_Rx_Buffer);
  for (i=0; i<USB_Rx_Cnt; i++)
  {
	if(dec(USB_Rx_Buffer[i])) /// ДО КОНЦА ДОДЕКОДИРОВАЛИ
	{
	      if(dec_check()) /// ПРОВЕРКА CRC32
              {
        		/// PARSED OK
			if(cmd_parse()==1) /// КОМАНДА ОТРАБОТАНА
			{ /// В НЕЙ И ЗАПОЛНЯЕТСЯ ENC БУФЕР СМ НИЖЕ
				dec_reset(); // СБРОСИТЬ ЕНКОДЕР
			}
                }
	}
  }
	SetEPRxValid(ENDP3);
  // Enable the receive of data on EP3
  
}
//
void SOF_Callback(void) /// 200 hz
{
	int i;
  static uint32_t FrameCount = 0;
  if(bDeviceState == CONFIGURED)
  {
    if (FrameCount++ == VCOMPORT_IN_FRAME_INTERVAL)
    {
                /// ПРОВЕРЯЕМ НОВЫЕ ДАННЫЕ
		for(i=0;i<enc_size();i++)
		{
			USB_Send_Data(enc_buffer()[i]);
		}
		enc_reset();
		esc_timeout();
      // Reset the frame counter
      FrameCount = 0;
      // Check the data to be sent through IN pipe
			
      Handle_USBAsynchXfer();
    }
  }
}
0
поясните кто-нть, кто понимает — зачем нужна 2 endpoint?
по ней ни обработок, ни каллбэков нету.
0 — для контоля
1 — прием
3 — передача.

а 2?
0
  • avatar
  • kab
  • 01 июня 2012, 23:01
Я лично был уверен, что тут используются только три ендпоинта: 0, 1, 3. Ткните, пожалуйста, носом, где используется в коде EP2? Вроде нет его?
0
А кто-нибудь знает, как вообще с этим устройством общаться?

У меня получается его открыть, считать дескриптор устройства и конфигурации. При попытке считать дескриптор ендпоинта ошибка «ERROR_PIPE», при попытке установить конфигурацию — «ERROR_BUSY». Любые записи в любые BULK ендпоинты мгновенно сообщают «ERROR_IO».

Пытался с оригинальной программой от STM для работы с COM портом работать, выкидывая аппаратное общение с собственно COM портом и вставляя загушки. Результат тот же.

Может тут сначала через control ендпоинт надо что-то передать?
0
Проблему решил.

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

Для меня было странно, что захваченное устройство таки открывалось.

Блин, целый день пропал!
0
Господа, помогите осознать, что происходит.

Микроконтроллер STM32F102C8T6.

В прошивке виртуального COM порта внутрь калбэка void EP1_IN_Callback(void) вставил мигание светодиодом, если были отправлены данные. Судя по миганию, вызывается постоянно, не зависимо от того, читает ли кто-нибудь данные или нет, сразу после вставки устройства в компьютер. Помогите осознать, с чем это связано.

То есть, получается, что всё, что было отправлено до того как подключили терминал, куда-то считывается и пропадает бесследно.

При подключении терминала всё, что микроконтроллер отправляет, появляется на терминале, никуда ничего не пропадает. После отключения терминала опять всё, как было — постоянные чтения и данные опять уходят в никуда.

Я подозревал, что система так работает с виртуальным COM портом. Пробовал в дескрипторе тип устройства с CDC (0x02) заменить на VendorSpecific (0xFF). Результат тот же — постоянные чтения, но устройство /dev/ttyACM0 не появляется. Может ещё что-то надо было поменять?

Если работать с устройством через функции системы fctrl, open, close, read, write, то всё норм. А вот если пытаться через libusb напрямую обращаться к ендпоинтам, то запись работает без проблем, а вот чтение обычно заканчивается прочтением сообщения нулевой длины (но без ошибок), но иногда удаётся прочитать, то что было отправлено. Такое впечатление, что моя программа соревнуется с системой, кто успеет первым считать, тот и получит данные.

Раньше один раз делал своё устройство, но не на основе виртуального COM порта, а на базе MassStorage примера для микроконтроллера LPC2148. Так там такого не было: всё отправленное приходило и только тогда, когда программа попросит, а не постоянно.
0
Парни Need Hellp!!! Нужен такой же проект, только для F205xx.
-1
www.st.com/stonline/stappl/resourceSelector/app?page=resourceSelector&doctype=FIRMWARE&SubClassID=1520
С библиотекой для USB там примеры идут
0
Доброго времени суток всем. Пытаюсь запустить USB с помощью этой библиотеки на STM32F107RCT6. Получаю разное поведение на разных компах (рабочем и домашнем). На рабочем вообще почти глухо. При втыкании кабеля получаю прерывание RXFLVL: RxFIFO non-empty. Соответственно библиотечный обработчик вычитывает из FIFO пакет, судя по документации, после этого в регистре DOEPINT0 бит STUP должен стать единичным. Этого не происходит. Ну собственно на этом вся «инициализация» заканчивается.
Дома немного по-другому. Библиотека вызывает callback CustomHID_GetDeviceDescriptor (я за основу брал пример HID устройства). callback отрабатывает и все. дальше опять глухо. Сделал элементарный лог событий — бросал в массив код события (по-сути код вызванного callback-а). Получается такая картина. Вызывается CustomHID_GetDeviceDescriptor. После этого дважды CustomHID_Reset. Так повторяется раза 3. Потом все замолкает. Windows соответственно сообщает что устройство — неизвестное. В свойствах в диспетчере устройств VID/PID=0. Все дескрипторы содраны с примера HID без изменений.
Может у кого-нибудь есть мысли в чем дело?
0
Разобрался. Сам же и отвечаю для тех, кто с таким столкнется. Проблема классическая — неверная частота контроллера. Но причина её возникновения следующая.
В файле system_stm32f10x.c из STM32 StdPeriphLib
в функции static void SetSysClockTo72(void)
RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 |
RCC_CFGR_PLLMULL9);
Первая строка сбрасывает бит PLLXTPRE регистра RCC_CFGR. Что не есть хорошо, так как это младший бит коэффициента делителя частоты PREDIR1.
В результате коэффициент деления оказывался на 1 меньше. Частота проца была около 82МГц
Решение проблемы — переделать первую строку в следующее:
RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
0
Пара вопросов:
1. Много ли надо переделывать, чтобы запустить проект на STM32F4?
2. Не собирается проект (открывал в Eclipse Helios)
Из консоли:
cs-make all
Building file: ../src/hw_config.c
Invoking: ARM Sourcery Windows GCC C Compiler
arm-none-eabi-gcc -DSTM32F10X_HD -DMALLOC_PROVIDED -DUSE_STDPERIPH_DRIVER -O0 -ffunction-sections -Wall -std=gnu99 -Wa,-adhlns=«src/hw_config.o.lst» -c -fmessage-length=0 -MMD -MP -MF«src/hw_config.d» -MT«src/hw_config.d» -mcpu=cortex-m3 -mthumb -g3 -gdwarf-2 -o«src/hw_config.o» "../src/hw_config.c"
../src/hw_config.c:17: fatal error: usb_lib.h: No such file or directory
compilation terminated.
cs-make: *** [src/hw_config.o] Error 1
0
на форуме были готовые примеры под ф4, поищи поиском
0
Люди, а кто-нибудь в кокосе собирал сей проект. У меня вот после некоторых доработок (инклюды добавлял) он собрался, но корректно работать отказался, камень STM32F103RET6. Под виндой определился, винда сказала, что устройство установлено, но потом тут же сказала, что устройство работает неправильно…
P.S.: я в стм-ках начинающий…
0
я собирал, в кококсе на собственной схеме под ret6 и c8t6.
работает на ура, проверяй железо.
0
А можете поделиться своим проектом (на всякий случай, если не жалко)?
0
Была такая проблема, оказалось причина в оптимизации. Для начала рекомендую отключить оптимизацию вообще — убрать опцию -O.
0
Да не жалко, но у меня проект уже давно сильно переработанный и завязанный на другие железки.
Просто брал из статьи и правил «дерганье» ножкой при подключении USB и всё.
0
Есть простой проект(CooCox) под STM32F4Discovery — в свое время он мне очень помог(сам проверял на F4 — все работает).
github.com/xenovacivus/STM32DiscoveryVCP
0
Теперь человек явно будет спокоен… Спустя 2 года=)))))
Ну Вы хоть дату коммента смотрите…
0
Вопрос не совсем по теме, но все-таки, в какой программе нарисована электрическая схема?
0
  • avatar
  • NBS
  • 25 ноября 2012, 23:59
картинка вырезалась из документа PDF. Судя по метаданным, эта схема нарисована в Protel DXP
0
Ну или альтиум, учитывая, что протел — его предок.
0
Отличная статья! А если не использовать внешнюю подтяжку для USB_DP, где можно включить внутреннию, в каком файле, ищу найти ни как не могу. Не подскажете?
0
Приветствую! У меня такой вопрос. В проекте инициализирована 18 линия EXTI. Как я понял, по этой линии генерируется прерывание, когда USB зашуршит, т.е. что бы проснуться по приходу данных с USB. А если мк у меня не спит, молотит постоянно, могу я выкинуть работу с EXTI и соответствующее прерывание? Или я не прав?
0
Товарищи, а вот такая штука… Может, есть у кого предположения.
Я работу с USART не вырезал, т.к., хотел попробовать заюзать IRDA, и вот с этим самым USART`ом есть проблемка — при передаче любых данных в Windows — (отправкой данных в USART) зацикливается на проверке флага TXE (TC, ессно, тоже) навечно. А вот с Linux такой проблемы нету — принимает и передает вполне себе нормально :)
0
Винды пробовал разные, и ХР и 7, 32 и 64-битные — пофигу.
0
Полезная публикация. Спасибо автору! Мне надо было поднять Virtual COM на STM32L152. Пришлось внести ряд изменений в проекте (в основном связаны с отличием функций стандартной библиотеки для L серии. Свой проект под STM32L152 я выложил в отдельной публикации USB Virtual COM на STM32Lxx — Keil Project
0
Повторите пожалуйста! Ссылка уже не работает!
0
0
Кто подскажет максимальную скорость данного Virtual COM?
0
Если не ошибаюсь, скорость отправки (именно отправки, из буфера) ограничена возможностями USB.
На примере Linux в качестве хост-системы (про Win мало чего подскажу в этом плане):
при подключении создается в /dev устройство ttyACM0 (частный случай), которое суть файл, читать/ из/в него можно с «неограниченной» скоростью. В случае реального COM, скорость обмена строго зависит от настроек физической части. В случае с USB нет такого ограничения (в смысле baudrate не устанавливается). Легко проверить с помощью терминала — для COM необходимо точно установить скорость обмена, с нашим virtual COM — терминал можно запустить с любым значением скорости и все будет работать.
0
Никак не могу заставить работать. МК виснет на настройках:
USB_Interrupts_Config();

USB_Init();

И именно когда функции стоят парой, если одну из них убрать, то зависание прекращается, но и комп его не определяет. В чем может быть проблема? МК STM32F103VCT6
0
Возможно у вас не определен вектор прерывания USB
0
Да вроде все точно как в примере. Или там еще что-то надо настраивать?
0
Посмотрите в режиме отладки где именно зависает
0
Похоже st.com реорганизует свой сайт каждые 30 минут ))
Ссылка на библиотеку битая…
0
ааааааа, собрал проект под stm32f107VC, целый день промудохался.
В итоге оказалось, что st.com ествественным образом игнорирует Linux, ибо драйвера только под $MS
0
А что за дистрибутив? libusb?
У меня просто без установки драйверов все работает (kubuntu). Да и всегда подобного рода устройства запускаются сразу (та же FT).
Что пишут
1. udevadm monitor (при подключении/отключении девайса)
2. lsusb (при подключеном)
3. ls /dev/ttyA*
0
вроде видится как ttyAHC, и терминал позволяет обращаться к нему ...
Но я совершенно не понимаю, где можно выцепить сами данные?
Если в примерах на 103-ю серию все было очень понятно, то примеры usb на 105-107 просто кошмар.
Мои попытки подрихтовать пример HID-а под себя, так ничего и принесли, добавил Endpoint, исправил дескриптор — репорты, теперь контроллер падает при приеме репорта, вот решил VCP договнокодить )
0
Повсюду «волшебные» переходы с магическими цифрами, операции с адресами, разобраться почему падает при попытке считать принятый репорт — не реально (
0
PS: где принять-передать разобрался, static uint16_t VCP_DataRx (uint8_t* Buf, uint32_t Len)
0
VCP_DataTx
0
Здравствуйте, уважаемые. Будьте так добры, подскажите. У меня микруха stm32f103c8. Собрал под нее проект по примеру, прошил, а usb на компе вообще никак не отображается, нет его. Отладочная плата показала, что в прерывание не происходит. Кварц стоит на 8Mhz. Может чего с частотой не так. В общем, подскажите люди добрые и знающие)
0
если схема как на оригинальных бордах, проблем вроде быть не должно.
А так… проверьте линию VBUS если используется и подтяжку на транзисторах сигнальной линии.
0
Я транзистор не вешал, просто напрямую D+ и D- подсоединил, а также минус выровнял. А что вы имеете ввиду под сигнальной линией (для особо одаренных)? Прерывание вообще тут происходит от сигналов порта, или внутренним таймером?
0
А подтяжку с D+ (или D- ли) на +3.3В ты не забыл?
0
А зачем она, я на стремном PIC напрямую вешал, и то все работала, подтяжка, как я понимаю не обязательна, или я не прав
0
Затем, что это требование шины USB и без нее хост даже не обратит внимание, что в него какую-то хрень воткнули.
Обрати внимание, какую линию подтягивать — зависит от желаемого скоростного режима (LS или FS/HS). Я не помню какую линию для какого режима подтягивать надо.
0
Спасибо, буду знать, странно, что PIC так работал. А резистор сколько вешать, 10кОм пойдет?
0
Тут я смотрю 1,5 кОм вроде
0
Неужели нельзя почитать статьи с основами или глянуть схемы стандартных отладочных плат? Нет, не пойдет, там нужен вполне конкретный резистор — 1.5к.
0
Хорошо, спасибо. Ну, лучше же все таки спросить знающего человека, постоянно какие-то нюансы возникают)
0
Нюансы? Это самые основы, которые знаю даже я, хотя я до сих пор не удосыжился толком изучить USB.
Кстати, подтяжку эту по определенным причинам принято делать управляемой. Именно поэтому там такие хитрые схемы с транзисторами.
0
То есть без ключа работать не будет, или не факт?
0
Сам с такой же фигней боролся, когда сперва собирал пример на stm32f4, потом на 103-м. В stm32f4, где есть otg, есть автоматическая подтяжка к 3-м вольтам на нужный порт, в pic'е вероятно тоже есть внутренняя подтяжка. В stm32f103, совершенно точно нужна внешняя подтяжка, согласно схеме выше или проще сразу к питанию 3.3В через 1.5k резистор.
0
Может кто подскажет. Запустил этот пример. Настроил прерывание от Systick на 1сек. в него всунул
printf("Hello world!\r\n");
— не отправляет. Начинает отправлять только если прерывание происходит где-то раз в 200 мск. Спасибо.
0
Новое место дров здесь.
Или без регистрации и СМС.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.