目录
一、输出比较
OC输出比较
输出比较可以通过CNT与CCR寄存器的关系来对输出电平进行置1,置0或翻转的操作,用于输出一定频率和占空比的PWM波形(CC是捕获/比较的意思,R是Register,寄存器的意思),这个捕获/比较寄存器是输入捕获和输出比较共用的,当使用输入捕获时,他就是捕获寄存器,当时用输出比较时,它就是比较寄存器。
每个高级定时器都拥有4个输出比较通道
高级定时器的前3个通道额外拥有死去生成和互补输出的功能
CNT是时基单元里面的计数器,CCR是捕获/比较寄存器,CCR是输入捕获和输出比较公用的,在输出比较这里,这块电路会比较CNT和CCR的值,CNT计数自增,CCR是我们给定的一个值,当CNT大于CCR,小于CCR或者等于CCR时,输出就会对应的置1,置0,置1,置0
通用定时器和高级定时器,都有四个输出比较的通道,可以同时输出四路PWM波形,四个通道有各自的CCR寄存器,但是他们共用一个CNT计数器。
高级定时器,前三个输出比较通道还额外具有死区生成和互补输出的功能,用于驱动三相无刷电机
二、PWM
PWM(Pluse Width Modulation)脉冲宽度调制
在具有惯性的系统中,可以通过对一系列脉冲的宽度进行调制,来等效地获得所需要的模拟量,常用于电机控速等领域,也就是说,使用这个PWM波形,是用来等效地实现一个模拟信号的输出,也就是以一个很快的频率,给电机通电、断电,也就使电机维持在一个中等速度
PWM参数:
频率 = 1/Ts
占空比 = Ton/Ts
分辨率 = 占空比变化步距
PWM波形是一个数字输出信号,由高低电平组成
例:数字输出端口控制LED,按理说LED只能由完全亮和完全灭两种状态,通过PWM实现,让LED不断点亮和熄灭,当点亮、熄灭频率足够大时,LED则不会闪烁,而是呈现一个中等亮度,当调控这个点亮和熄灭的时间比例时,则可以让LED呈现出不同的亮度级别,对于电机也是一样,通电、断电、通电,则电机速度就能维持在一个中等速度。
注:需要具有惯性的系统,才能使用PWM
频率:Ts代表一个高低电平变换周期的时间,PWM的频率越快,等效模拟信号就越平稳,同时性能开销就越大
占空比:高电平时间对于整个周期的时间比例,占空比决定了PWM等效出来的模拟电压的大小,占空比越大,等效的模拟电压就越来越趋近于高电平;占空比越小,等效的模拟电压就越来越趋近于低电平。
这个等效关系一般来说时线性的,如高电平为5V,低电平为0V,50%的占空比,等效于中间电压,就是2.5V;20%占空比就等效于1/5处的电压,就是1V。
分辨率:占空比高低的精细程度。
三、定时器结构
3.1输出比较通道(通用)
在这个图里,左边就是CNT计数器和CCR1第一路的捕获/比较寄存器 ,他俩进行比较,当CNT>CCR1或者CNT=CCR1时就会给输出模式控制器传一个信号,然后输出模式控制器就会改变它输出OC1REF的高低电平(REF时reference的缩写,意思是参考信号),然后上面还有个ETRF输入,这是定时器的一个小功能一般不用了解,接着REF信号可以前往主模式控制器,你可以把这个REF映射到主模式的TRGO输出上去,但主要还是下面那一路(极性选择部分),给这个寄存器写0,信号会往上走,就是信号电平不翻转进来啥样出去啥样,写1的话,信号就会往下走,就是信号通过一个非门取反,那输出信号就是输入信号高低电平反转的信号,再往后就是输出使能电路就是选择要不要输出,最后就是OC1引脚,这个引脚就是CH1的通道的引脚
3.2输出比较通道(高级)
右侧为外围电路,则OC1和OC1N为互补输出,分别控制上管和下管的导通和截止
理想情况下,上管导通的瞬间下管要在同一瞬间关闭,但是实际情况下可能会因为器件的不理想导致上管未完全关闭,下管就导通了,出现了短暂的上下管短暂导通的情况,引起器件发热,所以有了死区生成电路,它会在上管关闭的时候延迟一小段,再导通下管,下管导通的时候,延迟一小段,在导通上管
3.3输出模式控制器模式
下图为输出模式控制器可以设置的模式
这个模式控制器的输出是CNT和CCR的大小关系,输出是REF的高低电平,可以选择多种模式来更加灵活地控制REF输出,这个模式可以通过寄存器来进行配置。
模式的介绍
(1)冻结
理解为CNT和CCR无效,REF保持为原状态
作用:假设你正在输出PWM波,暂停一段时间输出,则可以设置这个模式,一旦切换为冻结模式后,输出就可以暂停了,并且高低电平也维持为暂停时刻的状态保持不变。
(2)匹配时电平翻转
该模式可以做波形输出
这个模型可以方便地输出一个频率可调,占空比始终为50%de PWM波形
假设设置CCR为0,那CNT每次更新清0时,就会产生一次CNT=CCR时间,就会导致输出电平翻转一次,每更新两次为一个周期,并且高电平和低电平地时间是始终相等的,也就是占空比始终为50%,当你改变定时器更新频率时,输出波形的频率也会随之改变。
输出波形的频率 = 更新频率/2
(3)CNT与CCR无效,REF强制为无效电平或者有效电平
如果想暂停波形输出,并且在展厅时期保持低电平或者高电平,则可以设置这两个强制输出模式
(4)PWM模式1、2
可用于输出频率和占空比都可调的PWM波形,是我们主要使用的模式
根据PWM模式1与PWM模式2的向上计数做对比,PWM模式2实际上就是PWM模式1输出取反,改变模式1与模式2,只是改变了REF电平的极性而已。
REF输出只有还有一个极性的配置,所以使PWM模式1的正极性与PWM模式2的反极性最终的输出是一样的,输出模式里可以设置极性,最终输出也可以设置极性。
3.4PWM基本结构
图中右上角的蓝色线代表CNT;黄色线代表ARR;红色线代表CCR。
配置好时基单元,这里的CNT就可以开始不断地自增运行。到下面则是输出比较单元,总共由4路。
首先是CCR捕获/比较器,CCR是我们设定的,CNT不断自增运行,同时两个之间不断进行比较
第二步,是输出模式控制器,以PWM模式1为例子,由右上图可以得出红色线的CCR的值是30,ARR的值最高是99,当蓝色线的CNT的值小于CCR时,REF为有效电平,所以置高电平,当CNT>=CCR时,REF为无效电平,所以置低电平。然后当CNT的值达到最高值与ARR值相等(99),CNT重置为0,依次进行。
之后的REF时一个频率可调,占空比也可调的PWM波形,最终在经过极性选择,输出使能,最终通向GPIO口。
注意:PWM占空比,当等于30的瞬间,就已经跳为低电平,0~29是高电平,总共为30个数
分辨率CCR的变化范围取决于AAR的值,ARR越大,CCR的范围就越大,对应的分辨率就越大。
3.5PWM参数值计算
四、外设
4.1舵机
型号SG90
执行逻辑:PWM信号输入到控制板,给控制板一个指定的目标角度,然后,电位器检测输出轴的当前角度,如果大于目标角度,电机会反转,如果小于目标角度,电机会正转最终使输出轴固定在指定角度
4.2直流电机
五、程序代码
5.1PWM驱动LED呼吸灯
初始化步骤
RCC开启时钟,把我们要用的TIM外设和GPIO外设的时钟打开
配置时基单元,包括前面的时钟源选择
配置输出比较单元包括CCR的值、极性选择、输出使能
配置GPIO(复用推挽输出)
运行控制
下面是STM32T_TIM的一些小功能配置
//这个函数是给输出比较结构体赋一个默认值的
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
//这四个函数是用来配置强制输出模式
//如果你在运行中暂停输出波形并且强制输出高或低电平可以用这四个函数
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);
//下面四个函数是用来配置输出比较模块的,OC就是Output Compare
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);
//仅高级定时器使用,在使用高级定时器输出PWM时,需要调用这个函数使能主输出,否则PWM将不能正常输出
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
//这些就是单独设置输出比较的极性的
//这里带个N就是高级定时器里的互补通道的配置,OC4没有互补通道,所以就没有OC4N的函数
//这里的函数可以设置极性,在结构体初始化的那个函数里也可以设置极性,这两个地方设置极性的作用是一样的只不过用结构体是一起初始化的,在这里是一个函数的单独修改。
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.c
#include "stm32f10x.h" // Device header
/**
* @brief PWM输出初始化函数
* @param 无
* @retval 无
*/
void PWM_Init(void)
{
// 1.配置时基单元
// 用RCC外设时钟控制打开定时器的基准时钟和外设GPIO的工作时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 引脚重映射配置,可以将TIM1_CH1的输出信号从PA0重映射到PA15
// RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
// GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);
// 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);
// 2.配置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);
// 3.初始化输出比较单元(通道)
// 这里使用PA0口,对应第一个输出比较通道
TIM_OCInitTypeDef TIM_OCInitStruct; // 在这个结构体中有部分是高级定时器才拥有的成员
TIM_OCStructInit(&TIM_OCInitStruct); // 如果不对结构体成员赋初始值,那么它的值将不确定,这样可能会导致一些奇怪的问题
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // OC输出模式 配置为PWM模式1
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // OC输出极性
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // OC输出使能
TIM_OCInitStruct.TIM_Pulse = 0; // Pulse的值会被加载到CCR寄存器中
TIM_OC1Init(TIM2, &TIM_OCInitStruct);
// 4.配置运行控制,打开定时器
TIM_Cmd(TIM2, ENABLE);
}
/**
* @brief 更改比较/捕获寄存器的值CCR(当 ARR + 1 == 100 时 CCR 即为占空比)
* @param Compare 无符号16位整型数,注意:它只能是正数
* @retval 无
*/
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2, Compare);
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
uint8_t i;
int main()
{
OLED_Init();
PWM_Init();
while(1)
{
for(i = 0; i < 100; i ++)
{
PWM_SetCompare1(i);
Delay_ms(10);
}
for(i = 0; i < 100; i ++)
{
PWM_SetCompare1(100 - i);
Delay_ms(10);
}
}
}
5.2PWM驱动舵机
PWM.c
#include "stm32f10x.h" // Device header
/**
* @brief PWM输出初始化函数
* @param 无
* @retval 无
*/
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 1.配置时基单元
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;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
// 2.配置GPIO输出端PA1为复用推挽输出模式
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);
// 3.初始化输出比较单元(通道)
// 这里使用PA1口,对应第二个输出比较通道
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // OC输出模式 配置为PWM模式1
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // OC输出极性
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // OC输出使能
TIM_OCInitStruct.TIM_Pulse = 0; // CCR:500~2500
TIM_OC2Init(TIM2, &TIM_OCInitStruct);
// 4.配置运行控制
TIM_Cmd(TIM2, ENABLE);
}
/**
* @brief 更改比较/捕获寄存器的值CCR
* @param Compare 无符号16位整型数,注意:它只能是正数,范围是500~2500
* @retval 无
*/
void PWM_SetCompare2(uint16_t Compare)
{
TIM_SetCompare2(TIM2, Compare);
}
Servo.c
#include "stm32f10x.h" // Device header
#include "PWM.h"
/**
* @brief 舵机初始化(模块化封装底层函数)
* @param 无
* @retval 无
*/
void Servo_Init(void)
{
PWM_Init();
}
/**
* @brief 设置舵机的旋转角度
* @param Angle,设置的角度,它的值可以是 0 ~ 180
* @retval 无
*/
void Servo_SetAngle(float Angle)
{
PWM_SetCompare2(Angle / 180 * 2000 + 500); // Angle通过换算得到对应的CCR的值
}
key.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
/**
* @brief 按键初始化函数,初始化PB0
* @param 无
* @retval 无
*/
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/**
* @brief 返回按下按键的值,若不按下按键默认返回0
* @param 无
* @retval KeyNum 按键对应的值
*/
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0;
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) // 读取PB0端口的值
{
Delay_ms(20);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0); // 如果不松手,程序将在此等待
Delay_ms(20);
KeyNum = 1;
}
return KeyNum;
}
main.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Servo.h"
#include "Key.h"
uint8_t KeyNum;
float Angle;
int main()
{
OLED_Init();
Servo_Init();
Key_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);
}
}
5.3PWM驱动直流电机
PWM.c
#include "stm32f10x.h" // Device header
/**
* @brief PWM输出初始化函数
* @param 无
* @retval 无
*/
void PWM_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// 1.配置时基单元
// 选择时基单元的时钟源(这里使用内部时钟)
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
// 提高PWM波的频率可以解决直流电机发出类似蜂鸣器的声音的问题
TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; // ARR
TIM_TimeBaseInitStructure.TIM_Prescaler = 36 - 1; // PSC
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
// 2.配置GPIO输出端为复用推挽输出模式
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 3.初始化输出比较单元(通道)
// 这里使用PA2口,对应第四个输出比较通道
TIM_OCInitTypeDef TIM_OCInitStruct;
TIM_OCStructInit(&TIM_OCInitStruct);
TIM_OCInitStruct.TIM_OCMode = TIM_OCMode_PWM1; // OC输出模式 配置为PWM模式1
TIM_OCInitStruct.TIM_OCPolarity = TIM_OCPolarity_High; // OC输出极性
TIM_OCInitStruct.TIM_OutputState = TIM_OutputState_Enable; // OC输出使能
TIM_OCInitStruct.TIM_Pulse = 0; // CCR
TIM_OC4Init(TIM2, &TIM_OCInitStruct);
// 4.配置运行控制
TIM_Cmd(TIM2, ENABLE);
}
/**
* @brief 更改比较/捕获寄存器CCR的值
* @param Compare 无符号16位整型数,注意:它只能是正数
* @retval 无
*/
void PWM_SetCompare4(uint16_t Compare)
{
TIM_SetCompare4(TIM2, Compare);
}
Motor.c
#include "stm32f10x.h" // Device header
#include "PWM.h"
/**
* @brief 直流电机初始化函数
* @param 无
* @retval 无
*/
void Motor_Init(void)
{
PWM_Init();
// 初始化电机方向控制脚PA4、PA5
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
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);
}
/**
* @brief 控制电机的转向和速度函数
* @param Speed 电机速度,可以为正也可以为负
* @retval 无
*/
void Motor_SetSpeed(int8_t Speed)
{
if (Speed >= 0) // 正传
{
GPIO_SetBits(GPIOA, GPIO_Pin_4);
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
PWM_SetCompare4(Speed);
}
else // 反转
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
GPIO_SetBits(GPIOA, GPIO_Pin_5);
PWM_SetCompare4(- Speed); // Speed 此时为负数,但是SetCompare的参数必须为正,故在前添加负号
}
}
Key.c
#include "stm32f10x.h" // Device header
#include "Delay.h"
/**
* @brief 按键初始化函数
* @param 无
* @retval 无
*/
void Key_Init(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 这里的速度是GPIO的输出速度,在输入模式下这个参数选择没有用处
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
/**
* @brief 返回按下按键的值,若不按下按键默认返回0
* @param 无
* @retval KeyNum 按键对应的值
*/
uint8_t Key_GetNum(void)
{
uint8_t KeyNum = 0;
if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) // 读取1端口的值
{
Delay_ms(20);
while (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0); // 如果不松手,程序将在此等待
Delay_ms(20);
KeyNum = 1;
}
return KeyNum;
}
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()
{
OLED_Init();
Key_Init();
Motor_Init();
OLED_ShowString(1, 1, "Speed:");
while(1)
{
KeyNum = Key_GetNum();
if (KeyNum == 1)
{
Speed += 20;
if (Speed > 100)
{
Speed = -100;
}
}
Motor_SetSpeed(Speed);
OLED_ShowSignedNum(1, 7, Speed, 3);
}
}