基于STM32的Modbus协议精简版程序

简介: 基于STM32的Modbus协议精简版程序

STM32 Modbus RTU从站程序,包含核心功能,易于理解和移植。

一、最简Modbus从站实现(单文件版)

modbus_slave.c

/**
 * @file modbus_slave.c
 * @brief STM32 Modbus RTU从站精简实现
 * @date 2024
 */

#include "stm32f10x.h"
#include <string.h>

/* Modbus配置 */
#define MODBUS_SLAVE_ADDR    0x01    // 从站地址
#define MODBUS_BAUDRATE      9600    // 波特率
#define REG_HOLDING_NUM      10      // 保持寄存器数量

/* 保持寄存器 */
static uint16_t holding_regs[REG_HOLDING_NUM] = {
   0};

/* 接收缓冲区 */
static uint8_t rx_buf[256];
static uint8_t rx_len = 0;
static uint8_t frame_complete = 0;

/* 函数声明 */
static uint16_t crc16(uint8_t *buf, uint16_t len);
static void send_response(uint8_t *buf, uint8_t len);
static void process_modbus_frame(void);

/**
 * @brief 初始化Modbus
 */
void Modbus_Init(void)
{
   
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    /* 1. 使能时钟 */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_AFIO, ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);

    /* 2. 配置GPIO */
    // TX: PA2
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // RX: PA3
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    /* 3. 配置USART */
    USART_InitStructure.USART_BaudRate = MODBUS_BAUDRATE;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
    USART_Init(USART2, &USART_InitStructure);

    /* 4. 使能接收中断 */
    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);

    /* 5. 配置NVIC */
    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    /* 6. 使能USART */
    USART_Cmd(USART2, ENABLE);

    /* 7. 初始化寄存器 */
    for(int i=0; i<REG_HOLDING_NUM; i++) {
   
        holding_regs[i] = i * 100;
    }
}

/**
 * @brief CRC16计算
 * @param buf 数据缓冲区
 * @param len 数据长度
 * @return CRC16值
 */
static uint16_t crc16(uint8_t *buf, uint16_t len)
{
   
    uint16_t crc = 0xFFFF;
    uint16_t i, j;

    for (i = 0; i < len; i++) {
   
        crc ^= buf[i];
        for (j = 0; j < 8; j++) {
   
            if (crc & 0x0001) {
   
                crc >>= 1;
                crc ^= 0xA001;
            } else {
   
                crc >>= 1;
            }
        }
    }
    return crc;
}

/**
 * @brief 发送响应
 * @param buf 数据缓冲区
 * @param len 数据长度
 */
static void send_response(uint8_t *buf, uint8_t len)
{
   
    uint16_t crc = crc16(buf, len);

    /* 发送数据 */
    for(uint8_t i=0; i<len; i++) {
   
        USART_SendData(USART2, buf[i]);
        while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
    }

    /* 发送CRC */
    USART_SendData(USART2, crc & 0xFF);
    while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
    USART_SendData(USART2, (crc >> 8) & 0xFF);
    while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
}

/**
 * @brief 处理Modbus帧
 */
