Руководство к быстрому старту по работе с периферией STM32F10x

Данная статья Logoпредназначена для тех, кто начинает изучать ARM-контроллеры ST Microelectronics серии STM32F10x и уже определился со средой разработки, но еще не взялся за собственно программирование. Я покажу, как средствами библиотеки STM32 Peripheral Library можно легко управлять периферией контроллера без прямого обращения к регистрам, а главное — научу, как искать информацию по работе с этой периферией.

Практически вся (если не вообще вся) периферия настраивается примерно одинаково, различия имеются только в специфических для каждого устройства параметрах и командах. Приведу пример:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

GPIO_Init( GPIOC , &GPIO_InitStructure);

Это пример настройки порта ввода-вывода (GPIO). Рассмотрим его поподробнее.

1. Включение тактирования периферийного устройства


Первым делом на порт подаётся тактирование:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);

Периферийные устройства тактируется либо от шины APB2, либо от APB1. От какой именно, можно узнать по названию константы. В нашем случае это RCC_APB2Periph_GPIOC, стало быть шина — APB2. Константа расположена в файле stm32f10x_rcc.h, давайте найдем ее. В Eclipse для этого достаточно щелкнуть мышкой по константе, зажав Ctrl.

Вот что мы находим в stm32f10x_rcc.h:

#define RCC_APB2Periph_AFIO              ((uint32_t)0x00000001)
#define RCC_APB2Periph_GPIOA             ((uint32_t)0x00000004)
#define RCC_APB2Periph_GPIOB             ((uint32_t)0x00000008)
#define RCC_APB2Periph_GPIOC             ((uint32_t)0x00000010)
#define RCC_APB2Periph_GPIOD             ((uint32_t)0x00000020)
......
#define RCC_APB1Periph_TIM13             ((uint32_t)0x00000080)
#define RCC_APB1Periph_TIM14             ((uint32_t)0x00000100)
#define RCC_APB1Periph_WWDG              ((uint32_t)0x00000800)
#define RCC_APB1Periph_SPI2              ((uint32_t)0x00004000)
#define RCC_APB1Periph_SPI3              ((uint32_t)0x00008000)
#define RCC_APB1Periph_USART2            ((uint32_t)0x00020000)
...

Поблизости можно найти все необходимые нам константы для запуска тактирования любого периферийного устройства. К примеру, если мы хотим воспользоваться USART2, мы воспользуемся константой RCC_APB1Periph_USART2, а поскольку из её названия видно, что USART2 тактируется от шины APB1, включение тактирования мы произведем следующим образом:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

Не забывайте, что одним вызовом функции можно включить тактирование сразу несколько устройств, задав в параметре функции несколько констант через оператор побитовое ИЛИ:

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC 
  | RCC_APB2Periph_ADC1, ENABLE);

Побитовое ИЛИ «соберёт» все единички из перечисленных констант, а сами константы, как вы уже догадались, являются маской, как раз предназначенной для такого использования.

Обратите внимание, что в некоторых случаях может потребоваться дополнительная настройка, например, для АЦП необходимо выставить предделитель частоты:

RCC_ADCCLKConfig(RCC_PCLK2_Div6);

Максимальная тактовая частота для АЦП составляет 12 МГц, это необходимо учесть при выборе предделителя.

Идём дальше.

2. Объявление и настройка структуры для инициализации


Следующий шаг — объявление некой инициализационной структуры (InitStructure), которая содержит все параметры для настройки периферийного устройства в виде переменных-членов структуры:

GPIO_InitTypeDef GPIO_InitStructure;

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

Структура описывается в заголовочном файле того устройства, к которому она относится. В данном примере мы инициализируем порты ввода-вывода (GPIO), следовательно, структура GPIO_InitTypeDef описана в файле stm32f10x_gpio.h. Найдем её:

typedef struct
{
  uint16_t GPIO_Pin;             
/*!< Specifies the GPIO pins to be configured.
  This parameter can be any value of @ref GPIO_pins_define */

  GPIOSpeed_TypeDef GPIO_Speed;  
/*!< Specifies the speed for the selected pins.
  This parameter can be a value of @ref GPIOSpeed_TypeDef */

  GPIOMode_TypeDef GPIO_Mode;    
/*!< Specifies the operating mode for the selected pins.
  This parameter can be a value of @ref GPIOMode_TypeDef */
} GPIO_InitTypeDef;

