车载系统软件工程师如何处理车载系统的通信协议栈开发

microPython Python最小内核源码解析
NI-motion运动控制c语言示例代码解析
python编程示例系列 python编程示例系列二
python的Web神器Streamlit
如何应聘高薪职位

车载系统通信协议栈的开发是一个复杂的过程,涉及多个层次的协议和大量的细节。下面是一个详细的分析,介绍车载系统通信协议栈的开发步骤,并附上示例代码。

1. 分析需求

首先,需要明确车载系统需要支持的通信协议。常见的车载通信协议包括:

  • CAN (Controller Area Network)
  • LIN (Local Interconnect Network)
  • FlexRay
  • MOST (Media Oriented Systems Transport)
  • Ethernet AVB (Audio Video Bridging)

2. 协议栈的分层

协议栈通常分为多个层次,每一层负责不同的功能。以CAN协议为例,典型的协议栈包括:

  1. 物理层(Physical Layer):负责电气信号传输。
  2. 数据链路层(Data Link Layer):负责数据帧的传输、错误检测与纠正。
  3. 网络层(Network Layer):负责数据包的路由与转发。
  4. 传输层(Transport Layer):负责数据的传输与流控制。
  5. 应用层(Application Layer):负责具体应用的实现。

3. 开发步骤

3.1 物理层

物理层通常由硬件提供,软件工程师需要配置和初始化硬件,比如设置波特率等参数。

void CAN_Init(void) {
    // 假设使用某个特定的CAN硬件
    // 设置波特率、模式等参数
    CAN_SetBaudRate(500000);  // 设置波特率为500kbps
    CAN_Enable();             // 启用CAN硬件
}
3.2 数据链路层

数据链路层负责帧的传输和错误检测。

typedef struct {
    uint32_t id;
    uint8_t data[8];
    uint8_t length;
} CAN_Frame;

void CAN_SendFrame(CAN_Frame *frame) {
    // 填充硬件寄存器,发送数据帧
    CAN_SetID(frame->id);
    CAN_SetData(frame->data, frame->length);
    CAN_Transmit();  // 触发发送
}

void CAN_ReceiveFrame(CAN_Frame *frame) {
    // 从硬件寄存器读取接收到的数据帧
    frame->id = CAN_GetID();
    frame->length = CAN_GetData(frame->data);
}
3.3 网络层

网络层的实现取决于具体的应用需求,例如是否需要数据包的路由功能。

void CAN_RouteFrame(CAN_Frame *frame) {
    // 简单的路由逻辑,根据ID进行转发
    if (frame->id == TARGET_ID) {
        ProcessFrame(frame);
    } else {
        ForwardFrame(frame);
    }
}
3.4 传输层

传输层负责数据传输的可靠性和流控制。

void CAN_TransmitData(uint32_t id, uint8_t *data, uint8_t length) {
    CAN_Frame frame;
    frame.id = id;
    frame.length = length;
    memcpy(frame.data, data, length);
    CAN_SendFrame(&frame);
}

void CAN_ReceiveData(CAN_Frame *frame) {
    if (CAN_FrameAvailable()) {
        CAN_ReceiveFrame(frame);
        // 处理接收到的数据
        ProcessReceivedData(frame);
    }
}
3.5 应用层

应用层负责具体的应用逻辑,例如诊断、控制等。

void ProcessFrame(CAN_Frame *frame) {
    switch (frame->id) {
        case DIAGNOSTIC_ID:
            HandleDiagnostic(frame);
            break;
        case CONTROL_ID:
            HandleControl(frame);
            break;
        default:
            // 处理其他类型的消息
            break;
    }
}

void HandleDiagnostic(CAN_Frame *frame) {
    // 处理诊断消息
}

void HandleControl(CAN_Frame *frame) {
    // 处理控制消息
}

示例代码总结

下面是一个完整的示例代码,展示了如何初始化CAN硬件并发送和接收数据帧。

#include <stdint.h>
#include <string.h>

