Авг 112012
 

Универсальный синхронно-асинхронный приемопередатчик  (USART) – один из “долгожителей” цифровой техники. В большинстве современных микроконтроллеров он является стандартным модулем и, наверное, еще долго будет применяться из-за своей простоты и надежности. В STM32 может быть несколько таких приемопередатчиков, все они имеют стандартный и легко настраиваемый интерфейс обмена. Ниже будет рассмотрена классическая задача – передача данных из микроконтроллера в компьютер через виртуальный COM-порт.

Старт- и стоп-биты, биты данных, контроль четности и т.д., вся эта структура интерфейса описана довольно подробно во множестве источников, поэтому на данной теме останавливаться не будем. Здесь же я изложу лишь свой опыт практической реализации пересылки данных из микроконтроллера в компьютер. В данном случае использован стандартный интерфейс RS232 для подключения к компьютеру, приемопередатчик работает в полнодуплексном режиме, то есть для передачи и приема данных используются разные выводы микроконтроллера (TX и RX), что позволяет передавать и принимать посылки независимо друг от друга. Передача данных – асинхронная.

Приемопередатчик в STM32, конечно, “навороченный”. Кроме RS232 есть возможность использовать и другие физические стандарты, такие как LIN,  IrDA и интерфейс Smartcard ISO 7816-3. Если кому-то интересно что это за стандарты, вот вам их краткое описание из Википедии:

“LIN (англ. Local Interconnect Network — локальная сеть) — стандарт промышленной сети, разработанный консорциумом европейских автопроизводителей и других известных компаний. Основные задачи, возлагаемые на LIN консорциумом европейских автомобильных производителей — объединение автомобильных подсистем и узлов (таких как дверные замки, стеклоочистители, стеклоподъёмники, управление магнитолой и климат-контролем, электролюк и так далее) в единую электронную систему. LIN-протокол утверждён Европейским Автомобильным Консорциумом как дешёвое дополнение к сверхнадёжному протоколу CAN.”

“Infrared Data Association — IrDA, ИК-порт, Инфракрасный порт — группа стандартов, описывающая протоколы физического и логического уровня передачи данных с использованием инфракрасного диапазона световых волн в качестве среды передачи. Является разновидностью оптической линии связи ближнего радиуса действия. Была особо популярна в конце 1990-х начале 2000-х годов. В данное время практически вытеснена более современными способами связи, такими как WiFi и Bluetooth.”

“ISO/IEC 7816 — стандарт относится к смарт-картам (в первую очередь контактным). Описывает форму карты, контактов, их расположение и назначение; протоколы обмена и некоторые аспекты работы с данными. Часть 3 описывает электрические параметры интерфейса и некоторые принципы установления связи для карт.”

Поскольку реализация обмена данными с использованием трех последних вышеприведенных физических стандартов в планы не входит, то при описании модуля USART и его регистров, все что относится к этим стандартам будет пропущено. Будут рассмотрены лишь те настройки регистров USART, которые необходимы для данной задачи – передачи данных через интерфейс RS232.

Начнем же со схемы подключения микроконтроллера к компьютеру. В компьютере, в данном случае, использован виртуальный COM-порт, то есть микроконтроллер подключен к USB через микросхему преобразователь USB – RS232, а именно FT232RL. О таком способе подключения микроконтроллера к компьютеру я уже писал в статье STM32. Встроенный bootloader. Прошивка через Flash Loader Demonstrator, правда схема будет немного отличаться в данном случае.

usart

В примере будет реализована передача данных от STM32 в компьютер, поэтому у микроконтроллера подключен только вывод TX, вход приемника RX здесь не задействован. Передатчик и приемник включаются и выключаются раздельно. Я использовал микроконтроллер STM32F103RCT6. Он запаян у меня на самодельной отладочной плате, прошивал я его через встроенный программатор ST-LINK платы STM32L-DISCOVERY. Порядок подключения и прошивки описан в статье STM32-DISCOVERY. Встроенный ST-LINK + STM32F103 + Keil, правда там в качестве среды разработки используется Keil, а в данном примере IAR, но тут разницы никакой нет, в IAR все настройки ST-LINK те же, что и для платы STM32L-DISCOVERY.

Передавать данные будем в следующем формате: старт-бит, 8 бит данных, один стоп-бит, без контроля четности, скорость 9600 бод. Далее подробней остановимся на регистрах микроконтроллера, рассмотрим какие именно биты могут быть задействованы при обмене данными через интерфейс RS232 в асинхронном режиме.

