Настройка UART на микроконтроллере STM8

Сайт: 

В последнее время на рынке электроники достойное место занимают 8-битные микроконтроллеры семейства STM8 фирмы STMicroelectronics, которые имеют относительно недорогую стоимость по сравнению с 8-битными микроконтроллерами других фирм, таких как Atmel или Microchip. Среди радиолюбителей большой популярностью пользуется очень дешевый микроконтроллер STM8S003F, который при цене 0.5$ (на лето 2014 года) имеет следующие основные характеристики:

  • Частота тактирования ядра до 16 МГц;
  • 1 К RAM памяти;
  • 8 К Flash памяти;
  • Напряжение питания от 2.95 до 5.5 В;
  • Управление питанием;
  • Два 16-битных таймера (с поддержкой ШИМ, захват, сравнения) и один 8-битный таймер, а также сторожевой таймер Watchdogtimer;
  • Модуль UART;
  • Модуль SPI;
  • Модуль I2C;
  • 5 каналов 10-битного АЦП;

Сегодня я хочу рассказать о том, как настроить UARTв асинхронном режиме передачи данных на микроконтроллере STM8S003F. Данный микроконтроллер содержит всего один модуль UART1 (в более навороченных микроконтроллерах есть еще UART2, UART3 и UART4).

Здесь я опишу лишь основные моменты работы с UART1, более подробную информацию можно получить из даташитов: STM8S003F и Reference manual

Итак, UART1 обладает следующими основными свойствами:

  • Полнодуплексный последовательный интерфейс с максимальной скоростью передачи 1Мбит/с;
  • Эмуляция SPI;
  • Настраиваемая скорость передачи данных;
  • Эмуляция Smartcard;
  • IrDA кодер/декодер;
  • Поддержка протокола LIN;
  • Однопроводной полудуплексный режим;

Структурная схема UART1 STM8

Прежде чем приступить к написанию подпрограмм для работы с UART1, нужно сказать пару слов о системе тактирования микроконтроллера STM8S. Для наглядности приведу функциональную схему контроллера тактирования (Clock control CLK):

Система тактирования STM8S

Процессор (CPU) может тактироваться от трех источников: внешнего высокоскоростного кварцевого резонатора, внутреннего высокоскоростного HSI RC-генератора 16МГц и внутреннего низкоскоростного LSIRC-генератора на 128 кГц. HSI RC-генератор имеет также делитель. По умолчанию в качестве источника тактирования выбирается именно этот генератор, с делением частоты на 8. Т.е. по умолчанию получаем частоту 2МГц. При необходимости программно можно переключиться на другой источник тактирования. Далее сигнал тактирования поступает на делитель CPUDIV, который и определяет частоту тактирования CPU. В контроллере STM8 есть такая важная особенность: можно разрешить или запретить тактирование каждого периферийного модуля (UART, I2C и т.д.). Сделано это для того, чтобы максимально снизить энергопотребление. По умолчанию тактирование для всей периферии разрешено.

Более подробно о системе тактирование можно почитать в «Reference manual» или есть хорошая статья на русском языке, только для другого семейства STM8L, но Вы найдете много схожего в ней. Вот ссылка на статью: 8L-Курс, Часть 4 - Тактирование

Ну что, приступим. Будем считать, что CPU тактируется от внутреннего RC-генератора на частоте 2МГц.

Первым делом нужно проинициализировать UART1.

1.     Задаем скорость приема/передатчика (Baud rategenerator). Возьмем для примера скорость 9600 бит/с. Baudrateгенератор настраивается через регистры UART_BRR1 и UART_BRR2.

UART_BRR1:

 

 

 

UART_BRR2:

 

 

 

Значение UART_DIV расчитывается по формуле: UART_DIV= Fmaster/Baud rate

2.     Настраиваем регистры управления UART_CR1 … UART_CR5. Так как мы используем UART1 в режиме асинхронного приемопередатчика, нам понадобится настроить только регистры UART_CR1 … UART_CR3. В остальных регистрах настраивается протокол LIN, IrDAи т.д., которые нас сейчас не интересуют.