// 假设使用某个特定的CAN硬件库
#include "can_hardware.h"

#define TARGET_ID 0x123
#define DIAGNOSTIC_ID 0x7DF
#define CONTROL_ID 0x456

typedef struct {
    uint32_t id;
    uint8_t data[8];
    uint8_t length;
} CAN_Frame;

void CAN_Init(void) {
    CAN_SetBaudRate(500000);  // 设置波特率为500kbps
    CAN_Enable();             // 启用CAN硬件
}

void CAN_SendFrame(CAN_Frame *frame) {
    CAN_SetID(frame->id);
    CAN_SetData(frame->data, frame->length);
    CAN_Transmit();  // 触发发送
}

void CAN_ReceiveFrame(CAN_Frame *frame) {
    frame->id = CAN_GetID();
    frame->length = CAN_GetData(frame->data);
}

void CAN_TransmitData(uint32_t id, uint8_t *data, uint8_t length) {
    CAN_Frame frame;
    frame.id = id;
    frame.length = length;
    memcpy(frame.data, data, length);
    CAN_SendFrame(&frame);
}

void CAN_ReceiveData(CAN_Frame *frame) {
    if (CAN_FrameAvailable()) {
        CAN_ReceiveFrame(frame);
        ProcessFrame(frame);
    }
}

void ProcessFrame(CAN_Frame *frame) {
    switch (frame->id) {
        case DIAGNOSTIC_ID:
            HandleDiagnostic(frame);
            break;
        case CONTROL_ID:
            HandleControl(frame);
            break;
        default:
            // 处理其他类型的消息
            break;
    }
}

void HandleDiagnostic(CAN_Frame *frame) {
    // 处理诊断消息
}

void HandleControl(CAN_Frame *frame) {
    // 处理控制消息
}

int main(void) {
    CAN_Init();

    // 示例:发送一个控制消息
    uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
    CAN_TransmitData(CONTROL_ID, data, sizeof(data));

    // 主循环,接收并处理CAN消息
    while (1) {
        CAN_Frame frame;
        CAN_ReceiveData(&frame);
    }

    return 0;
}

总结

车载系统通信协议栈的开发涉及多个层次的实现,每一层负责不同的功能。通过分层的方式,可以清晰地组织代码,并且每一层的实现可以独立测试和调试。上述示例代码展示了如何初始化CAN硬件并进行基本的发送和接收操作,可以作为开发更复杂协议栈的基础。

4. 协议栈的调试和测试

开发完成后,协议栈需要经过严格的调试和测试,以确保其稳定性和可靠性。以下是一些常见的方法和工具:

4.1 单元测试

对每一层的功能进行单元测试,确保各个模块的正确性。例如,可以使用模拟框架(mock framework)来模拟硬件接口并测试数据链路层的功能。

void Test_CAN_SendFrame(void) {
    CAN_Frame frame = { .id = 0x123, .data = {0x01, 0x02, 0x03, 0x04}, .length = 4 };
    Mock_CAN_SetID(frame.id);
    Mock_CAN_SetData(frame.data, frame.length);
    CAN_SendFrame(&frame);
    // 验证Mock函数是否被正确调用
    ASSERT(Mock_CAN_SetID_CalledWith(frame.id));
    ASSERT(Mock_CAN_SetData_CalledWith(frame.data, frame.length));
}
4.2 集成测试

在真实的硬件环境中进行集成测试,确保各层之间的协同工作正常。例如,可以使用CAN分析仪(CAN analyzer)来监控总线上的数据帧,验证发送和接收的正确性。

4.3 回归测试

在协议栈的开发和维护过程中,回归测试非常重要,确保新功能或修改不会引入新的问题。可以通过自动化测试脚本实现。

5. 性能优化

通信协议栈通常需要在资源受限的环境中运行,因此性能优化非常重要。以下是一些常见的优化方法:

5.1 减少中断延迟

在数据链路层,使用中断处理接收和发送完成信号,但要注意减少中断处理的时间,避免阻塞其他任务。

