第二部分,定时器的输出比较功能

OC(Output Compare)输出比较

输出比较可以通过比较CNT与CCR寄存器值的关系,来对输出电平进行置1、置0或翻转的操作,用于输出一定频率和占空比的PWM波形

每个高级定时器和通用定时器都拥有4个输出比较通道

高级定时器的前3个通道额外拥有死区生成和互补输出的功能

IC:输入捕获、CC:表示输入捕获和输出比较的单元、CCR:捕获比较寄存器

CCR是输入捕获和输出捕获比较共用的,会根据输出和输入比较的情况,转换对于寄存器功能;在输出比较中,电路会比较CNT和CCR的值,CNT自增,当CNT大于CCR、等于CCR和小于CCR时,会输出对于的置0或1,从而输出一个电平不断跳变的PWN波形;共用一个CNT计数器

PWM(Pulse Width Modulation)脉冲宽度调制

最常见的用途是产生PWM波形,用于驱动电机等设备

在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟参量,常应用于电机控速等领域,是数字信号

PWM参数:

     频率 = 1 / TS            占空比 = TON / TS           分辨率 = 占空比变化步距

Ts图下:代表一个高低电平变换周期的时间

Ton:高电平的时间、Toff  同理

 

频率越快,等效模拟的信号就越平稳,性能的开销就大

在今天的实验中我们实现的LED呼吸灯中,正常来说数字信号只有0和1,所以只会实现亮和灭的情况,但是通过PWM波形用数字信号来输出一段模拟信号就可以实现呼吸灯:让LED灯不断点亮、熄灭、点亮、熄灭,当点亮和熄灭的频率足够大时,LED就不会闪烁了,而是呈现出一个中等亮度,这时进行调控点亮和熄灭的时间比例,就能呈现出不同亮度;(唯快不破)

输出比较通道(通用)

整体电路所在的方位       

CCR与CNT比较,输出数字信号(0和1),输出控制器会改变它输出OC1PRE的高低电平;REF信号可以进入主模式控制器,这样可以将REF映射到TRGO输出上去;主要是走下一路,达到极性选择,通过极性选择到输出使能电路,选择要不要输出,输出至OC1引脚(引脚对应表)

极性选择 :给这个寄存器写0,信号就会往上走,信号电平不翻转;写1,信号会往下走,信号会通过一个非门取反,此时输出的信号就是输入信号高低电平反转的信号

REF信号:实际就是指这里信号的高低电平——参考信号

输出比较的8种模式

输出控制器里的执行逻辑,灵活地控制REF的输出;通过一个寄存器来进行配置

作用:

冻结:在输出PWM波形时,想要进行停止,就可以设置此,此时就暂停了输出,并且高低电平也维持在暂停时的样子

匹配时置无效电平&匹配时置有效电平:一般是高级定时器的说法,与关断、刹车灯功能一起的,有效电平就是高电平,无效电平则反之,一次性的,不适合输出连续变化的波形。

匹配时电平翻转:可以输出一个频率可调,占空比始终50%的PWM波形:比如当CCR设置为0时,CNT每次更新清0时,就会产生一次CNT=CCR的事件,从而导致输出电平翻转一次,每更新两次,输出为一个周期,同时高低电平时间始终相等,占空比始终为50%;则关系是输出频率=更新频率/2

强制为无效电平&强制为有效电平:与冻结差不多

PWM模式1& PWM模式2:用于输出频率和占空比都可调的PWM波形,主要使用

PWM的基本结构:

红色代表:CCR

蓝色代表:CNT

黄色代表:ARR

下面的图表示输出的PWM波形:

蓝色从0开始自增,直到与黄色相同时,出现中断,更新清0继续自增;

红色设置为30,因为输出控制器设置的模式是,当CNT<CCR时,为高电平;所以根据蓝色与黄色不断的更新,CCR会输出不同的REF信号,同时不仅可以调整红色设置的值,并且占空比也受CCR的调控。


