STM32L476, 利用中断使按键控制步进电机转动和停止

使用STM32L476, 利用中断使按键控制步进电机转动和停止。
下面的代码可以按一下,电机可以开始旋转,但是怎样才能按第二下的时候电机停止转动。

#include "stm32l476xx.h"

#define LED_PIN 5 
#define BUTTON_PIN 13

#define PORT_PIN_5 5
#define PORT_PIN_6 6
#define PORT_PIN_8 8
#define PORT_PIN_9 9

void configure_GPIO(void);
void configure_EXTI(void);
void configure_motor_GPIO();
void EXTI15_10_IRQHandler(void);
void specific_angle (int angle);

int main(void)
{
    configure_GPIO();
    configure_EXTI();
    configure_motor_GPIO();
    
    while (1) {
         //等待中断
    }
}


void configure_GPIO(void)
{
    // 开启端口A,C时钟
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN; 
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN;    
    
     //设置LED输出
    GPIOA->MODER &= ~(3UL << (2 * LED_PIN)); 
    GPIOA->MODER |=  (1UL << (2 * LED_PIN)); 

    // 设置按键输入
    GPIOC->MODER &= ~(3UL << (2 * BUTTON_PIN)); 
}

void configure_motor_GPIO(){
    // 设置端口C时钟
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN;
    // 设置5,6,8,9为输出
    GPIOC->MODER &= ~(GPIO_MODER_MODE5 | GPIO_MODER_MODE6 | GPIO_MODER_MODE8 | GPIO_MODER_MODE9);
    GPIOC->MODER |= (GPIO_MODER_MODE5_0 | GPIO_MODER_MODE6_0 | GPIO_MODER_MODE8_0 | GPIO_MODER_MODE9_0); 
}

void configure_EXTI(void)
{
    // 设置中断时钟
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    
    // 连接PC13
    SYSCFG->EXTICR[3] &= ~SYSCFG_EXTICR4_EXTI13;                                            
    SYSCFG->EXTICR[3] |= SYSCFG_EXTICR4_EXTI13_PC;        
    
    // 上升沿
    EXTI->RTSR1 |= EXTI_RTSR1_RT13;
    
    //启用 EXTI13 中断请求
    EXTI->IMR1 |= EXTI_IMR1_IM13;
    
    //启用 EXTI15-10 的中断
    NVIC_EnableIRQ(EXTI15_10_IRQn);
}


//如果有按键,步进电机开始转180度
void EXTI15_10_IRQHandler(void)
{
    if ((EXTI->PR1 & EXTI_PR1_PIF13) != 0) { 
        specific_angle(180);
        EXTI->PR1 = EXTI_PR1_PIF13;
    }
}

// 步进电机
void specific_angle (int angle){
    unsigned char fullstep[4] = {0x9,0xA, 0x6, 0x5};
    unsigned int i,j,k;
    unsigned int A,Anot,B,Bnot;
    unsigned int steps;
    
    steps = angle*512/360;
    
    for(j =0; j<steps; j++){
        for (i =0; i<4;i++){
            for(k =0;k<1500;k++); //short delay
                        A        = (fullstep[i] & 0x8)>> 3;
                        Anot  = (fullstep[i] & 0x4)>> 2;
                        B        = (fullstep[i] & 0x2)>> 1;
                        Bnot    = (fullstep[i] & 0x1)>> 0;
                    
                        GPIOC->ODR &= ~(1 << PORT_PIN_5 | 1 << PORT_PIN_6 | 1 << PORT_PIN_8 | 1 << PORT_PIN_9);
                        GPIOC->ODR |= ((A << PORT_PIN_5) | (Anot << PORT_PIN_6) | (B << PORT_PIN_8) | (Bnot << PORT_PIN_9));   
        }
    }
}


“Devil组”引证GPT后的撰写:

  • 在中断处理程序中,我们可以设置一个标志位来表示当前是否需要停止电机转动。当按下按钮时,标志位设为1,电机开始转动;再次按下按钮时,标志位设为0,电机停止转动。

具体实现可以按照以下步骤进行:

在全局变量中定义一个标志位,表示当前是否需要停止电机转动,初始值为0。

int stop_motor = 0;


2.在按键中断处理程序中,当按下按钮时,将标志位设为1,电机开始转动;再次按下按钮时,将标志位设为0,电机停止转动。

void EXTI15_10_IRQHandler(void)
{
    if ((EXTI->PR1 & EXTI_PR1_PIF13) != 0) { 
        if (stop_motor == 0) {
            specific_angle(180);
            stop_motor = 1;
        } else {
            stop_motor = 0;
        }
        EXTI->PR1 = EXTI_PR1_PIF13;
    }
}


