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

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

[复制链接]
STMCU小助手 发布时间:2022-3-16 10:53
红外接收头很常见,具体就不细说了,这里记录重点:
" ]4 I) F& t6 h/ r6 J$ s4 A! S$ G7 p; ^# n$ u% u  ~( x
NEC的特征
; x8 |( @% f, F0 P1:使用38 kHz 载波频率' _5 J: b+ ]$ f4 G
2:引导码间隔是9 ms + 4.5 ms
, ~0 a" m' g) E3:使用16 位客户代码9 o! t  V- D8 q4 J$ B3 N# F
4:使用8 位数据代码和8 位取反的数据代码
/ O& e! U/ Q* ^6 Z( N9 C0 Z
, F' B8 w! _1 L+ y$ Z6 F当发射器按键按下后,即有遥控码发出,所按的键不同遥控编码也不同。这种遥控码具有以下特征:采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”,其波形如图所示。
7 @2 ]2 l0 t  F8 K& W! _' l, o* x4 x+ k7 M( G

( _9 ?/ D' Z6 @/ F
7 f$ k; O4 `1 e7 f, O AM]YYXBP7@ZX4]66N2G8`DT.png
5 c& X& d" \* J
% {+ a& I5 {' A! S' i) m  {2 U, d) p! K& E: X* ~' Q- N
这里我采用同一个定时器TIM3的两个通道分别用来做红外的接收和发送,接收用通道3的输入捕获,发送用通道4的输出比较,程序采用分时复用的方式完成收发一体的实验效果。默认处于接收状态,在按键触发时切换成发射状态发送红外数据,发送完成后再次默认切换成接收。+ I8 Q6 {( S4 v8 a
; Q) y1 X+ C2 Y( O/ r
上接收部分的代码:
, k2 J/ \3 W2 c  ]5 L8 p( `9 z7 U0 Q. |6 e7 F
  1. #define NEC_HEAD                (u16)(4500) ! v' g1 Y: q6 p; A
  2. #define NEC_ZERO                 (u16)(560)$ R# A' u( n! s- w( e$ C3 A( W
  3. #define NEC_ONE                        (u16)(1680)
    . q  r$ [6 ?7 j; X* ^7 @1 K/ ]& y
  4. #define NEC_CONTINUE         (u16)(2500)
    + L  A8 Y% E; u, z& I

  5. . [% T% t3 l: N( N1 w
  6. #define NEC_HEAD_MIN (u16)(NEC_HEAD*0.8f)' _. c$ y2 Y5 [+ K/ k! j
  7. #define NEC_HEAD_MAX (u16)(NEC_HEAD*1.2f)8 ~7 g. o# q4 I
  8. 2 o& b! K# w' {6 E2 z8 q1 p
  9. #define NEC_ZERO_MIN (u16)(NEC_ZERO*0.8f)+ A) D0 \( ~& _# s
  10. #define NEC_ZERO_MAX (u16)(NEC_ZERO*1.2f)
    4 W1 X8 E( j. G; P. K9 p- u, j& e
  11. 8 l/ s- `! C( t1 U3 i/ O
  12. #define NEC_ONE_MIN (u16)(NEC_ONE*0.8f)
    7 H2 k5 u; t3 h) T/ G& e
  13. #define NEC_ONE_MAX (u16)(NEC_ONE*1.2f)2 P& T2 i' N2 I
  14. ) N# {: `- Z4 X) r" t0 y/ c
  15. #define NEC_CONTINUE_MIN (u16)(NEC_CONTINUE*0.8f)& w7 D2 l; L+ t* C6 I
  16. #define NEC_CONTINUE_MAX (u16)(NEC_CONTINUE*1.2f)
    * Y! N. w: v9 ~) M4 _' M4 s

  17. 6 [& x/ ~' E+ y5 O. y, C
  18. # }- ?, t( T& P: s6 m/ X
  19. //红外遥控接收初始化
    " c- E( I) V6 e2 f( l- ]6 X8 L
  20. void NEC_RX_Configuration(void)
    3 E( P0 l, X6 b4 @' K) s$ B# k
  21. {8 _& w" B! g5 x0 ]+ s# p
  22.     GPIO_InitTypeDef GPIO_InitStructure;4 S# P) `1 V. A; r; X7 M* F
  23.     NVIC_InitTypeDef NVIC_InitStructure;
    8 U, }" O0 ^, }; b
  24.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    + D- P4 v0 t) W( a
  25.     TIM_ICInitTypeDef  TIM_ICInitStructure;//输入捕获
    - o: d: X# @8 e. n% X
  26.         - P# v+ n& h( q. j4 }7 \
  27.     RCC_APB2PeriphClockCmd(NEC_RX_RCC|NEC_TX_RCC,ENABLE); //使能PORT时钟3 W7 x* P9 G+ `4 q& K2 A# T
  28.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);         //TIM3 时钟使能
    0 G" V0 [. c7 u
  29.         
    / e6 n+ n; M2 p% G% y$ L
  30.     GPIO_InitStructure.GPIO_Pin = NEC_RX_PIN;                         ' S0 v- e# N* ^2 S1 ~6 s- C1 t, _
  31.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;                  // 上拉输入
    % ], N2 n' t0 \) n2 v# i: h: E
  32.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;2 a8 \2 h! e, n
  33.     GPIO_Init(NEC_RX_PORT, &GPIO_InitStructure);2 A" b' q6 U6 \

  34. 3 o/ s$ k; w6 m. _! X
  35.     GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;                        
    3 N$ m0 G4 b- _, S
  36.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          //复用推挽输出
    7 M- O. F8 O4 O
  37.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;6 e- {- F( R  v3 D$ r: }
  38.     GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);
    3 Z7 F1 X, t; y/ T. `7 @
  39. ' r; l  J1 s/ S% v& O
  40.     TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出9 z! _7 v0 R1 C* D2 `) M$ |+ o
  41.     TIM_TimeBaseStructure.TIM_Prescaler =71;         //预分频器,1M的计数频率,1us加1.( V; a& }' F0 ^& g; b# S
  42.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim* n0 t6 l1 s; J0 H" ~; Y$ V4 y9 V6 w3 h
  43.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    1 q4 b" ^4 {4 G* m( Y) E
  44.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx
    7 N. {0 O7 I& v9 _' J
  45. # j& C/ j1 t& z: A  S
  46.     TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;  // 选择输入端 IC3映射到TI3上. m, |9 ~/ _$ c  n
  47.     TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;        //上升沿捕获  S) m- [# d, R0 g% w: o) `/ h2 \
  48.     TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;, @( j1 S5 f2 t, P' p
  49.     TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;         //配置输入分频,不分频& L9 O6 H" ^% b* J4 R1 A
  50.     TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波
    1 ?5 t6 r4 j" q9 A, ~6 L
  51.     TIM_ICInit(TIM3, &TIM_ICInitStructure);//初始化定时器输入捕获通道
    * o5 Z- Y/ j. t1 p  Z
  52. & |8 h. ~( L4 g7 e. j( ]
  53.     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断+ ~7 r4 `% {- u
  54.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级
    ! D9 G+ ]' Q, W6 x. @, }* m0 [
  55.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级" x% H4 J: I0 D6 ?- h4 o
  56.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    4 _+ Y/ r  P) ^4 Q7 K3 {( N
  57.     NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
    " y, M5 x" _9 {
  58. & ?$ u# C0 N# b- a/ T9 X! L
  59.     TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,ENABLE);//允许更新中断 ,允许CC3IE捕获中断1 G: `: l' O2 ]) o) k5 o
  60.         
    6 T  k& {! s5 i
  61.         TIM_ARRPreloadConfig(TIM3,ENABLE);        //重装载$ ~  y, b9 w" a4 R
  62. 1 a( b) H6 r: t/ f/ j9 [0 w
  63.     TIM_Cmd(TIM3,ENABLE );         //使能定时器38 c6 V. n$ [. J" e# X& @
  64. }
    % n' _$ s) l9 R$ ^" ]3 |

  65. 2 D6 L. R: H+ Q+ U# Z, `
  66. //遥控器接收状态' N) z( z& i/ N  R0 N' c8 `
  67. //[7]:收到了引导码标志/ f, \6 g) S, k: R5 F( i
  68. //[6]:得到了一个按键的所有信息
    / L9 L0 D% y3 ?; U
  69. //[5]:保留! |) z6 a! z: E5 o0 [
  70. //[4]:标记上升沿是否已经被捕获0 `% G% V3 J# l7 D5 t/ `( i$ F
  71. //[3:0]:溢出计时器( V: A, v5 p* Q" N- k
  72. u8         RmtSta=0;/ N- c- p- y( d- x7 H1 W: R
  73. u16 Dval;                //下降沿时计数器的值7 d4 i8 j2 n8 _2 d7 s- y
  74. u32 RmtRec=0;        //红外接收到的数据
    4 [9 ]- [5 e  a/ K
  75. u8  RmtCnt=0;        //按键按下的次数$ {2 `7 z5 V2 G8 d% Y- q, `
  76. //定时器4中断服务程序9 k9 t: D% h. {4 E; O, i2 a* U- I; W
  77. void TIM3_IRQHandler(void)
    7 J" ~+ y3 j5 `& x+ z, J
  78. {) Q5 n6 j8 U4 |9 h. E! o8 E- o* ]
  79.     if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)        //更新中断
    # O- k4 T4 F' Y, O% M7 C! I1 o
  80.     {7 F9 C7 q7 |; G
  81.         if(RmtSta&(1<<7))                                                                //上次有数据被接收到了$ o6 I8 D. D9 N' _# {  T" }& a& b
  82.         {; R5 q- S5 U$ V, F3 o5 k% }4 X
  83.             RmtSta&=~(1<<4);                                                        //取消上升沿已经被捕获标记
    1 ~/ O) O/ w( v$ Z
  84.             if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;                //标记已经完成一次按键的键值信息采集8 P0 P1 I, E8 u' \" i
  85.             if((RmtSta&0X0F)<14)RmtSta++;; l/ D8 P$ }, |5 p  f; _
  86.             else) Y& s$ I8 p* g  |9 _3 g' }" A
  87.             {
    2 S; J: Y2 I1 r. }" H% A" g/ E
  88.                 RmtSta&=~(1<<7);                                        //清空引导标识  j4 E' i) G; D
  89.                 RmtSta&=0XF0;                                                //清空计数器1 {3 P8 k. r& X
  90.             }
    $ ~( {" I3 u/ x- w1 A$ F9 W
  91.         }
      C+ F3 g/ B& y/ K6 v' A' U# w% @
  92.     }
    8 B# h+ z2 v$ A
  93.     if(TIM_GetITStatus(TIM3,TIM_IT_CC3)!=RESET)5 Y* @4 m& [% C  d
  94.     {
    9 |6 K/ a( r3 k; }) }, k! k+ }6 b: r6 {
  95.         if(NEC_READ_RX())//上升沿捕获
    / T% f3 S: M, e! L+ N
  96.         {% A& o8 r* M0 X
  97.             TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Falling);                //CC3P=1        设置为下降沿捕获
    / e% P8 S# o, P6 K4 R5 s3 G7 `
  98.             TIM_SetCounter(TIM3,0);                                        //清空定时器值* d# c! `5 y4 j" B1 m% U
  99.             RmtSta|=(1<<4);                                                        //标记上升沿已经被捕获4 @( _3 }" e9 \, p
  100.         } 7 I' v9 J& u0 d0 M* Y# H* o( Y$ ~
  101.                 else //下降沿捕获/ J) h+ j9 f+ E5 O# H8 u
  102.         {. L! c( O% f0 u2 ?/ m0 w" f4 j
  103.             Dval=TIM_GetCapture3(TIM3);                                //读取CCR3也可以清CC3IF标志位
    6 h8 b9 ?$ S+ n1 C- A2 r
  104.             TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Rising);                                //CC3P=0        设置为上升沿捕获
      n; l, `8 o- b- x/ F6 R) G
  105.             if(RmtSta&0X10)                                                        //完成一次高电平捕获
    1 K* e2 m, z% D5 @
  106.             {
    ; e2 G7 f% M' c0 ]: F
  107.                 if(RmtSta&(1<<7))//接收到了引导码. \* s4 I, W' U  [% D( K
  108.                 {# a" o  I6 c! f0 o: Z( q2 J
  109.                     if(Dval>NEC_ZERO_MIN && Dval<NEC_ZERO_MAX){                        //560为标准值,560us$ n7 J/ Y& `5 Y  ]! I& `0 ?- @: H
  110. # s4 s  z8 n1 V$ a- t; ^. ^
  111.                         RmtRec<<=1;                                        //左移一位.
    : b! u4 x% g# S4 P/ ~/ k
  112.                         RmtRec|=0;                                        //接收到05 M+ j# Q" r0 f& Q# @! Y  _
  113.                     } ( b) q  ]/ {: g' z) Z
  114.                                         else if(Dval>NEC_ONE_MIN && Dval<NEC_ONE_MAX){        //1680为标准值,1680us
    9 _0 t2 q  D% l: r
  115. 2 O$ S) k7 s) X, G# h" ?8 E1 q
  116.                         RmtRec<<=1;                                        //左移一位.  p. |8 U7 w* J1 h: @
  117.                         RmtRec|=1;                                        //接收到1. M& w- E, O8 E4 h# j
  118.                     } + D& s0 z% A0 [$ F
  119.                                         else if(Dval>NEC_CONTINUE_MIN && Dval<NEC_CONTINUE_MAX){        //得到按键键值增加的信息 2500为标准值2.5ms0 S5 R) Q5 d# H" N5 `& m; v
  120. . w1 ]# G) A7 E% q7 a. l: h
  121.                         RmtCnt++;                                         //按键次数增加1次
    ! \. ^  ^( V5 D: G. |+ S/ C- A' }7 J
  122.                         RmtSta&=0XF0;                                //清空计时器5 Y: K3 u" i# P; [$ B4 _6 D6 |! ]
  123.                     }
    5 L, ^" Q( X  Z
  124.                 } 9 F( [& u: C  g% i3 N
  125.                                 else if(Dval>NEC_HEAD_MIN&&Dval<NEC_HEAD_MAX)                //4500为标准值4.5ms
    1 h+ j3 n/ l3 u
  126.                 {3 D3 X( h( S/ H8 `; E+ F0 M$ Z
  127.                     RmtSta|=1<<7;                                        //标记成功接收到了引导码
    , Y) m- k6 \3 J: ~4 \
  128.                     RmtCnt=0;                                                //清除按键次数计数器
    $ v  m' L; R/ N& }0 g
  129.                 }7 ]# ?3 |$ I9 Y9 G, n
  130.             }# f( A9 O9 N; i0 ^; z, V4 Y1 R: D) `
  131.             RmtSta&=~(1<<4);3 Q2 `5 v' C% ^' i) f: {) @
  132.         }
    + L  F$ X' U# \1 k$ g$ j, t* n/ ~6 m$ K
  133.     }& z4 ?- b1 C/ ?" m4 p2 W
  134.     TIM_ClearITPendingBit(TIM3,TIM_IT_Update|TIM_IT_CC3);6 Z- ]3 P; @! ~  U2 ]  B8 T
  135. }
    4 j4 I: A* a! e/ z
  136. ) B& R% U# M$ q) q) o6 N% l2 w8 u3 i: G
  137. void NEC_GetValue(u16 *addr,u16 *value)
    * e7 ^# u$ g) [- G8 m5 A4 q
  138. {
    , P/ Z# j0 T; ~5 b- i% q
  139.     u8 t1,t2;
    : `# `  j9 I) S% [, s! N: O. o
  140.         *addr = 0;* w6 o6 x+ E7 K+ t" r, r
  141.         *value = 0;
    9 r0 M5 }' u' q
  142.     if(RmtSta&(1<<6)){                        //得到一个按键的所有信息了
    8 |# i5 ]+ L/ P6 b; p( V8 v+ V$ }
  143. $ B8 d2 B) L  X7 d% _
  144.         t1=RmtRec>>24;                        //得到地址码  M2 ], r; c7 L: |+ ?, ?
  145.         t2=(RmtRec>>16)&0xff;        //得到地址反码
    2 J0 ~9 j+ C6 Y
  146.         if(t1==(u8)~t2)        {                //检验遥控识别码(ID)及地址5 D* s. J( C, e0 n

  147. 0 t3 T: J' w4 k
  148.                         *addr = (t1<<8) | (t2);/ ]6 ~& d, x! j2 E/ V, q
  149.             t1=RmtRec>>8;3 X9 x4 W+ ~- h1 a# v( p
  150.             t2=RmtRec;' T1 q  e3 g9 Z; V5 T2 h& D; r
  151.             if(t1==(u8)~t2){                        
    + T* Y& E/ g0 m+ S
  152.                                 *value = (t1<<8) | (t2);
    2 j. ~  f4 q# d, S, c& W& ]
  153.                         }% n" ~* P2 [+ t+ L2 R: p
  154.                         else{/ ^- R  ?4 U) ^+ w2 V; E% l5 ~* k
  155.                                 *addr = 0;7 `: r8 D- n5 [" `- z9 f: p) e. O
  156.                                 *value=0;1 P: s1 i# ^8 G& p( o# ^8 T
  157.                         }
    7 O  N6 h9 ]: e. \7 M6 H
  158.         }
    / ]$ \; C& E( a9 X. u- @
  159.         if((*value==0)||((RmtSta&(1<<7))==0)){//按键数据错误/遥控已经没有按下了/ `: [- t3 E* ?- d
  160. 2 B5 R1 I: T7 E/ A
  161.             RmtSta&=~(1<<6);//清除接收到有效按键标识
    2 k+ B( j5 U& ^; d8 Q1 D( G
  162.             RmtCnt=0;                //清除按键次数计数器
    0 S+ s" M2 @8 p4 H* O0 [
  163.         }
    4 {5 N8 O' L; G, ?' ]! b- H0 c
  164.     }& r- A. o* b3 ?, i# H
  165. }
复制代码
5 {* [" ]/ ]1 ~) |
接收部分在配置上比较简单,配置一个1us计数拼了,周期为10000(也就是10ms)的计数器,同时开启更新中断和捕获中断,4 D5 W. Y* V* t6 d+ b
1 O7 N- ~# [, Z* }. _# j9 v
在更新中断,和捕获中断里面分别对接数据进行处理,这里要提一下就是捕获中断分上升沿捕获和下降沿捕获,在捕获到某一个电平后需要更新一下捕获的中断源。在每一次捕获的同时对时间间隔进行判断,判断的结果有引导码、数据码、重复码等,这里就不多解释了。1 }2 f% i" x8 O7 `! F

* U; l, O: u2 Q8 ~& _% D) ^8 J- n上发射部分代码:6 W- ^( p4 r: x* o) T) H# G, \+ M
) m. ]8 a7 U: K4 b8 K
  1. /****************************************************************************************/& |' ~1 ]$ ?% A% i6 V4 F
  2. #define CARRIER_38KHz() TIM_SetCompare4(TIM3,9)% v0 {& r( f) P
  3. #define NO_CARRIER()        TIM_SetCompare4(TIM3,0)
    8 J" q4 m; e& O4 I+ J
  4. 0 `3 _8 R: ^' N/ Q& _1 {1 G
  5. void NEC_TX_Configuration(void)            //红外传感器接收头引脚初始化" U' J& S/ W' ~- l) Q
  6. {1 }; l5 r0 x9 f8 k( h: g
  7.     GPIO_InitTypeDef GPIO_InitStructure;
    % Q8 V( n& d5 U3 R; U

  8. 8 V" }. p. R1 ~9 ^3 Z( D, O
  9.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;( b9 w0 ^/ V: f9 w5 C5 ^
  10.         TIM_OCInitTypeDef  TIM_OCInitStructure;                                //输出比较        
    ! d) q. a* ]  U* _- M
  11.         * @4 C4 S& y+ c' A
  12.     RCC_APB2PeriphClockCmd(NEC_TX_RCC,ENABLE);                         //使能PORT时钟& Y7 b6 s+ {. d4 u
  13.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);        //TIM3 时钟使能1 W( f  ^6 ]" [; }: p1 ~

  14. " b+ X, B: z3 @
  15.     GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;                         ' f- O4 p: r7 ^  J' ^( }
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         //复用推挽输出, V% a/ e/ h3 H
  17.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    + _) M# e! ~8 H
  18.     GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);
    4 A6 o) K1 j. E' p1 ]7 @* d+ H

  19. % Y5 A! ?' n! R2 S% Z0 y
  20.         TIM_Cmd(TIM3,DISABLE);
    , z! e' H4 p$ ]
  21.         TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,DISABLE);                        //关闭TIM3中断
    . D% }$ b. H6 T5 m0 H& n$ @
  22. + D% I1 A8 H$ V( H; `- D$ L1 `" e
  23.     TIM_TimeBaseStructure.TIM_Period = 25;                                                         //设定计数器自动重装值 最大10ms溢出
    " A& D  P3 d1 F. V* E
  24.     TIM_TimeBaseStructure.TIM_Prescaler =71;                                                 //预分频器,1M的计数频率,1us加1.
    # d( ]  W8 U5 O  T& `
  25.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;                 //设置时钟分割:TDTS = Tck_tim
    $ v6 b7 k1 ^! k% y+ @7 i/ \3 n& h
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;          //TIM向上计数模式9 q9 e* C+ V, D* G+ y
  27.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); * T$ l8 T0 l5 i5 T* l/ N+ y
  28.   s+ R. N! A0 M7 G5 C, i1 ~
  29.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                          //设置PWM1模式
    ( J3 b% z8 }+ u( `
  30.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能5 ]4 l- o% O1 s# Z. u9 Q/ r
  31.         TIM_OCInitStructure.TIM_Pulse = 0;                                                            //设置捕获比较寄存器的值8 X: \9 I% {" y4 p/ X$ s
  32.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;          //设置有效电平为高电平4 X4 v$ G$ i1 G0 {  A& F6 B/ K
  33.         TIM_OC4Init(TIM3, &TIM_OCInitStructure);                                           //生效初始化设置0 J, b2 I/ U0 {5 E
  34.         TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);                          //使能输出比较预装载1 B+ `) g6 x4 ]5 T
  35.         5 W7 t: h/ g( j  j" u3 `
  36.         TIM_Cmd(TIM3,ENABLE);9 b4 X2 w$ D7 i. |- C% C
  37. }2 `- d$ j  R2 e7 M7 z' ~; W

  38. # E. l' c& T" K1 y( R1 V5 W0 {9 \& r
  39. static void NEC_Send_Head(void){. T! Q0 K, o7 c" a  t+ g. F% Q
  40.         CARRIER_38KHz();        //比较值为1/3载波. P% ~! |  m, p5 k. @
  41.         Delay_us(9000);
    # D5 H7 n. T9 w" `' Y- A
  42.         NO_CARRIER();        //不载波4 d3 N! P7 H' i" R. t
  43.         Delay_us(4500);! g6 e8 n% P0 q, L6 y
  44. }
    6 @. t+ o: b: y. v! y
  45. ! S  I5 G6 s+ J) N
  46. static void NEC_Send_BYTE(u8 value)
    / t4 d2 ]. F' N5 m7 ~
  47. {3 X9 M' A0 K1 ]# f8 ?4 \2 {' l
  48.         u8 i;# ]; z3 t( g4 T  ^7 h8 V( d/ {* |
  49.         2 i, h( B7 }/ w6 h# z$ S8 }
  50.         for(i=0;i<8;i++)% |6 f$ x( W$ [3 B0 X3 S( t
  51.         {; o( ?  d. b  X/ V
  52.                 if( value & 0x80 ){  @, |0 @3 m* J# q% a
  53.                         CARRIER_38KHz();        //比较值为1/3载波" Y8 E+ H# R$ \9 b/ P8 o
  54.                         Delay_us(560);
    9 J" x2 e  i' Q+ D* p
  55.                         NO_CARRIER();        //不载波1 c/ [, I2 U9 j. i- j
  56.                         Delay_us(1680);
    9 }# [$ W7 x: H4 g
  57.                 }
    . s1 y6 r" w. `3 m$ b) h
  58.                 else{2 A: Q; Z8 Q9 ?
  59.                         CARRIER_38KHz();        //比较值为1/3载波4 v8 J: m8 K. L, Y, d. u5 q
  60.                         Delay_us(560);
    # x' J& m, T! @2 Z0 ^4 j5 C# J
  61.                         NO_CARRIER();        //不载波
    ' E2 T6 j& b+ E5 @
  62.                         Delay_us(560);. Z# c: w8 I& @% E/ Y0 _# H: B! {
  63.                 }
    5 `2 u/ ?: A2 W6 ~6 C( [
  64.                 value<<=1;
    # e' p+ V# u/ f; C$ Z
  65.         }
    - ]4 ?' I) n" ^  n( p, V$ [( q
  66. }
    0 E" M1 Y4 X$ L7 X1 B& C3 j
  67. 4 A$ H' V; P9 t
  68. static void NEC_Send_Repeat(u8 repeatcnt), a9 T3 w0 {2 d- Z
  69. {
    . g1 F4 U! B6 w, m+ Q- \
  70.         u8 i;9 J# ]) v5 m% a' x
  71.         * H* _$ T8 E1 h/ O4 o
  72.     if(repeatcnt==0)          //如果没有重复码就直接设置无载波,发射管进行空闲状态
    ( j$ s: y) P: W, {1 o& |3 F) |3 c
  73.     {
    2 u8 p: _; v5 v: C4 e: X
  74.         CARRIER_38KHz();; W9 I8 D/ U& d! I9 R
  75.         Delay_us(560);  i" R4 D4 b2 X3 G4 [7 H
  76.         NO_CARRIER();       \0 |) q3 }! K9 ~* K+ z
  77.     }
    & @0 m% p- g% c6 E
  78.         else - F3 G5 Z. I% s
  79.     {: B/ S' b. o2 q- `1 C. T
  80.         for(i=0;i<repeatcnt;++i)
    * a  Q. @) D. M' p1 D
  81.         {& l; f( H' y# j
  82.             CARRIER_38KHz();, ]0 a! P* D, D& ]7 U
  83.             Delay_us(560);
      @7 Y  w6 U/ f$ Q/ m! ~1 Z
  84.             NO_CARRIER();
    8 j& Z4 o0 Q/ }7 T3 e7 U8 |
  85.             Delay_ms(98);
    , y6 c! G5 a- k, x+ n) H
  86.             CARRIER_38KHz();( K' P( \/ c" M$ V% t4 b7 u0 X  _
  87.             Delay_us(9000);  1 T) e: k) [* y- _
  88.             NO_CARRIER();
    . J: o7 E! V6 q4 C# p4 Y
  89.             Delay_us(2250);                     $ u) ~( U5 p7 b+ C1 d
  90.         }
    . j5 S2 K- q% Z' d& I0 V

  91. 6 ^$ q  U- o+ c8 `
  92.                 CARRIER_38KHz();
    # k" ?( S+ v. Z" E$ _' H& I; G- V
  93.                 Delay_us(560);0 w" E& r4 F. h3 {
  94.                 NO_CARRIER();
    8 v) N* I' z/ o7 N7 E8 D$ |4 i
  95.     }: O) ]: y' i5 h0 }" `0 _
  96. }3 x5 g3 N# ~- o- c7 \+ K1 }* F

  97. % l% C" p# _& F5 R  r

  98. 3 D; l+ M' r' Z# t$ @
  99. void NEC_Send(u8 addr,u8 value,u8 cnt){/ p7 n# x2 U2 K5 j6 F0 x

  100. 1 ~- P0 J8 h9 D
  101.         NEC_TX_Configuration();
    # @2 L" m# W! ]3 l: |- t3 e
  102.         ( R* }6 Z: N5 S8 B6 l4 I& w  `7 }
  103.         NEC_Send_Head();                                //发送起始码' {5 x5 X6 v5 T6 r3 w) Z8 x
  104.         NEC_Send_BYTE(addr);                        //发送地址码H
    0 K) w" o3 B) z
  105.         NEC_Send_BYTE(~addr);                        //发送地址码L# ]/ @, p( P8 d2 E& h2 n
  106.         NEC_Send_BYTE(value);                        //发送命令码H! V( a: W+ D" k# ?% s' n! K
  107.         NEC_Send_BYTE(~value);                        //发送命令码L6 h  }" [) \) x8 f- m$ c% [5 G- w
  108.         NEC_Send_Repeat(cnt);                        //发送重复码        
    6 R7 d4 ~- \" L2 L, p

  109.   D& U0 p+ _5 p1 Z" W9 V! T
  110.         NEC_RX_Configuration();( _* ~( w0 ?$ Z. |
  111. }
复制代码

0 d* {  e) d8 u9 ]8 d, B/ a发送部分首先是重新配置定时器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)5 h6 w7 d' l3 [
  2. #define NO_CARRIER()        TIM_SetCompare4(TIM3,0)
复制代码
3 V, y7 S6 J7 S
载波的时候比较值设置的是9,没有载波的时候是0,9和26就约等于一个1/3的关系。! e% j7 v2 d9 q2 q' x
% j, n" x& i4 U" E5 F5 Y3 h" t# E
最后就是发射部分,细节不说了,按照NEC协议进行发送,依次是引导码、地址码、地址反码、数据码、数据反码、重复码等。可以看到发射数据前后加了有NEC发送和NEC接收的配置,这里就是关键的分时部分,因为程序主要出于接收,在触发器(这里使用的按键)触发的时候才会发射红外数据。
+ M3 Q4 A& a1 l/ \2 p0 F. m
" T4 s; i9 C4 y/ g6 @上述就是NEC底层部分。上层应用也贴出来以供参考5 L, R7 ^1 g$ Z1 R! f$ ?4 B' q6 L
" x5 g7 z2 M9 C9 M# i
- X. K% o/ P% I5 U6 S& x$ O. @
  1.                 keyvalue = Key_Scan();
    7 m3 d! K% K  [9 @: r0 `2 Q7 z& }
  2.                 if(keyvalue != KEY_NONE){. B0 i0 J* t: a, V, k; k4 T
  3.                         NEC_Send(taddr,tvalue,0);
      _% `$ f# `. S! @' d8 F) F6 O6 G9 v
  4.                         taddr++;# Z+ q8 D( c7 O! R
  5.                         tvalue++;
    0 j+ H4 E6 T4 u" n; n1 b, Z
  6.                 }3 _: L9 S" h3 `4 O( j5 \+ \' t
  7.         9 t) X5 C" V8 k* N& S, D& U
  8.                 NEC_GetValue(&addr,&value);
    8 B1 W3 _! Q3 X: y% s1 W" T% M

  9. 2 L, U; T4 f9 c; r) j' K
  10.                 if((value>>8)){" s# ^' S& ^$ z5 M! ~2 g4 s
  11.                         printf(" %04x,%04x \r\n",addr,value);
    $ U: g0 Z2 X1 M' B1 W( W8 f
  12.                         printf(" addr: %d,key: %d,cnt: %d\r\n",(addr>>8),(value>>8),RmtCnt);& f2 A7 Y! g2 v' j* I2 ?
  13.                         Oled_ShowNum(36,32,(addr>>8),3,8,16);+ F5 l1 K8 n: D
  14.                         Oled_ShowNum(96,32,(value>>8),3,8,16);
    % I8 g) D& l5 r" |. Q, Y5 T
  15.                         Oled_ShowNum(36,48,RmtCnt,3,8,16);
    % [/ n6 t# t+ d
  16.                         Oled_RefreshGram();! M1 I$ C& R4 b8 q2 `* v0 m
  17.                 }/ ?+ x8 M4 F- m" m' }' q* s: t

  18. 4 W$ v& ^" ~+ R
  19.                 Delay_ms(10);! @5 w+ n3 V. K. R/ Y
  20.                 if(++t==50){: l% X: r4 \5 P1 I4 I
  21.                         t=0;, r( b8 u1 K( n( d' L( l9 `
  22.                         Led_Tog();" O" m4 Z( q! q% T! w- U+ w) V, X
  23.                 }