参数计算

  • PWM频率:    Freq = CK_PSC / (PSC + 1) / (ARR + 1)
  • PWM占空比:       Duty = CCR / (ARR + 1)
  • PWM分辨率:       Reso = 1 / (ARR + 1)

CK_PSC:输入定时器的总频率;PSC=预分频器的分频数

占空比变化越细,越好

输出比较通道(高级定时器)

与通用定时器的区别:

当外部设备是一个大功率开关管(Mos管),两边开关分别连接着OC1和OC1N,假设是高电平导通,电平断开;两个开关中间对应输出;如果上管导通,下管断开,则是高电平,反之;但是如果两边都导通,则会短路或者两边都断开,则是高阻态;这就是推挽电路;如果有两个这样的电路相连接,则是H桥电路,可控制直流电路正反转了;如果有三个这样的电路,就可以用于驱动三相无刷电机;

回归内部,如果在上管关断的瞬间,下管立刻就打开,那可能会上管还没完全关断,下管已经导通,出现两边同时导通的现象,从而功率损耗,所以避免这个问题就设置了死区发生器电路,会在关闭的时候延迟一段时间,再导通;

外部设备:

舵机简介

  • 舵机是一种根据输入PWM信号占空比来控制输出角度的装置
  • 输入PWM信号要求:周期为20ms(50Hz),高电平宽度为0.5ms~2.5ms

 

逻辑:PWM信号输入到控制板,给控制板一个指定的目标角度,然后这个电位器检测输出轴的当前角度,如果大于目标角度,电机会反转,反之,最终使输出轴固定在指定角度。

输出轴角度:时间是指此输入信号的电平变化到下一个电平变化的时间

这里是PWM当作一个通信协议

硬件电路:

引脚定义图

直流电机及驱动简介

  • 直流电机是一种将电能转换为机械能的装置,有两个电极,当电极正接时,电机正转,当电极反接时,电机反转
  • 直流电机属于大功率器件,GPIO口无法直接驱动,需要配合电机驱动电路来操作
  • TB6612是一款双路H桥型的直流电机驱动芯片,可以驱动两个直流电机并且控制其转速和方向

硬件电路:

 

接线图

6-3 PWM驱动LED呼吸灯

这里我们LED正极接入A0引脚,负极在GND的方法,这样是高电平点亮,正极性驱动;占空比越高越亮;


  1. RCC开启时钟,把TIM外设和GPIO外设的时钟打开
  2. 配置时基单元,包括时钟源选择
  3. 配置输出比较单元:包括CCR的值、输出比较模式、极性选择、输出使能等由结构体统一配置
  4. 配置GPIO,把PWM对于的GPIO口,初始化为复用推挽输出的配置
  5. 运行控制

相关库函数:

//配置输出比较模块,一个函数配置一个单元,一共有四个单元,不同的通道对不同的GPIO口,对应关系:

得知:TIM2的ETR引脚和CH1通道1的引脚复用在PA0引脚上,所以我们要输出TIM2的OC1通道输出PWM,那就只能在PA0的引脚上输出;如果想跳出这个映射,则需要看这重定义功能表里是否对应接口映射,再通过配置AFIO来完成。

void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);//用来输出比较结构体赋一个默认值

//小功能和运行参数

//配置强制输出模式,就是设置100%高电平一样

void TIM_ForcedOC1Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);

void TIM_ForcedOC2Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);

void TIM_ForcedOC3Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);

void TIM_ForcedOC4Config(TIM_TypeDef* TIMx, uint16_t TIM_ForcedAction);

//四个函数用来配置CCR寄存器的预装功能的,就是缓存寄存器

void TIM_OC1PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);

void TIM_OC2PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);

void TIM_OC3PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);

void TIM_OC4PreloadConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPreload);

//用来配置快速使能的

void TIM_OC1FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);

void TIM_OC2FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);

void TIM_OC3FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);

void TIM_OC4FastConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCFast);

