kolhozing.ru
В началоАвторемонтМоделизмОргтехникаСад и огородЭлектроникаФорум

STM32F103, осваиваем USB

Для начала надо сделать небольшое отступление: МК серии stm32f1 делятся на несколько групп: low-, medium-, high- and XL-density и connectivity line. Принадлежность к первым четырем группам определяется объемом доступной памяти, а connectivity line стоит немного отдельно, это исключительно МК stm32f105 и stm32f107. В отличии от остальных они имеют USB OTG, но что еще немаловажно, у них немного другая схема получения тактового сигнала из HSI, в частности, добавлен еще один предделитель и два умножителя. Поэтому при переносе программы на старшие модели МК надо проверить все коэффициенты RCC.

Тактирование USB во всех МК сделано одинаково: тактовый сигнал подается с выхода PLL через предделитель с коэффициентами 1 или 1.5, т.е. для корректной работы USB необходимо что бы выходная частота PLL была либо 48МГц (предделитель 1), либо 72МГц (предделитель 1.5). В линейке Connectivity line коэффициенты предделителя 2 и 3, но и на его вход подается удвоенная частота PLL, так что в целом тоже самое: либо 48МГц, либо 72МГц. Тактирование шин и периферии, на первый взгляд, во всех МК реализовано одинаково.

Я использую отладочную плату Olimex STM32-H103 о которой уже рассказывал. На этой плате USB подключен по-умолчанию на PA11 и PA12, а на PC11 выведено включение подтяжки DATA+ к питанию, что бы сообщить хосту что устройство full-speed. Приведенный ниже код будет расчитан именно на такое подключение периферии. Конечно, это не единственная отладочная плата, есть и другие, например раз, два, три. Последняя из них, NUCLEO-F103RB, оригинальная от STMicroelectronics, имеет встроенный программатор ST-Link, который можно использовать и отдельно, а также разъемы для подключеня шилдов Arduino. Но отлаживаемый МК не имеет разведенного порта USB, поэтому для этой платы разъем с соответствующей обвязкой придется собирать на макетке. В любом случае при использовании этих плат, надо проверить подключение USB и при необходимости изменить константы и настройки GPIO в примере.

Для проверки работоспособности USB можно взять пример из библитеки libopencm3. В этом примере реализован простой Communication Device Class (CDC) с подклассом Abstract Control Model (ACM), т.е. последовательный интерфейс без контроля потока, в котором все принимаемые данные передаются обратно в режиме эха. Если пример скомпилировался и прошился нормально, то в логах можно увидеть что-то подобное:

Feb 24 05:26:14 kettle kernel: usb 1-5.7: new full-speed USB device number 54 using ehci-pci
Feb 24 05:26:14 kettle kernel: .
Feb 24 05:26:14 kettle kernel: usb 1-5.7: New USB device found, idVendor=0483, idProduct=5740
Feb 24 05:26:14 kettle kernel: usb 1-5.7: New USB device strings: Mfr=1, Product=2, SerialNumber=3
Feb 24 05:26:14 kettle kernel: usb 1-5.7: Product: CDC-ACM Demo
Feb 24 05:26:14 kettle kernel: usb 1-5.7: Manufacturer: Black Sphere Technologies
Feb 24 05:26:14 kettle kernel: usb 1-5.7: SerialNumber: DEMO
Feb 24 05:26:14 kettle kernel: cdc_acm 1-5.7:1.0: ttyACM0: USB ACM device
А в консоле по команде lsusb отобразится устройство
$ lsusb
...
Bus 001 Device 011: ID 0483:5740 STMicroelectronics STM32F407
...
Такое название соответствует в базе идентификаторов /usr/share/hwdata/usb.ids для VID/PID из библиотечного примера. Убедится в работе этого эха-устройства можно, например, запустив minicom и указав ему устройство /dev/ttyACM0. Запускать терминалку надо от имени пользователя, входяшего в группу dialout
$ ls -la /dev/ttyACM0 
crw-rw---- 1 root dialout 166, 0 мар  2 23:48 /dev/ttyACM0
Кроме терминальной программы можно обойтись обычными echo и cat в соответствующе устройство. На этом этапе может возникнуть проблема, вызванная излишней интеллектуальностью компонент операционной системы. В частности, найдя новое коммуникационное устройство, может запуститься какой-либо менеджер подключений и начать искать новый модем, поэтому все подобные менеджеры лучше отключить. Возможно, играет свою роль что в примере указан подкласс ACM и используемый протокол AT V.250.

