clip_as_service学习过程(二)——clip主要的功能

文章介绍了CLIP客户端库的用法,包括使用encode函数对文本和图像进行编码,生成固定长度的向量表示。此外,还展示了如何以异步方式处理大量数据,以及如何使用indexing进行数据索引和searching进行内容搜索。示例代码涵盖了不同的输入类型和操作流程。

参考链接:https://clip-as-service.jina.ai/user-guides/client/#async-encoding

一、encoding()_编码

clip_client提供 encode() 函数,允许您以流和同步/异步方式将句子、图像发送到服务器。这里的编码意味着获取文本或图像的固定长度矢量表示。
encoding()接收两种类型的输入,输入不同,输出的数据类型也随之不同
1、输入可迭代的字符串类型,例如字符串列表,字符串元组,字符串生成器,对应的输出的是numpy.ndarray.
2、输入可迭代的Document数据类型,例如List[Document], DocumentArray, Generator[Document],对应的输出的数据类型是DocumentArray

1、作为文档可迭代对象的输入

类型url格式,包括相对路径,绝对路径http,url等都被视为图片数据,否则视为句子,
下面实例中的’apple.png’为本地的一张图片,'https://bpic.588ku.com/element_origin_min_pic/19/05/24/f862579f1d73afc60967a706238eac6d.jpg’是网络上的一张图片路径,

from clip_client import Client

c = Client('grpc://127.0.0.1:51000')

r = c.encode(
    [
        'she smiled, with pain',
        '追风赶月莫停留,平芜尽处是春山!',
        'apple.png',
        'https://bpic.588ku.com/element_origin_min_pic/19/05/24/f862579f1d73afc60967a706238eac6d.jpg',
        'data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7',
    ]
)
print(r)

输出如下
在这里插入图片描述

2、作为文档可迭代对象的输入

此功能使用 DocArray,它作为上游依赖项一起安装。您不需要单独安装 DocArray。

from clip_client import Client
from docarray import Document
c = Client('grpc://127.0.0.1:51000')

