你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32实例-红外遥控实验②-程序设计

[复制链接]
STMCU小助手 发布时间:2022-6-26 18:24
    本实验使用到硬件资源如下:
(1)D1 指示灯
(2)串口 1
(3)红外遥控器和红外接收头
    D1 指示灯、串口 1 电路在前面章节都介绍过,这里就不多说,红外遥控器属于外部器件,只有红外接收头集成在开发板上,其电路如下图所示:
微信图片_20220626182411.png

$ U6 f  I5 I& m
    从电路图中可以看到,红外接收头的数据输出管脚连接在 STM32F1 芯片的PG15管脚上,这里我们使用PG15 引脚内部上拉,所以可以省去一个上拉电阻,不会有影响,当然也可以接一个 10K 的上拉电阻。通过配置 PG15 引脚为外部中断功能,按照 NEC 协议进行解码。
    D1 指示灯用来提示系统运行状态,红外遥控器用来发射红外键值的编码信
号,通过红外接收头进行解码,并将解码后的数据通过串口 1 打印输出。
    本实例所要实现的功能是:使用外部中断功能将遥控器键值编码数据解码后通过串口打印输出,同时控制 D1指示灯闪烁,提示系统运行。程序框架如下:
(1)使能 PG15 端口及 AFIO 时钟,映射 PG15 至外部中断线上,初始化 EXTI等
(2)编写红外解码函数(在 EXTI 中断处理)
(3)编写主函数
    外部中断的配置在“外部中断实验”章节中都有介绍,不清楚的可以回过头
看下。下面我们打开“ 红外遥控实验”工程,在APP 工程组中添加 hwjs.c 文件(里面包含了红外解码驱动程序),在 StdPeriph_Driver 工程组中添加stm32f10x_exti.c 库文件。EXTI 操作的库函数都放在stm32f10x_exti.c和stm32f10x_exti.h文件中, 所以使用到EXTI就必须加入 stm32f10x_exti.c文件,同时还要包含对应的头文件路径。
    这里我们分析几个重要函数。