void CAN_IRQHandler(void) {
    if (CAN_ReceiveInterruptFlag()) {
        CAN_Frame frame;
        CAN_ReceiveFrame(&frame);
        // 将处理移到任务中,减少中断处理时间
        Queue_Push(&frame);
    }
}
5.2 使用DMA

在传输大量数据时,可以使用DMA(Direct Memory Access)来减轻CPU的负担。

void CAN_TransmitData_DMA(uint32_t id, uint8_t *data, uint8_t length) {
    CAN_Frame frame;
    frame.id = id;
    frame.length = length;
    memcpy(frame.data, data, length);
    CAN_SendFrame_DMA(&frame);  // 使用DMA发送
}
5.3 高效的内存管理

使用静态内存分配或内存池(memory pool)来减少动态内存分配的开销和碎片。

#define MAX_FRAMES 10

CAN_Frame frame_pool[MAX_FRAMES];
uint8_t frame_pool_index = 0;

CAN_Frame* AllocateFrame(void) {
    if (frame_pool_index < MAX_FRAMES) {
        return &frame_pool[frame_pool_index++];
    }
    return NULL;  // 内存池已满
}

void FreeFrame(CAN_Frame* frame) {
    // 简单的内存池回收机制
    if (frame_pool_index > 0) {
        frame_pool_index--;
    }
}

6. 安全性和鲁棒性

车载系统的安全性和鲁棒性非常重要,需要考虑以下方面:

6.1 错误处理

在每一层实现错误检测和处理机制,确保系统能够在错误情况下继续运行或安全地停止。

void CAN_ReceiveFrame(CAN_Frame *frame) {
    if (CAN_FrameAvailable()) {
        frame->id = CAN_GetID();
        frame->length = CAN_GetData(frame->data);
        if (CAN_CheckError()) {
            // 处理错误
            HandleError();
        }
    }
}
6.2 安全通信

对于敏感数据,考虑使用加密和认证机制,防止数据被篡改或窃取。

6.3 容错设计

设计系统时考虑到硬件故障和软件异常,增加冗余和备份机制。

7. 示例代码改进

结合上述优化和安全性考虑,改进后的示例代码如下:

#include <stdint.h>
#include <string.h>
#include "can_hardware.h"

#define TARGET_ID 0x123
#define DIAGNOSTIC_ID 0x7DF
#define CONTROL_ID 0x456
#define MAX_FRAMES 10

typedef struct {
    uint32_t id;
    uint8_t data[8];
    uint8_t length;
} CAN_Frame;

CAN_Frame frame_pool[MAX_FRAMES];
uint8_t frame_pool_index = 0;

CAN_Frame* AllocateFrame(void) {
    if (frame_pool_index < MAX_FRAMES) {
        return &frame_pool[frame_pool_index++];
    }
    return NULL;  // 内存池已满
}

void FreeFrame(CAN_Frame* frame) {
    if (frame_pool_index > 0) {
        frame_pool_index--;
    }
}

void CAN_Init(void) {
    CAN_SetBaudRate(500000);  // 设置波特率为500kbps
    CAN_Enable();             // 启用CAN硬件
}

void CAN_SendFrame(CAN_Frame *frame) {
    CAN_SetID(frame->id);
    CAN_SetData(frame->data, frame->length);
    CAN_Transmit();  // 触发发送
}

void CAN_ReceiveFrame(CAN_Frame *frame) {
    if (CAN_FrameAvailable()) {
        frame->id = CAN_GetID();
        frame->length = CAN_GetData(frame->data);
        if (CAN_CheckError()) {
            // 处理错误
            HandleError();
        }
    }
}

void CAN_TransmitData(uint32_t id, uint8_t *data, uint8_t length) {
    CAN_Frame *frame = AllocateFrame();
    if (frame != NULL) {
        frame->id = id;
        frame->length = length;
        memcpy(frame->data, data, length);
        CAN_SendFrame(frame);
        FreeFrame(frame);
    }
}

