Программирование STM32F4. USART, прием данных.

Итак, совсем недавно мы познакомились с приемо-передатчиком USART в микроконтроллерах STM32F4, сваяли даже пример для передачи данных в окружающий мир (вот). Как и обещал, сегодня разберемся с приемом данных. Теории не будет, все уже вроде обсудили при работе с передачей, так что без лишних прелюдий переходим сразу к написанию программы 😉

Для начала рассмотрим общую последовательность действий, необходимых для запуска USART в STM32F4 и настройки его для работы в качестве приемника.

1. Работаем с тактированием – включаем тактирование приемо-передатчика, а заодно и тактирование порта, который отвечает за ножки Rx/Tx.
2. Сначала настраиваем нужные ножки микроконтроллера, а затем и сам USART.
3. Включаем прерывания – функция NVIC_EnableIRQ() и запускаем USART — USART_Cmd().
4. Наконец включаем прерывание, которое нам в данном случае понадобится, то есть прерывание по приему данных — для этого нам пригодится функция USART_ITConfig().

Вот в принципе и все. Вся работа будет в прерывании, соответственно необходимо будет реализовать обработчик прерывания. В общем, давайте уже это все осуществим на деле )

Будем использовать, как и при передаче данных, модуль USART1 микроконтроллера STM32F4, так что многое будет точно таким же, как и в том примере.

/*******************************************************************/
// Подключаем файлы
#include "stm32f4xx.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_usart.h"
 
 
 
/*******************************************************************/
// Объявляем переменные
GPIO_InitTypeDef gpio;
USART_InitTypeDef usart;
// Будем принимать 16 байт, к примеру )
uint8_t receivedData[16];
uint8_t bytesToReceive =16;
// Счетчик принятых байт
uint8_t receivedDataCounter = 0;
// Пока все идет точно также, как и в случае с передачей ;)
 
 
 
/*******************************************************************/
// Инициализируем все подряд
void initAll()
{
    // Глобальное разрешение прерываний
    __enable_irq();
 
    // Тактирование, куда ж без него-то
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
 
    // Настраиваем ножки  контроллера, тут все понятно
    GPIO_StructInit(&gpio);
 
    gpio.GPIO_Mode = GPIO_Mode_AF;
    gpio.GPIO_Pin = GPIO_Pin_9;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    gpio.GPIO_OType = GPIO_OType_PP;
    gpio.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOA, & gpio);
 
    gpio.GPIO_Mode = GPIO_Mode_AF;
    gpio.GPIO_Pin = GPIO_Pin_10;
    gpio.GPIO_Speed = GPIO_Speed_50MHz;
    gpio.GPIO_OType = GPIO_OType_PP;
    gpio.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_Init(GPIOA, & gpio);
 
   // Обязательно вызываем эту функцию
   GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
 
   // Настраиваем модуль USART
   USART_StructInit(&usart);
   usart.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
   usart.USART_BaudRate = 9600;	
   USART_Init(USART1, &usart);	
 
   // Включаем прерывания и запускаем USART
   NVIC_EnableIRQ(USART1_IRQn);
   USART_Cmd(USART1, ENABLE);
}
 
 
 
/*******************************************************************/
// Функция main()
int main()
{
    // Тут нам нужно лишь вызвать функцию инициализации и запустить процесс – 
    // то есть включить прерывание по приему данных
    initAll();
 
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
    while(1)
    {
         __NOP();
    }
 
 
 
/*******************************************************************/
// Прерывание
void USART1_IRQHandler()
{
    // Убеждаемся, что прерывание вызвано новыми данными в регистре данных
    if (USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
    {
        // Чистим флаг прерывания
        USART_ClearITPendingBit(USART1, USART_IT_RXNE);
 
        // То ради чего все затеяли – принимаем данные )
        receivedData[receivedDataCounter]  = USART_ReceiveData(USART1);
 
        // Приняли? Увеличиваем значение счетчика!
        receivedDataCounter ++;
 
        // Приняли 16 байт – выключаем все нафиг, данные у нас ;)
        if  (receivedDataCounter == bytesToReceive)
        {
            USART_ITConfig(USART1, USART_IT_RXNE, DISABLE);
        }
    }
}  
 
 
 
/*******************************************************************/

Вот такой получился пример. За исключением обработчика прерывания все также как и с передачей данных, можно было в принципе и в одну статью все запихать =)
Отмечу, кстати вызов функции GPIO_PinAFConfig():

GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);

Я когда первый раз работал в STM32F4 с USART’ом, упустил из виду то, что надо вызывать эту функцию и некоторое время не мог воткнуть почему ничего не работает ) После STM32F10x казалось, что достаточно вот этого:

gpio.GPIO_Mode = GPIO_Mode_AF;

Но нет, тут все серьезней 😉 Так что при работе с любой периферией в STM32F4 не забываем про вызов функции GPIO_PinAFConfig().

Что еще то можно сказать про USART? Да вроде бы все… Так что, если возникли какие-нибудь вопросы, обязательно спрашивайте в комментариях, а на сегодня это пожалуй все )

P. S. Я тут кстати себе сделал небольшую библиотечку для ускорения работы с USART’ом, на днях обязательно выложу, может кому пригодится

Понравилась статья? Поделись с друзьями!

