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

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

[复制链接]
STMCU小助手 发布时间:2022-3-16 10:53
红外接收头很常见,具体就不细说了,这里记录重点:  K7 e8 ?3 i! L) I; r

7 i4 P0 h0 l: WNEC的特征
( s* q; ]( I7 _3 o# ?1:使用38 kHz 载波频率
5 l) T/ k$ c  n8 k) i3 W0 B2:引导码间隔是9 ms + 4.5 ms% ]) r/ p/ H* u3 N
3:使用16 位客户代码% E8 F0 v3 C0 l5 ?6 x- `1 ?: b+ T
4:使用8 位数据代码和8 位取反的数据代码
7 p% ]8 `, B4 O; }( y7 }
7 p+ T+ K9 V6 h2 t6 {# F' g当发射器按键按下后,即有遥控码发出,所按的键不同遥控编码也不同。这种遥控码具有以下特征:采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”,其波形如图所示。
4 N% ^9 t1 a  N. H7 u+ V3 W6 v5 G# d- w  a" t  t
4 s4 a2 O" T& k7 _" z- w

) T, `4 N! M; @ AM]YYXBP7@ZX4]66N2G8`DT.png
, T& T3 \) a7 f/ @8 ~- r. D" Y3 C% n1 B% A( P2 K- F' q
9 z/ n, F5 P( `7 p; A
这里我采用同一个定时器TIM3的两个通道分别用来做红外的接收和发送,接收用通道3的输入捕获,发送用通道4的输出比较,程序采用分时复用的方式完成收发一体的实验效果。默认处于接收状态,在按键触发时切换成发射状态发送红外数据,发送完成后再次默认切换成接收。8 I+ k4 Y) I0 Q% S( T# _8 J/ ~0 v

, ]0 c/ r" m: g0 h! b) B1 X上接收部分的代码:
* a/ e& i- i8 }# g
; d& ~! S9 t5 ~' V( R+ q7 m
  1. #define NEC_HEAD                (u16)(4500)
    + y( ]( |  m+ n# r( K8 {/ e( ~4 ?# b, N
  2. #define NEC_ZERO                 (u16)(560)
    / B3 T- j0 |5 Q  A! b. ~! A
  3. #define NEC_ONE                        (u16)(1680): r" {  K: Z/ \& \/ w
  4. #define NEC_CONTINUE         (u16)(2500)! Y8 ]0 U; C4 q3 G7 h' M

  5. ! B0 s! K4 F! H. W1 e
  6. #define NEC_HEAD_MIN (u16)(NEC_HEAD*0.8f)- }& G, y$ @6 [7 w) w$ v6 I/ J
  7. #define NEC_HEAD_MAX (u16)(NEC_HEAD*1.2f)1 N2 C0 k9 ~' A6 l2 a, n) |
  8. : X$ x! a* Q# t' Z
  9. #define NEC_ZERO_MIN (u16)(NEC_ZERO*0.8f)
    5 b0 e9 i- r/ M+ J* f
  10. #define NEC_ZERO_MAX (u16)(NEC_ZERO*1.2f)+ S/ G- V, w2 P
  11.   @! B/ y( Q) p1 _
  12. #define NEC_ONE_MIN (u16)(NEC_ONE*0.8f)8 U4 p4 ~1 X: x- v% E
  13. #define NEC_ONE_MAX (u16)(NEC_ONE*1.2f). l/ ~9 \0 |6 p" Q0 y! \
  14. 9 e/ w( p8 e$ i! c( C
  15. #define NEC_CONTINUE_MIN (u16)(NEC_CONTINUE*0.8f)
    9 J# H. N. ^$ [0 Z1 a
  16. #define NEC_CONTINUE_MAX (u16)(NEC_CONTINUE*1.2f)  h# O  @' y7 P3 z; v

  17. * g1 e7 i' ]4 V% G5 q# E
  18. % B& P  f% f) R
  19. //红外遥控接收初始化0 p0 r7 [) ~  {* d; H6 q$ C/ V
  20. void NEC_RX_Configuration(void)9 X, R, ~5 s2 J1 C9 V
  21. {
    + a/ p( @! Z$ n# ]
  22.     GPIO_InitTypeDef GPIO_InitStructure;' P0 y6 f. _' `7 E
  23.     NVIC_InitTypeDef NVIC_InitStructure;9 b4 x6 l9 I  p3 V
  24.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    & l# ?6 h9 ?# @5 _1 E: F7 }5 C3 U
  25.     TIM_ICInitTypeDef  TIM_ICInitStructure;//输入捕获8 q' L4 o+ k* B  A8 ^& a5 y# r$ U
  26.         
    1 S. B: t, _* T# c  e5 u
  27.     RCC_APB2PeriphClockCmd(NEC_RX_RCC|NEC_TX_RCC,ENABLE); //使能PORT时钟
    5 @- \4 \% q% G7 M! K# x
  28.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);         //TIM3 时钟使能
    " L: i, Q0 Y8 ^7 T' G0 z0 G
  29.         
    . o+ r9 R/ o2 k, s: ~7 u
  30.     GPIO_InitStructure.GPIO_Pin = NEC_RX_PIN;                        
    ! I5 l5 G% J4 k8 H! J( i
  31.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;                  // 上拉输入
    7 _7 X& O) s8 I
  32.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;$ F8 ~! d! W# k) U9 _# R
  33.     GPIO_Init(NEC_RX_PORT, &GPIO_InitStructure);7 L4 W2 s; \  i# R  V: O8 _

  34.   ?8 ~2 c6 ?7 r* X$ }- O  @( L
  35.     GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;                        
    5 C+ Z0 F& j  f% {" f0 a& g
  36.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          //复用推挽输出8 m8 w: Z3 K9 ]  D) |6 o# E
  37.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;2 Y, j$ `. f- T' [" M/ Q' w  W' v
  38.     GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);
      ?" X( @  x4 A" C1 s$ h2 x4 L

  39. & p; g& N$ ^9 C2 c/ n; m8 J9 `& K6 W
  40.     TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出
    2 y2 T( Y( M2 A: ^- d
  41.     TIM_TimeBaseStructure.TIM_Prescaler =71;         //预分频器,1M的计数频率,1us加1.
    9 ^7 A/ K) h( F
  42.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    ' P' V/ D: \/ `  U9 x9 v
  43.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    & E3 c2 Q5 I( C2 L) N: f: ~# c6 j
  44.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx
    2 W' K  c0 Z7 w. n+ f" i; o+ U
  45. ( X7 m1 K' x, Q- F) \0 `! V" t+ |
  46.     TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;  // 选择输入端 IC3映射到TI3上
    ' f" W- J$ e: j+ s
  47.     TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;        //上升沿捕获
    * @9 f9 Y# \0 ^- k7 r7 V. Y, @
  48.     TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;5 c) U" n' M8 g( \; s1 p4 |# b' Z
  49.     TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;         //配置输入分频,不分频
    ) d: M5 t8 `) `9 s4 }* B( [' m
  50.     TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波
    + c/ F* Y! f% A  g: Z5 y
  51.     TIM_ICInit(TIM3, &TIM_ICInitStructure);//初始化定时器输入捕获通道
    9 c7 a# r4 t6 F5 r1 {
  52. % Z) |5 G8 p" H8 o' U% I
  53.     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断4 P& x1 Q2 Z6 t9 ~+ K
  54.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级
    5 s2 x5 l8 X. i& ]4 d
  55.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级. U# ]8 k( ~2 Q8 P
  56.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    - E; w! l9 d, b: @4 Z3 s3 P
  57.     NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器5 y3 K  ?3 {# w( S0 h$ l- A* m

  58. 3 F0 j: i( L5 }! c2 Z% |
  59.     TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,ENABLE);//允许更新中断 ,允许CC3IE捕获中断1 ?$ C: X1 |- y( r3 t9 b, f" H
  60.           S' u, ?! a- Z3 m' V2 c
  61.         TIM_ARRPreloadConfig(TIM3,ENABLE);        //重装载
    ! w- T! g% p% m* J( J' R+ v, b' q

  62. 8 i4 [% e) U1 h2 g* r# j9 J
  63.     TIM_Cmd(TIM3,ENABLE );         //使能定时器3
    0 c( u2 U/ r$ P) w# _0 y
  64. }  }6 O% e" j* A+ x4 |8 @

  65. ) D, o+ W! o4 d2 h
  66. //遥控器接收状态
    3 O2 _3 d. b& q* _/ L2 a
  67. //[7]:收到了引导码标志
    6 l1 [! }' a; I- h$ p2 p
  68. //[6]:得到了一个按键的所有信息0 i' Z% `7 r3 X4 m2 k
  69. //[5]:保留6 z' d' r7 g3 ]; p' m+ u
  70. //[4]:标记上升沿是否已经被捕获
      p$ v  M2 R% m
  71. //[3:0]:溢出计时器2 w0 \$ v7 w! v
  72. u8         RmtSta=0;5 F) e- X' d" Z: s+ g9 `) k4 j% ?) Q
  73. u16 Dval;                //下降沿时计数器的值2 k' T1 k0 V2 \$ j: [( e+ d
  74. u32 RmtRec=0;        //红外接收到的数据# n: r1 Q, r8 f! }5 m3 z. w
  75. u8  RmtCnt=0;        //按键按下的次数- ?, a5 c* r; h+ N8 k- R
  76. //定时器4中断服务程序
    ' G# W  }. G9 h: T7 Y
  77. void TIM3_IRQHandler(void)0 S/ y/ K/ Q6 i5 f  p7 G
  78. {9 k& `  r" Y% E' U
  79.     if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)        //更新中断
    ) ]8 s( n" h: j& R" X% u
  80.     {* J: g9 J- J* H1 f
  81.         if(RmtSta&(1<<7))                                                                //上次有数据被接收到了( r  j# ?6 p' o5 V8 R" a' V
  82.         {0 R: m# p5 m9 h# F: ^+ \$ J
  83.             RmtSta&=~(1<<4);                                                        //取消上升沿已经被捕获标记
    3 w8 G# ?6 R4 E3 \
  84.             if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;                //标记已经完成一次按键的键值信息采集0 h' X% J7 _+ x5 a' @# `/ Z
  85.             if((RmtSta&0X0F)<14)RmtSta++;
    " w% C0 W3 ^4 _$ O/ ]; _& k
  86.             else) C2 A( {% t* h$ v
  87.             {' c: E0 ?5 [1 S1 S% y+ d$ s
  88.                 RmtSta&=~(1<<7);                                        //清空引导标识
    0 Y8 M0 D3 ]4 x! g) W  N  T0 h
  89.                 RmtSta&=0XF0;                                                //清空计数器0 }/ t9 B$ _, L2 |
  90.             }
    . r7 p. ]8 Q; d- z. B8 g' \
  91.         }0 Y) Y; X) Q5 H/ r2 U$ h
  92.     }2 S6 K3 y! [- Z. a
  93.     if(TIM_GetITStatus(TIM3,TIM_IT_CC3)!=RESET)- z( M( v. J2 b. K% `
  94.     {
    & \( _' {/ Q3 Y. O, ]
  95.         if(NEC_READ_RX())//上升沿捕获
    : w. h8 i* ], O
  96.         {% y- h6 o) w! @1 o4 g/ {# {
  97.             TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Falling);                //CC3P=1        设置为下降沿捕获
    4 g3 n5 q/ c2 T6 S
  98.             TIM_SetCounter(TIM3,0);                                        //清空定时器值* \$ e! a2 d) o  n' T4 N
  99.             RmtSta|=(1<<4);                                                        //标记上升沿已经被捕获4 \- a% V. |- s6 A; `; y
  100.         }
    + ^. ^& w) ]3 g" {& G  f. U
  101.                 else //下降沿捕获
    " q' K) J" m! i, [8 y
  102.         {
    3 }6 ?" Y7 w2 F. u
  103.             Dval=TIM_GetCapture3(TIM3);                                //读取CCR3也可以清CC3IF标志位: a; @- S& g% j3 p7 P$ \
  104.             TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Rising);                                //CC3P=0        设置为上升沿捕获# a7 S! b' x0 Y& h5 a2 a/ g
  105.             if(RmtSta&0X10)                                                        //完成一次高电平捕获
    ; D* ^9 f0 `% h0 Y
  106.             {
    4 m7 @" g& {* w5 J$ ]4 H
  107.                 if(RmtSta&(1<<7))//接收到了引导码' i# {8 A" h; I, Y6 A8 M
  108.                 {
    3 p# L, |6 u( @: D$ [9 M
  109.                     if(Dval>NEC_ZERO_MIN && Dval<NEC_ZERO_MAX){                        //560为标准值,560us
    . M4 x2 N" J) ]- ^* d0 C9 F3 E
  110. 1 W! N; q, ~1 K; D1 `
  111.                         RmtRec<<=1;                                        //左移一位.* m- F; i- R5 h. v7 B4 n: K0 o
  112.                         RmtRec|=0;                                        //接收到07 |; w; Z! j! S
  113.                     }
    ) e* n0 I" s, i3 k% |; c
  114.                                         else if(Dval>NEC_ONE_MIN && Dval<NEC_ONE_MAX){        //1680为标准值,1680us
    9 w' n. `0 X7 S/ I
  115. - @; C8 ]# r, g+ b7 ~' j
  116.                         RmtRec<<=1;                                        //左移一位.
    + u: x, N4 g) v9 ~
  117.                         RmtRec|=1;                                        //接收到1
    , c) R& ^1 g, [% Z
  118.                     } . U2 L0 h' X& \9 E, w
  119.                                         else if(Dval>NEC_CONTINUE_MIN && Dval<NEC_CONTINUE_MAX){        //得到按键键值增加的信息 2500为标准值2.5ms% y6 C6 p& b# m9 p9 j1 I

  120. 7 W1 O* }6 y/ W9 y2 P' j: R: s
  121.                         RmtCnt++;                                         //按键次数增加1次: ~* C" I4 V5 G9 |+ I/ ^% v: g
  122.                         RmtSta&=0XF0;                                //清空计时器0 X# p  T) ^/ \& T. ^7 R
  123.                     }
    - {3 {# N6 j! O
  124.                 } % B+ g/ Q% P8 U) G: s8 [, \
  125.                                 else if(Dval>NEC_HEAD_MIN&&Dval<NEC_HEAD_MAX)                //4500为标准值4.5ms" d3 X9 @3 |6 A! g2 ^
  126.                 {0 \5 C, m0 s/ H: X$ h3 M
  127.                     RmtSta|=1<<7;                                        //标记成功接收到了引导码% r% W. b% e& a4 ~
  128.                     RmtCnt=0;                                                //清除按键次数计数器4 G: a6 Y1 W: d
  129.                 }* c$ y7 ], o/ n
  130.             }
    . o2 B/ o* A9 A9 j, l5 D& j7 |
  131.             RmtSta&=~(1<<4);
    7 H- F+ K7 N' U% V; R0 x+ v
  132.         }
      W1 A" C5 ^# B3 l( T2 E
  133.     }
    ! Y( V, e/ c+ @% T$ r( G0 d0 ?
  134.     TIM_ClearITPendingBit(TIM3,TIM_IT_Update|TIM_IT_CC3);
    & e- r" n  T$ g  _
  135. }
    7 j7 C9 {* w% u3 M" k3 N* o

  136. . Z9 N' n# p; y5 p8 ?. J& F
  137. void NEC_GetValue(u16 *addr,u16 *value)
    3 C! V' K1 x- z. H) c6 f
  138. {
    . y2 h+ O: R8 d: y' \
  139.     u8 t1,t2;6 A  U  Z8 I+ a, O- c% w
  140.         *addr = 0;3 ]/ R3 F8 {) c
  141.         *value = 0;
    - C4 O! ]& P& l6 ^
  142.     if(RmtSta&(1<<6)){                        //得到一个按键的所有信息了
    & y& `% a. u/ a8 p! k; |2 c, |# M

  143. ! t+ l* v, Y( P# {* Q# U
  144.         t1=RmtRec>>24;                        //得到地址码
    7 t- X7 t4 F4 @. h
  145.         t2=(RmtRec>>16)&0xff;        //得到地址反码: ]$ p" Y- H% o  Y* C/ s
  146.         if(t1==(u8)~t2)        {                //检验遥控识别码(ID)及地址
    $ |/ O$ m5 ]# ?: J. e7 m1 I) B3 n

  147. , U1 `7 }3 H* D) y
  148.                         *addr = (t1<<8) | (t2);
      [5 V; N: _8 k& o+ j$ I9 Q
  149.             t1=RmtRec>>8;3 b- n3 `0 D) {* p+ x
  150.             t2=RmtRec;
    % ^) v4 q$ \/ }& j  r& D" y
  151.             if(t1==(u8)~t2){                        ! [7 Y- E/ V0 o; C; Q
  152.                                 *value = (t1<<8) | (t2);# L' ^$ Z$ r2 t$ a/ q3 h: P
  153.                         }
    2 z4 b9 Q+ g$ H2 r; x% z9 @5 ?
  154.                         else{, `, _. b3 y- s+ L8 k, Q
  155.                                 *addr = 0;
    . z: b9 d' q: D& C2 S# {+ t
  156.                                 *value=0;" T) w) c4 P$ `0 D/ q* ?4 X9 D! g
  157.                         }
    : f/ y$ _' B2 ^2 v, L9 ?9 p: {( ]+ T! G3 m
  158.         }/ E- O0 h- C- V; I9 g
  159.         if((*value==0)||((RmtSta&(1<<7))==0)){//按键数据错误/遥控已经没有按下了, t3 _% @' \9 @, S
  160. , o- S$ ]+ r( y6 K* s
  161.             RmtSta&=~(1<<6);//清除接收到有效按键标识
    3 `, ]. f  t; Z7 \
  162.             RmtCnt=0;                //清除按键次数计数器
    2 s3 a+ R# Y3 v3 B' k, V% r
  163.         }
    4 {. Y0 v, [! `2 U" N9 |6 @
  164.     }& @9 p9 A% w* A8 H9 M  G
  165. }