* Y# d% e* f9 y( \( P
外部中断初始化函数
    我们知道红外接收头数据输出管脚是接在 PG15 上, 所以配置 PG15 为外部中断功能,初始化代码如下:
  1. /****************************************************************
    ! Y. f( u) I1 o/ O# x
  2. * 函 数 名 : Hwjs_Init, p; N! z: w7 `; L0 ?6 c
  3. * 函数功能 : 红外端口初始化函数 时钟端口及外部中断初始化
    % M+ f* c) W+ I8 L
  4. * 输 入 : 无
    . a0 P" i0 ^$ {) M. [
  5. * 输 出 : 无- S, l$ N, w! i2 ~! ^
  6. *****************************************************************/
    ( K: E# D2 h( r/ x" q) L, R
  7. void Hwjs_Init()) T; p4 L; ~0 J, s  c
  8. {
    5 w5 h" R- p$ N/ c
  9. GPIO_InitTypeDef GPIO_InitStructure;& w- E' j: i8 }) e8 T7 p6 m
  10. EXTI_InitTypeDef EXTI_InitStructure;
    ( E2 P6 s* Y* x3 ^1 U
  11. NVIC_InitTypeDef NVIC_InitStructure;! e/ _7 R/ K8 [- k1 [- |# [- H
  12. /* 开启 GPIO 时钟及管脚复用时钟 */
    ( y' ]0 A7 R8 g. W. @  ?! f
  13. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG|RCC_APB2Periph_AFIO,ENABLE);
    $ A4 C4 J  `* M9 Y+ y7 a8 ?1 F$ O
  14. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15;//红外接收
      P" T. [7 V) |
  15. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;. R6 M' j' |2 I4 V7 s$ W3 G
  16. GPIO_Init(GPIOG,&GPIO_InitStructure);' A2 R# d) U; g5 W+ H) ~
  17. GPIO_EXTILineConfig(GPIO_PortSourceGPIOG, GPIO_PinSource15); //选择 GPIO 管脚用作外部中断线路
      F4 M& @; o" y; c& ~
  18. EXTI_ClearITPendingBit(EXTI_Line15);+ Y& [- V( h& p! V# E4 b& g5 G1 L7 l
  19. /* 设置外部中断的模式 */
    ! n2 L5 t, D: A
  20. EXTI_InitStructure.EXTI_Line=EXTI_Line15;
    , i! I7 E5 {0 ]9 H
  21. EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
    * o# ~6 w& Z) ], @- c9 V+ s( O) H& Z
  22. EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;
    # z( P9 m; v' e9 b
  23. EXTI_InitStructure.EXTI_LineCmd=ENABLE;6 p* p! @  q! R2 V  K% \
  24. EXTI_Init(&EXTI_InitStructure);7 Z: X: D4 ?! I) w" G
  25. /* 设置 NVIC 参数 */3 o+ ^8 \# z  y! H# s6 P
  26. NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //打开全局中断
      N6 |8 _& D* ]! i4 S/ O; h
  27. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级为 0* B% D- p% Q5 `
  28. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级为1( w' L/ j% B0 R& O% K
  29. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能/ L8 f3 l* O9 ~3 ^3 f# ?) I  S
  30. NVIC_Init(&NVIC_InitStructure);
    8 J& J, G8 y0 x
  31. }
复制代码
, K0 c1 C1 ^2 N  u3 c" A
    在Hwjs_Init()函数中,首先使能 GPIOG 端口和 AFIO 时钟,然后将PG15 映射到外部中断线 15 上, 并配置 PG15 为上拉输入模式, 最后初始化 EXTI 及NVIC,这里需要注意,PG15 的外部中断通道是 EXTI15_10_IRQn。

3 m4 _3 U1 `: h" k3 }0 o$ }" i% K! s
红外解码函数
* C# @* a  a* m, @+ K
    初始化外部中断后,中断就已经开启了,当 PG15 引脚上来了一个下降沿,就会触发一次中断,在中断内我们可以计算高电平时间,通过高电平时间判断是否进入引导码及数据 0 和1。具体代码如下:
  1. void EXTI15_10_IRQHandler(void) //红外遥控外部中断
    ) x( h" I* p  D5 d! f
  2. {
    7 r4 Z4 Y) o' M
  3. u8 Tim=0,Ok=0,Data,Num=0;
    ) q# B! D- n/ t( S) n5 ~7 B2 z
  4. while(1), b0 C5 b. a  w' J, Z! O7 w
  5. {$ I3 H  w4 l& {
  6. if(GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_15)==1)
    % m2 Y: X1 C5 ?
  7. {
    2 C! w3 M$ V( D# u# `' q
  8. Tim=HW_jssj();//获得此次高电平时间
    5 F+ w" x1 C8 |' J7 A, ]' ]  Q
  9. if(Tim>=250) break;//不是有用的信号' O- b+ R8 H' F
  10. if(Tim>=200 && Tim<250)% C, m5 K0 p/ P4 a
  11. {* j7 B% B: c% O/ e: O5 ^* Y+ P
  12. Ok=1;//收到起始信号; f- n3 N! X6 i7 Q0 h9 P
  13. }8 O* T* U: {) o" z4 G/ j: c
  14. else if(Tim>=60 && Tim<90)# Q# ?! Z' m0 o0 T( l7 e; B
  15. {
    . G# o. z- q3 q# B3 @* k
  16. Data=1;//收到数据 10 g1 E1 L! |, i# Z$ B  G8 \
  17. }& T% C6 p" s! Q. `$ g
  18. else if(Tim>=10 && Tim<50)1 i7 N" `7 \& E0 n
  19. {, i! D4 v) d- T2 m
  20. Data=0;//收到数据 0: u! M: {9 F  V
  21. }' K1 Z/ ^* n3 P4 x7 Z
  22. if(Ok==1)! l9 x) v2 G6 l2 q+ J) ?9 ]
  23. {4 y# l9 R, _  Q
  24. hw_jsm<<=1;
    7 j, i' X7 o8 O9 }& z' Y# ~
  25. hw_jsm+=Data;
    7 u% e3 `( i- j# O4 H
  26. if(Num>=32)
    $ S6 O9 m5 Q: A; E) T5 D& h
  27. {
    6 D6 O9 G7 K: A7 `  z
  28. hw_jsbz=1;
    ; D( w1 t9 o, S' v, g
  29. break;  D- x3 n, r+ i( n- d+ H& _
  30. }4 T9 P; X2 e  g% {/ `+ d; Y( M
  31. }
    + m9 e( n1 ]  s& j. `) l6 H5 Q9 y
  32. Num++;
    $ C$ p6 g2 c( b4 x, X1 F
  33. }- G1 U; R4 d& _" g5 W
  34. }
    ( P  A8 O7 i' P( U' [, i
  35. EXTI_ClearITPendingBit(EXTI_Line15);+ C3 f# V: }; E+ o6 @$ l* W
  36. }
复制代码

% s9 h$ a6 q0 _6 ~, z2 T
    中断函数内调用了 HW_jssj函数获取高电平时间,此函数代码如下:
  1. /****************************************************************
      s- T  T, t5 M
  2. * 函 数 名 : HW_jssj
    & i) x' Z. |' t0 _, m4 i
  3. * 函数功能 : 高电平持续时间,将记录的时间保存在t中返回,其中一次大约20us
    " h: R1 _7 g* G) a. @- |
  4. * 输 入 : 无" B) V# r4 Q  c- S9 y7 Y
  5. * 输 出 : t
    - J% j" p( I7 E' ?
  6. *****************************************************************/7 i0 ^+ y- c: N. {% T
  7. u8 HW_jssj()
    # W/ K0 ~, ~7 Z+ c! m% w& S
  8. {3 [. P; P( z  F1 i# S) E6 h  I9 F
  9. u8 t=0;. X6 u$ }- F' R: R. v9 }
  10. while(GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_15)==1)//高电平. {1 x& F4 V5 ]1 l4 g# Q! G
  11. {
    ; d7 v3 n( d4 @) V& w# v
  12. t++;
    9 O! ~) ^  j6 M; l- x7 d
  13. delay_us(20);1 U5 @. U% \, w: G2 }
  14. if(t>=250) return t;//超时溢出
    6 _# P' O, s" C/ o/ P2 g
  15. }
    7 q& i+ e/ a4 A. e
  16. return t;
    1 h* F3 j9 ?; `; E$ ?
  17. }
复制代码

- g% G6 ~6 O5 N
    通过此函数返回值就可以计算高电平时间,t 累加一次积累的时间为20us。中断函数内,将此函数返回值保存在变量 Tim 中,通过 Tim 值就可以判断是否进入引导码,如果是引导码,就可以接着判断接收的数据是1 还是 0,然后将数据进行按低位在前高位在后顺序保存在 hw_jsm 内,当接收完 32 位数据后将hw_jsbz 标志位置 1,并退出解码函数。这里要注意,判断是否为引导码、数据1 和 0时,我们不要硬性的固定在高电平区分时间上,而应该给它一个在固定值旁的范围判断,以防止因为干扰而导致误操作。引导码及数据 0 和 1 的高电平区分时间在前面介绍 NEC 码已经讲解了,不清楚的可以回过头看下。

; M# V( v/ t) h
主函数( B/ ?, W; M# Q* \/ Y8 O
    编写好外部中断初始化和红外解码函数后,接下来就可以编写主函数了,代码如下:
  1. /****************************************************************
    , ^1 j1 s$ T! L% e7 ]! E2 ?$ m
  2. * 函 数 名 : main- @- f1 [5 {, @
  3. * 函数功能 : 主函数1 r! P: g% z( o+ F9 \
  4. * 输 入 : 无: t1 A+ p  s  X& Z8 a
  5. * 输 出 : 无: J$ V% T" V; p& u6 r
  6. *****************************************************************/
    + X; b- T- f! J# Y9 y5 r4 w4 F: C
  7. int main()
    ) h4 r7 ^6 b/ }6 d' M& m4 P
  8. {2 P5 R3 e9 v* J: W5 _: M
  9. u8 i=0;
    $ S% m1 Y  N5 u/ Y4 O) L8 b7 Z
  10. SysTick_Init(72);5 ]/ J7 P0 [! \& i% M9 @/ K: M& ?
  11. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2 组5 n1 B* L6 x- m1 V# l
  12. LED_Init();
    ) V; y% i: l, j- Z* c) n6 p* ?
  13. USART1_Init(9600);7 O/ G9 j6 C9 a; z  v7 M
  14. Hwjs_Init();! {) {' ~. ^% n1 q9 j
  15. while(1)7 x0 r) w: V$ G
  16. {
    : z& c& E9 v6 I/ P0 ?, G
  17. if(hw_jsbz==1) //如果红外接收到
    9 u' Z8 K& W- y5 [7 s
  18. {  f+ b4 v' o- T6 b( z
  19. hw_jsbz=0; //清零& v2 i9 y4 I8 {( b0 |, V  S
  20. printf("红外接收码 %0.8X\r\n",hw_jsm); //打印
    ! _( B2 j4 b6 W* o2 W
  21. hw_jsm=0; //接收码清零9 R1 g9 ]4 T' D/ g9 Z
  22. }1 z% a# ^! p+ b9 z6 V
  23. i++;
    ! T0 D" Y# y! R# `2 T4 X: K
  24. if(i%20==0)
    ! q* n, o- E4 F- W) Y
  25. {
    : _, r: p8 g4 T/ b3 W. n
  26. led1=!led1;- S. W. g- W9 @, Y+ g
  27. }! V  Y& n; t+ l
  28. delay_ms(10);$ p% X# _# _) T" }
  29. }7 d* e2 I' c* O  e
  30. }
