STM32单片机入门学习——第12节: [5-2]对射式红外传感器计次&旋转编码器计次

[复制链接]
发表于 2025-10-1 21:26:23 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
写这个文章是用来学习的,记载一下我的学习过程。渴望我能不绝对峙下去,我只是一个小白,只是想好好学习,我知道这会很难,但我还是想去做!

     本文写于:2025.04.03   
  
  
前言

   本次条记是用来记载我的学习过程,同时把我必要的困难和思考记下来,有助于我的学习,同时也作为一种风俗,可以督促我学习,是一个鼓励自己的过程,让我们开始32单片机的学习之路。
   欢迎各人给我提意见,能给我的嵌入式之旅提供方向和门路,现在作为小白,我就先学习32单片机了,就跟着B站上的江协科技开始学习了.
   在这里会记载下江协科技32单片机开辟板的配套视频教程所作的实行和学习条记内容,由于我之前有一个开辟板,我大概率会用我的板子模仿着来做.让我们一起加油!
   别的为了增强我的学习结果:每次条记把我不知道大概题目在反面提出来,再下一篇开头作为解答!
开辟板分析

   本人接纳的是慧净的开辟板,由于这个板子是我N年前就买的板子,索性就拿来用了。别的我也购买了江科大的学习套间。
   原理图如下
1、开辟板原理图

2、STM32F103C6和51对比

3、STM32F103C6焦点板

视频中的都用这个开辟板来实现,如果有资源就利用起来。别的也筹划实现江协科技的套件。
下图是实物图

引用

【STM32入门教程-2023版 过细解说 中笔墨幕】
还参考了下图中的册本:
STM32库开辟实战指南:基于STM32F103(第2版)

数据手册
解答和科普

一、外部克制代码


DO数字输出端,恣意接一个GPIO,当我们的挡光片大概编码器在对射式红外传感器中心颠末时,这个DO就会输出电平跳变的信号,就会触发STM32 PB14口的克制,我们在克制函数中,实行变量++的步伐,然后主循环里调用OLED表现这个变量。

第一步,设置RCC,把我们这里涉及的外设的时钟都打开
第二步,设置GPIO。选择我们的端口为输入模式
第三步,设置AFIO,选择我们用的这一起GPIO,毗连到反面的EXTI
第四步, 设置EXTI,选择边沿触发方式,比如上升沿、降落沿大概双边沿
另有选择触发相应方式,可以选择克制相应和变乱相应
第五步,设置NVIC,给我们这个克制选择一个符合的优先级,末了通过NVIC,外部克制信号就能进入CPU了。
如许CPU才气收到克制信号,才气跳转到克制函数里实行克制步伐。
设置时钟
  1. void CounterSensor_Init(void)                //外部中断配置
  2. {
  3.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);                //开启APB2的GPIOB的外设时钟
  4.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);                        //开启APB2的AFIO的时钟
  5.         //接着还有EXTI和NVIC两个外设:这两个外设时钟一直都是打开着的,不需要我们再开启时钟了
复制代码
EXTI和NVIC这两个外设的时钟是不绝都制开着的,不必要我们再开启时钟了.
NVIC是内核的外设,内核的外设都是不必要开启时钟的,和CPU一起住在皇宫里的。
设置GPIO
  1.         GPIO_InitTypeDef   GPIO_InitStructure;
  2.         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;                        //上拉输入默认高电平
  3.         GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;
  4.         GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  5.         GPIO_Init(GPIOB,&GPIO_InitStructure);
复制代码
对于克制来说:要选择浮空输入、上拉输入、大概下拉输入。这此中的一个模式(可以看参考文档)
设置AFIO

  1. GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource8);
复制代码
当实行完这个函数后,AFIO的第8个数据选择器就拨好了,此中输入端被拨到了GPIOA外设上,对应的就是PA8引脚,输出端固定毗连的是EXIT的第8个克制线路,如许PA8引脚的电平信号就可以顺遂通过AFIO,进入到后级EXTI电路了。
设置EXTI