static void process_modbus_frame(void)
{
   
    uint8_t addr = rx_buf[0];
    uint8_t func = rx_buf[1];
    uint16_t reg_addr, reg_num, value;
    uint8_t tx_buf[256];
    uint8_t tx_len = 0;

    /* 检查地址 */
    if(addr != MODBUS_SLAVE_ADDR && addr != 0x00) {
   
        return;  // 不是本机地址
    }

    /* 检查CRC */
    uint16_t recv_crc = (rx_buf[rx_len-1] << 8) | rx_buf[rx_len-2];
    if(crc16(rx_buf, rx_len-2) != recv_crc) {
   
        return;  // CRC错误
    }

    /* 广播地址不响应 */
    if(addr == 0x00) {
   
        return;
    }

    /* 处理功能码 */
    switch(func) {
   
        /* 读保持寄存器 (0x03) */
        case 0x03:
            reg_addr = (rx_buf[2] << 8) | rx_buf[3];
            reg_num  = (rx_buf[4] << 8) | rx_buf[5];

            if(reg_addr >= REG_HOLDING_NUM || 
               (reg_addr + reg_num) > REG_HOLDING_NUM) {
   
                // 返回异常
                tx_buf[0] = addr;
                tx_buf[1] = func | 0x80;
                tx_buf[2] = 0x02;  // 非法地址
                tx_len = 3;
                break;
            }

            tx_buf[0] = addr;
            tx_buf[1] = func;
            tx_buf[2] = reg_num * 2;  // 字节数
            tx_len = 3;

            for(uint16_t i=0; i<reg_num; i++) {
   
                tx_buf[tx_len++] = (holding_regs[reg_addr+i] >> 8) & 0xFF;
                tx_buf[tx_len++] = holding_regs[reg_addr+i] & 0xFF;
            }
            break;

        /* 写单个寄存器 (0x06) */
        case 0x06:
            reg_addr = (rx_buf[2] << 8) | rx_buf[3];
            value = (rx_buf[4] << 8) | rx_buf[5];

            if(reg_addr >= REG_HOLDING_NUM) {
   
                // 返回异常
                tx_buf[0] = addr;
                tx_buf[1] = func | 0x80;
                tx_buf[2] = 0x02;  // 非法地址
                tx_len = 3;
                break;
            }

            holding_regs[reg_addr] = value;

            // 回显请求
            memcpy(tx_buf, rx_buf, rx_len-2);
            tx_len = rx_len-2;
            break;

        /* 写多个寄存器 (0x10) */
        case 0x10:
            reg_addr = (rx_buf[2] << 8) | rx_buf[3];
            reg_num = (rx_buf[4] << 8) | rx_buf[5];
            uint8_t byte_count = rx_buf[6];

            if(reg_addr >= REG_HOLDING_NUM || 
               (reg_addr + reg_num) > REG_HOLDING_NUM ||
               byte_count != reg_num * 2) {
   
                // 返回异常
                tx_buf[0] = addr;
                tx_buf[1] = func | 0x80;
                tx_buf[2] = 0x02;  // 非法地址
                tx_len = 3;
                break;
            }

            // 写入数据
            uint8_t idx = 7;
            for(uint16_t i=0; i<reg_num; i++) {
   
                holding_regs[reg_addr+i] = (rx_buf[idx] << 8) | rx_buf[idx+1];
                idx += 2;
            }

            // 回显请求的前6个字节
            memcpy(tx_buf, rx_buf, 6);
            tx_len = 6;
            break;

        default:
            // 不支持的功能码
            tx_buf[0] = addr;
            tx_buf[1] = func | 0x80;
            tx_buf[2] = 0x01;  // 非法功能
            tx_len = 3;
            break;
    }

    /* 发送响应 */
    if(tx_len > 0) {
   
        send_response(tx_buf, tx_len);
    }
}

/**
 * @brief USART2中断处理函数
 */
void USART2_IRQHandler(void)
{
   
    if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) {
   
        uint8_t data = USART_ReceiveData(USART2);

        if(rx_len < sizeof(rx_buf)) {
   
            rx_buf[rx_len++] = data;
        }

        // 简单超时检测(实际应用中应使用定时器)
        static uint32_t last_time = 0;
        uint32_t now = SysTick->VAL;

        if(now - last_time > 1000) {
     // 简单超时判断
            frame_complete = 1;
        }
        last_time = now;

        USART_ClearITPendingBit(USART2, USART_IT_RXNE);
    }
}

/**
 * @brief 主循环调用,处理Modbus帧
 */
void Modbus_Poll(void)
{
   
    if(frame_complete && rx_len > 0) {
   
        process_modbus_frame();
        rx_len = 0;
        frame_complete = 0;
    }
}

/**
 * @brief 写保持寄存器
 * @param addr 寄存器地址
 * @param value 值
 */
void Modbus_WriteReg(uint16_t addr, uint16_t value)
{
   
    if(addr < REG_HOLDING_NUM) {
   
        holding_regs[addr] = value;
    }
}

/**
 * @brief 读保持寄存器
 * @param addr 寄存器地址
 * @return 寄存器值
 */
uint16_t Modbus_ReadReg(uint16_t addr)
{
   
    if(addr < REG_HOLDING_NUM) {
   
        return holding_regs[addr];
    }
    return 0;
}

main.c

#include "stm32f10x.h"
#include "modbus_slave.h"
#include <stdio.h>

/* 系统时钟配置 */
void SystemClock_Init(void)
{
   
    RCC_DeInit();
    RCC_HSEConfig(RCC_HSE_ON);
    while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET);

    RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9);
    RCC_PLLCmd(ENABLE);
    while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET);

    RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK);
    while(RCC_GetSYSCLKSource() != 0x08);

    SystemCoreClockUpdate();
}

/* 延时函数 */
void Delay_ms(uint32_t ms)
{
   
    for(uint32_t i=0; i<ms*8000; i++);
}

int main(void)
{
   
    /* 初始化系统 */
    SystemClock_Init();

    /* 初始化Modbus */
    Modbus_Init();

    /* 初始化寄存器示例值 */
    Modbus_WriteReg(0, 1234);   // 寄存器0
    Modbus_WriteReg(1, 5678);   // 寄存器1
    Modbus_WriteReg(2, 100);    // 寄存器2

    while(1)
    {
   
        /* 处理Modbus请求 */
        Modbus_Poll();

        /* 更新寄存器值(示例:模拟传感器数据) */
        static uint32_t counter = 0;
        if(++counter >= 1000) {
   
            counter = 0;
            uint16_t temp = Modbus_ReadReg(2);
            Modbus_WriteReg(2, temp + 1);  // 温度递增
        }

        Delay_ms(1);
    }
}

二、使用CubeMX生成的Modbus程序(HAL库版)

如果你使用STM32CubeMX生成代码,这是更现代的方式:

modbus.c (HAL库版本)

/* modbus.c - HAL库版本 */
#include "modbus.h"
#include <string.h>

#define MODBUS_ADDR     0x01
#define REG_COUNT       20

static uint16_t holding_regs[REG_COUNT] = {
   0};
static uint8_t rx_buffer[256];
static uint8_t rx_index = 0;
static uint32_t last_rx_time = 0;

/* CRC16计算 */
uint16_t Modbus_CRC16(uint8_t *data, uint16_t length)
{
   
    uint16_t crc = 0xFFFF;
    for(uint16_t i=0; i<length; i++) {
   
        crc ^= data[i];
        for(uint8_t j=0; j<8; j++) {
   
            if(crc & 0x0001) {
   
                crc >>= 1;
                crc ^= 0xA001;
            } else {
   
                crc >>= 1;
            }
        }
    }
    return crc;
}

/* 发送响应 */
void Modbus_SendResponse(uint8_t *data, uint16_t length)
{
   
    uint16_t crc = Modbus_CRC16(data, length);
    HAL_UART_Transmit(&huart2, data, length, 100);
    HAL_UART_Transmit(&huart2, (uint8_t*)&crc, 2, 100);
}

/* 处理Modbus请求 */
void Modbus_ProcessFrame(uint8_t *frame, uint16_t length)
{
   
    if(length < 4) return;

    uint8_t addr = frame[0];
    uint8_t func = frame[1];

    if(addr != MODBUS_ADDR && addr != 0x00) return;

    /* 检查CRC */
    uint16_t recv_crc = (frame[length-1] << 8) | frame[length-2];
    if(Modbus_CRC16(frame, length-2) != recv_crc) return;

    uint8_t tx_buffer[256];
    uint16_t tx_length = 0;

    switch(func) {
   
        case 0x03:  // 读保持寄存器
            {
   
                uint16_t start_addr = (frame[2] << 8) | frame[3];
                uint16_t reg_count = (frame[4] << 8) | frame[5];

                tx_buffer[0] = MODBUS_ADDR;
                tx_buffer[1] = 0x03;
                tx_buffer[2] = reg_count * 2;
                tx_length = 3;

                for(uint16_t i=0; i<reg_count; i++) {
   
                    if(start_addr + i < REG_COUNT) {
   
                        tx_buffer[tx_length++] = (holding_regs[start_addr+i] >> 8) & 0xFF;
                        tx_buffer[tx_length++] = holding_regs[start_addr+i] & 0xFF;
                    }
                }

                Modbus_SendResponse(tx_buffer, tx_length);
            }
            break;

        case 0x06:  // 写单个寄存器
            {
   
                uint16_t reg_addr = (frame[2] << 8) | frame[3];
                uint16_t reg_value = (frame[4] << 8) | frame[5];

                if(reg_addr < REG_COUNT) {
   
                    holding_regs[reg_addr] = reg_value;
                }

                // 回显
                memcpy(tx_buffer, frame, length-2);
                tx_length = length-2;
                Modbus_SendResponse(tx_buffer, tx_length);
            }
            break;
    }
}