Большинство параметров структуры, как правило, имеет тип, схожий с названием параметра и добавлением суффикса "_TypeDef". Такие типы обычно определены как enum (перечисление). Для примера найдем определения типа переменной GPIO_Mode (напоминаю, Ctrl + щелчок по GPIOMode_TypeDef):

typedef enum
{ GPIO_Mode_AIN = 0x0,
  GPIO_Mode_IN_FLOATING = 0x04,
  GPIO_Mode_IPD = 0x28,
  GPIO_Mode_IPU = 0x48,
  GPIO_Mode_Out_OD = 0x14,
  GPIO_Mode_Out_PP = 0x10,
  GPIO_Mode_AF_OD = 0x1C,
  GPIO_Mode_AF_PP = 0x18
} GPIOMode_TypeDef;

Мы нашли все возможные значения для параметра GPIO_Mode. Описание значений в файлах stm32f10x_gpio.h и .c мне найти не удалось, так что придется лезть в Reference Manual, узнавать возможные режимы работы портов ввода-вывода и сопоставлять их со значениями. Но в данном случае здесь и так все понятно:
  • GPIO_Mode_AIN — аналоговый вход;
  • GPIO_Mode_IN_FLOATING — вход без подтяжки, болтающийся (англ. float) в воздухе
  • GPIO_Mode_IPD — вход с подтяжкой к земле (англ. Pull-down)
  • GPIO_Mode_IPU — вход с подтяжкой к питанию (англ. Pull-up)
  • GPIO_Mode_Out_OD — выход с открытым стоком (англ. Open Drain)
  • GPIO_Mode_Out_PP — выход двумя состояниями (англ. Push-Pull — туда-сюда)
  • GPIO_Mode_AF_OD — выход с открытым стоком для альтернативных функций (англ. Alternate Function). Используется в случаях, когда выводом должна управлять периферия, прикрепленная к данному разряду порта (например, вывод Tx USART и т.п.)
  • GPIO_Mode_AF_PP — то же самое, но с двумя состояниями

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

Кстати, перед заполнением структуры данными, рекомендуется вызвать функцию GPIO_DeInit(GPIOx), сбрасывающую текущие настройки в значения по умолчанию.

3. Инициализация


Самый простой пункт. Вызываем функцию инициализации, куда передаем указатель на сформированную в п.2 структуру:

GPIO_Init( GPIOC , &GPIO_InitStructure);

Для большинства устройств также требуется вызов команды «включение». Пример для включения USART1 и ADC:

USART_Cmd(USART1, ENABLE);
ADC_Cmd(ADC1, ENABLE);

4. Настройка прерываний


4.1. Настройка и инициализация NVIC

С прерываниями дела обстоят ни чуть не сложнее, чем со структурой инициализации. Для начала надо настроить и проинициализировать контроллер прерываний (NVIC — Nested vectored interrupt controller). В архитектуре Cortex M3 каждому прерыванию можно выставить свой приоритет для случаев, когда возникает несколько прерываний одновременно. Поэтому NVIC представляет нам несколько вариантов формирования приоритетных групп. Я не буду вдаваться в подробности, приведу лишь пример выбора варианта приоритетных групп, он состоит всего из одной команды:

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

Эта команда выполняется один раз. Далее, для каждого прерывания, нам надо произвести настройку и инициализацию с помощью структуры — точно так же, как мы поступали в п.2. Вот, например, настройка прерывания для USART1:

NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

В параметре NVIC_IRQChannel мы указываем, какое именно прерывание мы инициализируем. Константа USART1_IRQn обозначает канал, отвечающий за прерывания, связанные с USART1. Найдя ее определение в файле stm32f10x.h, вы увидите еще множество констант (ADC1_IRQn, TIM1_TRG_COM_TIM17_IRQn и др.), обозначающих прерывания от других периферийных устройств.

