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

STM32F103, осваиваем USB, часть 2

Продолжаем разбираться с USB и для начала я хочу переписать код инициализации МК без вызовов библиотечных функций. Все используемые константы из заголовочных файлов библиотеки, но их можно определить самостоятельно на основе RM0008 Reference manual, в котором расписаны адреса и биты всех регистров.

Для начала разберемся с инициализацией RCC. Исходные данные: внешний кварцевый резонатор на 8МГц, на выходе PLL необходимо получить 48МГц, а заодно такую же частоту для SYSCLK, и установить частоты шин, не превыщающие максимально допустимые, в частности, не более 36МГц для APB1. На самом деле, SYSCLK можно взять не с PLL, с выхода HSI или HSE, т.е. получить 48МГц для USB, но ядро МК и периферию тактировать более низкой частотой напрямую с HSI/HSE для снижения энергопотребления. Логика инициализации RCC следующая: включить HSI, дождаться запуска генератора, переключить вход SYSCLK на HSI (это происходит после сброса МК, но на всякий случай лучше продублировать, если вдруг переход в начало кода будет по иной причине). Далее надо запустить HSE и остановить PLL, дождаться запуска первого и остановки второго, установить конфигурацию PLL и тактирования шин, запустить PLL, дождаться запуска PLL и переключить тактирование SYSCLK от PLL. Теперь эти шаги в коде по порядку:
Сбрасываем управляющие биты регистра RCC_CR и устанавливаем включение генераторов HSI и HSE

        RCC_CR |= ( RCC_CR & ~(RCC_CR_HSION | RCC_CR_HSEON | RCC_CR_PLLON | RCC_CR_HSEBYP | RCC_CR_CSSON) )
                    | RCC_CR_HSION 
                    | RCC_CR_HSEON ;
Дожидаемся включения HSI и очищаем все биты регистра RCC_CFGR, включая при этом тактирование SYSCLK от HSI
        while ((RCC_CR & RCC_CR_HSIRDY) == 0); // дождаться запуска HSI
        RCC_CFGR = (RCC_CFGR & 0xF8800000); // сброс коэф, тактирование от HSI
Дожидаемся запуска HSE и выключения PLL
        while ((RCC_CR & RCC_CR_PLLRDY) != 0);
        while ((RCC_CR & RCC_CR_HSERDY) == 0); 
Теперь надо настроить RCC_CFGR, т.е. задать источник, предделитель и умножитель для PLL, а так же предделители для шин МК. Тактовый сигнал с HSE без деления подается на вход умножителя с коэффициентом 6 что бы получить 48Мгц, предделитель USB устанавливается в 1, пределитель шин AHB и APB2 устанавливается в 1, предделитель APB1 устанавливается 2. Хотя если необходимо, для снижения энергопотребления можно снизить тактовые частоты для шин периферии. Теперь расписываем тоже самое битами в регистре RCC_CFRG. В комментарии к коду указано значение бит и что оно означает. А дальше устанавливаются соответствующие биты в регистре. Обратите внимание, что многие константы для значений в регистре RCC_CFGR (как и во многих других) заданы с «выравниванием» вправо. Т.е. для умножения PLL на 6 в регистр RCC_CFGR надо записать значение 0x100000, а константа RCC_CFGR_PLLMUL_PLL_CLK_MUL6 равна 4, т.е. 4 битам PLLMUL соответствуют константы от 0 до 15. И для того, что бы «разместить» в регистре их надо сдвинуть, величины сдвига тех или иных бит так же заданы в заголовочных файлах, константами с суффиксом SHIFT. Для PLLNUL эта константа называется RCC_CFGR_PLLMUL_SHIFT и равна 18.
/* RCC_CFGR
        MCO = 0 , не выводить тактовую частоту наружу
        USBPRE = 1 , /1 для PLL=48MHz
        PLLMUL = 4 (0b0100) , 8MHz x6 = 48MHz
        PLLXTPRE = 0 , HSE на вход PLLMUL без деления
        PLLSRC = 1 , PLL тактируется от HSE
        ADCPRE = 1 (0b01), APB2 / 4 = 12MHz
        PPRE2 = 0 , APB2 = AHB = 48MHz
        PPRE1 = 4 (0b0100) , ABP1 = AHB/2 = 24MHz 
        HPRE = 0 , AHB = SYSCLK = 48MHz
        SWS r/o, статус не трогаем
        SW = 2 (0b10)  , SYSCLK = PLL (48MHz)
        резерв + статус = 0xF880000C;
*/
        RCC_CFGR = (RCC_CFGR & 0xF8800000)  
                | RCC_CFGR_USBPRE
                | ( RCC_CFGR_PLLMUL_PLL_CLK_MUL6 << RCC_CFGR_PLLMUL_SHIFT )
                | RCC_CFGR_PLLSRC
                | ( RCC_CFGR_ADCPRE_PCLK2_DIV4 << RCC_CFGR_ADCPRE_SHIFT )
                | ( RCC_CFGR_PPRE1_HCLK_DIV2 << RCC_CFGR_PPRE1_SHIFT )
                ;
