m0_61528815 2024-01-26 21:11 采纳率: 50%
浏览 8

学习江科大的课程后,拼在一起的程序

#模式一:每按下一次按键,小灯亮0.5秒
模式二:按下按键实现PWM呼吸灯,可以用按键(PB11)改变呼吸灯的呼吸速度
按键1(PB0):切换模式
按键2(PB11):
在模式一 每按下一次按键小灯亮0.5秒
在模式二按键改变呼吸速度 按下五次后达到最大,再次按下后变为最慢呼吸速度
其中两个按键的中断正常触发,但有以下问题:
1、程序下载后,在PA0口的小灯(低电平触发)是亮的,但我在LED_Init中已经将引脚置为高电平
2、我要实现的功能都没有实现,只有在程序复位后第一次按下按键1有效果,然后别的按键都没用了。但我在debug中看到按键的中断正常触发,相应的值也改变了
#main.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"
#include "PWM.h"
#include "Key.h"
#include "LED.h"


uint16_t LightSensor_Count;                //全局变量,用于计数
uint16_t Key_Count;
uint16_t Mode_number = 1;
uint16_t key_Speed = 1;

int main(void)
{
    OLED_Power(); //给OLED供电
    
    /*模块初始化*/
    OLED_Init();        //OLED初始化
    Key_Init();    //按键初始化

  LED_Init();
  PWM_Init();

    
    while (1)
    {
          if(Mode_number == 1)
        {
                /*OLED显示*/
         OLED_ShowString(1, 1, "Light_Count:");
         OLED_ShowString(2, 1, "Key_Count:");
         OLED_ShowNum(1, 13, LightSensor_Count, 3);
         OLED_ShowNum(2, 11, Key_Count, 3);
        }
        if(Mode_number == 2)
        {
            PWM_speed_control(key_Speed);
        }
    }
}

#key.c


#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "PWM.h"

extern uint16_t Key_Count;    
extern uint16_t Mode_number;
extern uint16_t key_Speed;

/**
  * 函    数:按键初始化
  * 参    数:无
  * 返 回 值:无
  */
void Key_Init(void)
{
    /*开启时钟*/
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);        //开启GPIOB的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);        //开启AFIO的时钟,外部中断必须开启AFIO的时钟
    
    /*GPIO初始化*/
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11|GPIO_Pin_0;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);                        //将引脚初始化为上拉输入
    
    /*AFIO选择中断引脚*/
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource11);//将外部中断的14号线映射到GPIOB,即选择PB14为外部中断引脚
    
    /*EXTI初始化*/
    EXTI_InitTypeDef EXTI_InitStructure;                        //定义结构体变量
    EXTI_InitStructure.EXTI_Line = EXTI_Line11;                    //选择配置外部中断的11号线
    EXTI_InitStructure.EXTI_LineCmd = ENABLE;                    //指定外部中断线使能
    EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;            //指定外部中断线为中断模式
    EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;        //指定外部中断线为下降沿触发
    EXTI_Init(&EXTI_InitStructure);                                //将结构体变量交给EXTI_Init,配置EXTI外设
    
    /*NVIC中断分组*/
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                //配置NVIC为分组2
                                                                //即抢占优先级范围:0~3,响应优先级范围:0~3
                                                                //此分组配置在整个工程中仅需调用一次
                                                                //若有多个中断,可以把此代码放在main函数内,while循环之前
                                                                //若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
    
    /*NVIC配置*/
    NVIC_InitTypeDef NVIC_InitStructure;                        //定义结构体变量
    NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;        //选择配置NVIC的EXTI15_10线
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;                //指定NVIC线路使能
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;    //指定NVIC线路的抢占优先级为1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;            //指定NVIC线路的响应优先级为1
    NVIC_Init(&NVIC_InitStructure);                                //将结构体变量交给NVIC_Init,配置NVIC外设
    
        /*AFIO选择中断引脚*/
    GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);   //将外部中断的0号线映射到GPIOB,即选择PB0为外部中断引脚

    /*EXTI初始化*/
    EXTI_InitTypeDef EXTI_InitStructure_PB0;                       //定义结构体变量
    EXTI_InitStructure_PB0.EXTI_Line = EXTI_Line0;                 //选择配置外部中断的0号线
    EXTI_InitStructure_PB0.EXTI_LineCmd = ENABLE;                  //指定外部中断线使能
    EXTI_InitStructure_PB0.EXTI_Mode = EXTI_Mode_Interrupt;        //指定外部中断线为中断模式
    EXTI_InitStructure_PB0.EXTI_Trigger = EXTI_Trigger_Falling;    //指定外部中断线为下降沿触发
    EXTI_Init(&EXTI_InitStructure_PB0);                            //将结构体变量交给EXTI_Init,配置EXTI外设

    /*NVIC配置*/
    NVIC_InitTypeDef NVIC_InitStructure_PB0;                        //定义结构体变量
    NVIC_InitStructure_PB0.NVIC_IRQChannel = EXTI0_IRQn;           //选择配置NVIC的EXTI0线
    NVIC_InitStructure_PB0.NVIC_IRQChannelCmd = ENABLE;            //指定NVIC线路使能
    NVIC_InitStructure_PB0.NVIC_IRQChannelPreemptionPriority = 1;  //指定NVIC线路的抢占优先级为1
    NVIC_InitStructure_PB0.NVIC_IRQChannelSubPriority = 1;         //指定NVIC线路的响应优先级为1
    NVIC_Init(&NVIC_InitStructure_PB0);                             //将结构体变量交给NVIC_Init,配置NVIC外设
}



