STM8L. Настройка UART для новичков

Не увидел я на сайте статей для новичков по настройке и работе с UART на STM8L, поэтому решил восполнить этот недостаток. Сейчас я расскажу как просто и быстро его настроить.
В качестве отладочной платы с этим микропроцессором я буду использовать STM8L-Discovery, в которой есть свой отладчик ST-Link. Среду для программирования будем использовать IAR.

Запуск UART

И так, наш процессор STM8L152C6 в дефолтной конфигурации тактируется от HSI (High Speed Internal) — высокочастотного внутреннего RC генератора с частотой 16МГц. У меня нет внешнего кварца, поэтому будем использовать его.
При включении мк тактовый сигнал идет на ядро и на всю периферию (Таймеры, USART и т.д). Однако в STM8L тактирование всех периферийных устройств по умолчанию отключено для снижения энергопотребления. По этому, чтобы работать с UART и его регистрами нам нужно сначала подать на него тактирование.

Для управления делителем системного тактового сигнала используется регистр CLK_CKDIVR:

Мы будем использовать максимальную частоту ядра (16МГц в нашем случае) и устанавливаем 3 последних младших бита в нули:
CLK_CKDIVR=0x00; //делитель частоты 1

Далее нам нужно подать эту частоту на модуль USART чтобы уже начать с ним работать. Управление подачей тактирования на периферию осуществляется регистром CLK_PCKENRx. За включение каждого устройства отвечает отдельный бит:

Как видно из картинки выше, чтобы включить USART1 нужно установить в единицу 5й бит регистра CLK_PCKENR1:
CLK_PCKENR1_bit.PCKEN15 = 1;

Перейдем теперь к настройке USART1. Исходя из документации к плате stm8l-discovery узнаем, что ножка PC3USART1_TX, а ножка PC2USART1_RX. Это значит что ножку PC2 нужно установить на вход, а PC3 — на выход:

PC_DDR &= ~(1 << 2);  //PC2 RX USART1 receive (вход)
PC_DDR|=1<<3; //PC3 TX USART1 transmit (выход)

Настройка UART

Произведем настройки регистра USART_CR1:

Длинна слова в кадре задается битом М (4м битом) регистра USART_CR1
Если он не установлен, то длинна данных будет 8 бит. Если установлен — 9 бит.
Установим стандартным режим приема/передачи — 8 бит данных. Бит PCEN включает/выключает режим контроля четности.
Количество стоп-битов настраивается в битах 4 и 5 регистра USART_CR3:

00: 1 стоп бит
01: Зарезервировано
10: 2 стоп бита
11: 1.5 стоп бита (используется в режиме "Smartcard”)


Скорость обмена (BaudRate) определяется значением регистра делителя частоты тактирования UART, которое не может быть меньше 16 и рассчитывается по формуле:
Tx/Rx_baud_rate = fSYSCLK / USART_DIV

Для получения значения этого делителя мы должны тактовую частоту fSYSCLK поделить на желаемую скорость обмена 9600 bps (Tx/Rx_baud_rate). Мы получим нужное нам значение коэффициента деления USART_DIV:
16000000ГЦ/9600bps=1666,6 (0х0683 в 16-ричной с округлением)

Два байта значения USART_DIV записываются сначала в регистр USART_BRR2 затем в USART_BRR1.
USART1_BRR2 = 0x03;
USART1_BRR1 = 0x68;

Установка выводов UART в режим TX и RX вместо портов общего назначения осуществляется при помощи регистра USART2_CR2:

USART1_CR2_bit.REN=1; //прием
USART1_CR2_bit.TEN=1; //передача

Режим передачи данных

В этом режиме регистр USART_DR состоит из буфера TDR и сдвиговым регистром передатчика, передающий кадр непосредственно в линию TX. Как только у нас в регистре USART_DR появляются данные, они сразу же перемещаются в буферный регистр TDR, а из него уже в сдвиговый регистр. Флаг TXE устанавливается в регистре USART_SR когда содержание буфера TDR было передано в регистр сдвига и передача началась. Следовательно, буферный регистр TDR пуст и следующие данные могут быть записаны в регистр USART_DR без перезаписи буферного регистра TDR. Это значит, что при попытке записи в регистр USART_DR когда флаг TXE сброшен, буферный регистр не будет пустым (т.к. не закончилась отправка предыдущего байта в сдвиговом регистре), и ожидающий отправки байт в буфере TDR будет перезаписан. Этот флаг генерирует прерывание, если установлен бит TIEN регистра USART1_CR2.
0: Данные не переданы регистр сдвига
1: Данные переданы в сдвиговый регистр

Если передача кадра в сдвиговом регистре полностью завершена и флаг TXE установлен (буфер TDR чист), устанавливается флаг TC регистра USART_SR. Это означает полное завершение передачи каких-либо данных по UART. По этому флагу так же есть прерывание, если установлен бит TCIEN регистра USART1_CR2.
0: Передача не завершена
1: Передача завершена

Для того чтобы передать данные, мы должны ждать, пока буфер TDR освободится, и флаг TXE регистра USART_SR не будет установлен.
while(!(USART1_SR_bit.TXE)); //Ожидаем освобождения буферного регистра TDR
USART1_DR='F'; // Отправляем символ "F".


Режим приема данных

Теперь разберемся с приемом. В этом режиме регистр USART_DR состоит из буфера RDR и сдвиговым регистром приемника, читающего данные с ноги RX. Когда данные из сдвигового регистра перемещаются в буферный регистр RDR, устанавливается флаг RXNE регистра USART_SR. Эти данные становятся доступными в регистре USART_DR. Флаг RXNE очищается при попытке чтения регистра USART_DR. Так же генерируется прерывание, если бит RIEN установлен в регистре USART_CR2. Разрешаем это прерывание:
USART1_CR2_RIEN=1; //Прерывание по приему
#pragma vector=USART_R_OR_vector
     __interrupt void USART1_RXE(void)
     {
          USART1_DR=USART1_DR; //Отправляем тоже, что и приняли (эхо)
     }

Пример

Наконец, собираем все вместе. Вот полный код инициализации UART, отправка символа «F» при старте и режим приема «эхо»

#include <iostm8l152c6.h>
#include <intrinsics.h>  //Здесь описана функция __enable_interrupt ().

int main()
{
  CLK_CKDIVR=0x00; //делитель частоты 1
  CLK_PCKENR1_bit.PCKEN15 = 1; //Включаем тактирование модуля USART1
  
  PC_DDR &= ~(1 << 2); //PC2 RX USART1 receive (вход)
  PC_DDR|=1<<3; //PC3 TX USART1 transmit (выход)
  
  USART1_BRR2 = 0x03; //скорость 9600bps
  USART1_BRR1 = 0x68;
  
  USART1_CR2_bit.REN=1; //прием
  USART1_CR2_bit.TEN=1; //передача
  
  USART1_CR2_RIEN=1; //Прерывание по приему
  __enable_interrupt (); // Разрешаем прерывания. 
  
  while(!(USART1_SR_bit.TXE)); //Ожидаем освобождения буферного регистра TDR
  USART1_DR='F'; // Отправляем символ "F".

  while (1){} //беск. цикл

}

#pragma vector=USART_R_OR_vector
__interrupt void USART1_RXE(void)
{
          USART1_DR=USART1_DR; //Отправляем тоже, что и приняли (эхо)
}

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

RSS свернуть / развернуть
Спасибо за информацию!
0
Спасибо за инфу, но есть кое какая неточность:
PC_DDR|=0<<2; //PC2 RX USART1 receive (вход)

Запись не имеет смысла, т.к. по факту выходит PC_DDR |= 0; Что не даст никаких изменений в PC_DDR.
А если до этого был установлен бит, то он не спросится. Так что лучше поступить так:
PC_DDR &= ~(1 << 2);
0
Спасибо, учту
0
так исправьте уже — больше года висит эта позорная конструкция, причем в двух местах.
0
реально, исправьте!
а то новички могут сделать, а не будет работать, будут искать кошку, где её нет!
0
Он уж два года как не заходит сюда.
0
Спокойно, я тут. Мне на мыло ахтунг пришел, после ваших сегодняшних комментариев. Щас все исправлю)
0
Поправил.
Изначально было
PC_DDR|=0<<2; //PC2 RX USART1 receive (вход)
  PC_DDR|=1<<3; //PC3 TX USART1 transmit (выход

На всякий… Надеюсь, я все так поправил. Если что не так — кричите
0
Если я правильно понимаю логику этого регистра, то вторую строчку убирать не следовало. Только первую заменить.
0
точно.
первая теперь правильно, сбрасывает только 1 бит, относящийся к РС2,
а вторая строка устанавливает 1 бит, относящийся к РС3.
0
Упс. Простите.
Исправил
0
Спасибо за статью
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.