//外部事件时清除REF信号

void TIM_ClearOC1Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);

void TIM_ClearOC2Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);

void TIM_ClearOC3Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);

void TIM_ClearOC4Ref(TIM_TypeDef* TIMx, uint16_t TIM_OCClear);

//用来单独设置输出比较的极性,带N的就是高级定时器里互补通道的配置

void TIM_OC1PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);

void TIM_OC1NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);

void TIM_OC2PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);

void TIM_OC2NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);

void TIM_OC3PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);

void TIM_OC3NPolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCNPolarity);

void TIM_OC4PolarityConfig(TIM_TypeDef* TIMx, uint16_t TIM_OCPolarity);

//单独修改输出使能参数的

void TIM_CCxCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCx);

void TIM_CCxNCmd(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_CCxN);

//选择输出比较模式

void TIM_SelectOCxM(TIM_TypeDef* TIMx, uint16_t TIM_Channel, uint16_t TIM_OCMode);

//用来单独更改CCR寄存器值的函数,更改占空比

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);

void TIM_SetCompare2(TIM_TypeDef* TIMx, uint16_t Compare2);

void TIM_SetCompare3(TIM_TypeDef* TIMx, uint16_t Compare3);

void TIM_SetCompare4(TIM_TypeDef* TIMx, uint16_t Compare4);

//仅高级定时器使用,在使用高级定时器输出PWM时,需要调用这个函数使能输出

void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);

示波器:1KHz,CCR为50,此时占空比为50%

1KHz,CCR为10,占空比减少至10%,灯变暗

1KHz,CCR为90,占空比增加至90%,灯变亮

main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint8_t i;
int main(void){OLED_Init();PWM_Init();while(1){for(i=0;i<=100;i++){PWM_set(i);Delay_ms(50);}for(i=0;i<=100;i++){PWM_set(100-i);Delay_ms(50);}}
}PWM.c
#include "stm32f10x.h"                  // Device header
void PWM_Init(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);  //开启APB1的时钟函数,TIM2在APB1总线中//选择时基单元TIM_InternalClockConfig(TIM2);//系统默认是内部时钟,不写也可以//配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //给输入的滤波器一个采样频率TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数方式TIM_TimeBaseInitStructure.TIM_Period=100-1;//ARR自动重装器的值TIM_TimeBaseInitStructure.TIM_Prescaler=720-1;//PSC预分配器的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器的值,我们用的通用寄存器,所以直接写0就好了TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2,TIM_IT_Update);//初始化输出比较单元:频率1kHz,占空比为50%的PWM波形//TIM_OCInitStructure函数有一部分是高级定时器里的,要么就把全部成员拉出来配置,要么就用TIM_OCStructInit先赋一个初始值0TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//8种输出比较模式TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较极性TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出使能//因此通过观察得知,我们需要完成的呼吸灯的变化效果就与CCR的值息息相关,TIM_SetCompare1单独更改CCR值的函数就有用了TIM_OCInitStructure.TIM_Pulse=0;//就是CCR的值TIM_OC1Init(TIM2, &TIM_OCInitStructure);//现在把TIM2上的OC1通道上就可以输出PWM波形,这个波形是需要借助GPIO口才能输出,邮引脚定义表得知我们锁定在PA0引脚上/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出,因为普通的推挽输出,引脚的控制权是来自于输出数据寄存器的,那我们这时想让的是定时器来控制引脚,则需要使用复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);						//启动定时器TIM_Cmd(TIM2, ENABLE);
}
void PWM_set(uint16_t compare){TIM_SetCompare1(TIM2,compare);
}

重定义映射:

开启AFIO时钟

使用引脚重映射配置GPIO_PinRemapConfig

与TIM2的映射关系一共4个,我们要把PA0映射到PA5,就使用2或则和4的关系

APIO的函数参数

注意PA15上电后默认复用调试端口JTDI,需要把它的默认给取消了才能正常使用

继续使用GPIO_PinRemapConfig,参数

