При разработке Linux вы часто сталкиваетесь со связью по последовательному порту для завершения взаимодействия между двумя устройствами.
Последовательная связьзависит от чего-то, называемогопротокол последовательной связиправила,Он контролирует поток данных во время передачи данных.,включатьданныенастройки бита、скорость передачи данныхкорректирование、Определение контрольных битов и выбор стоповых битов и т. д.
Однако при последовательной связи мы обычно не знаем, сколько данных отправит другая сторона и когда данные будут отправлены. Короче говоря, вопрос заключается в следующем: как мы можем гарантировать получение полного набора данных? Чтобы оценить полный кадр, вам нужно знать, когда кадр начинается и когда он заканчивается.
Данные, передаваемые через последовательный порт, имеют разную длину. Если прием неполный, это напрямую повлияет на последующую бизнес-обработку. Чтобы справиться с проблемой получения данных переменной длины, у нас есть несколько распространенных методов обработки:
Давайте сначала разберемся, получить Включение, получение срабатывает, когда получатель получает данные прерывание。
Пример: регистр прерываний показан на рисунке ниже. Когда получатель получает данные, бит 5 (RXNE) регистра состояния последовательного порта на рисунке будет установлен в 1. Когда мы получим значение регистра USART_DR, эта позиция снова очистится, то есть установится в 0.
Таблица регистров состояния прерываний UART W 801 описана в следующей таблице.
Определение статуса прерывания uart в коде SDK следующее:
/*
* uart interrupt source register bits definition
*/
#define UIS_TX_FIFO_EMPTY (1UL<<0)
#define UIS_TX_FIFO (1UL<<1)
#define UIS_RX_FIFO (1UL<<2)
#define UIS_RX_FIFO_TIMEOUT (1UL<<3)
#define UIS_CTS_CHNG (1UL<<4)
#define UIS_BREAK (1UL<<5)
#define UIS_FRM_ERR (1UL<<6)
#define UIS_PARITY_ERR (1UL<<7)
#define UIS_OVERRUN (1UL<<8)
UIS_TX_FIFO_EMPTY
:отправлятьFIFOНулевое прерывание。когдаотправлятьFIFO(First In, First Out, очередь «первым пришел — первым обслужен») пуст, этот бит установлен, а значение бита равно (1UL<<0)
,Прямо сейчасдвоичныйиз 00000001
。UIS_TX_FIFO
:отправлятьFIFOпрерывать。когдаотправлятьFIFOсерединаизданные Срабатывает, когда сумма достигает определенного порога,Значение места(1UL<<1)
,Прямо сейчасдвоичныйиз00000010
。UIS_RX_FIFO
:перениматьFIFOпрерывать。когдаперениматьFIFOсерединаизданные Срабатывает, когда сумма достигает определенного порога,Значение места(1UL<<2)
,Прямо сейчасдвоичныйиз00000100
。UIS_RX_FIFO_TIMEOUT
:перениматьFIFOпрерывание по тайм-ауту。когдаперениматьFIFOЕстьданные,Но нового в течение определенного периода времени нетизданные Срабатывает по прибытии。Значение места(1UL<<3)
,Прямо сейчасдвоичныйиз00001000
。UIS_CTS_CHNG
:CTS(Clear To Send,Прозрачныйотправлять)Изменения сигналапрерывать。когдаCTSСигнализ Срабатывает при изменении статуса,Значение места(1UL<<4)
,Прямо сейчасдвоичныйиз00010000
。UIS_BREAK
:"break"обнаружение состоянияпрерывать。когдаобнаружен"break"Срабатывает, когда условие,как упоминалось ранее,"break"это долгое времяиз低电平Сигнал。Значение места(1UL<<5)
,Прямо сейчасдвоичныйиз00100000
。UIS_FRM_ERR
:ошибка кадрапрерывать。когдаобнаруженошибка кадра时触发,Ошибка кадра возникает вданныерамкаиз Если стартовый или стоповый бит неправильный。Значение места(1UL<<6)
,Прямо сейчасдвоичныйиз01000000
。UIS_PARITY_ERR
:Ошибка четностипрерывать。когда Используйте четность иперениматьприезжатьизданныес ожиданиямииз Срабатывает, когда биты четности не совпадают。Значение места(1UL<<7)
,Прямо сейчасдвоичныйиз10000000
。UIS_OVERRUN
:ошибка переполненияпрерывать。когдаперениматьFIFOПолный,но все равно срабатывает при поступлении новых данных,в результате чего возникает самый старыйизданныепокрытый。Значение места(1UL<<8)
,Прямо сейчасдвоичныйиз100000000
。суждениеполучить прерывание Нужно использовать UIS_RX_FIFO
,Прямо сейчасuart №1 в таблице состояний прерываний 2 Кусочек.
Определите переменные и семафоры приема данных, а также определите функцию обратного вызова приема.
// Сохранить полученные данные
char rx_fifo_buf[1024] = {0};
// Сохранение длины полученных данных
uint16_t rx_length = 0;
// Определить семафор
tls_os_sem_t *sem_rx = NULL;
// Получить обратный звонокфункция
s16 uart1_rx_callback(u16 len, void *user_data)
{
// Накопить полученные символы
rx_length += len;
tls_os_sem_release(sem_rx);
return WM_SUCCESS;
}
Определите скорость передачи данных, биты данных, стоповые биты и т. д. последовательного порта и вызовите tls_uart_port_init
Выполните инициализацию. Наконец, это последовательный порт UART. 1. Привяжите функцию обратного вызова приема.
// Инициализация последовательного порта
void uart1_init()
{
// Инициализируйте вывод uart1, часы последовательного порта будут автоматически включены внутри.
wm_uart1_rx_config(WM_IO_PB_07);
wm_uart1_tx_config(WM_IO_PB_06);
// Настройка параметров UART
tls_uart_options_t opt;
opt.baudrate = UART_BAUDRATE_B115200; // скорость передачи данных
opt.paritytype = TLS_UART_PMODE_DISABLED; // Нет паритета
opt.stopbits = TLS_UART_ONE_STOPBITS; // стоп-бит
opt.charlength = TLS_UART_CHSIZE_8BIT; // данныедлина opt.flow_ctrl = TLS_UART_FLOW_CTRL_NONE; // нет контроля потока
// Инициализируйте последовательный порт. Эта функция активирует внутреннее прерывание последовательного порта.
// Если второй параметр равен NULL, последовательный порт инициализирует последовательный порт в соответствии с параметрами по умолчанию. Подробности см. в реализации SDKфункции tls_uart_port_init.
if (WM_SUCCESS != tls_uart_port_init(TLS_UART_1, &opt, 0))
{
printf("uart1 init error\n");
}
// Другой способ инициализации последовательного порта: используйте для инициализации метод по умолчанию и настройте параметры. Инициализация по умолчанию может не соответствовать требованиям, затем используйте SDK для изменения параметров uart.
// if (WM_SUCCESS != tls_uart_port_init(TLS_UART_1, NULL, 0))
// {
// printf("uart1 init error\n");
// }
// tls_uart_set_baud_rate(TLS_UART_1, UART_BAUDRATE_B9600)//скорость передачи данных
// tls_uart_set_parity(TLS_UART_1, TLS_UART_PMODE_DISABLED);//Нет паритета
// tls_uart_set_stop_bits(TLS_UART_1, TLS_UART_ONE_STOPBITS);//стоп-бит
// Привязка последовательного порта обратный звонокфункция
tls_uart_rx_callback_register((u16)TLS_UART_1, (s16(*)(u16, void *))uart1_rx_callback, NULL);
}
получить прерывание судьи, в последовательном порту 1 получить В прерывании мы можем использовать port->regs->UR_INTS
получать UART Регистр состояния прерывания, если rxfifo trigger level interrupt
имеет значение, а UART в регистре маски прерывания UIS_RX_FIFO
Биты не маскируются (имеется в виду получить прерывание включено) затем введите получить ручка включения, звонок port->regs->UR_RXW
Получайте символы и сохраняйте их во временных переменных. recv->buf
середина.
#define UART_RX_INT_FLAG (UIS_RX_FIFO | UIS_RX_FIFO_TIMEOUT | UIS_BREAK |\
UIS_OVERRUN | UIS_FRM_ERR | UIS_PARITY_ERR)
#define UART_RX_ERR_INT_FLAG (UIS_BREAK | UIS_FRM_ERR | \
UIS_PARITY_ERR)
#define UART_TX_INT_FLAG (UIS_TX_FIFO | UIS_TX_FIFO_EMPTY)
ATTRIBUTE_ISR void UART1_IRQHandler(void)
{
struct tls_uart_port *port = &uart_port[1];
struct tls_uart_circ_buf *recv = &port->recv;
u8 rx_byte_cb_flag = uart_rx_byte_cb_flag[1];
u32 intr_src;
u32 rx_fifocnt;
u32 fifos;
u8 ch = 0;
u8 escapefifocnt = 0;
u32 rxlen = 0;
csi_kernel_intrpt_enter();
intr_src = port->regs->UR_INTS;
if ((intr_src & UIS_RX_FIFO) && (0 == (port->regs->UR_INTM & UIS_RX_FIFO)))
{
// иметь дело сполучить прерывание
rx_fifocnt = (port->regs->UR_FIFOS >> 6) & 0x3F; // от УАРТ Регистр статуса FIFO считывает количество данных
escapefifocnt = rx_fifocnt;
port->plus_char_cnt = 0;
rxlen = rx_fifocnt;
// Проверяет, меньше ли доступное пространство в кольцевом буфере «recv» или равно «RX_CACHE_LIMIT». Если это так, указатель конца буфера обновляется, чтобы ограничить объем полученных данных.
if (CIRC_SPACE(recv->head, recv->tail, TLS_UART_RX_BUF_SIZE) <= RX_CACHE_LIMIT)
{
recv->tail = (recv->tail + RX_CACHE_LIMIT) & (TLS_UART_RX_BUF_SIZE - 1);
}
while (rx_fifocnt-- > 0)
{
// UR_RXW — это UART RX Начальный адресный регистр, чтение и получение данных и сохранение их в буферной области.
ch = (u8) port->regs->UR_RXW;
recv->buf[recv->head] = ch; // Воляперениматьприезжатьизхарактер`ch`Хранить в кольцевом буфере`recv->buf`изположение головы。
recv->head = (recv->head + 1) & (TLS_UART_RX_BUF_SIZE - 1); // Обновите указатель заголовка области кольцевого буфера, чтобы подготовиться к следующему сохранению данных.
}
// вызов Получить обратный звонокфункция
if (port->rx_callback!=NULL && !rx_byte_cb_flag)
{
port->rx_callback(rxlen, port->priv_data); // Передайте длину полученных данных и личные данные (здесь не используются).
}
}
csi_kernel_intrpt_exit();
}
проходить tls_os_sem_create
Создайте семафор для связи между обратным вызовом и текущим потоком. действовать tls_os_sem_acquire
Дождитесь сигнала, установите здесь 20 тактов, если в 20 Не получено в течение циклов sem_rx
сигнал, что означает, что последовательный порт 20 В течение тактовых циклов данные не получены, время приема истекло, и был получен полный кадр.
позжепроходить tls_uart_read()
Считывание функции recv->buf
ценность и воля rx_length
Ясно, позвони tls_uart_write()
функция Воляданныепроходитьuart 1 Последовательный порт отправляет его обратно.
// Уарт нить
void uart1_task(void *sdata)
{
uint16_t rx_len = 0;
// Используется для определения того, успешно ли получен семафор.
tls_os_status_t os_status = TLS_OS_ERROR;
// Создайте семафор для связи между обратными вызовами и потоками.
tls_os_sem_create(&sem_rx, 0);
// Инициализировать последовательный порт
uart1_init();
for (;;)
{
os_status = tls_os_sem_acquire(sem_rx, 20); // Подождите прибытия семафора, подождите 20 тактов системы.
// Если возникает ошибка (не 0), то есть при 20Не получено в течение цикловsem_rxсигнал, что означает, что последовательный порт Данные не были получены в течение 20 тактов, время приема истекло, получен полный кадр
if (os_status)
{
// Если на последовательном порту есть данные, это означает, что пакет получен.
if (rx_length > 0)
{
// Прочтите данные этого пакета. Обратите внимание, что размер этого пакета не может превышать 1024 байта.
rx_len = tls_uart_read(TLS_UART_1, rx_fifo_buf, rx_length);
rx_length = 0;
tls_uart_write(TLS_UART_1, rx_fifo_buf, rx_len); // отправлять
}
}
}
}
Функция последовательной связи STM 32 является одной из наиболее часто используемых функций при разработке Linux и встраиваемых систем. С точки зрения получателя неизвестно, какой объем данных имеется у отправителя. Как оценить объем данных, отправленных за один раз, является проблемой для получателя.
Как упоминалось ранее, есть три решения этой проблемы. методы обнаружения прерывания и таймаута для обработки, а в W 801 Подробные руководства представлены на доске разработки.