Регистры:

Регистр статуса USART_SR:

TXE (Transmit data register empty) – регистр данных передатчика пустой. Устанавливается аппаратно, когда содержимое регистра данных USART_DR перемещается в регистр сдвига для последующей передачи. Сбрасывается при записи нового значения в регистр данных.

TC (Transmission complete) передача данных завершена.

RXNE (Read data register not empty) – регистр данных приемника не пустой. Устанавливается тогда, когда принятые данные перемещены из регистра сдвига в регистр данных USART_DR. После того, как данные из регистра данных USART_DR будут считаны, этот бит автоматически очищается.

ORE — (Overrun error) флаг устанавливается в том случае, когда предыдущие данные из USART_DR еще не считаны (RXNE=1), а в регистр сдвига уже приняты новые данные. Флаг сигнализирует о потере данных при приеме, текущее содержимое регистра данных будет затерто новыми принятыми данными.

PE (Parity error) ошибка при проверке четности принятых данных.

Регистр данных USART_DR:

Это регистр данных, используются младшие 9 бит, остальные зарезервированы. Фактически совмещает 2 регистра данных, передатчика и приемника, каждый со своим отдельным сдвиговым регистром.

Регистр USART_BRR:

А в этом регистре устанавливается скорость обмена данными. Причем коэффициент деления тактовой частоты можно задавать дробным числом, что позволяет в STM32 более гибко и точно настраивать скорость обмена. Биты DIV_Mantissa задают целую, а биты DIV_Fraction дробную части коэффициента деления. О порядке расчета будет ниже.

Управляющий регистр USART_CR1:

UE (USART enable) – установкой этого бита включается модуль USART.

M (Word length) – размерность слова данных:

  • 0 – 8 бит
  • 1 – 9 бит

PCE (Parity control enable) – установка бита включает режим контроля четности принятых данных. При включенном режиме старший бит принятых данных отвечает за проверку.

PS (Parity selection): задает варианты проверки на четность

  • 0 – четное число
  • 1 – нечетное число

PEIE (PE interrupt enable) – установка бита разрешает прерывание при появлении ошибки проверки на четность (при PE =1 в USART_SR).

TXEIE (TXE interrupt enable) – установка бита разрешает прерывание, при опустошении регистра данных передатчика (TXE = 1 в USART_SR).

TCIE (Transmission complete interrupt enable) – установка бита разрешает прерывание при завершении передачи данных (TC = 1 в USART_SR).

RXNEIE (RXNE interrupt enable) – установка бита разрешает прерывание при возможности потери принятых данных в результате “затирания” новыми, или когда регистр данных приемника содержит данные (ORE = 1 или RXNE = 1 в USART_SR).

TE (Transmitter enable) – установка бита включает передатчик.

RE (Receiver enable) – установка бита включает приемник.

Управляющий регистр USART_CR2:

STOP (STOP bits) – число стоп-бит.

  • 00 – 1 бит
  • 01 – 0.5 бита
  • 10 – 2 бита
  • 11 – 1.5 бита

Для модулей UART4 и UART5 (если они есть в кристалле) доступны только целые значения 1 или 2 стоп-бита. Дробные значения для стоп-битов используются при работе со Smartcard.

Управляющий регистр USART_CR3:

DMAT (DMA enable transmitter)

DMAR (DMA enable receiver)

Установка этих двух бит разрешает использование режима DMA для передатчика и приемника USART, соответственно.

EIE (Error interrupt enable) – общее разрешение прерываний при возникновении некоторых ошибок, в частности, при потере данных при приеме (ORE = 1 в USART_SR).

Описание остальных разрядов этих регистров, которые задействованы для других физических стандартов обмена (LIN,  IrDA, Smartcard), я сознательно опустил, выделив только необходимое для настройки обмена с компьютером через интерфейс RS232.

А теперь рассмотрим порядок настройки линий порта GPIO и модуля USART. Для осуществления передачи данных из микроконтроллера последовательность действий по настройке будет такая:

  • В первую очередь включаем тактирование нужных модулей, а это USART… пусть будет USART1 и тогда, соответственно, GPIOA.
  • Настраиваем нужные выводы GPIOA для работы с USART1. Лезем в datasheet на микроконтроллер, ищем выводы, задействованные под TX и RX USART1. Для моего STM32F103RCT6 в 64-выводном корпусе это выводы  PA9 – USART1_TX (42 ножка) и PA10 — USART1_RX (43 ножка). В данном примере используется только передача данных от микроконтроллера к компьютеру, поэтому необходимо настроить только вывод PA9. Этот вывод порта необходимо настроить как выход в режиме Push-Pull с отключенными резисторами подтяжки, и в режиме использования альтернативной функции.
  • Включаем USART1 установкой бита UE в регистре USART_CR1.
  • Устанавливаем размер передаваемого слова равным 8 бит (M = 0 в USART_CR1).
  • Число стоп-бит равно 1 (STOP = 00 в USART_CR2).
  • Скорость обмена примем равной 9600 бод. Для этого рассчитаем и занесем в регистр USART_BRR необходимый коэффициент деления тактовой частоты (точнее частоты тактирования домена APB2, в котором находится USART1). Формула для расчета следующая:

Baud rate

Здесь Tx/Rx baud – скорость обмена, в данном случае – 9600, fck – тактовая частота модуля USART, и USARTDIV – необходимый нам коэффициент деления.  Для формирования тактовой частоты по умолчанию после сброса используется внутренний источник HSI с частотой 8 МГц, для домена APB2 делитель частоты не задействован (тоже по умолчанию), поэтому и fck будет равна 8 МГц. Имея эти данные, и преобразовав формулу, получаем значение USARTDIV – коэффициента деления:

USARTDIV = 8000000 / (16*9600) = 52.08

Получили дробное число, которое теперь необходимо преобразовать в шестнадцатеричный вид для записи в регистр USART_BRR. Сначала берем только целую часть и преобразовав, получаем число 0x34. А дробную часть необходимо умножить на 16, округлить до целого, и затем тоже перевести в шестнадцатеричный вид.

0.8*16 =  12.8, округляем до 13 и, переведя в hex, получаем 0xD.

Теперь “стыкуем” целую и дробную части, в итоге в регистр USART_BRR будет записано число 0x34D.

  • Установкой бита TE в регистре USART_CR1 включаем передатчик.
  • Все, можно передавать данные, передатчик настроен и готов к работе. Данные загружаем в регистр USART_DR. Перед этим необходимо каждый раз дожидаться установки бита TC (Transmission complete) в регистре USART_SR, как подтверждения того, что передача предыдущего слова данных уже завершена.

Текст программы:

#include "stm32f10x.h" 

void Init(void); //Объявление функции инициализации GPIOA и USART1
void Transmit(char); //Объявление функции передачи одиночного символа

int main()
{
  Init(); //Вызов функции инициализации
  while(1)
  {
    Transmit('S'); //Вызов  функции передачи одиночного символа
    Transmit('T');
    Transmit('M');
    Transmit('3');
    Transmit('2');
    Transmit('F');
    Transmit('1');
    Transmit('0');
    Transmit('3');
    Transmit('r'); //Перевод позиции печати в крайнее левое положение
    Transmit('n'); //Перевод позиции печати на новую строку
    for(uint32_t i=0; i<0x000FFFFF; i++); //Временная задержка
  }
}

void Init()
{
  //RCC
  RCC->APB2ENR |= (RCC_APB2ENR_IOPAEN | RCC_APB2ENR_USART1EN); //Включаем тактирование GPIOA и USART1
  //GPIOA
  GPIOA->CRH |= (GPIO_CRH_CNF9_1 | GPIO_CRH_MODE9); //GPIOA - выход Push_Pull, альтернативная функция, скорость 50 МГц
  //USART1
  USART1->CR1 |= USART_CR1_UE; //Включаем USART1
  USART1->CR1 &= ~USART_CR1_M; //Размерность слова данных - 8 бит
  USART1->CR2 &= ~USART_CR2_STOP; //1 стоп-бит
  USART1->BRR = 0x34D; //Скорость обмена 9600 бод
  USART1->CR1 |= USART_CR1_TE; //Включаем передатчик USART1  
}

void Transmit(char data)
{
  while(!(USART1->SR & USART_SR_TC)); //Проверка завершения передачи предыдущих данных
  USART1->DR = data; //Передача данных
}

Запрограммировав микроконтроллер через ST-LINK платы DISCOVERY и подключив к компьютеру, сначала “погонял” программу в отладчике IAR, задал необходимую временную задержку. Принятые данные отслеживал используя программу Terminal. Загрузить ее можно ОТСЮДА.