三个参数:就是来解除复用的:
GPIO_Remap_SWJ_NoJTRST  :解除JTRST引脚的复用

GPIO_Remap_SWJ_JTAGDisable  :解除JTAG调试端口的复用

GPIO_Remap_SWJ_Disable  :把SWD和JTAG的调试端口全部解除,此参数需要考虑使用,因为如果使用了,那STLInk就没有输出端口了,需要额外去配置

PWM.c
#include "stm32f10x.h"                  // Device header
void PWM_Init(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);  //开启APB1的时钟函数,TIM2在APB1总线中//用引脚重映射,所以需要考虑使用AFIORCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);GPIO_PinRemapConfig(GPIO_FullRemap_TIM2,ENABLE);//PA0到PA15//注意PA15也有自己的默认,需要把它的默认给取消了才能正常使用GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//选择时基单元TIM_InternalClockConfig(TIM2);//系统默认是内部时钟,不写也可以//配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //给输入的滤波器一个采样频率TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数方式TIM_TimeBaseInitStructure.TIM_Period=100-1;//ARR自动重装器的值TIM_TimeBaseInitStructure.TIM_Prescaler=720-1;//PSC预分配器的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器的值,我们用的通用寄存器,所以直接写0就好了TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2,TIM_IT_Update);//初始化输出比较单元:频率1kHz,占空比为50%的PWM波形//TIM_OCInitStructure函数有一部分是高级定时器里的,要么就把全部成员拉出来配置,要么就用TIM_OCStructInit先赋一个初始值0TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//8种输出比较模式TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较极性TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出使能//因此通过观察得知,我们需要完成的呼吸灯的变化效果就与CCR的值息息相关,TIM_SetCompare1单独更改CCR值的函数就有用了TIM_OCInitStructure.TIM_Pulse=0;//就是CCR的值TIM_OC1Init(TIM2, &TIM_OCInitStructure);//现在把TIM2上的OC1通道上就可以输出PWM波形,这个波形是需要借助GPIO口才能输出,邮引脚定义表得知我们锁定在PA0引脚上/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出,因为普通的推挽输出,引脚的控制权是来自于输出数据寄存器的,那我们这时想让的是定时器来控制引脚,则需要使用复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15;//重映射GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);						//启动定时器TIM_Cmd(TIM2, ENABLE);
}
void PWM_set(uint16_t compare){TIM_SetCompare1(TIM2,compare);
}

6-4 PWM驱动舵机

CCR为500,对应转0°

修改参数:

CCR为2500,对应180°;

修改参数:

CCR为1500,对应90°;