Для корректной работы flash-памяти, необходимо установить задержку, для частот от 24МГц до 48МГц она составляет 1 такт
        FLASH_ACR = 0x30 | FLASH_ACR_LATENCY_1WS;
Теперь можно включить PLL, подождать его запуска и переключить SYSCLK на тактирование от PLL
        RCC_CR |= RCC_CR_PLLON; 
        while ((RCC_CR & RCC_CR_PLLRDY) == 0);
        RCC_CFGR = (RCC_CFGR & ~RCC_CFGR_SW ) | RCC_CFGR_SW_SYSCLKSEL_PLLCLK;
Для возможного использования библиотечных функций надо задать переменные с получившейся частотой шин AHB,APB1,APB2
        rcc_ahb_frequency = 48000000;
        rcc_apb1_frequency = 24000000;
        rcc_apb2_frequency = 48000000;
Последний шаг настройки RCC включение тактирования GPIOC, к которому подключено управления подтяжкой DATA+ к питанию (PC11) и светодиод на плате (PC12).
        RCC_APB2ENR |= RCC_APB2ENR_IOPCEN; // включить GPIOC
Теперь переходим к настройке ног порта GPIOC. Конфигурация каждого порта в/в задается в двух регистрах, по 8 ног на каждый. GPIOx_CRL для 8 младших и GPIOx_CRH для 8 старших. Для каждой ноги, таким образом, отводится по 4 бита. Два на режим и два на конфигурацию. Так же как для RCC_CFGR, все значения заданы без учета их расположения в регистрах, например, константу для конфигурации порта надо сдвинуть на 2 бита влево и сложить с константой режима. Но при этом номера ног, GPIOn, заданы их расположением в регистре, т.е. GPIO4 это не 4, а 0x10. Что бы сформировать биты конфигурации ног пришлось набросать несколько псевдофункций.
#define GPIO_CNF_SHIFT(port)   ((port&7)*4)
#define GPIO_CNF_MASK(port)  (0xF<< GPIO_CNF_SHIFT(port))
#define GPIO_CNF_BITS(port,cnf,mode)   (((cnf<<2)|mode)<<GPIO_CNF_SHIFT(port))
С помощью этих определений теперь можно настроить PC11 и PC12 на выход с частотой тактирования 2Мгц
        GPIOC_CRH &= ~(  GPIO_CNF_MASK(11) | GPIO_CNF_MASK(12) );
        GPIOC_CRH |=  GPIO_CNF_BITS(11,GPIO_CNF_OUTPUT_PUSHPULL,GPIO_MODE_OUTPUT_2_MHZ) |
                      GPIO_CNF_BITS(12,GPIO_CNF_OUTPUT_PUSHPULL,GPIO_MODE_OUTPUT_2_MHZ)					
        ;
Установки и сброс бита осуществляется записью в регистр BSRR (set) или BRR (reset). Для сброса можно тоже использовать и BSRR, но значение надо сдвинуть на 16 влево.
        GPIOC_BSRR = GPIO11;
        GPIOC_BRR = GPIO12;
Эти две строки включают подтяжку DATA+ к питанию и светодиод на плате.

Теперь немного оптимизации для USB, в частности, обработку принятых пакетов в прерывании. Для этого необходимо подключить заголовочный файл nvic.h, вынести переменную usbd_dev за пределы функции main и написать два простых обработчика прерываний.

#include <libopencm3/stm32/f1/nvic.h>

usbd_device *usbd_dev;

void usb_wakeup_isr(void) {
  usbd_poll(usbd_dev);
}

void usb_lp_can_rx0_isr(void) {
  usbd_poll(usbd_dev);
}
Что бы прерывания заработали их надо разрешить перед вызовом инициализации USB
        nvic_enable_irq(NVIC_USB_LP_CAN_RX0_IRQ);
        nvic_enable_irq(NVIC_USB_WAKEUP_IRQ);	
Вызов функции usbd_poll из цикла while можно убрать.

В результате всех этих действий во-первых из инициализации выброшено масса ненужных вызовов функций с многократными обращениями к регистрам МК и убраны лишнее ветвления, во-вторых прием данных от хоста осуществляется по прерываниям, что позволяет в основном цикле делать что-нибудь еще. Однако, приведенные изменения не лишены недостатков. Дело в том, что если по какой-либо причине HSE не запустится, например из-за разбитого кварца, то МК встанет в вечном цикле в ожидании запуска генератора. Поэтому в реальном изделии необходимо задать таймаут, в течении которого генератор должен запустится и по истечении этого времени, либо настраивать работу МК от HSI, либо как-то выдавать ошибку, например, миганием светодиода. Второй недостаток заключается в остановке МК если пропадет тактирование от HSE. Что бы этого избежать, надо настроить систему контроля тактовой частоты, которая автоматически переключит тактирования ядра на HSI при пропадании сигнала с HSE с одновременной генерацией прерывания NMI, что позволит как и в первом случае, либо настроить работу МК от внутреннего генератора, либо как-то сообщить о возникшей аппаратной ошибке.

А впланах на будущее разобраться с работой USB, которая пока скрыта за вызовами функций.


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

Ваше мнение:

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