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

记录一次使用面向对象的C语言封装步进电机驱动

简介

(2025/4/21)

        

        本库对目前仅针对TB6600驱动下的42步进电机的基础功能进行了一定的封装, 也是我初次尝试以面向对象的思想去编写嵌入式代码, 和直流电机的驱动步骤相似在调用stepmotor_attach()函数和stepmotor_init()函数之后仅通过结构体数组stepm然后指定枚举变量中的id即可完成对步进电机的基础操作, 其中最核心的是控制函数step_move的实现, 该函数可以在开环状态下指定步进电机的步数频率(速度)进行控制, 后续可能会更新一些经典的控制模型如梯形加减速.
先贴一下项目代码
项目源码

cubemx 配置

如果要使用此库, 你只需要在cubemx中完成以下配置:

  1. 打开定时器的PWM通道并将 Prescaler设置为83(我的时钟主频为84分频后为1MHz, 这个并不是一定得是1MHz, 后面会说)

  2. 打开NVIC

  3. 配置相关的GPIO, 如方向引脚, 这个很简单我就不贴图了

移植

如果你的硬件平台和我一样, 无脑粘贴就行, 如果不一样, 则需要修改相关代码

  1. 修改类型(如果不是stm32HAL库)
    主要是硬件层结构体的类型声明, 和stepmotor_attach函数, 需要修改类型