main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "key.h"
#include "PWM.h"uint8_t Keynum=0;//按键键码
float Angle;//角度变量
int main(void){OLED_Init();PWM_Init();
//	//PWM_set(500);//此时时间为0.5ms,处于不动的状态//PWM_set(2500);//此时时间为2.5ms,转180°
//	PWM_set(1500);//此时时间为1.5ms,转90°Key_Init();Servo_Init();OLED_ShowString(1,1,"Angle:");while(1){Keynum=Key_GetNum();if(Keynum==1){Angle+=30;if(Angle>180){Angle=0;}}Servo_Setangle(Angle);OLED_ShowNum(1,7,Angle,3);}
}
Servo.c
#include "stm32f10x.h"                  // Device header
#include "PWM.h"
//给舵机一个模块,用来实现目标功能void Servo_Init(void){PWM_Init();
}void Servo_Setangle(float Angle){PWM_set(Angle/180*2000+500);
}
PWM.c
#include "stm32f10x.h"                  // Device header
void PWM_Init(void){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);  //开启APB1的时钟函数,TIM2在APB1总线中/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟,设置舵机PWM的接口/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//复用推挽输出,因为普通的推挽输出,引脚的控制权是来自于输出数据寄存器的,那我们这时想让的是定时器来控制引脚,则需要使用复用推挽输出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);		//选择时基单元TIM_InternalClockConfig(TIM2);//系统默认是内部时钟,不写也可以//配置时基单元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //给输入的滤波器一个采样频率TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//计数方式TIM_TimeBaseInitStructure.TIM_Period=20000-1;//ARR自动重装器的值TIM_TimeBaseInitStructure.TIM_Prescaler=72-1;//PSC预分配器的值TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;//重复计数器的值,我们用的通用寄存器,所以直接写0就好了TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);TIM_ClearFlag(TIM2,TIM_IT_Update);//初始化输出比较单元:舵机要求的周期是20ms,则频率为1/20ms=50Hz,要求高电平时间是0.5-2.5ms//PWM频率:	Freq = CK_PSC / (PSC + 1) / (ARR + 1)//PWM占空比:	Duty = CCR / (ARR + 1)//PWM分辨率:	Reso = 1 / (ARR + 1)//TIM_OCInitStructure函数有一部分是高级定时器里的,要么就把全部成员拉出来配置,要么就用TIM_OCStructInit先赋一个初始值0TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM1;//8种输出比较模式TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//输出比较极性TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出使能//因此通过观察得知,我们需要完成的呼吸灯的变化效果就与CCR的值息息相关,TIM_SetCompare1单独更改CCR值的函数就有用了TIM_OCInitStructure.TIM_Pulse=0;//就是CCR的值TIM_OC2Init(TIM2, &TIM_OCInitStructure);  //通道修改为2,同一个定时器不同的特性:因为不同的通道相位,产生PWM都是一样的,但是CCR是可以单独设置的//启动定时器TIM_Cmd(TIM2, ENABLE);
}
void PWM_set(uint16_t compare){TIM_SetCompare2(TIM2,compare); //修改通道2的CCR
}

6-5 PWM驱动直流电机

main.c
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "Motor.h"
#include "Key.h"
uint8_t Keynum;
int8_t Speed;
int main(void){OLED_Init();Key_Init();Motor_Init();//Motor_Speed(50);//正转
//	Motor_Speed(-50);//反转OLED_ShowString(1,1,"Speed:");while(1){Keynum=Key_GetNum();if(Keynum==1){Speed +=20;if(Speed>100){Speed=-100;}}Motor_Speed(Speed);OLED_ShowSignedNum(1,7,Speed,3);}
}Motor.c
#include "stm32f10x.h"                  // Device header
#include "PWM.h"
void Motor_Init(void){PWM_Init();//需要额外去控制电机的方向控制的两个脚/*开启时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);		//开启GPIOA的时钟/*GPIO初始化*/GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);						//将PA1和PA2引脚初始化为推挽输出//默认就是低电平,所以不需要对输出电平进行设置就可以亮灯}void Motor_Speed(int8_t Speed){if(Speed>=0){//设置方向一个为高,一个为低GPIO_SetBits(GPIOA,GPIO_Pin_4);GPIO_ResetBits(GPIOA,GPIO_Pin_5);PWM_set(Speed);}else{GPIO_ResetBits(GPIOA,GPIO_Pin_4);GPIO_SetBits(GPIOA,GPIO_Pin_5);PWM_set(-Speed);}
}

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

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

相关文章

MATLAB核心技巧:从入门到精通

一 1.数值 显示 格式 format style 设置 eg: pi format longE; or 2.清除指令 clc 清除命令行窗口 clear 清除工作区 cls 3.搜索路径设置 path(path,E:\ads\) or addpath 4.M文件 用户把要实现的命令写在一个以.m为扩展的文件中&#xff0c;然后由matlab系统进行解读…

AnyDesk远程工具免费版,v9.5.110绿色便携版,秒连远程桌面,剪贴板同步超实用

[软件名称]: AnyDesk远程工具免费版 [软件大小]: 7.5 MB [软件大小]: 夸克网盘 | 百度网盘 软件介绍 AnyDesk 让远程工作变得轻而易举。无论您身处办公室的另一端还是世界的另一侧&#xff0c;只需在设备上下载、安装并启动 AnyDesk.exe&#xff0c;即可轻松访问远程屏幕。…

