Работа с UART микроконтроллера К1986ВЕ92QI (MDR32F9Q2I)

На примере микроконтроллера К1986ВЕ92QI (MDR32F9Q2I) и отладочной платы LDM-K1986BE92QI рассмотрим работу с UART, в соответствии с пунктом 27 спецификации на МК.

Будем выводить циклически строку в порт USB A (USB UART) функцией printf.

Также посланная из терминала строка выводится в него же. При посылке строки "led" выводится что команда найдена (имитация ввода команды).


/* Программа выводит в USB-UART циклически строку а также строку посланную в терминал на скорости 115 200 строку, 
если строка "led" то выводит "led find" (имитация приема и распознавания команды
для платы через терминал */

#include "MDR32F9Q2I.h" // Макроопределения для K1986BE92QI (MDR32F9Q2I)
#include <stdio.h>  // printf()
#include <stdarg.h> // va_list, va_arg..
#include <string.h> // strstr()

// Макроопределения чтобы можно было поменять используемый порт в одном месте
#define UART_RX 0	// Номер вывода RX UART
#define UART_TX 1	// Номер вывода TX UART
#define PORT_UART MDR_UART2	// Используемый UART
#define PORT_UART_GPIO MDR_PORTD	// Используемый порт для выводов UART
#define UART_IRQHandler UART2_IRQHandler	// Используемый обработчик прерываний
#define UART_IRQn UART2_IRQn	// Используемое прерывание

#define PIN_LED 3	// Номер вывода светодиода
#define PORT_LED MDR_PORTB	// Порт светодиода

void UART_IRQHandler(void); // Прототип обработчика прерывания (чтобы убрать предупреждение компилятора)

static void init_GPIO(void){ // Инициализация порта светодиода
  MDR_RST_CLK->PER_CLOCK |= (1 << 22); // Разрешение тактирования порта B (п.14.7.8)

  PORT_LED->OE |= (1 << PIN_LED);      // Вход или выход. 0 - вход, 1 - выход (п.16.1.2)
  PORT_LED->ANALOG |= (1 << PIN_LED);  // Аналог или цифра. 0 - аналоговый, 1 - цифровой (п.16.1.4) (п.4 т.2)
  PORT_LED->PWR |= (1 << PIN_LED * 2); // Передатчик. 0 - отключен, 1 - медленный, 2 - средний, 3 - быстрый (п.16.1.7)
}

static void init_CPU(void){ // Включаем тактирование от кварца 8 МГц. Умножители и делители не используем. То есть тактовая частота CPU 8 МГц.
  MDR_RST_CLK->HS_CONTROL = 0x01; // Вкл. HSE внешний кварц (8 МГц) (п.14.7.3)
  MDR_RST_CLK->CPU_CLOCK |= (2 << 0); // Источник для CPU_C1 = HSE (п.14.7.4)
  MDR_RST_CLK->CPU_CLOCK |= (1 << 8); // Источник для HCLK = CPU_C3
}

static void init_UART(void){ // Инициализация UART на скорость 115 200 (при 8 000 000 тактовой частоты)
  MDR_RST_CLK->PER_CLOCK |= (1 << 7);  // Разрешение тактирования UART2 (п.14.7.8) PD0 - RX (вход МК), PD1 - TX (выход порта)
  MDR_RST_CLK->PER_CLOCK |= (1 << 24); // Разрешение тактирования порта D (п.14.7.8)

  PORT_UART_GPIO->FUNC |= (2 << UART_RX * 2); // Функция RX. 0 - порт (GPIO), 1 - основная, 2 - альтернативная, 3 - переопределенная (п.16.1.2) (п.4 т.2)
  PORT_UART_GPIO->ANALOG |= (1 << UART_RX);   // Аналог или цифра. 0 - аналоговый, 1 - цифровой (п.16.1.4) (п.4 т.2)
  PORT_UART_GPIO->PWR |= (3 << UART_TX * 2);  // Передатчик. 0 - отключен, 1 - медленный, 2 - средний, 3 - быстрый (п.16.1.7)
	
  PORT_UART_GPIO->FUNC |= (2 << UART_TX * 2); // Функция TX. 0 - порт (GPIO), 1 - основная, 2 - альтернативная, 3 - переопределенная (п.16.1.2) (п.4 т.2)
  PORT_UART_GPIO->ANALOG |= (1 << UART_TX);   // Аналог или цифра. 0 - аналоговый, 1 - цифровой (п.16.1.4) (п.4 т.2)
  PORT_UART_GPIO->PWR |= (3 << UART_TX * 2);  // Передатчик. 0 - отключен, 1 - медленный, 2 - средний, 3 - быстрый (п.16.1.7)
	
  MDR_RST_CLK->UART_CLOCK = (
   (0 << 0u)    // Установка делителя для UART1 = undefined (п.14.7.11)
  |(0 << 8u) 		// Установка делителя для UART2 = 1
  |(0 << 24u) 	// Разрешение тактовой частоты UART1
  |(1 << 25u)); // Разрешение тактовой частоты UART2
	
	// Параметры делителя при частоте = 8 000 000 Гц и скорости = 115200
  PORT_UART->IBRD = 0x4;  // Целая часть делителя скорости 8 000 000 / (16 * 115 200) = 4,34 (п.27.5.4)
  PORT_UART->FBRD = 0x16; // Дробная часть делителя скорости (0,34 * 64 + 0,5) = 22 = 0x16 (п.27.5.4)
  PORT_UART->LCR_H = (
   (0 << 1)   // Разрешение проверки четности
  |(0 << 2)   // Четность/нечетность (нет контроля)
  |(0 << 3)   // Стоп-бит = 1 бит
  |(3 << 5)   // Длина слова = 8 бит
  |(0 << 7)); // Передача бита четности
	
//	PORT_UART->LCR_H |= (1 << 4); // Разрешен буфер FIFO приемника и передатчика
//	PORT_UART->IFLS &= ~(7 << 3); // Сбрасываем биты
//	PORT_UART->IFLS |= ~(4 << 3); // Прерывание по заполнености буфера на 7/8
  
	PORT_UART->CR = ((1 << 8) | (1 << 9) | 1); // Передачик и приемник разрешен, разрешение приемопередатчика UART2
	
	NVIC_EnableIRQ(UART2_IRQn); // Разрашаем прерывания от UART
	PORT_UART->IMSC |= (1 << 4); // Устанавливаем прерывание от приемника UARTRXINTR
}

static char printf_buf[260];  // Строка (буфер) для вывода
int printf(const char *format, ...){ // Реализация printf  
  char ch; // Считываемый символ из буфера для отправки в UART
  int count = 0; // Счетчик (индекс символа в строке)
  va_list arg_ptr; // Аргументы printf()
  va_start(arg_ptr, format); // Заполняем аргументы
  vsnprintf(printf_buf, sizeof(printf_buf) - 1, format, arg_ptr); // Помещаем итоговую строку в printf_buf
  va_end(arg_ptr); // Завершаем обработку переменного числа параметров
  ch = printf_buf[count]; // Берем первый символ для вывода в UART	
	while(ch){ // Перебираем символы в строке
		if(ch == '\n'){ // Если перенос строки
			PORT_UART->DR = '\r'; // Делаем возврат каретки (п.27.9.2)
			while(PORT_UART->FR & (1 << 5)){} // Ждем готовности UART (освобождения буфера)	(п.27.9.4)
		}
		PORT_UART->DR = ch; // Пишем байт в регистр данных (п.27.9.2)
		while(PORT_UART->FR & (1 << 5)){} // Ждем готовности UART (освобождения буфера)	(п.27.9.4)
		ch = printf_buf[++count];  // Берем следующий символ
	}
	return 0; // Нужно вернуть что-то
}

static char buf_UART[80]; // Буфер (строка) для вычитавания данных из UART
static int count_UART = 0; // Счетчик (индекс) символа в строке
void UART_IRQHandler(void){ // Обработчик прерываний UART
	char * pstr = NULL; // Указатель на строку поиска команды
	while((PORT_UART->FR & (1 << 4)) == 0){ // Ждем готовности UART (заполнения буфера)	(п.27.9.4)	
		char ch = (PORT_UART->DR & 0xFF); // Вычитываем символ данных из регистра UART, ограничиваем восьмью битами
		buf_UART[count_UART] = ch; // Записываем в строку принятый символ
		count_UART++; // Иникрементируем счетчик буфера
		printf("%c", ch); // Реализуем отображение вводимых символов
		if(ch == '\r' || ch == '\n'){ // Конец строки
			buf_UART[count_UART] = 0; // Записываем в буфер символ окончания строки
			count_UART = 0; // Сбрасываем счетчик (индекс) символа в строке
			printf("read %s\n", buf_UART); // Выводим строку в терминал
			pstr = strstr(buf_UART, "led\r"); // Ищем строку (имитация команды через терминал) с завершающим символом
			if(pstr == buf_UART){ // Если найдено совпадение с начала строки (указатели совпадают) 
				printf("command led find \n"); // Выводим что команда найдена
			}
		}
	}	
}

int main (void){ 	// Главная функция
	uint16_t count = 0; // Счетчик для мигания светодиодом
	init_CPU(); 		// Инициализация тактирования
	init_UART(); 		// Инициализация UART
	init_GPIO(); 	  	// Инициализация порта со светодиодом. 
	
	while(1){ // Основной цикл
		for(int i = 0; i < 500000; i++){} // Задержка	
		printf("Hi my dear friend!!! pi=%.2f, addr=%p\n", 3.14, (void *)&count_UART);		// Выводим в UART строку
		PORT_LED->RXTX = (MDR_PORTB->RXTX & 0xFFFFFFF0) | ((count & 0x01u) << PIN_LED); // Инвертировать значение бита светодиода. Не блокируем SWD порта JTAG A
		count++; // Инкрементируем счетчик
	}
}
2024-08-13



Понравилась страница?
Добавить в закладки
Или поделиться!

Связанные темы

Управление тактовой частотой микроконтроллера Миландр К1986ВЕ92QI (MDR32F9Q2I)
Работа с портами ввода-вывода (GPIO) микроконтроллера Миландр на CMSIS
Прерывания в микроконтроллерах Миландр. Системный таймер.
Настройка среды разработки для микроконтроллеров Миландр (К1986ВЕ92QI)
Прошивка микроконтроллера К1986ВЕ92QI на отладочной плате LDM-K1986BE92QI через UART и SWD с использованием ST-LINK
Работа с UART микроконтроллера К1986ВЕ92QI (MDR32F9Q2I)