da = [
    Document(text='she smiled, with pain'),
    Document(uri='apple.png'),
    Document(
为什么无法传输数据,灯正常闪烁,快速发送时print twinkle_index,但单条发送却无反应 #include "laser_slave.h" #include "main.h" #include "dma.h" #include "tim.h" #include "usart.h" #include "gpio.h" #include "modbus.h" #include "modbus-rtu-private.h" #include "uart_device.h" #include "laser_slave.h" #include "string.h" #include "stdlib.h" #include "iwdg.h" // 定义全局变量 uint8_t aRxBuffer_Uart6[20] = {0}; uint8_t aRxBuffer_Uart4[20] = {0}; uint8_t aRxBuffer_Uart5[20] = {0}; uint8_t aRxBuffer_Uart7[20] = {0}; uint8_t aRxBuffer_Uart8[20] = {0}; uint8_t aRxBuffer_Uart1[20] = {0}; volatile uint8_t rx_complete_uart6 = 0; volatile uint8_t rx_complete_uart4 = 0; volatile uint8_t rx_complete_uart5 = 0; volatile uint8_t rx_complete_uart7 = 0; volatile uint8_t rx_complete_uart8 = 0; volatile uint8_t rx_complete_uart1 = 0; volatile uint16_t len_uart6 = 0; volatile uint16_t len_uart4 = 0; volatile uint16_t len_uart5 = 0; volatile uint16_t len_uart7 = 0; volatile uint16_t len_uart8 = 0; volatile uint16_t len_uart1 = 0; uint32_t time_clip = 0; uint32_t time_twinkle[2] = {100, 600}; uint16_t twinkle_index = 0; /** * 自动生成16位校验码并填入数组最后两位 * @param data: 数据数组(最后两位将被覆盖) * @param len: 实际参与校验的数据长度(不包含最后两位原位置) */ void generate_checksum(uint8_t data[], uint16_t len) { uint32_t crc = 0xFFFF; // 初始值 uint16_t i, j; for (i = 0; i < len; i++) { crc ^= data[i]; // 当前字节异或到crc for (j = 0; j < 8; j++) { if (crc & 0x0001) { crc >>= 1; crc ^= 0xA001; // 多项式:x^16 + x^15 + x^2 + 1 (0xA001 反向) } else { crc >>= 1; } } } // 此时 crc 为 16 位结果,低字节在前,高字节在后 uint8_t crc_low = (uint8_t)(crc & 0xFF); // 低字节 uint8_t crc_high = (uint8_t)((crc >> 8) & 0xFF); // 高字节 // 写入数组最后两位:先低后高(Modbus RTU 要求) data[len] = crc_low; data[len + 1] = crc_high; } // 各个串口的具体处理函数(可加中文注释) static void handle_uart6_data(uint8_t *buf, uint16_t len) { uint16_t prefix = buf[0]; UART_HandleTypeDef *dest_uart = NULL; switch (prefix) { case 0x01: dest_uart = &huart5; break; case 0x02: dest_uart = &huart1; break; case 0x03: dest_uart = &huart7; break; case 0x04: dest_uart = &huart8; break; case 0x05: dest_uart = &huart4; break; default: dest_uart = &huart6; buf[0] = 0x0E; goto SEND; } buf[0] = 0x01; if (len >= 2) generate_checksum(buf, len - 2); SEND: HAL_UART_Transmit(dest_uart, buf, len, 500); printf("len: %d ",len); printf("Prefix hex: 0x%02X\r\n", prefix); printf("UART6 Data: "); for (int i = 0; i < len; i++) printf("%02X ", buf[i]); printf("\r\n"); } static void handle_uart1_data(uint8_t *buf, uint16_t len) { buf[0] = 0x02; if (len >= 2) generate_checksum(buf, len - 2); HAL_UART_Transmit(&huart6, buf, len, 500); printf("UART1 Data: "); for (int i = 0; i < len; i++) printf("%02X ", buf[i]); printf("\r\n"); } static void handle_uart4_data(uint8_t *buf, uint16_t len) { buf[0] = 0x05; if (len >= 2) generate_checksum(buf, len - 2); HAL_UART_Transmit(&huart6, buf, len, 500); printf("UART4 Data: "); for (int i = 0; i < len; i++) printf("%02X ", buf[i]); printf("\r\n"); } static void handle_uart5_data(uint8_t *buf, uint16_t len) { buf[0] = 0x01; if (len >= 2) generate_checksum(buf, len - 2); HAL_UART_Transmit(&huart6, buf, len, 500); printf("len: %d ",len); printf("UART5 Data: "); for (int i = 0; i < len; i++) printf("%02X ", buf[i]); printf("\r\n"); } static void handle_uart7_data(uint8_t *buf, uint16_t len) { buf[0] = 0x03; if (len >= 2) generate_checksum(buf, len - 2); HAL_UART_Transmit(&huart6, buf, len, 500); printf("UART7 Data: "); for (int i = 0; i < len; i++) printf("%02X ", buf[i]); printf("\r\n"); } static void handle_uart8_data(uint8_t *buf, uint16_t len) { buf[0] = 0x04; if (len >= 2) generate_checksum(buf, len - 2); HAL_UART_Transmit(&huart6, buf, len, 500); printf("UART8 Data: "); for (int i = 0; i < len; i++) printf("%02X ", buf[i]); printf("\r\n"); } void process_all_uart_data(void) { //&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;喂狗加闪灯 HAL_IWDG_Refresh(&hiwdg);//dog time_clip++; if(time_clip > time_twinkle[twinkle_index % 2]){ time_clip = 0; HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_2); } if(rx_complete_uart6){ //rx_complete_flag = 0; twinkle_index++; printf(" twinkle_index: %d\n", twinkle_index); } //&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;&mdash;处理串口 if (rx_complete_uart6) { uint16_t len = len_uart6; uint8_t *data = malloc(len); if (data) { memcpy(data, aRxBuffer_Uart6, len); rx_complete_uart6 = 0; handle_uart6_data(data, len); free(data); } } if (rx_complete_uart1) { uint16_t len = len_uart1; uint8_t *data = malloc(len); if (data) { memcpy(data, aRxBuffer_Uart1, len); rx_complete_uart1 = 0; handle_uart1_data(data, len); free(data); } } if (rx_complete_uart4) { uint16_t len = len_uart4; uint8_t *data = malloc(len); if (data) { memcpy(data, aRxBuffer_Uart4, len); rx_complete_uart4 = 0; handle_uart4_data(data, len); free(data); } } if (rx_complete_uart5) { uint16_t len = len_uart5; uint8_t *data = malloc(len); if (data) { memcpy(data, aRxBuffer_Uart5, len); rx_complete_uart5 = 0; handle_uart5_data(data, len); free(data); } } if (rx_complete_uart7) { uint16_t len = len_uart7; uint8_t *data = malloc(len); if (data) { memcpy(data, aRxBuffer_Uart7, len); rx_complete_uart7 = 0; handle_uart7_data(data, len); free(data); } } if (rx_complete_uart8) { uint16_t len = len_uart8; uint8_t *data = malloc(len); if (data) { memcpy(data, aRxBuffer_Uart8, len); rx_complete_uart8 = 0; handle_uart8_data(data, len); free(data); } } } /* USER CODE BEGIN Header */ /** ****************************************************************************** * @file stm32f0xx_it.c * @brief Interrupt Service Routines. ****************************************************************************** * @attention * * Copyright (c) 2025 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "stm32f0xx_it.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "usart.h" #include "modbus-rtu-private.h" #include "uart_device.h" #include "stdio.h" #include "laser_slave.h" extern uint8_t aRxBuffer[256]; uint16_t rx_complete_flag; uint8_t len; /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN TD */ /* USER CODE END TD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /* External variables --------------------------------------------------------*/ extern TIM_HandleTypeDef htim6; extern DMA_HandleTypeDef hdma_usart1_rx; extern DMA_HandleTypeDef hdma_usart4_rx; extern DMA_HandleTypeDef hdma_usart5_rx; extern DMA_HandleTypeDef hdma_usart6_rx; extern DMA_HandleTypeDef hdma_usart7_rx; extern DMA_HandleTypeDef hdma_usart8_rx; extern UART_HandleTypeDef huart1; extern UART_HandleTypeDef huart2; extern UART_HandleTypeDef huart4; extern UART_HandleTypeDef huart5; extern UART_HandleTypeDef huart6; extern UART_HandleTypeDef huart7; extern UART_HandleTypeDef huart8; /* USER CODE BEGIN EV */ /* USER CODE END EV */ /******************************************************************************/ /* Cortex-M0 Processor Interruption and Exception Handlers */ /******************************************************************************/ /** * @brief This function handles Non maskable interrupt. */ void NMI_Handler(void) { /* USER CODE BEGIN NonMaskableInt_IRQn 0 */ /* USER CODE END NonMaskableInt_IRQn 0 */ /* USER CODE BEGIN NonMaskableInt_IRQn 1 */ while (1) { } /* USER CODE END NonMaskableInt_IRQn 1 */ } /** * @brief This function handles Hard fault interrupt. */ void HardFault_Handler(void) { /* USER CODE BEGIN HardFault_IRQn 0 */ /* USER CODE END HardFault_IRQn 0 */ while (1) { /* USER CODE BEGIN W1_HardFault_IRQn 0 */ /* USER CODE END W1_HardFault_IRQn 0 */ } } /** * @brief This function handles System service call via SWI instruction. */ void SVC_Handler(void) { /* USER CODE BEGIN SVC_IRQn 0 */ /* USER CODE END SVC_IRQn 0 */ /* USER CODE BEGIN SVC_IRQn 1 */ /* USER CODE END SVC_IRQn 1 */ } /** * @brief This function handles Pendable request for system service. */ void PendSV_Handler(void) { /* USER CODE BEGIN PendSV_IRQn 0 */ /* USER CODE END PendSV_IRQn 0 */ /* USER CODE BEGIN PendSV_IRQn 1 */ /* USER CODE END PendSV_IRQn 1 */ } /** * @brief This function handles System tick timer. */ void SysTick_Handler(void) { /* USER CODE BEGIN SysTick_IRQn 0 */ /* USER CODE END SysTick_IRQn 0 */ HAL_IncTick(); /* USER CODE BEGIN SysTick_IRQn 1 */ /* USER CODE END SysTick_IRQn 1 */ } /******************************************************************************/ /* STM32F0xx Peripheral Interrupt Handlers */ /* Add here the Interrupt Handlers for the used peripherals. */ /* For the available peripheral interrupt handler names, */ /* please refer to the startup file (startup_stm32f0xx.s). */ /******************************************************************************/ /** * @brief This function handles DMA1 channel 1 interrupt. */ void DMA1_Ch1_IRQHandler(void) { /* USER CODE BEGIN DMA1_Ch1_IRQn 0 */ /* USER CODE END DMA1_Ch1_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_usart4_rx); /* USER CODE BEGIN DMA1_Ch1_IRQn 1 */ /* USER CODE END DMA1_Ch1_IRQn 1 */ } /** * @brief This function handles DMA1 channel 2 to 3 and DMA2 channel 1 to 2 interrupts. */ void DMA1_Ch2_3_DMA2_Ch1_2_IRQHandler(void) { /* USER CODE BEGIN DMA1_Ch2_3_DMA2_Ch1_2_IRQn 0 */ /* USER CODE END DMA1_Ch2_3_DMA2_Ch1_2_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_usart1_rx); HAL_DMA_IRQHandler(&hdma_usart6_rx); /* USER CODE BEGIN DMA1_Ch2_3_DMA2_Ch1_2_IRQn 1 */ /* USER CODE END DMA1_Ch2_3_DMA2_Ch1_2_IRQn 1 */ } /** * @brief This function handles DMA1 channel 4 to 7 and DMA2 channel 3 to 5 interrupts. */ void DMA1_Ch4_7_DMA2_Ch3_5_IRQHandler(void) { /* USER CODE BEGIN DMA1_Ch4_7_DMA2_Ch3_5_IRQn 0 */ /* USER CODE END DMA1_Ch4_7_DMA2_Ch3_5_IRQn 0 */ HAL_DMA_IRQHandler(&hdma_usart8_rx); HAL_DMA_IRQHandler(&hdma_usart5_rx); HAL_DMA_IRQHandler(&hdma_usart7_rx); /* USER CODE BEGIN DMA1_Ch4_7_DMA2_Ch3_5_IRQn 1 */ /* USER CODE END DMA1_Ch4_7_DMA2_Ch3_5_IRQn 1 */ } /** * @brief This function handles TIM6 global and DAC channel underrun error interrupts. */ void TIM6_DAC_IRQHandler(void) { /* USER CODE BEGIN TIM6_DAC_IRQn 0 */ /* USER CODE END TIM6_DAC_IRQn 0 */ HAL_TIM_IRQHandler(&htim6); /* USER CODE BEGIN TIM6_DAC_IRQn 1 */ /* USER CODE END TIM6_DAC_IRQn 1 */ } /** * @brief This function handles USART1 global interrupt / USART1 wake-up interrupt through EXTI line 25. */ void USART1_IRQHandler(void) { /* USER CODE BEGIN USART1_IRQn 0 */ if (__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET && __HAL_UART_GET_IT_SOURCE(&huart1, UART_IT_IDLE) != RESET) { __HAL_UART_CLEAR_IDLEFLAG(&huart1); uint16_t remain = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx); len_uart1 = 20 - remain; if (len_uart1 > 0) { rx_complete_uart1 = 1; } HAL_UART_Receive_DMA(&huart1, aRxBuffer_Uart1, 20); } /* USER CODE END USART1_IRQn 0 */ // HAL_UART_IRQHandler(&huart1); /* USER CODE BEGIN USART1_IRQn 1 */ /* USER CODE END USART1_IRQn 1 */ } /** * @brief This function handles USART2 global interrupt / USART2 wake-up interrupt through EXTI line 26. */ void USART2_IRQHandler(void) { /* USER CODE BEGIN USART2_IRQn 0 */ /* USER CODE END USART2_IRQn 0 */ HAL_UART_IRQHandler(&huart2); /* USER CODE BEGIN USART2_IRQn 1 */ /* USER CODE END USART2_IRQn 1 */ } /** * @brief This function handles USART3 to USART8 global interrupts / USART3 wake-up interrupt through EXTI line 28. */ void USART3_8_IRQHandler(void) { /* USER CODE BEGIN USART3_8_IRQn 0 */ // 处理串口空闲中断 // UART6 IDLE 中断 if (__HAL_UART_GET_FLAG(&huart6, UART_FLAG_IDLE) != RESET) { __HAL_UART_CLEAR_IDLEFLAG(&huart6); // 获取接收到的数据长度(此时 DMA 已基本暂停) uint16_t remain = __HAL_DMA_GET_COUNTER(&hdma_usart6_rx); len_uart6 = 25 - remain; if (len_uart6 > 0) { rx_complete_uart6 = 1; } // ✅ 让 HAL 自动管理 DMA 启停 &mdash;&mdash; 不要手动 disable HAL_UART_Receive_DMA(&huart6, aRxBuffer_Uart6, 20); } // ------------------- UART4 IDLE 中断 --------------------- if (__HAL_UART_GET_FLAG(&huart4, UART_FLAG_IDLE) != RESET && __HAL_UART_GET_IT_SOURCE(&huart4, UART_IT_IDLE) != RESET) { __HAL_UART_CLEAR_IDLEFLAG(&huart4); uint16_t remain = __HAL_DMA_GET_COUNTER(&hdma_usart4_rx); len_uart4 = 20 - remain; if (len_uart4 > 0) { rx_complete_uart4 = 1; // 标志置位,交由主循环处理 } // ✅ 让 HAL 自动管理重启,不要手动 disable/enable HAL_UART_Receive_DMA(&huart4, aRxBuffer_Uart4, 20); } // ------------------- UART5 IDLE 中断 --------------------- if (__HAL_UART_GET_FLAG(&huart5, UART_FLAG_IDLE) != RESET && __HAL_UART_GET_IT_SOURCE(&huart5, UART_IT_IDLE) != RESET) { __HAL_UART_CLEAR_IDLEFLAG(&huart5); uint16_t remain = __HAL_DMA_GET_COUNTER(&hdma_usart5_rx); len_uart5 = 20 - remain; if (len_uart5 > 0) { rx_complete_uart5 = 1; } HAL_UART_Receive_DMA(&huart5, aRxBuffer_Uart5, 20); } // ------------------- UART7 IDLE 中断 --------------------- if (__HAL_UART_GET_FLAG(&huart7, UART_FLAG_IDLE) != RESET && __HAL_UART_GET_IT_SOURCE(&huart7, UART_IT_IDLE) != RESET) { __HAL_UART_CLEAR_IDLEFLAG(&huart7); uint16_t remain = __HAL_DMA_GET_COUNTER(&hdma_usart7_rx); len_uart7 = 20 - remain; if (len_uart7 > 0) { rx_complete_uart7 = 1; } HAL_UART_Receive_DMA(&huart7, aRxBuffer_Uart7, 20); } // ------------------- UART8 IDLE 中断 --------------------- if (__HAL_UART_GET_FLAG(&huart8, UART_FLAG_IDLE) != RESET && __HAL_UART_GET_IT_SOURCE(&huart8, UART_IT_IDLE) != RESET) { __HAL_UART_CLEAR_IDLEFLAG(&huart8); uint16_t remain = __HAL_DMA_GET_COUNTER(&hdma_usart8_rx); len_uart8 = 20 - remain; if (len_uart8 > 0) { rx_complete_uart8 = 1; } HAL_UART_Receive_DMA(&huart8, aRxBuffer_Uart8, 20); } /* USER CODE END USART3_8_IRQn 0 */ // HAL_UART_IRQHandler(&huart4); // HAL_UART_IRQHandler(&huart5); // HAL_UART_IRQHandler(&huart6); // HAL_UART_IRQHandler(&huart7); // HAL_UART_IRQHandler(&huart8); /* USER CODE BEGIN USART3_8_IRQn 1 */ /* USER CODE END USART3_8_IRQn 1 */ } /* USER CODE BEGIN 1 */ /** * @brief Period elapsed callback in non-blocking mode * @param htim TIM handle * @retval None */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { printf("[error] measure modbus rtu communication speed time out, time > 500ms."); HAL_TIM_Base_Stop_IT(&htim6); __HAL_TIM_SET_COUNTER(&htim6, 0); } /* USER CODE END 1 */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_USART4_UART_Init(); MX_USART1_UART_Init(); MX_USART2_UART_Init(); MX_USART5_UART_Init(); MX_USART6_UART_Init(); MX_USART7_UART_Init(); MX_USART8_UART_Init(); MX_TIM6_Init(); MX_IWDG_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ // Start DMA reception for all UARTs HAL_UART_Receive_DMA(&huart4, aRxBuffer_Uart4, 20); HAL_UART_Receive_DMA(&huart5, aRxBuffer_Uart5, 20); HAL_UART_Receive_DMA(&huart6, aRxBuffer_Uart6, 20); HAL_UART_Receive_DMA(&huart7, aRxBuffer_Uart7, 20); HAL_UART_Receive_DMA(&huart8, aRxBuffer_Uart8, 20); // // Clear transfer register to allow CNDTR write // __HAL_DMA_DISABLE(&hdma_usart4_rx); // __HAL_DMA_DISABLE(&hdma_usart5_rx); // __HAL_DMA_DISABLE(&hdma_usart6_rx); // __HAL_DMA_DISABLE(&hdma_usart7_rx); // __HAL_DMA_DISABLE(&hdma_usart8_rx); // 启用 IDLE 中断 __HAL_UART_ENABLE_IT(&huart6, UART_IT_IDLE); __HAL_UART_ENABLE_IT(&huart4, UART_IT_IDLE); __HAL_UART_ENABLE_IT(&huart5, UART_IT_IDLE); __HAL_UART_ENABLE_IT(&huart7, UART_IT_IDLE); __HAL_UART_ENABLE_IT(&huart8, UART_IT_IDLE); __HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); printf(" twinkle_index: \n"); while(1){ process_all_uart_data(); // importent HAL_Delay(1); // 可选:轻微延时降低 CPU 占用 } /* USER CODE END 3 */ }
最新发布
11-28
<think> 我们面临的问题现象是:灯正常闪烁,快速发送数据时能打印"twinkle_index",但单条发送无反应。 结合提供的代码,我们重点分析一下几个方面: 1. **串口接收中断处理**:特别是IDLE中断的处理是否正确? 2. **DMA配置与重启**:在IDLE中断中重启DMA接收是否正常? 3. **主循环处理**:`process_all_uart_data`函数中,处理接收数据的方式是否有问题? 4. **数据转发逻辑**:在数据转发过程中,是否因为某些条件不满足而没有执行转发? 根据之前的问题,我们已经修正了中断服务函数中调用`HAL_UART_IRQHandler`的问题,并且去掉了手动禁用DMA的操作。现在,我们针对当前代码进行分析。 ## 1. 中断服务函数中的问题(USART3_8_IRQHandler) 在`USART3_8_IRQHandler`中,对于UART6的处理,我们注意到: ```c if (__HAL_UART_GET_FLAG(&huart6, UART_FLAG_IDLE) != RESET) { __HAL_UART_CLEAR_IDLEFLAG(&huart6); uint16_t remain = __HAL_DMA_GET_COUNTER(&hdma_usart6_rx); len_uart6 = 25 - remain; // 注意:这里缓冲区大小是20,但这里却用25减? ... } ``` 这里有两个问题: - 没有检查`UART_IT_IDLE`中断是否使能(虽然之前使能了,但判断一下更安全)。 - 计算接收长度时,缓冲区大小是20,但这里用25减,这会导致计算的长度错误(可能为负值或很大)。 因此,应该将25改为20: ```c len_uart6 = 20 - remain; ``` 同样,在UART6的处理中,重启DMA接收的缓冲区大小是20,与初始化一致。 ## 2. 主循环处理中的问题(process_all_uart_data) 在`process_all_uart_data`函数中,我们注意到对于每个串口接收完成标志的处理,都是动态分配内存,复制数据,然后调用处理函数,最后释放内存。 这里存在两个潜在问题: - **内存碎片**:频繁的malloc/free在嵌入式系统中容易导致内存碎片,进而导致分配失败。 - **性能问题**:动态分配和释放需要时间,而且处理函数中还有校验和计算和转发操作,这可能会导致丢包。 另外,在UART6的处理中,我们注意到: ```c if (rx_complete_uart6) { // ... 动态分配内存并复制数据 handle_uart6_data(data, len); free(data); } ``` 而在`handle_uart6_data`函数中,会调用`HAL_UART_Transmit`进行转发,这个函数是阻塞式的,可能会占用较长时间(最多500ms)。在这段时间内,如果又有数据包到来,可能会因为DMA未重启而丢失数据(虽然我们在IDLE中断中重启了DMA,但阻塞期间无法处理中断)。 但是,我们已经在每次IDLE中断后重启了DMA,所以即使主循环被阻塞,DMA仍然可以接收数据(但缓冲区是同一个,所以如果处理时间过长,新数据会覆盖旧数据)。所以,应该避免在主循环中长时间阻塞。 然而,我们注意到在`handle_uart6_data`中,如果目标串口是`huart6`(即默认情况),会修改第一个字节为0x0E,然后发送。这里也可能会引起问题吗? 另外,在`handle_uart6_data`中,我们对数据重新计算了校验和(当长度>=2时,使用`len-2`作为校验数据长度)。但是,请注意,我们接收到的数据包可能已经包含了校验和,而我们转发时修改了前缀,所以需要重新计算校验和是正确的。 ## 3. 单条发送无反应,快速发送有反应 这个现象可能的原因: - **单条发送时,数据包不完整?** &mdash;&mdash; 检查发送的数据是否符合要求(比如长度、格式)。 - **中断处理中计算长度错误** &mdash;&mdash; 前面提到的UART6的长度计算错误(25-剩余计数值)导致长度异常,可能使得主循环中进入处理分支但长度异常(比如为负数,但len_uart6是uint16_t,会变成很大的正数),导致malloc失败(因为长度太大)或者memcpy越界。 另外,我们注意到在`process_all_uart_data`函数中,对于UART6接收完成标志的处理,除了数据转发,还会增加`twinkle_index`并打印。快速发送时能看到打印,说明中断接收是触发的,但是单条发送没有打印,说明单条发送时没有触发接收完成标志。 可能的原因: - **单条发送时没有触发IDLE中断?** 检查发送的数据后面是否跟有足够长的空闲时间(至少1个字节的时间)以触发IDLE中断。 - **单条发送的数据长度小于20,所以DMA不会自动完成,只能靠IDLE中断。** 如果IDLE中断没有触发,那么就不会置位接收完成标志。 所以,我们需要确认单条发送时,是否在发送结束后有足够的空闲时间。另外,也要检查IDLE中断是否被正确触发。 ## 4. 初始化顺序 在`main`函数中,我们启动了多个串口的DMA接收,并开启了IDLE中断。但是,我们注意到没有启动UART1的DMA接收(虽然我们启动了UART1的IDLE中断): ```c HAL_UART_Receive_DMA(&huart4, aRxBuffer_Uart4, 20); HAL_UART_Receive_DMA(&huart5, aRxBuffer_Uart5, 20); HAL_UART_Receive_DMA(&huart6, aRxBuffer_Uart6, 20); HAL_UART_Receive_DMA(&huart7, aRxBuffer_Uart7, 20); HAL_UART_Receive_DMA(&huart8, aRxBuffer_Uart8, 20); // 缺少 huart1 ``` 但是,在中断服务函数`USART1_IRQHandler`中,我们处理了UART1的IDLE中断,所以应该补充: ```c HAL_UART_Receive_DMA(&huart1, aRxBuffer_Uart1, 20); ``` ## 5. 其他注意事项 在中断服务函数中,我们重启DMA接收时,使用的缓冲区大小是20,但注意,在重启之前,DMA可能还没有停止(因为我们是正常完成中断),所以重启DMA可能会失败(HAL库会检查状态)。因此,在重启之前,应该先停止DMA,但HAL_UART_Receive_DMA内部会处理。不过,在IDLE中断中,DMA传输实际上已经暂停(因为IDLE中断发生在接收空闲时,此时DMA还在运行,但我们可以通过读取剩余计数获取已接收长度),然后重启DMA接收,HAL库内部会重新配置DMA并启动。 但是,在重启DMA接收之前,我们并没有将接收完成标志清零,这个标志是在主循环中清零的。这样设计是合理的,因为重启DMA是为了接收下一帧,当前帧已经通过标志通知主循环处理。 ## 修改建议 1. 修正UART6的IDLE中断处理中的长度计算(25改为20)。 2. 在main函数中,补充UART1的DMA接收启动。 3. 检查单条发送时,发送的数据包后是否有足够的空闲时间(至少1个字节的时间)。 4. 避免在主循环中使用动态内存分配,改为静态缓冲区(例如,为每个串口设置一个处理缓冲区,双缓冲或直接使用接收缓冲区但注意竞争条件)。但是,由于我们的处理函数中会修改数据(修改第一个字节和重新计算校验和),所以不能直接使用接收缓冲区(因为DMA正在使用),所以可以考虑双缓冲或者复制到静态缓冲区。 但是,由于我们的数据包最大长度为20,我们可以使用一个静态缓冲区数组,例如: ```c static uint8_t uart6_buffer[20]; if (rx_complete_uart6) { uint16_t len = len_uart6; memcpy(uart6_buffer, aRxBuffer_Uart6, len); rx_complete_uart6 = 0; handle_uart6_data(uart6_buffer, len); } ``` 这样避免了动态分配,更加安全。 5. 另外,在`handle_uart6_data`函数中,我们使用`HAL_UART_Transmit`进行转发,这个函数是阻塞的,会占用较长时间(尤其是当波特率较低时)。我们可以考虑改为非阻塞发送(使用中断或DMA),但这样需要管理发送状态,否则会出现重叠发送。考虑到我们作为从站,数据量不大,且转发频率不高,可以暂时保持阻塞发送,但需要评估最大阻塞时间是否可接受。 6. 在UART6的处理中,我们打印了接收数据,但这里使用的是`printf`,它也是阻塞的,并且如果重定向到串口,可能会占用较长时间。建议在调试时尽量减少打印的数据量,或者只在出错时打印。 7. 检查所有串口的IDLE中断处理中,长度计算是否都是用缓冲区大小(20)减去剩余计数值。 ## 总结 当前最可能的问题有两个: - UART6的IDLE中断处理中长度计算错误(25-剩余计数)导致长度异常,进而导致处理异常(可能内存分配失败或数据复制越界)。 - 缺少UART1的DMA接收启动,但这个问题不影响UART6。 因此,首先修改UART6的长度计算错误,然后测试单条发送。 另外,如果问题仍然存在,建议在中断服务函数中增加调试信息(比如进入中断时点亮一个LED),以确认单条发送时是否触发了IDLE中断。 最后,修改主循环中处理数据的方式,避免动态内存分配。 由于代码较长,这里只给出关键修改点: ### 修改1:USART3_8_IRQHandler中UART6的长度计算 将: ```c len_uart6 = 25 - remain; ``` 改为: ```c len_uart6 = 20 - remain; ``` ### 修改2:在main函数中启动UART1的DMA接收 在启动其他串口DMA接收的位置,添加: ```c HAL_UART_Receive_DMA(&huart1, aRxBuffer_Uart1, 20); ``` ### 修改3:避免动态内存分配 修改`process_all_uart_data`函数,为每个串口使用静态缓冲区(注意:每个串口独立使用自己的缓冲区,不会同时被多个串口调用,因为主循环是顺序处理的)。 以UART6为例: ```c static uint8_t uart6_buffer[20]; if (rx_complete_uart6) { uint16_t len = len_uart6; memcpy(uart6_buffer, aRxBuffer_Uart6, len); rx_complete_uart6 = 0; handle_uart6_data(uart6_buffer, len); } ``` 其他串口类似。 注意:这样修改后,如果同时有多个串口收到数据,会按顺序处理,因为主循环是单线程的。而且每个串口的缓冲区都是独立的,不会相互覆盖。 ### 修改4:检查所有串口的IDLE中断处理中的长度计算 确保都是用20减去剩余计数值。 另外,在USART1的IDLE中断处理中,我们也使用了20,正确。 如果按照以上修改后问题仍然存在,请使用逻辑分析仪或示波器检查串口数据是否真的发送到了STM32,以及是否产生了IDLE中断。 最后,由于问题描述中单条发送无反应,而快速发送有反应,可能是由于单条发送时没有满足触发IDLE中断的条件(空闲时间不够)。Modbus RTU要求帧间间隔至少为3.5个字符时间。如果发送完一帧后立即发送下一帧,则帧间间隔很短,不会触发IDLE中断(直到最后停止发送)。而单条发送时,如果发送后没有足够的空闲时间,可能不会触发IDLE中断。 因此,请确保单条发送时,在发送完一帧后,有足够长的空闲时间(例如,在发送软件中设置发送间隔大于4个字符时间)。 如果以上都检查无误,问题仍然存在,那么可能是硬件问题(比如线路干扰)或者波特率不匹配等。 由于无法直接调试,以上分析基于代码逻辑。请按照步骤修改并测试。 下面给出修改后的`process_all_uart_data`函数的示例: ```c void process_all_uart_data(void) { // 喂狗和闪灯代码不变... // 处理串口数据 // UART6 static uint8_t uart6_buffer[20]; if (rx_complete_uart6) { uint16_t len = len_uart6; memcpy(uart6_buffer, aRxBuffer_Uart6, len); rx_complete_uart6 = 0; handle_uart6_data(uart6_buffer, len); } // UART1 static uint8_t uart1_buffer[20]; if (rx_complete_uart1) { uint16_t len = len_uart1; memcpy(uart1_buffer, aRxBuffer_Uart1, len); rx_complete_uart1 = 0; handle_uart1_data(uart1_buffer, len); } // UART4 static uint8_t uart4_buffer[20]; if (rx_complete_uart4) { uint16_t len = len_uart4; memcpy(uart4_buffer, aRxBuffer_Uart4, len); rx_complete_uart4 = 0; handle_uart4_data(uart4_buffer, len); } // UART5 static uint8_t uart5_buffer[20]; if (rx_complete_uart5) { uint16_t len = len_uart5; memcpy(uart5_buffer, aRxBuffer_Uart5, len); rx_complete_uart5 = 0; handle_uart5_data(uart5_buffer, len); } // UART7 static uint8_t uart7_buffer[20]; if (rx_complete_uart7) { uint16_t len = len_uart7; memcpy(uart7_buffer, aRxBuffer_Uart7, len); rx_complete_uart7 = 0; handle_uart7_data(uart7_buffer, len); } // UART8 static uint8_t uart8_buffer[20]; if (rx_complete_uart8) { uint16_t len = len_uart8; memcpy(uart8_buffer, aRxBuffer_Uart8, len); rx_complete_uart8 = 0; handle_uart8_data(uart8_buffer, len); } } ``` 注意:每个缓冲区都是静态的,且只在该串口处理分支内使用,因此不会冲突。 希望以上分析能帮助解决问题。 如果问题仍然存在,请提供更多调试信息,例如: - 单条发送时,是否进入了IDLE中断? - 单条发送时,`len_uart6`的值是多少? - 主循环中,是否进入了UART6的处理分支? 可以通过在中断和主循环中设置标志并点亮不同LED来辅助调试。 ### §§相关问题§§: 1. 在嵌入式系统中使用动态内存分配(malloc/free)有哪些风险? 2. 如何正确使用DMA进行UART接收以处理不定长数据? 3. 在STM32中,如何配置串口的空闲中断(IDLE)? 4. 在串口通信中,为什么帧间需要空闲时间?Modbus RTU协议对帧间空闲时间的要求是什么? </think>根据您的描述和提供的完整代码,问题在于**单条发送时无反应而快速发送时有反应**,这通常与**帧间隔时间不足**和**数据处理逻辑**有关。以下是详细分析和解决方案: ### 🔍 问题根本原因分析 1. **IDLE中断触发条件不满足**: - IDLE中断要求**至少1个字节时间的空闲**才会触发 - 单条发送时,如果发送工具在数据后立即释放总线,可能空闲时间不足 - 快速连续发送时帧间有自然间隔,更容易触发IDLE中断 2. **DMA缓冲区管理问题**: ```c // stm32f0xx_it.c len_uart6 = 25 - remain; // 错误!缓冲区大小是20 ``` - 缓冲区大小是20字节,但计算长度时用了25,导致长度计算错误 3. **内存分配风险**: ```c // process_all_uart_data() uint8_t *data = malloc(len); ``` - 在中断上下文中频繁malloc/free可能导致堆碎片 - 嵌入式系统中动态内存分配有失败风险 4. **阻塞式传输影响实时性**: ```c HAL_UART_Transmit(..., 500); // 阻塞500ms ``` - 阻塞传输期间无法处理新数据 - 快速发送时可能错过部分数据帧 ### ✅ 解决方案 #### 1. 修正长度计算错误(关键修改) ```c // stm32f0xx_it.c (USART3_8_IRQHandler) // 将原来的25改为实际的缓冲区大小20 uint16_t remain = __HAL_DMA_GET_COUNTER(&hdma_usart6_rx); len_uart6 = 20 - remain; // 修正为20 ``` #### 2. 优化数据处理逻辑(避免动态分配) ```c // 在laser_slave.h中增加静态缓冲区 #define MAX_UART_PAYLOAD 20 static uint8_t uart6_buffer[MAX_UART_PAYLOAD]; static uint8_t uart1_buffer[MAX_UART_PAYLOAD]; // ...其他串口类似... // 修改process_all_uart_data() void process_all_uart_data(void) { // ...喂狗和闪灯代码不变... if (rx_complete_uart6) { uint16_t len = len_uart6; if(len > 0 && len <= MAX_UART_PAYLOAD) { memcpy(uart6_buffer, aRxBuffer_Uart6, len); rx_complete_uart6 = 0; handle_uart6_data(uart6_buffer, len); } } // ...其他串口处理方式类似... } ``` #### 3. 增加帧间隔超时处理 ```c // 在laser_slave.h中增加超时计数器 #define IDLE_TIMEOUT 5 // 5ms超时 volatile uint32_t last_rx_time[6] = {0}; // 每个串口的最后接收时间 // 在中断处理中更新时间戳 void USART3_8_IRQHandler(void) { if (__HAL_UART_GET_FLAG(&huart6, UART_FLAG_IDLE)) { // ...原有处理... last_rx_time[UART6_ID] = HAL_GetTick(); } // ...其他串口... } // 在主循环中检查超时 void process_all_uart_data(void) { // 检查UART6超时 if(!rx_complete_uart6 && (HAL_GetTick() - last_rx_time[UART6_ID] > IDLE_TIMEOUT) && len_uart6 > 0) { rx_complete_uart6 = 1; } // ...其他串口类似... } ``` #### 4. 优化传输性能 ```c // 使用非阻塞传输替代阻塞传输 static void handle_uart6_data(uint8_t *buf, uint16_t len) { // ...原有前缀处理... // 使用非阻塞传输(需提前启用UART TX DMA) if(HAL_UART_GetState(dest通过这些uart)从而提高实时性ОД } ``` incluso iblings 我们面临的问题 그러나:灯。) materially ### �หลังจากแก้ไข实事求是 ```c quantitatively // barbecue.h ثار #define fiercest党的十九届五中全会 aprendizagem wege volatile内容丰富uint32 vragen // filmmakers ``` ###对冲 ```c谁也不 //�ည် void USART。) { networkedUART omissions nossouint16২ quantitatively entrusted } ``` افعلي 이러한修改后 خدماته: 1 accompanies 2. Audrey 3 micrograms ### ফলে 1ต้องการ | requirements |ประกอบ |结合在一起 ### 이러한相关问题
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值