对于标志位,有的比力告急,在指标值位后会触发克制,在克制函数里,如果你想检察标志位和扫除标志位,就用下面两个函数;
以是你现在主步伐里检察和扫除标志位,就用上面两个函数;
如果想在克制函数里检察和扫除标志位,就用下面两个函数。
都是读写寄存器,只不外下面两个函数只能读写与克制有关的标志位,而且对克制是否允许做出来判定,而上面的这两个函数只是一样平常的读写标志位,没有额外的处置惩罚,能不能触发克制的标志位都能读取,以是发起主步伐用上面两个,克制步伐里用下面两个。只不外是举行了区分。
  1.         EXTI_InitTypeDef  EXTI_InitStructure;
  2.         EXTI_InitStructure.EXTI_Line=EXTI_Line8 ;
  3.         EXTI_InitStructure.EXTI_LineCmd=ENABLE;
  4.         EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
  5.         EXTI_InitStructure.EXTI_Trigger= EXTI_Trigger_Falling;
  6.         EXTI_Init(&EXTI_InitStructure);
复制代码
现在的设置是:将EXTI的第8个线路设置为克制模式,降落沿触发,然后开启克制,如许PA8的电平信号就能通过EXTI通向下一级NVIC了。
设置NVIC
NVIC是内核外设,以是它的库函数是被ST发配到杂项这里来了,打开misc.h文件

1、分组
2、设置一下初始化参数

界说不在本文件在stm32f10x.h文件
克制通道列表




  1. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                //只能用一组,放在模块中进行分组,要确保每个模块分组都选的同一个
  2.         NVIC_InitTypeDef   NVIC_InitStructure;
  3.         NVIC_InitStructure.NVIC_IRQChannel= EXTI15_10_IRQn   ;       
  4.         NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
  5.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
  6.         NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
  7.         NVIC_Init(&NVIC_InitStructure);
复制代码
外部克制函数设置完成
外部信号从GPIO到AFIO,再到EXTI,再到NVIC,终极通向CPU,如许才气让CPU由主步伐跳转到克制步伐实行。克制步伐应该放到那里呢?
克制函数
在STM32中,克制函数都是固定的,每个克制通道都对应一个克制函数,克制函数的名字可以参考一下启动文件,


这就是克制函数的格式,克制函数都是无参无返回值的,克制函数名字不要写错了,写错了就进不了克制了,最好是直接从启动文件复制过来,如许就不会有题目。
在克制函数里,一样平常都是先举行一个克制标志位的判定,确保是我们想要的克制源触发的这个函数,由于这个函数EXTI5和EXTI9都能进来,以是要先判定是不是我们想要的EXTI8进来的,
这个时间就必要去exti.h看一下,
  1. if(EXTI_GetITStatus(EXTI_Line8)==SET)
  2.         {
  3.                
  4.         }
复制代码
判定一下是不是返回值为SET,如果是的话,我们就可以实行克制步伐了,末了克制步伐竣事后,肯定要调用一下扫除克制标志位的函数,由于只要克制标志位置1了,步伐就会跳转到克制函数,如果你不扫除克制标志位,那它就会不绝申请克制,如许步伐就会不绝相应克制,实行克制函数,那么步伐就卡死在克制函数里了,以是我们每次克制函数竣事后,都应该扫除一下克制标志位。
  1. void  EXTI9_5_IRQHandler (void)
  2. {
  3.         if(EXTI_GetITStatus(EXTI_Line8)==SET)
  4.         {
  5.                
  6.        
  7.         EXTI_ClearITPendingBit(EXTI_Line8);
  8.         }
  9.        
  10. }
复制代码
测试能不能进入克制
将 Dialog修改为DARMSTM.DLL Parameter修改为-pSTM32F103C8(这里请根据你的芯片填写型号)
main.c
  1. #include "stm32f10x.h"                  // Device header
  2. #include "Delay.h"
  3. #include "LED.h"
  4. #include "Key.h"
  5. #include "OLED.h"
  6. #include "CountSensor.h"
  7. int main(void)
  8. {
  9.                                                        
  10.         OLED_Init();
  11.         OLED_ShowString(1,1,"Hello STM32 MCU");
  12.         OLED_ShowString(2,1,"Count:");
  13.         CounterSensor_Init();
  14.         while(1)
  15.         {
  16.        
  17.                 OLED_ShowNum(2,7,CounterSensor_Get(),5);
  18.         }
  19. }