Программирование STM32F4. USART, прием данных.: 30 комментариев
  1. Было бы классно объединить 2 статьи (прием и передача) в одну.
    Скажем передаем с USART1 «led1» и загорается определенный светодиод, а если принят мусор или что-то ещё загорается другой… ну как-то так. Это было бы наглядно, и понятно. У меня пока только STMка активно передает, а вот с приемом что-то не то.
    STM32F3[4]-discaveri (код для 3 от 4ой не очень отличается, проверил, даже пины для усарт1 одинаковы)
    PS а ещё, было бы офигенно раскурить пример PWM с изменением яркости светодиода, на пальцах, и с STM либами 🙂

  2. Спасибо за статью. Пытаюсь разобраться с USART в STM32F3FDISCOVERY . С передачей проблем нет. Проблема с приемом — принимается только первый байт. Следующий байт не замещает первый. Как вытолкнуть принятый байт?..

    • Все должно само замещаться..Если все в линии нормально, то есть байты действительно высылаются контроллеру, то что-то не так в коде

  3. Я пробовал, но что-то пошло не так… Инит, судя по всему, останавливал все процессы в организме отладочника… Заработало при такой комбинации:
    (Ах, да, я использовал третий уарт, т.к. в СТМ_Дискавери на первом программатор ещё висит и мусорит мне в терминал, позже буду на первый переходить как свою плату сделаю…)

    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    USART_ClockInitTypeDef USART_ClockInitstructure;

    /* Enable GPIOD clock */
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);

    /* Enable USART clock */
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);

    /* Connect PXx to USARTx_Tx*/
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource8, GPIO_AF_USART3);

    /* Connect PXx to USARTx_Rx*/
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource9, GPIO_AF_USART3);

    /* Connect PXx to USARTx_Ct*/
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource11, GPIO_AF_USART3);

    /* Connect PXx to USARTx_Rt*/
    GPIO_PinAFConfig(GPIOD, GPIO_PinSource12, GPIO_AF_USART3);

    /* Configure USART as alternate function */
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    // Tx Rx Ct Rt
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_11 | GPIO_Pin_12;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOD, &GPIO_InitStructure);

    USART_InitStructure.USART_BaudRate = bauld;
    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 Clock Initialization */
    USART_ClockInitstructure.USART_Clock = USART_Clock_Disable ;
    USART_ClockInitstructure.USART_CPOL = USART_CPOL_High ;
    USART_ClockInitstructure.USART_LastBit = USART_LastBit_Disable;
    USART_ClockInitstructure.USART_CPHA = USART_CPHA_1Edge;

    /* USART configuration */
    USART_Init(USART3, &USART_InitStructure);
    USART_ClockInit(USART3, &USART_ClockInitstructure);

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

    // Включаем прерывания и запускаем USART
    NVIC_EnableIRQ(USART3_IRQn);

    USART_ITConfig(USART3, USART_IT_RXNE, ENABLE);

    Из явного могу отметить только отсутствие включения прерываний ручного, да блок инициализации часов уарта. Из прерывания я сделал эхо, а так оно работает без изменений…

    Спасибо за статью! Реально помогла без особой головной боли настроить приём информации с терминала.

  4. Спасибо за пример, все отлично. Но вот вопрос: как отправить данные на плату?
    Как реализовать общение пк и микросхемы?

  5. Подскажите, пожалуйста.
    Если использую следующую инициализацию:
    usart3.NVIC_IRQChannel = USART3_IRQn;
    usart3.NVIC_IRQChannelPreemptionPriority = 2;
    usart3.NVIC_IRQChannelSubPriority = 2;
    usart3.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&usart3);
    то есть ли необходимость использовать:
    NVIC_EnableIRQ(USART3_IRQn);
    Чем отличаются в этом контексте первая и последняя строки. Если последняя строка разрешает прерывания по USART3, то первая что делает?

    • Можно и так и так в принципе. В первом варианте заполняются поля структуры usart3 и затем вызывается функция инициализации

  6. Т.е., если я буду использовать систему приоритетов прерываний, то мне НУЖНО использовать эти строки с полями структуры. При этом НЕТ НЕОБХОДИМОСТИ использовать NVIC_EnableIRQ(USART3_IRQn)? (это вопрос один). И, наоборот. Если используются прерывания и НЕ НУЖНЫ приоритеты, то ДОСТАТОЧНО использовать ТОЛЬКО NVIC_EnableIRQ(USART3_IRQn) БЕЗ ЗАПОЛНЕНИЯ полей структур? (это вопрос два)
    Я правильно понимаю?

  7. что делает строчка
    gpio.GPIO_PuPd = GPIO_PuPd_UP;
    на F100 F303 вроде она не используется, стало интересно!

  8. Извините, что немного оффтоп, но не могли бы вы подсказать почему заголовок функции прерывания по USART3 не видит прототипа?Плата STM32F429-Discovery.
    void USART3_IRQHandler(void)
    {}
    В хейдере:
    #include «stm32f4xx_it.h»
    #include «main.h»
    #include «stm32f4xx_usart.h»
    #include «stm32f4xx_gpio.h»
    #include «init_UART.h»
    #include «GSM_WISMO228.h»

    Т.е. 1 и 2 определяет но 3 нет, хотя в файле startup_stm32f429_439xx.s есть конструкции для этого прерывания.

  9. Здравствуйте! Зашил предложенную тут программу, но на приёме вместо 0х11 … 0х88 вижу массив из этого:
    «\300\300w\367\200\300\300w\367\200\300\300w\367\200\300»
    Что это? Пишу в CooCox. смотрю через Debug.
    И массив при отправке выглядит вот так:
    «21\»3DUfw»
    Что то с отображением?

  10. А для работы USART сколько надо проводников? 2? TX и RX? или надо ещё что-то?

    • Да, 2, но обязательно необходимо обеспечить общую землю на коммутируемых устройствах.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *