[Разработка Linux] Последовательный порт получает данные переменной длины, получая схему прерывания + таймаута.
[Разработка Linux] Последовательный порт получает данные переменной длины, получая схему прерывания + таймаута.

Предисловие

При разработке Linux вы часто сталкиваетесь со связью по последовательному порту для завершения взаимодействия между двумя устройствами.

Последовательная связьзависит от чего-то, называемогопротокол последовательной связиправила,Он контролирует поток данных во время передачи данных.,включатьданныенастройки бита、скорость передачи данныхкорректирование、Определение контрольных битов и выбор стоповых битов и т. д.

Однако при последовательной связи мы обычно не знаем, сколько данных отправит другая сторона и когда данные будут отправлены. Короче говоря, вопрос заключается в следующем: как мы можем гарантировать получение полного набора данных? Чтобы оценить полный кадр, вам нужно знать, когда кадр начинается и когда он заканчивается.

Определить полный режим кадра

Данные, передаваемые через последовательный порт, имеют разную длину. Если прием неполный, это напрямую повлияет на последующую бизнес-обработку. Чтобы справиться с проблемой получения данных переменной длины, у нас есть несколько распространенных методов обработки:

  1. метод фиксированного формата: Обе стороны согласились, что кадр данных использует определенную последовательность символов для обозначения начала кадра данных и конца кадра данных. Например, последовательность символов «FF FE» указывает на начало кадра, начиная с «FF». EF» указывает на конец кадра. Получатель начинает сбор данных, когда он получает начальную последовательность, пока не получит конечную последовательность. Хотя этот метод прост и понятен, он требует, чтобы получатель оценивал каждый символ, что потребляет CPU ресурсы и увеличить потребление энергии.
  2. получить прерываниеи решение о тайм-ауте: Когда последовательный порт получит данные, он инициирует получение прерывание. И обычно между двумя кадрами данных будет промежуток, поэтому мы можем спроектировать таймер. Если в течение установленного времени не будет получено новых символов, текущий кадр данных будет считаться полученным после таймаута.
  3. метод прерывания простоя: Когда последовательный порт не получает новые данные в течение определенного периода времени, срабатывает прерывание простоя. Прерывание простоя на самом деле связано с получением Принцип определения таймаута прерывания аналогичен, но прерывание простоя является аппаратным. программное обеспечение имеет функции, которые позволяют Решение о тайм-ауте прерывания должно быть реализовано нами самими. После получения прерывания простоя мы можем считать, что получен полный кадр данных.

получить прерывание

Давайте сначала разберемся, получить Включение, получение срабатывает, когда получатель получает данные прерывание。

Пример: регистр прерываний показан на рисунке ниже. Когда получатель получает данные, бит 5 (RXNE) регистра состояния последовательного порта на рисунке будет установлен в 1. Когда мы получим значение регистра USART_DR, эта позиция снова очистится, то есть установится в 0.

получить прерывание и реализацию решения по таймауту

аппаратное обеспечение

  1. Ляньшэндэ (Ж) 801 развиватьтарелка
  2. строка данных typec: используется для отладки или загрузки программ.
  3. USB-TTL: используется для последовательной связи UART 1.

Таблица регистров состояния прерываний UART W 801 описана в следующей таблице.

Определение статуса прерывания uart в коде SDK следующее:

Язык кода:javascript
копировать
/*
 * 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)
  1. UIS_TX_FIFO_EMPTY:отправлятьFIFOНулевое прерывание。когдаотправлятьFIFO(First In, First Out, очередь «первым пришел — первым обслужен») пуст, этот бит установлен, а значение бита равно (1UL<<0),Прямо сейчасдвоичныйиз 00000001
  2. UIS_TX_FIFO:отправлятьFIFOпрерывать。когдаотправлятьFIFOсерединаизданные Срабатывает, когда сумма достигает определенного порога,Значение места(1UL<<1),Прямо сейчасдвоичныйиз00000010
  3. UIS_RX_FIFO:перениматьFIFOпрерывать。когдаперениматьFIFOсерединаизданные Срабатывает, когда сумма достигает определенного порога,Значение места(1UL<<2),Прямо сейчасдвоичныйиз00000100
  4. UIS_RX_FIFO_TIMEOUT:перениматьFIFOпрерывание по тайм-ауту。когдаперениматьFIFOЕстьданные,Но нового в течение определенного периода времени нетизданные Срабатывает по прибытии。Значение места(1UL<<3),Прямо сейчасдвоичныйиз00001000
  5. UIS_CTS_CHNG:CTS(Clear To Send,Прозрачныйотправлять)Изменения сигналапрерывать。когдаCTSСигнализ Срабатывает при изменении статуса,Значение места(1UL<<4),Прямо сейчасдвоичныйиз00010000
  6. UIS_BREAK:"break"обнаружение состоянияпрерывать。когдаобнаружен"break"Срабатывает, когда условие,как упоминалось ранее,"break"это долгое времяиз低电平Сигнал。Значение места(1UL<<5),Прямо сейчасдвоичныйиз00100000
  7. UIS_FRM_ERR:ошибка кадрапрерывать。когдаобнаруженошибка кадра时触发,Ошибка кадра возникает вданныерамкаиз Если стартовый или стоповый бит неправильный。Значение места(1UL<<6),Прямо сейчасдвоичныйиз01000000
  8. UIS_PARITY_ERR:Ошибка четностипрерывать。когда Используйте четность иперениматьприезжатьизданныес ожиданиямииз Срабатывает, когда биты четности не совпадают。Значение места(1UL<<7),Прямо сейчасдвоичныйиз10000000
  9. UIS_OVERRUN:ошибка переполненияпрерывать。когдаперениматьFIFOПолный,но все равно срабатывает при поступлении новых данных,в результате чего возникает самый старыйизданныепокрытый。Значение места(1UL<<8),Прямо сейчасдвоичныйиз100000000

суждениеполучить прерывание Нужно использовать UIS_RX_FIFO,Прямо сейчасuart №1 в таблице состояний прерываний 2 Кусочек.

Реализация кода

Инициализация последовательного порта

Определите переменные и семафоры приема данных, а также определите функцию обратного вызова приема.

Язык кода:javascript
копировать
// Сохранить полученные данные
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. Привяжите функцию обратного вызова приема.

Язык кода:javascript
копировать
// Инициализация последовательного порта
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 середина.

Язык кода:javascript
копировать
#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 Последовательный порт отправляет его обратно.

Язык кода:javascript
копировать
// Уарт нить
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 Подробные руководства представлены на доске разработки.

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose