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

【经验分享】基于STM32F103的NEC红外发送接收使用同一个定时器的一体设计

[复制链接]
STMCU小助手 发布时间:2022-3-16 10:53
红外接收头很常见,具体就不细说了,这里记录重点:" G* n. _4 M2 j6 D2 R

4 }# R( ~% D/ p, I) WNEC的特征5 c+ q& M! _* ?; T8 W4 h
1:使用38 kHz 载波频率/ b* e$ g, w  o3 g
2:引导码间隔是9 ms + 4.5 ms! N4 s: ^2 t0 {) X
3:使用16 位客户代码  A% v0 K4 J4 N5 F' @0 k
4:使用8 位数据代码和8 位取反的数据代码
' ^. {% a+ C# r! }8 Q1 y+ G  G
: p& x8 ^# ?7 p  Q3 p0 x当发射器按键按下后,即有遥控码发出,所按的键不同遥控编码也不同。这种遥控码具有以下特征:采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”,其波形如图所示。* i" E- t6 q7 p7 D0 t, z

4 ^2 }$ k" P" x- k0 b3 Z
% e  ?* P% p- m# Y; {: M- E
: G! N, {' m% l) W1 [/ K5 [ AM]YYXBP7@ZX4]66N2G8`DT.png # J: a9 n: E; V1 \, e

8 g; c% S  K6 g. m2 m& I
" v; _: ]( b0 W# @% R3 O这里我采用同一个定时器TIM3的两个通道分别用来做红外的接收和发送,接收用通道3的输入捕获,发送用通道4的输出比较,程序采用分时复用的方式完成收发一体的实验效果。默认处于接收状态,在按键触发时切换成发射状态发送红外数据,发送完成后再次默认切换成接收。1 B' D5 ?' c$ D$ p; F' A
8 Q* G/ p; D! E
上接收部分的代码:
* a4 E6 w% d& ]; Q7 e* Y  Z$ J1 b1 P- l+ E/ {
  1. #define NEC_HEAD                (u16)(4500)
    6 r0 _( Q/ |. M2 ~  M# I7 h
  2. #define NEC_ZERO                 (u16)(560)
    2 o$ H1 F( }) t9 ^7 Z5 Z# i
  3. #define NEC_ONE                        (u16)(1680)8 p# s( S/ K/ A) y- Y
  4. #define NEC_CONTINUE         (u16)(2500)
    4 O* s4 k# T& k  f/ [9 v4 p
  5. - C9 ^% ~# V- R6 q0 l
  6. #define NEC_HEAD_MIN (u16)(NEC_HEAD*0.8f)! h( C) w+ V+ u2 V. H% i" f- S
  7. #define NEC_HEAD_MAX (u16)(NEC_HEAD*1.2f)
    3 G6 e$ s( Q* g8 x- f, }7 L
  8. 1 h. N) K+ N0 z  }* q
  9. #define NEC_ZERO_MIN (u16)(NEC_ZERO*0.8f)7 g# }% \+ l: _2 ?. }6 l
  10. #define NEC_ZERO_MAX (u16)(NEC_ZERO*1.2f)+ ]& m1 ?2 w- m' W& R" {5 A
  11. 6 w2 t) x2 h9 n" Y% b
  12. #define NEC_ONE_MIN (u16)(NEC_ONE*0.8f)
    1 r* m7 ?  Q) q  Z4 A7 t9 R
  13. #define NEC_ONE_MAX (u16)(NEC_ONE*1.2f)
    2 s/ @% B6 N5 }1 I$ N

  14. & F6 y4 b, R! s( ?) k- M
  15. #define NEC_CONTINUE_MIN (u16)(NEC_CONTINUE*0.8f)
    , q% b) E& i$ s
  16. #define NEC_CONTINUE_MAX (u16)(NEC_CONTINUE*1.2f)
    + E0 s  U: J$ T6 ~5 G" T5 x( r

  17. 1 }& g9 y& F7 Z8 S- P" s6 W
  18. ) N: C% j0 a4 i# _$ s5 ~
  19. //红外遥控接收初始化  |  P9 j+ N8 n0 v) ~- S
  20. void NEC_RX_Configuration(void)7 J, U2 m* [9 m) s/ S) k
  21. {
    " }0 R' n" P/ W% N3 z9 K) B% U1 Z
  22.     GPIO_InitTypeDef GPIO_InitStructure;5 K* K4 N5 F7 c0 `. h: \
  23.     NVIC_InitTypeDef NVIC_InitStructure;
      s4 G/ k) h: f4 t5 I# S
  24.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    3 e# f  ^5 _; p0 t0 a
  25.     TIM_ICInitTypeDef  TIM_ICInitStructure;//输入捕获
    ( ~8 ^; c2 M1 ^9 m3 H$ f) V/ c
  26.         
    " y# `0 _# E$ \! y: c
  27.     RCC_APB2PeriphClockCmd(NEC_RX_RCC|NEC_TX_RCC,ENABLE); //使能PORT时钟
    $ ], u6 R! a8 B6 t* M
  28.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);         //TIM3 时钟使能
    1 X: Y$ Q: u: y9 K+ p
  29.         $ \) n5 ]4 P3 n( \: b+ p
  30.     GPIO_InitStructure.GPIO_Pin = NEC_RX_PIN;                         , C4 V( g7 K% P1 a- L$ J
  31.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;                  // 上拉输入8 [/ l7 K$ E+ @9 {' V# q# D$ z
  32.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;9 l7 ^6 f% B1 [; H6 Q& ~/ }
  33.     GPIO_Init(NEC_RX_PORT, &GPIO_InitStructure);9 P! _8 M, b& }+ N- ]1 `  j

  34. ) d. h* h3 ?0 O4 h" m; A
  35.     GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;                        
    1 {: H- s5 v, F) {8 s% z
  36.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          //复用推挽输出) ?+ t* R2 G1 p( D8 i
  37.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;2 _% p* J0 X3 G
  38.     GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);, g2 k' j; t  w5 Q/ j; C5 o

  39. , }7 n) h. R' D2 s, G) \
  40.     TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出% @! d$ W6 t6 R: E' J( z) W
  41.     TIM_TimeBaseStructure.TIM_Prescaler =71;         //预分频器,1M的计数频率,1us加1.0 D( h( z( ^* E1 G. }5 t
  42.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    3 S; S0 H: Z0 I* U0 m# H$ x* K7 T
  43.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式# W; G6 |+ d! g- k; A- w3 w
  44.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx6 ^& t; u2 f, o) \2 U

  45. 8 l; @8 H! F' f' N
  46.     TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;  // 选择输入端 IC3映射到TI3上$ d+ p. U  C2 m* g( f
  47.     TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;        //上升沿捕获
    ) V# Q. Z# q4 ]0 j! [
  48.     TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;  T7 N8 a3 C" K) T* H: x- Y
  49.     TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;         //配置输入分频,不分频% Q+ ?& r' z" t/ f7 V+ s
  50.     TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波
    2 L0 O! m5 L* u6 v% H! v# s
  51.     TIM_ICInit(TIM3, &TIM_ICInitStructure);//初始化定时器输入捕获通道
    6 z( E: G  S6 }$ z, I( E' O
  52. ! T. ?) ]  _8 G3 I. Z
  53.     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断6 v% F& p- f/ u8 d0 I: H
  54.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级7 V) F; l; A/ L* J) \" O
  55.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级
    $ N5 n) F7 k: l$ H7 Y# y
  56.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    1 z3 h. N4 V; l/ ~4 l, n6 ^
  57.     NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
    5 O/ H: [/ h6 p

  58. 1 x* E* p  h5 x7 }( d: L
  59.     TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,ENABLE);//允许更新中断 ,允许CC3IE捕获中断- q. @  g# ]6 f$ q2 R( _% z  o
  60.         
    ( ~6 X2 r$ T9 q  W
  61.         TIM_ARRPreloadConfig(TIM3,ENABLE);        //重装载
    1 D0 H+ c/ X" a+ f- ]
  62. ) o9 }. v8 s; |
  63.     TIM_Cmd(TIM3,ENABLE );         //使能定时器3' @* {; f* c# t/ G& l* f; h$ T
  64. }
    + z; R/ N; A) A7 _* ^6 e+ \) f$ s

  65. , o$ y1 J7 M: F: ^3 u
  66. //遥控器接收状态
    . R, Z* Y! x: A4 {$ O
  67. //[7]:收到了引导码标志7 N* I) W+ ^1 ~% u4 F
  68. //[6]:得到了一个按键的所有信息
    ' m% ]: A: d# ^8 ?& b% B- W1 n
  69. //[5]:保留
    , t# e  c6 R+ u/ i8 i1 w
  70. //[4]:标记上升沿是否已经被捕获
    ! x) g/ }- ?- F( j9 y3 U4 @
  71. //[3:0]:溢出计时器
    " ~6 r8 p& B" B3 T
  72. u8         RmtSta=0;' }; [5 W2 h; {( O$ A. B
  73. u16 Dval;                //下降沿时计数器的值5 z' N( u& N* b: m8 k2 o& B
  74. u32 RmtRec=0;        //红外接收到的数据) ^% l! l0 ~. E0 S
  75. u8  RmtCnt=0;        //按键按下的次数9 e* n+ [& w# O9 `
  76. //定时器4中断服务程序
    ) T- ~0 W9 ?- M: X( }; j7 V- S
  77. void TIM3_IRQHandler(void)
    ) g0 H( B! @2 ?% L# y
  78. {
    + g5 _2 u# Z2 ^% J" C! X- e
  79.     if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)        //更新中断
    , I( l$ |, `% b% T% {) F& l* F
  80.     {& a, Y& [/ i+ @1 D
  81.         if(RmtSta&(1<<7))                                                                //上次有数据被接收到了
    / \+ I3 u( L- S: d$ _
  82.         {7 L' X8 B" k9 R/ A; d5 A2 \/ L
  83.             RmtSta&=~(1<<4);                                                        //取消上升沿已经被捕获标记  ]! c5 }. x2 Q  e
  84.             if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;                //标记已经完成一次按键的键值信息采集
    ) F( r& H% c) H9 W, R2 |$ m8 i
  85.             if((RmtSta&0X0F)<14)RmtSta++;4 u- d" O' g% Y) P+ R
  86.             else) k# G7 F) r; e9 n  ^/ E$ N" O
  87.             {3 s$ Z! e+ A5 i" k1 {- ~
  88.                 RmtSta&=~(1<<7);                                        //清空引导标识
    * g- P# f  J/ f2 B3 s5 W' l  t
  89.                 RmtSta&=0XF0;                                                //清空计数器
    , B- ?" i( s" @) R9 k
  90.             }
    . X: E- T, H9 [& k& \5 l/ ~# e+ ^
  91.         }
    ' C3 n( ]# r3 e
  92.     }5 W; Q$ F7 ^4 X3 p5 ~9 E
  93.     if(TIM_GetITStatus(TIM3,TIM_IT_CC3)!=RESET)
    ) o! p8 X$ R. U- |/ {
  94.     {* H6 W6 y. ]7 K5 A9 p
  95.         if(NEC_READ_RX())//上升沿捕获: U' m: X! ^4 }
  96.         {- z' f. q; r/ @  v
  97.             TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Falling);                //CC3P=1        设置为下降沿捕获
      I7 q9 A) p9 i3 W0 z# x1 U, h
  98.             TIM_SetCounter(TIM3,0);                                        //清空定时器值& x( X! H" x  _9 z
  99.             RmtSta|=(1<<4);                                                        //标记上升沿已经被捕获
    ' V3 x1 a5 L9 d
  100.         }   o9 v" r2 J+ z& s$ O3 K, c
  101.                 else //下降沿捕获
    2 a; E3 p  l: G
  102.         {
    - T* ~9 q, P7 ?5 m* a$ [
  103.             Dval=TIM_GetCapture3(TIM3);                                //读取CCR3也可以清CC3IF标志位! _3 q; K' i4 K% e0 g
  104.             TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Rising);                                //CC3P=0        设置为上升沿捕获
    - x, m9 [9 s1 R; u- w) ?
  105.             if(RmtSta&0X10)                                                        //完成一次高电平捕获2 d, l" }- I6 Q& T
  106.             {
    8 a+ o% G/ T- H! ^# ]
  107.                 if(RmtSta&(1<<7))//接收到了引导码9 D, }! k4 Z2 J. _# V( B# U! P
  108.                 {+ Y- m9 o+ N9 R/ W
  109.                     if(Dval>NEC_ZERO_MIN && Dval<NEC_ZERO_MAX){                        //560为标准值,560us
    # F; D6 x) r% J& `4 z0 ]5 j/ n

  110. 2 o: s( S/ Z1 A
  111.                         RmtRec<<=1;                                        //左移一位.( _; c" J8 _* k
  112.                         RmtRec|=0;                                        //接收到0
    & `( i; C6 @6 `2 p
  113.                     }
    . ?6 b$ q' k* d+ @, n+ {& P3 W
  114.                                         else if(Dval>NEC_ONE_MIN && Dval<NEC_ONE_MAX){        //1680为标准值,1680us+ r6 L9 j( c+ t' g3 l
  115. 2 d3 t3 e9 s/ U+ l
  116.                         RmtRec<<=1;                                        //左移一位.
    ( J) A  D% Z) s- G
  117.                         RmtRec|=1;                                        //接收到1
    1 o4 C+ \7 u2 B" J, F
  118.                     } 9 b+ r6 g5 M' q* F  o
  119.                                         else if(Dval>NEC_CONTINUE_MIN && Dval<NEC_CONTINUE_MAX){        //得到按键键值增加的信息 2500为标准值2.5ms; ^4 ?9 X9 L. v0 T+ g# i
  120. 5 P5 ?( \7 F/ W8 q, W
  121.                         RmtCnt++;                                         //按键次数增加1次
    & E, t. r, Z  F  y" e0 Y
  122.                         RmtSta&=0XF0;                                //清空计时器
    : o. f/ Q" K4 H7 Z* X
  123.                     }
    9 F' K' Y4 n( d+ Z) |
  124.                 } 8 }$ ?: ]7 H; K# e
  125.                                 else if(Dval>NEC_HEAD_MIN&&Dval<NEC_HEAD_MAX)                //4500为标准值4.5ms
    $ I  M% f2 O- Y" J6 \& L. k
  126.                 {
    - B* n8 i3 c5 u* |
  127.                     RmtSta|=1<<7;                                        //标记成功接收到了引导码
    - D  G; l( [" k: e- X- h9 \
  128.                     RmtCnt=0;                                                //清除按键次数计数器, t5 n: @6 I3 G- G  q
  129.                 }0 i, R4 b! P  o; Z5 u( M4 O
  130.             }8 Q/ @$ Y5 {7 Y# r6 `
  131.             RmtSta&=~(1<<4);
    ' {% {4 ^- h, Y# i4 b1 h- k
  132.         }
    ( y% N/ m& H: z7 |/ W  O
  133.     }
    " m9 M1 D: Q/ t* I
  134.     TIM_ClearITPendingBit(TIM3,TIM_IT_Update|TIM_IT_CC3);
      k+ x6 y  q1 S8 `% c
  135. }
    3 D, M) i$ N+ c5 A1 f+ Y0 Z: C1 E" J

  136. / x0 K4 d# S) O9 F0 o3 ?' |# j
  137. void NEC_GetValue(u16 *addr,u16 *value); y/ q3 b3 c2 |" X& L4 K5 d
  138. {
    0 s+ k- k. Z, n' k1 o, r! R/ c+ m
  139.     u8 t1,t2;/ B/ k1 k  {/ L4 R. {: a) |
  140.         *addr = 0;
    2 i' I2 X: e. Q0 m) c  |
  141.         *value = 0;0 }. c$ x) c6 ^7 |) D5 {6 s
  142.     if(RmtSta&(1<<6)){                        //得到一个按键的所有信息了
    - e0 B: m/ \6 i8 z" C( M6 H

  143. 1 V% d4 o( u" n6 A1 Z8 t
  144.         t1=RmtRec>>24;                        //得到地址码0 F! x# L: X9 H
  145.         t2=(RmtRec>>16)&0xff;        //得到地址反码  g1 ^4 r& k# j8 c" n& A( n/ e
  146.         if(t1==(u8)~t2)        {                //检验遥控识别码(ID)及地址  e# r: F: l/ \$ R

  147. ) O& L  b8 u# y! ]  C9 {6 G
  148.                         *addr = (t1<<8) | (t2);
    ) k7 Q2 D+ c3 g4 D* \
  149.             t1=RmtRec>>8;
    , W8 H8 F" Q! t. E' n5 Q
  150.             t2=RmtRec;
    ; |7 k. v# C# x3 ?3 G+ [) S, t! E; R, K
  151.             if(t1==(u8)~t2){                        
    2 o8 g( o8 e0 P- \
  152.                                 *value = (t1<<8) | (t2);6 |; p3 e8 f; i  x8 d2 h
  153.                         }
    " N, Z6 c2 u4 {8 B; D
  154.                         else{
    3 m3 s' D- B' F" e" V+ D" R0 Z
  155.                                 *addr = 0;) F9 V3 N9 _5 S# n3 `
  156.                                 *value=0;7 J7 E4 }/ k$ w9 `
  157.                         }
    $ {; F0 D7 [( ~- M
  158.         }
    - S: S# O7 t5 E+ m
  159.         if((*value==0)||((RmtSta&(1<<7))==0)){//按键数据错误/遥控已经没有按下了
    4 t" @; h; }/ y0 e; K

  160. 3 n9 H' C: Y6 |- f$ z
  161.             RmtSta&=~(1<<6);//清除接收到有效按键标识" t) J& C" B0 p; e) f( b
  162.             RmtCnt=0;                //清除按键次数计数器0 ?1 W/ L* c. \5 j1 ]
  163.         }# n8 K% w  N6 q1 n2 N
  164.     }
    3 K3 k' n" k$ b9 R; b( d+ T2 s
  165. }
复制代码

1 m$ ]' a/ k" u# {' o# p1 F1 }9 v% l接收部分在配置上比较简单,配置一个1us计数拼了,周期为10000(也就是10ms)的计数器,同时开启更新中断和捕获中断,
3 d  `; I/ J& L& Z/ W6 i/ p0 Y5 X  F+ T* m2 j2 b. A+ J+ o; o
在更新中断,和捕获中断里面分别对接数据进行处理,这里要提一下就是捕获中断分上升沿捕获和下降沿捕获,在捕获到某一个电平后需要更新一下捕获的中断源。在每一次捕获的同时对时间间隔进行判断,判断的结果有引导码、数据码、重复码等,这里就不多解释了。9 e1 T; N( q0 c0 y" N1 {1 [, R
5 o. ~( s7 ^$ U6 T  I% L. n1 ^& o
上发射部分代码:
: N7 l4 {1 F" t6 L( a& J; ]! v
; ]7 _- x- Z' }% v" V& l# ^
  1. /****************************************************************************************/
    7 p6 z$ x# p/ [' X
  2. #define CARRIER_38KHz() TIM_SetCompare4(TIM3,9)
    * P8 L( [$ E) A7 S3 {5 l5 @
  3. #define NO_CARRIER()        TIM_SetCompare4(TIM3,0)! k1 @& N. x/ P' y% x: E
  4. 9 X& \8 z2 v2 \' C. I- R' u" i
  5. void NEC_TX_Configuration(void)            //红外传感器接收头引脚初始化+ e% s1 f  h5 m
  6. {
    ' f& I9 F* e; s2 K- b
  7.     GPIO_InitTypeDef GPIO_InitStructure;; h8 l8 i0 u# l: N; j3 P( g6 T

  8. - N' P% D. }1 ]$ M+ Q
  9.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;& R# i3 h0 b% h: t* f/ K
  10.         TIM_OCInitTypeDef  TIM_OCInitStructure;                                //输出比较        
    7 d6 o/ b' o: p7 H' P) h
  11.         
    . H: i4 U2 Q1 N" }, q
  12.     RCC_APB2PeriphClockCmd(NEC_TX_RCC,ENABLE);                         //使能PORT时钟/ @9 O  M, N; o: ?
  13.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);        //TIM3 时钟使能
    $ \9 O: o: f/ K. O& h" N
  14. : T5 g4 C/ ^& T
  15.     GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;                        
    0 B- F& r8 k# ?, B
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         //复用推挽输出
    9 d; R4 c* H+ {& l' N+ }
  17.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;+ M) M2 |2 `3 s: N) [2 n, n: l
  18.     GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);
    7 I& W* O2 C' K& l
  19. % N  f; k/ Q( k" G6 `( g
  20.         TIM_Cmd(TIM3,DISABLE);
    - E  K& |& x! w$ J; _: K( m7 r" @
  21.         TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,DISABLE);                        //关闭TIM3中断
    4 L* z, i/ X+ H- }, g

  22. & z! B6 o4 s0 ~
  23.     TIM_TimeBaseStructure.TIM_Period = 25;                                                         //设定计数器自动重装值 最大10ms溢出0 x- M. N# m! p4 x
  24.     TIM_TimeBaseStructure.TIM_Prescaler =71;                                                 //预分频器,1M的计数频率,1us加1.4 }/ Y0 ]' ?: s  I% K  c6 |
  25.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;                 //设置时钟分割:TDTS = Tck_tim
    ! A  a  a0 E8 o1 x" T
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;          //TIM向上计数模式
    0 H1 C8 C7 c( t" J
  27.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); ! E' j- j# Q, h8 X) s5 Q
  28. 6 Q+ X: Z+ b) {2 _  k* Y1 s. r8 D/ i# L
  29.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                          //设置PWM1模式1 n& e$ L6 u0 G1 P9 y
  30.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
    - t/ Z8 n# a/ c9 r4 `8 E7 Y
  31.         TIM_OCInitStructure.TIM_Pulse = 0;                                                            //设置捕获比较寄存器的值, g, |- `- z: E+ n3 h  T0 J; T
  32.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;          //设置有效电平为高电平7 c0 O- O" k) M3 P9 c8 @& E' i
  33.         TIM_OC4Init(TIM3, &TIM_OCInitStructure);                                           //生效初始化设置" T6 R8 N; d- @- S' p, g! n
  34.         TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);                          //使能输出比较预装载  b8 R6 h  B% |+ ?- Q! k! t& T
  35.         
    5 N0 b" C9 W7 u  G, u3 A2 Q7 A: j
  36.         TIM_Cmd(TIM3,ENABLE);
    + G* H( M) D9 t
  37. }+ U$ _1 P$ U, c: x5 P! e2 _
  38. ' V. W) E, Z7 e/ [
  39. static void NEC_Send_Head(void){) I/ d" @2 W) F: J
  40.         CARRIER_38KHz();        //比较值为1/3载波3 Z! a4 f0 s1 @; j! W
  41.         Delay_us(9000);
    . f6 L$ j6 I6 \/ }: K. M. P/ s
  42.         NO_CARRIER();        //不载波# [2 o1 J2 ?9 v* Q7 |* {
  43.         Delay_us(4500);
    0 ~, K* E/ b( A4 k# D
  44. }) Y/ w! ^& }8 O
  45. . M* Z3 A5 [' s$ M1 H0 `. Z" J
  46. static void NEC_Send_BYTE(u8 value)
    9 d& z. b4 L: c6 R3 }% I3 {  j
  47. {. Y+ o: _8 j+ M& t
  48.         u8 i;! m% h9 d1 [  H
  49.         
    / {  C: P1 B4 T0 q
  50.         for(i=0;i<8;i++)
    . ^0 X& |# a% B  Q  |
  51.         {
    4 h0 Q8 f7 t- S3 P7 X& C+ `
  52.                 if( value & 0x80 ){' j6 }: Z) b" V" s
  53.                         CARRIER_38KHz();        //比较值为1/3载波0 P6 I- z9 e; w3 O, n
  54.                         Delay_us(560);: C( o/ y5 T0 S7 G( g
  55.                         NO_CARRIER();        //不载波5 `* r" ?5 s( T5 p+ h% Z+ A
  56.                         Delay_us(1680);
    $ i+ U8 f: Z# W: n8 x# M
  57.                 }
    5 b' C8 a" h- J$ k; `. H. C: n
  58.                 else{
    7 X9 M% A* d& x. h( ~
  59.                         CARRIER_38KHz();        //比较值为1/3载波
    # V8 U# @1 w5 j/ q0 U0 j
  60.                         Delay_us(560);
    1 N9 t! e6 F3 w4 j/ C2 K
  61.                         NO_CARRIER();        //不载波
    4 R* P) z- b1 H- E
  62.                         Delay_us(560);( W9 Z) H8 a& S1 J8 n  Y, v( ~
  63.                 }* q0 v0 h. Z  P1 e0 X
  64.                 value<<=1;
    ; f8 w4 @- b' J$ j, \' k6 n
  65.         }
    2 s, e, v) ^' U' S) ]( e
  66. }' F. O8 x/ _. U  o. ]) ^% e' a

  67. - q3 \+ d4 a+ ]7 Q9 s" r+ l8 V
  68. static void NEC_Send_Repeat(u8 repeatcnt)
    - K8 Y2 a* L) M" X; _& r" l
  69. {4 P  m% i5 L5 D: C  n$ O% z
  70.         u8 i;
    % b& T: @% t3 h6 M) J
  71.         + b4 `8 D% P* w9 X7 @5 a' G) O6 h( I
  72.     if(repeatcnt==0)          //如果没有重复码就直接设置无载波,发射管进行空闲状态
    - _/ V: e4 z6 G1 w$ J) T4 P
  73.     {
    % B' v7 O; m7 G- H" m; r
  74.         CARRIER_38KHz();% G% E1 z) Q. ?9 f( b: U
  75.         Delay_us(560);% {$ g- E  Y$ ?5 @) h# p' G& K
  76.         NO_CARRIER();     & }1 c5 h$ U; _& |  N# R
  77.     }
    ! Z+ @% b, {3 B% v1 p7 h% w* A
  78.         else 4 v6 L9 g+ o, x' O7 Z2 g
  79.     {
    : i8 O: J9 E# j
  80.         for(i=0;i<repeatcnt;++i)
    : \- I" l1 x% X1 F$ [+ @
  81.         {. _  P" \1 m: T
  82.             CARRIER_38KHz();, _" G% T- i3 y% i1 ^
  83.             Delay_us(560);
    " Z3 ?2 [9 L- ?( ~- O
  84.             NO_CARRIER();4 T5 q! Z7 h! K" z
  85.             Delay_ms(98);
    , T  U8 ^0 n- r8 a" a
  86.             CARRIER_38KHz();
    1 Z1 E4 U3 g8 e6 t
  87.             Delay_us(9000);  ( p; \0 J& a% E: q8 R
  88.             NO_CARRIER();
    + ^- @% ^" [$ m. ]* Y5 R
  89.             Delay_us(2250);                     1 |' ?- S* n7 k/ G! r1 f
  90.         }/ p. z" y9 r! ]7 k+ Z) \, }

  91. & j, f& ?7 _4 g) g5 A( t
  92.                 CARRIER_38KHz();
    1 k% {: P# D# H' ^* ^) @
  93.                 Delay_us(560);. j6 }4 i- `3 e+ r1 A  t# a
  94.                 NO_CARRIER();
    0 \; X/ U" ?/ n" n
  95.     }+ N& z  g5 x1 @6 Z+ `6 y
  96. }" _. P" X# S2 A# H
  97. 6 q. Z6 w9 _, U/ H& l  J4 n% B8 f

  98. 7 {4 ~  }: [# f7 ]  K
  99. void NEC_Send(u8 addr,u8 value,u8 cnt){4 [; Q, x  e7 r0 o' v; ~7 |
  100. # D( y7 w. P) A( z
  101.         NEC_TX_Configuration();
    : h$ ~! G9 H( O" {/ W5 `* w' d$ z# V1 n
  102.         & c9 k; r% D: i6 I4 v0 V9 x& @
  103.         NEC_Send_Head();                                //发送起始码
    . n! {) _# v2 z  g& F% u
  104.         NEC_Send_BYTE(addr);                        //发送地址码H
    ! I! L6 p0 N$ x# b5 c" |
  105.         NEC_Send_BYTE(~addr);                        //发送地址码L
    6 M8 n4 ^$ P$ b" W6 e( `% @% [. Z
  106.         NEC_Send_BYTE(value);                        //发送命令码H  y3 c; {1 u8 s' a
  107.         NEC_Send_BYTE(~value);                        //发送命令码L, w! K7 c! W: T9 B; t6 Q
  108.         NEC_Send_Repeat(cnt);                        //发送重复码        
    6 q4 l" q# W- Q& t: @& }5 `# q

  109. - k0 J8 D2 `/ s; x, W
  110.         NEC_RX_Configuration();
    " I. c5 Z+ M6 M7 A
  111. }
复制代码

, e) A4 u( E( s! }/ @5 I  k发送部分首先是重新配置定时器3,在配置里面计数频率依然是1us,也就是1MHz,计数周期修改成了25,也就是26个周期(0也算一个),这样算下来的话1MHz / 26 ≈ 38.4615KHz,这个跟红外接收差不多能匹配,当然红外接收头这里用的是38K的,所以配置成38K的,市面上也有不是38K的,比如36K,40K等。然后是载波,载波这里用的是差不多1/3的载波,从哪里可以体现呢?可以看到有这样一个宏定义:
  1. #define CARRIER_38KHz() TIM_SetCompare4(TIM3,9)& p% z+ p$ X6 K) D9 s
  2. #define NO_CARRIER()        TIM_SetCompare4(TIM3,0)
复制代码

# `1 _4 Y4 G$ ?# {- r1 Z载波的时候比较值设置的是9,没有载波的时候是0,9和26就约等于一个1/3的关系。+ b4 w& m& N# w" G. Q5 T3 q* Z