复制代码
Key.C和H
  1. #include "stm32f10x.h"                  // Device headeruint16_t  CounterSensor_Count;void CounterSensor_Init(void)                //外部克制设置{        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);                //开启APB2的GPIOB的外设时钟        RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);                        //开启APB2的AFIO的时钟        //接着另有EXTI和NVIC两个外设:这两个外设时钟不绝都是打开着的,不必要我们再开启时钟了                GPIO_InitTypeDef   GPIO_InitStructure;        GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;                        //上拉输入默认高电平        GPIO_InitStructure.GPIO_Pin=GPIO_Pin_8;        GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;        GPIO_Init(GPIOA,&GPIO_InitStructure);                GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource8);
  2.                 EXTI_InitTypeDef  EXTI_InitStructure;
  3.         EXTI_InitStructure.EXTI_Line=EXTI_Line8 ;
  4.         EXTI_InitStructure.EXTI_LineCmd=ENABLE;
  5.         EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
  6.         EXTI_InitStructure.EXTI_Trigger= EXTI_Trigger_Falling;
  7.         EXTI_Init(&EXTI_InitStructure);
  8.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                //只能用一组,放在模块中举行分组,要确保每个模块分组都选的同一个        NVIC_InitTypeDef   NVIC_InitStructure;        NVIC_InitStructure.NVIC_IRQChannel=  EXTI9_5_IRQn     ;                NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;        NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;        NVIC_Init(&NVIC_InitStructure);        }uint16_t  CounterSensor_Get(void){        return  CounterSensor_Count;}void  EXTI9_5_IRQHandler (void){        if(EXTI_GetITStatus(EXTI_Line8)==SET)        {                        CounterSensor_Count++;        EXTI_ClearITPendingBit(EXTI_Line8);        }        }
复制代码
  1. #ifndef  __COUNT_SENSOR_H
  2. #define  __COUNT_SENSOR_H
  3. void CounterSensor_Init(void);
  4. uint16_t  CounterSensor_Get(void);
  5. #endif
复制代码
实现征象
按键触发外部克制,实现计数加1,降落沿触发,感觉按键必须得到场消抖步伐。

   按键触发外部克制计数加1

二、旋转编码器计次


旋转编码器,下面这两个A,B相的输出引脚,分别接到了STM32的PB0和PB1两个引脚。

正向旋转时,A,B相输出的是如许的波形,反向旋转时,输出是下面的波形,如果把一相的降落相沿作触发克制,在克制时间读取另一相的电平,正转就是高电平,反转就是低电平,如许就能区分旋转方向了,这不外有一点小瑕疵;
A,B相都触发克制,只有在B相降落沿和A相低电平常,才判定为正转,在A相降落沿和B相低电平常,才判定为反转,如许就能包管正转和反转都是转到位了,才实行数字加减的操纵,同时也演示一下两个克制的初始化代码。
main.c
  1. #include "stm32f10x.h"                  // Device header
  2. #include "Delay.h"
  3. #include "LED.h"
  4. #include "Key.h"
  5. #include "OLED.h"
  6. #include  "Encoder.h"
  7. int16_t Num;
  8. int main(void)
  9. {
  10.                                                        
  11.         OLED_Init();
  12.         Encoder_Init();
  13.        
  14.         OLED_ShowString(1,2,"Hello STM32 MCU");
  15.         OLED_ShowString(2,1,"Num:");
  16.         while(1)
  17.         {
  18.                 Num+=Encoder_Get();                //Get是旋转编码器产生的正负脉冲数,所以这个返回值+=Num,就能对Num进行加减操作了
  19.                 OLED_ShowSignedNum(2,5,Num,5);
  20.                
  21.         }
  22. }