/* UART接收完成回调 */
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
   
    if(huart->Instance == USART2) {
   
        uint32_t now = HAL_GetTick();

        if(now - last_rx_time > 5) {
     // 5ms超时
            rx_index = 0;
        }

        if(rx_index < sizeof(rx_buffer)) {
   
            rx_buffer[rx_index++] = huart->pRxBuffPtr[0];
        }

        last_rx_time = now;

        // 重新启动接收
        HAL_UART_Receive_IT(&huart2, huart->pRxBuffPtr, 1);
    }
}

/* Modbus轮询 */
void Modbus_Poll(void)
{
   
    uint32_t now = HAL_GetTick();

    if(rx_index > 0 && (now - last_rx_time) > 10) {
     // 10ms帧间隔
        Modbus_ProcessFrame(rx_buffer, rx_index);
        rx_index = 0;
    }
}

main.c (HAL库版本)

/* main.c - HAL库版本 */
#include "main.h"
#include "usart.h"
#include "gpio.h"
#include "modbus.h"

void SystemClock_Config(void);

int main(void)
{
   
    HAL_Init();
    SystemClock_Config();
    MX_GPIO_Init();
    MX_USART2_UART_Init();

    /* 启动UART接收中断 */
    uint8_t rx_byte;
    HAL_UART_Receive_IT(&huart2, &rx_byte, 1);

    /* 初始化寄存器 */
    holding_regs[0] = 1000;
    holding_regs[1] = 2000;
    holding_regs[2] = 3000;

    while(1) {
   
        Modbus_Poll();
        HAL_Delay(1);
    }
}

三、Modbus测试工具

1. 使用Modbus Poll测试

连接设置:
- 连接:Serial Port
- 串口:COM3 (根据你的实际端口)
- 波特率:9600
- 数据位:8
- 校验:None
- 停止位:1
- 模式:RTU

测试命令:
1. 读取保持寄存器:
   从站地址:1
   功能码:03
   起始地址:0
   数量:10

2. 写入单个寄存器:
   从站地址:1
   功能码:06
   寄存器地址:0
   值:1234