Следующими двумя строками мы указываем приоритет прерываний (максимальные значения этих двух параметров определяются выбранной приоритетной группой). Последняя строка, собственно, включает использование прерывания.

Структура настроена, инициализируем:

NVIC_Init(&NVIC_InitStructure);

C настройкой NVIC мы закончили.

4.2. Включение прерываний для периферийного устройства

Включение прерываний производится функцией, которую вы можете найти заголовочном файле периферийного устройства. Она выглядит примерно так (рассмотрим на примере USART1):

void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, 
  FunctionalState NewState);

В заголовочном файле stm32f10x_usart.h нет никаких комментариев по поводу ее использования, а вот в файле реализации — stm32f10x_usart.c — есть всё, что нам нужно:

/**
  * @brief  Enables or disables the specified USART interrupts.
  * @param  USARTx: Select the USART or the UART peripheral. 
  *   This parameter can be one of the following values:
  *   USART1, USART2, USART3, UART4 or UART5.
  * @param  USART_IT: specifies the USART interrupt sources to be enabled or disabled.
  *   This parameter can be one of the following values:
  *     @arg USART_IT_CTS:  CTS change interrupt (not available for UART4 and UART5)
  *     @arg USART_IT_LBD:  LIN Break detection interrupt
  *     @arg USART_IT_TXE:  Transmit Data Register empty interrupt
  *     @arg USART_IT_TC:   Transmission complete interrupt
  *     @arg USART_IT_RXNE: Receive Data register not empty interrupt
  *     @arg USART_IT_IDLE: Idle line detection interrupt
  *     @arg USART_IT_PE:   Parity Error interrupt
  *     @arg USART_IT_ERR:  Error interrupt(Frame error, noise error, overrun error)
  * @param  NewState: new state of the specified USARTx interrupts.
  *   This parameter can be: ENABLE or DISABLE.
  * @retval None
  */
void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
{
   ...
}

Разработчики библиотеки нам всё доступно объяснили. Я только отмечу, что на один канал прерываний назначено несколько событий. В данном случае на канал USART1_IRQn, который мы активировали при настройке NVIC, назначено аж 8 событий, описанные в этом комментарии: USART_IT_CTS, USART_IT_LBD и далее по списку. При вызове функции USART_ITConfig нам нужно указать, на какие именно события прерывание должно срабатывать. К примеру, если мы хотим получить прерывание при приёме байта, мы должны должны отловить событие USART_IT_RXNE (RX is Not Empty — принятый буфер не пуст). Команда будет выглядеть следующим образом:

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

Обратите внимание, что в отличие от констант RCC описанные здесь константы не являются масками, и через побитовое ИЛИ их объединять нельзя. Например, для включения прерывания и при приёме байта и при успешной передаче байта, придется писать две команды:

USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_ITConfig(USART1, USART_IT_TXE, ENABLE);


4.3. Обработчик прерывания

Обработчиком прерывания является функция, название которой совпадает с названием соответствующего вектора прерывания в startup-файле. Startup-файлы, входящие в состав STM32 Peripheral Library, написаны на ассемблере. Вот фрагмент файла startup_stm32f10x_md_vl.s:

DCD     SPI1_IRQHandler                 ; SPI1
DCD     SPI2_IRQHandler                 ; SPI2
DCD     USART1_IRQHandler               ; USART1
DCD     USART2_IRQHandler               ; USART2
DCD     USART3_IRQHandler               ; USART3
DCD     EXTI15_10_IRQHandler            ; EXTI Line 15..10

Мне так же встретился файл, написанный на Си:

void WEAK  SPI1_IRQHandler(void);
void WEAK  SPI2_IRQHandler(void);
void WEAK  USART1_IRQHandler(void);
void WEAK  USART2_IRQHandler(void);
void WEAK  USART3_IRQHandler(void);
void WEAK  EXTI15_10_IRQHandler(void);

В любом случае мы можем легко найти название обработчика прерывания, для рассматриваемого примера это USART1_IRQHandler(void). Нам осталось только описать функцию-обработчик прерывания:

void USART1_IRQHandler(void) {
   // Код обработчика прерывания
}