复制代码

: c( h' \- z, d  ]' j$ T接收部分在配置上比较简单,配置一个1us计数拼了,周期为10000(也就是10ms)的计数器,同时开启更新中断和捕获中断,
$ M& e' e1 Z: Q3 r
& T, O5 |" M1 S8 |4 d9 z2 m% a在更新中断,和捕获中断里面分别对接数据进行处理,这里要提一下就是捕获中断分上升沿捕获和下降沿捕获,在捕获到某一个电平后需要更新一下捕获的中断源。在每一次捕获的同时对时间间隔进行判断,判断的结果有引导码、数据码、重复码等,这里就不多解释了。
$ b" W$ i' S7 U3 I
' a  |. r  n' l3 B上发射部分代码:& C. z. H: T+ f8 z

4 L: \' h/ p! W4 S0 p/ n
  1. /****************************************************************************************/
    8 H) A& C9 u5 A4 h3 o7 i
  2. #define CARRIER_38KHz() TIM_SetCompare4(TIM3,9)2 r* n: q+ [8 o$ C5 J/ }9 D. d( Q
  3. #define NO_CARRIER()        TIM_SetCompare4(TIM3,0)4 [% g$ _3 }1 r+ I
  4. / K6 f) i4 ]. t1 o& v. g6 X
  5. void NEC_TX_Configuration(void)            //红外传感器接收头引脚初始化
    ! c0 ^. G; j: t* y- V% r: X
  6. {3 f- n! m: C! }, F  [
  7.     GPIO_InitTypeDef GPIO_InitStructure;0 K- l) b: m  U

  8. - A8 v7 }3 p2 i4 b" Q: |9 N
  9.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    9 }3 ~/ J% B  Z* i6 j% }: K
  10.         TIM_OCInitTypeDef  TIM_OCInitStructure;                                //输出比较        " ]& K+ V5 _# H% W5 }
  11.         6 T5 [' x. c& {1 h7 v
  12.     RCC_APB2PeriphClockCmd(NEC_TX_RCC,ENABLE);                         //使能PORT时钟
    4 Z5 i. X' E$ J7 ]
  13.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);        //TIM3 时钟使能
    # `& G; Y/ F; q9 _. _
  14. 2 U; Y4 \( s2 s3 Z' L2 E4 G
  15.     GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;                        
    : H- L# K8 ^% g/ T
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         //复用推挽输出
    7 H; r% b9 k8 E7 |9 [8 ?
  17.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;' f7 H' ^8 `. N) E' T8 R
  18.     GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);
    9 \: M4 A! }/ ?8 }8 z: C2 d

  19. * R5 V+ D2 r* T4 E8 ~0 y! b. C
  20.         TIM_Cmd(TIM3,DISABLE);
    - v: D" H: `" v2 F7 |
  21.         TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,DISABLE);                        //关闭TIM3中断# O* P0 Z, Y' K. b, y4 B9 P0 r

  22. 0 f. z5 g4 l1 E4 e) k$ ^( m8 U
  23.     TIM_TimeBaseStructure.TIM_Period = 25;                                                         //设定计数器自动重装值 最大10ms溢出
    * _/ Z) b- y  A; o# _; `- d
  24.     TIM_TimeBaseStructure.TIM_Prescaler =71;                                                 //预分频器,1M的计数频率,1us加1.
    5 \, _# C  z# J  K0 m
  25.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;                 //设置时钟分割:TDTS = Tck_tim
    5 m" {( l+ E2 l$ L5 ^5 O
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;          //TIM向上计数模式
    $ x: T4 k$ \3 Y+ Z2 Q
  27.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
      u0 s6 w3 a2 U' _6 J& W
  28. - f- s- w. y/ S1 _' q5 o+ @# K1 B  j
  29.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                          //设置PWM1模式
    ! s8 N& z3 Y0 }0 S
  30.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
    ! Q/ K' A1 @( L; G! G6 e
  31.         TIM_OCInitStructure.TIM_Pulse = 0;                                                            //设置捕获比较寄存器的值
    4 ~) }* z: @; s6 v1 L0 x+ [& v
  32.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;          //设置有效电平为高电平' F3 I, P" t) R" c; o; p
  33.         TIM_OC4Init(TIM3, &TIM_OCInitStructure);                                           //生效初始化设置
    - C: `* \2 p4 s" D4 p9 M1 F
  34.         TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);                          //使能输出比较预装载
    # s, U) N8 X  u0 _" q, [2 x
  35.         
    5 |9 i0 x# k$ E: ]  b; b# y
  36.         TIM_Cmd(TIM3,ENABLE);& X2 I# y4 u$ F+ u. ?% K% ^4 C
  37. }
    # r; L% Q: W, [' W
  38. : d( l! g2 X2 C9 A
  39. static void NEC_Send_Head(void){2 U2 p0 W7 b; N  V9 u
  40.         CARRIER_38KHz();        //比较值为1/3载波
    # s3 m; R5 p+ V" |* D/ o% A
  41.         Delay_us(9000);5 {2 c8 f0 X8 J& W4 D
  42.         NO_CARRIER();        //不载波. t) J2 m) i  u$ J
  43.         Delay_us(4500);4 u: U8 f3 _; J! Q
  44. }
    6 I& C0 r8 p, d4 E& H1 J& H
  45. % k6 k: z! `# B
  46. static void NEC_Send_BYTE(u8 value)
    1 s  |5 G5 f0 z
  47. {
    ' f) D  v! q# `6 A8 @7 t
  48.         u8 i;" B: Q! p" N9 ?% e6 j& h& i5 f
  49.         7 H9 }+ d7 P* j7 f
  50.         for(i=0;i<8;i++)3 h# C8 r5 j. p2 Q$ z9 O% d
  51.         {
    " f, ^* l! T( h3 W, o8 d
  52.                 if( value & 0x80 ){
    . r0 l0 c% i* N# f; U  }
  53.                         CARRIER_38KHz();        //比较值为1/3载波3 L6 D( B7 V: J* P2 a4 n$ J
  54.                         Delay_us(560);
    2 f% _: R1 A8 b# f1 P4 z  E$ p4 x  S9 {
  55.                         NO_CARRIER();        //不载波
    5 t9 `$ s1 h5 H
  56.                         Delay_us(1680);) O% E6 I1 ^+ l4 g- _
  57.                 }
    4 q1 t5 X; ]; d' B5 ~+ T
  58.                 else{
    # ~& D/ v! {/ u, R4 @  H
  59.                         CARRIER_38KHz();        //比较值为1/3载波3 ?+ h4 M  U0 c1 l3 L
  60.                         Delay_us(560);8 D1 E5 y. f) ?+ N! K) Y
  61.                         NO_CARRIER();        //不载波6 G" K; b; P8 k1 C/ V# M' {+ d
  62.                         Delay_us(560);% ]. G$ d0 o+ {3 F. \3 D
  63.                 }- [. k- y5 x" j+ ?
  64.                 value<<=1;
    1 y3 G, Z% G2 b+ L: C
  65.         }9 ]4 f, z/ w4 R; Q
  66. }( Q6 ?& Q5 m8 y

  67. & R  s3 B, h  Q0 S, q' ~
  68. static void NEC_Send_Repeat(u8 repeatcnt)
    ) V2 S* K" L. S4 a5 `
  69. {7 h3 W9 E+ r1 l6 ~& w1 ^2 P
  70.         u8 i;( T$ B" I& P. U
  71.         + W8 |) N- a8 X3 o! P- A
  72.     if(repeatcnt==0)          //如果没有重复码就直接设置无载波,发射管进行空闲状态
    0 [* @+ r1 I1 B3 h3 [
  73.     {+ @0 W% ], C0 }% A
  74.         CARRIER_38KHz();
    $ P# N8 k1 D; L: i' x7 h$ U9 E% _  |* s
  75.         Delay_us(560);9 G: [' ?. k6 p! [3 p& P* E
  76.         NO_CARRIER();     
    ! q4 L' R. W+ n: M3 N' ^- m$ h$ f0 T
  77.     }
    / B$ d: B: V9 J2 p- T
  78.         else
    0 [) T% Y+ ?' B; c8 v5 C& x
  79.     {
    ; F' S' f+ s% p% T2 M2 E
  80.         for(i=0;i<repeatcnt;++i)
    , U, P5 k$ p( C& }/ i/ Z& S
  81.         {
    " V5 U7 A1 G: H/ o1 c; g
  82.             CARRIER_38KHz();* ?3 w* r, ^% G: C, ]
  83.             Delay_us(560);
    $ R1 q: ?+ T+ ^
  84.             NO_CARRIER();1 S, `  S; N* @
  85.             Delay_ms(98);$ o. d2 [8 v2 I) S
  86.             CARRIER_38KHz();6 u% t  q8 f6 ~9 h
  87.             Delay_us(9000);  
    " C4 z2 `$ Y7 [9 P* T6 }
  88.             NO_CARRIER();2 F+ i3 ^+ g! L1 ^( a: W7 ?" Q, }
  89.             Delay_us(2250);                     
    * F& w2 H9 W, Q# K4 z
  90.         }- H. n/ P$ \5 x& y
  91. 1 }6 X7 M3 f6 t( z/ v# z& o3 ^
  92.                 CARRIER_38KHz();
    6 i9 `+ `2 x( V" j
  93.                 Delay_us(560);
    , y, C. E  C& ?# @& X) m" Q
  94.                 NO_CARRIER();
    ' L' K9 Z2 B$ e- q' U
  95.     }
    6 G3 I0 n4 u6 `* w  g& h8 T' W
  96. }# `+ B" \; n4 L' y5 ^0 ^
  97. 1 M% _3 A# t1 K4 n& h0 A

  98. : z* Z4 f- G* x6 ?; X/ w: |- @7 {, n( @
  99. void NEC_Send(u8 addr,u8 value,u8 cnt){  b$ c$ i$ ]2 b' x8 u/ k3 D6 u
  100. : I. P2 t2 x5 z' q6 ]
  101.         NEC_TX_Configuration();" }' S5 L! v* S  H4 s
  102.         
    " K/ C! Y& X1 E) Q
  103.         NEC_Send_Head();                                //发送起始码: a5 _6 K# k* y+ e
  104.         NEC_Send_BYTE(addr);                        //发送地址码H
    8 K+ _8 X) b0 F/ E; I7 ]
  105.         NEC_Send_BYTE(~addr);                        //发送地址码L
    3 \3 i  _) [; y: Y. e& T( v
  106.         NEC_Send_BYTE(value);                        //发送命令码H( y0 D+ _( P$ O$ T. _3 O  n+ X) b
  107.         NEC_Send_BYTE(~value);                        //发送命令码L' C  g6 b) J4 A: @1 Q. i! m
  108.         NEC_Send_Repeat(cnt);                        //发送重复码        - R( [/ }4 O0 @$ _+ D9 \* X
  109. 9 b" T9 T9 P# j
  110.         NEC_RX_Configuration();/ H) T8 u7 a7 `) {% }) H# Q
  111. }
复制代码

! o2 V8 {7 K4 e; G发送部分首先是重新配置定时器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): }) `& A8 ?6 @$ x& Z3 P
  2. #define NO_CARRIER()        TIM_SetCompare4(TIM3,0)
复制代码

1 U8 o: z, r0 E# N' F载波的时候比较值设置的是9,没有载波的时候是0,9和26就约等于一个1/3的关系。
6 d7 D  [/ I  c
& R* P+ o/ M: V) s6 A( ]. `/ s最后就是发射部分,细节不说了,按照NEC协议进行发送,依次是引导码、地址码、地址反码、数据码、数据反码、重复码等。可以看到发射数据前后加了有NEC发送和NEC接收的配置,这里就是关键的分时部分,因为程序主要出于接收,在触发器(这里使用的按键)触发的时候才会发射红外数据。% T8 C% j; P; P- D0 h  Q0 {

. p* d* ]: H+ G# }. i3 |& O0 a上述就是NEC底层部分。上层应用也贴出来以供参考9 \% h! w0 w. H" C; O
; A. A3 f! i0 z6 Q) Y" m: M
% H- D: y' u; e* }  U9 |! ~
  1.                 keyvalue = Key_Scan();' {4 D3 m! b* |6 [
  2.                 if(keyvalue != KEY_NONE){
      h, U7 Z( v8 n* D
  3.                         NEC_Send(taddr,tvalue,0);& g! F" _' ]  b4 w
  4.                         taddr++;; w/ z6 c" K- M+ {( {4 K9 ?
  5.                         tvalue++;4 k5 _' @) s6 e  E
  6.                 }
    2 d2 \" N& F( q7 M" d) x0 M& p
  7.         & Y9 B6 _  _$ @; R- s
  8.                 NEC_GetValue(&addr,&value);
    7 k7 K3 J) m, _' _1 r

  9. . n. `* o" \* i$ D+ h  V
  10.                 if((value>>8)){
    * D  ?' }/ x% a4 ]5 S9 @
  11.                         printf(" %04x,%04x \r\n",addr,value);# y: q4 L1 x: e$ F7 Q
  12.                         printf(" addr: %d,key: %d,cnt: %d\r\n",(addr>>8),(value>>8),RmtCnt);* K6 ~: u: Q1 l- a
  13.                         Oled_ShowNum(36,32,(addr>>8),3,8,16);5 W0 Q  j, w9 R0 \: ^
  14.                         Oled_ShowNum(96,32,(value>>8),3,8,16);- J4 U1 f3 y$ Y/ H
  15.                         Oled_ShowNum(36,48,RmtCnt,3,8,16);( ^, N2 J1 {+ L( S' u6 R
  16.                         Oled_RefreshGram();
    1 Q( t% v" b5 g
  17.                 }
    4 c% g2 r" ^  u! y# e1 ^- D

  18. 9 g: }( v/ v# r
  19.                 Delay_ms(10);
    . y$ \' h0 ?) I
  20.                 if(++t==50){! m0 |7 ]# ^2 D8 R
  21.                         t=0;
    + u" a, r' {- L6 G( Y
  22.                         Led_Tog();" ^  Y) c0 z: y3 Z
  23.                 }
复制代码

# S: ~( A" l8 s  ~6 e3 C0 F最后要说一点的就是关于系统的延时,延时部分需要慎重,此处我也是踩了一坑,也一便记录下来。
0 d- L/ k7 I+ d; t
6 I' m5 x" c7 f* `原来的底层:
& X- g! g& {, S- C* `; `/ n
+ I4 Y' E# H: ]. L, T
  1. //u32 System_ms=0;
    ! a% F* ~' C0 s
  2. / s& ~! {0 D, ?9 {
  3. //u32 usTicks;! Z8 N4 b4 v5 w6 x
  4. //void Systick_Configuration(void)
    2 `) {/ b& f( |
  5. //{
    # }: w0 S5 z" r/ D
  6. //        RCC_ClocksTypeDef RCC_ClocksStucture;* v% y9 B6 m- E5 w" N
  7. //        RCC_GetClocksFreq(&RCC_ClocksStucture);        
    2 ^- d$ o8 I% B1 {
  8. //        usTicks = RCC_ClocksStucture.SYSCLK_Frequency/1000000;
    . b/ Y- X* S' r0 P0 W2 X
  9. //        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);. j- M+ l& [9 i3 Q; p! a' e: j
  10. //        SysTick_Config(RCC_ClocksStucture.SYSCLK_Frequency/1000);
    " m+ Y$ l& Z/ z4 X$ X. U/ T
  11. //}7 M3 N* g  P. c' R0 r
  12. 0 \, W$ Q4 W" K$ F  T  {4 S5 F
  13. //u32 GetSystick_us(void)% D9 K$ k8 E7 s/ J6 x$ L& g
  14. //{& u9 O' E2 [3 ^, g# P
  15. //        register u32 ms,cycle_cnt;* y" z/ D! U  I3 U% D! b
  16. //        do{4 D& [6 ^3 _$ r7 H1 M+ h
  17. //                ms = System_ms;
    & O8 S2 f9 E" [" k. h' H3 G
  18. //                cycle_cnt = SysTick->VAL;# |4 T+ i" {" A5 K
  19. //        }while(ms != System_ms);) o: y6 n& ^+ g: z
  20. //        return (System_ms*1000) + (usTicks * 1000 - cycle_cnt) / usTicks;0 n: n- e, a, f( P3 b6 @' w
  21. //}
    ) D  n8 O3 [6 u4 @& x* ~4 J
  22. 6 O$ O1 w3 F" `' x+ A
  23. //u32 GetSystick_ms(void)+ I. @: |& ~, T. t# A. p
  24. //{
    3 i# }; C  l4 {7 R
  25. //        return System_ms;* Z( B! W# u' W  a1 t  S
  26. //}
    - I" P3 h6 W9 t  Q, X
  27. , X; q4 Y: z7 s+ Q0 m
  28. //void Delay_us(u32 nus)
    * _* d6 f. @7 n) \; T6 j; O" s
  29. //{/ E, ]1 I/ a4 u9 c2 j! }9 N: G
  30. //        u32 now = GetSystick_us();- g( G! g# ^! O3 s, A% K2 f6 F
  31. //        while(GetSystick_us() - now < nus);
    ! y" h; P& Y3 q9 S' w! k2 q
  32. //}
    - k2 c) c+ z$ W7 y/ Z

  33. 5 F4 e8 I; B& O1 Q2 d
  34. //void Delay_ms(u32 nms)
    1 v# {" M7 {4 X: f# I! G
  35. //{
    2 ~5 |/ A' g" J+ m
  36. //        while(nms--)
    ( X, L' C7 h: _$ I( L# j6 A, q* ^! n
  37. //                Delay_us(1000);' g" c' P" h. M* R( q" F
  38. //}
    3 K/ M$ R6 ^$ u! p

  39. ) N' O$ G1 j9 k
  40. void SysTick_Handler(void)2 F9 I' F6 g' J$ |) T+ K$ |
  41. {1 L/ }) E/ Q$ }
  42. //        System_ms++;
    0 B) ^3 _' Y: j3 O0 J; [1 [9 L
  43. }
复制代码
# q# z9 s- ^  ]$ t3 P( Z
这里用到的延时us和ms函数都会调用获取时基函数,这样的话ms还好一点,到us级别就不是很准,再有就是滴答中断里面也有时基去计数,再低级的中断那也是中断,会打断前台程序的运行。- F. B0 g) b8 b: k

) n" r6 e2 P: [! V* N改过之后的:
# f  ~# v. o4 S) E+ b) F1 b9 C3 e2 Q& X8 t/ X
  1. void Systick_Configuration(void)( w2 v* D6 h( m. \0 d+ D+ N- _- g
  2. {/ M; ^8 s$ x) E2 J  v
  3.     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //设置时钟源8分频+ S6 ]; {2 B+ J; s) A
  4.     SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;            //使能中断. t% h0 Q) \3 M6 G( U8 ^+ _* _
  5.     SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;             //开定时器6 ?4 c, x$ v' B, L4 h3 G3 K$ w; D
  6.     SysTick->LOAD = 9;                                    //随意设置一个重装载值7 I5 \& m& J9 c* y
  7. }
    % }) e6 O( V6 f

  8. + D8 S; t4 E* L; B( X  a; C' n  ^
  9. . A: `' ~3 M2 i, e. b8 s/ x' B
  10. void Delay_us(u32 xus)+ A. f' e. l3 a/ n: u- Q+ t
  11. {# u" \, e' R$ v$ |1 k* z
  12.     SysTick->LOAD = 9 * xus; //计9次为1us,xus则重装载值要*9/ |7 t# Z" P, z
  13.     SysTick->VAL = 0;        //计数器归零
    0 P( D, A/ C" r+ p) i! f
  14.     while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待计数完成
    $ e# @0 J( n2 @) I; K& `
  15. }: \0 B) ^2 L$ y0 x0 F
  16. # m5 w! W7 _% w8 r: P$ u9 I1 s
  17. * I" T' A3 g& Q( I2 _; I
  18. void Delay_ms(u32 xms)0 e  q3 f4 H- {) E/ R& b- m
  19. {
      n4 k$ Z3 ~1 I; a7 u; x* C  F
  20.     SysTick->LOAD = 9000; //计9次为1us,1000次为1ms/ {  t/ K* Q0 s/ t. e0 U
  21.     SysTick->VAL = 0;     //计数器归零
    0 V$ s4 ]: y# H8 x& p( U/ B
  22.     while (xms--)
    ! A1 k9 @+ n- A: n5 m
  23.     {* u; J! m* L! G# q" c
  24.         while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待单次计数完成
    / f- M. ?) o& T# V/ E$ D
  25.     }
    4 Z1 @3 J. b1 H5 w. x) ~: c
  26. }
复制代码

) a0 K- J# V' O( ?; n这样的话中断依然打开,但是中断里是空的,其次延时函数也因为不调用获取时基直接判断寄存器而更加精确!
2 ~  {( ?) P# i# V$ m% E; v. i
3 b7 i+ ~$ g1 D+ Q" ]# h( {% m' A
4 B& b" D( ]# ]# \5 G
收藏 评论0 发布时间:2022-3-16 10:53

举报

0个回答
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版