2. Python测试脚本

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
STM32 Modbus从站测试脚本
"""

import serial
import time
import struct

class ModbusTest:
    def __init__(self, port='COM3', baudrate=9600):
        self.ser = serial.Serial(
            port=port,
            baudrate=baudrate,
            bytesize=8,
            parity='N',
            stopbits=1,
            timeout=1
        )

    def crc16(self, data):
        """计算CRC16"""
        crc = 0xFFFF
        for byte in data:
            crc ^= byte
            for _ in range(8):
                if crc & 0x0001:
                    crc >>= 1
                    crc ^= 0xA001
                else:
                    crc >>= 1
        return crc.to_bytes(2, 'little')

    def read_holding_registers(self, slave_addr, start_addr, count):
        """读取保持寄存器"""
        # 构建请求帧
        frame = bytes([
            slave_addr,      # 从站地址
            0x03,            # 功能码
            start_addr >> 8, # 起始地址高字节
            start_addr & 0xFF, # 起始地址低字节
            count >> 8,      # 寄存器数量高字节
            count & 0xFF     # 寄存器数量低字节
        ])

        # 添加CRC
        frame += self.crc16(frame)

        # 发送请求
        self.ser.write(frame)
        time.sleep(0.1)

        # 读取响应
        response = self.ser.read(5 + count*2)
        return response

    def write_single_register(self, slave_addr, reg_addr, value):
        """写入单个寄存器"""
        frame = bytes([
            slave_addr,
            0x06,
            reg_addr >> 8,
            reg_addr & 0xFF,
            value >> 8,
            value & 0xFF
        ])

        frame += self.crc16(frame)
        self.ser.write(frame)
        time.sleep(0.1)
        return self.ser.read(8)

    def test(self):
        """测试函数"""
        print("开始Modbus测试...")

        # 测试1:读取寄存器
        print("测试1:读取保持寄存器")
        response = self.read_holding_registers(0x01, 0, 3)
        print(f"响应: {response.hex()}")

        # 测试2:写入寄存器
        print("\n测试2:写入寄存器")
        response = self.write_single_register(0x01, 0, 9999)
        print(f"响应: {response.hex()}")

        # 测试3:再次读取
        print("\n测试3:再次读取")
        response = self.read_holding_registers(0x01, 0, 1)
        print(f"响应: {response.hex()}")

        self.ser.close()

if __name__ == "__main__":
    tester = ModbusTest(port='COM3', baudrate=9600)
    tester.test()

参考代码 stm32-modbus协议程序 www.youwenfan.com/contentali/71977.html

四、常见问题解决

1. 通信不稳定

// 在USART初始化中添加:
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;

// 确保正确配置GPIO
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;  // TX
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  // RX

2. CRC计算错误

// 验证CRC计算
uint8_t test_data[] = {
   0x01, 0x03, 0x00, 0x00, 0x00, 0x01};
uint16_t crc = crc16(test_data, 6);
// 正确的CRC应该是:0x840A

3. 帧间隔检测

// 使用定时器精确检测3.5个字符时间
// 9600bps时,3.5个字符 ≈ 3.5ms
void TIM3_IRQHandler(void)
{
   
    if(TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {
   
        frame_complete = 1;  // 帧结束
        TIM_Cmd(TIM3, DISABLE);
    }
}

五、扩展功能

1. 添加更多功能码

case 0x01:  // 读线圈
case 0x02:  // 读离散输入
case 0x04:  // 读输入寄存器
case 0x05:  // 写单个线圈
case 0x0F:  // 写多个线圈
case 0x10:  // 写多个寄存器

2. 添加异常处理

#define MODBUS_EXCEPTION_ILLEGAL_FUNCTION  0x01
#define MODBUS_EXCEPTION_ILLEGAL_ADDRESS   0x02
#define MODBUS_EXCEPTION_ILLEGAL_VALUE     0x03
#define MODBUS_EXCEPTION_SLAVE_FAILURE     0x04

3. 添加寄存器映射表

typedef struct {
   
    uint16_t addr;
    uint16_t value;
    uint8_t  access;  // 0:只读, 1:读写
} REGISTER_MAP;

REGISTER_MAP reg_map[] = {
   
    {
   0x0000, 0x1234, 1},  // 可读写
    {
   0x0001, 0x5678, 0},  // 只读
    // ...
};

这个精简版Modbus程序可以直接在你的STM32F103上运行,支持最常用的03和06功能码。

目录
相关文章
|
21天前
|
自然语言处理 算法 测试技术
阿里云百炼Qwen 3.7 Plus vs Max:纯文本旗舰性能、成本与场景适配实与多模态全能的选型指南
2026年,大模型市场进入精细化竞争阶段,单一能力的模型已难以满足多元场景需求,厂商纷纷推出差异化产品线,在性能、成本、模态能力间寻找最优平衡。阿里云百炼平台推出的Qwen 3.7系列,包含Max与Plus两款旗舰模型,前者定位纯文本推理旗舰,后者主打多模态全能,二者共享百万级上下文窗口与超长自治执行能力,却在核心能力、价格与适用场景上形成鲜明差异。本文基于2026年最新实测数据,从核心参数、文本能力、多模态能力、智能体表现、性价比与场景选型六大维度,全面解析两款模型的差异,为个人开发者、企业用户提供精准选型参考,帮助在不同业务场景中实现能力与成本的最优匹配。
245 0
|
20天前
|
人工智能 运维 Prometheus
从 API 到 AI Agent:阿里云云监控 CLI + Agent Skill 实战
阿里云推出云监控CLI与Agent Skill,将运维能力转化为AI可执行工作流。用户通过自然语言指令,即可由Agent自动完成资源接入、告警管理及数据查询等任务,实现可控、可审计的智能化运维自动化。
398 129
|
20天前
|
中间件 开发工具 git
Coding Agent 下半场:从个人提效到组织级研发体系
Coding Agent 下半场聚焦组织级研发体系,本文围绕 AgentScope Harness 展开了沙箱隔离、会话恢复等通用架构,为企业提供工程化解决方案参考。
446 141
|
20天前
|
人工智能 弹性计算 JSON
基础设施到 Agent 体验丨从 Claude Fable 5 看安全护栏的演进
安全护栏被 Claude Fable 5 推向前台,也许会被应用于更加广泛的场景。你平时感受不到它,但当它生效的时候,它会告诉你。这将是护栏最好的状态。
302 124
|
20天前
|
人工智能 运维 安全
阿里云 Agent Infra 上长出的约束基建
Harness = 定义约束 + 校验输出 + 建立反馈回路。
299 125
|
20天前
|
人工智能 缓存 API
阿里云百炼 Token Plan 三大坐席对比:Credits资费额度、Token消耗与性价比分析
阿里云百炼TokenPlan含标准版(198元/月,2.5万Credits)、高级版(698元/月,10万Credits)和尊享版(1398元/月,25万Credits)。经测算,尊享版单Credits仅0.0056元,折合百万Tokens约1.12元,显著低于按量计费(2元/百万Tokens),性价比高,值得订阅。在阿里云百炼平台:https://t.aliyun.com/U/fPVHqY 免费领取千万Tokens
|
10天前
|
弹性计算 人工智能 运维
保姆级实操:阿里云ECS/轻量服务器完整部署Hermes Agent全流程手册
阿里云服务器部署Hermes Agent可解决本地部署的稳定性问题,实现全天候在线智能体服务。三种部署方案覆盖不同用户需求,一键部署适合新手,手动部署适合自定义配置。通过合理配置模型密钥、端口放行与运维优化,可长期稳定运行Hermes Agent,用于代码开发、自动化任务、数据处理等各类场景。
172 3
|
21天前
|
存储 缓存 人工智能
FlashMemory深度解析:DeepSeek-V4如何将1M上下文KV Cache压到10%
长上下文推理是大模型落地的核心痛点,传统Transformer的KV Cache随序列长度线性增长,1M token上下文在常规模型中需占用超80GB显存,直接导致长文本服务成本高企、部署门槛极高。2026年,DeepSeek-V4系列模型推出的FlashMemory技术,通过多层级压缩与混合存储架构,将1M上下文的KV Cache footprint从传统方案的83.9GB降至9.6GB,压缩比达**约1/10**,同时保持推理精度与速度优势,让1M上下文成为默认配置成为可能。本文从KV Cache瓶颈本质、FlashMemory核心架构、关键技术模块、代码实现到性能验证,全面解析这一长上下
274 2
|
20天前
|
人工智能 缓存 API
阿里云百炼TokenPlan值得买吗?标准版/高级版/尊享版Credits单价全对比,算完就清楚了
阿里云百炼TokenPlan值不值得买?本文对比标准版(198元/2.5万Credits)、高级版(698元/10万)、尊享版(1398元/25万)的Credits单价及折算百万Tokens成本(尊享版仅1.12元),并对比按量计费(2元/百万Tokens),结论:TokenPlan更划算,尤其适合高频AI使用者。在阿里云百炼官网:https://t.aliyun.com/U/fPVHqY 免费领取千万Tokens
192 0
|
20天前
|
人工智能 缓存 自然语言处理
阿里云百炼Token Plan团队版全解析:三档套餐怎么选?Credits计费规则一文搞懂
阿里云百炼Token Plan(团队版)是面向企业/团队的AI大模型订阅服务,以Credits统一计量,支持qwen3.6-plus、glm-5、qwen-image-2.0等文本与图像多模态模型,兼容Claude Code、OpenClaw等主流AI工具,提供标准(198元/月)、高级(698元)、尊享(1398元)三档坐席,预算可控、数据不用于训练,华北2地域可用。在阿里云百炼官网:https://t.aliyun.com/U/fPVHqY 免费领取千万Tokens
218 0