当前位置: 首页 > news >正文

在STM32 FreeRTOS环境中使用mutex和ringbuffer实现多任务的UART同步通信

在STM32 FreeRTOS环境中使用mutex和ringbuffer实现多任务的UART同步通信。完整的STM32 FreeRTOS UART多任务同步方案,主要特点包括:

核心组件

1. 环形缓冲区 (RingBuffer)

  • 发送和接收各有独立的环形缓冲区
  • 支持多生产者/消费者模式
  • 提供满/空状态检查

2. 互斥量 (Mutex)

  • txMutex: 保护发送操作的线程安全
  • rxMutex: 保护接收操作的线程安全
  • 防止多任务同时访问UART资源

3. 信号量和队列

  • txSemaphore: 发送完成通知
  • rxQueue: 接收数据通知队列

使用方法

初始化:

// 在main函数中调用
UartMultiTask_Init(&huart1);

发送数据:

char msg[] = "Hello World\r\n";
UartManager_Transmit(&uart1_manager, (uint8_t*)msg, strlen(msg), 1000);

接收数据:

uint8_t rxBuffer[64];
uint16_t len = UartManager_Receive(&uart1_manager, rxBuffer, sizeof(rxBuffer), 1000);

同步机制

  1. 发送同步: 使用互斥量确保同一时间只有一个任务可以发送
  2. 接收同步: 通过队列通知机制实现异步接收
  3. 中断安全: 在中断中使用FromISR版本的FreeRTOS API

任务示例

代码中包含三个示例任务:

  • UartSendTask: 定期发送消息
  • UartReceiveTask: 接收数据并回显
  • UartLogTask: 系统日志输出

这个方案确保了多任务环境下UART通信的稳定性和线程安全性,适用于需要多个任务同时使用UART的复杂应用场景。


实现代码如下:

