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

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

[复制链接]
STMCU小助手 发布时间:2022-6-26 18:24
    本实验使用到硬件资源如下:
(1)D1 指示灯
(2)串口 1
(3)红外遥控器和红外接收头
    D1 指示灯、串口 1 电路在前面章节都介绍过,这里就不多说,红外遥控器属于外部器件,只有红外接收头集成在开发板上,其电路如下图所示:
微信图片_20220626182411.png
& g& R5 u, U# j5 k6 D% b
    从电路图中可以看到,红外接收头的数据输出管脚连接在 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文件,同时还要包含对应的头文件路径。
    这里我们分析几个重要函数。
( L% Z$ j, v" t) _
外部中断初始化函数
    我们知道红外接收头数据输出管脚是接在 PG15 上, 所以配置 PG15 为外部中断功能,初始化代码如下:
  1. /****************************************************************  z% Z! L7 s. [, ~- J
  2. * 函 数 名 : Hwjs_Init- j$ D% H% U$ ~( d* q
  3. * 函数功能 : 红外端口初始化函数 时钟端口及外部中断初始化
    " X+ q* w2 x0 P1 b1 l0 c. o' s, Z
  4. * 输 入 : 无7 A9 P: e+ ~2 K  f+ Y: _2 c
  5. * 输 出 : 无
    , s, `/ C6 ^9 O( B
  6. *****************************************************************/
    ' ?  X2 T0 r2 p8 W6 U& ~" ?0 b/ \5 T
  7. void Hwjs_Init()$ u2 |, @3 ]: l# U" Q
  8. {
    * Z: i1 B% C* J9 E( q
  9. GPIO_InitTypeDef GPIO_InitStructure;
    $ G- W0 \* Q% N
  10. EXTI_InitTypeDef EXTI_InitStructure;
    8 S' x& \3 G9 J8 z) R
  11. NVIC_InitTypeDef NVIC_InitStructure;
    $ y6 F! t9 S* l' l3 L
  12. /* 开启 GPIO 时钟及管脚复用时钟 */, a9 e, U6 t- y8 v
  13. RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOG|RCC_APB2Periph_AFIO,ENABLE);7 \" B+ t$ J7 \$ j! b' j
  14. GPIO_InitStructure.GPIO_Pin=GPIO_Pin_15;//红外接收
    5 z3 Z# L6 i8 ^& ?9 G
  15. GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
    / l' H  S1 N% f5 K! E
  16. GPIO_Init(GPIOG,&GPIO_InitStructure);
    6 O+ w/ k4 b. h. z- c- p% d$ f8 c
  17. GPIO_EXTILineConfig(GPIO_PortSourceGPIOG, GPIO_PinSource15); //选择 GPIO 管脚用作外部中断线路2 o2 m7 X$ ~2 [0 O: S
  18. EXTI_ClearITPendingBit(EXTI_Line15);
    & M9 D& D9 [4 x* C: c$ _: m
  19. /* 设置外部中断的模式 */
    $ e+ d9 O. f7 P& S
  20. EXTI_InitStructure.EXTI_Line=EXTI_Line15;, a1 c/ m6 `& Y- s. k
  21. EXTI_InitStructure.EXTI_Mode=EXTI_Mode_Interrupt;
    9 U; Z# y0 ~7 ^# q( c+ O
  22. EXTI_InitStructure.EXTI_Trigger=EXTI_Trigger_Falling;) l9 F9 i  d3 L3 g
  23. EXTI_InitStructure.EXTI_LineCmd=ENABLE;
    ! T+ B, j" e3 _* t4 |. ]
  24. EXTI_Init(&EXTI_InitStructure);1 y. N+ n! V2 z) {$ }
  25. /* 设置 NVIC 参数 */
    $ W( K) a" U: A# L2 C. W
  26. NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn; //打开全局中断* B5 ]' k2 H* X+ H
  27. NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //抢占优先级为 07 V& k. c  d' c/ r2 \; |
  28. NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //响应优先级为1  q  m' K4 x( K) X2 [
  29. NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能
    . _$ I! ~. I" i5 ]* V- R% W. Z
  30. NVIC_Init(&NVIC_InitStructure);
    3 g" F; u( O9 `
  31. }
复制代码

! H9 @+ i5 [0 h
    在Hwjs_Init()函数中,首先使能 GPIOG 端口和 AFIO 时钟,然后将PG15 映射到外部中断线 15 上, 并配置 PG15 为上拉输入模式, 最后初始化 EXTI 及NVIC,这里需要注意,PG15 的外部中断通道是 EXTI15_10_IRQn。
0 o, v0 d3 Q3 ]( p1 r
红外解码函数7 ]3 W' S. Y- a8 e
    初始化外部中断后,中断就已经开启了,当 PG15 引脚上来了一个下降沿,就会触发一次中断,在中断内我们可以计算高电平时间,通过高电平时间判断是否进入引导码及数据 0 和1。具体代码如下:
  1. void EXTI15_10_IRQHandler(void) //红外遥控外部中断: W9 z  D% }. d# I9 f4 |, A2 v
  2. {
    # _) k" K3 M5 K8 ^( Z/ r- f0 e
  3. u8 Tim=0,Ok=0,Data,Num=0;
    . h4 @) w& s3 T
  4. while(1)
    4 W# e7 @( {, c
  5. {9 I5 g6 _, ^* p* y3 ?( |3 c# G
  6. if(GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_15)==1)& q& G; h. k6 n4 l+ l( c* m
  7. {" k' J9 x4 Q, V7 g
  8. Tim=HW_jssj();//获得此次高电平时间
    6 e  z) N" _) b7 n4 O
  9. if(Tim>=250) break;//不是有用的信号
    ; q, k& s8 i* N; T: d, S' \
  10. if(Tim>=200 && Tim<250)7 h+ k% q. A/ u3 [2 b
  11. {
    $ `# h5 ^8 @; i/ K
  12. Ok=1;//收到起始信号
    / Z0 X! S" i7 B+ M
  13. }, ?  x, Z3 t7 _& Z1 i2 ^% W. t( d
  14. else if(Tim>=60 && Tim<90)
    5 @. [" s% S' _. W# h& `' \$ a/ |' E0 D
  15. {% X% l5 [$ x: S
  16. Data=1;//收到数据 1
    1 e. C9 @+ F7 O9 _, n. O  K0 Z
  17. }
    ! ]0 K! p- x9 P2 a# M) C' \& p
  18. else if(Tim>=10 && Tim<50)+ B2 Y% Q( a4 H. \' M4 l8 J; c% e
  19. {
    & k$ ^& I& f. Q0 Z# S0 B+ N
  20. Data=0;//收到数据 0
    9 Q, T3 B) D- P% r# T% O- S
  21. }5 H8 r9 b0 p% B. ?1 G1 C8 T' p
  22. if(Ok==1)
    : `5 {. g3 ~# U) d( X, t& z9 N
  23. {
    , x; u/ f* {4 t) a, w
  24. hw_jsm<<=1;3 O+ `% U6 V, z
  25. hw_jsm+=Data;
    6 m' |9 c' T5 B
  26. if(Num>=32)
    0 I$ c2 _; i# c* i% ^3 X
  27. {
    # O" ~4 X4 l; e; H$ R: T7 ]3 `$ S
  28. hw_jsbz=1;
    3 [3 s; {! S) m4 a, X' s
  29. break;
    - k$ C) J% X9 o" k
  30. }
    6 A2 P% P8 s9 g- I! b/ g' z  x
  31. }
    : e& b! x0 V' T+ n* U' X2 o
  32. Num++;/ D, r+ s* x' g/ V& x% J9 E' W& i
  33. }' a; I. L  P8 p9 f. y
  34. }, @9 n1 T& X" ]. f% A
  35. EXTI_ClearITPendingBit(EXTI_Line15);4 P. Q( ^; u# F+ ^$ V4 S2 d" v
  36. }
复制代码

. M5 l, q/ q- s6 {
    中断函数内调用了 HW_jssj函数获取高电平时间,此函数代码如下:
  1. /****************************************************************
    - ?( b& D  r4 N4 B
  2. * 函 数 名 : HW_jssj
      z/ ~* w7 O+ y/ t
  3. * 函数功能 : 高电平持续时间,将记录的时间保存在t中返回,其中一次大约20us
    ' H' U- \; F6 D9 U- @* Q6 A7 m1 U
  4. * 输 入 : 无
    & i  t+ C  M5 Z/ X
  5. * 输 出 : t: Y: \# y( a) ?! b5 V. b& N
  6. *****************************************************************/
    # {9 _# ~' W. M( w6 n
  7. u8 HW_jssj()  w+ ^7 j8 d( X6 l; f; n
  8. {
    & c) C+ ~0 b5 Z
  9. u8 t=0;
    : r7 O1 O5 y7 J6 p
  10. while(GPIO_ReadInputDataBit(GPIOG,GPIO_Pin_15)==1)//高电平
    % N. E9 H& H" [) H4 v
  11. {
    + k& r) M& d6 C2 ~/ k  ?+ w
  12. t++;
    + ^8 T9 x+ u" }3 ?6 _
  13. delay_us(20);
    , p  ~, h; j" U# U: G
  14. if(t>=250) return t;//超时溢出
    2 h1 Z2 @: s  I3 z/ I6 ^0 @  E
  15. }3 p2 ]& D6 p6 k& r
  16. return t;7 z9 C8 |( t- L* J2 @2 Y
  17. }
复制代码

4 [- a6 d2 \; t9 ^
    通过此函数返回值就可以计算高电平时间,t 累加一次积累的时间为20us。中断函数内,将此函数返回值保存在变量 Tim 中,通过 Tim 值就可以判断是否进入引导码,如果是引导码,就可以接着判断接收的数据是1 还是 0,然后将数据进行按低位在前高位在后顺序保存在 hw_jsm 内,当接收完 32 位数据后将hw_jsbz 标志位置 1,并退出解码函数。这里要注意,判断是否为引导码、数据1 和 0时,我们不要硬性的固定在高电平区分时间上,而应该给它一个在固定值旁的范围判断,以防止因为干扰而导致误操作。引导码及数据 0 和 1 的高电平区分时间在前面介绍 NEC 码已经讲解了,不清楚的可以回过头看下。
, c9 W& e; k# U
主函数: c& _( O% p' A9 f
    编写好外部中断初始化和红外解码函数后,接下来就可以编写主函数了,代码如下:
  1. /****************************************************************
    " E  x) Q7 J- o
  2. * 函 数 名 : main% K  |+ [0 _6 r# Q/ e4 l
  3. * 函数功能 : 主函数
    6 @' L  e) E5 g  K/ b
  4. * 输 入 : 无
    ' l' p' y# v  v2 a; T0 h" U; h
  5. * 输 出 : 无
    7 f* f" d* o$ ?2 r
  6. *****************************************************************/
    & \+ S6 y0 J5 M, D, @
  7. int main()3 h, W6 U/ X) u! u
  8. {+ F% B3 C: g& v; ~* m
  9. u8 i=0;$ A0 `+ n2 W/ Z4 G7 J
  10. SysTick_Init(72);
    3 r6 Z5 p$ `5 O0 w/ J9 S2 X
  11. NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2 组
    ; p, O6 b  d* U+ r
  12. LED_Init();
    & o# f9 T6 @% Y( t
  13. USART1_Init(9600);$ K0 ^3 I4 f" I: L6 w
  14. Hwjs_Init();
    $ X% L6 ~' b: a7 F" F5 y/ H- ^
  15. while(1)
    % E+ i- j* O6 k- \5 D+ T! u
  16. {+ b/ F! \: b9 F9 E$ v+ ^7 Y
  17. if(hw_jsbz==1) //如果红外接收到
    4 X* k9 m7 ^5 I  e7 L( s
  18. {
    . O8 {$ @  G' j; E3 C' K9 G
  19. hw_jsbz=0; //清零
    , \4 n3 }' A2 C; y' {$ @! ^, R
  20. printf("红外接收码 %0.8X\r\n",hw_jsm); //打印% P, r" J3 F8 p+ R0 n' ?7 A- Y
  21. hw_jsm=0; //接收码清零6 G- N6 j$ J9 B+ D9 J
  22. }
    7 m, Z; B1 q9 B4 n- X* Y) q5 o
  23. i++;
    2 E. x1 \, b8 p+ L2 L7 }
  24. if(i%20==0)
    9 O6 J3 a& W9 |9 \
  25. {# O9 E# Y& Q% v
  26. led1=!led1;- q" C: j% `' Q: N' J, ~
  27. }
    9 F- v- N- U& ^: U# T8 Z. T
  28. delay_ms(10);# g7 ~6 y# E: S, L  q
  29. }; P$ v2 y% g; A: ~; R
  30. }
复制代码

4 S# ?. E- o! [& d; o6 {+ @
    主函数实现的功能很简单,首先调用之前编写好的硬件初始化函数,包括
SysTick系统时钟, 中断分组, LED初始化等。然后调用我们前面编写的Hwjs_Init函数初始化 PG15 外部中断功能,最后进入 while 循环,通过 hw_jsbz 标志判断是否解码成功,如果成功将解码后的数据 hw_jsm 打印输出,同时控制 D1 指示灯闪烁,提示系统正常运行。

3 k  W! |$ N- h5 }5 _
实验现象
, Q8 L6 R, F: \! n
    将工程程序编译后下载到开发板内,可以看到 D1 指示灯不断闪烁,表示程序正常运行。当按下红外遥控器键时,串口将打印输出解码后的数据(地址码+地址反码+控制码+控制反码)。如果想在串口调试助手上看到输出信息,可以打开 “串口调试助手” , 首先勾选下标号1 DTR框,然后再取消勾选。这是因为此串口助手启动时会把系统复位住,通过 DTR状态切换下即可。然后设置好波特率等参数后,串口助手上即会收到串口发送过来的信息。(串口助手上先勾选下标号 1 DTR 框,然后再取消勾选)如下图所示:
微信图片_20220626182401.png

' w! Q- y, c7 E
    我们配的遥控器所有键的地址码与反码都是相同的,不同的是控制码和控制反码,其实知道了控制码就知道了控制反码,所以如果要使用红外遥控控制其他设备,可以通过区分控制码来实现。
$ b7 c# u( v2 M# r& g/ g
赞 收藏 评论0 发布时间:2022-6-26 18:24

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版