6 G9 ~7 N" t, v4 o9 q最后就是发射部分,细节不说了,按照NEC协议进行发送,依次是引导码、地址码、地址反码、数据码、数据反码、重复码等。可以看到发射数据前后加了有NEC发送和NEC接收的配置,这里就是关键的分时部分,因为程序主要出于接收,在触发器(这里使用的按键)触发的时候才会发射红外数据。
" p0 B2 F, j9 h0 x; e$ i; ~" a& w5 _
上述就是NEC底层部分。上层应用也贴出来以供参考
" B5 v0 ~8 X) ~* ~" |/ J7 N' E) \# e' K7 }# v! [4 M
' }$ p# J) }8 R7 e# ~; a
  1.                 keyvalue = Key_Scan();8 _7 _0 d4 _* B
  2.                 if(keyvalue != KEY_NONE){: V5 i0 v+ d. w1 }& p5 d
  3.                         NEC_Send(taddr,tvalue,0);; O& l: D) K! K7 I5 ?  h
  4.                         taddr++;
    : p* k9 Q( w; C5 T+ n- O
  5.                         tvalue++;
    . W1 z9 W1 g# _: r$ n
  6.                 }
    4 f' F9 @4 X: ]) P+ H, F" X
  7.         
    ; ?8 g9 A$ Z" r  M! Y; `. k
  8.                 NEC_GetValue(&addr,&value);2 B5 a2 t2 ]- T0 X( l' W

  9. , I% S& U- U+ [" K: ~1 b5 @2 w: j
  10.                 if((value>>8)){5 J: q; d/ o8 F7 ?! [
  11.                         printf(" %04x,%04x \r\n",addr,value);" o( x- j/ C6 N/ e; _
  12.                         printf(" addr: %d,key: %d,cnt: %d\r\n",(addr>>8),(value>>8),RmtCnt);  p! |0 H- \7 u" r0 l# W  ~
  13.                         Oled_ShowNum(36,32,(addr>>8),3,8,16);
    ( ]6 T) y+ a8 L+ V8 z
  14.                         Oled_ShowNum(96,32,(value>>8),3,8,16);
    - g- w' ?" f: o( g. P5 ~
  15.                         Oled_ShowNum(36,48,RmtCnt,3,8,16);
    & V$ N8 O2 e7 ]0 M3 W
  16.                         Oled_RefreshGram();
    " M  _& s2 h$ a" J" k  t! I" P! ^/ U
  17.                 }
    0 o5 u3 n5 ?; e2 ?
  18. ( n; k/ m- W. {  w8 _
  19.                 Delay_ms(10);- y& j  z- c) T- H
  20.                 if(++t==50){' {& j( u2 p0 d9 ~
  21.                         t=0;7 P( b; z& Y  j0 ]7 O0 a; B
  22.                         Led_Tog();
    ) F7 V" {* J3 }7 _4 J  ]
  23.                 }
复制代码

! w- t6 r! F9 {" @- n  X' F最后要说一点的就是关于系统的延时,延时部分需要慎重,此处我也是踩了一坑,也一便记录下来。2 _, X6 \- z" Z8 @7 c

* _& b+ z! k+ I& }$ }原来的底层:/ P8 n9 I7 j8 J& W7 q; p' h
* I6 _9 `/ T0 e7 L" C$ t. f
  1. //u32 System_ms=0;7 w% q% ~$ H1 y+ M* z

  2. : ^7 k) U) q+ E' o8 C* _* h
  3. //u32 usTicks;- z8 C8 v: ?# _* E
  4. //void Systick_Configuration(void)* V1 h, ^0 N. t) w
  5. //{
    1 G* o6 ^$ R; |( o+ o9 p/ ]. ^
  6. //        RCC_ClocksTypeDef RCC_ClocksStucture;
    / Y; e0 b, ?5 `
  7. //        RCC_GetClocksFreq(&RCC_ClocksStucture);        
    ) R2 i) }+ l- A  Z
  8. //        usTicks = RCC_ClocksStucture.SYSCLK_Frequency/1000000;
    ' O5 V3 @8 d+ h6 P
  9. //        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);1 m1 u, n+ R* [3 i6 f/ l
  10. //        SysTick_Config(RCC_ClocksStucture.SYSCLK_Frequency/1000);1 c, c/ M) X% \/ q, L9 s6 u
  11. //}
    ! I3 W7 U* U' D, T/ F; v5 y

  12. 4 N, s; E4 P+ }
  13. //u32 GetSystick_us(void)
      m' [. i! x% A1 R
  14. //{
    0 a, E% O! j0 z+ B& B
  15. //        register u32 ms,cycle_cnt;
    4 y! h+ Q# a# {- k2 ]  a8 z
  16. //        do{8 F; N- o, P) A& ]
  17. //                ms = System_ms;
    & k' z% ?0 a6 {6 p/ q
  18. //                cycle_cnt = SysTick->VAL;
    % y9 ?1 H! i6 ]
  19. //        }while(ms != System_ms);
    # y& ~, G1 U3 F" t* J( n% g# R
  20. //        return (System_ms*1000) + (usTicks * 1000 - cycle_cnt) / usTicks;
    + q& ]' |8 U0 I8 F" d: A" L
  21. //}7 f; r; {; {2 f; e/ e; L

  22. $ k9 Y2 O7 y( X- p7 l) Y3 r0 F7 `: _
  23. //u32 GetSystick_ms(void)) a6 L2 G: @+ z$ L, L8 j
  24. //{
    ! B+ q) N* G8 d- f/ e
  25. //        return System_ms;
    ' i5 E; T, t1 q2 f) C
  26. //}
    0 X& R  @* H8 H0 ~9 b
  27. $ o+ q. g1 S5 ~) k# J
  28. //void Delay_us(u32 nus)
    & D% h3 @6 r8 s" p  F
  29. //{" a3 a6 E, \8 ?8 Y# U
  30. //        u32 now = GetSystick_us();
    & l9 L% k1 |( S+ ?) v" O" m8 H& Y. d
  31. //        while(GetSystick_us() - now < nus);
    ! x8 i6 c6 @' V
  32. //}1 e% }! `5 R7 U$ d4 H
  33. : o8 a! j& |$ V& m  T6 }) s
  34. //void Delay_ms(u32 nms)8 G) v% _7 K) X; E' S1 ^7 W
  35. //{
    6 y9 O# y( [- W9 s7 Z
  36. //        while(nms--); n9 Z5 m# ?8 ?' i& l8 h: k/ Q
  37. //                Delay_us(1000);6 Y: ~. _% f, B- W
  38. //}$ {- c1 ?/ r3 f$ S& Z8 k; L9 v: X

  39. . m' C& o: g% k) G; k
  40. void SysTick_Handler(void)
    9 e$ @; r8 ~/ S: L+ D) @0 Q
  41. {
    , c' f3 U1 P) A  ]9 K* i
  42. //        System_ms++;
    : w+ o7 C+ v. ~$ C- `& D
  43. }
复制代码

( ^  j9 }- t+ v9 U& |, S这里用到的延时us和ms函数都会调用获取时基函数,这样的话ms还好一点,到us级别就不是很准,再有就是滴答中断里面也有时基去计数,再低级的中断那也是中断,会打断前台程序的运行。2 P! G8 ^9 C9 o$ Z

0 t. p) i3 q& B改过之后的:
& u9 y( A; V; X: a: j+ s" y/ \) ]# f+ \8 p
  1. void Systick_Configuration(void)  }/ G& M" Q1 T* t% }! A
  2. {
    5 z" K) X5 ^6 y+ ]1 @% c: f
  3.     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //设置时钟源8分频* L. x! h7 O1 }) |
  4.     SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;            //使能中断' w) p% A6 w+ o" j8 C+ M/ R
  5.     SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;             //开定时器. [7 _+ D# T6 i7 L
  6.     SysTick->LOAD = 9;                                    //随意设置一个重装载值$ J+ y# j6 {: W, Q5 h( d7 t
  7. }
    3 Q4 k9 a$ u" d  @7 R
  8. 5 m+ W8 V& I1 U  n$ `
  9. $ I( _5 T$ d% B9 f
  10. void Delay_us(u32 xus)( L# Q' `+ w/ u3 p# w! c7 R
  11. {4 ~8 ?+ J2 y! m6 ?& p
  12.     SysTick->LOAD = 9 * xus; //计9次为1us,xus则重装载值要*9
      {8 T7 D* _" P& I8 c9 O7 L
  13.     SysTick->VAL = 0;        //计数器归零/ O/ h9 S, H! m
  14.     while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待计数完成
    ) F+ v  `3 s+ [; H* m  _) l/ a
  15. }1 g' e2 e. A- B8 k9 [( F
  16. & N" L' w8 l+ N- Z" q5 G8 K

  17. ' U8 T3 K1 J7 t* Z) k' V8 D
  18. void Delay_ms(u32 xms)
    3 `. M4 b! ?! h+ M: _+ K  V5 H
  19. {
    , S+ Y2 Q7 {- J" A' W/ N  K) e! z
  20.     SysTick->LOAD = 9000; //计9次为1us,1000次为1ms. ]# D  M$ W  R7 D: u
  21.     SysTick->VAL = 0;     //计数器归零% d2 E( }8 z/ K, S. Y+ ~" X+ g
  22.     while (xms--)
    ; ~+ E+ h1 ~- `5 ]$ Y* y
  23.     {$ l! G! l) ~* v- J8 H  G
  24.         while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待单次计数完成: g3 T9 {" e" T& }) }
  25.     }
    & b2 @0 ~& f: K
  26. }
复制代码
! f5 v; L" E- N2 @
这样的话中断依然打开,但是中断里是空的,其次延时函数也因为不调用获取时基直接判断寄存器而更加精确!
& H# j# ]. L( `- T" e2 x) {( e
1 n; o7 A( {# M/ Z. H. {' A/ G8 A
1 ~. f' N- N; W3 X  @+ d
赞 收藏 评论0 发布时间:2022-3-16 10:53

举报

0个回答

所属标签

相似技术帖

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