UART_CR1:

  • R8 - сюда записывается 9-й принятый бит, если прием осуществляется в 9-битном режиме.
  • T8 - сюда записывается 9-й передаваемый бит, если передача осуществляется в 9-битном режиме.
  • UARTD– отключает UART по завершению передачи текущего байта (для снижения энергопотребления)
  • М – задается длина данных 8 бит (М=0) или 9 бит (М=1)
  • WAKE– определяет метод выхода контроллера из спящего режима
  • PCEN – контроль четности
  • PS– выбор четности
  • PIEN– настройка прерываний при ошибке четности

UART_CR2:

  •  
  •  
  •  
  •  
  • TIEN– настройка прерываний по очистке передающего регистра
  • TСIEN– настройка прерываний по завершению передачи
  • RIEN– настройка прерываний по заполнению приемного регистра
  • ILIEN– настройка прерываний по освобождению линии передачи
  • TEN – включение передатчика
  • REN –включение приемника
  • RWU– настройка спящего режима приемника
  • SBK– отправлять “break” или нет (используется например в RS-485)

UART_CR3:

 

 

Здесь включается режим LIN и настраивается сигнал синхронизации CLK, если используется синхронный режим передачи. В данном регистре нас интересует только поле STOP. Здесь задается количество стоповых битов.

Теперь приведу пример функции инициализации:

 

//******************************************************************************
// Инициализация UART1 STM8S003
//******************************************************************************
void uart_init(unsigned long baud_rate, unsigned long f_master){
  
  //Значение регистра BRR
  unsigned long brr;
  
  //Настраиваем TX на выход, а RX на вход
  PD_DDR_bit.DDR5 = 1;  //TX
  PD_DDR_bit.DDR6 = 0;  //RX
  
  //RX - плавающий вход
  PD_CR1_bit.C16 = 0;
  //Отключает внешние прерывания для RX
  PD_CR2_bit.C26 = 0;
  
  //Настройка скорости передачи
  brr = f_master/baud_rate;
    
  UART1_BRR2 = brr & 0x000F;
  UART1_BRR2 |= brr >> 12;
  UART1_BRR1 = (brr >> 4) & 0x00FF;
  
  //Четность отключена
  UART1_CR1_PIEN = 0;
  //Контроль четности отключен
  UART1_CR1_PCEN = 0;
  //8-битный режим
  UART1_CR1_M = 0;
  //Включить UART
  UART1_CR1_UART0 = 0;
  
  //Запретить прерывание по опустошению передающ. регистра
  UART1_CR2_TIEN = 0;
  //Запретить прерывание по завершению передачи
  UART1_CR2_TCIEN = 0;
  //Запретить прерывание по заполнению приемного регистра
  UART1_CR2_RIEN = 0;
  //Запретить прерывание по освобождению линии
  UART1_CR2_ILIEN = 0;
  //Передатчик включить
  UART1_CR2_TEN = 1;
  //Приемник включить
  UART1_CR2_REN = 1;
  //Не посылать break-символ
  UART1_CR2_SBK = 0;
  
  //Один стоп-бит
  UART1_CR3_STOP = 0;
}

 

Теперь необходимо написать функции приема и отправки данных

Модуль UART1 содержит также регистр состояний USART_SR:

  • TXE– передающий регистр пуст (готов для записи следующих данных)
  • TC – передача завершена
  • RXNE– приемный регистр не пустой (приняты данные)
  • IDLE – линия передачи свободна
  • OR– ошибка перезаписи (возникает, когда в приемном регистре есть данные, а были приняты новые данные до чтения старых)
  • NF – флаг помехи
  • PE – ошибка четности

Отправляя и принимая данные через UARTнеобходимо контролировать биты данного регистра.

Для того, чтобы начать передачу данных, необходимо убедиться, что передающий регистр свободен (TXE=1) и записать данные в регистр UART_DR.