复制代码
& Z7 p  t& P! l4 K9 N
最后要说一点的就是关于系统的延时,延时部分需要慎重,此处我也是踩了一坑,也一便记录下来。8 R4 K& y' w4 A# \

4 e% j+ [: `, w原来的底层:* x2 E7 M& n% u- F
# \0 g2 [% a( w
  1. //u32 System_ms=0;7 e/ S( h% s$ U2 K: k& X6 z
  2. ' i  b3 i- x! k% l3 k
  3. //u32 usTicks;
    ( M# Q+ ?1 Y- d( x# v' Q
  4. //void Systick_Configuration(void)
    * {8 b0 b! A; e. G9 ]) H
  5. //{
    $ j+ P2 @; s& X+ Y
  6. //        RCC_ClocksTypeDef RCC_ClocksStucture;
    4 n0 [$ K# T9 ?# Y' D9 p
  7. //        RCC_GetClocksFreq(&RCC_ClocksStucture);        
    * @+ S6 C  g$ v
  8. //        usTicks = RCC_ClocksStucture.SYSCLK_Frequency/1000000;' A2 s3 W7 e2 E( m8 n( m
  9. //        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);4 s- m5 r& T- r: X) |" e- s
  10. //        SysTick_Config(RCC_ClocksStucture.SYSCLK_Frequency/1000);
    ( j2 S/ {8 ^$ q
  11. //}1 |( v1 d1 Z' v9 ~9 T9 w7 `
  12. . n+ I' i( x7 |/ i
  13. //u32 GetSystick_us(void)7 l9 R3 ^2 R7 ~, @; K" `( D+ I
  14. //{
      N8 U/ _) ?& B2 i" E
  15. //        register u32 ms,cycle_cnt;
    1 }% w1 K" ~. ?
  16. //        do{
    ; q, s9 N0 S5 g9 h4 d" o
  17. //                ms = System_ms;
    ) q- j) p- o- \
  18. //                cycle_cnt = SysTick->VAL;4 l- N7 Z, A  Y/ X' a! V
  19. //        }while(ms != System_ms);9 S. d) A- r7 g5 C  U6 I' Y
  20. //        return (System_ms*1000) + (usTicks * 1000 - cycle_cnt) / usTicks;
    8 [1 ^5 s/ d5 F# T0 w
  21. //}, L) g8 g5 o9 C. ?9 T7 u
  22. 6 O8 c. Q) J- I& G6 K
  23. //u32 GetSystick_ms(void)
      ~! Y. g, T/ ~. V# |1 B
  24. //{
    1 Z- z" u. ]0 D2 m6 ^
  25. //        return System_ms;
    5 p" n" c# V2 I3 Z0 T: e
  26. //}& H! V. ^5 F! }" [& X0 n
  27. . j% x, U! o" P3 W. P0 L
  28. //void Delay_us(u32 nus)
    , x4 l" u+ D; o, o
  29. //{
    0 i: G1 R6 Z( q8 h, \
  30. //        u32 now = GetSystick_us();! g' [9 `% H% C$ J4 ?; a! `' @
  31. //        while(GetSystick_us() - now < nus);& G9 i" H* f* }
  32. //}; P1 Q* M  r9 k

  33. % M7 X8 X: [$ G/ y# V- a
  34. //void Delay_ms(u32 nms)! z$ T( _$ I( X9 a2 q1 {5 ~5 t
  35. //{5 `% r' B; Y6 m4 k3 x
  36. //        while(nms--)- H- S( ~, I- S6 J6 l/ C
  37. //                Delay_us(1000);! m" _. e+ Z* u5 V( ?
  38. //}1 {$ l, t9 r9 w2 U& A2 v# l) v

  39. 9 W  j+ S& e/ w0 O
  40. void SysTick_Handler(void)
    + _8 z7 Q5 v/ ]0 J
  41. {
    1 B9 s8 O( f- x1 p0 H2 X6 e
  42. //        System_ms++;
    9 v) w+ p) E' k
  43. }