复制代码

9 }* L( P9 @1 _2 ~8 ?6 K" M
    主函数实现的功能很简单,首先调用之前编写好的硬件初始化函数,包括
SysTick系统时钟, 中断分组, LED初始化等。然后调用我们前面编写的Hwjs_Init函数初始化 PG15 外部中断功能,最后进入 while 循环,通过 hw_jsbz 标志判断是否解码成功,如果成功将解码后的数据 hw_jsm 打印输出,同时控制 D1 指示灯闪烁,提示系统正常运行。
. A1 i( _# A3 x6 d8 g( w* T
实验现象
8 ~- A( ^8 d: z) G# }+ I
    将工程程序编译后下载到开发板内,可以看到 D1 指示灯不断闪烁,表示程序正常运行。当按下红外遥控器键时,串口将打印输出解码后的数据(地址码+地址反码+控制码+控制反码)。如果想在串口调试助手上看到输出信息,可以打开 “串口调试助手” , 首先勾选下标号1 DTR框,然后再取消勾选。这是因为此串口助手启动时会把系统复位住,通过 DTR状态切换下即可。然后设置好波特率等参数后,串口助手上即会收到串口发送过来的信息。(串口助手上先勾选下标号 1 DTR 框,然后再取消勾选)如下图所示:
微信图片_20220626182401.png
' B9 ^5 m7 P5 H1 n3 Y5 a
    我们配的遥控器所有键的地址码与反码都是相同的,不同的是控制码和控制反码,其实知道了控制码就知道了控制反码,所以如果要使用红外遥控控制其他设备,可以通过区分控制码来实现。

* q1 W0 x. q/ A& O  w
收藏 评论0 发布时间:2022-6-26 18:24

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版