- Лоботрясы

Поиск
Перейти к контенту

Главное меню:

Статьи по STM32

Читать в Яндекс.Подписках



   STM32. Дрыгаем ножками (или как организовать работу с портами). Часть 1
Просмотров: 6952
    Дальнейшее знакомство с STM32F050F4P6 (STM32F031F4P6) резонно продолжить с изучения работы портов ввода-вывода, т.к. микроконтроллер, в первую очередь, предназначен для работы с разнообразной периферией, да и тема ножек, нас мужиков, всегда интересовала не в последнюю очередь.
     Как человек, давно и плотно общающийся с семейством AVR, по поводу конфигурации и управления портами STM32, могу многозначительно только сказать - Мдааа! (это сохраняется и ко всей аппаратной части STM-ок). Нет, это не замечание удивлённого человека, это, скорее, восхищение! Широкий набор настроек (опять же, по отношению к AVR!) позволяет тонко реализовать работу портов ввода-вывода (GPIO) под требуемую задачу. Это может быть и настройка вывода в качестве аналогового входа/выхода или же выбор определённой скорости (частоты) работы портов, что влияет на энергопотребление микросхемы и устройства в целом. Но отсюда вытекает и некоторая "громоздкость" управления портами (а что поделать?! где-то находишь, а где-то теряешь!), выражающаяся в использовании структур, применяющихся для настройки основных функций GPIO (да и не только их). Но нас уже не напугаешь - мы знаем, что CooCox богат всевозможными примерами, из которых можно почерпнуть процедуру инициализации и работы с различными модулями и периферией. Но прежде чем начнём дрыгать ногами на практике, рассмотрим организацию портов ввода-вывода в микроконтроллерах STM32. Все дальнейшие примеры, которые я буду приводить в пределах цикла статей про STM32, будут основаны на изучении даташита (а точнее, справочного руководства) микроконтроллера STM32F050F4P6 (STM32F031F4P6), который вы можете скачать с сайта производителя перейдя по ссылке. Но принцип работы с различными модулями и регистрами сего контроллера соответствует, за мелкими нюансами, всему семейству STM-ок. 
     Итак-с, начнём!  
     Каждый порт ввода-вывода (GPIO) имеет четыре 32-х разрядных регистра конфигурации (GPIOx_MODER, GPIOx_OTYPER, GPIOx_OSPEEDR и GPIOx_PUPDR)два 32-х разрядных регистра данных (GPIOx_IDR и GPIOx_ODR) и 32-х разрядный регистр атомарной установки/сброса бита (GPIOx_BSRR). Порты A и B имеют также 32-х разрядный регистр блокировки (GPIOx_LCKR) и два 32-х разрядных регистра выбора альтернативной функции (GPIOx_AFRH и GPIOx_AFRL).  
     Регистр GPIOx_ODR позволяет выводить данные, записанные в него, на выводы портов I/O (аналог PORTx в микроконтроллерах AVR). Ввод данных с портов I/O осуществляется с помощью регистра GPIOx_IDR (аналог PINx в микроконтроллерах AVR). Регистр установки/сброса бита GPIOx_BSRR позволяет проводить побитовую запись в регистр GPIOx_ODR. Всё это справедливо, если выводы не используют альтернативные функции, которые на каждый вывод (пин) могут доходить до 16-ти. Через регистр GPIOx_MODER осуществляется настройка вывода в качестве цифрового или аналогового входа/выхода, а также выбор режима альтернативной функции. Назначение альтернативной функции выводам I/O осуществляется через регистры GPIOx_AFRH и GPIOx_AFRL.
     Небольшая таблица альтернативных функций, выбираемых для порта А через регистр PIOx_AFR:
     Каждый вывод порта микроконтроллера имеет возможность использовать одну из альтернативных функций выводов (максимум, на один вывод может приходится до 16-ти альтернативных функций: AF0 - AF15), которые могут быть настроены через регистры GPIOx_AFRL (пины 0-7) и  GPIOx_AFRH  (пины 8-15). По умолчанию, после сброса микроконтроллера все пины ввода/вывода будут подключены к альтернативной функции 0 (AF0). "Eventout" в таблице - это использование функции внешнего прерывания (наподобие INTx в микроконтроллерах AVR), но это уже - другая история...
     Выбор режима выхода, подключение подтягивающего резистра и выбор скорости работы выводов осуществляется регистрами GPIOx_OTYPER, GPIOx_PUPDR и GPIOx_OSPEEDR соответственно.
     Что, слишком много страшных регистров я перечислил? Без паники! Как я уже упоминал, доступ к данным регистрам в процессе конфигурирования портов осуществляется через объявленную в библиотеке GPIO структуру (GPIO_InitTypeDef), где наша задача для элементов этой структуры просто выбрать необходимый параметр из предложенных создателями библиотеки. Как выбрать интересующую вас библиотеку смотрите здесь.
     Все возможные параметры, которыми можно конфигурировать регистры портов ввода-вывода, вы можете просмотреть в файле stm32f0xx_gpio.h. Также вы можете изучить особенности библиотечных функций, выбрав в верхнем левом меню View пункт Repository, и после этого выбрать интересующую вас библиотеку. После этого в правом окне программы появится детальное описание библиотеки – описание функций, значения параметров и возвращаемые значения. На рисунках ниже я выбрал в библиотеке GPIO описание функции GPIO_SetBits():
     Если вы откроете любой пример работы с портами, представленный для наглядной работы библиотеки, вы можете заметить такую инициализацию структуры:    
     GPIO_InitTypeDef     GPIO_InitStruct;