void CAN_ReceiveData(void) {
    CAN_Frame frame;
    if (CAN_FrameAvailable()) {
        CAN_ReceiveFrame(&frame);
        ProcessFrame(&frame);
    }
}

void ProcessFrame(CAN_Frame *frame) {
    switch (frame->id) {
        case DIAGNOSTIC_ID:
            HandleDiagnostic(frame);
            break;
        case CONTROL_ID:
            HandleControl(frame);
            break;
        default:
            // 处理其他类型的消息
            break;
    }
}

void HandleDiagnostic(CAN_Frame *frame) {
    // 处理诊断消息
}

void HandleControl(CAN_Frame *frame) {
    // 处理控制消息
}

void HandleError(void) {
    // 错误处理逻辑
}

int main(void) {
    CAN_Init();

    // 示例:发送一个控制消息
    uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
    CAN_TransmitData(CONTROL_ID, data, sizeof(data));

    // 主循环,接收并处理CAN消息
    while (1) {
        CAN_ReceiveData();
    }

    return 0;
}

总结

车载系统通信协议栈的开发涉及多个层次的实现和优化。通过分层开发、严格测试、性能优化和安全性设计,可以构建一个可靠、高效的车载通信系统。上述示例代码展示了一个简单的CAN协议栈的实现思路,希望对开发者有所帮助。

8. 进阶功能

在基本功能实现的基础上,可以进一步扩展协议栈,添加一些高级功能和特性,以满足更复杂的应用需求。

8.1 多帧消息处理

对于一些大于一个CAN帧的数据,需要实现多帧消息的分片和重组。例如,UDS(Unified Diagnostic Services)协议在CAN总线上传输时,经常需要处理多帧消息。

typedef struct {
    uint32_t id;
    uint8_t data[4096];  // 假设最大为4096字节
    uint16_t length;
} CAN_MultiFrameMessage;

void CAN_SendMultiFrameMessage(CAN_MultiFrameMessage *msg) {
    uint16_t remaining = msg->length;
    uint8_t seq = 0;

    while (remaining > 0) {
        CAN_Frame frame;
        frame.id = msg->id;
        frame.data[0] = seq++;  // 帧序号
        uint8_t chunk_size = (remaining > 7) ? 7 : remaining;
        memcpy(&frame.data[1], &msg->data[msg->length - remaining], chunk_size);
        frame.length = chunk_size + 1;
        CAN_SendFrame(&frame);
        remaining -= chunk_size;
    }
}

void CAN_ReceiveMultiFrameMessage(CAN_MultiFrameMessage *msg) {
    CAN_Frame frame;
    uint8_t expected_seq = 0;

    while (1) {
        CAN_ReceiveFrame(&frame);
        if (frame.data[0] == expected_seq) {
            uint8_t chunk_size = frame.length - 1;
            memcpy(&msg->data[msg->length], &frame.data[1], chunk_size);
            msg->length += chunk_size;
            expected_seq++;
            if (chunk_size < 7) {
                // 最后一帧
                break;
            }
        } else {
            // 错误处理
            HandleError();
            break;
        }
    }
}
8.2 诊断和监控功能

车载系统需要实时监控和诊断功能。例如,监控总线负载、错误帧计数等。

typedef struct {
    uint32_t error_count;
    uint32_t bus_load;
} CAN_Diagnostics;

CAN_Diagnostics diagnostics;

void CAN_ErrorHandler(void) {
    diagnostics.error_count++;
    // 错误处理逻辑
}

void CAN_MonitorBusLoad(void) {
    // 假设有一个方法可以获取当前总线负载
    diagnostics.bus_load = CAN_GetBusLoad();
}
8.3 时间戳和同步

在一些实时系统中,需要对消息进行时间戳和同步处理。可以在接收帧时记录时间戳。

typedef struct {
    uint32_t id;
    uint8_t data[8];
    uint8_t length;
    uint32_t timestamp;  // 时间戳
} CAN_TimestampedFrame;

