6.舵机控制¶

6.6.2.1. 编程要点¶

定时器 IO 配置

定时器时基结构体TIM_TimeBaseInitTypeDef配置

定时器输出比较结构体TIM_OCInitTypeDef配置

封装一个舵机角度控制函数

在main函数中编写按键舵机控制代码

定时器复用功能引脚初始化¶

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24/*宏定义*/

#define GENERAL_TIM TIM4

#define GENERAL_TIM_GPIO_AF GPIO_AF2_TIM4

#define GENERAL_TIM_CLK_ENABLE() __TIM4_CLK_ENABLE()

#define PWM_CHANNEL_1 TIM_CHANNEL_1

//#define PWM_CHANNEL_2 TIM_CHANNEL_2

//#define PWM_CHANNEL_3 TIM_CHANNEL_3

//#define PWM_CHANNEL_4 TIM_CHANNEL_4

/* 累计 TIM_Period个后产生一个更新或者中断*/

/* 当定时器从0计数到PWM_PERIOD_COUNT,即为PWM_PERIOD_COUNT+1次,为一个定时周期 */

#define PWM_PERIOD_COUNT 999

/* 通用控制定时器时钟源TIMxCLK = HCLK/2=84MHz */

/* 设定定时器频率为=TIMxCLK/(PWM_PRESCALER_COUNT+1) */

#define PWM_PRESCALER_COUNT 1679

/*PWM引脚*/

#define GENERAL_TIM_CH1_GPIO_PORT GPIOD

#define GENERAL_TIM_CH1_PIN GPIO_PIN_12

//#define GENERAL_TIM_CH2_GPIO_PORT GPIOD

//#define GENERAL_TIM_CH2_PIN GPIO_PIN_13

使用宏定义非常方便程序升级、移植。如果使用不同的定时器IO,修改这些宏即可。

定时器复用功能引脚初始化

定时器复用功能引脚初始化¶

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21static void TIMx_GPIO_Config(void)

{

GPIO_InitTypeDef GPIO_InitStruct;

/* 定时器通道功能引脚端口时钟使能 */

__HAL_RCC_GPIOA_CLK_ENABLE();

/* 定时器通道1功能引脚IO初始化 */

/*设置输出类型*/

GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;

/*设置引脚速率 */

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

/*设置复用*/

GPIO_InitStruct.Alternate = GENERAL_TIM_GPIO_AF;

/*选择要控制的GPIO引脚*/

GPIO_InitStruct.Pin = GENERAL_TIM_CH1_PIN;

/*调用库函数,使用上面配置的GPIO_InitStructure初始化GPIO*/

HAL_GPIO_Init(GENERAL_TIM_CH1_GPIO_PORT, &GPIO_InitStruct);

}

定时器通道引脚使用之前必须设定相关参数,这选择复用功能,并指定到对应的定时器。

使用GPIO之前都必须开启相应端口时钟。

定时器模式配置¶

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35TIM_HandleTypeDef TIM_TimeBaseStructure;

static void TIM_PWMOUTPUT_Config(void)

{

TIM_OC_InitTypeDef TIM_OCInitStructure;

/*使能定时器*/

GENERAL_TIM_CLK_ENABLE();

TIM_TimeBaseStructure.Instance = GENERAL_TIM;

/* 累计 TIM_Period个后产生一个更新或者中断*/

//当定时器从0计数到PWM_PERIOD_COUNT,即为PWM_PERIOD_COUNT+1次,为一个定时周期

TIM_TimeBaseStructure.Init.Period = PWM_PERIOD_COUNT;

// 通用控制定时器时钟源TIMxCLK = HCLK/2=84MHz

// 设定定时器频率为=TIMxCLK/(PWM_PRESCALER_COUNT+1)

TIM_TimeBaseStructure.Init.Prescaler = PWM_PRESCALER_COUNT;

/*计数方式*/

TIM_TimeBaseStructure.Init.CounterMode = TIM_COUNTERMODE_UP;

/*采样时钟分频*/

TIM_TimeBaseStructure.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;

/*初始化定时器*/

HAL_TIM_Base_Init(&TIM_TimeBaseStructure);

/*PWM模式配置*/

TIM_OCInitStructure.OCMode = TIM_OCMODE_PWM1; // 配置为PWM模式1

TIM_OCInitStructure.Pulse = 0.5/20.0*PWM_PERIOD_COUNT; // 默认占空比

TIM_OCInitStructure.OCFastMode = TIM_OCFAST_DISABLE;

/*当定时器计数值小于CCR1_Val时为高电平*/

TIM_OCInitStructure.OCPolarity = TIM_OCPOLARITY_HIGH;

/*配置PWM通道*/

HAL_TIM_PWM_ConfigChannel(&TIM_TimeBaseStructure, &TIM_OCInitStructure, PWM_CHANNEL_1);

/*开始输出PWM*/

HAL_TIM_PWM_Start(&TIM_TimeBaseStructure,PWM_CHANNEL_1);

}

首先定义两个定时器初始化结构体,定时器模式配置函数主要就是对这两个结构体的成员进行初始化,然后通过相

应的初始化函数把这些参数写入定时器的寄存器中。有关结构体的成员介绍请参考定时器详解章节。

不同的定时器可能对应不同的APB总线,在使能定时器时钟是必须特别注意。通用控制定时器属于APB1,

定时器内部时钟是84MHz。

在时基结构体中我们设置定时器周期参数为PWM_PERIOD_COUNT(999),频率为50Hz,使用向上计数方式。