Убедившись в работоспособности платы идем дальше в сторону оптимизации, в частности

  1. Изменить тактирование от внутреннего RC-генератор HSI на внешний 8МГц кварцевый HSI.
  2. Переписать обработку принятных данных с помощью прерываний. Зря что ли в МК стоит развесистый NVIC?
  3. Избавиться от ряда библиотечных функций, которые неплохо добавляют лишнего в код

Для начала переведем тактирование с HSI на HSE. Казалось бы, что может быть проще, заменить 8МГц HSI на 8МГц HSE? Но нет, проблемы есть. В примере используется PLL с частотой 48МГц и такая же частота установлена для SYSCLK. А вот функции, включающей 48Мгц при тактировании от внешнего 8МГц генератора в библиотеке нет. Либо 24Мгц, либо 72ГГц. Придется написать свою функцию rcc_clock_setup_in_hse_8mhz_out_48mhz() по аналогии с имеющимися rcc_clock_setup_in_hse_8mhz_out_24mhz() и rcc_clock_setup_in_hse_8mhz_out_72mhz(). Сравнив код этих двух функций, находим отличия:

SYSCLK=24MHz

	rcc_set_adcpre(RCC_CFGR_ADCPRE_PCLK2_DIV2);  /* Set. 12MHz Max. 14MHz */
	rcc_set_ppre1(RCC_CFGR_PPRE1_HCLK_NODIV);    /* Set. 24MHz Max. 36MHz */
	flash_set_ws(FLASH_ACR_LATENCY_0WS);

	rcc_set_pll_multiplication_factor(RCC_CFGR_PLLMUL_PLL_CLK_MUL3);

	rcc_ahb_frequency = 24000000;
	rcc_apb1_frequency = 24000000;
	rcc_apb2_frequency = 24000000;


SYSCLK=72MHz

	rcc_set_adcpre(RCC_CFGR_ADCPRE_PCLK2_DIV8);  /* Set.  9MHz Max. 14MHz */
	rcc_set_ppre1(RCC_CFGR_PPRE1_HCLK_DIV2);     /* Set. 36MHz Max. 36MHz */
	flash_set_ws(FLASH_ACR_LATENCY_2WS);

	rcc_set_pll_multiplication_factor(RCC_CFGR_PLLMUL_PLL_CLK_MUL9);

	rcc_ahb_frequency = 72000000;
	rcc_apb1_frequency = 36000000;
	rcc_apb2_frequency = 72000000;