复制代码
Encoder.c和h
  1. #include "stm32f10x.h"                  // Device header
  2. int16_t                Encoder_Count;
  3. void Encoder_Init(void)
  4. {
  5. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);                //开启APB2的GPIOB的外设时钟
  6.         RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);                        //开启APB2的AFIO的时钟
  7.         //接着还有EXTI和NVIC两个外设:这两个外设时钟一直都是打开着的,不需要我们再开启时钟了
  8.        
  9.         GPIO_InitTypeDef   GPIO_InitStructure;
  10.         GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;                        //上拉输入默认高电平
  11.         GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0|GPIO_Pin_1;
  12.         GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
  13.         GPIO_Init(GPIOB,&GPIO_InitStructure);
  14.        
  15.         GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource0);
  16.         GPIO_EXTILineConfig(GPIO_PortSourceGPIOA,GPIO_PinSource1);        //
  17.        
  18.         EXTI_InitTypeDef  EXTI_InitStructure;
  19.         EXTI_InitStructure.EXTI_Line=EXTI_Line0|EXTI_Line1 ;                //
  20.         EXTI_InitStructure.EXTI_LineCmd=ENABLE;
  21.         EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
  22.         EXTI_InitStructure.EXTI_Trigger= EXTI_Trigger_Falling;
  23.         EXTI_Init(&EXTI_InitStructure);
  24.         NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);                //只能用一组,放在模块中进行分组,要确保每个模块分组都选的同一个
  25.         NVIC_InitTypeDef   NVIC_InitStructure;
  26.         NVIC_InitStructure.NVIC_IRQChannel=  EXTI0_IRQn     ;       
  27.         NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
  28.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
  29.         NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
  30.         NVIC_Init(&NVIC_InitStructure);
  31.        
  32.         NVIC_InitStructure.NVIC_IRQChannel=  EXTI1_IRQn     ;       
  33.         NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
  34.         NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
  35.         NVIC_InitStructure.NVIC_IRQChannelSubPriority=2;
  36.         NVIC_Init(&NVIC_InitStructure);
  37.        
  38. }
  39. int16_t Encoder_Get(void)        //返回变化值用于外部加减一个变量
  40. {
  41.         int16_t Temp;
  42.         Temp=Encoder_Count;
  43.         Encoder_Count=0;
  44.         return Temp;
  45. }
  46. void EXTI0_IRQHandler(void)
  47. {
  48.         if(EXTI_GetITStatus(EXTI_Line0)==SET)
  49.         {
  50.                 if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_1))
  51.                 {
  52.                         Encoder_Count--;
  53.                
  54.                 }
  55.                 EXTI_ClearITPendingBit(EXTI_Line0);
  56.         }
  57. }
  58. void EXTI1_IRQHandler(void)
  59. {
  60.         if(EXTI_GetITStatus(EXTI_Line1)==SET)
  61.         {
  62.                 if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0))
  63.                 {
  64.                         Encoder_Count++;
  65.                 }
  66.                 EXTI_ClearITPendingBit(EXTI_Line1);
  67.         }
  68. }
复制代码
  1. #ifndef   __ENCODER_H
  2. #define          __ENCODER_H
  3. void Encoder_Init(void);
  4. int16_t  Encoder_Get(void);
  5. #endif
复制代码
实行征象

   旋转编码器计次

**第一个就是,在这个克制函数里。最好不要实行耗时过长的代码,:克制函数要简短快速。别刚进克制就实行一个Delay多少毫秒如许的代码, 由于克制是处置惩罚突发的变乱。如果你为了一个突发的变乱待着克制里不出来了, 那主步伐就会受到严峻的壅闭。
别的就是。最好不要在克制函数和主函数调用雷同的函数大概操纵同一个硬件, 如果你既在主步伐里调用OLED。又在克制里调用OLED, OLED就会表现错误。
在主步伐中,OLED刚表现一半,然后就进克制了,结果克制还是OLED表现函数,那OLED就挪到其他地方表现了,这时还没有题目,但当克制竣事后,必要继承原来的表现,这时就出现题目了, == 由于硬件的表现位置被挪到其他位置了==,以是再返来的时间,继承表现的内容就会跟着跑到其他地方去,这就会造成题目,固然克制有掩护现场和规复现场,但这只能包管CPU步伐能正常返回不出题目,对于外部硬件的话,并没有在进入克制时,举行现场掩护,以是克制返回后,就出现题目了。
末了不要在主函数和克制函数里,操纵大概产生辩论的硬件,在实现功能的时间,可以在克制里操纵变量大概标志位,当克制返回时,我再对这个变量举行表现和操纵。
如许既可以大概包管克制函数的简单快捷,又能包管不产生辩论的硬件操纵,这就是克制步伐筹划的注意事项。可以多用用变量大概标志位,来镌汰代码之间的耦合性,让部分代码相互独立,仅利用变量、标志位大概函数作为接口,如许能让步伐更加清楚,代码更增矫健。
题目

1、按键克制能不能消抖
总结

本节课重要是了学习了克制的代码实现,表现举行设置NVIC的整个门路买通,第一步,设置RCC,把我们这里涉及的外设的时钟都打开第二步,设置GPIO。选择我们的端口为输入模式第三步,设置AFIO,选择我们用的这一起GPIO,毗连到反面的EXTI第四步, 设置EXTI,选择边沿触发方式,比如上升沿、降落沿大概双边沿另有选择触发相应方式,可以选择克制相应和变乱相应第五步,设置NVIC,给我们这个克制选择一个符合的优先级,末了通过NVIC,外部克制信号就能进入CPU了。
然后另有设置两个克制时的操纵,也是打来有的地方也是可以|,有的必要单独设置,再写一行打开所必要的时钟。
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表