因为我们使用的是内部时钟,所以外部时钟采样分频成员不需要设置,重复计数器我们没用到,也不需要设置,

然后调用HAL_TIM_Base_Init初始化定时器。

在输出比较结构体中,设置输出模式为PWM1模式,通道输出高电平有效,设置默认脉宽为PWM_PERIOD_COUNT,

PWM_PERIOD_COUNT是我们定义的一个宏,用来指定占空比大小,实际上脉宽就是设定比较寄存器CCR的值,

用于跟计数器CNT的值比较。然后调用HAL_TIM_PWM_ConfigChannel初始化PWM输出。

最后使用HAL_TIM_PWM_Start函数让计数器开始计数和通道输出。

设置定时器占空比¶

1

2

3

4

5

6

7

8

9

10

11

12void set_steering_gear_dutyfactor(uint16_t dutyfactor)

{

#if 1

{

/* 对超过范围的占空比进行边界处理 */

dutyfactor = 0.5/20.0*PWM_PERIOD_COUNT > dutyfactor ? 0.5/20.0*PWM_PERIOD_COUNT : dutyfactor;

dutyfactor = 2.5/20.0*PWM_PERIOD_COUNT < dutyfactor ? 2.5/20.0*PWM_PERIOD_COUNT : dutyfactor;

}

#endif

TIM2_SetPWM_pulse(PWM_CHANNEL_1, dutyfactor);

}

封装一个舵机占空比设置函数,接收一个参数用于设置PWM的占空比,并对输入的参数进行合法性检查,将脉冲宽度限制

在0.5~2.5ms之间。

设置舵机角度¶

1

2

3

4

5

6void set_steering_gear_angle(uint16_t angle_temp)

{

angle_temp = (0.5 + angle_temp / 180.0 * (2.5 - 0.5)) / 20.0 * PWM_PERIOD_COUNT; // 计算角度对应的占空比

set_steering_gear_dutyfactor(angle_temp); // 设置占空比

}

该函数用于设置舵机角度,传入角度值然后计算占空比,最后条用set_steering_gear_dutyfactor()来设置占空比。

串口控制¶

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46void deal_serial_data(void)

{

int angle_temp=0;

//接收到正确的指令才为1

char okCmd = 0;

//检查是否接收到指令

if(receive_cmd == 1)

{

if(UART_RxBuffer[0] == 'a' || UART_RxBuffer[0] == 'A')

{

//设置速度

if(UART_RxBuffer[1] == ' ')

{

angle_temp = atoi((char const *)UART_RxBuffer+2);

if(angle_temp>=0 && angle_temp <= 180)

{

printf("\n\r角度: %d\n\r", angle_temp);

angle_temp = (0.5 + angle_temp / 180.0 * (2.5 - 0.5)) / 20.0 * PWM_PERIOD_COUNT;

ChannelPulse = angle_temp; // 同步按键控制的比较值

set_steering_gear_angle(angle_temp);

okCmd = 1;

}

}

}

else if(UART_RxBuffer[0] == '?')

{

//打印帮助命令

show_help();

okCmd = 1;

}

//如果指令有无则打印帮助命令

if(okCmd != 1)

{

printf("\n\r 输入有误,请重新输入...\n\r");

show_help();

}

//清空串口接收缓冲数组

receive_cmd = 0;

uart_FlushRxBuffer();

}

}

以上为串口接收处理函数,接收正确的指令后将字符串计算出正确的角度值,判断角度值是否是在有效范围内,

同步按键调节的占空比防止按钮调节时转动范围过大。

main函数¶

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47int main(void)

{

/* HAL 库初始化 */

HAL_Init();

/* 初始化系统时钟为168MHz */

SystemClock_Config();

/* 初始化按键GPIO */

Key_GPIO_Config();

/* 初始化串口 */

DEBUG_USART_Config();

/* 通用定时器初始化并配置PWM输出功能 */

TIMx_Configuration();

printf("野火舵机控制实验\r\n");

show_help();

while(1)

{

/* 处理数据 */

if (Key_Scan(KEY1_GPIO_PORT, KEY1_PIN) == KEY_ON)

{

ChannelPulse -= 10; // 减少占空比

ChannelPulse = 0.5/20.0*PWM_PERIOD_COUNT > ChannelPulse ? 0.5/20.0*PWM_PERIOD_COUNT : ChannelPulse; // 检查占空比的合法性

set_steering_gear_dutyfactor(ChannelPulse); // 设置占空比

}

/* 处理数据 */

if (Key_Scan(KEY2_GPIO_PORT, KEY2_PIN) == KEY_ON)

{

ChannelPulse += 10; // 增加占空比

ChannelPulse = (2.5/20.0*PWM_PERIOD_COUNT) < ChannelPulse ? (2.5/20.0*PWM_PERIOD_COUNT) : ChannelPulse; // 检查占空比的合法性

set_steering_gear_dutyfactor(ChannelPulse); // 设置占空比

}

/* 串口处理 */

deal_serial_data();

}

}

初始化串口、定时器输出PWM和按键等外设,最后在循环里面处理按键和串口接收的数据。当KEY1按下后,

减少占空比,并检查占空比是否在有效范围内,然后设置占空比,当KEY2按下后,增加占空比,并检查占空比

是否在有效范围内,然后设置占空比。最后调用deal_serial_data()来处理串口接收的函数。

王者荣耀鹰眼铭文属性有哪些 王者荣耀鹰眼铭文适合哪个英雄|《Fate/GO》模拟器常见问题FAQ