Электроника и программирование

UART. Передача данных

Обновлено 12.04.2025

Исправим код в файле main.c из предыдущей статьи так, чтобы при переключении светодида по UART ещё отправлялось слово Hello.

Для этого в среде Mik32 IDE на панели Project Explorer скопируем наш проект blinky_timer_irq из предыдущей статьи, и сохраним его под названием uart_tx_timer_irq:

Копирование проекта

Затем откроем файл main.c, и изменим код следующим образом:

1) Включаем файл uart.h, в котором содержатся определения констант для UART:

Добавление заголовочного файла uart.h

2) Добавляем процедуры по инициализации UART и отправке данных:

Добавление процедур по инициализации UART и отправке данных

3) Включаем тактирование UART_1 и GPIO_1:

Включение тактирования UART_1 и GPIO_1

4) Настраиваем вывод P1.9 как UART_TX:

Настройка вывода UART_TX

5) Добавляем инициализацию UART_1 в main():

Вызов инициализации UART_1 из main():

6) Добавляем отправку слова Hello в обработчик прерывания по таймеру. Тогда оно будет отправляться каждую секунду:

Отправка данных в обработчике прерывания

Таким образом, у нас получился такой файл main.c:

#include <mik32_memory_map.h>
#include <pad_config.h>
#include <gpio.h>
#include <power_manager.h>
#include <wakeup.h>
#include <timer32.h> //определения констант для таймера
#include <epic.h> //определения констант для контроллера прерываний
#include <scr1_csr_encoding.h> //определения констант регистров ядра
#include <csr.h> //определения для операций взаимодействия с регистрами ядра
#include <uart.h> //определения констант для UART

/*
* Данный пример демонстрирует работу с GPIO и PAD_CONFIG.
* В примере настраивается вывод, который подключенный к светодиоду, в режим GPIO.
* Светодиод переключается в прерывании от таймера.
*
*/

#define PIN_LED 9	// Светодиод управляется выводом PORT_0_9
#define PIN_BUTTON 10	// Кнопка управляет сигналом на выводе PORT_0_10

void initUART(UART_TypeDef *uart, uint32_t uart_divider) {
    uart->DIVIDER = uart_divider; // divider = 32 000 000 / baud
	uart->CONTROL1 = UART_CONTROL1_TE_M | UART_CONTROL1_RE_M| UART_CONTROL1_UE_M; //TX, RX, Enable

    while (!(uart->FLAGS & UART_FLAGS_TEACK_M)
		&& !(uart->FLAGS & UART_FLAGS_REACK_M));
}

void writeByte(UART_TypeDef *uart, char byte) {
	uart->TXDATA = byte;
	while (!(uart->FLAGS & UART_FLAGS_TC_M));
}

void writeLine(UART_TypeDef *uart, char *line) {
	for (int i = 0; line[i] != '\0'; i++) {
		writeByte(uart, line[i]);
	}
}

void trap_handler() {
	if (EPIC->STATUS & (1 << EPIC_TIMER32_0_INDEX)) //если прерывание от таймера
	{
		GPIO_0->OUTPUT ^= 1 << PIN_LED; // Установка сигнала вывода 9 порта 0 в противоположный уровень
		writeLine(UART_1, "Hello\n");
		TIMER32_0->INT_CLEAR = TIMER32_INT_OVERFLOW_M; //Сброс флага прерывания таймера
		EPIC->CLEAR = 1 << EPIC_TIMER32_0_INDEX; //Сброс флага прерывания линии таймера
	}
}

void EnableInterrupts() {
	// Разрешение прерываний (установка соответствующих регистров состояния в ядре)
	set_csr(mstatus, MSTATUS_MIE);
	set_csr(mie, MIE_MEIE);
}

void InitClock()
{
	PM->CLK_APB_P_SET |=  PM_CLOCK_APB_P_GPIO_0_M // Включение тактирования GPIO_0
						| PM_CLOCK_APB_P_GPIO_1_M // Включение тактирования GPIO_1
						| PM_CLOCK_APB_P_UART_1_M;// Включение тактирования UART_1
	PM->CLK_APB_M_SET |=  PM_CLOCK_APB_M_PAD_CONFIG_M // Включение тактирования контроллера выводов
						| PM_CLOCK_APB_M_PM_M // Включение тактирования блока управления питанием
						| PM_CLOCK_APB_M_TIMER32_0_M // Включение тактирования таймера32_0
						| PM_CLOCK_APB_M_EPIC_M; // Включение тактирования контроллера прерываний
}

int main()
{
	InitClock(); // Включение тактирования GPIO

	PAD_CONFIG->PORT_0_CFG &= ~(0b11 << (2 * PIN_LED));	// Установка вывода 9 порта 0 в режим GPIO
	PAD_CONFIG->PORT_0_CFG &= ~(0b11 << (2 * PIN_BUTTON)); // Установка вывода 10 порта 0 в режим GPIO

	PAD_CONFIG->PORT_1_CFG &= ~(0b11 << (2 * 9));
	PAD_CONFIG->PORT_1_CFG |= 0b01 << (2 * 9);	// Установка вывода 9 порта 1 в режим UART1_TX

	GPIO_0->DIRECTION_OUT = 1 << PIN_LED;	// Установка направления вывода 9 порта 0 на выход
	GPIO_0->DIRECTION_IN = 1 << PIN_BUTTON; // Установка направления вывода 10 порта 0 на вход

	//Настраиваем максимальное значение счётчика 32*10^6,
	//что при такрировании 32 МГц будет соответствовать 1 секунде
	TIMER32_0->TOP = 32000000u;
	//Включаем прерывание при переполнении счётчика
	TIMER32_0->INT_MASK = TIMER32_INT_OVERFLOW_M;

	EPIC->MASK_EDGE_CLEAR = 0xFFFF; //сброс маски прерываний по фронту
	EPIC->CLEAR = 0xFFFF; //сброс флагов прерываний
	EPIC->MASK_EDGE_SET = 1 << EPIC_TIMER32_0_INDEX;//установка маски прерываний по фронту от таймера32_0

	EnableInterrupts();

	TIMER32_0->ENABLE = TIMER32_ENABLE_TIM_EN_M; //включаем таймер

	initUART(UART_1, 278); // 115200 baud

	while (1)
	{
	}
}

Теперь собираем проект, и загружаем прошивку в отладочную плату. Светодиод, как и раньше, должен начать мигать раз в секунду. Но теперь, чтобы увидеть, как данные передаются по UART, нужно установить на компьютере драйвер для установленного на отладочной плате преобразователя UART<->USB CH340 с сайта производителя микросхемы. Теперь в диспетчере устройств при подключении отладочной платы будет появляться COM-порт. У меня это COM10, у вас может быть другой номер.

В Mik32 IDE откроем терминал:

Открытие терминала

В открывшемся окне terminal подключаем COM-порт:

Подключение COM-порта

Вводим данные COM-порта и нажимаем ОК:

Ввод данных COM-порта

И видим принимаемые с отладочной платы данные:

Данные с отладочной платы

Наверх Оглавление