/* STM32 FreeRTOS UART多任务同步实现 */#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
#include "stm32f4xx_hal.h"
#include <string.h>
#include <stdio.h>/* 环形缓冲区结构 */
typedef struct {uint8_t *buffer;uint16_t head;uint16_t tail;uint16_t size;uint16_t count;
} RingBuffer_t;/* UART管理结构 */
typedef struct {UART_HandleTypeDef *huart;RingBuffer_t rxRingBuffer;RingBuffer_t txRingBuffer;SemaphoreHandle_t txMutex;SemaphoreHandle_t rxMutex;SemaphoreHandle_t txSemaphore;QueueHandle_t rxQueue;uint8_t rxByte;uint8_t txActive;
} UartManager_t;/* 全局变量 */
#define UART_BUFFER_SIZE 256
static uint8_t uart1_rx_buffer[UART_BUFFER_SIZE];
static uint8_t uart1_tx_buffer[UART_BUFFER_SIZE];
static UartManager_t uart1_manager;/* 环形缓冲区操作函数 */
void RingBuffer_Init(RingBuffer_t *rb, uint8_t *buffer, uint16_t size) {rb->buffer = buffer;rb->head = 0;rb->tail = 0;rb->size = size;rb->count = 0;
}uint8_t RingBuffer_Put(RingBuffer_t *rb, uint8_t data) {if (rb->count >= rb->size) {return 0; // 缓冲区满}rb->buffer[rb->head] = data;rb->head = (rb->head + 1) % rb->size;rb->count++;return 1;
}uint8_t RingBuffer_Get(RingBuffer_t *rb, uint8_t *data) {if (rb->count == 0) {return 0; // 缓冲区空}*data = rb->buffer[rb->tail];rb->tail = (rb->tail + 1) % rb->size;rb->count--;return 1;
}uint16_t RingBuffer_GetCount(RingBuffer_t *rb) {return rb->count;
}uint8_t RingBuffer_IsEmpty(RingBuffer_t *rb) {return (rb->count == 0);
}uint8_t RingBuffer_IsFull(RingBuffer_t *rb) {return (rb->count >= rb->size);
}/* UART管理器初始化 */
void UartManager_Init(UartManager_t *mgr, UART_HandleTypeDef *huart) {mgr->huart = huart;// 初始化环形缓冲区RingBuffer_Init(&mgr->rxRingBuffer, uart1_rx_buffer, UART_BUFFER_SIZE);RingBuffer_Init(&mgr->txRingBuffer, uart1_tx_buffer, UART_BUFFER_SIZE);// 创建互斥量mgr->txMutex = xSemaphoreCreateMutex();mgr->rxMutex = xSemaphoreCreateMutex();// 创建信号量mgr->txSemaphore = xSemaphoreCreateBinary();// 创建队列用于接收数据通知mgr->rxQueue = xQueueCreate(10, sizeof(uint8_t));mgr->txActive = 0;// 启动UART接收中断HAL_UART_Receive_IT(mgr->huart, &mgr->rxByte, 1);
}/* UART发送函数 - 支持多任务安全 */
HAL_StatusTypeDef UartManager_Transmit(UartManager_t *mgr, uint8_t *data, uint16_t len, uint32_t timeout) {HAL_StatusTypeDef status = HAL_OK;uint16_t i;// 获取发送互斥量if (xSemaphoreTake(mgr->txMutex, pdMS_TO_TICKS(timeout)) != pdTRUE) {return HAL_TIMEOUT;}// 将数据放入发送缓冲区for (i = 0; i < len; i++) {if (!RingBuffer_Put(&mgr->txRingBuffer, data[i])) {status = HAL_ERROR; // 缓冲区满break;}}// 如果发送器空闲,启动发送if (!mgr->txActive && !RingBuffer_IsEmpty(&mgr->txRingBuffer)) {uint8_t txData;if (RingBuffer_Get(&mgr->txRingBuffer, &txData)) {mgr->txActive = 1;HAL_UART_Transmit_IT(mgr->huart, &txData, 1);}}// 等待发送完成if (status == HAL_OK) {xSemaphoreTake(mgr->txSemaphore, pdMS_TO_TICKS(timeout));}// 释放互斥量xSemaphoreGive(mgr->txMutex);return status;
}/* UART接收函数 - 支持多任务安全 */
uint16_t UartManager_Receive(UartManager_t *mgr, uint8_t *data, uint16_t maxLen, uint32_t timeout) {uint16_t received = 0;uint8_t rxData;TickType_t startTime = xTaskGetTickCount();// 获取接收互斥量if (xSemaphoreTake(mgr->rxMutex, pdMS_TO_TICKS(timeout)) != pdTRUE) {return 0;}while (received < maxLen) {// 从环形缓冲区获取数据if (RingBuffer_Get(&mgr->rxRingBuffer, &rxData)) {data[received++] = rxData;} else {// 缓冲区为空,等待新数据if (xQueueReceive(mgr->rxQueue, &rxData, pdMS_TO_TICKS(10)) == pdTRUE) {data[received++] = rxData;} else {// 检查超时if ((xTaskGetTickCount() - startTime) >= pdMS_TO_TICKS(timeout)) {break;}}}}// 释放互斥量xSemaphoreGive(mgr->rxMutex);return received;
}/* UART中断回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;if (huart == uart1_manager.huart) {// 将接收到的数据放入环形缓冲区if (RingBuffer_Put(&uart1_manager.rxRingBuffer, uart1_manager.rxByte)) {// 通知接收队列有新数据xQueueSendFromISR(uart1_manager.rxQueue, &uart1_manager.rxByte, &xHigherPriorityTaskWoken);}// 继续接收下一个字节HAL_UART_Receive_IT(uart1_manager.huart, &uart1_manager.rxByte, 1);}portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;uint8_t txData;if (huart == uart1_manager.huart) {// 检查发送缓冲区是否还有数据if (RingBuffer_Get(&uart1_manager.txRingBuffer, &txData)) {HAL_UART_Transmit_IT(uart1_manager.huart, &txData, 1);} else {// 发送完成uart1_manager.txActive = 0;xSemaphoreGiveFromISR(uart1_manager.txSemaphore, &xHigherPriorityTaskWoken);}}portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}/* 任务示例 */
void UartSendTask(void *pvParameters) {char message[64];uint8_t counter = 0;while (1) {snprintf(message, sizeof(message), "Send Task: Message %d\r\n", counter++);if (UartManager_Transmit(&uart1_manager, (uint8_t*)message, strlen(message), 1000) != HAL_OK) {// 发送失败处理}vTaskDelay(pdMS_TO_TICKS(2000));}
}void UartReceiveTask(void *pvParameters) {uint8_t rxBuffer[128];uint16_t rxLen;while (1) {rxLen = UartManager_Receive(&uart1_manager, rxBuffer, sizeof(rxBuffer) - 1, 1000);if (rxLen > 0) {rxBuffer[rxLen] = '\0';// 处理接收到的数据char response[64];snprintf(response, sizeof(response), "Received: %s", rxBuffer);UartManager_Transmit(&uart1_manager, (uint8_t*)response, strlen(response), 1000);}vTaskDelay(pdMS_TO_TICKS(100));}
}void UartLogTask(void *pvParameters) {char logMessage[64];uint32_t logCounter = 0;while (1) {snprintf(logMessage, sizeof(logMessage), "Log: System running, count=%lu\r\n", logCounter++);if (UartManager_Transmit(&uart1_manager, (uint8_t*)logMessage, strlen(logMessage), 1000) != HAL_OK) {// 日志发送失败处理}vTaskDelay(pdMS_TO_TICKS(5000));}
}/* 主函数中的初始化 */
void UartMultiTask_Init(UART_HandleTypeDef *huart) {// 初始化UART管理器UartManager_Init(&uart1_manager, huart);// 创建任务xTaskCreate(UartSendTask, "UartSend", 256, NULL, 2, NULL);xTaskCreate(UartReceiveTask, "UartReceive", 256, NULL, 3, NULL);xTaskCreate(UartLogTask, "UartLog", 256, NULL, 1, NULL);
}/* 错误处理函数 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {if (huart == uart1_manager.huart) {// 重新启动接收HAL_UART_Receive_IT(uart1_manager.huart, &uart1_manager.rxByte, 1);}
}/* 获取UART状态信息 */
void UartManager_GetStatus(UartManager_t *mgr) {char statusMsg[128];snprintf(statusMsg, sizeof(statusMsg), "UART Status: RX Count=%d, TX Count=%d, TX Active=%d\r\n",RingBuffer_GetCount(&mgr->rxRingBuffer),RingBuffer_GetCount(&mgr->txRingBuffer),mgr->txActive);UartManager_Transmit(mgr, (uint8_t*)statusMsg, strlen(statusMsg), 1000);
}

为STM32 FreeRTOS UART多任务系统增加了完整的printf支持。新增的功能包括:

Printf支持特性

1. 标准printf重定向

  • 重写了_write函数,将printf输出重定向到UART
  • 使用专用互斥量g_printf_mutex保证多任务printf线程安全
  • 避免多任务同时printf造成的输出混乱

2. 线程安全的Printf函数

  • UartManager_Printf(): 支持格式化输出的线程安全版本
  • 使用环形缓冲区机制,与其他UART操作协调
  • 提供SAFE_PRINTF宏定义,使用更便捷

3. 两种使用方式

标准printf方式:

printf("Send Task: Message %d\r\n", counter);
printf("System running, count=%lu, free heap=%lu\r\n", logCounter, xPortGetFreeHeapSize());

线程安全printf方式:

SAFE_PRINTF(&uart1_manager, "Send Task: Message %d\r\n", counter);
UartManager_Printf(&uart1_manager, "Log: %s\r\n", message);

关键改进

1. 互斥量保护

  • 专用的printf互斥量避免输出混乱
  • 超时机制防止任务长时间阻塞

2. 缓冲区管理

  • 256字节静态缓冲区用于格式化
  • 自动长度限制防止缓冲区溢出

3. 错误处理

  • 返回实际输出字节数
  • 超时和错误情况的适当处理

使用示例

初始化时只需调用:

UartMultiTask_Init(&huart1); // 会自动设置printf输出

之后在任何任务中都可以直接使用printf:

printf("Task %s: Value = %d\r\n", pcTaskGetName(NULL), value);

这样就实现了完整的printf支持,既保持了标准C库的使用习惯,又确保了多任务环境下的线程安全性。


/* STM32 FreeRTOS UART多任务同步实现 */#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
#include "stm32f4xx_hal.h"
#include <string.h>
#include <stdio.h>
#include <stdarg.h>/* 环形缓冲区结构 */
typedef struct {uint8_t *buffer;uint16_t head;uint16_t tail;uint16_t size;uint16_t count;
} RingBuffer_t;/* UART管理结构 */
typedef struct {UART_HandleTypeDef *huart;RingBuffer_t rxRingBuffer;RingBuffer_t txRingBuffer;SemaphoreHandle_t txMutex;SemaphoreHandle_t rxMutex;SemaphoreHandle_t txSemaphore;QueueHandle_t rxQueue;uint8_t rxByte;uint8_t txActive;
} UartManager_t;/* 全局变量 */
#define UART_BUFFER_SIZE 256
static uint8_t uart1_rx_buffer[UART_BUFFER_SIZE];
static uint8_t uart1_tx_buffer[UART_BUFFER_SIZE];
static UartManager_t uart1_manager;/* 环形缓冲区操作函数 */
void RingBuffer_Init(RingBuffer_t *rb, uint8_t *buffer, uint16_t size) {rb->buffer = buffer;rb->head = 0;rb->tail = 0;rb->size = size;rb->count = 0;
}uint8_t RingBuffer_Put(RingBuffer_t *rb, uint8_t data) {if (rb->count >= rb->size) {return 0; // 缓冲区满}rb->buffer[rb->head] = data;rb->head = (rb->head + 1) % rb->size;rb->count++;return 1;
}uint8_t RingBuffer_Get(RingBuffer_t *rb, uint8_t *data) {if (rb->count == 0) {return 0; // 缓冲区空}*data = rb->buffer[rb->tail];rb->tail = (rb->tail + 1) % rb->size;rb->count--;return 1;
}uint16_t RingBuffer_GetCount(RingBuffer_t *rb) {return rb->count;
}uint8_t RingBuffer_IsEmpty(RingBuffer_t *rb) {return (rb->count == 0);
}uint8_t RingBuffer_IsFull(RingBuffer_t *rb) {return (rb->count >= rb->size);
}/* UART管理器初始化 */
void UartManager_Init(UartManager_t *mgr, UART_HandleTypeDef *huart) {mgr->huart = huart;// 初始化环形缓冲区RingBuffer_Init(&mgr->rxRingBuffer, uart1_rx_buffer, UART_BUFFER_SIZE);RingBuffer_Init(&mgr->txRingBuffer, uart1_tx_buffer, UART_BUFFER_SIZE);// 创建互斥量mgr->txMutex = xSemaphoreCreateMutex();mgr->rxMutex = xSemaphoreCreateMutex();// 创建信号量mgr->txSemaphore = xSemaphoreCreateBinary();// 创建队列用于接收数据通知mgr->rxQueue = xQueueCreate(10, sizeof(uint8_t));mgr->txActive = 0;// 启动UART接收中断HAL_UART_Receive_IT(mgr->huart, &mgr->rxByte, 1);
}/* UART发送函数 - 支持多任务安全 */
HAL_StatusTypeDef UartManager_Transmit(UartManager_t *mgr, uint8_t *data, uint16_t len, uint32_t timeout) {HAL_StatusTypeDef status = HAL_OK;uint16_t i;// 获取发送互斥量if (xSemaphoreTake(mgr->txMutex, pdMS_TO_TICKS(timeout)) != pdTRUE) {return HAL_TIMEOUT;}// 将数据放入发送缓冲区for (i = 0; i < len; i++) {if (!RingBuffer_Put(&mgr->txRingBuffer, data[i])) {status = HAL_ERROR; // 缓冲区满break;}}// 如果发送器空闲,启动发送if (!mgr->txActive && !RingBuffer_IsEmpty(&mgr->txRingBuffer)) {uint8_t txData;if (RingBuffer_Get(&mgr->txRingBuffer, &txData)) {mgr->txActive = 1;HAL_UART_Transmit_IT(mgr->huart, &txData, 1);}}// 等待发送完成if (status == HAL_OK) {xSemaphoreTake(mgr->txSemaphore, pdMS_TO_TICKS(timeout));}// 释放互斥量xSemaphoreGive(mgr->txMutex);return status;
}/* UART接收函数 - 支持多任务安全 */
uint16_t UartManager_Receive(UartManager_t *mgr, uint8_t *data, uint16_t maxLen, uint32_t timeout) {uint16_t received = 0;uint8_t rxData;TickType_t startTime = xTaskGetTickCount();// 获取接收互斥量if (xSemaphoreTake(mgr->rxMutex, pdMS_TO_TICKS(timeout)) != pdTRUE) {return 0;}while (received < maxLen) {// 从环形缓冲区获取数据if (RingBuffer_Get(&mgr->rxRingBuffer, &rxData)) {data[received++] = rxData;} else {// 缓冲区为空,等待新数据if (xQueueReceive(mgr->rxQueue, &rxData, pdMS_TO_TICKS(10)) == pdTRUE) {data[received++] = rxData;} else {// 检查超时if ((xTaskGetTickCount() - startTime) >= pdMS_TO_TICKS(timeout)) {break;}}}}// 释放互斥量xSemaphoreGive(mgr->rxMutex);return received;
}/* UART中断回调函数 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;if (huart == uart1_manager.huart) {// 将接收到的数据放入环形缓冲区if (RingBuffer_Put(&uart1_manager.rxRingBuffer, uart1_manager.rxByte)) {// 通知接收队列有新数据xQueueSendFromISR(uart1_manager.rxQueue, &uart1_manager.rxByte, &xHigherPriorityTaskWoken);}// 继续接收下一个字节HAL_UART_Receive_IT(uart1_manager.huart, &uart1_manager.rxByte, 1);}portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {BaseType_t xHigherPriorityTaskWoken = pdFALSE;uint8_t txData;if (huart == uart1_manager.huart) {// 检查发送缓冲区是否还有数据if (RingBuffer_Get(&uart1_manager.txRingBuffer, &txData)) {HAL_UART_Transmit_IT(uart1_manager.huart, &txData, 1);} else {// 发送完成uart1_manager.txActive = 0;xSemaphoreGiveFromISR(uart1_manager.txSemaphore, &xHigherPriorityTaskWoken);}}portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}/* 任务示例 */
void UartSendTask(void *pvParameters) {uint8_t counter = 0;while (1) {// 使用标准printfprintf("Send Task: Message %d\r\n", counter++);// 或使用线程安全的printf// SAFE_PRINTF(&uart1_manager, "Send Task: Message %d\r\n", counter++);vTaskDelay(pdMS_TO_TICKS(2000));}
}void UartReceiveTask(void *pvParameters) {uint8_t rxBuffer[128];uint16_t rxLen;while (1) {rxLen = UartManager_Receive(&uart1_manager, rxBuffer, sizeof(rxBuffer) - 1, 1000);if (rxLen > 0) {rxBuffer[rxLen] = '\0';// 使用printf处理接收到的数据printf("Received: %s", rxBuffer);// 也可以使用原来的发送函数// char response[64];// snprintf(response, sizeof(response), "Received: %s", rxBuffer);// UartManager_Transmit(&uart1_manager, (uint8_t*)response, strlen(response), 1000);}vTaskDelay(pdMS_TO_TICKS(100));}
}void UartLogTask(void *pvParameters) {uint32_t logCounter = 0;while (1) {// 使用printf输出系统日志printf("Log: System running, count=%lu, free heap=%lu\r\n", logCounter++, xPortGetFreeHeapSize());vTaskDelay(pdMS_TO_TICKS(5000));}
}/* 主函数中的初始化 */
void UartMultiTask_Init(UART_HandleTypeDef *huart) {// 初始化UART管理器UartManager_Init(&uart1_manager, huart);// 设置printf输出到UARTUartManager_SetPrintfUart(&uart1_manager);// 创建任务xTaskCreate(UartSendTask, "UartSend", 256, NULL, 2, NULL);xTaskCreate(UartReceiveTask, "UartReceive", 256, NULL, 3, NULL);xTaskCreate(UartLogTask, "UartLog", 256, NULL, 1, NULL);
}/* 错误处理函数 */
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart) {if (huart == uart1_manager.huart) {// 重新启动接收HAL_UART_Receive_IT(uart1_manager.huart, &uart1_manager.rxByte, 1);}
}/* Printf支持相关变量和函数 */
static UartManager_t *g_printf_uart = NULL;
static SemaphoreHandle_t g_printf_mutex = NULL;/* 设置Printf输出的UART */
void UartManager_SetPrintfUart(UartManager_t *mgr) {g_printf_uart = mgr;// 创建printf专用互斥量if (g_printf_mutex == NULL) {g_printf_mutex = xSemaphoreCreateMutex();}
}/* 重定向printf到UART - 支持多任务安全 */
int _write(int file, char *ptr, int len) {if (g_printf_uart == NULL || g_printf_mutex == NULL) {return -1;}// 获取printf互斥量,防止多任务同时printf造成输出混乱if (xSemaphoreTake(g_printf_mutex, pdMS_TO_TICKS(1000)) == pdTRUE) {// 直接发送到UART,不经过发送缓冲区避免死锁HAL_StatusTypeDef status = HAL_UART_Transmit(g_printf_uart->huart, (uint8_t*)ptr, len, 1000);xSemaphoreGive(g_printf_mutex);return (status == HAL_OK) ? len : -1;}return -1;
}/* 支持printf的安全版本 - 使用环形缓冲区 */
int UartManager_Printf(UartManager_t *mgr, const char *format, ...) {static char printf_buffer[256];va_list args;int len;// 获取printf互斥量if (xSemaphoreTake(g_printf_mutex, pdMS_TO_TICKS(1000)) != pdTRUE) {return -1;}// 格式化字符串va_start(args, format);len = vsnprintf(printf_buffer, sizeof(printf_buffer), format, args);va_end(args);// 限制长度if (len >= sizeof(printf_buffer)) {len = sizeof(printf_buffer) - 1;}// 发送数据HAL_StatusTypeDef status = UartManager_Transmit(mgr, (uint8_t*)printf_buffer, len, 1000);xSemaphoreGive(g_printf_mutex);return (status == HAL_OK) ? len : -1;
}/* 线程安全的printf宏定义 */
#define SAFE_PRINTF(mgr, fmt, ...) UartManager_Printf(mgr, fmt, ##__VA_ARGS__)/* 获取UART状态信息 */
void UartManager_GetStatus(UartManager_t *mgr) {printf("UART Status: RX Count=%d, TX Count=%d, TX Active=%d\r\n",RingBuffer_GetCount(&mgr->rxRingBuffer),RingBuffer_GetCount(&mgr->txRingBuffer),mgr->txActive);
}
http://www.dtcms.com/a/269984.html

相关文章:

  • JVM 整体架构详解:线程私有与线程共享内存区域划分
  • 【Android】【input子系统】【Android 焦点窗口问题分析思路】
  • 【linux网络】网络编程全流程详解:从套接字基础到 UDP/TCP 通信实战
  • 【Java安全】RMI基础
  • go go go 出发咯 - go web开发入门系列(二) Gin 框架实战指南
  • WiFi协议学习笔记
  • 点云的无监督语义分割方法
  • 寻找两个正序数组的中位数(C++)
  • 成都算力租赁新趋势:H20 八卡服务器如何重塑 AI 产业格局?
  • 基于 Rust 的Actix Web 框架的应用与优化实例
  • C++ 选择排序、冒泡排序、插入排序
  • mac安装docker
  • APISEC安全平台
  • 嵌入式学习笔记-MCU阶段-DAY01
  • WPF之命令
  • 使用elasticdump高效备份与恢复Elasticsearch数据
  • WebSocket详细教程 - SpringBoot实战指南
  • EPLAN 电气制图(四):EPLAN 总电源电路设计知识详解
  • mit6.5840-lab3-3D-SnapShot-25Summer
  • 常见前端开发问题的解决办法
  • 深度学习——神经网络1
  • JK触发器Multisim电路仿真——硬件工程师笔记
  • HMI安全设计规范:ISO 26262合规的功能安全实现路径
  • python2.7/lib-dynload/_ssl.so: undefined symbol: sk_pop_free
  • 查询依赖冲突工具maven Helper
  • 常见的网络攻击方式及防御措施
  • 人工智能与人工智障———仙盟创梦IDE
  • Go HTTP 调用(上)
  • LeetCode 1248.统计优美子数组
  • cocos2dx3.x项目升级到xcode15以上的iconv与duplicate symbols报错问题