复制代码
- U( k/ }/ q! c0 c
这里用到的延时us和ms函数都会调用获取时基函数,这样的话ms还好一点,到us级别就不是很准,再有就是滴答中断里面也有时基去计数,再低级的中断那也是中断,会打断前台程序的运行。
. l! J' Q' V+ A$ V  V0 r
6 @1 l/ @6 A% W' O4 W- P5 ]改过之后的:. m9 E$ ], J+ _( i- ]

( i' U1 v! U8 i2 _- V
  1. void Systick_Configuration(void)7 w6 O  ^9 _* Z0 \/ C& o$ W
  2. {0 K  N3 {' Y. x& v/ o
  3.     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //设置时钟源8分频7 o4 D3 x( p4 K6 K. l
  4.     SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;            //使能中断
    ) t, p0 b) v  g  ]# G
  5.     SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;             //开定时器5 R* `$ \2 ]2 Z8 p
  6.     SysTick->LOAD = 9;                                    //随意设置一个重装载值  m: Q7 }6 j9 _5 R5 r/ Q- S' o
  7. }
    5 A7 q- V; q! s; O+ C

  8. + z0 X3 m$ H* Z8 ^: r

  9. . J* D- p* _- w+ [6 V; F5 [7 J* N
  10. void Delay_us(u32 xus)
    ) b+ n6 x% `; q4 Q! w- ^
  11. {
      O4 d& }0 Z* X8 [1 \
  12.     SysTick->LOAD = 9 * xus; //计9次为1us,xus则重装载值要*98 g5 _/ @9 r* F& K$ X
  13.     SysTick->VAL = 0;        //计数器归零
    * s2 e4 B$ R9 z( y6 `" t
  14.     while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待计数完成
    0 {2 F& f, |$ m
  15. }( p$ Q1 o5 t! J6 p9 k) X

  16. 6 @) x; p' _( c8 ^: ?  B# f* ^
  17. 9 ^  c1 ]  [6 W- l
  18. void Delay_ms(u32 xms)
    ; l/ I" G& g; N. @( }( J' l; \$ n8 v# e
  19. {* K. l* w1 U5 ]$ v! J* b3 N
  20.     SysTick->LOAD = 9000; //计9次为1us,1000次为1ms
    " r5 o' Y$ X- h5 r2 u/ ]
  21.     SysTick->VAL = 0;     //计数器归零
    8 K1 A3 x3 Q$ {6 Q4 e4 I
  22.     while (xms--)' O! w0 c) d( q
  23.     {  N% J, D3 ^2 }2 P3 z1 L, t
  24.         while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待单次计数完成0 K7 I; K6 {: A
  25.     }  v) w/ i3 ~2 r; J
  26. }
复制代码

0 `, d, H( L* O& J3 H: c1 b这样的话中断依然打开,但是中断里是空的,其次延时函数也因为不调用获取时基直接判断寄存器而更加精确!
5 u! q* K) }2 z0 e( M& G6 e' c- P7 h7 ?* i' B
' T1 Y6 F! c* V! e+ z0 h. C# y
收藏 评论0 发布时间:2022-3-16 10:53

举报

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