Blog

Keep up to date with the latest news

7.步进电机¶

7.6.2.2. 第二种方式:使用GPIO中断模拟脉冲控制¶

编程要点

通用GPIO配置

步进电机、定时器中断初始化

在定时器中断翻转IO引脚

在main函数中编写轮询按键控制步进电机旋转的代码

宏定义

功能引脚相关宏定义¶

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22#define MOTOR_PUL_TIM TIM2

#define MOTOR_PUL_CLK_ENABLE() __TIM2_CLK_ENABLE()

#define MOTOR_PUL_IRQn TIM2_IRQn

#define MOTOR_PUL_IRQHandler TIM2_IRQHandler

//引脚定义

/*******************************************************/

//Motor 方向

#define MOTOR_DIR_PIN GPIO_PIN_1

#define MOTOR_DIR_GPIO_PORT GPIOE

#define MOTOR_DIR_GPIO_CLK_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE()

//Motor 使能

#define MOTOR_EN_PIN GPIO_PIN_0

#define MOTOR_EN_GPIO_PORT GPIOE

#define MOTOR_EN_GPIO_CLK_ENABLE() __HAL_RCC_GPIOE_CLK_ENABLE()

//Motor 脉冲

#define MOTOR_PUL_PIN GPIO_PIN_5

#define MOTOR_PUL_GPIO_PORT GPIOI

#define MOTOR_PUL_GPIO_CLK_ENABLE() __HAL_RCC_GPIOI_CLK_ENABLE()

使用宏定义非常方便程序升级、移植。如果使用不同的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

35

36

37

38

39

40

41

42

43

44

45/**

* @brief 配置按键用到的I/O口

* @param 无

* @retval 无

*/

void Key_GPIO_Config(void)

{

GPIO_InitTypeDef GPIO_InitStructure;

/*开启按键GPIO口的时钟*/

KEY1_GPIO_CLK_ENABLE();

KEY2_GPIO_CLK_ENABLE();

KEY3_GPIO_CLK_ENABLE();

KEY4_GPIO_CLK_ENABLE();

KEY5_GPIO_CLK_ENABLE();

/*选择按键的引脚*/

GPIO_InitStructure.Pin = KEY1_PIN;

/*设置引脚为输入模式*/

GPIO_InitStructure.Mode = GPIO_MODE_INPUT;

/*设置引脚不上拉也不下拉*/

GPIO_InitStructure.Pull = GPIO_PULLDOWN;

/*使用上面的结构体初始化按键*/

HAL_GPIO_Init(KEY1_GPIO_PORT, &GPIO_InitStructure);

/*选择按键的引脚*/

GPIO_InitStructure.Pin = KEY2_PIN;

/*使用上面的结构体初始化按键*/

HAL_GPIO_Init(KEY2_GPIO_PORT, &GPIO_InitStructure);

/*选择按键的引脚*/

GPIO_InitStructure.Pin = KEY3_PIN;

/*使用上面的结构体初始化按键*/

HAL_GPIO_Init(KEY3_GPIO_PORT, &GPIO_InitStructure);

/*选择按键的引脚*/

GPIO_InitStructure.Pin = KEY4_PIN;

/*使用上面的结构体初始化按键*/

HAL_GPIO_Init(KEY4_GPIO_PORT, &GPIO_InitStructure);

/*选择按键的引脚*/

GPIO_InitStructure.Pin = KEY5_PIN;

/*使用上面的结构体初始化按键*/

HAL_GPIO_Init(KEY5_GPIO_PORT, &GPIO_InitStructure);

}

开启按键IO对应的时钟,并在主函数中设置按键轮询。当按键按下时,会进入并且执行相应代码。

定时器初始化配置

定时器初始化配置¶

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/*

* 注意:TIM_TimeBaseInitTypeDef结构体里面有5个成员,TIM6和TIM7的寄存器里面只有

* TIM_Prescaler和TIM_Period,所以使用TIM6和TIM7的时候只需初始化这两个成员即可,

* 另外三个成员是通用定时器和高级定时器才有.

*-----------------------------------------------------------------------------

* TIM_Prescaler 都有

* TIM_CounterMode TIMx,x[6,7]没有,其他都有(通用定时器)

* TIM_Period 都有

* TIM_ClockDivision TIMx,x[6,7]没有,其他都有(通用定时器)

* TIM_RepetitionCounter TIMx,x[1,8]才有(高级定时器)

*-----------------------------------------------------------------------------

*/

static void TIM_Mode_Config(void)