3.在电机控制函数中,加入判断标志位的语句,如果标志位为1,则继续转动电机,否则停止电机转动。

void specific_angle (int angle){
    unsigned char fullstep[4] = {0x9,0xA, 0x6, 0x5};
    unsigned int i,j,k;
    unsigned int A,Anot,B,Bnot;
    unsigned int steps;
    
    steps = angle*512/360;
    
    for(j =0; j<steps; j++){
        for (i =0; i<4;i++){
            for(k =0;k<1500;k++); //short delay
                        A        = (fullstep[i] & 0x8)>> 3;
                        Anot  = (fullstep[i] & 0x4)>> 2;
                        B        = (fullstep[i] & 0x2)>> 1;
                        Bnot    = (fullstep[i] & 0x1)>> 0;
                    
                        GPIOC->ODR &= ~(1 << PORT_PIN_5 | 1 << PORT_PIN_6 | 1 << PORT_PIN_8 | 1 << PORT_PIN_9);
                        GPIOC->ODR |= ((A << PORT_PIN_5) | (Anot << PORT_PIN_6) | (B << PORT_PIN_8) | (Bnot << PORT_PIN_9));   
        }
        if (stop_motor == 1) {
            break;
        }
    }
    // 电机停止转动
    GPIOC->ODR &= ~(1 << PORT_PIN_5 | 1 << PORT_PIN_6 | 1 << PORT_PIN_8 | 1 << PORT_PIN_9);
}


这样,当按下按钮时,电机开始转动,再次按下按钮时,电机停止转动。

参考GPT的内容和自己的思路,要让电机在按下按键时开始旋转,并在第二次按下按键时停止旋转,可以使用一个全局变量来记录当前电机的状态,例如:

volatile int motor_on = 0; //电机状态,0表示停止,1表示旋转


在中断处理程序 EXTI15_10_IRQHandler 中,根据 motor_on 的状态来决定是开始旋转还是停止旋转:

void EXTI15_10_IRQHandler(void)
{
    if ((EXTI->PR1 & EXTI_PR1_PIF13) != 0) { 
        if (motor_on) {
            specific_angle(0); //停止旋转
            motor_on = 0;
        } else {
            specific_angle(180); //开始旋转
            motor_on = 1;
        }
        EXTI->PR1 = EXTI_PR1_PIF13;
    }
}


这样,第一次按下按键时,电机开始旋转,motor_on 被设置为 1;第二次按下按键时,电机停止旋转,motor_on 被设置为 0。在 specific_angle 函数中,如果 angle 为 0,则电机不会转动,否则电机会按照指定的角度旋转。

回答不易,还请采纳!!!

该回答引用ChatGPT
修改代码如下

#include "stm32l476xx.h"

#define LED_PIN 5 
#define BUTTON_PIN 13

#define PORT_PIN_5 5
#define PORT_PIN_6 6
#define PORT_PIN_8 8
#define PORT_PIN_9 9

void configure_GPIO(void);
void configure_EXTI(void);
void configure_motor_GPIO();
void EXTI15_10_IRQHandler(void);
void EXTI0_IRQHandler(void);
void specific_angle(int angle);

int is_running = 0;

int main(void)
{
    configure_GPIO();
    configure_EXTI();
    configure_motor_GPIO();
    
    while (1) {
        //等待中断
    }
}

void configure_GPIO(void)
{
    // 开启端口A,C时钟
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN; 
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN;    
    
    // 设置LED输出
    GPIOA->MODER &= ~(3UL << (2 * LED_PIN)); 
    GPIOA->MODER |=  (1UL << (2 * LED_PIN)); 

    // 设置按键输入
    GPIOC->MODER &= ~(3UL << (2 * BUTTON_PIN)); 

    // 设置停止按钮输入
    GPIOA->MODER &= ~(3UL << (2 * 0)); 
}

void configure_motor_GPIO()
{
    // 设置端口C时钟
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN;

    // 设置5,6,8,9为输出
    GPIOC->MODER &= ~(GPIO_MODER_MODE5 | GPIO_MODER_MODE6 | GPIO_MODER_MODE8 | GPIO_MODER_MODE9);
    GPIOC->MODER |= (GPIO_MODER_MODE5_0 | GPIO_MODER_MODE6_0 | GPIO_MODER_MODE8_0 | GPIO_MODER_MODE9_0); 
}