AI: 给Gemini CLI配上“说明书”, 精通的GEMINI.md项目记忆

嘿&#xff0c;各位技术同好&#xff01;今天我们来聊一个能极大提升AI编程助手效率的酷炫功能——Google Gemini CLI 中的 GEMINI.md 文件。 在日常开发中&#xff0c;我们越来越依赖像 Gemini 这样的 AI 助手来帮我们写代码、调试 Bug 甚至重构项目。但大家是否遇到过这种情况…

[激光原理与应用-205]:光学器件 - LD与DFB的比较

一、相同点核心原理均基于半导体材料的受激辐射机制&#xff0c;通过电子-空穴复合产生光子。依赖谐振腔实现光反馈与放大&#xff0c;形成激光振荡。采用电泵浦方式驱动&#xff0c;电流注入激发载流子&#xff0c;实现粒子数反转。材料体系主要使用III-V族化合物半导体&#…

Cursor手机版:一半是神,一半是坑

大家好&#xff0c;我是羊仔&#xff0c;专注AI工具、智能体、编程。今天想和大家聊的这个工具&#xff0c;叫Cursor&#xff0c;可能很多朋友已经不陌生了&#xff0c;它作为一款AI原生代码编辑器&#xff0c;之前可谓是风光无两。但最近&#xff0c;它又搞了点新花样&#xf…

康养休闲旅游服务虚拟仿真实训室:筑牢技能人才培养的数字基石

随着康养休闲旅游行业数字化、网络化、智能化发展趋势的深化&#xff0c;行业对高素质技能人才的实践能力和数字素养提出了更高要求。康养休闲旅游服务虚拟仿真实训室作为对接行业需求、创新实践教学模式的重要载体&#xff0c;正成为中等职业教育康养休闲旅游服务专业人才培养…

【Python 高频 API 速学 ⑤】

一、为什么把字典和集合放同一篇&#xff1f; • 底层都是哈希表&#xff0c;API 设计高度对称。 • 日常任务无非「读-写-去重-集合运算」&#xff0c;这 5 个方法就能打穿。二、三件套 & 二板斧一览名称作用返回值原地&#xff1f;dict.get(key, default)安全读取值或 de…

el-tree方法的整理

1.点击树的文字不要收缩仅点击图标的时候收缩 expand-on-click-node&#xff1a;是否在点击节点的时候展开或者收缩节点&#xff0c; 默认值为 true&#xff0c;如果为 false&#xff0c;则只有点箭头图标的时候才会展开或者收缩节点。<el-tree:expand-on-click-node"f…

支持多网络协议的测试工具(postman被无视版)

本文介绍接口调试工具&#xff0c;尽可能覆盖支持多种网络协议。写给一直写http接口&#xff0c;突然调试其他协议接口的开发 在后端开发中&#xff0c;接口调试工具的选择取决于网络协议类型和具体需求。以下是覆盖多种协议的主流工具分类推荐&#xff0c;附关键特点和场景建议…

太阳平近点角详解:概念、计算与应用

太阳平近点角详解&#xff1a;概念、计算与应用 1. 基本定义 **太阳平近点角&#xff08;Mean Anomaly&#xff0c;M&#xff09;**是描述天体&#xff08;如地球&#xff09;在其轨道上平均运动位置的角度参数。对于太阳系中的行星或卫星而言&#xff0c;它表示假设天体以恒定…

ruoyi关闭shiro校验,任何接口可以直接访问

文章目录1.找到ShiroConfig.java文件2.上述适用于get请求&#xff0c;post请求如何关闭&#xff1f;1.找到ShiroConfig.java文件 修改代码 // 原始代码 filterChainDefinitionMap.put("/**", "user,kickout,onlineSession,syncOnlineSession,csrfValidateFilt…