void CAN_ReceiveTimestampedFrame(CAN_TimestampedFrame *frame) {
    if (CAN_FrameAvailable()) {
        frame->id = CAN_GetID();
        frame->length = CAN_GetData(frame->data);
        frame->timestamp = CAN_GetTimestamp();  // 获取时间戳
        if (CAN_CheckError()) {
            // 处理错误
            HandleError();
        }
    }
}

9. 安全性增强

为了提高车载系统的安全性,可以采用以下方法:

9.1 消息认证

使用消息认证码(MAC)来验证消息的完整性和真实性。

void CAN_SendAuthenticatedFrame(CAN_Frame *frame, uint8_t *key) {
    uint8_t mac[8];
    ComputeMAC(frame->data, frame->length, key, mac);
    memcpy(&frame->data[frame->length], mac, sizeof(mac));
    frame->length += sizeof(mac);
    CAN_SendFrame(frame);
}

void CAN_ReceiveAuthenticatedFrame(CAN_Frame *frame, uint8_t *key) {
    if (CAN_FrameAvailable()) {
        frame->id = CAN_GetID();
        frame->length = CAN_GetData(frame->data);
        uint8_t received_mac[8];
        memcpy(received_mac, &frame->data[frame->length - sizeof(received_mac)], sizeof(received_mac));
        uint8_t computed_mac[8];
        ComputeMAC(frame->data, frame->length - sizeof(received_mac), key, computed_mac);
        if (memcmp(received_mac, computed_mac, sizeof(received_mac)) == 0) {
            // 认证成功
        } else {
            // 认证失败,处理错误
            HandleError();
        }
    }
}
9.2 数据加密

对于敏感数据,可以使用加密算法进行保护。

void CAN_SendEncryptedFrame(CAN_Frame *frame, uint8_t *key) {
    EncryptData(frame->data, frame->length, key);
    CAN_SendFrame(frame);
}

void CAN_ReceiveEncryptedFrame(CAN_Frame *frame, uint8_t *key) {
    if (CAN_FrameAvailable()) {
        frame->id = CAN_GetID();
        frame->length = CAN_GetData(frame->data);
        DecryptData(frame->data, frame->length, key);
        if (CAN_CheckError()) {
            // 处理错误
            HandleError();
        }
    }
}

10. 完整示例代码

综合上述功能和优化,下面是一个更完整的示例代码:

#include <stdint.h>
#include <string.h>
#include "can_hardware.h"
#include "security.h"

#define TARGET_ID 0x123
#define DIAGNOSTIC_ID 0x7DF
#define CONTROL_ID 0x456
#define MAX_FRAMES 10

typedef struct {
    uint32_t id;
    uint8_t data[8];
    uint8_t length;
    uint32_t timestamp;
} CAN_TimestampedFrame;

typedef struct {
    uint32_t id;
    uint8_t data[4096];
    uint16_t length;
} CAN_MultiFrameMessage;

typedef struct {
    uint32_t error_count;
    uint32_t bus_load;
} CAN_Diagnostics;

CAN_Frame frame_pool[MAX_FRAMES];
uint8_t frame_pool_index = 0;
CAN_Diagnostics diagnostics;

CAN_Frame* AllocateFrame(void) {
    if (frame_pool_index < MAX_FRAMES) {
        return &frame_pool[frame_pool_index++];
    }
    return NULL;  // 内存池已满
}

void FreeFrame(CAN_Frame* frame) {
    if (frame_pool_index > 0) {
        frame_pool_index--;
    }
}

void CAN_Init(void) {
    CAN_SetBaudRate(500000);  // 设置波特率为500kbps
    CAN_Enable();             // 启用CAN硬件
}

void CAN_SendFrame(CAN_Frame *frame) {
    CAN_SetID(frame->id);
    CAN_SetData(frame->data, frame->length);
    CAN_Transmit();  // 触发发送
}

