在Proteus软件仿真STM32F103寄存器方式PID调速电机

因为电脑中只装了IAR,所以本次编译环境就只能是IAR,所用软件版本是9.32.1。

本次仿真为,纯手写代码,不用任何库,包括启动文件也是手写。

首先是启动文件,该文件是汇编文件,命名为start.s,只写最基本的部分,可以正常启动就行,不需要完整的中断向量表。

    MODULE      ?cstartupSECTION     CSTACK:DATA:NOROOT(3)SECTION     .intvec:CODE:NOROOT(2)EXTERN      __iar_program_startEXTERN      SysTick_HandlerPUBLIC      __vector_tableDATA__vector_tableDCD         sfe(CSTACK)DCD         Reset_HandlerDCD         NMI_HandlerDCD         NMI_HandlerDCD         NMI_HandlerDCD         NMI_HandlerDCD         NMI_HandlerDCD         NMI_HandlerDCD         NMI_HandlerDCD         NMI_HandlerDCD         NMI_HandlerDCD         NMI_HandlerDCD         NMI_HandlerDCD         NMI_HandlerDCD         NMI_HandlerDCD         SysTick_HandlerTHUMBPUBWEAK Reset_HandlerSECTION .text:CODE:REORDER:NOROOT(2)
Reset_HandlerLDR         R0,=__iar_program_startBX          R0PUBWEAK NMI_HandlerSECTION .text:CODE:REORDER:NOROOT(1)
NMI_HandlerB           NMI_HandlerEND

接下来是main.c文件,因为只是仿真调速,代码量不多,所以就都写在同一个文件中了。

首先是Systick结构体定义以及定义Systick指针并指向Systick指向的位置,以及初始化和Systick中断的实现,方便计时功能。

typedef struct
{volatile uint32_t CTRL;volatile uint32_t LOAD;volatile uint32_t VAL;volatile uint32_t CALIB;
}SysTick_Def;#define SysTick_BASE    0xE000E010#define SysTick         ((SysTick_Def *)(SysTick_BASE))
void SysTick_Init(void)
{SysTick->LOAD = 800-1;SysTick->VAL = 0;SysTick->CTRL = 0x07;
}static volatile uint32_t TickCount = 0;void SysTick_Handler(void)
{TickCount ++;if(TickCount >= 10001){TickCount = 1;}
}uint32_t Get_SysTickCount(void)
{return TickCount;
}

然后是时钟管理单元RCC和GPIO的结构体的定义。

typedef struct
{volatile uint32_t CR;volatile uint32_t CFCR;volatile uint32_t CIR;volatile uint32_t APB2RSTR;volatile uint32_t APB1RSTR;volatile uint32_t AHBENR;volatile uint32_t APB2ENR;volatile uint32_t APB1ENR;volatile uint32_t BDCR;volatile uint32_t CSR;
}RCC_TypeDef;typedef struct
{volatile uint32_t CRL;volatile uint32_t CRH;volatile uint32_t IDR;volatile uint32_t ODR;volatile uint32_t BSRR;volatile uint32_t BRR;volatile uint32_t LCKR;
}GPIO_TypeDef;#define RCC_BASE        0x40021000
#define GPIOA_BASE      0x40010800
#define GPIOB_BASE      0x40010C00
#define GPIOC_BASE      0x40011000
#define GPIOD_BASE      0x40011400
#define GPIOE_BASE      0x40011800#define RCC             ((RCC_TypeDef *)(RCC_BASE))
#define GPIOA           ((GPIO_TypeDef *)(GPIOA_BASE))
#define GPIOB           ((GPIO_TypeDef *)(GPIOB_BASE))
#define GPIOC           ((GPIO_TypeDef *)(GPIOC_BASE))
#define GPIOD           ((GPIO_TypeDef *)(GPIOD_BASE))
#define GPIOE           ((GPIO_TypeDef *)(GPIOE_BASE))

接着是GPIOB的初始化

