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

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

[复制链接]
STMCU小助手 发布时间:2022-3-16 10:53
红外接收头很常见,具体就不细说了,这里记录重点:
2 j/ I2 `; b# H. Y& }: B4 z* E+ M5 Z% I5 _/ h! A+ r$ v5 \5 [- {
NEC的特征
- f' d, a; J8 `# [8 _1:使用38 kHz 载波频率
% s0 L! K5 t7 `2:引导码间隔是9 ms + 4.5 ms/ i$ {5 W  D- m
3:使用16 位客户代码5 I, v5 F7 p8 L0 m# l2 F
4:使用8 位数据代码和8 位取反的数据代码' f8 m- V2 U5 H
' S5 ]  g4 q2 b% K$ g; o
当发射器按键按下后,即有遥控码发出,所按的键不同遥控编码也不同。这种遥控码具有以下特征:采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”,其波形如图所示。- i, x. }8 |9 P! }, ^
( V/ o' I/ H( V/ H

5 h, n0 ]9 m4 `+ U3 e( E# H0 I, A' b  B" p4 Z; c( p) A
AM]YYXBP7@ZX4]66N2G8`DT.png
  [% G) t! o) I- r8 j( X; l4 c  ?
; H- z" u5 @6 I6 t2 U; I
这里我采用同一个定时器TIM3的两个通道分别用来做红外的接收和发送,接收用通道3的输入捕获,发送用通道4的输出比较,程序采用分时复用的方式完成收发一体的实验效果。默认处于接收状态,在按键触发时切换成发射状态发送红外数据,发送完成后再次默认切换成接收。
" _5 r' A# V, B7 ]# U8 l. \( G  @- V* i
上接收部分的代码:  K" [: Q' H/ X9 V0 N

/ Y/ |4 V9 d' v# N
  1. #define NEC_HEAD                (u16)(4500) ( B, O9 N; l; }9 Q: T
  2. #define NEC_ZERO                 (u16)(560)" l' n6 V8 c, ^! [
  3. #define NEC_ONE                        (u16)(1680)" l' ]* Y3 z% J" q* V2 O, `
  4. #define NEC_CONTINUE         (u16)(2500)
    4 j$ I& p3 Z3 h+ T, R1 U

  5. 7 c, j5 `" x5 e# E: s! w
  6. #define NEC_HEAD_MIN (u16)(NEC_HEAD*0.8f)8 Y6 G: z3 P$ N+ d
  7. #define NEC_HEAD_MAX (u16)(NEC_HEAD*1.2f)( W) ]: o3 G" {
  8. 0 s* K- w, p  c( @1 w7 M
  9. #define NEC_ZERO_MIN (u16)(NEC_ZERO*0.8f)
    2 G# Q' J) t! ~. ?2 u
  10. #define NEC_ZERO_MAX (u16)(NEC_ZERO*1.2f)
    / _# r; t- i* x3 E
  11. * C" r, ?- l4 @3 `
  12. #define NEC_ONE_MIN (u16)(NEC_ONE*0.8f)
    ' l; ]/ [' X2 o) v; b1 C4 U
  13. #define NEC_ONE_MAX (u16)(NEC_ONE*1.2f)& n) `5 y* c$ Q
  14. 1 P0 Q% p# g) i) \9 b3 \, m
  15. #define NEC_CONTINUE_MIN (u16)(NEC_CONTINUE*0.8f)7 ]- f% t! T! M: T
  16. #define NEC_CONTINUE_MAX (u16)(NEC_CONTINUE*1.2f)
    % j/ K: H/ F' x. [

  17. 7 x; R- Y4 E( V  P

  18. " }. {  }- T: O
  19. //红外遥控接收初始化
    ) f% A, {0 n0 b- ^
  20. void NEC_RX_Configuration(void)5 R1 h1 F2 d' [9 @" w$ h
  21. {
    / @  `  ?+ B( }$ F+ a) j1 S' \
  22.     GPIO_InitTypeDef GPIO_InitStructure;6 l9 F8 O' P; S! ~
  23.     NVIC_InitTypeDef NVIC_InitStructure;
    1 Z4 z6 p3 h4 u( o- d* D3 k- j; G2 r  v
  24.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;( i" l! E* R7 z
  25.     TIM_ICInitTypeDef  TIM_ICInitStructure;//输入捕获, x4 ^; W5 \+ z: G6 a# ^$ ~$ c' r
  26.         
    + }1 h7 y8 L$ G# i! Y& W4 r9 v
  27.     RCC_APB2PeriphClockCmd(NEC_RX_RCC|NEC_TX_RCC,ENABLE); //使能PORT时钟
    " Y( R) W9 j  W- R+ N4 }1 o
  28.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);         //TIM3 时钟使能, g8 S$ G- K, z9 M) v6 ?
  29.         " l7 S4 S9 h2 V
  30.     GPIO_InitStructure.GPIO_Pin = NEC_RX_PIN;                        
    ( e, ^5 |7 j  @6 Q# S, l
  31.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;                  // 上拉输入0 {& z3 f. [1 m0 D
  32.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;% P1 D4 ^$ r5 t: }* a7 Y' U
  33.     GPIO_Init(NEC_RX_PORT, &GPIO_InitStructure);
    8 i- s' L2 i- _9 c2 H

  34. # x) w" B1 h! M; g; w# T
  35.     GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;                         : _' [+ F% E0 ~( \: m
  36.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          //复用推挽输出
    9 n4 b1 Y4 h6 t& Z
  37.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;; b1 K7 E4 t, M; \7 v+ ^8 @
  38.     GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);
    ( {+ z4 H; D* S! t6 X0 H
  39. # G4 G: i5 O( ~' U" d( {0 a
  40.     TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出9 `- V7 N: _) M: t
  41.     TIM_TimeBaseStructure.TIM_Prescaler =71;         //预分频器,1M的计数频率,1us加1.3 i6 }  C2 W( Y+ |" V5 b
  42.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim% r' B. e3 @6 l& |
  43.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    7 [& R& J2 }  y4 k$ o2 h# w
  44.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx$ M9 ^; M+ `$ R) j- B) q7 m
  45. * `$ e8 x" R9 c, Y% j6 X: ^
  46.     TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;  // 选择输入端 IC3映射到TI3上0 n# |. P; W( }  y4 z
  47.     TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;        //上升沿捕获* m# Z1 g. Q) G. v$ P
  48.     TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;
    ( b$ x" _  \+ B- Z" }; l. u4 M# U
  49.     TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;         //配置输入分频,不分频
    / P: q: e0 a  I* g% `% B2 z1 E% m
  50.     TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波
    & A+ U, P' `+ w, {
  51.     TIM_ICInit(TIM3, &TIM_ICInitStructure);//初始化定时器输入捕获通道" h* J7 N& `- u- b( A
  52. ; I0 ~" P7 O  ]- X5 N
  53.     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
    $ y% L2 f9 X8 M4 }/ x
  54.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级- ~# Y0 X% L2 u. e3 W
  55.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级
    ! F8 F+ z$ @" L. h9 N# l
  56.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    - o: t& p" K8 K$ x/ t' ]$ j1 e
  57.     NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
    % `" J& I: ]% N5 b7 @0 S

  58. 6 Z  E: k0 v8 x! u9 s- E! m/ U
  59.     TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,ENABLE);//允许更新中断 ,允许CC3IE捕获中断6 _% N+ k! T: [
  60.         
    ( l$ S" U2 g! f* ]
  61.         TIM_ARRPreloadConfig(TIM3,ENABLE);        //重装载$ \1 d' i7 A- f8 i

  62. 6 j, J: ~% c$ K7 F
  63.     TIM_Cmd(TIM3,ENABLE );         //使能定时器3, ]0 P# E6 _, Y2 r8 x% G# X; l
  64. }
    ; c# I) T. R6 N! E
  65. 3 `$ l; k8 j" ^3 E3 ~9 L
  66. //遥控器接收状态4 c+ d' y6 k5 e
  67. //[7]:收到了引导码标志
    ; `# s% O: @2 b
  68. //[6]:得到了一个按键的所有信息
    . [1 j( k5 V5 ^- ^% @2 J6 T
  69. //[5]:保留
    & j$ _) A& v" V$ `3 @" t2 H2 L
  70. //[4]:标记上升沿是否已经被捕获# y; S/ E1 S0 Y6 e! \8 J. r. ^% S
  71. //[3:0]:溢出计时器
    8 a. E1 [" o6 j, L2 ~
  72. u8         RmtSta=0;
    2 `3 A) c2 w) k" Y
  73. u16 Dval;                //下降沿时计数器的值
    1 J% I1 j2 O7 e  S
  74. u32 RmtRec=0;        //红外接收到的数据
    5 t& E0 O7 i* t, C9 V1 z
  75. u8  RmtCnt=0;        //按键按下的次数
    ; B+ ^8 O* v3 v/ A
  76. //定时器4中断服务程序. R! K9 V! O2 p3 J' m: U
  77. void TIM3_IRQHandler(void); A% W  G. o6 b$ D9 r5 \  j! C& B
  78. {7 D/ z# y8 K/ u- P4 z9 f7 r
  79.     if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)        //更新中断  M5 {) F2 [5 t
  80.     {
    ; O0 ?3 M2 s, V+ k3 p
  81.         if(RmtSta&(1<<7))                                                                //上次有数据被接收到了* ^$ H8 S* w3 h
  82.         {3 }. A$ `; [5 ~" Q/ q
  83.             RmtSta&=~(1<<4);                                                        //取消上升沿已经被捕获标记
    : O/ f( g, |+ z* P2 j/ [
  84.             if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;                //标记已经完成一次按键的键值信息采集/ r. S4 \; D6 [9 G3 B2 |- k; s
  85.             if((RmtSta&0X0F)<14)RmtSta++;$ J. S) l( t& m
  86.             else) {" B: g6 V: i! d4 C- o
  87.             {+ D( Y% u& r  g. `/ Q: _8 ~
  88.                 RmtSta&=~(1<<7);                                        //清空引导标识
    2 P  ~5 ^/ P  K* o$ C  X% T) N
  89.                 RmtSta&=0XF0;                                                //清空计数器. g& }7 ^0 w- J9 O
  90.             }/ c! T- d, a1 p) h9 {( K, U7 l7 C
  91.         }9 X( o$ Q. w) D/ C0 x' u
  92.     }5 A. [5 S4 |2 ]* x7 S- M! v
  93.     if(TIM_GetITStatus(TIM3,TIM_IT_CC3)!=RESET)
    4 u3 a7 w9 @9 E) @- I, @
  94.     {, i, `3 p( d5 H1 ?7 X+ X- P
  95.         if(NEC_READ_RX())//上升沿捕获
    1 a- w+ L/ \+ X' w- h
  96.         {* Y2 u5 ~; v" J( Z
  97.             TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Falling);                //CC3P=1        设置为下降沿捕获. S5 S# w; D1 s, x
  98.             TIM_SetCounter(TIM3,0);                                        //清空定时器值! h) P* W# ?+ l. t# A
  99.             RmtSta|=(1<<4);                                                        //标记上升沿已经被捕获
    : E3 f0 P5 W# A9 P3 v* _3 I( m- C% B) c
  100.         }
    6 x% M8 q8 g) S
  101.                 else //下降沿捕获( K! K' s$ O7 @$ U4 \  r/ m- U
  102.         {
    * y6 d( g: g; t$ L! Q" n3 p# P
  103.             Dval=TIM_GetCapture3(TIM3);                                //读取CCR3也可以清CC3IF标志位7 I) |+ ^4 x/ i
  104.             TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Rising);                                //CC3P=0        设置为上升沿捕获0 p6 {, E! ^8 E
  105.             if(RmtSta&0X10)                                                        //完成一次高电平捕获% @! s( x9 K, S# _$ H$ U
  106.             {
    9 D, G2 N3 e" \2 _6 {4 L% g' ]
  107.                 if(RmtSta&(1<<7))//接收到了引导码9 g5 V( r: P0 K, C4 W8 V
  108.                 {
    ) A* _* s* ], F3 b/ I5 y  M; X' i
  109.                     if(Dval>NEC_ZERO_MIN && Dval<NEC_ZERO_MAX){                        //560为标准值,560us
    8 y) L( E' p. e! ^4 ^  }7 K

  110.   {' y' C+ G# V1 G/ \
  111.                         RmtRec<<=1;                                        //左移一位., {* \/ V0 \7 ^. A
  112.                         RmtRec|=0;                                        //接收到0, y5 l1 ~. U( J2 I
  113.                     } 5 W4 y# T3 @8 Z! c: C2 y( v- u
  114.                                         else if(Dval>NEC_ONE_MIN && Dval<NEC_ONE_MAX){        //1680为标准值,1680us3 b4 C  ?3 T: W1 s3 w: |, L

  115. + p# G) W' {. s; E. D
  116.                         RmtRec<<=1;                                        //左移一位.
    ' r' ?( r+ K) J' N+ X' u* h
  117.                         RmtRec|=1;                                        //接收到1# Y# _/ j! _0 {; z9 m. B
  118.                     } % k8 {% M. N, h% U0 X
  119.                                         else if(Dval>NEC_CONTINUE_MIN && Dval<NEC_CONTINUE_MAX){        //得到按键键值增加的信息 2500为标准值2.5ms* O6 ~% r' ~, Y8 A4 V

  120. . J; `2 L& o0 J  z
  121.                         RmtCnt++;                                         //按键次数增加1次, V: Y4 I  @: ], `
  122.                         RmtSta&=0XF0;                                //清空计时器3 d+ B, y, o6 k1 h0 _
  123.                     }
    : s: b5 `0 p: d4 w3 ^  U
  124.                 }
    & X  `  S0 I; {# p
  125.                                 else if(Dval>NEC_HEAD_MIN&&Dval<NEC_HEAD_MAX)                //4500为标准值4.5ms
    3 D' s4 k1 P4 a( `+ d  X# e1 [
  126.                 {
    & x7 W* ~" X: A, M: E% N
  127.                     RmtSta|=1<<7;                                        //标记成功接收到了引导码
    & o4 y$ D3 L" u+ p
  128.                     RmtCnt=0;                                                //清除按键次数计数器9 S: V$ i: D8 `" w1 C% T) `% t
  129.                 }
    + L3 _9 ^  a( q) M' g' \& z6 m
  130.             }
    7 K: q* j0 z/ @8 e) ~- A# m
  131.             RmtSta&=~(1<<4);
      J, L9 [; B  J, y" L( z
  132.         }
      x+ `" `+ Z% \' N5 T
  133.     }# I) Q! `, R$ f# P* B
  134.     TIM_ClearITPendingBit(TIM3,TIM_IT_Update|TIM_IT_CC3);
    * P! J9 V1 e: Z6 N
  135. }
    1 a5 i+ F. g: k

  136. 1 a! d/ b9 a: K. @% ^2 {9 `6 o8 X
  137. void NEC_GetValue(u16 *addr,u16 *value)6 j9 B! O3 F2 S) s/ O2 n; A
  138. {
    . W7 G' L% W! a: a3 i- _- s5 T4 E
  139.     u8 t1,t2;5 Y& a! f" L" t/ }9 Z
  140.         *addr = 0;
    % P5 A4 }4 h: L- ]$ H0 V' T3 V# m
  141.         *value = 0;5 }& E* @' z+ q5 v
  142.     if(RmtSta&(1<<6)){                        //得到一个按键的所有信息了; C, K3 j. f2 j, |* Q( v$ ]
  143. 3 R6 d, z! R- k+ Y/ Y, B/ e
  144.         t1=RmtRec>>24;                        //得到地址码, j( V+ M3 _7 e$ g, B$ C, T
  145.         t2=(RmtRec>>16)&0xff;        //得到地址反码! _8 U0 @4 `2 q! H
  146.         if(t1==(u8)~t2)        {                //检验遥控识别码(ID)及地址
    # Z3 \4 F7 K+ x4 H
  147. 2 z% v) \& q$ y4 l+ D, M
  148.                         *addr = (t1<<8) | (t2);
    $ P' n% E7 ]4 W5 G) v( K
  149.             t1=RmtRec>>8;7 J. j$ Q" L- i4 [
  150.             t2=RmtRec;
    9 `: A7 j# t2 j/ z
  151.             if(t1==(u8)~t2){                        1 B( |5 ~! ~6 d8 [$ F9 h$ ]- w  K
  152.                                 *value = (t1<<8) | (t2);4 C1 D* q! l6 q5 E
  153.                         }' G2 ?* ]% j- Z$ f7 y6 L3 [3 y
  154.                         else{
    ; p6 }& Y8 D& u' A7 R( m, s
  155.                                 *addr = 0;) K# n5 u, @1 N& a  @
  156.                                 *value=0;% L3 I: |* H# M; v0 {. z. ^4 Q: y
  157.                         }- L% e' Q& `6 s: R7 z
  158.         }6 N; ?: s7 a9 C6 V  E& a
  159.         if((*value==0)||((RmtSta&(1<<7))==0)){//按键数据错误/遥控已经没有按下了) l8 H" j0 b  q3 s0 z
  160. " i* |% i  ^+ A) m7 c5 R- {
  161.             RmtSta&=~(1<<6);//清除接收到有效按键标识2 K) Q( O- w3 T  w( d0 N) L
  162.             RmtCnt=0;                //清除按键次数计数器. h2 J8 @$ J  |6 f; e8 A! f) c
  163.         }
    . ?( K9 D# O* G# h" ~7 B" ~
  164.     }4 A' ]+ d: B! j* m/ u
  165. }
复制代码
% }) z5 z; _" R9 N2 Z
接收部分在配置上比较简单,配置一个1us计数拼了,周期为10000(也就是10ms)的计数器,同时开启更新中断和捕获中断,' [' T2 q; X0 r3 F4 u
: H  U5 |8 u- c$ t6 }$ L' O% P1 y
在更新中断,和捕获中断里面分别对接数据进行处理,这里要提一下就是捕获中断分上升沿捕获和下降沿捕获,在捕获到某一个电平后需要更新一下捕获的中断源。在每一次捕获的同时对时间间隔进行判断,判断的结果有引导码、数据码、重复码等,这里就不多解释了。6 o* b9 O  ]( N' L: X9 K
6 H5 Q! ?) s( E
上发射部分代码:
0 L8 C5 E! D! c6 E- ~6 O; f5 o1 e
1 p& E% E  N  ]& u( l, K) e6 x0 p$ K
  1. /****************************************************************************************/, z+ G- y5 m7 k1 k9 b& {5 i
  2. #define CARRIER_38KHz() TIM_SetCompare4(TIM3,9)
    # }# k1 U2 Z- R9 h% p
  3. #define NO_CARRIER()        TIM_SetCompare4(TIM3,0)
    % E% w# L- x1 Z0 \
  4. & p( B: k4 j1 z. `2 J5 F, M
  5. void NEC_TX_Configuration(void)            //红外传感器接收头引脚初始化4 T& m* Z( U, Y. }) x) v
  6. {6 Y. ?* D. u! h( w0 x
  7.     GPIO_InitTypeDef GPIO_InitStructure;
    * M1 b9 ~% {- g- ~
  8. 5 u6 `& j6 @9 W' }" m1 ~7 f
  9.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;- N, h: n; W, o
  10.         TIM_OCInitTypeDef  TIM_OCInitStructure;                                //输出比较        , Q( r  _- D6 ]* u$ M$ J: W7 X
  11.         
    0 c. X8 C) w- F
  12.     RCC_APB2PeriphClockCmd(NEC_TX_RCC,ENABLE);                         //使能PORT时钟
    9 k8 |  k) m- \% f
  13.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);        //TIM3 时钟使能
    : h$ W) D7 F1 p6 P& P  A  F0 R

  14. : ^6 Y4 }0 q# M. l
  15.     GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;                        
    1 v) y" p: z2 |& \' @" ?# F
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         //复用推挽输出
    * ^0 T6 l+ ~' d. m* F+ W+ }
  17.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;# m+ D& `5 M: B2 W3 O& ~- S' p
  18.     GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);0 W) i- y) {7 @5 Y6 {2 `! M
  19. 3 k7 `& }9 Z5 `' ?8 t
  20.         TIM_Cmd(TIM3,DISABLE);5 t  Z% o& e& A3 }+ I4 Y
  21.         TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,DISABLE);                        //关闭TIM3中断  u5 @. g# k& c0 r

  22. ) n6 V! K' L* T% `
  23.     TIM_TimeBaseStructure.TIM_Period = 25;                                                         //设定计数器自动重装值 最大10ms溢出3 Y# U8 d/ c8 u. B1 W4 {
  24.     TIM_TimeBaseStructure.TIM_Prescaler =71;                                                 //预分频器,1M的计数频率,1us加1.; X" f( H) d6 A) f* I9 i
  25.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;                 //设置时钟分割:TDTS = Tck_tim. b" x! ~" L9 K/ T; u- T3 E( ?
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;          //TIM向上计数模式  `& U" y# ~$ s2 n7 h- P9 z8 ]
  27.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); 2 j1 `: L/ f5 P8 p2 i

  28. ' W4 B# @! H7 W- h
  29.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                          //设置PWM1模式/ H$ o) Z4 ]) d8 i, `) [
  30.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
    " G: S5 q3 M3 v: M' ~, D4 C7 r
  31.         TIM_OCInitStructure.TIM_Pulse = 0;                                                            //设置捕获比较寄存器的值
    9 {" l: [6 ^' Y# l) _+ g# R
  32.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;          //设置有效电平为高电平
    % Y$ I9 \3 B% q2 c9 b! n4 P
  33.         TIM_OC4Init(TIM3, &TIM_OCInitStructure);                                           //生效初始化设置" _6 m  S  c9 F
  34.         TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);                          //使能输出比较预装载$ I/ U, u  q. p9 }% N" g; o
  35.         # a! u- D8 u- e! X1 G0 v' H
  36.         TIM_Cmd(TIM3,ENABLE);
    % i' D: [/ Z( h6 a2 [
  37. }
    1 C0 F% {3 B: |

  38. - A' N) a: S5 N1 G& Z7 m+ V
  39. static void NEC_Send_Head(void){( K& c2 A8 z1 Z1 V8 Y( A
  40.         CARRIER_38KHz();        //比较值为1/3载波
    2 w2 r1 B# y% b" K: f' B  ?
  41.         Delay_us(9000);/ I- I) y. h- M
  42.         NO_CARRIER();        //不载波! ]6 ]( X/ s" g' ?# n, Q5 z
  43.         Delay_us(4500);
    / S) u5 L6 y9 F6 s0 \
  44. }
    : i$ ?- `5 V& M( ~+ P" F, l+ k

  45. $ G( Z& g- G* l8 V8 T
  46. static void NEC_Send_BYTE(u8 value)
    7 g; A' y* Z% {: \' Y. d1 I* M. ~
  47. {, X1 _; J( U8 u/ A: v
  48.         u8 i;
    1 o% g. C. W) d
  49.         
    # C# f* E: b" n) M, G
  50.         for(i=0;i<8;i++)
    2 s/ I/ s& ?; t) O+ |
  51.         {/ _! p8 J: G" d  T
  52.                 if( value & 0x80 ){
    ) x* w2 B2 S3 K* C
  53.                         CARRIER_38KHz();        //比较值为1/3载波
    ( _  c8 c$ c  ?& Y& |# [
  54.                         Delay_us(560);0 ?) _' G0 I6 e9 [  x7 V6 z
  55.                         NO_CARRIER();        //不载波
    - \- w- W9 N$ r/ F  |; n
  56.                         Delay_us(1680);
    2 H: E- F9 X( W* Z
  57.                 }, t& k; |! o$ V4 ~
  58.                 else{9 X$ i4 ]7 ^3 L7 e& j9 @3 n
  59.                         CARRIER_38KHz();        //比较值为1/3载波
    - D3 ~2 p' d9 q9 P7 y7 h. x& c
  60.                         Delay_us(560);
    ; f2 R. U& h, ?& Z: f
  61.                         NO_CARRIER();        //不载波: u5 o* V+ j) O7 W' l
  62.                         Delay_us(560);- b. T) v5 Y$ V# g) h3 s6 C
  63.                 }' O3 m8 C& p& S3 i! n) v. W
  64.                 value<<=1;
    0 m5 x* ~/ @8 x
  65.         }2 [! \) y1 z+ {$ r9 J$ W
  66. }$ K( J' u/ H- t1 k- W* a

  67. # p4 G2 N: J; T8 ^; u3 B" _# w
  68. static void NEC_Send_Repeat(u8 repeatcnt)/ R5 {0 l" s: H, e' R& z; t- d
  69. {) ~2 W7 k9 u- c9 I
  70.         u8 i;- ?' O9 B2 E2 o' q" j
  71.         
    1 p! H3 c2 @- w8 f5 @" }5 Z
  72.     if(repeatcnt==0)          //如果没有重复码就直接设置无载波,发射管进行空闲状态$ h& i7 F5 [' ^- d
  73.     {
    3 C* u8 K1 {7 \+ }( t4 P" j+ k* Z
  74.         CARRIER_38KHz();
    " I" y( i* ^9 B2 N# S! K7 Q' l
  75.         Delay_us(560);
    5 v" v. @6 Y$ Q: k( n. }
  76.         NO_CARRIER();     
    1 u% Z5 g. W6 [$ @0 Z* P
  77.     }
    & [, Z7 I# R) h# m" A4 E
  78.         else 3 x2 S4 g# v8 r7 F8 G
  79.     {
    ; c0 Z. H0 K+ a
  80.         for(i=0;i<repeatcnt;++i)
    0 }5 Q/ |7 `, z( s2 E
  81.         {
    ) ]  s) l, S& g9 f: T
  82.             CARRIER_38KHz();
    3 ^$ C' R8 ~; Q4 @* {3 _+ v
  83.             Delay_us(560);
    : n& \* t! l! J# X' @2 p
  84.             NO_CARRIER();# k# J% ]& F( l; r
  85.             Delay_ms(98);& h" J: N/ ]: d1 G/ ~. _$ C
  86.             CARRIER_38KHz();
    3 G/ j7 K# M; p: V& R: j8 z' ^
  87.             Delay_us(9000);  ; o) }& @0 _, h9 h; J9 I
  88.             NO_CARRIER();
    5 ^5 P5 U( U2 ~/ T. ]! l% D* X
  89.             Delay_us(2250);                     9 v1 G0 P! M# h  x  [, Y* Z
  90.         }: s# H9 ?  W- M, v# k( D
  91. ' P) r. g$ X' ?  Y
  92.                 CARRIER_38KHz();
    & H8 ?* P! E/ U  L
  93.                 Delay_us(560);7 e8 h3 G3 W7 y% L+ ~
  94.                 NO_CARRIER();
    2 t: }* ?& I$ k* d
  95.     }8 @- _/ I$ n! M) P+ Y, I
  96. }& B; }1 {  f2 L, `0 V% F0 k7 d
  97. ' Y# H4 w5 o0 y7 O5 H$ n- K

  98. % u9 G* O6 w  h! H3 q
  99. void NEC_Send(u8 addr,u8 value,u8 cnt){
    / {7 s9 [% U6 c9 t5 f9 W
  100. 2 O+ X* A7 R5 f4 @, E- y( o
  101.         NEC_TX_Configuration();
    5 w3 j; ^8 U! D6 M: [( y
  102.         
    7 e9 H( ^: f6 Q0 p
  103.         NEC_Send_Head();                                //发送起始码
    & Q/ {/ Z( c3 L& D' ~: v
  104.         NEC_Send_BYTE(addr);                        //发送地址码H
    " S# N& P; M! Y' g5 Y& t
  105.         NEC_Send_BYTE(~addr);                        //发送地址码L  Y; N! J; Z1 j8 E/ T
  106.         NEC_Send_BYTE(value);                        //发送命令码H6 }) _+ @, d, W! e5 c) R4 d3 }
  107.         NEC_Send_BYTE(~value);                        //发送命令码L) Q. m' |; \9 k/ K9 L8 a! I
  108.         NEC_Send_Repeat(cnt);                        //发送重复码        3 z/ c- Q2 P4 I+ m- m2 u- n

  109. - w2 G. u" v/ \( \) c2 j" b9 |2 U
  110.         NEC_RX_Configuration();4 }5 G+ u) y( V2 f* K
  111. }
复制代码

. G0 Z0 R/ `+ G& b) t发送部分首先是重新配置定时器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 S: E1 c8 ?0 u1 P& F
  2. #define NO_CARRIER()        TIM_SetCompare4(TIM3,0)