void CAN_ReceiveFrame(CAN_Frame *frame) {
    if (CAN_FrameAvailable()) {
        frame->id = CAN_GetID();
        frame->length = CAN_GetData(frame->data);
        if (CAN_CheckError()) {
            diagnostics.error_count++;
            HandleError();
        }
    }
}

void CAN_SendMultiFrameMessage(CAN_MultiFrameMessage *msg) {
    uint16_t remaining = msg->length;
    uint8_t seq = 0;

    while (remaining > 0) {
        CAN_Frame frame;
        frame.id = msg->id;
        frame.data[0] = seq++;  // 帧序号
        uint8_t chunk_size = (remaining > 7) ? 7 : remaining;
        memcpy(&frame.data[1], &msg->data[msg->length - remaining], chunk_size);
        frame.length = chunk_size + 1;
        CAN_SendFrame(&frame);
        remaining -= chunk_size;
    }
}

void CAN_ReceiveMultiFrameMessage(CAN_MultiFrameMessage *msg) {
    CAN_Frame frame;
    uint8_t expected_seq = 0;

    while (1) {
        CAN_ReceiveFrame(&frame);
        if (frame.data[0] == expected_seq) {
            uint8_t chunk_size = frame.length - 1;
            memcpy(&msg->data[msg->length], &frame.data[1], chunk_size);
            msg->length += chunk_size;
            expected_seq++;
            if (chunk_size < 7) {
                // 最后一帧
                break;
            }
        } else {
            HandleError();
            break;
        }
    }
}

void CAN_TransmitData(uint32_t id, uint8_t *data, uint8_t length) {
    CAN_Frame *frame = AllocateFrame();
    if (frame != NULL) {
        frame->id = id;
        frame->length = length;
        memcpy(frame->data, data, length);
        CAN_SendFrame(frame);
        FreeFrame(frame);
    }
}

void CAN_ReceiveData(void) {
    CAN_Frame frame;
    if (CAN_FrameAvailable()) {
        CAN_ReceiveFrame(&frame);
        ProcessFrame(&frame);
    }
}

void ProcessFrame(CAN_Frame *frame) {
    switch (frame->id) {
        case DIAGNOSTIC_ID:
            HandleDiagnostic(frame);
            break;
        case CONTROL_ID:
            HandleControl(frame);
            break;
        default:
            // 处理其他类型的消息
            break;
    }
}

void HandleDiagnostic(CAN_Frame *frame) {
    // 处理诊断消息
}

void HandleControl(CAN_Frame *frame) {
    // 处理控制消息
}

void HandleError(void) {
    // 错误处理逻辑
}

void CAN_MonitorBusLoad(void) {
    diagnostics.bus_load = CAN_GetBusLoad();
}

int main(void) {
    CAN_Init();

    // 示例:发送一个控制消息
    uint8_t data[] = {0x01, 0x02, 0x03, 0x04};
    CAN_TransmitData(CONTROL_ID, data, sizeof(data));

    // 主循环,接收并处理CAN消息
    while (1) {
        CAN_ReceiveData();
        CAN_MonitorBusLoad();
    }

    return 0;
}

总结