Функция будет вызываться при срабатывании прерывания. Однако, есть 2 важных момента. Во-первых, прерывание срабатывает на несколько событий, и нам надо проверять, какое именно из событий вызвало функцию. Во-вторых, бит ожидания обработки прерывания не всегда сбрасывается аппаратно, поэтому мы должны обязательно вручную, программно, сбросить этот бит, показав, что прерывание обработано. Иначе процессор войдет в бесконечный цикл, постоянно вызывая нашу функцию, считая, что если бит не сброшен, значит прерывание не обработано и функцию надо вызвать снова.

Учитывая эти 2 аспекта, «шаблонный» код обработчика мы подготовим вот таким:

void USART1_IRQHandler(void) {

    // Обработка события RXNE
    if ( USART_GetITStatus(USART1, USART_IT_RXNE) ) {
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
        // ... Код обработчика ...
    };

    // Обработка события TXE
    if ( USART_GetITStatus(USART1, USART_IT_TXE) ) {
        USART_ClearITPendingBit(USART1, USART_IT_TXE);
        // ... Код обработчика ...
    };

    // Обработка других событий... 
};

Здесь должно быть всё понятно. Функция USART_GetITStatus() проверяет наличие прерывания по определенному событию, Если проверка успешна, функция USART_ClearITPendingBit() сбрасывает тот самый «бит ожидания обработки», затем идет собственно код обработчика.

5. Подводя итог


Это был последний шаг. Выработанный алгоритм по настройке периферийного устройства получился такой:

  • Задействовать тактирование устройства.

    • Функция RCC_APBxPeriphClockCmd(RCC_APBxPeriph_***, ENABLE);
    • при необходимости задать предделитель, пример для АЦП: CC_ADCCLKConfig(RCC_PCLK2_Div6);
    • Имена констант искать файле stm32f10x_rcc.h
    • По имени константы определить шину тактирования (APB1 или APB2)
  • Произвести инициализацию с помощью init-структуры

    • Сбросить настройки с помощью функции ***_DeInit();
    • В заголовочном файле периферийного устройства (stm32f10x_***.h) найти название и описание структуры ***_InitTypeDef
    • Создать переменную типа этой структуры: ***_InitTypeDef ***_InitStructure;
    • Присвоить параметрам структуры требуемые значения
    • Вызвать команду инициализации ***_Init(&***_InitStructure);
    • При необходимости дать команду включения ***_Cmd(***, ENABLE);
  • Настроить прерывания

    • Инициализировать NVIC. Константы для NVIC_IRQChannel искать в stm32f10x.h;
    • Включить прерывания на определенные события функцией ***_ITConfig(). Значения констант искать в заголовочном файле периферии (stm32f10x_***.h)
    • Найти в startup-файле имя вектора прерывания, создать функцию-обработчик прерывания с таким же именем
    • Внутри обработчика проверять события функцией ***_GetITStatus() и обязательно очищать бит ожидания функцией ***_ClearITPendingBit().

Алгоритм обобщенный, но поможет вам справиться с большинством проблем, которые возникают на начальном этапе изучения микроконтроллеров STM32. Хочу отметить, что поиск в Google по именам функций и структур частенько дает примеры кода. Воспользуйтесь ими в качестве подсказки.

6. Пример


В качестве примера представлю, что я не умею работать с АЦП и на основе вышеописанного алгоритма попробую написать код, производящий все необходимые действия для простейшей работы с АЦП: запуск единичного преобразования с порта A1 и чтение значения по прерыванию.

// Подал тактовые импульсы на АЦП.
// Названия констант нашел в stm32f10x_rcc.h
RCC_ADCCLKConfig(RCC_PCLK2_Div6);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);

// Сбросил настройку
ADC_DeInit(ADC1);

// В stm32f10x_adc.h нашел название структуры ADC_InitTypeDef,
// объявил переменную с этим типом
ADC_InitTypeDef  ADC_InitStructure;

// Заполнил значения. Имена констант нашел в том же stm32f10x_adc.h,
// еще полистал Reference Manual для ознакомления с режимами
ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
ADC_InitStructure.ADC_ScanConvMode = DISABLE;
ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
ADC_InitStructure.ADC_NbrOfChannel = 1;