数据结构进阶 详谈红黑树

目录 1. 红⿊树的概念 红⿊树的规则 红⿊树如何确保最⻓路径不超过最短路径的2倍的&#xff1f; 红⿊树的效率&#xff1a; 2. 红⿊树的实现 红⿊树的结构 红⿊树的插⼊ 红⿊树树插⼊⼀个值的⼤概过程 情况1&#xff1a;变⾊ 情况2&#xff1a;单旋变⾊ 情况3&#…

【MySQL】MySQL去重查询详解

前言 在日常的数据库操作中&#xff0c;数据去重是一个非常常见的需求。无论是查询结果去重、数据清洗&#xff0c;还是统计分析&#xff0c;我们都需要掌握MySQL中的各种去重技术。本文将详细介绍MySQL中常用的去重关键字和操作方法&#xff0c;结合实际业务场景&#xff0c;帮…

Pinterest视觉营销自动化:亚矩阵云手机实例与多分辨率适配技术

Pinterest月活突破4.5亿的视觉经济时代&#xff0c;多分辨率适配与跨设备一致性成为品牌触达用户的核心挑战。传统营销因素材模糊、设备参数固化&#xff08;如固定分辨率1080P&#xff09;、行为机械化&#xff08;如定时批量上传&#xff09;&#xff0c;导致点击率低于行业均…

01数据结构-图的邻接矩阵和遍历

01数据结构-图的邻接矩阵和遍历1.图的遍历1.1深度优先遍历1.2广度优先搜索2.邻接矩阵的代码实现1.图的遍历 1.1深度优先遍历 深度优先搜索的过程类似于树的先序遍历&#xff0c;首先从例子中体会深度优先搜索&#xff0c;例如下图1是个无向图&#xff0c;采用深度优先算法遍历…

OpenAI发布的GPT-5 更新了哪些内容,它的核心能力有哪些?AI编码能力这么强,前端程序员何去何从?

目录**1. GPT-5的核心能力与技术突破****1.1 智能水平的质变****1.2 代码生成与优化****1.3 上下文处理与长文本能力****1.4 安全与可靠性改进****2. GPT-5的应用场景与案例****2.1 医疗领域****2.2 教育与学习****2.3 企业级应用****2.4 软件开发****3. 技术细节与创新****3.1…

【无标题】AI 赋能日常效率:实用案例与操作心得分享

大语言模型&#xff08;LLM&#xff09;早已不再是实验室里的专属品&#xff0c;而是逐渐渗透到我们工作与生活的方方面面。从繁琐的文档处理到复杂的信息筛选&#xff0c;从学习辅助到日常规划&#xff0c;AI 正以 "微生产力" 的形式重塑我们的效率边界。本文将分享…

Java-线程线程的创建方式

一.进程和线程进程&#xff1a;进程是资源分配的基本单位&#xff0c;每个进程都有自己独立的内存空间&#xff0c;可以看作是一个正在运行的程序实例线程&#xff1a;线程是CPU调度的基本单位&#xff0c;属于进程&#xff0c;一个进程可以包含多个线程。线程共享进程的内存空…

Electron 中 license-keys 的完整集成方案

secure-electron-license-keys 是一个专门为 Electron 应用设计的 npm 包&#xff0c;用于实现离线许可证密钥的创建、验证和管理&#xff0c;帮助开发者保护应用程序&#xff0c;确保只有拥有合法许可证的用户才能使用。以下是关于它的详细介绍&#xff1a; 在 Electron 应用中…

AI推理的“灵魂五问”:直面2025算力鸿沟与中国的破局之路

摘要&#xff1a;2025年&#xff0c;AI产业的重心已从训练全面转向推理&#xff0c;但一场严峻的“体验”危机正悄然上演。中美AI推理性能的巨大鸿沟&#xff0c;正让国内厂商面临用户流失的切肤之痛。本文以问答形式&#xff0c;直面当前中国AI产业在推理“最后一公里”上最尖…