Terminal

Проверив настройки COM-порта, жмем кнопку Connect, и наблюдаем прием данных.

К сожалению быстро запустить USART на плате STM32L-DISCOVERY у меня пока не получилось. Попробую одолжить для проверки другую плату, хотя маловероятно что “спалил” свою. Символ “ноль” она отсылает при установке бита TE, скорее всего есть какие-то “подводные камни”, надо глубже копать в документации.

P.S. Проблему подключения платы STM32L-DISCOVERY все таки решил, нашел ошибку в коде. Изначально готовил два варианта – для STM32F и STM32L. Первый вариант программы заработал сразу, второй временно отложил. Теперь о подключении платы STM32L-DISCOVERY можно почитать в следующем посте STM32. USART. Часть 2.

Загрузить проект можно по ссылке:

Проект в IAR для передатчика USART1 STM32F103

Другие статьи:

  17 Responses to “STM32. USART. Часть 1.”

  1. Тоже были проблемы с запуском USART на Discovery. Попробуйте такой вариант инициализации:
    void USART1_Init(void)
    {
    //Включение тактирования
    RCC->APB2ENR |= RCC_APB2ENR_USART1EN; //Тактирование USART1
    RCC->APB2ENR |= RCC_APB2ENR_AFIOEN; //Тактирование альтернативных функций GPIO
    RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; //Тактирование GPIO

    // Настройка портов В/В
    GPIOA->CRH &= ~GPIO_CRH_MODE9;
    GPIOA->CRH &= ~GPIO_CRH_CNF9;
    GPIOA->CRH |= GPIO_CRH_MODE9_1;
    GPIOA->CRH |= GPIO_CRH_CNF9_1;

    // Настройка USART1
    USART1->CR1 |= USART_CR1_UE; //Включение модуля USART1
    USART1->CR1 &= ~USART_CR1_M; //8 бит данных
    USART1->CR2 &= ~USART_CR2_STOP; //Количество стоп-битов: 1
    USART1->BRR = 0x09C4; //Cкорость обмена 9600 бод
    USART1->CR1 |= USART_CR1_TE; //Включение передатчика
    USART1->CR1 |= USART_CR1_RE; //Включение приемника

    }

  2. Забыл добавить, тактируется USART1 на 24 МГц.

    • А откуда значение 24 МГц? У меня все по умолчанию после сброса: источник HSI на 8 МГц, PLL выключен, делители AHB и APB тоже не задейстовованы.
      Вроде в линейке value line (STM32F100 на STM32VL-DISCOVERY) 24 МГц это именно максимальное значение частоты для домена APB2, в котором находится USART1.

      • Безусловно все зависит от настроек. Просто у меня в файле первичных настроек «system_stm32f10x.c» выбрана частота как :
        #define SYSCLK_FREQ_24MHz 24000000

        • Внешний кварц?

        • Я так понял, что эти настройки используются для внешнего кварца с частотой 8 МГц. В моем случае, а у меня в примере используется STM32F103RCT6 — в настройках проекта задано STM32F10X_HD, при использовании внешнего кварца должно быть
          #define SYSCLK_FREQ_72MHz 72000000
          «The System clock configuration functions provided within this file assume that:
          — For Low, Medium and High density devices an external 8MHz crystal is used to drive the System clock.»
          Если же в качестве источника используется внутренний генератор HSI, по умолчанию после сброса, тогда:
          «After each device reset the HSI (8 MHz) is used as system clock source.»
          Во всяком случае, у меня при таких настройках скорости обмена все работает стабильно, соответствуя частоте тактирования SYSCLK = 8 МГц. Для интереса надо будет поэкспериментировать — вывести SYSCLK на вывод MCO и посмотреть осциллографом.

  3. Все таки запустил USART и на плате STM32L-DISCOVERY. Вдумчиво всмотрелся в документацию, проверил код инициализации, и нашел ошибку. Сейчас код «причешу» и опубликую коротенький пост с кодом программы для серии STM32L.

    • Привет. Никак не могу запустить USART на STM32L. Хотел для начала хотя бы переслать один символ с одного USARTа на другой, связал их проводками, настроил и ничего.

      Код настройки USART:
      RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
      USART1->CR1 |= USART_CR1_UE;
      USART1->CR1 &= ~USART_CR1_M;
      USART1->CR2 &= ~USART_CR2_STOP;
      USART1->BRR = 0xD;
      USART1->CR1 |= USART_CR1_TE;
      USART1->CR1 |= USART_CR1_RE;
      NVIC_EnableIRQ(USART1_IRQn);

      RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
      USART2->CR1 |= USART_CR1_UE;
      USART2->CR1 &= ~USART_CR1_M;
      USART2->CR2 &= ~USART_CR2_STOP;
      USART2->BRR = 0xD;
      USART2->CR1 |= USART_CR1_TE;
      USART2->CR1 |= USART_CR1_RE;
      NVIC_EnableIRQ(USART2_IRQn);
      Все делаю на прерываниях

      Код настройки GPIO:
      RCC->AHBENR |= RCC_AHBENR_GPIOAEN;
      GPIOA->MODER |= GPIO_MODER_MODER2_1;
      GPIOA->MODER |= GPIO_MODER_MODER3_1;
      GPIOA->MODER |= GPIO_MODER_MODER9_1;
      GPIOA->MODER |= GPIO_MODER_MODER10_1;
      GPIOA->OTYPER &= ~GPIO_OTYPER_OT_2;
      GPIOA->OTYPER &= ~GPIO_OTYPER_OT_3;
      GPIOA->OTYPER &= ~GPIO_OTYPER_OT_9;
      GPIOA->OTYPER &= ~GPIO_OTYPER_OT_10;
      GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR2);
      GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR3);
      GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR9);
      GPIOA->PUPDR &= ~(GPIO_PUPDR_PUPDR10);
      GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR2;
      GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR3;
      GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR9;
      GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR10;
      GPIOA->AFR[0] |= (GPIO_AFRL_AFRL2 & 0x7);
      GPIOA->AFR[0] |= (GPIO_AFRL_AFRL3 & 0x7);
      GPIOA->AFR[1] |= (GPIO_AFRH_AFRH9 & 0x7);
      GPIOA->AFR[1] |= (GPIO_AFRH_AFRH10 & 0x7);

      RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
      GPIOB->MODER |= GPIO_MODER_MODER6_0;
      GPIOB->MODER |= GPIO_MODER_MODER7_0;
      На порту Б два сетодиода для индикации

      Ну и собственно посылка:
      void TransmitByte_Usart1(void)
      {
      while(!(USART1->SR & USART_SR_TC));
      USART1->DR = ‘A’;
      }

      И прерывание:
      void USART2_IRQHandler(void)
      {
      if(USART2->SR & USART_SR_RXNE)
      {
      GPIOB->ODR ^= GPIO_ODR_ODR_7;
      }
      }

      Все компилится, запускаю дебагер, прогоняю и в ответ тишина. Ничего никуда не отправляется, прерывания не срабатывают, даже при пошаговой отладке после этой строчки USART1->DR = ‘A’; в регистре DR ничего нет.
      Подскажи в чем проблема плиз, четыре дня убил, все везде излазил испробовал и ничего. До этого года три сидел на микрочипах, таких проблем не было, а тут ничего не получается….

  4. «USARTDIV = 8000000 / (16*9600) = 52.08

    0.8*16 = 12.8, округляем до 13 и, переведя в hex, получаем 0xD.»

    Здесь ошибка: фракционная часть 0.08*16=1.28 -> 1, т.о. USART_BRR=0x341

  5. Конечно, не у всех есть разъем RS232 (COM). Но у кого есть проще сделать с RS-232, да цена FT232RL. Подключал винчестеры, модемы. Проблем не было, проводники минимальной длины. Хотя у меня до 20см и работает.
    http://images.62live.ru/v.php?id=c8d239871836ea40d8242ccfabcaf0d4
    Только питание через резистор 51ом (из-за 5в) и после конденсатор 0,1мкф. Схем полно: http://www.spt.ru/victor/74ls14.htm
    http://www.sgh-x100.narod.ru/datacable.htm
    http://monitor.net.ru/forum/samsung-sp0802n-80g-info-371946.html
    С первого раза прошивка не пошла, нужно перед работой с программой Flash Loader Demonstrator давать сброс и настройки программы согласно руководству. Схему можно делать даже на транзисторах, не пробовал. Эта микросхема имеет гистерезис и поэтому более устойчива к помехам. Есть советские аналоги.
    Пробовал работать с переходником USB-RS232 HL-340, не вышло. Похоже, инвертированы сигналы.