void PBH_Out_Init(uint8_t pin)
{uint32_t reg = GPIOB->CRH;reg &= ~(0xF << ((pin & 0x07) << 2));reg |= (0x02 << ((pin & 0x07) << 2));//推挽输出 20MGPIOB->CRH = reg;
}void PBL_Out_Init(uint8_t pin)
{uint32_t reg = GPIOB->CRL;reg &= ~(0xF << ((pin & 0x07) << 2));reg |= (0x02 << ((pin & 0x07) << 2));//推挽输出 20MGPIOB->CRL = reg;
}void PB_Out_Init(uint8_t pin)
{if(pin & 0x08)PBH_Out_Init(pin);elsePBL_Out_Init(pin);
}void PB_Out(uint8_t pin,uint8_t State)
{if(State)GPIOB->BSRR = 0x01 << (pin & 0x0F);elseGPIOB->BRR = 0x01 << (pin & 0x0F);
}void PBH_In_Init(uint8_t pin)
{uint32_t reg = GPIOB->CRH;reg &= ~(0xF << ((pin & 0x07) << 2));reg |= (0x08 << ((pin & 0x07) << 2));//上下拉输入GPIOB->CRH = reg;
}void PBL_In_Init(uint8_t pin)
{uint32_t reg = GPIOB->CRL;reg &= ~(0xF << ((pin & 0x07) << 2));reg |= (0x08 << ((pin & 0x07) << 2));//上下拉输入GPIOB->CRL = reg;
}void PB_In_Init(uint8_t pin)
{if(pin & 0x08)PBH_In_Init(pin);elsePBL_In_Init(pin);
}uint8_t  PB_In(uint8_t pin)
{return (GPIOB->IDR >> pin) & 0x01;
}

需要写一个简单的PWM输出,用于给电机调速,调用周期是10kHz,PWM的输入范围限制在0~99,共100个数。

uint8_t Pwm_num = 10;void PWM_Out(void)//rate:10Khz
{static uint8_t count = 0;if(count == 0)PB_Out(12,1);if(count == Pwm_num)PB_Out(12,0);count ++;if(count ==100)count = 0;
}

这个系统需要显示,为了简单,这里直接使用共阳数码管。

void SEG_Init(void)
{GPIOA->CRL = 0x22222222;GPIOA->CRH = 0x22222222;GPIOA->ODR = 0x0000FF00;
}const unsigned char displayCodeArray[16] =
{0xC0,//00xF9,//10xA4,//20xB0,//30x99,//40x92,//50x82,//60xF8,//70x80,//80x90,//90x88,//A0x83,//B0xC6,//C0xA1,//D0x86,//E0x8E,//F
};uint8_t dispalyArray[8] = {0xFF};void SEG_Step(void)
{static uint8_t dp = 0;uint16_t temp = dispalyArray[7 - dp];temp |= (1 << (dp + 8));GPIOA->ODR = temp;dp ++;dp &= 0x07;
}void TargetSSpeed_Write(uint16_t speed)
{dispalyArray[0] = 0xFF;dispalyArray[1] = 0xFF;dispalyArray[2] = 0xFF;dispalyArray[3] = 0xFF;for(uint32_t i = 0;i < 4;i ++){dispalyArray[i] = displayCodeArray[speed % 10];speed /= 10;if(speed == 0)break;}
}void CurrentSpeed_Write(uint16_t speed)
{dispalyArray[4] = 0xFF;dispalyArray[5] = 0xFF;dispalyArray[6] = 0xFF;dispalyArray[7] = 0xFF;for(uint32_t i = 0;i < 4;i ++){dispalyArray[i + 4] = displayCodeArray[speed % 10];speed /= 10;if(speed == 0)break;}
}

仿真过程中需要输入目标速度,所以用了三个独立按键,分别是速度+,速度-,速度确认,仿真环境就不用做防抖处理了。

void Key_Init(void)
{PB_In_Init(13);PB_In_Init(14);PB_In_Init(15);PB_Out(13,1);PB_Out(14,1);PB_Out(15,1);
}uint8_t Key_read(void)
{static uint8_t lastKey;uint8_t temp = (GPIOB->IDR >> 13) & 0x07;if(lastKey == temp)return 0;lastKey = temp;if(temp == 6)return 1;if(temp == 5)return 2;if(temp == 3)return 3;return 0;
}uint16_t TargetSpeedDispalyValue = 50;
uint32_t TargetSpeed = 500;void TargetSpeet_read(void)
{uint8_t key = Key_read();//if(key == 0)return;if(key == 1)TargetSpeedDispalyValue++;if(key == 2)TargetSpeedDispalyValue--;TargetSSpeed_Write(TargetSpeedDispalyValue);if(key == 3){TargetSpeed = TargetSpeedDispalyValue * 10;}
}

