В последнее время на рынке электроники достойное место занимают 8-битные микроконтроллеры семейства STM8 фирмы STMicroelectronics, которые имеют относительно недорогую стоимость по сравнению с 8-битными микроконтроллерами других фирм, таких как Atmel или Microchip. Среди радиолюбителей большой популярностью пользуется очень дешевый микроконтроллер STM8S003F, который при цене 0.5$ (на лето 2014 года) имеет следующие основные характеристики:
- Частота тактирования ядра до 16 МГц;
- 1 К RAM памяти;
- 8 К Flash памяти;
- Напряжение питания от 2.95 до 5.5 В;
- Управление питанием;
- Два 16-битных таймера (с поддержкой ШИМ, захват, сравнения) и один 8-битный таймер, а также сторожевой таймер Watchdog timer;
- Модуль UART;
- Модуль SPI;
- Модуль I2C;
- 5 каналов 10-битного АЦП;
Сегодня я хочу рассказать о том, как настроить UART в асинхронном режиме передачи данных на микроконтроллере STM8S003F. Данный микроконтроллер содержит всего один модуль UART1 (в более навороченных микроконтроллерах есть еще UART2, UART3 и UART4).
Здесь я опишу лишь основные моменты работы с UART1, более подробную информацию можно получить из даташитов: STM8S003F и Reference manual
Итак, UART1 обладает следующими основными свойствами:
- Полнодуплексный последовательный интерфейс с максимальной скоростью передачи 1Мбит/с;
- Эмуляция SPI;
- Настраиваемая скорость передачи данных;
- Эмуляция Smartcard;
- IrDA кодер/декодер;
- Поддержка протокола LIN;
- Однопроводной полудуплексный режим;

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

Процессор (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 rate generator). Возьмем для примера скорость 9600 бит/с. Baudrate генератор настраивается через регистры UART_BRR1 и UART_BRR2.
UART_BRR1:


Значение 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!", 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.