typedef struct {// todo 硬件参数层TIM_HandleTypeDef* htim;    //定时器句柄uint32_t Channel;           // 输出通道GPIO_TypeDef* enType;       //使能引脚类别uint16_t  enPin;            //使能引脚pinGPIO_TypeDef* dirType;      //方向引脚类别uint16_t  dirPin;           //方向引脚pin
}STEPMOTOR_HARDWARE;
void stepmotor_attach(STEPID id,TIM_HandleTypeDef* htim,uint32_t Channel,           // 输出通道GPIO_TypeDef* enType,        //使能引脚类别uint16_t  enPin,             //使能引脚pinGPIO_TypeDef* dirType,      //方向引脚类别uint16_t  dirPin           //方向引脚pin);

    2. 底层接口函数封装
这里都添加在了step_motor.c中的部分以static声明的函数当中, 根据注释功能修改函数的实现:

static void en_set(STEPID id){// todo 使能置高STEPMOTOR_HARDWARE* hw = &stepmotorhws[id];hw->enType->BSRR = hw->enPin;
}
static void en_reset(STEPID id){// todo 使能置低STEPMOTOR_HARDWARE* hw = &stepmotorhws[id];hw->enType->BSRR = (uint32_t)hw->enPin << 16U;                 // 置位
}static void dir_set(STEPID id){// todo 方向置高STEPMOTOR_HARDWARE* hw = &stepmotorhws[id];hw->dirType->BSRR = hw->dirPin;
}
static void dir_reset(STEPID id){// todo 方向置低STEPMOTOR_HARDWARE* hw = &stepmotorhws[id];hw->dirType->BSRR = (uint32_t)hw->dirPin << 16U;                 // 置位
}static void tim_start(STEPID id){// todo 定时器启动STEPMOTOR_HARDWARE* hw = &stepmotorhws[id];hw->htim->Instance->CR1 |= TIM_CR1_CEN;
}
static void tim_stop(STEPID id){// todo 定时器停止并清0STEPMOTOR_HARDWARE* hw = &stepmotorhws[id];__HAL_TIM_SET_COUNTER(hw->htim, 0);__HAL_TIM_CLEAR_FLAG(hw->htim, TIM_FLAG_UPDATE);
}static void pwm_start(STEPID id){// todo pwm启动STEPMOTOR_HARDWARE* hw = &stepmotorhws[id];HAL_TIM_PWM_Start(hw->htim, hw->Channel);
}
static void pwm_stop(STEPID id){// todo pwm停止 其实将比较值赋为0就行STEPMOTOR_HARDWARE* hw = &stepmotorhws[id];HAL_TIM_PWM_Stop(hw->htim, hw->Channel);
}static void pwm_startIT(STEPID id){// todo pwm中断开启STEPMOTOR_HARDWARE* hw = &stepmotorhws[id];HAL_TIM_PWM_Start_IT(hw->htim, hw->Channel);
}
static void pwm_stopIT(STEPID id){// todo pwm中断停止STEPMOTOR_HARDWARE* hw = &stepmotorhws[id];HAL_TIM_PWM_Stop_IT(hw->htim, hw->Channel);
}
static void pwm_setcompare(STEPID id, int32_t freq){// todo 设置50%的占空比// todo 重新设置占空比(占空比永远为比较值的一半, 即50%占空比)STEPMOTOR_HARDWARE* hw = &stepmotorhws[id];if(freq > 0){hw->htim->Instance->ARR = Hclk/freq - 1;__HAL_TIM_SET_COMPARE(hw->htim, hw->Channel, hw->htim->Instance->ARR/2); // 占空比无所谓 一半即可}
}

    3. 频率宏定义
step_motor.h中的:

#define   Hclk       1000000  //时间总频

这个是预分频后的频率
根据实际情况来, 如果你是72MHz主频, 定时器的Prescaler设置为71, 则不用改, 因为分频后依然为1MHz
如果你的定时器的Prescaler值设置为0, 那么这个就是你的时钟主频了
这个值参与ARR的赋值计算(在**static void pwm_setcompare(STEPID id, int32_t freq);**中), 所以你得好好根据自己ARR的量程来, 像c8t6是65535最大, 那么你最好将Prescaler值给大一些              

    4. 中断修改(如果不是stm32HAL库)
具体的修改逻辑看看下面的步数&速度控制这个标题下的内容, 根据你的平台中断逻辑进行修改.

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{if(htim == stepm[SM1].hw->htim)    // 回调函数检测为某个步进电机对应的定时器{Stepper_UpdateHandler(SM1);}
}

初始化

成员变量概览

先看看一个对象所包含的属性, 这里我都敲了注释, 主要是使用STEPMOTOR作为对象的类型.

typedef struct {// todo 硬件参数层TIM_HandleTypeDef* htim;    //定时器句柄uint32_t Channel;           // 输出通道GPIO_TypeDef* enType;       //使能引脚类别uint16_t  enPin;            //使能引脚pinGPIO_TypeDef* dirType;      //方向引脚类别uint16_t  dirPin;           //方向引脚pin// todo 硬件API重写层STEPMOTOR_INTERFACE enSet;                // 方向引脚置位STEPMOTOR_INTERFACE enReset;                // 方向引脚置位STEPMOTOR_INTERFACE dirSet;                // 方向引脚置位STEPMOTOR_INTERFACE dirReset;                // 方向引脚置位STEPMOTOR_INTERFACE htimStop;              // 停止并复位定时器STEPMOTOR_INTERFACE htimStart;              // 重新启动定时器STEPMOTOR_INTERFACE pwmStop;                // pwm停止STEPMOTOR_INTERFACE pwmStart;               // pwm启动STEPMOTOR_INTERFACE pwmStopIT;             // pwm中断计数停止STEPMOTOR_INTERFACE pwmStartIT;            // pwm中断计数启动STEPMOTOR_SPEED_INTERFACE pwmSetCompare;          // pwm设置为50%的占空比}STEPMOTOR_HARDWARE;typedef struct{STEPMOTOR_HARDWARE* hw;  // 硬件接口封装层// todo 参数层uint32_t cur_freq;  // 当前频率Hzuint16_t cur_step;  // 当前步数uint8_t dir;        // 当前方向// todo 目标值uint16_t tar_step;  // 目标步数// todo 限制层uint8_t is_limit_step;          // 你是否要限制步数uint8_t is_finish;              // 是否完成路程int16_t accumulate_step;       // 累计步数int32_t max_step;              // 最大步数  - 配合累计步数以限幅int32_t min_step;              // 最小步数  - 配合累计步数以限幅uint32_t min_freq;             // 最小运行频率uint32_t max_freq;             // 最大运行频率// todo 函数接口层STEPMOTOR_INTERFACE limitStep;STEPMOTOR_INTERFACE noLimitStep;STEPMOTOR_STEP_INTERFACE stepMove;      // 指定步数和速度进行移动STEPMOTOR_INTERFACE stop;               // 立即停止STEPMOTOR_RANGE_INTERFACE setRange;     // 设置相关范围的接口}STEPMOTOR;

我们需要将step_motor.cstep_motor.h添加到你的工程目录下面, 然后调用初始化函数

void stepmotor_attach(STEPID id,                  // 电机idTIM_HandleTypeDef* htim,    // 电机对应定时器uint32_t Channel,           // 输出通道GPIO_TypeDef* enType,        //使能引脚类别uint16_t  enPin,             //使能引脚pinGPIO_TypeDef* dirType,      //方向引脚类别uint16_t  dirPin           //方向引脚pin);
stepmotor_init();

前者stepmotor_attach是引脚定向, 为了后面我们可以用结构体数组引出各种属性和函数进行调用, 以下是函数的实现流程

void stepmotor_attach(STEPID id,TIM_HandleTypeDef* htim,uint32_t Channel,           // 输出通道GPIO_TypeDef* enType,        //使能引脚类别uint16_t  enPin,             //使能引脚pinGPIO_TypeDef* dirType,      //方向引脚类别uint16_t  dirPin           //方向引脚pin){STEPMOTOR_HARDWARE* hw = &stepmotorhws[id];// todo 硬件引脚重指定hw->htim = htim;            // 硬件接口初始化hw->Channel = Channel;hw->enType = enType;hw->enPin = enPin;hw->dirType = dirType;hw->dirPin = dirPin;// todo 硬件函数接口hw->enSet = en_set;hw->enReset = en_reset;hw->dirSet = dir_set;hw->dirReset = dir_reset;hw->htimStart = tim_start;hw->htimStop = tim_stop;hw->pwmStop = pwm_stop;hw->pwmStart = pwm_start;hw->pwmStartIT = pwm_startIT;hw->pwmStopIT = pwm_stopIT;hw->pwmSetCompare = pwm_setcompare;// todo 控制接口函数stepm[id].hw = hw;stepm[id].limitStep = limit_step;stepm[id].noLimitStep = no_limit_step;stepm[id].stepMove = step_move;    // 移动函数接口赋值stepm[id].stop = step_stop;        // 立即停止接口指定stepm[id].setRange = set_range;    // 范围设置指向
}

stepmotor_init() 是初始化函数, 在这里cubemx已经初始化完成, 我只添加了相关外设如定时器中断等启动函数也可以添加自己的初始化代码.

void stepmotor_init(void){for(uint8_t i = 0; i < STEP_SUM; i++){STEPMOTOR* m = &stepm[i];                      // 获取对象指针// todo 其他初始化// todo 启动m->hw->pwmStartIT(i);   // PWM中断m->hw->enSet(i);        // 使能引脚使能}
}

使用库进行控制

步数&速度控制

目前没有添加太多的算法, 仅仅是开环的指定速度位移移动的函数, 不过这应该也是后续底层最为核心的函数:

    // 指定id             id      指定走多少步  指定速度/频率
stepm[STEPID].stepMove(STEPID, int32_t,     uint32_t);

仅仅是使用的话, 只需要传入对应电机对象id和相关参数即可, 其中第二个参数是用于指定目标是多少步, 支持正负号, 可以输入负值, 它意味着电机朝反方向转.
同时, 因为我们在初始化配置的时候开启了PWM中断所以在这里我们也需要在中断中添加部分代码:

void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim)
{if(htim == stepm[SM1].hw->htim)    // 回调函数检测为某个步进电机对应的定时器{Stepper_UpdateHandler(SM1);}
}

在中断回调函数HAL_TIM_PWM_PulseFinishedCallback中, 我们只需要重复执行Stepper_UpdateHandler函数就行, 关于这个函数的实现其实很简单, 下面我会说明.

stepMove函数的具体实现如下:

// todo 以指定速度(频率), 移动指定步数
static void step_move(STEPID id, int32_t tar_steps, uint32_t freq){STEPMOTOR* m = &stepm[id];if(tar_steps == 0 || freq <=0){m->is_finish = 1;return;}m->is_finish = 0;   // 刷新完成标志位// todo 停止并复位定时器m->hw->pwmStopIT(id);m->hw->htimStop(id);//todo 方向引脚置位m->dir = (tar_steps >= 0) ? 1 : 0;if(m->dir) m->hw->dirSet(id);else m->hw->dirReset(id);// todo 设置目标步数 并对其进行步数限幅if(m->is_limit_step){int32_t pre_accumulate_step = m->accumulate_step;  // 存储上一次的步数m->accumulate_step = (int16_t) LIMIT(m->accumulate_step + tar_steps, m->max_step, m->min_step);m->tar_step = abs( m->accumulate_step - pre_accumulate_step);}else{m->tar_step = tar_steps;}m->cur_step = 0;    // 每刷新一次当前步数置为0// todo 限制频率范围  注意符号和取值范围freq = LIMIT((int32_t)freq, (int32_t)m->max_freq, (int32_t)m->min_freq);m->cur_freq = freq; // 获取当前频率// todo 重新设置占空比(占空比永远为比较值的一半, 即50%占空比)m->hw->pwmSetCompare(id, freq);// todo 重新启动定时器m->hw->htimStart(id);// todo 启用更新中断m->hw->pwmStartIT(id);
}

以及中断函数

// todo (重要)定时器更新中断处理(需在stm32f4xx_it.c(或者中断回调函数)中调用)
void Stepper_UpdateHandler(STEPID id) {STEPMOTOR* m = &stepm[id];if(m->cur_step < m->tar_step) {m->cur_step++;} else {                            // todo 如果检测到当前步数到了目标值就直接PWM_Stop不发波了m->is_finish = 1;m->hw->pwmStopIT(id);}
}

每一步的操作我都敲了注释, 总体上的控制思路如下(这也是比较常用的控制思路):
通过PWM中断去对每一步进行计数存入成员变量cur_step中, 方波每个周期进一次中断, 同时步进电机收到一个脉冲, 电机走一步. 当电机的当前步数大于等于目标步数pwm停止.

电机停止

stepm[STEPID].stop(STEPID);

调用即可, 函数实现仅仅是让目标步数等于当前步数

// todo 立即停止电机
static void step_stop(STEPID id) {STEPMOTOR * m = &stepm[id];//__HAL_TIM_DISABLE_IT(m->hw->htim, TIM_IT_UPDATE);m->tar_step = m->cur_step;
}

设置范围

//                      id     模式    最大值      最小值
stepm[STEPID].setRange(STEPID, char , int32_t , int32_t );

这里提供了两种模式:

  • 'f': 设置速度/频率范围 -- 较为常见
  • 's': 设置最大累计步数范围 -- 我控制二维步进云台时用的, 防止云台跑到范围外, 对累计的步数进行限幅
// todo 立即停止电机
static void set_range(STEPID id, char mode, int32_t ma, int32_t mi){STEPMOTOR* m = &stepm[id];switch (mode) {case 'f':   // todo 设置频率范围m->max_freq = ma;m->min_freq = mi;break;case 's':m->max_step = ma;m->min_step = mi;break;}
}

是否启用位移限幅

主要是对标志位is_limit_step进行操作, 如果你不需要累计位移控制, 比如说小车的应用场景, 可以置位不再限幅

//todo 调用示例
stepm[STEPID].limitStep(STEPID);
stepm[STEPID].noLimitStep(STEPID);//todo 实现
static void limit_step(STEPID id){stepm[id].is_limit_step = 1;}
static void no_limit_step(STEPID id){stepm[id].is_finish = 0;}

相关文章:

记录一次使用面向对象的C语言封装步进电机驱动

简介 (2025/4/21) 本库对目前仅针对TB6600驱动下的42步进电机的基础功能进行了一定的封装, 也是我初次尝试以面向对象的思想去编写嵌入式代码, 和直流电机的驱动步骤相似在调用stepmotor_attach()函数和stepmotor_init()函数之后仅通过结构体数组stepm然后指定枚举变量中的id即…...

Spark-streaming核心编程

1.导入依赖‌&#xff1a; <dependency> <groupId>org.apache.spark</groupId> <artifactId>spark-streaming-kafka-0-10_2.12</artifactId> <version>3.0.0</version> </dependency> 2.编写代码‌&#xff1a; 创建Sp…...

Exposure Adjusted Incidence Rate (EAIR) 暴露调整发病率:精准量化疾病风险

1. 核心概念 1.1 传统发病率的局限性 1.1.1 公式与定义 传统发病率公式为新发病例数除以总人口数乘以观察时间。例如在某社区观察1年,有10例新发病例,总人口1000人,发病率即为10/10001=0.01。 此公式假设所有个体暴露时间和风险相同,但实际中个体差异大,如部分人暴露时间…...

vue3+TS+echarts 折线图

需要实现的效果如下 <script setup lang"ts" name"RepsSingleLineChart">import * as echarts from echartsimport { getInitecharts } from /utils/echartimport type { EChartsOption } from echarts// 定义 props 类型interface Props {id: strin…...

MYSQL中为什么不建议delete数据

在 MySQL 中不建议频繁使用 delete 删除数据的原因主要在于性能、数据安全等方面的问题&#xff0c;以下是具体介绍&#xff1a; 性能问题 磁盘空间与碎片&#xff1a;delete 操作只是将数据标记为 “已删除”&#xff0c;并不会立即释放磁盘空间&#xff0c;频繁执行会导致大量…...

Linux多线程技术

什么是线程 在一个程序里的多执行路线就是线程。线程是进程中的最小执行单元&#xff0c;可理解为 “进程内的一条执行流水线”。 进程和线程的区别 进程是资源分配的基本单位&#xff0c;线程是CPU调度的基本单位。 fork创建出一个新的进程&#xff0c;会创建出一个新的拷贝&…...

12个HPC教程汇总!从入门到实战,覆盖分子模拟/材料计算/生物信息分析等多个领域

在科学研究、工程仿真、人工智能和大数据分析等领域&#xff0c;高性能计算 (High Performance Computing, HPC) 正扮演着越来越重要的角色。它通过并行处理、大规模计算资源的整合&#xff0c;极大提升了计算效率&#xff0c;使原本耗时数日的任务能够在数小时内完成。 随着计…...

[OpenGL] Lambertian材质漫反射BRDF方程的解释与推导

一、简介 本文简单的介绍了 Physical Based Rendering, PBR 中的 Lambertian 材质漫反射BRDF公式 f r l a m b e r t i a n c d i f f π fr_{lambertian}\frac{c_{diff}}{\pi} frlambertian​πcdiff​​的推导。 二、漫反射项 根据 渲染方程&#xff1a; L o ( v ) ∫ …...

小火电视桌面TV版下载-小火桌面纯净版下载-官方历史版本安装包

别再费心地寻找小火桌面的官方历史版本安装包啦&#xff0c;试试乐看家桌面吧&#xff0c;它作为纯净版本的第三方桌面&#xff0c;具有诸多优点。 界面简洁纯净&#xff1a;乐看家桌面设计简洁流畅&#xff0c;页面简洁、纯净无广告&#xff0c;为用户打造了一个干净的电视操…...

VSFTPD+虚拟用户+SSL/TLS部署安装全过程(踩坑全通)

Author : Spinach | GHB Link : http://blog.csdn.net/bocai8058文章目录 前言准备配置虚拟用户1.创建虚拟用户列表文件2.生成数据库文件3.设置虚拟用户独立访问权限 配置PAM认证1.创建PAM配置文件2.测试PAM认证 创建虚拟用户映射的系统用户生成SSL/TLS证书配置VSFTPD服务1…...

07 Python 字符串全解析

文章目录 一. 字符串的定义二. 字符串的基本用法1. 访问字符串中的字符2. 字符串切片3. 字符串拼接4. 字符串重复5.字符串比较6.字符串成员运算 三. 字符串的常用方法1. len() 函数2. upper() 和 lower() 方法3. strip() 方法4. replace() 方法5. split() 方法 四. 字符串的进阶…...

androidstudio安装配置

B站配置视频AndroidStudio安装配置教程&#xff08;最新版本教程&#xff09;3分钟搞定 快速安装使用_哔哩哔哩_bilibili 1、环境变量 D:\AndroidSdk ANDROID_HOME ANDROID_SDK_HOME 2、新建 3、配置 distributionUrlhttps://mirrors.cloud.tencent.com/gradle/gradle-8.11.1-…...

全面解析 MCP(Model Context Protocol):AI 大模型的“万能连接器”

一、MCP 的定义与技术定位 **MCP(Model Context Protocol,模型上下文协议)**是由 Anthropic 公司于 2024 年 11 月推出的开源协议,旨在为 AI 大模型与外部数据源、工具之间建立标准化连接通道。它被业界称为 “AI 的 USB-C 接口”,通过统一的通信协议和数据结构,解决大模…...

《AI大模型趣味实战》基于RAG向量数据库的知识库AI问答助手设计与实现

基于RAG向量数据库的知识库AI问答助手设计与实现 引言 随着大语言模型&#xff08;LLM&#xff09;技术的快速发展&#xff0c;构建本地知识库AI问答助手已成为许多企业级应用的需求。本研究报告将详细介绍如何基于FLASK开发一个使用本地OLLAMA大模型底座的知识库AI问答助手&…...

Lua 第8部分 补充知识

8.1 局部变量和代码块 Lua 语言中的变量在默认情况下是全局变量 &#xff0c;所有的局部变量在使用前必须声明 。 与全局变量不同&#xff0c;局部变量的生效范围仅限于声明它的代码块。一个代码块&#xff08; block &#xff09;是一个控制结构的主体&#xff0c;或是一个函…...

正则表达式三剑客之——awk命令

目录 一.什么是awk 二.awk的语法格式 1.选项 2. 模式&#xff08;Pattern&#xff09; 3. 操作&#xff08;Action&#xff09; 4. 输入文件&#xff08;file&#xff09; 5.总结 三.awk的工作原理 1. 逐行扫描输入 2. 匹配模式 1.正则表达式&#xff1a; 2.逻辑…...

BeeWorks Meet:私有化部署视频会议的高效选择

在数字化时代&#xff0c;视频会议已成为企业沟通协作的重要工具。然而&#xff0c;对于金融、政务、医疗等对数据安全和隐私保护要求极高的行业来说&#xff0c;传统的公有云视频会议解决方案往往难以满足其严格的安全标准。此时&#xff0c;BeeWorks Meet 私有化部署视频会议…...

[Mybatis-plus]

简介 MyBatis-Plus &#xff08;简称 MP&#xff09;是一个 MyBatis的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变。Mybatis-plus官网地址 注意&#xff0c;在引入了mybatis-plus之后&#xff0c;不要再额外引入mybatis和mybatis-spring&#xff0c;避免因为版本…...

IPv6 技术细节 | 源 IP 地址选择 / Anycast / 地址自动配置 / 地址聚类分配

注&#xff1a;本文为 “IPv6 技术细节” 相关文章合集。 部分文章中提到的其他文章&#xff0c;一并引入。 略作重排&#xff0c;未整理去重。 如有内容异常&#xff0c;请看原文。 闲谈 IPv6 - 典型特征的一些技术细节 iteye_21199 于 2012-11-10 20:54:00 发布 0. 巨大的…...

【高频考点精讲】ES6 String的新增方法,处理字符串更方便了

ES6 String的新增方法:处理字符串从未如此优雅 【初级】前端开发工程师面试100题(一) 【初级】前端开发工程师面试100题(二) 【初级】前端开发工程师的面试100题(速记版) 作为天天和字符串打交道的码农,谁还没被indexOf和substring折磨过?ES6给String对象新增的几个方…...

【工具】使用 MCP Inspector 调试服务的完全指南

Model Context Protocol (MCP) Inspector 是一个交互式开发工具&#xff0c;专为测试和调试 MCP 服务器而设计。本文将详细介绍如何使用 Inspector 工具有效地调试和测试 MCP 服务。 1. MCP Inspector 简介 MCP Inspector 提供了直观的界面&#xff0c;让开发者能够&#xff…...

【音视频】AVIO输入模式

内存IO模式 AVIOContext *avio_alloc_context( unsigned char *buffer, int buffer_size, int write_flag, void *opaque, int (*read_packet)(void *opaque, uint8_t *buf, int buf_size), int (*write_packet)(void *opaque, uint8_t *buf, int buf_size), int64_t (*seek)(…...

AI与思维模型【76】——SWOT思维模型

一、定义 SWOT思维模型是一种用于分析事物内部和外部因素的战略规划工具。其中&#xff0c;S代表优势&#xff08;Strengths&#xff09;&#xff0c;是指事物自身所具备的独特能力、资源或特点&#xff0c;这些因素有助于其在竞争中取得优势&#xff1b;W代表劣势&#xff08…...

大模型提示词如何编写

一、提示词的核心三要素 明确目标&#xff08;What&#xff09; 告诉 AI「你要它做什么」&#xff0c;越具体越好。 ❌ 模糊&#xff1a;写一篇文章 ✅ 清晰&#xff1a;写一篇 800 字的高考作文&#xff0c;主题 “坚持与创新”&#xff0c;结构分引言、三个论点&#xff08;…...

python如何取消word中的缩进

在python-docx中&#xff0c;取消缩进可以通过将相应的缩进属性设置为None或0来实现。以下是取消不同类型缩进的方法&#xff1a; 取消左缩进 from docx import Documentdoc Document(existing_document.docx)for paragraph in doc.paragraphs:# 取消左缩进paragraph.paragr…...

DDL小练习

1.创建一张t_user表 要求属性有id(INT),name(VARCHAR),sex(VARCHAR),birthday(DATE) 其中id和name不能为空&#xff0c;添加数据并测试。 创建数据库 create database spt2503; 创建数据库中的t_user表 create table t_user (id int not null, name varchar(20) not…...

Uniapp:scroll-view(区域滑动视图)

目录 一、基本概述二、属性说明三、基本使用3.1 纵向滚动3.2 横向滚动一、基本概述 scroll-view,可滚动视图区域。用于区域滚动。 二、属性说明 属性名类型默认值说明平台差异说明scroll-xBooleanfalse允许横向滚动scroll-yBooleanfalse允许纵向滚动三、基本使用 3.1 纵向滚…...

【前端】【面试】在前端开发中,如何实现图片的渐进式加载,以及这样做的好处是什么?

题目&#xff1a;在前端开发中&#xff0c;如何实现图片的渐进式加载&#xff0c;以及这样做的好处是什么&#xff1f; 在浏览器端实现图片的“渐进式加载”&#xff08;Progressive Image Loading&#xff09;常用的三种方式 方法思路典型实现要点适用场景优缺点简述1. 使…...

单精度浮点运算/定点运算下 MATLAB (VS) VIVADO

VIVADO中单精度浮点数IP核计算结果与MATLAB单精度浮点数计算结果的对比 MATLAB定点运算仿真&#xff0c;对比VIVADO计算的结果 目录 前言 一、VIVADO与MATLAB单精度浮点数运算结果对比 二、MATLAB定点运算仿真 总结 前言 本文介绍了怎么在MATLAB中使用单精度浮点数进行运算…...

基于大模型对先天性巨结肠全流程预测及医疗方案研究报告

目录 一、引言 1.1 研究背景与意义 1.2 研究目的与创新点 二、大模型在先天性巨结肠预测中的理论基础 2.1 大模型概述 2.2 大模型预测先天性巨结肠的可行性分析 三、术前预测与准备方案 3.1 大模型对术前病情的预测 3.1.1 疾病确诊预测 3.1.2 病情严重程度评估 3.2 …...