Для того, чтобы принять байт данных, нужно дождаться, пока данные попадут в приемный регистр (установится RXNE=1) и сразу же прочитать их через регистр UART_DR.

Обе операции записи и чтения можно организовать на прерываниях и без них.

Рассмотрим пример фукций приема и передачи без прерываний:

// Отправка байта
void uart_tx_byte(unsigned char data){
    while(!UART1_SR_TXE);
    UART1_DR = data;
}

// Отправка массива данных
void uart_tx_data(unsigned char * data, unsigned char len){
  while(len--){
      uart_tx_byte(*data++);
  }
}
// Прием байта
unsigned char uart_rx_byte(){
    unsigned char data;
      
    while(!UART1_SR_RXNE);
    data = UART1_DR;
    return data;
}

// Прием массива данных
void uart_rx_data(unsigned char * data, unsigned char len){
  while(len--){
      *data++ = uart_rx_byte();
  }
}

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

В этом случае нужно разрешить прерывание по приему, установив бит RIEN=1 в регистре UART_CR2. В функции uart_init добавляем  строчку UART1_CR2_RIEN=1  вместо UART1_CR2_RIEN=0  

Кроме этого необходимо в начале программы глобально разрешить прерывания. В IAR Embedded Workbeanch это делается функцией __enable_interrupt(), а по сути включение прерывание осуществляется ассемблерной командой RIM.

После разрешения прерываний нужно описать функцию-обработчик (которая заменит функцию uart1_rx из прошлого примера).

// Обработчик прерываний по приему UART
#pragma vector=UART1_R_RXNE_vector 
__interrupt void uart_rx_interrupt(void){
  unsigned char data;
  
  data = UART1_DR;
  
  //Отсылаем принятый байт обратно
  uart_tx_byte(data);
}

Данная функция-обработчик отсылает обратно принятый байт.

UART1_R_RXNE_vector– это адрес вектора прерывания. Его значение можно найти в даташите.

Ну а вот так может выглядеть основная подпрограмма:

 

int main( void )
{
  
#ifndef UART_INT_ENABLE        
  unsigned char data;
#endif  
  
  uart_init(UART_BAUD_RATE, F_MASTER);
  
  uart_tx_data("Hello, world!\r\n", 15);

#ifdef UART_INT_ENABLE  
  __enable_interrupt();
#endif  
  
  while(1){
    
#ifndef UART_INT_ENABLE      
      uart_rx_data(&data, 1);
      uart_tx_data(&data, 1);
#endif
      
  }
}

Прикрепляю проект с исходными файлами в IAR STM8.

Комментарии

Функцию инициализации можно упростить, потому что регистры конфигурации по умолчанию настроены так как нам нужно (вариант без прерываний):

// Инициализация UART1 STM8S003

void uart_init(unsigned long baud_rate, unsigned long f_master){
  
  //Значение регистра BRR
  unsigned long brr;
  
  //Настраиваем TX на выход, а RX на вход
  PD_DDR_bit.DDR5 = 1;  //TX
  PD_DDR_bit.DDR6 = 0;  //RX
  
  //RX - плавающий вход
  PD_CR1_bit.C16 = 0;
  //Отключает внешние прерывания для RX
  PD_CR2_bit.C26 = 0;
  
  //Настройка скорости передачи
  brr = f_master/baud_rate;
    
  UART1_BRR2 = brr & 0x000F;
  UART1_BRR2 |= brr >> 12;
  UART1_BRR1 = (brr >> 4) & 0x00FF;
  
  //Передатчик включить
  UART1_CR2_TEN = 1;
  //Приемник включить
  UART1_CR2_REN = 1;
}

Добрый день.
Прошил этот пример. На терминальную программу приходят не те данные.
Подключал через переходник Uport1150 и без него на порт на маме.
У меня подозрения на уровни напряжения сигнала для RS232.
Можете выслать мне или добавить в пост электрическую схему?