Usb vcp что это
STM Урок 33. HAL. USB. Virtual Com Port
Урок 33
HAL. USB. Virtual Com Port
Отладочную плату ипользуем ту же: STM32F4-DISCOVERY.
Проект создаём из проекта I2CLCD80. Назовем его USB_OTG_CDC. Запустим проект в Cube, включим USB_OTG_FS в режим Device_Only
В USB_DEVICE в разделе Class For FS IP выберем пункт Communication Device Class (Virtual Port Com).
Лапки портов PD4-PD7, PB8, PB9 отключим, это пережиток прошлых занятий
В Clock Configuration выберем следующие делители (нажмите на картинку для увеличения изображения)
В Configuration ничего не трогаем, т.к. прерывания там выставились сами.
Сгенерируем и запустим проект, подключим lcd.c и настроим программатор на автоперезагрузку.
Соберем проект. Прошьём контроллер. У нас появится неизвестное устройство, скачаем драйвер на наше виртуальное устройство usb. Для этого зайдем на сайт st.com, в строке поиска там вводим virtual com port, скачиваем и устанавливаем драйвер. Затем желательно зайти в папку с установленным драйвером, выбрать папку, соответствующую разрядности нашей операционной системы, и запускаем также установку и оттуда.
У нас скорей всего устройство установится с ошибкой (код 10)
Есть несколько типов решений, мне понравился именно этот, т.к. более простой: в файле usbd_cdc.h заменим размер пакета, вместо 512 напишем 256 в данной строке:
#define CDC_DATA_HS_MAX_PACKET_SIZE 256 /* Endpoint IN & OUT Packet size */
Соберём, прошьём и увидим, что ошибка исчезла.
Сначала попытаемся передать данные на ПК.
Для этого мы сначала откроем файл usbd_cdc_if.c и исправим там в 2х строчках 4 на 64
/* It’s up to user to redefine and/or remove those define */
#define APP_RX_DATA_SIZE 64
#define APP_TX_DATA_SIZE 64
В файле main.c закомментируем весь пользовательский код кроме инициализации и очистки дисплея
Также в main.c подключим файл usbd_cdc_if.h для видимости функций приема и передачи
/* USER CODE BEGIN Includes */
Немного изменим в главной функции строковую переменную, убавив в ней размер и добавив префикс tx
В файле usbd_cdc_if.c добавим прототип функции передачи, скопировав объявление из реализации данной функции в том же файле
/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
uint8_t CDC_Transmit_FS(uint8_t* Buf, uint16_t Len);
/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */
В main() внесём данные в строку
В бесконечном цикле попробуем эти данные отправить в порт USB, используя функцию, прототип которой мы добавили
CDC_Transmit_FS((unsigned char*)str_tx, strlen(str_tx));
/* USER CODE END WHILE */
Соберём код, прошьём контроллер и посмотрим результат в терминальной программе.
Вроде передать нам что-то удалось. Теперь попробуем что-нибудь принять. Здесь чуть посложнее, т.к. для этого используется уже обработчик прерывания, коим является в файле usbd_cdc_if.c функция CDC_Receive_FS.
Добавим ещё одну строковую глобальную переменную в main()
/* USER CODE BEGIN PV */
Объявим её также и в файле usbd_cdc_if.c
/* USER CODE BEGIN PRIVATE_VARIABLES */
extern char str_rx[21];
/* USER CODE END PRIVATE_VARIABLES */
В функцию CDC_Receive_FS в этом же файле добавим некоторый код и кое-что закомментируем
static int8_t CDC_Receive_FS (uint8_t* Buf, uint32_t *Len)
Добавим переменную в main()
Занесенные в наш буфер данные попробуем вывести на дисплей, для этого в бесконечном цикле в функции main() добавим определённый код
CDC_Transmit_FS((unsigned char*)str_tx, strlen(str_tx));
Соберём проект. Прошьём код и посмотрим результат, вводя в терминальной программе и отправляя в порт USB какие-нибудь строки.
22 комментария на “ STM Урок 33. HAL. USB. Virtual Com Port ”
«Есть несколько типов решений, мне понравился именно этот, т.к. более простой: в файле usbd_cdc.h заменим размер пакета, вместо 512 напишем 256 в данной строке….»
Просто измените размер кучи (Minimum Heap Size) в настройка CubeMX. Вместо значения 0x200 задайте 0x400.
И комп увидит устройство без ошибок.
При инициализации структур компилятору элементарно не хватает места, заданного по умолчанию, для выделения памяти.
Пардон, очепятка вышла. Не компилятору, а функции malloc.
Спасибо, так действительно проще.
Сначала не смог реализовать данный пример на SystemWorkbench в части приёма данных и передачи их из функции приёма в main посредством массива str_rx с модификатором extern – компилятор ругается на использование неопределённых переменных, а если задать ему какие-нибудь значения, то только эти заданные значения и будут передаваться. Вышел из положения объявив массив обмена str_rx в заголовочном файле usbd_cdc_if.h
Спасибо.Я сделал так.В хидер usbd_cdc_if.h добавил две строчки
extern uint8_t UserRxBufferFS[1000];
uint8_t receiveBufLen;
В метод CDC_Receive_FS добавил перед return receiveBufLen = *Len;
И в main ловил данные просто одним условием
if(receiveBufLen > 0)// если получены данные от ПК
<
HAL_Delay(250);
CDC_Transmit_FS((uint8_t*) UserRxBufferFS,receiveBufLen);
// эхо для наглядности
receiveBufLen = 0;// сброс получения
>
Всё просто,а UserRxBufferFS чистить не нужно от мусора,он сам чистится.
Здравствуйте! Спасибо огромное за ваши уроки, тут пожалуй лучший ресурс с уроками по стм32!
Хочу спросить, а как использовать CDC_Receive_FS в main.c? Я проделал в usbd_cdc_if.c «эхо», но мне нужно принимать из него и гнать дальше. Наверное вопрос больше в целом по си чем по контроллеру, а то иначе мне получается надо много всего переносить в usbd_cdc_if.c.
Думаю, что следует добавить в main.c функцию, а в файле usbd_cdc_if.c – на неё прототип и вызвать её в CDC_Receive_FS, И весь свой пользовательский код затем писать в файле main.c.
Это именно СИ. Так что обязательно подтяните свои знания по языку.
Ох, видимо сперва надо читать коментарии, прочитал тот что выше.
При переходе на USB cтолкнулся с такой проблемой. Скажем, конструкция, приведённая в примере, а именно
sprintf(str_tx,»USB Transmit\r\n»);
CDC_Transmit_FS((unsigned char*)str_tx, strlen(str_tx));
работает без проблем. Но, если я делаю так
sprintf(str_tx,»USB Transmit»);
CDC_Transmit_FS((unsigned char*)str_tx, strlen(str_tx));
CDC_Transmit_FS((unsigned char*)»\r\n», 2);
то CDC_Transmit_FS((unsigned char*)»\r\n», 2); не срабатывает (не успевает) и данные летят без переноса строки. Если ставить задержку, то работает как надо. По неопытности, может, это я и принял бы как должное, если бы перед этим не работал бы с UART где такая же конструкция работает без проблем. Для работы с UART уже написана довольно хорошая часть программы и менять её структуру очень не хочется, тем более, что данные передаются не в текстовом формате а в посылке имеется несколько меток. Что можно сделать, чтобы посылки могли идти подряд без задержки?
Скорей всего придется делать конкатенацию передаваемых строк с помощью strcat. Была аналогичная проблема при использовании CDC. Автор применял этот метод в одном из уроков.
Здравствуйте
А если я хочу передавать данные с микроконтроллера на компьютер?
Константин:
А мы их туда и передали.
Установил различные драйвера VCP от STM, но при этом плата не определяется при подключении её к компьютеру. только виден STLink Virtual COM Port. Кто уже сталкивался с такой проблемой.
Оказалась, что проблема с дровами. Надо их полностью сносить и устанавливать заново.
You can use(for example):
sprintf(str_tx,»ADC:%d \r\n»,ADC_Data);
CDC_Transmit_FS((unsigned char*)str_tx, strlen(str_tx));
where ADC_Data is your ADC value.
Спасибо за примеры. С USB в базовой библиотеке что-то не так. При первом подключении ком порт работает, но при передергивании USB – становится неизвестным устройством, иногда не сразу а через 5-10 секунд после повторного подключения…
Сейчас копаю в сторону функций вызываемых на отключение и подключение USB. Первое что кажется подозрительным, то что на подключение вызывается инициализация а на отключение USBD_LL_Suspend, затем на подключение снова инициализация, хотя есть USBD_LL_Resume. Пока дальнейших идей нет. Может что-то подскажете?
могу скачать драйвера для виртуального ком порта. У меня STM32F415RG, может есть у кого?
Usb vcp что это
Драйвер USB Virtual COM-port (VCP) CANNY 3 Tiny PRO позволяет подключать его к внешним устройствам, например к ПК, с помощью имеющегося на плате контроллера разъема microUSB, для возможности обмена с ними данными в процессе работы пользовательской диаграммы. Драйвер позволяет организовать обмен данными в дуплексном режиме, т.е. одновременно выполнять отправку и прием данных.
Объем приемного и передающего буферов данных драйвера USB Virtual COM-port контроллеров CANNY 3 Tiny PRO составляет 32 байта.
Примечание: Настройки скорости и формата передачи данных пользователю не доступны. При активации драйвера, соединение с внешним устройством возможно только на скорости 9600 бод, при формате передачи данных 8-N-1.
Режимы работы драйвера
Ниже приведено описание допустимых и возвращаемых значений регистров управления работой драйвера.
Регистры конфигурации драйвера USB Virtual COM-port.
Регистр | Ожидаемые значения |
---|---|
Регистр включения драйвера VCP | ≥ 1 = активировать драйвер VCP; 0 = деактивировать драйвер VCP. |
Регистр | Возвращаемые значения |
---|---|
Регистр ошибки передачи VCP | ≥ 1 = во время обмена данными по VCP произошла ошибка; 0 = во время обмена данными по VCP ошибок не зафиксировано, драйвер работает в нормальном режиме. |
Регистр | Возвращаемые значения |
---|---|
Регистр наличия принятых данных VCP | 1 = сообщение получено и помещено в буфер приема соответствующего канала драйвера VCP; 0 = в буфере приема драйвера VCP отсутствуют актуальные данные. |
Регистр длины принятого сообщения VCP | 0…32 = значение, равное количеству байт данных, в принятом пакете данных VCP. |
Регистр принятого сообщения VCP D1:D0 … Регистр принятого сообщения VCP D31:D30 | 0…0xFFFF = значения соответствующих байт данных приемного буфера VCP, по два байта на регистр. |
Регистр | Ожидаемые значения |
---|---|
Регистр начала передачи VCP | ≥ 1 = загрузить данные из регистров передачи в буфер передачи драйвера VCP; канала драйвера VCP; 0 = не загружать данные в буфер передачи драйвера VCP. |
Регистр длины сообщения передачи VCP | 0…32 = количество байт данных, которое будет необходимо передать в линию, при получении команды на отправку. |
Регистр принятого сообщения VCP D1:D0 … Регистр принятого сообщения VCP D31:D30 | 0…0xFFFF = значения соответствующих байт данных для передачи, по два байта на регистр. |
Работа контроллера в режиме USB Virtual COM-port
Работая в режиме USB Virtual COM-port контроллер может осуществлять дуплексный прием/передачу данных.
Пример функциональной диаграммы для обмена данными по VCP.
Контроллер, получив сообщение длиной 2 байта по интерфейсу VCP, сохраняет их в соответствующей именованной сети для дальнейшей обработки и на следующем цикле выполнения диаграммы отправляет обратно заранее подготовленные 2 байта данных. Получение данных по VCP CANNY 3 Tiny PRO подтверждает коротким однократным включением своего контрольного светодиода, параллельного каналу №8.
Проект виртуального СОМ порта для отладочной платы STM32H107
Для изучения работы USB в микроконтроллерах STM32F105, STM32F107 существует определенное количество отладочных плат. Большинство из них имеет высокую стоимость, но, при этом, существуют демо-проекты, написанные и отлаженные производителем STMicroelectronics, которые без дополнительных усилий можно сразу загрузить в эти платы и все будет работать.
Существуют и дешевые отладочные платы, например STM32-H107 фирмы Olimex. Но проблема в том, что для дешевых плат нельзя найти готовые для использования демо-проекты.
Скачать его можно по ссылке ниже
Распакуйте архив проекта. Запустите Atollic TrueStudio.
Выберите File->Import->General->Existing Projects into workSpace->Next.
Выберите папку проекта VCP.
Установите галку Copy projects into workspace.
Скомпилируйте проект и он готов к загрузке в плату STM32-H107.
При создании проекта установлена конфигурация JTAG порта процессора как ST-Link.
Я использовал ST-Link, имеющийся на отладочной плате STM32VLDISCOVERY. См. рисунок ниже.
Схема соединений следующая:
Предварительно, на персональном компьютере (PC) надо установить драйвер от STMicroelectronics VCP_V1.3.1_Setup.exe.
Загрузите исполняемый код в STM32-H107 и запустите его выполнение.
Через небольшое время, необходимое для инициализации виртуального COM порта, на PC в диспетчере оборудования, в разделе порты COM и LPT появится устройство виртуальный COM порт, как показано на рисунке ниже.
Номер порта назначается первый свободный в системе.
Если этот номер не устраивает, его можно изменить средствами операционной системы ОС.
В этой статье не рассматривается использование виртуального COM порта по его прямому назначению, когда данные, принятые от РС через USB, отправляются на передачу через USARTx (UARTx) микропроцессоров STM32F105 или STM32F107, а данные, принятые USARTx(UARTx), отправляются на передачу через USB микропроцессоров на РС. Это требуется только в случае построения USB-serial преобразователей.
Гораздо более интересное использование этого демо-проекта для обмена по USB в режиме CDC (common device class).
В классическом варианте использования CDC должна быть библиотека функций для использования в программе микроконтроллера STM32F10x (это
библиотека STM32_USB-Host-Device_Lib_V2.1.0 и она свободно распространяется на сайте STMicroelectronics).
Со стороны РС необходим драйвер CDC (можно свободно скачать с сайта STMicroelectronics) и библиотеки функций для работы с USB при написании программы для РС. Последние STMicroelectronics не предоставляет, но можно купить библиотеки, созданные третьей стороной, причем, не дешево.
Обойти вопрос покупки библиотек можно, используя со стороны микроконтроллера режим передачи CDC, а со стороны РС только драйвер виртуального COM порта. Все необходимые функции для работы с виртуальным COM портом такие же, как для обычных СОМ портов и содержатся в WinSDK. Функции объявлены в файле winbase.h, а содержатся в библиотеке kernel32.lib.
Эти функции следующие:
При помощи этой функции открывается СОМ порт.
Ниже дается пример вызова функции:
При помощи этой функции закрывается СОМ порт.
BOOL CloseHandle(HANDLE hObject); // handle to object to close
Функция для передачи данных от PC на COM порт:
Данные от СОМ порта принимаются в отдельном потоке при помощи:
Для того, чтобы на стороне микроконтроллера использовать режим CDC, в библиотечный файл usbd_cdc_vcp.c добавлена функция
Таким образом, функция CDC_DataTx является точкой входа для передачи данных по USB от микроконтроллера на РС.
На стороне PC использовалась самодельная программа для приема/передачи байтов по порту RS232, окно которой показано ниже:
Рис.1
Она позволяет:
— оперативно открывать/закрывать COM порт с любым номером
— устанавливать требуемую скорость работы порта
— имеет поток, который принимает байты от порта и выводит их на левый экран.
Для случая проверки максимальной скорости передачи, вывод принятых байтов на экран заблокирован, а добавлен счетчик, который выводит на форму количество принятых байтов в секунду(обведено красным).
— передавать из правого окна одиночные байты, массивы байтов и строки.
Для проверки максимальной скорости передачи от микроконтроллера к PC, на плате STM32-H107 надо нажать кнопку WKUP. Загорается зеленый светодиод, микроконтроллер в цикле main следит за переменной USB_Tx_State. Когда она становится равной 0(т.е.ресурсы USB на передачу свободны), загружает эти ресурсы очередным массивом данных.
Из рис.1 видно, что скорость передачи составила 424.5 килобайт/сек. Такая скорость получена при установке в 0 параметра
#define CDC_IN_FRAME_INTERVAL 0 /* Number of frames between IN transfers */ в файле usbd_conf.h. Если параметр увеличивать, скорость будет уменьшаться.
В этой проверке установленная скорость виртуального порта не играет никакой роли.
Прием байтов от РС
Из программы на РС передается байт 123 (см.рис.2)
Рис.2
На рис.3 видно, в каком месте проекта появляется принятый от РС пакет.
Рис.3
Принятый от РС пакет содержит переданный байт 123(фигурная скобка), USB_Rx_Cnt=1, т.е длина пакета равна 1.
Для проверки максимальной скорости передачи байтов от PC на микроконтроллер программа на РС передавала пакет из 64 байтов и ждала подтверждения от микроконтроллера о приеме пакета. Потом цикл повторялся. Функция usbd_cdc_DataOut в программе микроконтроллера была модифицирована следующим образом:
Т.е функция APP_FOPS.pIf_DataRx(USB_Rx_Buffer, USB_Rx_Cnt); закомментирована, чтобы она не передавала принятые байты в USART микроконтроллера. Поток, работающий на прием на РС, подсчитывал, при этом, 1-байтовые ответы от микроконтроллера.
На рис.4 показан результат теста:
Рис.4
Скорость передачи составила 8926 х 64 = 571264 байт/с.
Следует отметить, что и в этом тесте установленная скорость виртуального порта не имеет никакого значения. Можно поставить малую скорость, например 115200 бит/с, а результат остается тем же.
Можно подвести итог:
Начинаем работать в STM32CubeMX. Часть 3
Продолжаем цикл про основы работы STM32MXCube и программированию микроконтроллеров STM32.
Небольшое вступление к третьей части
Вначале я хочу сказать, что с этой части я буду использовать отладочную плату NUCLEO-F767ZI. Эта плата более доступна, чем STM32F746G Discovery, использует микроконтроллер в корпусе LQFP144, а не BGA, и сама плата более удобна для встраивания в разные DIY-проекты. Она имеет Ethernet и USB, а также JTAG-отладчик. Недостатком платы является отсутствие LCD, но он нам пока не нужен.
Хотя плата имеет другой микроконтроллер, все проекты из предыдущих частей переносятся на неё почти без изменений (нужно только поменять номера пинов). Также следует учесть, что на этой плате микроконтроллер тактируется источником 8 МГц. Кварц для тактирования микроконтроллера предусмотрен схемой, но не распаян, сигнал 8 МГц снимается с JTAG-отладчика. Если ваш проект использует интерфейс USB, то желательно включать тактирование от HSE, а не от внутреннего RC-осциллятора, так как RC-осциллятор не обладает достаточной точностью и стабильностью частоты. При попытке включить RC-осциллятор при наличии в проекте USB, STM32CubeMX выдаст предупреждение и предложит переключиться на HSE (то есть на внешний высокостабильный источник тактирования). Для того, чтобы задействовать внешний источник тактирования (8МГц) на вкладке PinOut следует в пункте RCC включить HCE и выбрать пункт BYPASS Clock Source. На практике USB-интерфейс всё равно работает, даже от RC, но лучше не рисковать.
Я перевёл на эту плату проекты из предыдущих частей и залил их на гитхаб.
В комментариях к предыдущим частям были вопросы по поводу IDE. STM32CubeMX позволяет автоматически создавать проекты для различных IDE: IAR (EWARM), MDK ARM v4, MDK ARM v5, Atollic TRUEStudio, SW4STM32 и др. Я пользуюсь Atollic TRUEStudio, который доступен для скачивания с официального сайта бесплатно.
Также я проверил материал из предыдущих частей и внёс ряд поправок.
Хочу поблагодарить Shamrel за ценные комментарии к предыдущей части.
USB VCP
Одним из самых простых режимов работы USB является режим VCP — Virtual COM Port. Настройка работы с ним потребует от вас минимальных усилий.
В STM32CubeMX находим на вкладке Pinout раздел USB_OTG_FS и устанавливаем Mode=Device_Only:
В разделе USB_DEVICE устанавливаем Class For FS IP в режим CDC VCP (Communication Device Class Virtual Com Port):
Теперь нужно настроить конфигурацию тактирования так, чтобы частота USB составляла 48 MHz:
Идём дальше, на вкладку Configuration, и отключаем параметр VBUS Sensing:
Генерируем код и открываем проект в IDE.
Находим файл usbd_cdc_if.c и в него вставляем следующее:
Здесь реализован режим эха: всё, что приходит в порт, мы немедленно отправляем обратно.
Компилируем и прошиваем микроконтроллер. Затем подключаем разъём User USB платы к компьютеру. Система должна обнаружить новый COM-порт.
Для Linux: проверяем ls /dev/tty*, появилось устройство /dev/ttyACM0. Проверяем, и здесь нас ждут ещё сюрприз: отказано в доступе. Нужно добавить себя в группу dialout:
(где user — ваше имя пользователя)
Для работы с устройством в Windows вам понадобится скачать и установить драйвер. Для работы в OS X и Linux специальный драйвер не нужен.
Запускаем (например) Putty, настраиваем параметры порта. Они должны совпадать с параметрами, указанными в свойствах порта (см. «диспетчер устройств/порты»).
Пробуем открыть порт в Putty и что-то послать в порт:
Если порт не открывается, можно попробовать выйти из режима отладки в IDE и перезапустить плату. Всё должно заработать.
как бороться с ошибкой код 10 виртуального порта?
Помогает в файле usbd_cdc.h вместо 512 поставить 256 в строке:
#define CDC_DATA_HS_MAX_PACKET_SIZE (512 было) 256 /* Endpoint IN & OUT Packet size */
Как мы увидели, работа с USB в режиме виртуального COM-порта очень проста. Единственный недостаток этого режима — очень низкая скорость передачи данных. Интерфейс USB в режиме Full Speed обеспечивает до 12 Мбит/c, в режиме High Speed — до 480 Мбит/c, но VCP ограничивает скорость жалкими 128 кбит/c.
Можно сделать высокую скорость передачи данных, но пока отложим это до следующего раза.
Сейчас попробуем запустить АЦП, получить с него значения и отправить на компьютер, реализовав очень простой (и очень медленный) «осциллограф». Чтобы было интереснее, мы подадим на АЦП синусоидальный сигнал, сформированный ЦАП. Так как мы уже делали это в прошлой части, я просто скопирую код в новый проект (с небольшими изменениями, которые большой роли не играют).
Сначала немного об АЦП, встроенном в микроконтроллер. Микроконтроллер STM32F767ZI имеет три 12-разрядных АЦП, типа SAR (последовательного приближения), имеющие производительность до 2 MSPS (млн. выборок в секунду). Этот тип АЦП отличается высокой скоростью преобразования, но меньшей точностью, чем сигма-дельта АЦП. Вход опорного напряжения VREF соединён с VDDA, и, через индуктивность, с VDD. Таким образом, опорное напряжение в нашем случае равно 3,3В. Особенностью SAR ADC является использование на входе схемы выборки-хранения, содержащей конденсатор. В момент выборки значения сигнала конденсатор подключается ко входу и заряжается до величины входного сигнала. Если источник сигнала будет иметь слишком большое внутреннее сопротивление, конденсатор не успеет зарядиться полностью, и мы получим заниженное значение. Этот и другие моменты использования АЦП изложены в [1].
АЦП данного микроконтроллера имеет множество режимов работы [2], мы рассмотрим только один из них. Попробуем получить одновременно два значения сигнала с двух АЦП, строго синхронно, и записать их в буфер через DMA.
Итак, создаём новый проект, добавляем в него уже готовый код для генерации синусоиды на DAC и для USB VCP (через него мы будем отсылать данные на компьютер). Далее (проводами) соединяем выход ЦАП с входами АЦП1 и АЦП2. Для того, чтобы как-то различать сигналы на аналоговых входах, я соединил АЦП1 c ЦАП напрямую, а АЦП2 — через делитель напряжения на переменном резисторе, чтобы можно было менять амплитуду сигнала.
Если вы будете подавать на вход АЦП сигнал от внешнего источника, следует помнить, что сигнал на любом входе микроконтроллера должен быть ограничен значениями 0 — Vcc, что в большинстве практических случаев приводит к необходимости сдвига и усиления (или ослабления) сигнала.
Также следует учесть, что в микроконтроллерах STM32 используются АЦП последовательного приближения (SAR), которые потребляют от источника сигнала довольно большой ток в момент измерения, и требуют источника сигнала с низким импедансом.
Рис. 1. Схема выборки-хранения SAR ADC (не из STM32, но совершенно аналогичный)
В моменты выборки сигнала конденсаторы (рис. 1) подключаются ко входу АЦП и должны зарядиться до полного уровня сигнала за очень короткое время, потребляя при этом значительный ток. Если источник сигнала будет иметь большое сопротивление, они не успеют зарядиться, и показания АЦП будут неверными. На практике это означает, что мы должны в большинстве случаев использовать внешний буферный усилитель. Так как сегодня мы сосредоточимся на программных аспектах задачи, мы можем обойтись без усилителя, но следует помнить, что без усилителя показания АЦП будут существенно искажены, и в реальных проектах он нужен.
Рис. 2. Схема выборки-хранения вызывает провалы уровня сигнала на входе АЦП.
К сожалению, в документации STM32 эти вопросы рассмотрены слабо, но я могу порекомендовать руководство [3].
Если читателям будет интересно, я могу рассмотреть основы схемотехники аналоговых узлов сопряжения сигналов с АЦП в следующей статье.
Нам нужны будут в нашем проекте два таймера. Один из них будет задавать период работы ЦАП, второй — АЦП. Настроим аналого-цифровой преобразователь на работу в двухканальном режиме с одновременной выборкой. Выборка будет происходить по таймеру TIM2. Полученные значения будут складываться в буфер с помощью DMA.
Мы будем использовать однократный режим работы DMA (есть также циклический, с ним мы уже познакомились при изучении ЦАП). После того, как буфер заполнится значениями с АЦП, мы копируем его содержимое в другой буфер (с некоторой обработкой), передаём его через USB и запускаем процесс снова. Также для отладки и индикации режима работы мы используем два порта GPIO, к которым подключены светодиоды.
Итак, у нас создан проект, в который мы добавили DAC и таймер TIM1. Ещё нам нужно добавить ADC1 (вход IN9), ADC2 (вход IN12) и таймер TIM2. Также нам понадобится USB_OTG_FS.
Настраиваем ADC1 на работу в режиме одновременной выборки, с запуском по таймеру 2:
ADC2 при этом настраивается автоматически:
Обращаем внимание, что размер передаваемых данных Word, а не Half Word, т.к. за один раз передаются данные с двух АЦП, упакованные в 32-битное слово. Настраиваем таймер TIM2:
USB настраиваем так же, как мы это уже делали. Генерируем код.
Я не буду здесь расписывать весь исходник проекта, остановлюсь лишь на ключевых моментах. Запуск цепочки таймер-ADC-DMA:
Почему мы не можем передать через USB непосредственно содержимое исходного буфера? Так как скорость VCP слишком мала, мы не сможем передавать весь поток данных с АЦП. Мы захватываем кусок сигнала, передаём его «наверх», потом захватываем следующий кусок и т. д. Если мы не предпримем специальных мер, то в порт будут передаваться случайные фрагменты исходного сигнала. Поэтому нужно сделать программный аналог «триггера», как у цифрового осциллографа. Мы будем передавать в порт не рандомный фрагмент, захваченный АЦП, а кусок сигнала после возникновения некоторого условия. Таким условием может быть пересечение сигналом некоторого уровня в направлении снизу вверх: sample0_0 = threshold, где threshold — порог срабатывания.
Именно для этого мы делаем буфер АЦП в два раза больше буфера VCP, и просматриваем его до середины в поисках такого условия. Если условие не наступило, не отправляем в порт ничего, и запускаем следующий цикл АЦП.
DMA упаковывает сигналы с двух АЦП в одно 32-битное слово. Не будем менять этот формат, просто добавим единицу в старший разряд первого отчёта в буфере, чтобы ПО верхнего уровня могло распознать начало «кадра»:
Для отображения сигнала на компьютере я написал маленькую программу на C#:
Она в основном собрана из компонентов в Visual Studio и содержит минимум кода. Её исходники также доступны на Github.
Что дальше
В следующей части мы рассмотрим интерфейс Ethernet и немного операционную систему реального времени FreeRTOS.
Ссылки
Исходники проектов к всему циклу статей можно скачать на github. Все проекты сделаны для платы Nucleo F767ZI и используют IDE Atollic TRUEStudio.
Благодарю за внимание, о замеченных ошибках и опечатках прошу сообщать в личку. Продолжение следует.