{

MOTOR_PUL_CLK_ENABLE();

TIM_TimeBaseStructure.Instance = MOTOR_PUL_TIM;

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

//当定时器从0计数到4999,即为5000次,为一个定时周期

TIM_TimeBaseStructure.Init.Period = 300-1;

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

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

TIM_TimeBaseStructure.Init.Prescaler = 84-1;

// 计数方式

TIM_TimeBaseStructure.Init.CounterMode=TIM_COUNTERMODE_UP;

// 采样时钟分频

TIM_TimeBaseStructure.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;

// 初始化定时器TIMx, x[2,5] [9,14]

HAL_TIM_Base_Init(&TIM_TimeBaseStructure);

// 开启定时器更新中断

HAL_TIM_Base_Start_IT(&TIM_TimeBaseStructure);

}

首先对定时器进行初始化,定时器模式配置函数主要就是对这结构体的成员进行初始化,

然后通过相应的初始化函数把这些参数写入定时器的寄存器中。

有关结构体的成员介绍请参考定时器详解章节。

由于定时器坐在的APB总线不完全一致,所以说,定时器的时钟是不同的,在使能定时器时钟时必须特别注意,

在这里使用的是定时器2,通用定时器的总线频率为84MHZ,分频参数选择为(84-1),也就是当计数器计数到1M时为一个周期,

计数累计到(300-1)时产生一个中断,使用向上计数方式。产生中断后翻转IO口电平即可。

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

然后调用HAL_TIM_Base_Init初始化定时器并开启定时器更新中断。

步进电机初始化

步进电机初始化¶

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/**

* @brief 引脚初始化

* @retval 无

*/

void stepper_Init()

{

/*定义一个GPIO_InitTypeDef类型的结构体*/

GPIO_InitTypeDef GPIO_InitStruct;

/*开启Motor相关的GPIO外设时钟*/

MOTOR_DIR_GPIO_CLK_ENABLE();

MOTOR_PUL_GPIO_CLK_ENABLE();

MOTOR_EN_GPIO_CLK_ENABLE();

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

GPIO_InitStruct.Pin = MOTOR_DIR_PIN;

/*设置引脚的输出类型为推挽输出*/

GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;

GPIO_InitStruct.Pull =GPIO_PULLUP;

/*设置引脚速率为高速 */

GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;

/*Motor 方向引脚 初始化*/

HAL_GPIO_Init(MOTOR_DIR_GPIO_PORT, &GPIO_InitStruct);

/*Motor 脉冲引脚 初始化*/

GPIO_InitStruct.Pin = MOTOR_PUL_PIN;

HAL_GPIO_Init(MOTOR_PUL_GPIO_PORT, &GPIO_InitStruct);

/*Motor 使能引脚 初始化*/

GPIO_InitStruct.Pin = MOTOR_EN_PIN;

HAL_GPIO_Init(MOTOR_EN_GPIO_PORT, &GPIO_InitStruct);

/*关掉使能*/

MOTOR_EN(OFF);

/*初始化定时器*/

TIMx_Configuration();

}

步进电机引脚使用必须选择相应的模式和设置对应的参数,使用GPIO之前都必须开启相应端口时钟。

初始化结束后可以先将步进电机驱动器的使能先关掉MOTOR_EN(OFF),需要旋转的时候,再将其打开即可。

最后需要初始化定时器,来反转引脚电平以达到模拟脉冲的目的。

主函数

主函数¶

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/**

* @brief 主函数

* @param 无

* @retval 无

*/

int main(void)

{

int i=0,j=0;

int dir_val=0;

int en_val=0;

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

SystemClock_Config();

/*初始化USART 配置模式为 115200 8-N-1,中断接收*/

DEBUG_USART_Config();

printf("欢迎使用野火 电机开发板 步进电机 IO口模拟控制 例程\r\n");

printf("按下按键2可修改旋转方向,按下按键3可修改使能\r\n");

/*按键中断初始化*/

Key_GPIO_Config();

/*步进电机初始化*/

stepper_Init();

MOTOR_EN(0);

while(1)

{

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

{

// LED2 取反

LED2_TOGGLE;

/*改变方向*/

dir_val=(++i % 2) ? CW : CCW;

MOTOR_DIR(dir_val);

}

if( Key_Scan(KEY3_GPIO_PORT,KEY3_PIN) == KEY_ON )

{

// LED1 取反

LED1_TOGGLE;

/*改变使能*/

en_val=(++j % 2) ? CW : CCW;

MOTOR_EN(en_val);

}

}

}

主函数中首先对系统和外设初始化,在while(1)里面是两个判断语句,主要作用是使能开关和方向的改变,在if语句中可以改变步进电机的状态。

当KEY2按下后,改变旋转方向, 转换方向 即CW,正转运行;相对地,CCW,反转运行 采用三元运算符 i是奇数是反转运行,i是偶数是正转运行。

与方式一不同的是,从延时模拟脉冲变成了中断翻转电平增加了脉冲的准确性。