要想把电机调到指定的速度,就需要读取电机的编码器,并计算速度。这个编码器是AB增量式编码器,就直接按4倍频计数了。

const uint8_t EncodeCode[4] = {0x06,0x02,0x00,0x04};
static uint8_t EncodeNum;void Encode_Init(void)
{PB_In_Init(0);PB_In_Init(1);PB_In_Init(2);PB_Out(0,1);PB_Out(1,1);PB_Out(2,1);uint8_t temp = GPIOB->IDR & 0x06;for(uint8_t i = 0;i < 4;i ++){if(temp == EncodeCode[i]){EncodeNum = i;return;}}
}uint8_t Encode_read(void)
{uint8_t ret = 0;uint8_t temp = GPIOB->IDR & 0x07;if((temp & 0x06) == EncodeCode[EncodeNum])return 0;//没有检测到变化if((temp & 0x06) == EncodeCode[(EncodeNum + 1) & 0x03]){ret = 1;//+1EncodeNum ++;}if((temp & 0x06) == EncodeCode[(EncodeNum - 1) & 0x03]){ret = 2;//-1EncodeNum --;}EncodeNum &= 0x03;if(temp & 0x01)ret += 8;//零点return ret;
}static int32_t EncodeValue = 0;void EncodeValueAdd(void)
{uint8_t temp = Encode_read();if(temp & 0x01)EncodeValue ++;if(temp & 0x02)EncodeValue --;
}int CurrentSpeed = 0;void MotoSpeed(void)//rate 100hz
{static int32_t EncodeValueLast = 0,count = 0;int32_t temp = EncodeValue - EncodeValueLast;EncodeValueLast = EncodeValue;static int32_t speed[8] = {0};speed[count ++] = temp * 60;//speed = temp * 10 / 100 * 60 * 10;count &= 0x07;CurrentSpeed = 0;for(uint8_t i = 0;i < 8;i ++)CurrentSpeed += speed[i];CurrentSpeed /= 8;
}void dispalySpeed(void)
{CurrentSpeed_Write(CurrentSpeed / 10);
}

最后需要实现的是PID控制代码了,当然这个是位置式PID了。

typedef struct
{float Kp;float Ki;float Kd;int32_t integral;int32_t e1;int32_t e2;
}Pid_Def;float Pid_Calculate(Pid_Def* PID,int32_t TargetValue,int32_t CurrentValue)
{int32_t e0 = TargetValue - CurrentValue;PID->integral += e0;float Output = (PID->Kp * e0)+ (PID->Ki * PID->integral)+ (PID->Kd * (e0 - PID->e1));PID->e1 = e0;return Output;
}Pid_Def Pid = {0.19,0.01,0.01,0,0,0};void PwmOutValue_Calculate(void)
{float value = Pid_Calculate(&Pid,TargetSpeed,CurrentSpeed);//value += Pwm_num;value = value > 99 ? 99 : value;Pwm_num = (uint8_t)(value < 0 ? 0 : value);
}

最后是main函数了,以及函数声明了。