void configure_EXTI(void)
{
    // 设置中断时钟
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    
    // 连接PC13
    SYSCFG->EXTICR[3] &= ~SYSCFG_EXTICR4_EXTI13;                                            
    SYSCFG->EXTICR[3] |= SYSCFG_EXTICR4_EXTI13_PC;        

    // 连接PA0
    SYSCFG->EXTICR[0] &= ~SYSCFG_EXTICR1_EXTI0;                                            
    SYSCFG->EXTICR[0] |= SYSCFG_EXTICR1_EXTI0_PA;        
    
    // 上升沿
    EXTI->RTSR1 |= EXTI_RTSR1_RT13;
    EXTI->RTSR1 |= EXTI_RTSR1_RT0;
    
    //启用 EXTI13 和 EXTI0 中断请求
    EXTI->IMR1 |= EXTI_IMR1_IM13;
    EXTI->IMR1 |= EXTI_IMR1_IM0;
    
    //启用 EXTI15-10 的中断
    NVIC_EnableIRQ(EXTI15_10_IRQn);
    
    //启用 EXTI0 的中断
    NVIC_EnableIRQ(EXTI0_IRQn);
}

//如果有按键,步进电机开始转180度
void EXTI15_10_IRQHandler(void)
{
    if ((EXTI->PR1 & EXTI_PR1_PIF13) != 0) { 
        specific_angle(180);
        is_running = 1;
        EXTI->PR1 = EXTI_PR1_PIF13;
    }
}

//止电机
void EXTI0_IRQHandler(void)
{
      if ((EXTI->PR1 & EXTI_PR1_PIF0) != 0) {
          specific_angle(0); // 电机停止
          is_running = 0;
          EXTI->PR1 = EXTI_PR1_PIF0;
      }
}

void specific_angle(int angle)
{
        unsigned char fullstep[4] = {0x9, 0xA, 0x6, 0x5};
        unsigned int i, j, k;
        unsigned int A, Anot, B, Bnot;
        unsigned int steps;
        steps = angle * 512 / 360;

      for (j = 0; j < steps; j++) {
          if (!is_running) {
      break; // 如果电机停止,退出循环
}

    for (i = 0; i < 4; i++) {
        for (k = 0; k < 1500; k++); // 短暂的延迟

        A = (fullstep[i] & 0x8) >> 3;
        Anot = (fullstep[i] & 0x4) >> 2;
        B = (fullstep[i] & 0x2) >> 1;
        Bnot = (fullstep[i] & 0x1) >> 0;
        
        GPIOC->ODR &= ~(1 << PORT_PIN_5 | 1 << PORT_PIN_6 | 1 << PORT_PIN_8 | 1 << PORT_PIN_9);
        GPIOC->ODR |= ((A << PORT_PIN_5) | (Anot << PORT_PIN_6) | (B << PORT_PIN_8) | (Bnot << PORT_PIN_9));   
        }
     }      
}


参考gpt和自己的思路,为了使按下按键能够停止步进电机的旋转,需要记录电机的运动状态并在下一次中断时停止电机的旋转。下面是修改后的代码:


#include "stm32l476xx.h"
#define LED_PIN 5 
#define BUTTON_PIN 13
#define PORT_PIN_5 5
#define PORT_PIN_6 6
#define PORT_PIN_8 8
#define PORT_PIN_9 9

void configure_GPIO(void);
void configure_EXTI(void);
void configure_motor_GPIO();
void EXTI15_10_IRQHandler(void);
void specific_angle(int angle);
void stop_motor(void);

int motor_running = 0;

int main(void)
{
    configure_GPIO();
    configure_EXTI();
    configure_motor_GPIO();
    while (1) {
         //等待中断
    }
}

void configure_GPIO(void)
{
    // 开启端口A,C时钟
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOAEN; 
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN;    
    // 设置LED输出
    GPIOA->MODER &= ~(3UL << (2 * LED_PIN)); 
    GPIOA->MODER |=  (1UL << (2 * LED_PIN)); 
    // 设置按键输入
    GPIOC->MODER &= ~(3UL << (2 * BUTTON_PIN)); 
}

void configure_motor_GPIO(){
    // 设置端口C时钟
    RCC->AHB2ENR |= RCC_AHB2ENR_GPIOCEN;
    // 设置5,6,8,9为输出
    GPIOC->MODER &= ~(GPIO_MODER_MODE5 | GPIO_MODER_MODE6 | GPIO_MODER_MODE8 | GPIO_MODER_MODE9);
    GPIOC->MODER |= (GPIO_MODER_MODE5_0 | GPIO_MODER_MODE6_0 | GPIO_MODER_MODE8_0 | GPIO_MODER_MODE9_0); 
}