// Инициализировал АЦП заполненной структурой
ADC_Init(ADC1, &ADC_InitStructure);

// Включил 
ADC_Cmd(ADC1, ENABLE);

// Разрешил прерывания по окончанию преобразования
// Список возможных констант получен поиском по ADC_ITConfig 
// (найдено в stm32f10x_adc.c), из них выбрал нужную.
// EOC это End of conversion
ADC_ITConfig(ADC1, ADC_IT_EOC, ENABLE);

// Гугление показало, что для АЦП надо производить калибровку
// Тупо скопировал найденный код. Специфичная для АЦП вещь,
// поэтому в алгоритме ее нет. 
ADC_ResetCalibration(ADC1);
while (ADC_GetResetCalibrationStatus(ADC1)) { };
ADC_StartCalibration(ADC1);
while (ADC_GetCalibrationStatus(ADC1)) { };

// Настроил группы приоритета прерываний 
// (эта строка пишется, один раз за всю программу, где-то в начале)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);

// Создал структуру NVIC и заполнил ее значениями
// Название константы ADC1_IRQn взял из stm32f10x.h
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = ADC1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);

// Написал обработчик прерывания по рекомендуемому шаблону,
// не забыв проверить источник прерывания и сбросить бит
void ADC1_IRQHandler(void) {
    if (ADC_GetITStatus(ADC1, ADC_IT_EOC)) {
        ADC_ClearITPendingBit(ADC1, ADC_IT_EOC);
        // Чтение значения АЦП
        // Функцию нашел в stm32f10x_adc.h
        val = ADC_GetConversionValue(ADC1);
    };
};

// Как запустить преобразование не знаю,
// обратился к примеру из гугла. Оказалось, вот так:
ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_1Cycles5);
ADC_SoftwareStartConvCmd(ADC1, ENABLE);


Вот и всё. Таким же макаром можно освоить работу с любой несложной периферией на базовом уровне. Ну а дальше читать Reference Manual, изучать список функций stm32f10x_adc.h и их предназначение.

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

RSS свернуть / развернуть
Это моя первая в жизни статья, просьба ногами не бить :)
0
Эпично. +1.
+2
  • avatar
  • _YS_
  • 12 сентября 2011, 20:45
Плюсую, несмотря на то, что всегда испытывал рвотные позывы при виде инициализации периферии с помощью передаваемых в функцию структур.
0
Я сам не очень люблю подобные «обёртки» над тем, что можно и руками сделать. Но в данном случае выбрал структуры, посчитав их более удобными. А вот с портами ввода-вывода предпочитаю работать напрямую, через ODR и IDR.
0
Но полюбому, статья отличная, спасибо.
0
Отлично! Тоже хотел об этом как-нибудь написать, и заодно про то как все без косяков включить в проект Keil, но Вы меня опередили :)

Отдельное спасибо за АЦП, с ним как раз мне нужно было разбираться, сэкономили мне время!
0
  • avatar
  • Ezhik
  • 12 сентября 2011, 21:18
годнота! +1 Бля, опустили меня, не мого голосовать.
0
тоже понравилось
только уж больно много писанины в этих стм32
0
Хорошая, нужная, понятная статья. Все разложено по своим местам. Думаю, необходимо при описании процедур подготовки к работе с периферийными блоками упомянуть о настройке принадлежащих ему внешних выводах. Это тоже важный этап (или подэтап), его пропускать нельзя ни для DAC ни для ADC ни для SPI и т.п… Это, кстати, не лишне произвести и в приведенном примере работы с АЦП.
0
  • avatar
  • Rebe
  • 16 сентября 2011, 18:34