где в исходнике программы объявляется структура GPIO_InitStruct по существующему шаблону структуры типа GPIO_InitTypeDef (в файле stm32f0xx_gpio.h), которая, в свою очередь, имеет такой вид:

typedef struct
{
uint32_t    GPIO_Pin; //Указываются пины (или выводы) порта, которые будут конфигурироваться
GPIOMode_TypeDef    GPIO_Mode; //Указывается режим работы выбранных пинов
GPIOSpeed_TypeDef    GPIO_Speed; //Указывается скорость для выбранных пинов
GPIOOType_TypeDef    GPIO_OType; //Указывается режим выхода для выбранного пина
GPIOPuPd_TypeDef    GPIO_PuPd; //Указывается подтяжка к плюсу питания/земле для выбранных пинов
} GPIO_InitTypeDef;

     Как видите, каждый элемент структуры за что-то да отвечает!
     А теперь посмотрим, какой параметр может принять каждый из элементов данной структуры:

     Интересующие нас пины (выводы) портов в библиотеке CooCox объявляются таким образом:
#define GPIO_Pin_0                   ((uint16_t)0x0001) /*!< Выбран пин 0 */
#define GPIO_Pin_1                   ((uint16_t)0x0002) /*!< Выбран пин 1 */ 
#define ... и т.д. и т.п.
что ничуть не мешает выбрать нужный вам пин операцией типа (1<<0) или (1<<1), полюбившейся многим поклонникам AVR.

     В качестве режима работы выбранных пинов может быть выбран один из следующих:
GPIO_Mode_IN - вход
GPIO_Mode_OUT - выход
GPIO_Mode_AF - альтернативная функция (USART, SPI, I2C ...) 
GPIO_Mode_AN - режим аналогового входа/выхода

     Возможная скорость работы порта:
GPIO_Speed_Level_1 - низкая 2 MHz 
GPIO_Speed_Level_2 - средняя 10 MHz 
GPIO_Speed_Level_3 - высокая 50 MHz 
     Также, библиотекой GPIO позволяется замена этих параметров на:
GPIO_Speed_2MHz 
GPIO_Speed_10MHz
GPIO_Speed_50MHz 
     Выбирайте наиболее понравившийся вам тип записи.

     В режиме выхода для выбранного пина можно выбрать следующий параметр:
GPIO_OType_PP - двухтактный выход 
GPIO_OType_OD - выход с открытым истоком

     И последный параметр - это режим подтяжки выбранного пина:
GPIO_PuPd_NOPULL - без внутреннего подтягивающего резистора
GPIO_PuPd_UP - подтяжка к плюсу питания
GPIO_PuPd_DOWN - подтяжка к земле

     А теперь небольшой кусок программного кода, в котором покажу уже рабочий пример инициализации порта ввода-вывода GPIOA:
GPIO_InitTypeDef     GPIO_InitStruct;

void port_init(void)
{
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); //Включаем тактирование порта GPIOA
GPIO_InitStruct.GPIO_Pin = ( GPIO_Pin_0 | GPIO_Pin_1 ); //Какие выводы конфигурируем
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //Выход
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_Level_1; //Устанавливаем скорость работы модуля GPIO
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //Двухтактный выход
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; //Без подтягивающих резисторов
GPIO_Init(GPIOA, &GPIO_InitStruct); //Запись всех выбранных значений непосредственно в регистры GPIO микроконтроллера
}

     RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); - это функция для включения тактирования модуля GPIO. Изначально, все модули в микроконтроллерах STM32 отключены от тактовой частоты, поэтому приходится включать их вручную. Каждый модуль тактируется от определённой шины, например, порты GPIOA, GPIOB и GPIOC тактируются от шины AHB, таймеры TIM2, TIM3 и TIM6 - от шины APB1, а модули ADC1, SPI1 и USART1 - тактируются от шины APB2. Все эту информацию можно почитать в даташите или найти в файлах stm32f0xx_rcc.h и stm32f0xx_rcc.c. А для понимания того, откуда компилятор знает, что под именем порт GPIOA лежит именно этот порт, рекомендую прочесть хорошую статью "Программируем STM32F — откуда что берется?".
     

Опубликовано 6.11.2014
© Igoryosha, 2014

 
 
Назад к содержимому | Назад к главному меню