void configure_EXTI(void)
{
    // 设置中断时钟
    RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
    // 连接PC13
    SYSCFG->EXTICR[3] &= ~SYSCFG_EXTICR4_EXTI13;                                            
    SYSCFG->EXTICR[3] |= SYSCFG_EXTICR4_EXTI13_PC;        
    // 上升沿
    EXTI->RTSR1 |= EXTI_RTSR1_RT13;
    //启用 EXTI13 中断请求
    EXTI->IMR1 |= EXTI_IMR1_IM13;
    //启用 EXTI15-10 的中断
    NVIC_EnableIRQ(EXTI15_10_IRQn);
}

//如果有按键,步进电机开始转动,如果电机正在运行,按下按键会停止电机的旋转
void EXTI15_10_IRQHandler(void)
{
    if ((EXTI->PR1 & EXTI_PR1_PIF13) != 0) { 
        if (!motor_running) {
            specific_angle(180);
            motor_running = 1;
        } else {
            stop_motor();
            motor_running = 0;
        }
        EXTI->PR1 = EXTI_PR1_PIF13;
    }
}

void stop_motor(void)
{
    // 将电机驱动端口全部设为低电平
    GPIOC->ODR &= ~(1 << PORT_PIN_5 | 1 << PORT_PIN_6 | 1 << PORT_PIN_8 | 1 << PORT_PIN_9);
}

在 stop_motor 函数中,我们只需将电机驱动端口全部设为低电平即可停止电机。

请注意,在使用 GPIO 控制电机时,为了防止电机电流反冲击损坏芯片,最好使用反向并联二极管保护电路。此外,你需要根据你的具体电机和控制电路来调整步进电机的驱动方式和具体角度,以便获得所需的运动效果。

参考新必应。根据,你可以使用一个全局变量来记录按键的状态,比如:

int flag = 0; //定义一个全局变量flag,初始为0


然后,在中断服务函数中,每次按键时改变flag的值,比如:

void EXTI15_10_IRQHandler(void)
{
    if ((EXTI->PR1 & EXTI_PR1_PIF13) != 0) { 
        flag = !flag; //每次按键时改变flag的值,0变1,1变0
        EXTI->PR1 = EXTI_PR1_PIF13;
    }
}

最后,在主函数中,根据flag的值来决定是否执行步进电机的转动函数,比如:

int main(void)
{
    configure_GPIO();
    configure_EXTI();
    configure_motor_GPIO();
    
    while (1) {
         if(flag == 1){ //如果flag为1,则执行步进电机转动函数
             specific_angle(180);
         }
    }
}



这样就可以实现按一下开始转动,再按一下停止转动的功能了。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
要实现按一次控制电机开始旋转,再按一次控制电机停止旋转的功能,可以考虑对按键的按下次数进行计数,当按下次数为奇数时,启动电机转动;当按下次数为偶数时,则停止电机转动。

修改EXTI15_10_IRQHandler()函数如下:

void EXTI15_10_IRQHandler(void)
{
    static int count = 0; // 定义静态变量count记录按键按下次数
    if ((EXTI->PR1 & EXTI_PR1_PIF13) != 0) { 
        count++; //按键按下次数加1
        if (count % 2 == 1) { //奇数次按下,启动电机转动
            specific_angle(180); //转动180度
        }
        else { //偶数次按下,停止电机转动
            GPIOC->ODR &= ~(1 << PORT_PIN_5 | 1 << PORT_PIN_6 | 1 << PORT_PIN_8 | 1 << PORT_PIN_9); //将驱动电机的GPIO置为低电平
        }
        EXTI->PR1 = EXTI_PR1_PIF13; //清除中断标志位
    }
}

在main函数中加入while循环即可:

int main(void)
{
    configure_GPIO();
    configure_EXTI();
    configure_motor_GPIO();
    
    while (1) {
        //等待中断
    }
}

如果我的回答解决了您的问题,请采纳!

不知道你这个问题是否已经解决, 如果还没有解决的话:

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

您可以在按下第二个按键时,使用另一个GPIO引脚来停止电机。在代码中添加一个中断处理程序来检测第二个按键的状态,并相应地设置GPIO输出来停止电机转动。

以下是一个基本的示例代码片段,其中使用PA0和PA1作为输入引脚来控制电机的启停:

```c
#include "stm32l4xx.h"

void EXTI0_IRQHandler(void)

定义一个全局变量,记录电机状态,在中断处理函数内使用全局变量进行判断并操作,切记变量定义前面必须加volatile 不然中断处理函数内获取变量不会是最新的