UART в STM32

На борту микроконтроллера STM32 есть несколько UARTов.
Если глянуть что поэтому поводу говорит википедия > UART, мы узнаем что это один из самых распространенных протоколов обмена информации.
Итак у STM32  есть таких несколько а именно:

Пин
Функция
PA8
USART1_CK
PA11
USART1_CTS
PA12
USART1_RTS
PA9
USART1_TX
PA10
USART1_RX
PA4
USART2_CK
PA0
USART2_CTS
PA1
USART2_RTS
PA2
USART2_TX
PA3
USART2_RX
PB12
USART3_CK
PB13
USART3_CTS
PB14
USART3_RTS
PB10
USART3_TX
PB11
USART3_RX


Как мы видим  у нас есть 3 полноценных  USART с распиновкой. Давайте подключим отладочную плату к FT232RL по такой схеме
  • PA9 от STM32 (USART1_TX) подключим к RXD на FT232RL
  • PA10 от STM32 (USART1_RX) подключим TXD на FT232RL
  • GND на STM32 подключим GND на FT232RL
у меня вышло что то такое
(сорри за качество фото с мобильника, но сути не меняет)


Теперь напишем в Coocox IDE программу для нашего МК. Будем использовать стандартные библиотеки от ST.
 Для того что бы пользоваться USART нам необходимо инициализировать и настроить его.

  • включить системное тактирование для USART1
  • настроить пины RX/TX для USART1
  • установить baudrate для USART1
  • включить USART1  и его RX/TX компоненты.
В установках flow-control и  stop-bit уже установлены на N и 1 по умолчанию, нет
необходимости конфигурировать их для нашего примера.
Для того что бы отправлять или получать данные мы будем использвать DR(Data Register) используемого USART.
Если прочитать регистр то мы получим последним полученный байт. Если записать в регистр, то записанный байт будет передан.


Есть два решения для отправки/приема данных.

Первое (не очень хорошее). Для отправки используется задержка после записи в регистр, и если задержка достаточно велика, мы можем быть уверены в том, что наши данные были переданы. Далее будем опрашивать регистр до тех пор пока прочитанный байт не будет отличатья от последнего прочитанного.
Мы же будем использовать решение лучше. Для нашего примера пригодится SR(Status Register) на USART1. SR имеет бит, который сообщаетбыли ли переданы/получены данные. Как только DR будет прочитан, Флаг полученных данных автоматически сбрасывается.
Альтернативно можно включить прерывание USART1 для полученных данных. Если мы это сделаем, прерывание будет обрабатываться каждый раз как только будут поступать новые данные.
 Далее я более детально опишу, как настроить, получать и принимать данные на языке С. Для этого нам нужно сделать 3 функции.

  • board_init: тут проведиться вся инициализация
  • usart_rec: блок получения данных
  • usart_snd: блко отправки данных.


board_init

Для начала нам необходимо включить тактирование USART1 и GPIOA ( так RX/TX находятся именно тут). Для использования  втроенных светодиодов , необходимо включить CPIOC.
Сделаем это установивши регистр RCC для шины APB2 (как GPIOA так USART1 покдлючены к данной шине):
    RCC->APB2ENR = 0
        // Включаем USART1
        | RCC_APB2ENR_USART1EN
        // Включаем IO Port A
        | RCC_APB2ENR_IOPAEN;
Теперь надо насторить GPIO для РA9(TX) и PA10(RX). Пин ТХ должен быть настроен пин-выход с альтернативной функцией (USART1_TX). Конфигурируем это пин на выход режиме push-pull и частоте 50 МГц. Собрав все эти биты для настройки вместе мы получим значение 0хВ для данной конфигурации. Р10 (RX) должен быть настроен на вход в режиме floating input так как подключенная FT232RL сама позаботится о pull-up/pull-down. Для настройки floating input для нужного пина нужно в регистр записать значение 0х4. В коде это буде выглядеть вот так:
    // Устанавливаем PA9  (TX) на альтернавтивную функцию выхода push-pull при 50 MHz
    // Установим PA10 (RX) на floating input
    GPIOA->CRH = 0x000004B0;

Замечание: мы используем CHR для пинов с 8 по 15 так они находят в регистре Н, а пины с 0 по 7 части регистра L.
Далее нам необходимо настроит USART. Для начала опеределим используемый бодрейт. Это делается используя регистр BRR в USART. Так как у STM32 фракционный генератор можно получить любой бодрейт из системного тактирования (systems bus clock). Значение для регистра BRR можно легко расчиать из такой простой формулы:

USART_BRR = [BUS_FREQUENCY_in_Hz] / [BAUD_RATE]

Для того что бы настроить USART на 38400 бод,запишем следующее в код:
    // Настроим BRR делением тактирования на необходиимый бодрейт
    USART1->BRR = 8000000/38400;
И наконец нужно включить UART, TX, RX. Делается это установкой соответствующих флагов в конфигурирующем регистре 1 (CR1) USART1.
USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
Вот и все. Ваш USART теперь готов общаться со скоростью 38400 бод при 8 битах данный, без flow control и одним stop-bit.

usart_rec


Для того что прочитать полученные данные,  все что необходиом сделать ,то это подождать пока  RXNE (RX not empty (не пустой))  бит  не будет установлен в регистре статуса SR в  USART1. После того как  бит будет установлен, мы читаем данные из  регистра данных DR в USART1. Так как регистр DR 32 битный, но используется только  9 бит,  мы применим маску  0x1FF для входящих данных, что бы не получать мусор. После того как регистр DR будет проичан, USART сбрасывает RXNE флаг автоматически. В нашей программе это будет выглядеть вот так.
   /* Ждем пока данные будут получены. */
   while ((USART1->SR & USART_SR_RXNE) == 0);

   // читаем данные RX,  применяя маскуDR мы получаем только 9 бит)
   return USART1->DR & 0x1FF;

