此文章提供了一个通用的函数接口,仅需配置相关IO。基于Hal库开发。
控制芯片:STM32F103ZET6
电机驱动:TB6612
电机类型:520编码器电机(12V 110RPM 减速比90)







配置完成后生成代码
/* 此文件为编码器电机闭环调试,包括速度环和位置环
* 配置:TIM2(PA0、PA1):编码器模式
* TIM1-CH1(PE9):PWM输出
* IN1:PF1
* IN2: PF2
* ENABLE: PF0
* 使用方法:1、初始化Motor_Init()
* 2、发送电流SetCurrent()
*/
#include "encoder.h"
encoderMotor_t encoderMoto[ENCODER_MOTO_COUNT]; //编码器电机结构体
pid_t Encoder_Motor_Pid_Pos[ENCODER_MOTO_COUNT]; //编码器电机位置环PID结构体
pid_t Encoder_Motor_Pid_Spd[ENCODER_MOTO_COUNT]; //编码器电机速度环PID结构体
void Motor_Init(void)
{
HAL_TIM_Encoder_Start(&htim2, TIM_CHANNEL_ALL); //开启编码器定时器
__HAL_TIM_ENABLE_IT(&htim2,TIM_IT_UPDATE); //开启编码器定时器更新中断,防溢出处理
__HAL_TIM_SET_COUNTER(&htim2, 10000); //编码器定时器初始值设定为10000
encoderMoto[0].IOControl.htim_pwm = htim1;
encoderMoto[0].IOControl.IN1_Port = GPIOF;
encoderMoto[0].IOControl.IN2_Port = GPIOF;
encoderMoto[0].IOControl.IN1_Pin = GPIO_PIN_1;
encoderMoto[0].IOControl.IN2_Pin = GPIO_PIN_2;
}
//
/* 编码器电机发送电流函数
* motor:编码器电机参数结构体
* val:转动的速度或角度,SPEED最大为110,POSITION一圈为3960
* mode:模式选择:速度环:SPEED
* 位置环:POSITION
*/
void SetCurrent(encoderMotor_t *motor, int32_t val, uint32_t mode)
{
float pos_output,spd_output;
if(mode == 1)
spd_output = pid_calc(&Encoder_Motor_Pid_Spd[0], motor->speed, val);
else
{
pos_output = pid_calc(&Encoder_Motor_Pid_Pos[0], motor->totalAngle, val);
spd_output = pid_calc(&Encoder_Motor_Pid_Spd[0], motor->speed, pos_output);
}
if(spd_output > 0)
{
HAL_GPIO_WritePin(motor->IOControl.IN1_Port, motor->IOControl.IN1_Pin, GPIO_PIN_SET); //控制正反转
HAL_GPIO_WritePin(motor->IOControl.IN2_Port, motor->IOControl.IN2_Pin, GPIO_PIN_RESET);
__HAL_TIM_SET_COMPARE(&motor->IOControl.htim_pwm, TIM_CHANNEL_1, (uint32_t)(spd_output));
}
else
{
HAL_GPIO_WritePin(motor->IOControl.IN1_Port, motor->IOControl.IN1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(motor->IOControl.IN2_Port, motor->IOControl.IN2_Pin, GPIO_PIN_SET);
__HAL_TIM_SET_COMPARE(&motor->IOControl.htim_pwm, TIM_CHANNEL_1, (uint32_t)(-spd_output));
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static int16_t count = 0;
if(htim->Instance==htim6.Instance) //1ms中断
{
count++;
if(count >= 10)
{
count = 0;
int16_t pluse = COUNTERNUM - RELOADVALUE/2;
encoderMoto[0].totalAngle = pluse + encoderMoto[0].loopNum * RELOADVALUE/2;
encoderMoto[0].speed = (float)(encoderMoto[0].totalAngle - encoderMoto[0].lastAngle)/(4*PLUSE_OF_CIRCLE*RR)*6000; //进行速度计算,根据前文所说的,4倍频,编码器13位,减速比30,再乘以6000即为每分钟输出轴多少转
encoderMoto[0].lastAngle = encoderMoto[0].totalAngle; //更新转过的圈数
}
}
else if(htim->Instance == htim2.Instance) //如果是编码器更新中断,即10ms内,脉冲数超过了计数范围,需要进行处理
{
if(COUNTERNUM < 10000) encoderMoto[0].loopNum++;
else if(COUNTERNUM > 10000) encoderMoto[0].loopNum--;
__HAL_TIM_SetCounter(&htim2, 10000); //重新设定初始值
}
}
#ifndef __ENCODER_H
#define __ENCODER_H
#include "tim.h"
#include "gpio.h"
#include "main.h"
#include "stm32_hal_legacy.h"
#include "pid.h"
#define ENCODER_MOTO_COUNT 1 //编码器电机数量
#define RR 90 //电机减速比
#define PLUSE_OF_CIRCLE 11
#define RELOADVALUE __HAL_TIM_GetAutoreload(&htim2) //获取自动装载值,本例中为20000
#define COUNTERNUM __HAL_TIM_GetCounter(&htim2) //获取编码器定时器中的计数值
#define MOTOR_1 1
enum{
POSITION = 0,
SPEED = 1,
};
/* 编码器电机接口定义结构体 */
typedef struct _IOControl
{
TIM_HandleTypeDef htim_encoder;
TIM_HandleTypeDef htim_pwm;
GPIO_TypeDef *IN1_Port;
GPIO_TypeDef *IN2_Port;
uint16_t IN1_Pin;
uint16_t IN2_Pin;
}IOControl_t;
/* 编码器电机参数结构体 */
typedef struct _EncoderMotor{
int8_t ID;
int16_t loopNum; //防超上限
int32_t lastAngle; //上1ms转的角度
int32_t totalAngle; //总角度
float speed; //电机输出轴转速,单位RPM
float set;
IOControl_t IOControl;
}encoderMotor_t;
extern encoderMotor_t encoderMoto[ENCODER_MOTO_COUNT];
extern pid_t Encoder_Motor_Pid_Pos[ENCODER_MOTO_COUNT]; //编码器电机位置环PID结构体
extern pid_t Encoder_Motor_Pid_Spd[ENCODER_MOTO_COUNT]; //编码器电机速度环PID结构体
void SetCurrent(encoderMotor_t *motor, int32_t val, uint32_t mode);
void Motor_Init(void);
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
#endif
#include "pid.h"
void abs_limit(float *a, float ABS_MAX)
{
if(*a > ABS_MAX)
*a = ABS_MAX;
if(*a < -ABS_MAX)
*a = -ABS_MAX;
}
void PID_struct_init(pid_t* pid,
uint32_t maxout,
uint32_t intergral_limit,
float kp,
float ki,
float kd)
{
pid->IntegralLimit = intergral_limit;
pid->MaxOutput = maxout;
pid->p = kp;
pid->i = ki;
pid->d = kd;
}
float pid_calc(pid_t* pid, float get, float set)
{
pid->get = get;
pid->set = set;
pid->err = set - get; /*set - measure,得到偏差*/
pid->pout = pid->p * pid->err;
pid->iout += pid->i * pid->err;
pid->dout = pid->d * (pid->err - pid->lastError);
abs_limit(&(pid->iout), pid->IntegralLimit); /*积分限幅*/
pid->pos_out = pid->pout + pid->iout + pid->dout;
abs_limit(&(pid->pos_out), pid->MaxOutput); /*限定输出值的大小*/
/*更新数据*/
pid->lastError = pid->err;
return pid->pos_out; /*PID输出*/
}
#ifndef __PID_H_
#define __PID_H_
#include "main.h"
typedef struct __pid_t
{
float p,i,d;
float err,lastError; //误差
float set; //目标值
float get; //测量值
float pout; //P输出
float iout; //I输出
float dout; //D输出
float pos_out; //本次位置式输出,即 pos_out = pout + iout + dout
float last_pos_out; //上次位置式输出
uint32_t MaxOutput; //输出限幅
uint32_t IntegralLimit; //积分限幅
}pid_t;
void abs_limit(float *a, float ABS_MAX);
void PID_struct_init(pid_t* pid,
uint32_t maxout,
uint32_t intergral_limit,
float kp,
float ki,
float kd);
float pid_calc(pid_t* pid, float get, float set);
#endif
#include "encoder.h"
#include "pid.h"
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM1_Init();
MX_TIM2_Init();
MX_TIM6_Init();
/*以上为cube生成*/
HAL_TIM_Base_Start_IT(&htim6); //开启1ms定时器中断
HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_1);
Motor_Init();
PID_struct_init(&Encoder_Motor_Pid_Pos[0], 100000, 1000, 0.2, 0.0, 0);
PID_struct_init(&Encoder_Motor_Pid_Spd[0], 1000, 1000, 30, 0.05, 0.01);
while (1)
{
SetCurrent(&encoderMoto[0], 20, SPEED);
}
}
我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%
我正在使用ruby1.9解析以下带有MacRoman字符的csv文件#encoding:ISO-8859-1#csv_parse.csvName,main-dialogue"Marceu","Giveittohimóhe,hiswife."我做了以下解析。require'csv'input_string=File.read("../csv_parse.rb").force_encoding("ISO-8859-1").encode("UTF-8")#=>"Name,main-dialogue\r\n\"Marceu\",\"Giveittohim\x97he,hiswife.\"\
当我在Rails控制台中按向上或向左箭头时,出现此错误:irb(main):001:0>/Users/me/.rvm/gems/ruby-2.0.0-p247/gems/rb-readline-0.4.2/lib/rbreadline.rb:4269:in`blockin_rl_dispatch_subseq':invalidbytesequenceinUTF-8(ArgumentError)我使用rvm来管理我的ruby安装。我正在使用=>ruby-2.0.0-p247[x86_64]我使用bundle来管理我的gem,并且我有rb-readline(0.4.2)(人们推荐的最少
我正在使用Ruby2.1.1和Rails4.1.0.rc1。当执行railsc时,它被锁定了。使用Ctrl-C停止,我得到以下错误日志:~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`gets':Interruptfrom~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.2/lib/spring/client/run.rb:47:in`verify_server_version'from~/.rvm/gems/ruby-2.1.1/gems/spring-1.1.
我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新rubygems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems
我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden
我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha
华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
文章目录1.开发板选择*用到的资源2.串口通信(个人理解)3.代码分析(注释比较详细)1.主函数2.串口1配置3.串口2配置以及中断函数4.注意问题5.源码链接1.开发板选择我用的是STM32F103RCT6的板子,不过代码大概在F103系列的板子上都可以运行,我试过在野火103的霸道板上也可以,主要看一下串口对应的引脚一不一样就行了,不一样的就更改一下。*用到的资源keil5软件这里用到了两个串口资源,采集数据一个,串口通信一个,板子对应引脚如下:串口1,TX:PA9,RX:PA10串口2,TX:PA2,RX:PA32.串口通信(个人理解)我就从串口采集传感器数据这个过程说一下我自己的理解,