Настройка 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, нужно сказать пару слов о системе тактирования микроконтроллера 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 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.
Можете выслать мне или добавить в пост электрическую схему?