‘壹’ 怎么用stm32这样的单片机控制4G模块
如果是AT命令方式,比较简单。通过串口发送AT命令就能控制。如果想拨号上网有点麻烦,因为我过的4G,需要SDK来和模块通信。而SDK只提供linux平台的驱动。
或者有的4G模块,PCIE直接支持ACM驱动,但是这也得LINUX支持。单纯的STM32想支持拨号方式的上网,有些困难。
‘贰’ 用stm32单片机 如何用按键控制继电器的吸合
你说的问题很简单:就是让单片机的不断检测某一个输入端口,这个端口的电位由一个按键控制,如果这个按键闭合,就将某一个输出端口置高电平,通过接口电路控制继电器线圈通电,即吸合。反之,当检测到按键释放,就将某一个输出端口置低电平,通过接口电路控制继电器线圈断电,即释放。
根据这个思想你就自己编一个小程序就可以了。
‘叁’ 用stm32单片机怎么控制步进电机的正转,反转。刚学单片机。好多疑问啊。
刚学单片机就用STM32?好牛叉的样子。
控制方法很简单,
比如五线四相步进电机
四拍控制,轮流给每一相一次高电平(其他相低电平),A--延时--B--延时----C---延时--D--延时----A----延时--B这样循环,但是频率不能太大,一般步进电机转速在1200转/s以内,反转倒过来给电平就行了,D--C--B---A
详细请网络搜步进电机
‘肆’ STM32单片机怎么产生脉冲信号控制步进电动机
STM32单片机怎么产生脉冲信号控制步进电动机:
#include "stepmotor.h"
#include
u32 PUL_CNT; // TIM3脉冲计数
vu32 step_done;
vu32 run_state;
#define run_state_stop 0
#define run_state_acc 1
#define run_state_run 2
#define run_state_dec 3
void STEPMOTOR_CTRL_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_OCInitTypeDef TIM_OCInitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //GPIO时钟使能
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //定时器3时钟使能
//RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //定时器2时钟使能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7; //PA7为TIM3通道2
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推免输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO口响应速度
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //PA6为DIR控制输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推免输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO口响应速度
GPIO_Init(GPIOA, &GPIO_InitStructure);
//TIM3_Configuration
TIM_TimeBaseStructure.TIM_Period = 23999; //自动重装载寄存器
TIM_TimeBaseStructure.TIM_Prescaler = 2; //预分频器,t=(23999+1)*(2+1)/72M
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器向上计数模式
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; //时钟分频因子
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0x0; //每次溢出都产生事件更新
TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); //写TIM3各寄存器参数
TIM_ClearFlag(TIM3,TIM_FLAG_Update); //中断标志位清零
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //允许捕获/比较3中断
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //PWM模式2 TIM3_CCMR1[14:12]=111 在向上计数时,一旦TIMx_CNT
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输入/捕获2输出允许
TIM_OCInitStructure.TIM_Pulse = 40; //确定占空比,这个值决定了有效电平的时间。
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性,低电平有效
TIM_OC2Init(TIM3, &TIM_OCInitStructure); //配置定时器输出模式,比较参数等
TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能TIM3在CCR2上的预装载寄存器
//TIM2_Configuration
TIM_DeInit(TIM2); //TIM2重新配置为缺省值,默认状态
TIM_TimeBaseStructure.TIM_Period = 359; //自动重装载寄存器
TIM_TimeBaseStructure.TIM_Prescaler = 199; //时钟预分频器
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器向上计数模式
TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; //时钟分频因子
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); //配置TIM2寄存器各参数
TIM_ClearFlag(TIM2,TIM_FLAG_Update); //中断标志位清零
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //允许捕获/比较2中断
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn ; //选择定时器TIM3
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //选择抢先式优先级(与中断嵌套级别有关)
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2; //选择子优先级(同抢先式优先级的响应顺序)
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //选择使能中断源
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn ; //选择定时器TIM2
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //选择抢先式优先级(与中断嵌套级别有关)
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //选择子优先级(同抢先式优先级的响应顺序)
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //选择使能中断源
NVIC_Init(&NVIC_InitStructure);
}
void TIM3_Configuration(u32 period)
{
TIM3->ARR = period-1;
TIM3->CCR2 = period >> 2;
//TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
//TIM_OCInitTypeDef TIM_OCInitStructure;
//TIM_TimeBaseStructure.TIM_Period = period-1; //自动重装载寄存器
//TIM_TimeBaseStructure.TIM_Prescaler = 29; //预分频器,f=72M/[(period+1)*(29+1)], ft = 2400000
//TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器向上计数模式
//TIM_TimeBaseStructure.TIM_ClockDivision = 0x0; //时钟分频因子
//TIM_TimeBaseStructure.TIM_RepetitionCounter = 0x0; //每次溢出都产生事件更新
//TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure); //写TIM3各寄存器参数
//TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //PWM模式2 TIM3_CCMR1[14:12]=111 在向上计数时,一旦TIMx_CNT
//TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输入/捕获2输出允许
//TIM_OCInitStructure.TIM_Pulse = period >> 2; //确定占空比,25%
//TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //输出极性,低电平有效
//TIM_OC2Init(TIM3, &TIM_OCInitStructure); //配置定时器输出模式,比较参数等
}
//void MOTOR_RUN(u32 acc, u32 dec, u32 topspeed, u32 dis)
//步进电机运行参数
//acc -- 加速度,单位: round/min/s
//dec -- 减速度,单位: round/min/s
//topspeed -- 最高速度,单位: round/min
//dis -- 总角位移,单位: round/10000
void MOTOR_RUN(u32 acc, u32 dec, u32 topspeed, u32 dis)
{
u32 t_acc,t_dec,step_all,step_acc,step_dec,step_run;
u32 i,tim_cnt,tim_rest,tim_cnt_temp;
step_all = (float)dis * (N_MOTOR * 0.0001);
t_acc = topspeed * 1000 / acc; //unit: ms
t_dec = topspeed * 1000 / dec; //unit: ms
if(topspeed * (t_acc + t_dec) / 12 > dis) //达不到最高速度 // topspeed/60/1000 * (t_acc + t_dec) / 2 > dis / 10000
{
topspeed = sqrt(dis * acc * dec * 12 / (acc + dec) / 1000);
t_acc = topspeed * 1000 / acc; //unit: ms
t_dec = topspeed * 1000 / dec; //unit: ms
}
step_acc = N_MOTOR * ((float)topspeed*topspeed/(acc*120));
step_dec = N_MOTOR * ((float)topspeed*topspeed/(dec*120));
if(step_all > step_acc + step_dec)
step_run = step_all - step_acc - step_dec;
else
step_run = 0;
//tim_cnt = 5.2363 * ft / (sqrt(acc*N_MOTOR/2)); //(ft * sqrt(60)*0.676) / sqrt(acc*N_MOTOR/2);
tim_cnt = 7.7460 * ft / (sqrt(acc*N_MOTOR/2));
tim_rest = 0;
i = 0;
TIM3_Configuration(tim_cnt);
run_state = run_state_acc;
TIM_Cmd(TIM3,ENABLE);
step_done = 0;
while(step_done==0);
while(i
{
i++;
//tim_cnt_temp = tim_cnt;
//tim_cnt = tim_cnt - (2*tim_cnt+tim_rest) / (4*i+1);
//tim_rest = (2*tim_cnt_temp+tim_rest) % (4*i+1);
tim_cnt_temp = tim_cnt / ( sqrt((float)(i+1)) + sqrt((float)(i)) );
TIM3_Configuration(tim_cnt_temp);
step_done = 0;
while(step_done==0);
}
if(step_run > 0)
{
run_state = run_state_run;
tim_cnt = ft * 60 / (N_MOTOR*topspeed);
i = 0;
TIM3_Configuration(tim_cnt);
while(i
{
step_done = 0;
while(step_done==0);
i++;
}
}
run_state = run_state_dec;
tim_rest = 0;
i=0;
tim_cnt = tim_cnt + (2*tim_cnt+tim_rest) / (4*(step_dec-i)-1);
while(i
{
TIM3_Configuration(tim_cnt);
step_done = 0;
while(step_done==0);
i++;
tim_cnt_temp = tim_cnt;
tim_cnt = tim_cnt + (2*tim_cnt+tim_rest) / (4*(step_dec-i)-1);
tim_rest = (2*tim_cnt_temp+tim_rest) % (4*(step_dec-i)-1);
}
run_state = run_state_stop;
TIM_Cmd(TIM3,DISABLE);
}
void TIM2_IRQHandler(void)
{
}
void TIM3_IRQHandler(void)
{
TIM_ClearFlag(TIM3,TIM_FLAG_Update);
step_done = 1;
//PUL_CNT++;
}
文件:stepmotor.h 声明步进电机控制头文件
#define N_MOTOR 10000 //步进电机细分
#define ft 24000000
void STEPMOTOR_CTRL_INIT(void);
void MOTOR_RUN(u32 acc, u32 dec, u32 topspeed, u32 dis);
文件:main.c 主函数,设置加速度,减速度,最大速度和步数的参数值
#include "main.h"
#define LED_SET() GPIO_SetBits(GPIOB,GPIO_Pin_8)
#define LED_RST() GPIO_ResetBits(GPIOB,GPIO_Pin_8)
#define SET_DIR_CW() GPIO_SetBits(GPIOA,GPIO_Pin_6)
#define SET_DIR_CCW() GPIO_ResetBits(GPIOA,GPIO_Pin_6)
void NVIC_Configuration(void);
void LED_init(void);
void soft_delayms(u16 t);
int main(void)
{
SystemInit();
STEPMOTOR_CTRL_INIT();
soft_delayms(1000);
while(1)
{
SET_DIR_CW();
MOTOR_RUN(600,600,1000,500000);
soft_delayms(1000);
SET_DIR_CCW();
MOTOR_RUN(600,600,1000,500000);
soft_delayms(1000);
}
return 0;
}
void NVIC_Configuration(void)
{
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0000); //将中断矢量放到Flash的0地址
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //设置优先级配置的模式,详情请阅读原材料中的文章
}
void LED_init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct);
}
void soft_delayms(u16 t)
{
u16 tt;
while(t--)
{
tt = 10000;
while(tt--);
}
}
#ifndef _MAIN_H
#define _MAIN_H
#include "stm32f10x.h"
#include "stepmotor.h"
#endif
‘伍’ stm32单片机能写复杂控制算法么
1.PID原理
1.1 P I D三个参数简单理解
1.2 P I D
1.3 PI PD PID适用系统
2.串级PID原理
3.PID代码
3.1 单级PID
3.1.1 初始化PID结构体
3.1.2 单级PID计算
3.1.3PID初始化
3.1.4 清空PID
3.2 串级PID
3.2.1 初始化串级PID结构体
3.2.2 串级PID计算
4.PID的使用
4.1 定义PID结构体并初始化
4.2 定义电机速度函数
4.3 在检测霍尔码盘时发送速度给电机
4.4 实验效果
1.PID原理
PID是什么,P,I,D的分别功能
你和PID调参大神之间,就差这篇文章!
1.1 P I D三个参数简单理解
P(比例): 简单来说,P就是凉了加热水,热了加凉水。比目标值小,我就增加一点,比目标值大,我就减小一点。(现在)
P可能出现的问题: 1.P太小,达到目标值需要花费很长的时间,而且会有稳态误差。2.P太大,达到目标值时可能会一直震荡。
I(积分): 将一段时间内的误差累积起来加到输出上,可以消除历史误差对当前实际曲线的影响,提高系统的稳定性。 (过去)
I可能出现的问题: 1.I太小,可以消除稳态误差,但太慢了,对于某些需要很快响应的系统,显然不能满足要求。2.I太大,累计误差占比过大,就会出现抖动现象,难以收敛。
D(微分): 减小最大超调量。(下图中③就是最大超调量。) 可以有效减小震动的幅度。让曲线收敛更快 (未来)
D可能出现的问题: 1.D太小,作用小,时间长。2.D太大,为了减小超调量,补偿的过多,导致震荡很久。
在这里插入图片描述
1.2 P I D
先调P,逐渐增加P直到系统出现震荡,将当前值乘0.7就是较为合适的值。
再调I,将稳态误差逐渐降低。
后调D,将最大超调量降到最低。
1.3 PI PD PID适用系统
PI:响应速度要求不那么高的系统。
PD:大惯性系统。超调量太大。
PID:都可以。
网上将PID原理太多太多了,我的理解也都是参见上面的内容。认真看肯定有收获。
2.串级PID原理
【串级PID】浅谈串级PID作用及意义——快速理解串级PID结构优势
这里个人理解就是,单机PID就是稳定速度。而需要带位置和角度的就要用串级PID了。常用于平衡车,板球系统等。
而转速闭环称为串级PID的内环,位置 (角度) 闭环称为串级PID的外环。其实也很好理解,位移是速度的积分,只有速度慢慢稳定,位置才能确定。
3.PID代码
3.1 单级PID
3.1.1 初始化PID结构体
typedef struct _PID
{
float kp,ki,kd;
float error,lastError;//误差、上次误差
float integral,maxIntegral;//积分、积分限幅
float output,maxOutput;//输出、输出限幅
}PID;
1
2
3
4
5
6
7
1
2
3
4
5
6
7
3.1.2 单级PID计算
#define LIMIT(x,min,max) (x)=(((x)<=(min))?(min):(((x)>=(max))?(max):(x)))
//单级pid计算
void PID_SingleCalc(PID *pid,float reference,float feedback)
{
//更新数据
pid->lastError=pid->error;
pid->error=reference-feedback;
//计算微分
pid->output=(pid->error-pid->lastError)*pid->kd;
//计算比例
pid->output+=pid->error*pid->kp;
//计算积分
pid->integral+=pid->error*pid->ki;
LIMIT(pid->integral,-pid->maxIntegral,pid->maxIntegral);//积分限幅
pid->output+=pid->integral;
//输出限幅
LIMIT(pid->output,-pid->maxOutput,pid->maxOutput);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
3.1.3PID初始化
void PID_Init(PID *pid,float p,float i,float d,float maxI,float maxOut)
{
pid->kp=p;
pid->ki=i;
pid->kd=d;
pid->maxIntegral=maxI;
pid->maxOutput=maxOut;
}
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
3.1.4 清空PID
//清空一个pid的历史数据
void PID_Clear(PID *pid)
{
pid->error=0;
pid->lastError=0;
pid->integral=0;
pid->output=0;
}
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
3.2 串级PID
3.2.1 初始化串级PID结构体
typedef struct _CascadePID
{
PID inner;//内环
PID outer;//外环
float output;//串级输出,等于inner.output
}CascadePID;
1
2
3
4
5
6
1
2
3
4
5
6
3.2.2 串级PID计算
//串级pid计算
void PID_CascadeCalc(CascadePID *pid,float angleRef,float angleFdb,float speedFdb)
{
PID_SingleCalc(&pid->outer,angleRef,angleFdb);//计算外环(角度环)
PID_SingleCalc(&pid->inner,pid->outer.output,speedFdb);//计算内环(速度环)
pid->output=pid->inner.output;
}
1
2
3
4
5
6
7
1
2
3
4
5
6
7
4.PID的使用
STM32应用(九)编码器及其测速原理、L298N电机驱动控制编码器电机
在这篇博客的配置下,只需要修改部分代码。以单级PID为例子。
4.1 定义PID结构体并初始化
PID pid;
void Motor_Init(void)
{
PID_Init(&pid,10,0,0,1000,1000);
HAL_TIM_Encoder_Start(&htim1, TIM_CHANNEL_ALL); //开启编码器定时器
__HAL_TIM_ENABLE_IT(&htim1,TIM_IT_UPDATE); //开启编码器定时器更新中断,防溢出处理
HAL_TIM_Base_Start_IT(&htim6); //开启10ms定时器中断
HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); //开启PWM
__HAL_TIM_SET_COUNTER(&htim1, 10000); //编码器定时器初始值设定为10000
motor.loopNum = 0; //防溢出
}
1
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
4.2 定义电机速度函数
void Motor_Send()
{
float output = 0;
PID_SingleCalc(&pid, motor.targetSpeed, motor.speed);
output = pid.output;
if(output > 0) //正转
{
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, (uint32_t)output);
IN1(1);
IN2(0);
}
else //反转
{
__HAL_TIM_SetCompare(&htim2, TIM_CHANNEL_1, (uint32_t)(-output));
IN1(0);
IN2(1);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
4.3 在检测霍尔码盘时发送速度给电机
if(htim->Instance==htim6.Instance) //10ms中断
{
int16_t pluse = COUNTERNUM - RELOADVALUE/2;
motor.totalAngle = pluse + motor.loopNum * RELOADVALUE/2;
motor.speed = (float)(motor.totalAngle - motor.lastAngle)/(4*13*RR)*6000; //进行速度计算,根据前文所说的,4倍频,编码器13位,减速比30,再乘以6000即为每分钟输出轴多少转
motor.lastAngle = motor.totalAngle; //更新转过的圈数
Motor_Send();//发送速度
}
‘陆’ STM32怎么实现单片机控制LED灯常亮10S后熄灭
首先实现LED灯的点亮和熄灭,控制连接LED灯的管脚输出高低电平就可以实现。如果电流比较大可以增加三极管驱动电路。10秒定时可以用定时器实现,设置一个1秒的定时器。上电点亮LED灯,并开始计时,10秒时间到熄灭LED就可以了。