定时器Timer 链接到标题
理论部分参加cd的ppt。这部分记录都是以实际代码为主。
中断 链接到标题
这个在微机原理讲的很详细了,有一个叫NVIC的中断控制器,可以在不占用CPU的情况下,直接响应中断请求。(这个在微机原理也讲过)
CountSensor配置 链接到标题
初始化函数 链接到标题
- 首先开启外设时钟RCC
- 然后配置GPIO
- 之后就是AFIO的配置了,从物理上去理解就是选择哪个GPIO端口的中断信号进入AFIO,这部分用到了一个之前没有了解过的函数GPIO_EXTILineConfig(翻找定义容易了解),
- 然后就是EXIT了,翻找其函数,用心体会
- NVIC的配置:也是好烦的查表过程。
中断使用函数 链接到标题
- 也就是在中断发生时计数一次的函数
- 这个函数也有一点特殊,属于是启动函数,因此名字要从表中来,并且无需在.h中声明
获取值函数 链接到标题
main.c 链接到标题
编写好函数
void CountSensor_Init(void)
/*下面就是配置外部中断的部分了*/
{
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //使能GPIOB时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //使能AFIO时钟
//EXTI理论上要配的,但是实际上没有这一项(可能是内置了?)
/*配置GPIO*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU; //上拉输入
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_14; //PB14
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz; //50MHz
GPIO_Init(GPIOB,&GPIO_InitStructure); //初始化GPIOB14
/*配置AFIO*/
GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14); //PB14作为外部中断源
/*配置EXTI*/
EXTI_InitTypeDef EXTI_InitStructure;
EXTI_InitStructure.EXTI_Line=EXTI_Line14; //PB14作为外部中断线
EXTI_InitStructure.EXTI_LineCmd=ENABLE; //使能外部中断线
EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt; //中断模式
EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling; //下降沿触发
EXTI_Init(&EXTI_InitStructure); //初始化外部中断
/*配置NVIC*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//需要注意的是每一个函数只有一个优先级,所以理论上这个函数应该放在主函数里(当然,放在模块里也没有问题,不过要保证在主函数里面的所有模块的优先级都是一样的)
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=EXTI15_10_IRQn; //中断通道号(这个需要在工程文档中去找)
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE; //使能中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1; //响应优先级
NVIC_Init(&NVIC_InitStructure); //初始化中断
}
uint16_t CountSensor_Count;
uint16_t CountSensor_Get(void)
{
return CountSensor_Count; //返回计数值
}
void EXTI15_10_IRQHandler(void)
{
if(EXTI_GetITStatus(EXTI_Line14) != RESET) //判断中断是否发生
{
CountSensor_Count++; //计数加1
EXTI_ClearITPendingBit(EXTI_Line14); //清除中断标志位
}
}
#ifndef __COUNT_SENSOR_H
#define __COUNT_SENSOR_H
void CountSensor_Init(void);
/*中断函数不需要调用,因为他是在启动文件自动执行的*/
uint16_t CountSensor_Get(void);
#endif
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "CountSensor.h"
int main(void)
{
OLED_Init(); //初始化OLED
CountSensor_Init();
OLED_ShowString(1,1,"Count:");
while (1)
{
OLED_ShowNum(1,7,CountSensor_Get(),5); //显示计数值
}
}
编码计数器 链接到标题
同样涉及中断计数,这里放上我的逻辑错误。
- 漏掉
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
从而在一开始就跑不了。
- 加上了那一句,但是效果还是很怪,所以对照标准样例接着debug,发现是我再GPIO1的中断函数中没有同时对两个GPIO口判断电位。
void EXTI1_IRQHandler(void) { if (EXTI_GetITStatus(EXTI_Line1) == SET) { /*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/ if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) { if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0) { Encoder_Count ++; } } EXTI_ClearITPendingBit(EXTI_Line1); } }
void EXTI1_IRQHandler(void) { if(EXTI_GetITStatus(EXTI_Line1) ==SET) //判断中断是否发生 { if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0) //即判断是否是正转 { Encoder_Count++; } EXTI_ClearITPendingBit(EXTI_Line1); //清除中断标志位 } }
定时器定时中断 链接到标题
这部分将用到更多中断的知识。 首先当然还是建立模块Timer.c和Timer.h,
初始化函数 链接到标题
目的就是要打通下面的这一幅图:
- 需要看定时器本身的库函数
stm32f10x_tim.h
提示这里可以学习一下kel本身的标签工具(可以将经常用的函数标记出来),对代码运行没有影响,仅标记而已 - 这里有大把的函数,需要根据想要选图中的那一条路径构造定时器合理选择。
- 这里选用TIM2(通用定时器)
- 基础操作不说,在时钟频率的确定有一点细节
注释
计数器溢出频率 $$ CK_CNT_OV= \frac{CK_PSC(ARR+1)}{(PSC+1)} $$ 所以参数设置可设
TIM_TimeBaseStructure.TIM_Period = 10000-1; //ARR TIM_TimeBaseStructure.TIM_Prescaler = 7200-1; //PSC
-1是从公式里面看的,由于是除法,意思是对72Mhz的时钟进行7200分频,分频后为10KHz,要计10000个数,也就是1s的时间。
中断服务函数的使用 链接到标题
这属于是C语言里面的知识了,即计数数Num需要在主程序main.c中输出使用,但是对其的定时累加操作是在出于Timer.c中过的中断函数中进行的。如果直接在主文件
unit16_t Num = 0;
会报错,这里有两种解决方法
使用全局变量 链接到标题
需要使用全局变量,即在Timer.c中声明
extern uint16_t Num;
然后就可以正常使用
将中断函数复制到主程序 链接到标题
这其实就是属于一种非阻塞式的编程方式了,
最终写出来的代码如下
#include"stm32f10x.h"
extern uint16_t Num;
void Timer_Init(void)
{
/*选用TIM2*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能TIM2时钟
TIM_InternalClockConfig(TIM2);//这行可写可不写,因为默认就是内部时钟
/*配置时基单元*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); //初始化TIM2
//给结构体成员赋值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//用于设置时钟分频,从而用于给滤波器工作
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
//下面的三个参数就是设置计数器的频率的了,比如如果想定时1s
TIM_TimeBaseStructure.TIM_Period = 100-1; //计数器的值
TIM_TimeBaseStructure.TIM_Prescaler = 7200-1; //分频值
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //重复计数器值,高级定时器才有,这里没有就给0
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); //初始化TIM2
/*配置中断*/
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); //开启了中断和NVIC通路
/*NVIC配置*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC分组
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //TIM2中断(之前配置的是GPIO中断,这里配置的是时钟中断)具体也是去跳转定义看文档
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能TIM2中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级
NVIC_Init(&NVIC_InitStructure); //初始化NVIC
/*使能定时器*/
TIM_Cmd(TIM2,ENABLE); //使能TIM2
}
void TIM2_IRQHandler(void) //TIM2中断服务函数
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET) //判断是否是TIM2的更新中断
{
Num++; //Num自加1
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); //清除TIM2的更新中断标志位
//在这里添加你想要执行的代码
}
}
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Encoder.h"
#include "Timer.h"
uint16_t Num = 0;
int main(void)
{
OLED_Init(); //初始化OLED
Timer_Init(); //初始化定时器
OLED_ShowString(1,1,"Num:"); //显示Num
while (1)
{
OLED_ShowNum(1,5,Num,4); //显示Num
//下面的这个是测试的,就是直接用内置的时钟显示器来与我们写的定时器对比
OLED_ShowNum(2,5,TIM_GetCounter(TIM2),5); //显示定时器计数值
}
}
用光电门来模拟计数器 链接到标题
即通过传感器光电门通过GPIO端口产生电平信号,由这个信号来模拟时钟。
初始化函数的更改 链接到标题
- 这时候就不能是默认了,手动改为外部时钟,
/*通过ETR配置外部时钟模式2*/
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00); //配置外部时钟模式2:不需要分频,不反向(指敏感性,上升沿还是下降沿等等),不使用滤波器
- 然后就是之前操作过的对GPIO口的配置
/*配置GIOP*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能GPIOA时钟
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入(也可以使用上拉)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; //PA0
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA
-
分频设置的 预分频和 自动重装值改小一点
提示因为手动模拟没有这么快 -
然后为了调试方便加一个
uint16_t Timer_GetCounter(void) //获取定时器计数值
{
return TIM_GetCounter(TIM2); //返回TIM2的计数值
}
用来看TM2的计数值
输出比较OC 链接到标题
通过比较CNT与CCR的值来实现PWM输出


模式 | 描述 |
---|---|
冻结 | CNT=CCR时,REF保持为原状态 |
匹配时置有效电平 | CNT=CCR时,REF置有效电平 |
匹配时置无效电平 | CNT=CCR时,REF置无效电平 |
匹配时电平翻转 | CNT=CCR时,REF电平翻转 |
强制为无效电平 | CNT与CCR无效,REF强制为无效电平 |
强制为有效电平 | CNT与CCR无效,REF强制为有效电平 |
PWM模式1 | 向上计数:CNT<CCR时,REF置有效电平,CNT≥CCR时,REF置无效电平 向下计数:CNT>CCR时,REF置无效电平,CNT≤CCR时,REF置有效电平 |
PWM模式2 | 向上计数:CNT<CCR时,REF置无效电平,CNT≥CCR时,REF置有效电平 向下计数:CNT>CCR时,REF置有效电平,CNT≤CCR时,REF置无效电平 |
其中PWM模式是常用的,可以用于处理连续信号, 更多的也是见cd的ppt。
PWM模块 链接到标题
初始化函数 链接到标题
目标是打通
GPIO 链接到标题
- copy 一下GPIO.c的GPIO初始化函数,或者……前面很多写好的模块都有,更改一下对应的是输出引脚
- 注意GPIO口的模式要设置为复用输出模式GPIO_Mode_AF_PP,原因如下图:
即若要让输出控制交给定时器,就要设置为复用输出模式
时基单元 链接到标题
也是copy一下Timer.c的时基单元配置,可以去掉NVIC的配置
输出比较模式 链接到标题
- 使用TIM_OC……Init类函数,比如这里使用的TIM_OC1Init,
- 这个函数自带一个初始化函数,给结构体赋一个默认值,可以在调用后再调整个别自己需要更改的值。
提示
- 在初始上面的结构体的时候有一个量是GPIO口,代表着这个PWM会出输出在哪个GPIO口上,关于如何配置实际上是有讲究的,在引脚定义表
中已经详细给出了哪些功能只能出现在哪些引脚上(默认复用功能),
- 除此之外为了防止多个信号在一个GPIO口上的冲突,stm32还添加了一定的重定义功能,允许在某些情况下将冲突的端口分开。这在进一步的编写中会遇到
- 在初始上面的结构体的时候有一个量是GPIO口,代表着这个PWM会出输出在哪个GPIO口上,关于如何配置实际上是有讲究的,在引脚定义表
参数计算 链接到标题
- 频率: $$ f_{PWM} = \frac{CK_PSC}{(PSC+1)(ARR+1)} $$
- 占空比: $$ Duty = \frac{CCR}{ARR+1} $$
- 分辨率:
$$
Reso=\frac{1}{ARR+1}
$$
这里CK_PSC是时钟频率,是已知的,其他都是自己设置,CCR通过
TIM_OCInitStructure.TIM_Pulse
来设置,以获得想要的参数。
单独更改比较器值 链接到标题
一个内置函数即可
呼吸灯 链接到标题
使用上述的函数,编写程序,在主函数中不断改变占空比(比如先加到最大值,然后递减,减到最小值,如此周期往复),但是在写好程序后笔者发现自己的程序一直在报错
User\main.c(5): error: #130: expected a "{"
即使删剩主死循环也无果,推测是模块冲突?只好重建了一下test文件目录。
int main(void)
{
OLED_Init();
PWM_Init();
while(1)
{
for(i=0;i<=100;i++)
{
PWM_SetCompare1(i);
Delay_ms(10);
}
for(i=100;i>=0;i--)
{
PWM_SetCompare1(i);
Delay_ms(10);
}
}
}
#include"stm32f10x.h"
void PWM_Init(void)
{
//初始化时基单元
/*选用TIM2*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能TIM2时钟
/*配置GPIO*/
//配置时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//配置端口
//先配置号端口的三个参数
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_InternalClockConfig(TIM2);//这行可写可不写,因为默认就是内部时钟
/*配置时基单元*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); //初始化TIM2
//给结构体成员赋值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//用于设置时钟分频,从而用于给滤波器工作
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
//下面的三个参数就是设置计数器的频率的了,比如如果想定时1s
TIM_TimeBaseStructure.TIM_Period = 100-1; //计数器的值
TIM_TimeBaseStructure.TIM_Prescaler = 720-1; //分频值
TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //重复计数器值,高级定时器才有,这里没有就给0
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); //初始化TIM2
/*使能定时器*/
TIM_Cmd(TIM2,ENABLE); //使能TIM2
//初始化输出比较单元
///先对结构体初始化
TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCStructInit(&TIM_OCInitStructure); //先对所有结构体成员赋一个默认值
//给结构体成员赋值
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM模式1
TIM_OCInitStructure.TIM_OCPolarity= TIM_OCPolarity_High; //输出极性高(就是不取反直接输出)
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能输出
TIM_OCInitStructure.TIM_Pulse = 0; //设置比较值,决定占空比
TIM_OC1Init(TIM2, &TIM_OCInitStructure);
}
void PWM_SetCompare1(uint16_t Compare)
{
TIM_SetCompare1(TIM2,Compare);
}
引脚重映射 链接到标题
通过AFIO的配置来实现引脚的重映射,首先就是要开启其时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); //使能AFIO时钟
然后通过下面的两句分别复用以及解除原来的复用
//重映射
GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); //重映射TIM2到PA0 GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //重映射TIM2到PA0
然后,笔者在这里贪图ai补全一时爽,错写成了
GPIO_Remap_SWJ_GDisable
意味着调试端口被禁用,导致无法再用stlink调试,stm32秒变砖头, 解决方案
- 视频中说用串口发送一个正常程序过去
- 还有一种方法来源网上,即按下reset键后迅速在电脑上启动烧录,在第一次之后无需再按reset即可正常烧录
如此,PA0就挪到了PA15上了,之后的GPIO口都用PA15来代替PA0了,即下面这一行
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15;
PWM驱动舵机 链接到标题
好,不过这一章节无需重映射,所以援引呼吸灯编好的模块,将上面的重映射部分注释掉。
- 这里需要注意的是舵机的PWM频率是50Hz,所以ARR要设置为20000-1
然后改的主要是主程序
#include "stm32f10x.h" // Device header #include "Delay.h" #include "OLED.h" #include "Servo.h" #include "Key.h" uint8_t KeyNum; float Angle = 0; int main(void) { 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; } OLED_ShowNum(1,7, Angle, 3); Servo_SetAngle(Angle); } }
增加了一个角度变换函数而已,为了方便再模块化了一下
void Servo_Init(void) { PWM_Init(); } void Servo_SetAngle(float Angle) { PWM_SetCompare2(Angle/180*2000+500); //500~2500 }
- Pin_0改为Pin_1(我们用的PA1来控制舵机)
- 改通道:
TIM_OC1Init
为TIM_OC2Init
- 改
PWM_SetCompare1
为PWM_SetCompare2
{< notice tip >}} 原因参见中文手册119页的表43,有如下片段
复用功能 没有重映像 …… TIM2_CH1_ETR PA0 …… TIM2_CH2 PA1 …… 已清晰标明对应的GPIO要用哪个时钟和通道了 所以改完后的代码:
#include"stm32f10x.h" void PWM_Init(void) { //初始化时基单元 /*选用TIM2*/ RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能TIM2时钟 //使能AFIO时钟(用于下面的重映射) // RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE); // //重映射 // GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE); //重映射TIM2到PA0 // GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE); //重映射TIM2到PA0 /*配置GPIO*/ //配置时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //配置端口 //先配置号端口的三个参数 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_TimeBaseStructure; TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); //初始化TIM2 //给结构体成员赋值 TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;//用于设置时钟分频,从而用于给滤波器工作 TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数 //下面的三个参数就是设置计数器的频率的了,比如如果想定时1s TIM_TimeBaseStructure.TIM_Period = 20000-1; //计数器的值 TIM_TimeBaseStructure.TIM_Prescaler = 72-1; //分频值 TIM_TimeBaseStructure.TIM_RepetitionCounter = 0; //重复计数器值,高级定时器才有,这里没有就给0 TIM_TimeBaseInit(TIM2,&TIM_TimeBaseStructure); //初始化TIM2 //初始化输出比较单元 ///先对结构体初始化 TIM_OCInitTypeDef TIM_OCInitStructure; TIM_OCStructInit(&TIM_OCInitStructure); //先对所有结构体成员赋一个默认值 //给结构体成员赋值 TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //PWM模式1 TIM_OCInitStructure.TIM_OCPolarity= TIM_OCPolarity_High; //输出极性高(就是不取反直接输出) TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能输出 TIM_OCInitStructure.TIM_Pulse = 0; //设置比较值,决定占空比 TIM_OC2Init(TIM2, &TIM_OCInitStructure); /*使能定时器*/ TIM_Cmd(TIM2,ENABLE); //使能TIM2 } void PWM_SetCompare2(uint16_t Compare) { TIM_SetCompare2(TIM2,Compare); }
驱动直流电机 链接到标题
在舵机的基础上增加了控制方向以及速度的函数,可以理解为一个定速过程了。
#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();
Motor_Init();
Key_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);
}
}
void Motor_Init(void)
{
///这一段来自于LED.c的更改
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);
PWM_Init();
}
void Motor_SetSpeed(int8_t Speed)
{
if (Speed >= 0)
{
GPIO_SetBits(GPIOA, GPIO_Pin_4);
GPIO_ResetBits(GPIOA, GPIO_Pin_5);
PWM_SetCompare3(Speed);
}
else
{
GPIO_ResetBits(GPIOA, GPIO_Pin_4);
GPIO_SetBits(GPIOA, GPIO_Pin_5);
PWM_SetCompare3(-Speed);
}
}