Ой, и правда, я совсем забыл. Спасибо. Исправлю на днях.
0
Эх, всё времени нет исправить :(
0
Скажи, ПЛЗ, разобрался ли ты в чем разница между:
TIM_GetITStatus — TIM_ClearITPendingBit
и
TIM_GetFlagStatus — TIM_ClearFlag
(Ну, не обязательно ТИМ — вообще?)
0
IT — это сокращенно interrupt. Это бит, поднимаемый при срабатывании прерывания.

Flag — это просто флаг, используемый периферией. К прерываниям никак не относящийся. Точный пример не приведу, но, наверное, в таймере есть флаг «переполнение», который поднимается при переполнении и которым можно пользоваться при написании программы.
0
Уверен? А конкретней? Если поглядеть на регистры UART то там левых флагов нет, на каждый из них привязано по прерыванию.
0
Автору СПАСИБО!
0
ковыряю платку stm32f4 discovery пытаюсь подцепить вывод.чтение в uart. вроде все нормально, но почему то вместо данных выводит какой то мусор в виде 2х букв «Ю» и «Э». uart в компе настроен правильно BaudRate = 115200; WordLength = 8b. привожу кусок кода.
/* Includes */
#include "stm32f4xx.h"

uint8_t usa;

int main(void)
{
	//configure clock for USART
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
	//configure clock for GPIO
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
	//configure AF
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource8,GPIO_AF_USART3);
	GPIO_PinAFConfig(GPIOD,GPIO_PinSource9,GPIO_AF_USART3);

	//configure ports, &GPIO_InitStructure);
	GPIO_InitTypeDef GPIO_InitStructure;

	GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOD, &GPIO_InitStructure);

	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_Init(GPIOD, &GPIO_InitStructure);

	USART_InitTypeDef USART_InitStructure;

	USART_InitStructure.USART_BaudRate = 115200;
	USART_InitStructure.USART_WordLength = USART_WordLength_8b;
	USART_InitStructure.USART_StopBits = USART_StopBits_1;
	USART_InitStructure.USART_Parity = USART_Parity_No;
	USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
	USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

	USART_Init(USART3, &USART_InitStructure);

	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = USART3_IRQn;
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

	NVIC_Init(&NVIC_InitStructure);

	USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);

	/* Enable USART */
	USART_Cmd(USART3, ENABLE);

	while(1)
	{
		USART_SendData(USART3, (uint8_t)'S');
	}
}

0
Зачем настроено прерывание USART_IT_RXNE? Обработчик есть?
Уверен, что baud rate верный? А может, контроллер затактирован не тем, чем надо, и baud rate съехал у контроллера? Я бы ткнулся осциллографом и посмотрел на период импульсов.
0
Прерывание настроено на прием данных usart, но в данной программе не используется (можно удалять ничего не измениться). baud rate и другие настройки порта на компе настроены 100% верно (в готовом freertos) все работает нормально. конфиг контроллера вроде тоже по даташиту (взят из заготовки атолик, под мою платку).
*=============================================================================
  *=============================================================================
  *        Supported STM32F4xx device revision    | Rev A
  *-----------------------------------------------------------------------------
  *        System Clock source                    | PLL (HSE)
  *-----------------------------------------------------------------------------
  *        SYSCLK(Hz)                             | 168000000
  *-----------------------------------------------------------------------------
  *        HCLK(Hz)                               | 168000000
  *-----------------------------------------------------------------------------
  *        AHB Prescaler                          | 1
  *-----------------------------------------------------------------------------
  *        APB1 Prescaler                         | 4
  *-----------------------------------------------------------------------------
  *        APB2 Prescaler                         | 2
  *-----------------------------------------------------------------------------
  *        HSE Frequency(Hz)                      | 8000000
  *-----------------------------------------------------------------------------
  *        PLL_M                                  | 8
  *-----------------------------------------------------------------------------
  *        PLL_N                                  | 336
  *-----------------------------------------------------------------------------
  *        PLL_P                                  | 2
  *-----------------------------------------------------------------------------
  *        PLL_Q                                  | 7
  *-----------------------------------------------------------------------------
  *        PLLI2S_N                               | NA
  *-----------------------------------------------------------------------------
  *        PLLI2S_R                               | NA
  *-----------------------------------------------------------------------------
  *        I2S input clock                        | NA
  *-----------------------------------------------------------------------------
  *        VDD(V)                                 | 3.3
  *-----------------------------------------------------------------------------
  *        High Performance mode                  | Enabled
  *-----------------------------------------------------------------------------
  *        Flash Latency(WS)                      | 5
  *-----------------------------------------------------------------------------
  *        Prefetch Buffer                        | OFF
  *-----------------------------------------------------------------------------
  *        Instruction cache                      | ON
  *-----------------------------------------------------------------------------
  *        Data cache                             | ON
  *-----------------------------------------------------------------------------
  *        Require 48MHz for USB OTG FS,          | Enabled
  *        SDIO and RNG clock                     |
  *-----------------------------------------------------------------------------
  *=============================================================================
  ****************************************************************************** 