通过上述详细分析和示例代码,可以看出车载系统通信协议栈的开发是一个复杂的过程,需要考虑多层次的功能实现和优化。希望这些内容对车载系统软件工程师处理车载系统的通信协议栈开发有所帮助。
c#如何开发一个linux远程终端工具,类似putty
microPython的源码解析之 objset.c
python的Arcade 库如何安装使用以及功能和用途
python处理网格数据的一个库GridDataFormats
microPython的源码解析之 pairheap.c
微软通过openai巩固其在软件领域霸权地位
NI-Motion如何控制运动控制器上轴速度,通过读取模拟数字转换器(ADC)的值来动态调整速度C语言示例代码
python 获取文件夹下文件列表(不递归)
D-Link Australia利用Python控制固件更新
一个好的编程接口需要具备哪些要素
Union Investment如何利用Python和机器学习(ML)技术来改进其投资流程
python的OS库如何使用
microPython的源码解析之 reader.c
python的aria2p库介绍
运动控制卡
python hello world
Union Investment如何利用Python和机器学习(ML)技术来改进其投资流程
构建我们的Python代码库依赖图
c++加QT,如何动态股票实时行情均值,比如动态10个行情点均值
开源的全文搜索引擎Elasticsearch
python加PyQT如何开发一个端口扫描工具
microPython的源码解析之 showbc.c
jupyter 深度理解四 之pixiedust
Python如何从新浪财经爬去分价表数据
python的gradio库如何使用
python如何操作git库
chatGPT如何与工业软件领域结合
python的locale模块
Python如何调用pygame库来启动摄像头捕获图像并显示
NI-Motion通过National Instruments的FlexMotion软件控制运动控制卡上的轴进行运动C语言代码示例
Python 用于协作机器人
MicroPython在STM32微控制器上的启动逻辑的实现
详细解读一下c++模版编程,并举例
研究人员发现了一种影响支持推测执行的现代CPU架构的新数据泄露攻击。
自动化工具软件详细列举
openai和alphago什么关系
使用Python进行前沿依赖测试
Python如何测网速
WordStream 选择 Python 作为他们的平台
python的Cirq库如何使用
量化交易策略 背离策略
python的imaplib
python web应用开发神器 入门十四
C++模版元编程 和模版编程有啥区别
几种设计模式在Python开发中的应用
microPython的源码解析之 modgc.c
python 如何实现语法高亮
如何使用openai生成图像 请给出示例代码
microPython的源码解析之 builtinimport.c
python的shutil库如何使用
python的smtplib
python 如何播放声音
Python如何在意想不到的方式中发挥作用,尤其是在嵌入式系统开发中的应用。
NI-Motion 如何使用圆弧插补功能来移动控制器上的轴,C语言示例代码
python的Pybullet库如何安装使用以及功能和用途,请给出详细示例代码,并用中文回答
c++加QT如何操作RS485接口
microPython的源码解析之 objtype.c
c#程序与USB HID设备进行通信
microPython的源码解析之 objstrunicode.c
c#语言利用GPU进行加速计算
如何用python开发一个linux终端
气象学家如何利用Python
python如何快速创建交互式应用程序
openai的API实现代码函数检索
python 的pyglet库如何使用
microPython的源码解析之 modcmath.c
openai参数数量是如何计算出来,举个计算例子,比如ada模型
通过 Einblick 可视化画布中的基于 Python 的操作符重新构想数据科学
如何应聘数据处理专员,年薪大致在78000元到156000元之间
python蓝牙设备通信的功能的库python-lightblue
microPython的源码解析之 objreversed.c
Python如何计算字符串的显示宽度
microPython的源码解析之 asmx64.c
开源 AI库Stable Diffusion 介绍
NI-Motion如何在一个运动控制器上创建并运行一个简单的板载程序的C语言示例代码
python的一个打包工具cx_Freeze
python的库scipy介绍
Python如何把sqlite完全加载到内存中操作
python分布式系统技术集成的应用
腾讯有哪些人工智能相关的开源代码
windows的PC如何进行分布式AI计算
Electron框架介绍
python的Bokeh库如何使用
linux的命令体系有什么优势
python的markdown2库的使用
如何应聘普通测试工程师
python的sympy库介绍
python web应用开发神器 入门十八
Python的opencv库进行图像分割
量化交易策略 做多做空策略
NI-Motion 如何设置一个或多个断点,并通过 RTSI 线路(实时信号接口)来监控和响应这些断点的C语言示例代码
C++加QT如何实现RS232的高速通信?
linux如何开发一些自定义命令
c#的Cloo 库介绍
microPython的源码解析之 objstr.c
如何控制多部手机进行同时测试,俗称群控
R语言和python语言的区别在什么地方,各自的应用场景是什么
如何应聘医疗领域高级软件工程师,年薪范围大约在 21 万至 50 万元人民币
python的webbrowser库如何使用
python的任务调度库 Advanced Python Scheduler (APScheduler)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

openwin_top

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值