复制代码
1 B$ Z" I( P' S& I8 `8 A; s& K+ N
载波的时候比较值设置的是9,没有载波的时候是0,9和26就约等于一个1/3的关系。
4 u8 ~# ]( D& Z* u4 M& ?* T
/ ^  W; V% S7 K  M最后就是发射部分,细节不说了,按照NEC协议进行发送,依次是引导码、地址码、地址反码、数据码、数据反码、重复码等。可以看到发射数据前后加了有NEC发送和NEC接收的配置,这里就是关键的分时部分,因为程序主要出于接收,在触发器(这里使用的按键)触发的时候才会发射红外数据。% F0 `; `% W* s4 ~5 r) E) r
  V" f% _7 u0 o, I. x  V' l* r
上述就是NEC底层部分。上层应用也贴出来以供参考: a) V- P& ~2 T3 B

! {: v, p8 h& k# p' z
+ F7 V' i) [% |' K  |5 k, k1 G6 w
  1.                 keyvalue = Key_Scan();
    0 ?( U0 Q! m) i. k
  2.                 if(keyvalue != KEY_NONE){, R; H/ L" G5 p! X; R2 U
  3.                         NEC_Send(taddr,tvalue,0);
    ( _* R( \7 B) X' e7 x6 m
  4.                         taddr++;
    1 q9 |1 |% m* t7 f5 y
  5.                         tvalue++;
    / e& Y+ n/ t8 ]6 O+ U" ^
  6.                 }8 a- K9 ^* i( z8 [- N; [
  7.         
    7 u3 W  y) m3 a6 a! y8 F0 K* H0 g
  8.                 NEC_GetValue(&addr,&value);
    . M) G# ]' _+ t, Z- S' r5 Q
  9. 3 z; L1 O" Z* Y: m" D: E
  10.                 if((value>>8)){
    6 |- U; X( C  s! x( ~2 @  ]
  11.                         printf(" %04x,%04x \r\n",addr,value);
    5 K' c5 s1 W  k2 S. T8 l5 B
  12.                         printf(" addr: %d,key: %d,cnt: %d\r\n",(addr>>8),(value>>8),RmtCnt);
    " E/ b$ n" g& ~/ H
  13.                         Oled_ShowNum(36,32,(addr>>8),3,8,16);
    6 X7 m  @: h3 d( U2 O. f* p2 k
  14.                         Oled_ShowNum(96,32,(value>>8),3,8,16);
    2 G, a2 Y' Q! C9 Y8 `7 W
  15.                         Oled_ShowNum(36,48,RmtCnt,3,8,16);
    7 ^+ g9 w7 s" s% B; ^* V
  16.                         Oled_RefreshGram();
    0 z9 z8 z9 U4 ~' z) T7 n" P
  17.                 }$ R: f- r: q7 Z" d& k% Z* x  @
  18. 4 U. G- t; |+ e4 e% N* k
  19.                 Delay_ms(10);) a0 c' ?# e/ z& d4 S, T. {* {  ^
  20.                 if(++t==50){) S) y- N' u* U
  21.                         t=0;1 D+ F2 h; g# c" O% {
  22.                         Led_Tog();. K4 i$ U( i# F8 H; q
  23.                 }
复制代码

  F; U- [1 F) ~, Q+ l最后要说一点的就是关于系统的延时,延时部分需要慎重,此处我也是踩了一坑,也一便记录下来。; k3 G! O1 t$ g4 m. V
1 ~5 @0 s- N# b/ {4 }
原来的底层:; P$ q- A4 b1 r9 o: \3 ~: `

$ e8 x+ k5 Z% Y5 A3 U6 ?. K
  1. //u32 System_ms=0;
    9 d: `6 |4 m! {3 q% r  d
  2. 6 I4 t' i  ?! `, I4 W8 k9 v
  3. //u32 usTicks;
    / K$ |, t; N* H# h3 r, h3 x) r
  4. //void Systick_Configuration(void)
    . K) B$ Y4 E3 x, `( W9 S8 v* `0 w  I
  5. //{
    1 W7 ^3 _8 K4 ?% s4 q  ^
  6. //        RCC_ClocksTypeDef RCC_ClocksStucture;
    : _. A- z8 B3 c1 l) C
  7. //        RCC_GetClocksFreq(&RCC_ClocksStucture);        
    ) Z' x: Y' w4 |& Q3 E
  8. //        usTicks = RCC_ClocksStucture.SYSCLK_Frequency/1000000;" l4 M; n! Y& D
  9. //        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
    2 s% a& F. V, ]
  10. //        SysTick_Config(RCC_ClocksStucture.SYSCLK_Frequency/1000);- _1 G9 A% t& Y
  11. //}7 w7 I" X4 {$ K/ Z1 c3 j% e

  12. 0 \7 H, M5 g2 P+ U1 s. v
  13. //u32 GetSystick_us(void); _- T0 ]  d6 b$ H
  14. //{
    - R; ?9 f; W$ J) Q' P
  15. //        register u32 ms,cycle_cnt;
    8 j; A! b/ y) P4 O3 ]( i
  16. //        do{
    , ~! q) h& p# p) _; n0 V4 S
  17. //                ms = System_ms;
    3 A. B1 k6 q1 c4 ~
  18. //                cycle_cnt = SysTick->VAL;' o; q- X. y1 k7 p; r: K% J
  19. //        }while(ms != System_ms);9 c: `5 c' K4 _) h
  20. //        return (System_ms*1000) + (usTicks * 1000 - cycle_cnt) / usTicks;
    , s2 ?) A3 L- R7 h0 b3 g% O
  21. //}" o7 _8 v: W5 Y# L% }
  22. $ l, k6 c, K; s% l
  23. //u32 GetSystick_ms(void)
    " h; j  A  q3 G2 E8 ~3 R7 K2 A
  24. //{
    , s7 w# G5 U# b7 G
  25. //        return System_ms;* t% a; O$ s% e* H
  26. //}
    : C4 s6 ]! ?" |3 `6 i& b0 |
  27. : w" P5 H( x! t& o$ q* i8 Y+ E
  28. //void Delay_us(u32 nus)/ w5 }4 e/ w- u
  29. //{
    3 L* M, e4 C9 ^
  30. //        u32 now = GetSystick_us();
    ( M4 N1 g3 I9 `' l# {) p  G' o7 r
  31. //        while(GetSystick_us() - now < nus);
    8 P/ _- s0 A' G4 S9 Q" L! r
  32. //}
    0 h5 l; h# a. o" u

  33. ) B4 z0 b3 `( G0 t
  34. //void Delay_ms(u32 nms)  Y, i: l/ z* o) X( Y+ @( v" ~6 Q3 _
  35. //{4 Z% N5 h! U% p+ ?; {: Z
  36. //        while(nms--)
    6 _0 N+ r1 c' m$ g1 R+ c
  37. //                Delay_us(1000);% k# w2 I* }1 C
  38. //}
    " w+ u+ `5 J2 [  O( e6 x

  39. 8 M6 s8 E) Z2 C$ W' k8 `
  40. void SysTick_Handler(void)9 d+ Y4 W  f8 W3 a! Z: V
  41. {: b4 `2 J! G+ f0 @
  42. //        System_ms++;
    1 b1 w& q8 Y8 p. r4 }( u
  43. }
复制代码

+ X  e- C, k  v0 D8 @: T) h这里用到的延时us和ms函数都会调用获取时基函数,这样的话ms还好一点,到us级别就不是很准,再有就是滴答中断里面也有时基去计数,再低级的中断那也是中断,会打断前台程序的运行。
4 z% v, {9 a" U8 a3 A% N" b9 n  s& y/ \/ Z( G: {' }( L5 x- [
改过之后的:
+ q7 M: e: C2 r/ c* h" t5 f2 c4 Q% K/ A2 M
  1. void Systick_Configuration(void)
    + }/ D. m/ p1 e1 @' {+ p
  2. {: w" e8 O8 a9 \* e
  3.     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //设置时钟源8分频
    + l) A3 z& q" o. }
  4.     SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;            //使能中断
    6 [1 K( P2 o. O" |
  5.     SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;             //开定时器
    5 \& y, a( T( e- c' Z' s1 p( ?. W
  6.     SysTick->LOAD = 9;                                    //随意设置一个重装载值2 [/ Z0 e6 q/ V& ~+ Q' N4 J- S
  7. }1 ^3 g9 a/ ^5 c$ r8 t+ i  p

  8. % M6 F- @, W: ~0 k1 C0 m4 \

  9. 2 K0 }3 @9 Z1 x! c
  10. void Delay_us(u32 xus)% h# G5 ]* @/ L9 T+ a$ S) B, a
  11. {
    & x- o$ Z( v0 _* c
  12.     SysTick->LOAD = 9 * xus; //计9次为1us,xus则重装载值要*9; x4 c1 V7 Q; \! s6 Z* Q8 }! K. }: Q
  13.     SysTick->VAL = 0;        //计数器归零
    ! Q5 K2 t& y9 r# g9 ]
  14.     while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待计数完成
    . s! F2 x9 u+ _4 W
  15. }
    8 ?, H1 s- l$ e- }/ Q# `4 I  W
  16. & D! [0 z( U  G! I" O/ a
  17. % _- Z) p, w5 s. A( c
  18. void Delay_ms(u32 xms)9 [' V* s; T( }. E6 `4 f
  19. {# {+ r% k1 H( W
  20.     SysTick->LOAD = 9000; //计9次为1us,1000次为1ms* L( h+ k* M8 ?3 I- u9 R  D
  21.     SysTick->VAL = 0;     //计数器归零7 j+ Y+ K4 B* K
  22.     while (xms--)
    * s- R' M9 O" ^. f# n9 }0 r% Q0 a
  23.     {! R3 g: N0 s+ p/ }
  24.         while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待单次计数完成
    8 e0 n6 G1 F( j6 Y0 c0 V
  25.     }3 k; b; ^% U; |. x
  26. }
复制代码

, v$ v/ u9 h' `这样的话中断依然打开,但是中断里是空的,其次延时函数也因为不调用获取时基直接判断寄存器而更加精确!
0 m- P9 Y8 C- }: m1 ?$ e6 x0 H% Y$ H7 ?4 d# E, W
4 p; j8 F" G# e4 H! P
收藏 评论0 发布时间:2022-3-16 10:53

举报

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