На первый взгляд все просто: надо изменить коэффициенты предделителей и задержку при обращении к flash, поэтому берем за основу любую из этих двух функций и набрасываем следующий код:
void rcc_clock_setup_in_hse_8mhz_out_48mhz(void)
{
	/* Enable internal high-speed oscillator. */
	rcc_osc_on(RCC_HSI);
	rcc_wait_for_osc_ready(RCC_HSI);

	/* Select HSI as SYSCLK source. */
	rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSICLK);

	/* Enable external high-speed oscillator 8MHz. */
	rcc_osc_on(RCC_HSE);
	rcc_wait_for_osc_ready(RCC_HSE);
	rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_HSECLK);

	/*
	 * Set prescalers for AHB, ADC, ABP1, ABP2.
	 * Do this before touching the PLL (TODO: why?).
	 */
	rcc_set_hpre(RCC_CFGR_HPRE_SYSCLK_NODIV);    /* Set. 48MHz Max. 72MHz */
	rcc_set_adcpre(RCC_CFGR_ADCPRE_PCLK2_DIV4);  /* Set. 12MHz Max. 14MHz */
	rcc_set_ppre1(RCC_CFGR_PPRE1_HCLK_DIV2);     /* Set. 24MHz Max. 36MHz */
	rcc_set_ppre2(RCC_CFGR_PPRE2_HCLK_NODIV);    /* Set. 48MHz Max. 72MHz */
	
	/*
	 * Sysclk runs with 24MHz -> 0 waitstates.
	 * 0WS from 0-24MHz
	 * 1WS from 24-48MHz
	 * 2WS from 48-72MHz
	 */
	flash_set_ws(FLASH_ACR_LATENCY_1WS);

	/*
	 * Set the PLL multiplication factor to 3.
	 * 8MHz (external) * 6 (multiplier) = 48MHz
	 */
	rcc_set_pll_multiplication_factor(RCC_CFGR_PLLMUL_PLL_CLK_MUL6);

	/* Select HSE as PLL source. */
	rcc_set_pll_source(RCC_CFGR_PLLSRC_HSE_CLK);

	/*
	 * External frequency undivided before entering PLL
	 * (only valid/needed for HSE).
	 */
	rcc_set_pllxtpre(RCC_CFGR_PLLXTPRE_HSE_CLK);

	/* Enable PLL oscillator and wait for it to stabilize. */
	rcc_osc_on(RCC_PLL);
	rcc_wait_for_osc_ready(RCC_PLL);

	/* Select PLL as SYSCLK source. */
	rcc_set_sysclk_source(RCC_CFGR_SW_SYSCLKSEL_PLLCLK);

	/* Set the peripheral clock frequencies used */
	rcc_ahb_frequency = 48000000;
	rcc_apb1_frequency = 24000000;
	rcc_apb2_frequency = 48000000;
}
Теперь в функции main надо заменить rcc_clock_setup_in_hsi_out_48mhz() на rcc_clock_setup_in_hse_8mhz_out_48mhz() , скомпилировать, прошить в МК и.... «Таких ударов он не испытывал давно», в логах ошибки:
Feb 24 05:16:42 kettle kernel: usb 1-5.7: new full-speed USB device number 38 using ehci-pci
Feb 24 05:16:42 kettle kernel: usb 1-5.7: device descriptor read/64, error -32
Feb 24 05:16:42 kettle kernel: usb 1-5.7: device descriptor read/64, error -32
Feb 24 05:16:43 kettle kernel: usb 1-5.7: new full-speed USB device number 39 using ehci-pci
Feb 24 05:16:43 kettle kernel: usb 1-5.7: device descriptor read/64, error -32
Я не сразу понял в чем дело, пришлось заглянуть в код функции rcc_clock_setup_in_hsi_out_48mhz(). А причина ошибок в настройках RCC. Как выше было написано, для тактирования USB необходима частота PLL 48MHz или 72MHz, и если использовать функцию rcc_clock_setup_in_hse_8mhz_out_72mhz(), то устройство опознается системой и будет работать. Дело в том, что после сброса, предделитель для USB уставливается 1.5, и поэтому при частоте PLL 72МГц частота USB правильая, 48МГц, а при частоте PLL 48МГц USB будет работать на частоте 36МГц. По подтяжке DATA+ к питанию хост обнаруживает full-speed USB Device, но не может передать/принять данные. Для правильной установки частоты USB в функции rcc_clock_setup_in_hsi_out_48mhz() предделитель устанавливается в единицу.
	rcc_set_usbpre(RCC_CFGR_USBPRE_PLL_CLK_NODIV);  
Эту строку надо добавить после установки предделителей для шин AHB, ADC, ABP1, ABP2. После такой модификации кода устройство вновь определяется и работает. Но уже от внешнего генератора.

На этом пока все, продолжение следует.


рубрика: электроника
Комментарии
Ваше имя:

Ваше мнение:

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