/**
  * 函    数:EXTI15_10外部中断函数
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
  *           函数名为预留的指定名称,可以从启动文件复制
  *           请确保函数名正确,不能有任何差异,否则中断函数将不能进入
  */
void EXTI15_10_IRQHandler(void)
{
    
    if (EXTI_GetITStatus(EXTI_Line11) == SET)        //判断是否是外部中断14号线触发的中断
    {
        /*如果出现数据乱跳的现象,可再次判断引脚电平,以避免抖动*/
        Delay_ms(20);
        if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0)
        {
            if(Mode_number == 1)
            {
            Key_Count++;
            GPIO_ResetBits(GPIOA, GPIO_Pin_0);
      Delay_ms(500);        
            GPIO_SetBits(GPIOA, GPIO_Pin_0);    
            }
            else if(Mode_number == 2)
            {
                key_Speed++;
                if(key_Speed == 6)
                {
                    key_Speed = 1;
                }
            }
        }
        EXTI_ClearITPendingBit(EXTI_Line11);        //清除外部中断11号线的中断标志位
                                                    //中断标志位必须清除
                                                    //否则中断将连续不断地触发,导致主程序卡死
    }
}

void EXTI0_IRQHandler(void)
{
    if(EXTI_GetITStatus(EXTI_Line0) != RESET)  // 检查是否是 PB0 的中断
    {
            Delay_ms(20);        
            if (GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)            //读PB0输入寄存器的状态,如果为0,则代表按键2按下
            {
                                                    //延时消抖
                if(Mode_number == 1)
                    Mode_number =2;
                else if(Mode_number == 2)
                    Mode_number = 1;
            }
        EXTI_ClearITPendingBit(EXTI_Line0);  // 清除中断挂起位
    }
}


#pwm.c

#include "stm32f10x.h"                  // Device header
#include "Delay.h"

/**
  * 函    数:PWM初始化
  * 参    数:无
  * 返 回 值:无
  */
void PWM_Init(void)
{
    /*开启时钟*/
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);            //开启TIM2的时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);            //开启GPIOA的时钟
    
    /*GPIO重映射*/
//    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);            //开启AFIO的时钟,重映射必须先开启AFIO的时钟
//    GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);            //将TIM2的引脚部分重映射,具体的映射方案需查看参考手册
//    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);        //将JTAG引脚失能,作为普通GPIO引脚使用
    
    /*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);                            //将PA0引脚初始化为复用推挽输出    
                                                                    //受外设控制的引脚,均需要配置为复用模式        
    
    /*配置时钟源*/
    TIM_InternalClockConfig(TIM2);        //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
    
    /*时基单元初始化*/
    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;            //重复计数器,高级定时器才会用到
    TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);             //将结构体变量交给TIM_TimeBaseInit,配置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;                                //初始的CCR值
    TIM_OC1Init(TIM2, &TIM_OCInitStructure);                        //将结构体变量交给TIM_OC1Init,配置TIM2的输出比较通道1
    
    /*TIM使能*/
    TIM_Cmd(TIM2, ENABLE);            //使能TIM2,定时器开始运行
}

/**
  * 函    数:PWM设置CCR
  * 参    数:Compare 要写入的CCR的值,范围:0~100
  * 返 回 值:无
  * 注意事项:CCR和ARR共同决定占空比,此函数仅设置CCR的值,并不直接是占空比
  *           占空比Duty = CCR / (ARR + 1)
  */
void PWM_SetCompare1(uint16_t Compare)
{
    TIM_SetCompare1(TIM2, Compare);        //设置CCR1的值
}

void PWM_speed_control(uint16_t Speed)
{
      uint16_t i;
        for (i = 0; i <= 100; i+=Speed)
        {
            PWM_SetCompare1(i);            //依次将定时器的CCR寄存器设置为0~100,PWM占空比逐渐增大,LED逐渐变亮
            Delay_ms(10);                //延时10ms
        }
        for (i = 0; i <= 100; i+=Speed)
        {
            PWM_SetCompare1(100 - i);    //依次将定时器的CCR寄存器设置为100~0,PWM占空比逐渐减小,LED逐渐变暗
            Delay_ms(10);                //延时10ms
        }
}



  • 写回答