осциллографа под рукой нет чтобы ткнуть к сожалению
0
маленько причесал код. теперь он выглядит вот так, но проблему не решило =(

#include "stm32f4xx.h"

void vUSART2_Init( void ) {
    USART_ClockInitTypeDef USART_ClockInitStruct;
    USART_InitTypeDef USART_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    // Enable needed clocks for uart.
    RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2, ENABLE );
    RCC_AHB1PeriphClockCmd( RCC_AHB1Periph_GPIOA, ENABLE );

    // Make sure you use 'GPIO_PinSource2' and NOT 'GPIO_Pin_2'.  Using the
    // latter will not work!
    GPIO_PinAFConfig( GPIOA, GPIO_PinSource2, GPIO_AF_USART2 );
    GPIO_PinAFConfig( GPIOA, GPIO_PinSource3, GPIO_AF_USART2 );

    // Setup Tx / Rx pins.
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;			// Tx Pin
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_Init( GPIOA, &GPIO_InitStructure );
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;			// Rx Pin
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init( GPIOA, &GPIO_InitStructure );

    // Make sure syncro clock is turned off.
    USART_ClockStructInit( &USART_ClockInitStruct );
    USART_ClockInit( USART2, &USART_ClockInitStruct  );

    // Setup transmit complete irq.
    USART_ITConfig( USART2, USART_IT_TC, ENABLE );

    // Use defaults (except baud rate).
    USART_StructInit( &USART_InitStructure );
    USART_InitStructure.USART_BaudRate = 115200;
    USART_Init( USART2, &USART_InitStructure );
    USART_Cmd( USART2, ENABLE );

    /*NVIC_InitTypeDef NVIC_InitStructure;
    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);*/
}

void Delay(__IO uint32_t nCount)
{
  for(; nCount != 0; nCount--);
}

void print(const char* str)
{
	int i = 0; while(str[i]) {
		i++; USART_SendData(USART2, str[i]);
		while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET) {}
	}
}

int main(void)
{

		vUSART2_Init();
		Delay(2000000);
        print("Hi_world!!!/r/n");
		Delay(2000000);
        while(1)
        {
        	;;
        }
}


0
Честно говоря я даже свой код почти никогда не анализирую, а пытаюсь продиагностировать проблему более быстрыми решениями. В данном случае — осциллографом. Он покажет:
а) работает ли вообще UART
б) измерит baud rate
Если осциллографа нет — сойдет какой-нибудь счетчик (для непрерывно отправляй 0b10101010, с учетом стартового бита «0» и стопового «1» будет непрерывный меандр с частотой baud rate).

Еще как вариант — снизить baud rate до очень низких значений (1200 бод, например), «на взгляд» оценить с какой частотой в терминале появляются буквы «Э», посмотреть ASCII-код этого символа и нарисовать осциллограмму, которую реально принимает СОМ-порт компа. И дальше проанализировать.
0
попробовал отправить просто FF получил на компе FC (понял что дело все таки в скорости). видимо как то криво затактирован usart. потыкал скорости в терминале при 38400 бод все заработало. будем разбираться.

0
результат этой программы
0
Не смог найти где в стандартных либах прописаны кортекс'овские базы
регистров ISER,ICER,ISPR,ICPR и др. от NVIC? Неужели их ручками вносить?
0
  • avatar
  • valio
  • 02 февраля 2013, 18:36
Нужно скачать с сайта st.com StdPeripheralLib для Вашего семейства.
0
Спасибо, разобрался. Старый SPL был в примере, и сборка либ
по другому подключалась. У кейла всегда с прибабахом проекты.
0
сохранил в виде pdf
мне понравилось
спасибо.
приятно читать подобное
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.