赞
踩
目录
本文章主要介绍 STM32 电机相关软件的配置,PWM的相关介绍,csdn上面有很多资料
PWM(Pulse Width Modulation) 介绍:
单片机: STM32F103RCT6芯片 high density(相关资料可以去 ST 官网下载)
直流减速电机(自带减速机,减速比为1/64):
步进电机、伺服电机、舵机、无刷电机、有刷电机区别_行稳方能走远的博客-CSDN博客_舵机和步进电机
电机驱动芯片: L293D驱动模块 (支持PWM 调速)
驱动原理可以看下面这个小破站的视频:基本思路
//快速上手L293D电机驱动器,并控制直流电机的正反转和转速【IC原来如此】_哔哩哔哩_bilibili
//轻松学会L293D电机驱动模块应用从0到1详细教程_哔哩哔哩_bilibili
(最后这个视频 介绍的清楚一点)
电路图
电机驱动芯片是采用 H桥集成电路 可以看下面这个 博主的详细介绍
工作原理:将直流电机 的两个引脚 接到 L293D的输出端,通过两个输出端的电流流向,来实现电机的正反转。
(驱动电路内部的逻辑电路方便实现高低电平的控制,来进行正反转)
l293d电机驱动原理_H桥驱动电路如何使电机正反转?原理图及其分析过程,终于懂了..._weixin_39628070的博客-CSDN博客
引脚示意图 (我采用的是16个引脚的,4个接地,VS upply Voltage (pin 8) VSS Logic Supply Voltage (pin 16) 逻辑供电电压 )
里面涉及的部分专有名词缩写:
PWM:脉宽调制
TIMx:单片机定时器,分通用 和 高级 定时器
ARR: 自动重装载寄存器,可以对其值进行定义(
计数器从自动重载寄存器 ARR 值开始计数,每来一个 CK_CNT 脉冲计数器就减 1,直到计数器值为 0,然后计数器又从自动重载寄存器 ARR 值开始递减计数并生成计数器下溢事件
)
CCR: 捕获/比较寄存器 TIMx CCR1/2/3/4
特点:只能向上计数,只能定时,没有外部IO
特点:可以向上/向下计数的定时器,可以定时,可以输出比较,可以输入捕捉,每个定时器有4个IO口
与通用定时器不同的是它有三相电机互补输出信号,并且每个定时器有8个IO口
参考链接:https://www.csdn.net/tags/NtTaQg5sMjYwNjUtYmxvZwO0O0OO0O0O.html
简介: 脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术,广泛应用在从测量、通信到功率控制与变换的许多领域中。
原理与介绍:
部分内容参考自下面文章
参考链接:pwm电机调速的原理介绍与代码实现_Clichong的博客-CSDN博客_pwm调速
1、为什么 PWM 可以调速?
简单来说,就是通过输出交变的不同占空比的电压,从而实现对电机的转速的调节。
电机两端的电压是101010这么突变,但是电流由于线圈自感电动势的存在,电机中仍然有电流流过,电机并没有停止转动,还是继续转动。那么体现到电机,引入一个 《等效电压》的概念,极限情况下,当占空比很小,全部是低电平,这个时候,电机由于无法驱动,将停止转动。
而:
直流电机转速公式:n=U-(IR+L×di/dt)/Kφ。直流电机的转速是由定子磁场强度、转子线圈匝数及供电电压等共同决定的。
书接上段:当《等效电压》⬇减小的时候,假设其他条件不变(当然肯定会变),转速变小,也就是PWM 占空比变小时,电机转速变慢。
2、PWM实现调速的原理与介绍
STM32F103RCT6中,
PWM模式可以产生一个由ARR 寄存器确定频率,由CCRi 寄存器确定占空比的信号。
占空比:pwm占空比就是一个脉冲周期内有效电平在整个周期所占的比例。
通过调节PWM的占空比就能调节IO口上电压的持续性变化,因此也能够控制外设的功率进行持续性变化,也就能控制直流电机的转速快慢。
那么重点就在于如何调节PWM波形的输出。
脉冲宽度调制模式可以生成一个信号,该信号频率由 TIMx_ARR 寄存器值决定,其占空比则
由 TIMx_CCRx 寄存器值决定。
解析:上面就是设置PWM波其中的一个模式,CCR是可以设置占空比的,TIMx_CNT:计数器自增向上 占空比=ccr/arr*100
当CCRx = 4时候,CNT自增,CNT<CCR时候输出高电平,因为ARR为8,那么占空比就为4/8= 50%
当CCRx = 8时候,CNT自增,CNT<CCR时候输出高电平,因为ARR为8,那么占空比就为4/8= 100%
当CCRx > 8时候,CNT自增,CNT<CCR时候输出高电平,一直输出高电平
当CCRx = 0时候,一直输出低电平
————————————————
原文链接:https://blog.csdn.net/yanyonglin123/article/details/122870270
上述原文方便理解 PWM 的占空比设置原理
3、PWM信号配置流程
①TIM_TimeBaseInitTypeDef 是 定义 PWM 相关参数的结构体 (里面有四个成员 TIM_Period、TIM_Prescaler、TIM_CKD、TIM_CounterMode)
TIM_TimeBaseStructure.TIM_Period = arr; //自动重载寄存器的值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
TIM_OCInit 结构体 :
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择定时器模式:TIM 脉冲宽度调制模式2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_Pulse = 0; //设置待装入捕获比较寄存器的脉冲值
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性:TIM输出比较极性高
TIM_OC3Init(TIM4, &TIM_OCInitStructure); //根据TIM_OCInitStructure中指定的参数粗始化TIMX
TIM_OC4Init(TIM4, &TIM_OCInitStructure); //根据TIM_OCInitStructure中指定的参数粗始化TIMX
GPIO_Init 结构体 用来定义 单片机的引脚 要使用 GPIO 引脚 都要用这个结构体来定义引脚
② 使能 时钟,使寄存器工作
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);// 使能通用寄存器TIM4
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB , ENABLE); // 使能 GPIO 外设时钟
使能 RCC_APB2PeriphClockCmd
使能怎么理解呢?enable ,因为STM 32 为了避免功耗,通过使能 开关 来使能时钟,这样寄存器才能工作。
51单片机中就一个时钟系统包一切都包了,在STM 32 中,我们很明确地做好了分工,让大家各司其职。
什么时候用RCC_APB1PeriphClockCmd这个外设时钟,什么时候用RCC_APB2PeriphClockCmd这个外设时钟呢?
通过上面图可以发现,寄存器TIM4 我们是用 RCC_APB1PeriphClockCmd 使能,GPIO 外设时钟是用 RCC_APB2PeriphClockCmd 使能。
4、PWM调速代码
GPIO_InitStructure.GPIO_Pin = LEFT_MOTOR_GO; //左电机方向控制 PB7
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(LEFT_MOTOR_GO_GPIO, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = LEFT_MOTOR_PWM; 左电机方向控制 PB8
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(LEFT_MOTOR_PWM_GPIO, &GPIO_InitStructure);
GPIO配置函数:
- void TIM4_PWM_Init(unsigned short arr,unsigned short psc) //arr set 7199£»pcs is 0
- {
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // TIM_TimeBaseInitTypeDef 定义结构体
- TIM_OCInitTypeDef TIM_OCInitStructure;
- GPIO_InitTypeDef GPIO_InitStructure;
-
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB , ENABLE);
-
- GPIO_InitStructure.GPIO_Pin = LEFT_MOTOR_GO;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_Init(LEFT_MOTOR_GO_GPIO, &GPIO_InitStructure);
-
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_Init(LEFT_MOTOR_PWM_GPIO, &GPIO_InitStructure);
-
-
- GPIO_InitStructure.GPIO_Pin = RIGHT_MOTOR_GO; //
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_Init(RIGHT_MOTOR_GPIO, &GPIO_InitStructure);
-
-
- GPIO_InitStructure.GPIO_Pin = RIGHT_MOTOR_PWM;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_Init(RIGHT_MOTOR_PWM_GPIO, &GPIO_InitStructure);
-
- TIM_TimeBaseStructure.TIM_Period = arr;
- TIM_TimeBaseStructure.TIM_Prescaler =psc;
- TIM_TimeBaseStructure.TIM_ClockDivision = 0;
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
- TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); //
-
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
- TIM_OCInitStructure.TIM_Pulse = 0;
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //
- TIM_OC3Init(TIM4, &TIM_OCInitStructure); //
- TIM_OC4Init(TIM4, &TIM_OCInitStructure); //
-
- TIM_CtrlPWMOutputs(TIM4,ENABLE);
-
- TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);
- TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);
-
- TIM_ARRPreloadConfig(TIM4, ENABLE);
- TIM_Cmd(TIM4, ENABLE);
- }
电机主函数:
运动函数:
通过一个SetMotorSpeed设置运动函数, cSpeed 用于定义电机的转速(实际绝对数值就是上面的占空比0-100),
- void SetMotorSpeed(unsigned char ucChannel,signed char cSpeed)
- {
- // static short sMotorSpeed = 0;
- short sPWM;
- // float fDir = 1;
-
- if (cSpeed>=100) cSpeed = 100;
- if (cSpeed<=-100) cSpeed = -100;
-
- sPWM = 7201 - fabs(cSpeed)*72; // spwm=7201-50*72=3101
- switch(ucChannel) //ucChannel=1 leftwheel; =0 and right rightwheel
- {
- case 0://右轮
- TIM_SetCompare3(TIM4,sPWM); //
- if (cSpeed>0)
- RIGHT_MOTOR_GO_RESET;
- else if(cSpeed<0)
- RIGHT_MOTOR_GO_SET;
- break;
- case 1://左轮
- TIM_SetCompare4(TIM4,sPWM); //TIM_SetCompare4 is
- if (cSpeed>0)
- LEFT_MOTOR_GO_SET; //#define LEFT_MOTOR_GO_SET GPIO_SetBits(LEFT_MOTOR_GO_GPIO,LEFT_MOTOR_GO)
- else if (cSpeed<0)
- LEFT_MOTOR_GO_RESET;
- break;
- }
- }
其中 TIM_SetCompare3函数定义如下:
- void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3)
- {
- /* Check the parameters */
- assert_param(IS_TIM_LIST3_PERIPH(TIMx)); //assert_param 库函数用于传参的检验
- /* Set the Capture Compare3 Register value */
- TIMx->CCR3 = Compare3; // Compare3 赋值给CCR3
- }
assert_param 库函数在stm32 手册中是 如下定义:
上述TIM_setcompare3库函数是做如下定义,主要功能是捕获比较3寄存器值,上述程序中是将
main.c 主函数
- #include "stm32f10x.h"
- #include "delay.h"
- #include "motor.h"
-
-
- int main(void)
- {
- delay_init();
- TIM4_PWM_Init(7199,0); //初始化PWM
- while(1)
- {
- STM32_back(30,1000);//后退1s,速度设置为50%
- STM32_brake(500);//停止0.5s
- STM32_run(50,1000);
- STM32_brake(500);
- STM32_Left(50,1000);
- STM32_Right(50,1000);
- STM32_Spin_Right(50,1000);
- STM32_Spin_Left(50,1000);
- STM32_brake(500);
- }
- }
motor.c 主函数(包含上面的PWM 配置函数 和 电机速度设置函数(让电机动起来), 及 电机各种运动函数 ),前后文都有详细说明,就不赘述啦。。
- #include "motor.h"
- #include "Math.h"
- #include "delay.h"
- #include "stm32f10x.h" // Device header
-
- signed short sPWMR,sPWML,dPWM;
-
-
- void TIM4_PWM_Init(unsigned short arr,unsigned short psc) //arr set 7199£»pcs is 0
- {
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
- TIM_OCInitTypeDef TIM_OCInitStructure;
- GPIO_InitTypeDef GPIO_InitStructure;
-
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);//
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOB , ENABLE);
-
- GPIO_InitStructure.GPIO_Pin = LEFT_MOTOR_GO;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_Init(LEFT_MOTOR_GO_GPIO, &GPIO_InitStructure);
-
- GPIO_InitStructure.GPIO_Pin = LEFT_MOTOR_PWM;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_Init(LEFT_MOTOR_PWM_GPIO, &GPIO_InitStructure);
-
-
- GPIO_InitStructure.GPIO_Pin = RIGHT_MOTOR_GO;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
- GPIO_Init(RIGHT_MOTOR_GPIO, &GPIO_InitStructure);
-
-
- GPIO_InitStructure.GPIO_Pin = RIGHT_MOTOR_PWM;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
- GPIO_Init(RIGHT_MOTOR_PWM_GPIO, &GPIO_InitStructure);
-
- TIM_TimeBaseStructure.TIM_Period = arr;
- TIM_TimeBaseStructure.TIM_Prescaler =psc;
- TIM_TimeBaseStructure.TIM_ClockDivision = 0;
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
- TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure);
-
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;
- TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
- TIM_OCInitStructure.TIM_Pulse = 0;
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
- TIM_OC3Init(TIM4, &TIM_OCInitStructure);
-
- TIM_OC4Init(TIM4, &TIM_OCInitStructure);
-
-
- TIM_CtrlPWMOutputs(TIM4,ENABLE);
-
-
- TIM_OC3PreloadConfig(TIM4, TIM_OCPreload_Enable);
- TIM_OC4PreloadConfig(TIM4, TIM_OCPreload_Enable);
-
- TIM_ARRPreloadConfig(TIM4, ENABLE);
- TIM_Cmd(TIM4, ENABLE);
- }
-
- void SetMotorSpeed(unsigned char ucChannel,signed char cSpeed)
- {
- // static short sMotorSpeed = 0;
- short sPWM;
- // float fDir = 1;
-
- if (cSpeed>=100) cSpeed = 100;
- if (cSpeed<=-100) cSpeed = -100;
-
- sPWM = 7201 - fabs(cSpeed)*72; // spwm=7201-50*72=3101
- switch(ucChannel) //ucChannel=1 leftwheel; =0 and right rightwheel
- {
- case 0:
- TIM_SetCompare3(TIM4,sPWM); // CCR3/ARR ARR= 7199 CRR3=SPWM=3101
- if (cSpeed>0)
- RIGHT_MOTOR_GO_RESET;
- else if(cSpeed<0)
- RIGHT_MOTOR_GO_SET;
- break;
- case 1:
- TIM_SetCompare4(TIM4,sPWM); //TIM_SetCompare4 is
- if (cSpeed>0)
- LEFT_MOTOR_GO_SET; //#define LEFT_MOTOR_GO_SET GPIO_SetBits(LEFT_MOTOR_GO_GPIO,LEFT_MOTOR_GO)
- else if (cSpeed<0)
- LEFT_MOTOR_GO_RESET;
- break;
- }
- }
-
- //--------------------------------- -------------------------------
- void STM32_run(signed char speed,int time)
- {
- signed char f_speed = - speed;
- SetMotorSpeed(1,f_speed);
- SetMotorSpeed(0,speed);
- delay_ms(time);
-
- }
-
- void STM32_brake(int time) //ɲ³µº¯Êý
- {
- SetMotorSpeed(1,0);
- SetMotorSpeed(0,0);
- RIGHT_MOTOR_GO_RESET;
- LEFT_MOTOR_GO_RESET;
- delay_ms(time);
- }
-
- void STM32_Left(signed char speed,int time)
- {
- SetMotorSpeed(1,0);
- SetMotorSpeed(0,speed);
- delay_ms(time);
-
- }
- void STM32_Spin_Left(signed char speed,int time)
- {
- SetMotorSpeed(1,speed);
- SetMotorSpeed(0,speed);
- delay_ms(time);
-
- }
- void STM32_Right(signed char speed,int time)
- {
- signed char f_speed = - speed;
- SetMotorSpeed(1,f_speed);
- SetMotorSpeed(0,0);
- delay_ms(time);
-
- }
- void STM32_Spin_Right(signed char speed,int time)
- {
- signed char f_speed = - speed;
- SetMotorSpeed(1,f_speed);
- SetMotorSpeed(0,f_speed);
- delay_ms(time);
-
- }
- void STM32_back(signed char speed,int time)
- {
- signed char f_speed = - speed;
- SetMotorSpeed(1,speed);
- SetMotorSpeed(0,f_speed);
- delay_ms(time);
-
- }
main.h 函数
- #ifndef __MOTOR_H_
- #define __MOTOR_H_
-
- #include "stm32f10x.h"
-
- //void MotorGPIO_Configuration(void);
- //void run(void);
-
- void TIM4_PWM_Init(unsigned short arr,unsigned short psc);
- void SetMotorSpeed(unsigned char ucChannel,signed char cSpeed);
-
- void STM32_run(signed char speed,int time);
- void STM32_brake(int time);
- void STM32_Left(signed char speed,int time);
- void STM32_Spin_Left(signed char speed,int time);
- void STM32_Right(signed char speed,int time);
- void STM32_Spin_Right(signed char speed,int time);
- void STM32_back(signed char speed,int time);
-
-
-
- //电机驱动引脚IO 定义
- /*
- LEFT_MOTOR_GO PB8
- LEFT_MOTOR_PWM PB9
- RIGHT_MOTOR_GO PA6
- RIGHT_MOTOR_PWM PB10
- */
-
- #define LEFT_MOTOR_GO GPIO_Pin_7
- #define LEFT_MOTOR_GO_GPIO GPIOB
- #define LEFT_MOTOR_GO_SET GPIO_SetBits(LEFT_MOTOR_GO_GPIO,LEFT_MOTOR_GO)
- #define LEFT_MOTOR_GO_RESET GPIO_ResetBits(LEFT_MOTOR_GO_GPIO,LEFT_MOTOR_GO)
-
- #define LEFT_MOTOR_PWM GPIO_Pin_8
- #define LEFT_MOTOR_PWM_GPIO GPIOB
- #define LEFT_MOTOR_PWM_SET GPIO_SetBits(LEFT_MOTOR_PWM_GPIO,LEFT_MOTOR_PWM)
- #define LEFT_MOTOR_PWM_RESET GPIO_ResetBits(LEFT_MOTOR_PWM_GPIO,LEFT_MOTOR_PWM)
-
- #define RIGHT_MOTOR_GO GPIO_Pin_4
- #define RIGHT_MOTOR_GPIO GPIOA
- #define RIGHT_MOTOR_GO_SET GPIO_SetBits(RIGHT_MOTOR_GPIO,RIGHT_MOTOR_GO)
- #define RIGHT_MOTOR_GO_RESET GPIO_ResetBits(RIGHT_MOTOR_GPIO,RIGHT_MOTOR_GO)
-
- #define RIGHT_MOTOR_PWM GPIO_Pin_9
- #define RIGHT_MOTOR_PWM_GPIO GPIOB
- #define RIGHT_MOTOR_PWM_SET GPIO_SetBits(RIGHT_MOTOR_PWM_GPIO,RIGHT_MOTOR_PWM)
- #define RIGHT_MOTOR_PWM_RESET GPIO_ResetBits(RIGHT_MOTOR_PWM_GPIO,RIGHT_MOTOR_PWM)
-
- #endif
前进函数:
通过一个 STM32_run(signed char speed,int time) 设置前进 函数
本实验有左右两个电机,4个pin脚来控制电机正反转 及转速,
下面函数主要功能:
调用
SetMotorSpeed(1,f_speed);//左轮 正转
SetMotorSpeed(0,speed);//右轮正转
通过延时函数来确定转动时间
delay_ms(time); //延时函数
- void STM32_run(signed char speed,int time) //前进函数
- {
- signed char f_speed = - speed;
- SetMotorSpeed(1,f_speed);//左轮
- SetMotorSpeed(0,speed);//右轮
- delay_ms(time); //延时函数
-
- }
延时函数:
STM32 延时函数概述: LED 灯的闪烁,LCD屏的刷新、电机的控制和一些接口驱动如I2C、SPI总线驱动等都要用到延时函数。 这几个 不太懂,我现在是STM 32 小白,想了解的,可以看下面:链接介绍。
详细介绍GPIO、I2C、SPI通讯原理以及物理层原理_17岁boy想当攻城狮的博客-CSDN博客_gpio和spi
分类:软件延时 和 硬件延时
软件延时即是通过让CPU“空转”,通过计算不同指令周期的时间,对照CPU主频大小,大致算出延时时间,很显然,缺点:这种实现方法不精确,优点:但却很好实现;
硬件延时:即是在系统时钟的驱动下,通过硬件对寄存器设定累加或累减直到满足一定条件,
优点:这种延时方法能够做到很精确,而且不占用CPU资源,CPU可以设定好延时时间后去干别的活,但是可能要对所用到的寄存器进行设置。这里再补充一下,通过硬件进行延时,其实现又分为配置定时器延时和通过中断延时。
我们这里采用第二种
代码实现:
第一种:
void delay_us(u16 t)
{
u16 i =0;
for(i=0;i
}
第二种:硬件延时
定时器延时:STM32中CM3内核中包含一个SysTick定时器,它是一个24位倒计数定时器,计数到0后又从RELOAD寄存器中自动重装定时器初值。
配置延时函数:
外部时钟8MHZ,倍频到72MHZ,然后SysTick定时器再8分频,所以SysTick定时器的工作频率为9MHZ.也就是说一秒跳动9MHZ.又定义了fac_us和fac_ms.它们分别为延时的基数。
STM 32中 有 SysTick_Type 形式结构体定义
- typedef struct
- {
- __IO uint32_t CTRL; /*!< Offset: 0x00 SysTick Control and Status Register */
- __IO uint32_t LOAD; /*!< Offset: 0x04 SysTick Reload Value Register */
- __IO uint32_t VAL; /*!< Offset: 0x08 SysTick Current Value Register */
- __I uint32_t CALIB; /*!< Offset: 0x0C SysTick Calibration Register */
- } SysTick_Type;
其中:
CTRL寄存器控制着SysTick定时器,LOAD寄存器表示计数完了以后再次重装的值,也就是下面函数马上要根据实际定时长度进行赋值的,VAL寄存器表示当前当前计数值的值,CALIB不常用,不用关心。
代码如下:
- void delay_ms(u16 nms)
- {
- u32 temp;
- SysTick->LOAD=(u32)nms*fac_ms; //时间加载 (SysTick->LOAD为24bit)
- SysTick->VAL =0x00; //清空计数器
- SysTick->CTRL|=SysTick_CTRL_ENABLE_Msk ; //开始倒数
- do
- {
- temp=SysTick->CTRL;
- }while((temp&0x01)&&!(temp&(1<<16))); //等待时间到达 do while 循环语句
- SysTick->CTRL&=~SysTick_CTRL_ENABLE_Msk; //关闭计数器
- SysTick->VAL =0X00; //清空计数器
- }
/*其中
|=是按位或并赋值的意思,和+= -= 用法差不多,
a=128: (0001 0000 0000)
b=127: (0000 1111 1111) (高位用0补齐)
按位或就是(0001 1111 1111)=255
则 a|=b; a 的值变为 就是 255 ,a与b 做或运算之后,将值赋给a */
&=~ 这个运算符 的运算顺序是,先做~ (程序中取非 数值由1变为0)然后,&= CTRL结构体数值为0 ,关闭计数器
参考链接:C++运算符优先级_nicky_zs的博客-CSDN博客_c++运算符的优先级
/*其中
c语言中u8,u16,u32和int区别为符号不同、数据范围不同、内存占用的空间不同。
一、符号不同
1、u8:u8表示无符号char字符类型。
2、u16:u16表示无符号short短整数类型。
3、u32:u32表示无符号int基本整数类型。
4、int:int表示带符号int基本整数类型。
二、数据范围不同
1、u8:u8的数据范围为0~+127[0~2^8-1]。
2、u16:u16的数据范围为0~+65535[0~2^16-1]。
3、u32:u32的数据范围为0+2147483647[02^32-1]。
4、int:int的数据范围为-2147483648~+2147483647[-2^31~2^31-1]。
三、内存占用空间不同
1、u8:u8的内存占用空间大小为只占一个字节。
2、u16:u16的内存占用空间大小为占用两个字节。
3、u32:u32的内存占用空间大小为占用四个字节。
4、int:int的内存占用空间大小为占用八个字节。
- #define SysTick_CTRL_ENABLE_Pos 0 /*!< SysTick CTRL: ENABLE Position */
- #define SysTick_CTRL_ENABLE_Msk (1ul << SysTick_CTRL_ENABLE_Pos) /*!< SysTick CTRL: ENABLE Mask */
/*其中 1uL 为长整型1 , SysTick_CTRL_ENABLE_Msk 数值为长整型1
/***************那怎么来计算我寄存器能延时多久?********************************************/
本例中,前进函数中,传递的实参 为1000 即在函数中,nms=1000.
SysTick->LOAD=(u32)nms*fac_ms;
static u8 fac_us=0;//us延时倍乘数 static u16 fac_ms=0;//ms延时倍乘数 void delay_init() { SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8);//选择外部时钟HCLK/8 fac_us=SystemCoreClock/8000000; //72000000/8000000 = 9 fac_ms=(u16)fac_us*1000; // 值为9000 } 这个函数是us延时函数,上面已经说了,SysTick时钟工作频率为9MHZ. 比如要延时10us.时SysTick->LOAD = 10*fac_us =10*9 =90.对于每秒跳动9MHZ的时钟,数90下,正好时间是10us.下面的以此类推。 ———————————————— 原文链接:https://blog.csdn.net/weixin_31451231/article/details/112891411
/* nms延时函数
注意nms的范围,SysTick->LOAD为24位寄存器,所以,参数限制为(72MHz下):
nms*fac_ms=nms*9000 <2^24
算得对72M条件下,nms<=1864 */
中断延时:同样使用SysTick定时器实现延时,还可以通过中断方式实现,通过库函数SysTick_Config()配置SysTick定时器,同时开中断,由于设置的nms会在中断中递减,所以delay_ms函数中只要不断查询time_delay的值是否为0即可,
unsigned long time_delay;
void delay_ms(volatile unsigned long nms)
{
if(SysTick_Config(SYSCLK_FREQ_72MHz/1000))
{
while(1);
}
time_delay = nms;
while(time_delay);
SysTick->CTRL = 0x00;
SysTick->VAL =0x00;
}
中断中的实现:
void SysTick_Handler(void)
{
if(time_delay)
{
time_delay--;
}
}
————————————————
以上延时很多都是参考链接:https://blog.csdn.net/weixin_31451231/article/details/112891411
刹车:
通过将占空比设置为0,全部是低电平来控制刹车,延时函数来设置刹车时间
- void STM32_brake(int time) //刹车函数
- {
- SetMotorSpeed(1,0);//左轮速度设置为0
- SetMotorSpeed(0,0);//右轮速度设置为0
- RIGHT_MOTOR_GO_RESET;
- LEFT_MOTOR_GO_RESET;
- delay_ms(time); //延时函数
- }
左右转:
- void STM32_Spin_Left(signed char speed,int time) //左转
- {
- SetMotorSpeed(1,0);//左轮不动
- SetMotorSpeed(0,speed); //右轮为正
- delay_ms(time); // 延时毫秒
-
- }
-
- void STM32_Right(signed char speed,int time) //右转
- {
- signed char f_speed = - speed; //为啥赋负值呢?
- SetMotorSpeed(1,f_speed);
- SetMotorSpeed(0,0);
- delay_ms(time);
后退函数:
- void STM32_back(signed char speed,int time) //后退
- {
- signed char f_speed = - speed;
- SetMotorSpeed(1,speed);//
- SetMotorSpeed(0,f_speed);//
- delay_ms(time); //
-
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。