void SysTick_Init(void);
uint32_t Get_SysTickCount(void);
void PB_Out_Init(uint8_t pin);
void PB_Out(uint8_t pin,uint8_t State);
void PWM_Out(void);
void SEG_Init(void);
void SEG_Step(void);
void TargetSSpeed_Write(uint16_t speed);
void CurrentSpeed_Write(uint16_t speed);
void Key_Init(void);
void TargetSpeet_read(void);
void Encode_Init(void);
void EncodeValueAdd(void);
void MotoSpeed(void);//rate 200hz
void dispalySpeed(void);
void PwmOutValue_Calculate(void);void main()
{SysTick_Init();RCC->APB2ENR = 0xFFFFFFFF;Key_Init();SEG_Init();Encode_Init();PB_Out_Init(12);PB_Out(12,1);PB_Out_Init(9);PB_Out(9,1);PB_Out_Init(10);PB_Out(10,0);while(1){uint32_t tick = Get_SysTickCount();//10 KhzPWM_Out();if((tick >= 5) && (tick % 5 == 0))//0.5ms{EncodeValueAdd();}if((tick >= 10) && (tick % 10 == 0))//1ms{}if((tick >= 20) && (tick % 20 == 0))//2ms{SEG_Step();}if((tick >= 100) && (tick % 100 == 0))//10ms{}if((tick >= 200) && (tick % 200 == 0))//20ms{}if((tick >= 500) && (tick % 500 == 0))//50ms{TargetSpeet_read();}if((tick >= 1000) && (tick % 1000 == 0))//100ms{MotoSpeed();}if((tick >= 2000) && (tick % 2000 == 0))//200ms{PwmOutValue_Calculate();dispalySpeed();}while(tick == Get_SysTickCount()){}}
}

最后在贴一张仿真图。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/492064.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

QtitanChart组件——高效、灵活的Qt数据可视化解决方案

在现代应用开发中&#xff0c;数据可视化已经成为不可或缺的一部分。无论是商业分析工具、财务报表、工程图表&#xff0c;还是科学实验数据展示&#xff0c;如何以直观、易理解的方式展示数据&#xff0c;往往决定了软件的可用性与用户体验。对于Qt开发者来说&#xff0c;Qtit…

YOLOv11融合[CVPR2024]Starnet中的star block特征提取模块

YOLOv11v10v8使用教程&#xff1a; YOLOv11入门到入土使用教程 YOLOv11改进汇总贴&#xff1a;YOLOv11及自研模型更新汇总 《Rewrite the Stars》 一、 模块介绍 论文链接&#xff1a;https://arxiv.org/abs/2403.19967 代码链接&#xff1a;https://github.com/ma-xu/Rewri…

LabVIEW农机自主导航监控系统

随着现代农业技术的快速发展&#xff0c;自主导航农机的需求日益增加&#xff0c;提高作业效率和减少劳动成本成为农业现代化的关键目标。本文介绍了一个基于LabVIEW的农机自主导航监控系统的开发案例&#xff0c;该系统通过先进的传感器与控制技术&#xff0c;实现农机在田间作…

JAVA:代理模式(Proxy Pattern)的技术指南

1、简述 代理模式(Proxy Pattern)是一种结构型设计模式,用于为其他对象提供一种代理,以控制对这个对象的访问。通过代理模式,我们可以在不修改目标对象代码的情况下扩展功能,满足特定的需求。 设计模式样例:https://gitee.com/lhdxhl/design-pattern-example.git 2、什…

番外篇 | Hyper-YOLO:超图计算与YOLO架构相结合成为目标检测新的SOTA !

前言:Hello大家好,我是小哥谈。Hyper-YOLO,该方法融合了超图计算以捕捉视觉特征之间复杂的高阶关联。传统的YOLO模型虽然功能强大,但其颈部设计存在局限性,限制了跨层特征的融合以及高阶特征关系的利用。Hyper-YOLO在骨干和颈部的联合增强下,成为一个突破性的架构。在COC…

php基础:正则表达式

1.正则表达式 正则表达式是用于描述字符排列和匹配模式的一种语法规则。它主要用于字符串的模式分割、匹配、查找及替换操作。到目前为止&#xff0c;我们前面所用过的精确&#xff08;文本&#xff09;匹配也是一种正则表达式。 在PHP中&#xff0c;正则表达式一般是由正规字…

Postman接口测试:全局变量/接口关联/加密/解密

&#x1f345; 点击文末小卡片&#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 全局变量和环境变量 全局变量&#xff1a;在postman全局生效的变量&#xff0c;全局唯一 环境变量&#xff1a;在特定环境下生效的变量&#xff0c;本环境内唯一 …

vue响应式数据-修改对象的属性值,视图不更新

如图&#xff1a; 一&#xff1a;问题是&#xff1a; 我把数据处理后能console.log()打印出来&#xff0c;但是页面的内容不能同步的更新渲染&#xff1b; 二&#xff1a;要求&#xff1a; 在数组循环列表里面&#xff0c;我点击单个的item按钮时&#xff0c;需要实时加载进度…

configure错误:“C compiler cannot create executables“

执行./configure命令出现如下奇怪的错误&#xff0c;百思不得姐&#xff1a; ./configure命令的日志文件为config.log&#xff0c;发生错误时&#xff0c;该文件的内容&#xff1a; This file contains any messages produced by compilers while running configure, to aid d…

【Java学习笔记】多线程基础

并行&#xff1a;同一时刻&#xff0c;多任务同时进行 多任务分别进行 一、线程相关概念 1.程序 是为完成特定任务、用某种语言编写的一组指令的集合。 简单的说:就是我们写的代码 2.进程 &#xff08;1&#xff09;进程指的就是运行中的程序&#xff0c;比如我们使用QQ,就…

PyTorch基础入门

目录 前言一、[张量的广播&基本运算](https://www.bilibili.com/video/BV1Gg411u7Lr/?spm_id_from333.999.0.0)1. 张量的广播特性2. 逐点&规约&比较运算 二、张量的线性代数运算1. BLAS & LAPACK2. 矩阵形变及特殊矩阵构造3. 矩阵基本运算4. 矩阵的线性代数运…

XXE靶机攻略

XXE-Lab靶场 1.随便输入账号密码 2.使用bp抓包 3.插入xml代码&#xff0c;得到结果 xxe靶机 1.安装好靶机&#xff0c;然后输入arp-scan -l&#xff0c;查找ip 2.输入ip 3.使用御剑扫描子域名 4.输入子域名 5.输入账号密码抓包 6.插入xml代码 7.使用工具解码 8.解码完毕放入文…

计算机知识笔试

一、计算机网络 1.网络分类 树型、总线型、环型一般是局域网所用的拓扑结构&#xff0c;广域网和远程计算机网络普遍采用网状拓扑结构。 2.OSI模型各层功能 比特、帧、端到端 3.传输层协议 TCP:传输控制协议 UDP:用户数据报协议 传输层、可靠、连接 4.网络层协议 IP协议是…

设计规规范:【App 配色】

文章目录 引言I App 配色组成色彩象征 & 联想II 知识扩展设计流程图UI设计交互设计UI交互设计引言 设计规范,保持设计一致性,提高设计效率。宏观上对内统一,管理与合作变得容易。 按类型管理颜色、文本样式、图标、组件(symbol)。 蓝湖设计规范云 https://lanhuapp.co…

计算生物学与系统生物学

计算生物学 计算生物学是一个利用数学、统计学、计算机科学和生物学知识来解决生物学问题的学科。它的核心目标是通过开发和应用计算方法来分析和解读大量的生物数据&#xff0c;以揭示生命现象的规律和机制。 1. 基因组学分析 计算生物学在基因组学中的应用主要集中在基因的…

timestamp 时间戳转换成日期的方法 | java.util

时间戳通常是一个long数据&#xff08;注意java中赋值时需要带上L标识是long整型&#xff0c;否则int过长报错&#xff09; 代码实现 常用工具类&#xff1a; java.util.Datejava.time.Instantjava.time.format.DateTimeFormatter toInstant() 方法的功能是将一个 Date 对象…

【OpenCV计算机视觉】图像处理——平滑

本篇文章记录我学习【OpenCV】图像处理中关于“平滑”的知识点&#xff0c;希望我的分享对你有所帮助。 目录 一、什么是平滑处理 1、平滑的目的是什么&#xff1f; 2、常见的图像噪声 &#xff08;1&#xff09;椒盐噪声 ​编辑&#xff08;2&#xff09; 高斯噪声 &a…

在IDE中使用Git

我们在开发的时候肯定是经常使用IDE进行开发的&#xff0c;所以在IDE中使用Git也是非常常用的&#xff0c;接下来以IDEA为例&#xff0c;其他的VS code &#xff0c;Pycharm等IDE都是一样的。 在IDEA中配置Git 1.打开IDEA 2.点击setting 3.直接搜索git 如果已经安装了会自…

数据可视化-1. 折线图

目录 1. 折线图适用场景分析 1. 1 时间序列数据展示 1.2 趋势分析 1.3 多变量比较 1.4 数据异常检测 1.5 简洁易读的数据可视化 1.6 特定领域的应用 2. 折线图局限性 3. 折线图代码实现 3.1 Python 源代码 3.2 折线图效果&#xff08;网页显示&#xff09; 1. 折线图…

Linux -- 线程控制相关的函数

目录 pthread_create -- 创建线程 参数 返回值 代码 -- 不传 args&#xff1a; 编译时带 -lpthread 运行结果 为什么输出混杂&#xff1f; 如何证明两个线程属于同一个进程&#xff1f; 如何证明是两个执行流&#xff1f; 什么是LWP&#xff1f; 代码 -- 传 args&a…