1条回答 默认 最新

  • CSDN-Ada助手 CSDN-AI 官方账号 2024-01-27 08:17
    关注

    【以下回答由 GPT 生成】

    问题分析: 根据问题描述,主要存在两个问题: 1. PA0口的小灯在程序下载后是亮的,尽管在LED_Init函数中已经将引脚置为高电平。 2. 按下按键后只有在程序复位后第一次按下按键1才有效果,别的按键都没用了。

    解决方案: 1. 问题一的解决方案: 问题一可能是由于LED_Init函数没有正确配置引脚的原因导致的。我们需要检查LED_Init函数的代码,并确保引脚正确配置为高电平。

    假设LED_Init函数的代码如下所示:

    void LED_Init(void) {
        GPIO_InitTypeDef GPIO_InitStruct;
    
        // 开启GPIOA的时钟
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    
        // 配置PA0引脚为推挽输出
        GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStruct);
    
        // 将PA0引脚置为高电平
        GPIO_SetBits(GPIOA, GPIO_Pin_0);
    }
    

    根据上述代码,我们可以看到GPIO_SetBits(GPIOA, GPIO_Pin_0)语句确实将PA0引脚置为高电平。所以问题可能是其他地方引起的。

    经过查阅资料,发现PA0引脚与JTAG功能有冲突。在使用PA0引脚之前,需要屏蔽或关闭相关的JTAG功能。具体解决办法如下: 在main函数的开头添加以下代码,将JTAG功能引脚屏蔽掉:

    // 屏蔽JTAG引脚,使用PA0引脚
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
    
    1. 问题二的解决方案: 问题二可能是由于中断程序没有正确配置或者中断触发方式不正确导致的。

    首先,我们需要检查Key_Init函数的代码,并确保相关按键的中断配置正确。

    假设Key_Init函数的代码如下所示:

    void Key_Init(void) {
        GPIO_InitTypeDef GPIO_InitStruct;
        NVIC_InitTypeDef NVIC_InitStruct;
        EXTI_InitTypeDef EXTI_InitStruct;
    
        // 开启GPIOB的时钟
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
    
        // 配置PB0和PB11引脚为浮空输入
        GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_11;
        GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
        GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOB, &GPIO_InitStruct);
    
        // 配置外部中断0和外部中断15
        EXTI_InitStruct.EXTI_Line = EXTI_Line0 | EXTI_Line11;
        EXTI_InitStruct.EXTI_Mode = EXTI_Mode_Interrupt;
        EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
        EXTI_InitStruct.EXTI_LineCmd = ENABLE;
        EXTI_Init(&EXTI_InitStruct);
    
        // 配置中断优先级
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
        NVIC_InitStruct.NVIC_IRQChannel = EXTI0_IRQn;
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
        NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStruct);
    
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
        NVIC_InitStruct.NVIC_IRQChannel = EXTI15_10_IRQn;
        NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
        NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStruct);
    }
    

    根据上述代码,我们可以看到EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;语句将外部中断的触发方式设置为上升沿和下降沿都触发。这可能导致中断触发过于频繁,从而影响按键的功能。为了减少频繁中断触发,我们可以将其改为只在按键按下时触发,即修改EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;。

    另外,我们还需要检查中断处理函数是否正确配置。

    假设EXTI0_IRQHandler函数的代码如下所示:

    void EXTI0_IRQHandler(void) {
        if (EXTI_GetITStatus(EXTI_Line0) != RESET) {
            // 按键1按下
            Key_Count++;
            if (Mode_number == 1) {
                LED_Toggle();
                Delay_ms(500);
            }
    
            EXTI_ClearITPendingBit(EXTI_Line0);
        }
    }
    

    假设EXTI15_10_IRQHandler函数的代码如下所示:

    void EXTI15_10_IRQHandler(void) {
        if (EXTI_GetITStatus(EXTI_Line11) != RESET) {
            // 按键2按下
            key_Speed++;
    
            EXTI_ClearITPendingBit(EXTI_Line11);
        }
    }
    

    根据上述代码,我们可以看到在中断处理函数中有执行了EXTI_ClearITPendingBit来清除中断标志位,确保下一次中断可以正常触发。

    综上所述,以下是问题的解决方案:

    对于问题一:
    在main函数的开头添加以下代码,将JTAG功能引脚屏蔽掉:
    ```c
    // 屏蔽JTAG引脚,使用PA0引脚
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
    GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable, ENABLE);
    

    对于问题二: 在Key_Init函数中,将EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising_Falling;改为EXTI_InitStruct.EXTI_Trigger = EXTI_Trigger_Rising;,以减少频繁中断触发。

    确保在中断处理函数中执行了EXTI_ClearITPendingBit来清除中断标志位。

    请注意,以上解决方案仅供参考,请根据具体情况进行调整。 ```


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

报告相同问题?

问题事件

  • 创建了问题 1月26日

悬赏问题

  • ¥15 关于#网络#的问题:网络是从楼上引一根网线下来,接了2台傻瓜交换机,也更换了ip还是不行
  • ¥15 资源泄露软件闪退怎么解决?
  • ¥15 各位佬,下面的问题怎么用python实现?
  • ¥15 CCF-CSP 2023 第三题 解压缩(50%)
  • ¥30 comfyui openpose报错
  • ¥20 Wpf Datarid单元格闪烁效果的实现
  • ¥15 图像分割、图像边缘提取
  • ¥15 sqlserver执行存储过程报错
  • ¥100 nuxt、uniapp、ruoyi-vue 相关发布问题
  • ¥15 浮窗和全屏应用同时存在,全屏应用输入法无法弹出