board_snd

Для того что бы отправить данные, нам просто нужно их записать в регистр данных DR. Далее подождем  пока флаг ТХЕ (TX empty (пустой) в регистре статуса SR  будет установлен и можем быть уверены что данные переданы. В коде это будет выглядеть вот так:
   USART1->DR = data;

   // ждем отправки TX
   while ((USART1->SR & USART_SR_TXE) == 0);
Вот код для main.c
 /**
 * USART Loopback sample for STM32 Discovery board
 *
 * 26.07.2011, Stefan Wendler, devnull@kaltpost.de
 *
 * читает байт RX с USART1, отображает в квадратных
 * скобках  и шлет в TX   USART1.
 *
 * т.е. вводим"A", программа отвечает "[A]".
 *
 * Каждый раз как только байт получен, мигает зеленый светодиод,
 * а как только отправляются 3 байта мигает синий.
 * 
 *
 * USART1 настроен 38400, 8, N, 1
 * PA9 - TX , PA10 - RX.
 *
 */
#include "stm32f10x.h"

#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_i2c.h"//i2c
#include <stdio.h>

#include "stm32f10x.h"

#define LED_BLUE 8 /* PC8-синий LED */
#define LED_GREEN 9 /* PC9 зеленый LED */
#define MODE_ON 0 /* Offset to add to pin for ON */
#define MODE_OFF 16 /* Offset to add to pin for OFF */

void nmi_handler(void);
void hardfault_handler(void);
int  main(void);

/**
 * Four vectors - the starting stack pointer value,
 * code entry point and NMI and Hard-Fault handlers
 */
unsigned int * myvectors[4]
__attribute__ ((section("vectors")))= {
    (unsigned int *) 0x20002000,
    (unsigned int *) main,
    (unsigned int *) nmi_handler,
    (unsigned int *) hardfault_handler
};

/**
 * Инициализируем плату
 *  - влючаем тактирование
 *  - настраиваем GPIO
 *  - настраиваем USARТ1
 */
void board_init(void) {

    // Большиство периферии подключено к APB2. Включаем тактирование
    // на интересуещей нас переферии
    RCC->APB2ENR = 0
                   // Влючаем USART1
                   | RCC_APB2ENR_USART1EN
                   // ВлючаемIO Порта A
                   | RCC_APB2ENR_IOPAEN
                   // Включаем IO Порта C
                   | RCC_APB2ENR_IOPCEN;

    // Установим PC8 и PC9 в режим outpute (встроенные  LED)
    GPIOC->CRH = 0x11;

    // Установим PA9  (TX)альтернативную функцию выхода  push-pull при 50 MHz
    //УстановимPA10 (RX) в floating input
    GPIOA->CRH = 0x000004B0;

    // Сконфигурируем BRR делением bus clock на baud rate
    USART1->BRR = 8000000/38400;

    // Активируем USART, TX, и RX схему
    USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
}

/**
 * Задержка для"cnt" NOPs.
 *
 * @param[in] cnt количество NOPs для задержки
 */
void delay(int cnt) {
    while (cnt-- > 0) {
        asm("nop");
    }
}

/**
 * Получим один байт отUSART1.  будем ждать
 * пока USART не даст сигнал о том что байт получен
 *
 * @return полученый байт
 */
int usart_rec(void) {
    /* Ждем готовность принятия данных. */
    while ((USART1->SR & USART_SR_RXNE) == 0);

    // читаем данные RX , применяя маску  DR (будем принимать только 9 битs)
    return USART1->DR & 0x1FF;
}

/**
 * Шлем один байт в \USART1. Ждем пока
 * USART не отошлет байт.
 *
 * @param[in] байт который нужно отправить
 */
void usart_snd(int data) {
    USART1->DR = data;

    // ждемTX
    while ((USART1->SR & USART_SR_TXE) == 0);
}

/**
 * Шлем строку в USART1
 *
 * @param[in] str которую надо отправить */
void usart_snd_str(char *str) {
    int   i = 0;

    while(str[i] != 0) {
        usart_snd(str[i++]);
    }
}

/**
 * Включаем LED на PORTC on/off.
 *
 * @param[in] пин светодиода(0-15) для включения/вылючения
 * @param[in] режим светодиода (LED_ON | LED_OFF)
 */
void set_led(int led, int mode) {
    GPIOC->BSRR = 1<<(led + mode);
}

/**
 * Мигаем светодиодом.
 *
 * @param[in] пин светодиода(0-15)для мигания
 */
void flash_led(int led) {
    set_led(led, MODE_ON);
    delay(15000);
    set_led(led, MODE_OFF);
}
int main(void) {

    board_init();

    usart_snd_str("\n\rПожалуйста введите что нибудь\n\r");

    for(;;) {
        // читаем USART
        int rec = usart_rec();

        // мигнем зеленым светодиодом если принила данные
        flash_led(LED_GREEN);

        if(rec == '\r') {
            usart_snd_str("\n\r");
        } else {
            // шлем байт в скобках
            usart_snd('[');
            usart_snd(rec);
            usart_snd(']');
        }

        // мигаем синим после оправки 3 байт
        flash_led(LED_BLUE);
    }
}

void nmi_handler(void) {
    return ;
}

void hardfault_handler(void) {
    return ;
}

и вот что выходит  в терминале

Коментарі

  1. Перехожу с avr-8bit на stm32 статья как раз в тему, с подсветкой синтаксиса кода было бы немного более читабельней, удачи в начинаниях!

    ВідповістиВидалити

Дописати коментар

Популярні дописи з цього блогу

Что значит поступать разумно?