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

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

[复制链接]
STMCU小助手 发布时间:2022-3-16 10:53
红外接收头很常见,具体就不细说了,这里记录重点:1 h2 b6 n5 S( [' B: N, \( h
. @9 R) U  L& Q- n6 i
NEC的特征
- J9 @5 H9 d6 T1:使用38 kHz 载波频率
* J3 O- w8 X8 H& P& y6 p2:引导码间隔是9 ms + 4.5 ms+ }( I0 p5 @5 L6 q: ~" |4 ]
3:使用16 位客户代码3 G4 e9 w# N, A! S* I# W9 Q. S
4:使用8 位数据代码和8 位取反的数据代码
; U& B; W7 o  K
) j2 |8 P# c( g5 d; ?当发射器按键按下后,即有遥控码发出,所按的键不同遥控编码也不同。这种遥控码具有以下特征:采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”,其波形如图所示。
+ G' W% H! [8 p1 V1 F, r) W* {. S. |
1 \' f3 ?) W9 z' }" Z
" N# w5 p+ O8 q, U4 q5 W& G+ ~
) G) h4 n" |6 r& r AM]YYXBP7@ZX4]66N2G8`DT.png
8 E2 X# [# b2 Q
- @4 ]. z. T4 E& }0 n4 Y* Y! E* K: ~$ V( C+ f; T& N( y
这里我采用同一个定时器TIM3的两个通道分别用来做红外的接收和发送,接收用通道3的输入捕获,发送用通道4的输出比较,程序采用分时复用的方式完成收发一体的实验效果。默认处于接收状态,在按键触发时切换成发射状态发送红外数据,发送完成后再次默认切换成接收。* i; X2 M6 e  J  l- K; m- I0 w9 c2 C
1 |" u, ~; U4 n, N7 D) H4 C
上接收部分的代码:! d, W, A8 S! A; W- Z

) E, W3 y6 s4 u1 [& V" r2 r
  1. #define NEC_HEAD                (u16)(4500)
    * p7 G, |% g( \* S% Q; `6 y2 p1 i0 G  E3 n
  2. #define NEC_ZERO                 (u16)(560)
    : E7 f: j0 y1 D& D; ^: i
  3. #define NEC_ONE                        (u16)(1680)
    7 Z+ P8 q1 p( |& w$ K
  4. #define NEC_CONTINUE         (u16)(2500)- n( _5 W: U" \# K8 E/ ]

  5. 0 L) L4 x! N/ ?. T& A1 N9 y
  6. #define NEC_HEAD_MIN (u16)(NEC_HEAD*0.8f)
    . O, c$ g9 Y# v$ S4 j
  7. #define NEC_HEAD_MAX (u16)(NEC_HEAD*1.2f)
      H4 ~) ^8 D6 F" }. F- Z+ E, d
  8. 4 t/ G& r! T0 G' Q
  9. #define NEC_ZERO_MIN (u16)(NEC_ZERO*0.8f)
    5 {4 [$ Z0 g1 G8 |$ G
  10. #define NEC_ZERO_MAX (u16)(NEC_ZERO*1.2f)
    " N6 ]5 x7 m# D" O4 J
  11. ; Y8 N0 k, _0 T: t! A7 e0 R* E
  12. #define NEC_ONE_MIN (u16)(NEC_ONE*0.8f)# O2 p. N, y6 |# k0 {) j- g2 }. }4 P
  13. #define NEC_ONE_MAX (u16)(NEC_ONE*1.2f); G& O- v1 U7 D) I( m) L
  14. + ]! n. M$ w* O- B  h  s- {% l* b+ Z- D7 o
  15. #define NEC_CONTINUE_MIN (u16)(NEC_CONTINUE*0.8f)
    ( \- g' T& b! M1 f7 N
  16. #define NEC_CONTINUE_MAX (u16)(NEC_CONTINUE*1.2f)
    8 P5 S* p7 c, s0 v$ l0 n
  17. - {) S: q& J" f8 Q4 q' t, f- B* S* H
  18. 2 _1 I# ^+ j$ y( l# t
  19. //红外遥控接收初始化
    ; t5 j. S( M7 P; L5 a& G7 n6 R
  20. void NEC_RX_Configuration(void). I% g+ |0 Q9 K
  21. {, T9 a) ~, E- E$ i2 k3 C3 f
  22.     GPIO_InitTypeDef GPIO_InitStructure;) }4 p( |8 L, r! z, D
  23.     NVIC_InitTypeDef NVIC_InitStructure;
    : ~/ T/ N' F& E& x
  24.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    5 F4 ~7 V, Y! Z, e6 f8 M+ o: g
  25.     TIM_ICInitTypeDef  TIM_ICInitStructure;//输入捕获
    5 M: k: S# S% g9 k, D+ s+ t- i7 O  r
  26.         1 H% ~( J! J; G0 V* X
  27.     RCC_APB2PeriphClockCmd(NEC_RX_RCC|NEC_TX_RCC,ENABLE); //使能PORT时钟% ^0 r* B+ d2 L3 ?1 G/ ]+ }' N* l
  28.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);         //TIM3 时钟使能
    ( @7 U0 R5 {% I% p9 o" w; F
  29.         ; J2 P, p& u' M! p
  30.     GPIO_InitStructure.GPIO_Pin = NEC_RX_PIN;                        
    0 |0 R( K5 _  a
  31.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;                  // 上拉输入
    4 C, A3 R, E. o  y* q7 p; w3 ]* q
  32.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;& O# p0 U) |. j7 @$ z
  33.     GPIO_Init(NEC_RX_PORT, &GPIO_InitStructure);" u& `) t$ E% d3 G

  34. , `4 m# Q; E  K- e+ }3 F
  35.     GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;                         + n/ p" J; @' B+ r" z, V& ~6 }
  36.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          //复用推挽输出
    ' b" ~6 b4 |1 A1 e6 b
  37.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;) h" d. P, [" e2 u) M) B
  38.     GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);! |3 y+ T- R! r  w9 U" a

  39. " F! Z4 [4 M: C% Y6 L' H
  40.     TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出
    0 ~+ r8 P/ F  @
  41.     TIM_TimeBaseStructure.TIM_Prescaler =71;         //预分频器,1M的计数频率,1us加1.7 y5 e. ]& Q! Z# H6 L8 e6 l. c: ^
  42.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    2 h# w; O8 D6 @: g; R3 t
  43.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    ' T' Z3 W# u4 O4 V8 w3 d
  44.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx3 k& a. F6 ~/ d( I# {+ U
  45. , z* B, [! `8 }5 i- Z
  46.     TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;  // 选择输入端 IC3映射到TI3上
    . g4 m# G  \' K; y- p" @
  47.     TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;        //上升沿捕获2 r, R7 z3 x. l8 Y/ v# J
  48.     TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;7 W! |5 f/ t  K* Z3 N  D2 D
  49.     TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;         //配置输入分频,不分频
    0 U7 Y0 m* w, }5 F% H& J
  50.     TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波
    2 d: }! o) x. _- w1 B8 T6 \* X
  51.     TIM_ICInit(TIM3, &TIM_ICInitStructure);//初始化定时器输入捕获通道& ?& X* [+ y3 s$ |. z
  52. 2 y/ i' o/ c* m4 K
  53.     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
    * i3 B* q. r# o5 F+ z. E0 q2 u
  54.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级' o& `: x& n: ]% h4 L
  55.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级; t. V4 H5 e8 B) g3 ^; o
  56.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
    1 B! Q+ p6 ~4 j5 V
  57.     NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
    ( D  C$ r+ j6 j
  58. 1 g9 N' f2 j7 p7 V# x1 [  H5 [
  59.     TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,ENABLE);//允许更新中断 ,允许CC3IE捕获中断
    ' A4 a5 @: A( ^3 Y4 g5 Y( ]8 t% I
  60.         , K$ C$ z  p; \4 f8 C
  61.         TIM_ARRPreloadConfig(TIM3,ENABLE);        //重装载9 L) \6 z( R$ A" ~% ^
  62. # `  [* {; x3 m1 d
  63.     TIM_Cmd(TIM3,ENABLE );         //使能定时器3+ l& \6 C) y0 e- ?- s
  64. }6 m( x# b! n+ [0 N+ k, U, ~7 C

  65. & Q2 [: v" E  b; b9 [: P
  66. //遥控器接收状态
    7 J' X) Z7 N, Q* E# e' D- R* V) P
  67. //[7]:收到了引导码标志
    # e' |7 k- {- O* A, M3 v* h# i
  68. //[6]:得到了一个按键的所有信息! c' V3 q7 K$ X9 ?/ U9 Y
  69. //[5]:保留7 U, t( y3 q9 z4 m( t- m2 ~
  70. //[4]:标记上升沿是否已经被捕获7 x7 d, W  J2 v, P  C6 V1 a
  71. //[3:0]:溢出计时器
    4 E$ J$ ]1 q( t; O, R, _  o
  72. u8         RmtSta=0;, a  L- m. E- p  }
  73. u16 Dval;                //下降沿时计数器的值
    - i4 W+ W4 ~9 j7 X
  74. u32 RmtRec=0;        //红外接收到的数据
    / s9 @& v- r4 {3 v1 B3 E4 _
  75. u8  RmtCnt=0;        //按键按下的次数
    / k9 n8 e1 j1 N) w; f
  76. //定时器4中断服务程序
    % E. Q0 U9 _& c* L& \3 t; I' C1 \9 ]
  77. void TIM3_IRQHandler(void). y# W! }. W7 s/ O  L- }
  78. {) h; o, u5 t, |  }
  79.     if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)        //更新中断
    ) p5 x) q9 O, p: n7 G+ l& P4 m
  80.     {' x% P; S, C; G9 O" a0 @7 G! w
  81.         if(RmtSta&(1<<7))                                                                //上次有数据被接收到了
    9 [" v% ?3 b: c2 R- i
  82.         {
    ( ?: ^1 a$ t: p: F! k3 J! o/ @
  83.             RmtSta&=~(1<<4);                                                        //取消上升沿已经被捕获标记8 N, N! u$ O3 H1 d* i
  84.             if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;                //标记已经完成一次按键的键值信息采集
    3 H( \! `& y- z# P' u3 A
  85.             if((RmtSta&0X0F)<14)RmtSta++;
    : Z6 |9 u5 g* T+ P) p' ?
  86.             else- o3 C2 O! S* W9 q; O9 Z) g% u
  87.             {0 ~# s2 t' J* [
  88.                 RmtSta&=~(1<<7);                                        //清空引导标识
    4 k# W( c8 I: a( e
  89.                 RmtSta&=0XF0;                                                //清空计数器2 a6 x# i  |' z
  90.             }) K! H3 L5 y0 @3 `) k
  91.         }+ t  y1 k% U$ e  _; G* F. E
  92.     }
    3 B: W, Q, E/ w& M' [
  93.     if(TIM_GetITStatus(TIM3,TIM_IT_CC3)!=RESET)3 k+ G0 {5 U1 h1 i& J, q
  94.     {
      _6 W8 Q2 I% M+ \( j: \
  95.         if(NEC_READ_RX())//上升沿捕获  z- L+ H" L  D  x8 v
  96.         {
    8 P* i" Y4 m3 p; }+ c0 A
  97.             TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Falling);                //CC3P=1        设置为下降沿捕获
    * Y* F* J4 i# r" H" S6 L/ K
  98.             TIM_SetCounter(TIM3,0);                                        //清空定时器值9 Q, f! P3 p4 I) G0 t3 s
  99.             RmtSta|=(1<<4);                                                        //标记上升沿已经被捕获: `; K& \; _. U, E% s: w
  100.         } 0 M" f3 R* H& N& _
  101.                 else //下降沿捕获
    % N. l5 ^1 L) i- X1 ?0 z
  102.         {
    - S; V  V: p' O( v; y
  103.             Dval=TIM_GetCapture3(TIM3);                                //读取CCR3也可以清CC3IF标志位  U$ A# N; |3 N
  104.             TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Rising);                                //CC3P=0        设置为上升沿捕获
    , Z  H& X- A. _1 N! o
  105.             if(RmtSta&0X10)                                                        //完成一次高电平捕获9 Z, M3 F' @% k( [$ _' ^# Y0 v
  106.             {
    - I# n$ m, W$ A1 n2 V" H/ x, v$ X
  107.                 if(RmtSta&(1<<7))//接收到了引导码
    1 c6 o5 ~( ?4 f$ p* L2 z
  108.                 {
    ; Z9 B% K3 x, x9 ~
  109.                     if(Dval>NEC_ZERO_MIN && Dval<NEC_ZERO_MAX){                        //560为标准值,560us
    6 S, u/ e& J; a( S% `
  110. 3 l0 {5 z8 {( r! A
  111.                         RmtRec<<=1;                                        //左移一位.# s, ]# t' l/ A  N( g1 i9 P
  112.                         RmtRec|=0;                                        //接收到0
    0 i0 E  t% A& ?# T$ T/ N  M
  113.                     } 6 b9 V& _1 e  f& J. R8 F" O/ y
  114.                                         else if(Dval>NEC_ONE_MIN && Dval<NEC_ONE_MAX){        //1680为标准值,1680us
    + j4 @5 r0 m3 D  d2 q% S- t

  115.   W" B7 ]; u4 N4 r
  116.                         RmtRec<<=1;                                        //左移一位.0 v8 ~7 w# i9 W- B* Z: b% B
  117.                         RmtRec|=1;                                        //接收到1% I% A) Y1 R) n$ r9 Y; v
  118.                     }
    % x+ D3 ]4 Y) z% n5 e  R- P. Z
  119.                                         else if(Dval>NEC_CONTINUE_MIN && Dval<NEC_CONTINUE_MAX){        //得到按键键值增加的信息 2500为标准值2.5ms' r; ?- x  o/ u# i: V; _

  120. ; ]( ~) w( O$ j  m7 m, k2 V
  121.                         RmtCnt++;                                         //按键次数增加1次
    / {; o. @9 t) l6 d6 z8 _. X
  122.                         RmtSta&=0XF0;                                //清空计时器
    ! c, K* Z; ^8 a, d6 T8 a7 {( O
  123.                     }2 n0 h8 E# B# R0 V7 t% w
  124.                 } ; j1 q) Q: u! G+ ?
  125.                                 else if(Dval>NEC_HEAD_MIN&&Dval<NEC_HEAD_MAX)                //4500为标准值4.5ms
    2 q2 s% ]  R; i, z+ c
  126.                 {
    " L* {% D6 D; q( M
  127.                     RmtSta|=1<<7;                                        //标记成功接收到了引导码; V3 h. C! y) ]; W2 ]1 O  `  d
  128.                     RmtCnt=0;                                                //清除按键次数计数器
    : {+ x2 ^) Y7 g0 S6 F& s
  129.                 }) Q0 g, H3 k6 j2 W+ T# y  ^7 ~
  130.             }
    7 f9 B* [6 ?9 b# [% s, U/ ?  y
  131.             RmtSta&=~(1<<4);
    , z2 s! p3 z6 A8 P# B3 A
  132.         }" e# w! Q/ ^. }- _* F0 i& a4 [
  133.     }
    7 E- ]7 Y0 ?6 O; p) r1 l
  134.     TIM_ClearITPendingBit(TIM3,TIM_IT_Update|TIM_IT_CC3);
    / l6 j$ @* C0 `7 E
  135. }
    $ g8 V( N6 z3 E8 V& Q8 D
  136. 8 E" V0 g& w0 Q; ]! j0 Y3 s
  137. void NEC_GetValue(u16 *addr,u16 *value)6 M3 y1 |2 `9 ^
  138. {
    % m! F! k* W# C
  139.     u8 t1,t2;
    7 s2 |! {. y& r. S) ]! q* T  \
  140.         *addr = 0;
      D% G2 W7 `+ _% N5 _! v6 Z
  141.         *value = 0;! T, X: }! \* J/ i9 T. a5 t
  142.     if(RmtSta&(1<<6)){                        //得到一个按键的所有信息了) }! H3 Y. P+ J! ^# [, p7 _, D
  143. 2 t7 [8 K" D. ^! k
  144.         t1=RmtRec>>24;                        //得到地址码
    9 N, k* W+ _/ s; [- T
  145.         t2=(RmtRec>>16)&0xff;        //得到地址反码/ P2 c2 }" P' e+ j, d9 v
  146.         if(t1==(u8)~t2)        {                //检验遥控识别码(ID)及地址
    % u. w0 p7 e0 t( A9 M
  147. 2 D) X2 ?2 i4 n8 l8 m: a
  148.                         *addr = (t1<<8) | (t2);: N3 {3 I- g7 |  I* \- w
  149.             t1=RmtRec>>8;
    1 R: q0 L; l; K! A* k7 l6 M
  150.             t2=RmtRec;$ O2 _; I" }$ m7 H" |" f3 A3 z
  151.             if(t1==(u8)~t2){                        1 b4 o6 D! H7 C7 N! r3 A
  152.                                 *value = (t1<<8) | (t2);
    + ?! R. p+ q2 B* P0 |/ o% g  a- u
  153.                         }& U0 m& m7 X/ G+ R: U% c
  154.                         else{
    ! E, O8 O+ K# z; y2 o6 [
  155.                                 *addr = 0;
    4 P, d5 M3 _" E7 d
  156.                                 *value=0;
    & G0 Z5 j: I! |% t7 l
  157.                         }
    % }3 S# e0 ^6 T7 F4 [$ u
  158.         }+ y& q( S- t9 c5 b) b& a7 `) q; Q" U
  159.         if((*value==0)||((RmtSta&(1<<7))==0)){//按键数据错误/遥控已经没有按下了/ I" L% a& x! ?
  160. 8 f  j: m1 x7 j6 B
  161.             RmtSta&=~(1<<6);//清除接收到有效按键标识; I- ]( b5 A" T/ W. i0 W3 s
  162.             RmtCnt=0;                //清除按键次数计数器& k8 }- e; \2 v5 K$ }
  163.         }
    ! V- N% k  b( \. _2 M5 b; c
  164.     }+ a6 j% y  l! \9 ~$ Z6 h1 f
  165. }
复制代码

5 o+ |4 u. i7 M7 |& i" v接收部分在配置上比较简单,配置一个1us计数拼了,周期为10000(也就是10ms)的计数器,同时开启更新中断和捕获中断,
$ u- s. q) r& F! N
) ?$ |' o, E* A1 E0 J在更新中断,和捕获中断里面分别对接数据进行处理,这里要提一下就是捕获中断分上升沿捕获和下降沿捕获,在捕获到某一个电平后需要更新一下捕获的中断源。在每一次捕获的同时对时间间隔进行判断,判断的结果有引导码、数据码、重复码等,这里就不多解释了。5 W  m- D0 d4 r& X+ w' G
1 R8 Z$ a2 P, m5 k
上发射部分代码:, b3 E: ]; {' \3 Z' i' Z/ }' p

8 P5 C0 O1 |& b1 j3 q# {
  1. /****************************************************************************************/  A6 N6 o; P( b0 {; E: A& l
  2. #define CARRIER_38KHz() TIM_SetCompare4(TIM3,9)
    5 ], b- i) I  L0 {
  3. #define NO_CARRIER()        TIM_SetCompare4(TIM3,0)- ?/ o; J# a0 x3 ~& ~
  4. ! v+ W8 @. f& q
  5. void NEC_TX_Configuration(void)            //红外传感器接收头引脚初始化
    1 k5 B1 L' j2 x; v2 d
  6. {  U3 p$ ], j" @
  7.     GPIO_InitTypeDef GPIO_InitStructure;
    & D  b" q( I5 |/ q. X9 Q' T/ F

  8. ' t  I7 f" {0 G7 L, p/ ^; V0 m: U
  9.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;! |! U4 [( g: Z
  10.         TIM_OCInitTypeDef  TIM_OCInitStructure;                                //输出比较        3 u8 L* t6 _4 e9 T
  11.         
    ) Y, l* }8 |+ ]; r9 M
  12.     RCC_APB2PeriphClockCmd(NEC_TX_RCC,ENABLE);                         //使能PORT时钟6 J6 g: u9 P3 U5 O8 M+ O' V; J
  13.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);        //TIM3 时钟使能4 u, Q4 f- W( ?+ ?4 r

  14. / f) d; v) \/ q" |3 y9 n
  15.     GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;                        
    & {# d) f- ?( L) p# i. E8 q
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         //复用推挽输出' \( }2 c1 Z' W8 Q. q( a5 k. f
  17.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;2 W% v7 H1 v7 v8 Y  [) j* b
  18.     GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);( S, v  I) F% E, \- s$ n
  19. % _5 k7 o8 K: `+ g' i2 m, ^
  20.         TIM_Cmd(TIM3,DISABLE);
    7 N/ y6 ~; F  B7 d: E6 `
  21.         TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,DISABLE);                        //关闭TIM3中断
    0 C2 `! `5 Q: B2 b' O* R

  22.   s5 _% M7 u  R" p
  23.     TIM_TimeBaseStructure.TIM_Period = 25;                                                         //设定计数器自动重装值 最大10ms溢出1 X1 t) l. y/ R# M$ P, v. h
  24.     TIM_TimeBaseStructure.TIM_Prescaler =71;                                                 //预分频器,1M的计数频率,1us加1.7 y  Y4 V) N$ H; z% n
  25.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;                 //设置时钟分割:TDTS = Tck_tim
    7 _! p- g4 j- B8 _9 A. \
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;          //TIM向上计数模式
    + _: S" W5 B$ [# ~) A
  27.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);
    & R, C0 s6 u* G- I- {* ?

  28. 7 ]( I+ Q# t  r( ]7 a
  29.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                          //设置PWM1模式
    6 p3 r1 N  {. C7 v1 h
  30.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能" z# ]) z% e. d6 z2 K
  31.         TIM_OCInitStructure.TIM_Pulse = 0;                                                            //设置捕获比较寄存器的值  K- O+ B1 q" t. Z
  32.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;          //设置有效电平为高电平/ w. G% @  u/ \. m- o
  33.         TIM_OC4Init(TIM3, &TIM_OCInitStructure);                                           //生效初始化设置4 h3 s  _$ M6 S( y) g+ X
  34.         TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);                          //使能输出比较预装载
    , u, T# B7 G9 \4 ^
  35.         
    ! b% O( O1 v/ G- b) T
  36.         TIM_Cmd(TIM3,ENABLE);, n2 {; n8 j5 ~3 q* V" H
  37. }6 t( B; d. |. L: v

  38. : S& P- a2 L- R: e
  39. static void NEC_Send_Head(void){4 `+ |" g5 @; C6 ?
  40.         CARRIER_38KHz();        //比较值为1/3载波, B8 M9 t4 }+ X. n8 k% W1 h8 ^- B
  41.         Delay_us(9000);
    ' @- Y/ e+ e( X+ b
  42.         NO_CARRIER();        //不载波
    2 y5 w8 e/ S8 B4 q
  43.         Delay_us(4500);
    - D, w5 G! P' A" K& t2 v
  44. }8 I: k. |! X1 P( N

  45. ' b* f  x" N3 L% z
  46. static void NEC_Send_BYTE(u8 value)
    + v7 u% C) j! u  L  H8 c0 C
  47. {
    % U, v3 E. Y( ^- R
  48.         u8 i;
      K' j; U/ u) X# m) v7 \
  49.         
    5 ~7 K1 M+ C2 e8 \- R
  50.         for(i=0;i<8;i++)+ v4 Q! J" [  Z, k; q7 |
  51.         {; w" K$ S: e) o( |% G5 c% m+ t
  52.                 if( value & 0x80 ){- k8 |7 z, B* q# r7 M& `( m6 w5 H# p
  53.                         CARRIER_38KHz();        //比较值为1/3载波& I3 A; }8 v0 t
  54.                         Delay_us(560);
    . N1 i- q$ ?- G; o9 d
  55.                         NO_CARRIER();        //不载波( e9 ]: p$ D4 a! w/ E/ H  w
  56.                         Delay_us(1680);. O6 p- m) @2 F, B4 A3 ~: C" ?
  57.                 }7 _0 u7 O1 l' \2 d! _
  58.                 else{
    8 y# z  }! a& }7 q) `4 P
  59.                         CARRIER_38KHz();        //比较值为1/3载波
    0 c# x6 Z8 k$ Y  Q) k
  60.                         Delay_us(560);
    - O! g6 O4 z& R7 U+ d
  61.                         NO_CARRIER();        //不载波
    ) F% ]" @- a& ]
  62.                         Delay_us(560);
    3 n9 j6 o3 r, H3 C! Y! e
  63.                 }5 I+ c# b1 [$ |: b( S4 z
  64.                 value<<=1;- {, Z4 V  S4 P1 G7 T, G
  65.         }: x3 M0 }5 u4 x8 w( T
  66. }. O8 S2 w# l2 N, G5 E. u

  67. ! I' V# `/ o+ t( {+ r' \
  68. static void NEC_Send_Repeat(u8 repeatcnt)
    / n9 x. |3 t7 H# j& n
  69. {" l2 _) [1 W3 N& P  O
  70.         u8 i;
    . O0 ~/ U+ a2 ^: \8 V% j4 W  X$ o
  71.         $ P9 s( t5 Y& a; b3 u
  72.     if(repeatcnt==0)          //如果没有重复码就直接设置无载波,发射管进行空闲状态
    6 o7 N3 j8 Z: p& t( \
  73.     {- w- k: s  J2 h3 y4 R9 g3 f
  74.         CARRIER_38KHz();- b* c) J: X9 L1 q
  75.         Delay_us(560);, S& ?" P/ @  M) A* y  A$ M
  76.         NO_CARRIER();     3 M# h8 O& c! `2 U) g' C
  77.     }- ^) a$ _( q5 r. e8 o
  78.         else * G( B* R9 ?7 \  Q9 _
  79.     {
    6 ]6 D* ?' a: g: H
  80.         for(i=0;i<repeatcnt;++i)4 N/ e% ?  ?3 S# {
  81.         {3 j, F1 g7 z  q9 q1 s9 L
  82.             CARRIER_38KHz();- N( W! W! ]5 z$ S- D# @0 u- H
  83.             Delay_us(560);
    4 E: C4 }: j# H5 e; x! |
  84.             NO_CARRIER();
    ( s$ C. t" ]8 l
  85.             Delay_ms(98);0 B2 ]. g- ^" T$ e
  86.             CARRIER_38KHz();8 h  S8 J/ U4 ?) U* C; ~  D! G
  87.             Delay_us(9000);    ]$ w2 m) O. r+ l9 H( ~
  88.             NO_CARRIER();
    4 B! W: P4 c( d  {# }
  89.             Delay_us(2250);                     
    9 n( }" T  v6 l  t4 N0 A9 M+ V
  90.         }" _+ @8 ^  L  e' }' x8 d
  91. 1 [5 @# v: k0 K4 M* S
  92.                 CARRIER_38KHz();
    ! j' Z9 R) Y+ |( u" o; n8 O
  93.                 Delay_us(560);* v2 d* t# r6 b# U  Z4 m
  94.                 NO_CARRIER(); * n+ r+ ~: ~0 X" u! q
  95.     }
    ) Y8 S# p2 [0 F: D$ Q
  96. }. |/ I4 a5 k# e5 _7 q- }' K

  97. # |2 T7 W0 _- L+ C6 p5 @
  98. + G2 [; d! O8 E# Z* _8 ]& e
  99. void NEC_Send(u8 addr,u8 value,u8 cnt){
    5 c% l3 v0 |7 M: I
  100. & V/ }) s8 H5 y' P! T
  101.         NEC_TX_Configuration();2 U$ ^1 ], L9 B) |
  102.         
    % r  F5 {2 A2 o( X
  103.         NEC_Send_Head();                                //发送起始码+ e& [( a& O; \: T+ {
  104.         NEC_Send_BYTE(addr);                        //发送地址码H
    7 b+ D1 g; Q5 n1 T
  105.         NEC_Send_BYTE(~addr);                        //发送地址码L
    6 l9 @1 A5 a7 _: r5 U1 y
  106.         NEC_Send_BYTE(value);                        //发送命令码H5 V* L/ ^; n; H& B) h) _7 U& l
  107.         NEC_Send_BYTE(~value);                        //发送命令码L
    3 C  C% f% T: K5 v, L
  108.         NEC_Send_Repeat(cnt);                        //发送重复码        
    1 D/ V+ w. Z' ~7 l$ v
  109. 3 \; [$ {5 {9 ~( i% H" Y
  110.         NEC_RX_Configuration();
    ! _4 C* h/ |, N& R
  111. }
复制代码

9 E5 p  N+ s. E+ z) M发送部分首先是重新配置定时器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: a% A) R9 _- N
  2. #define NO_CARRIER()        TIM_SetCompare4(TIM3,0)
复制代码
3 t8 d# M' _, p) u3 z  i% _
载波的时候比较值设置的是9,没有载波的时候是0,9和26就约等于一个1/3的关系。- t( ~0 g% f: k* G6 @3 ?

! q1 g  m$ [# o: u" ~  M最后就是发射部分,细节不说了,按照NEC协议进行发送,依次是引导码、地址码、地址反码、数据码、数据反码、重复码等。可以看到发射数据前后加了有NEC发送和NEC接收的配置,这里就是关键的分时部分,因为程序主要出于接收,在触发器(这里使用的按键)触发的时候才会发射红外数据。4 n& u" L" H/ U# B# g
  I% p1 b& o2 j$ [% v
上述就是NEC底层部分。上层应用也贴出来以供参考
& m& t) E# H( \3 \& A% m2 U3 X+ z3 r2 V/ ~" W
6 `) F$ M. L% I) ]
  1.                 keyvalue = Key_Scan();) x" A4 `  L( e5 |8 u
  2.                 if(keyvalue != KEY_NONE){; l6 a0 ]$ W; ^3 n: m
  3.                         NEC_Send(taddr,tvalue,0);6 U$ o$ b. |+ N& y$ X( X+ Z2 \
  4.                         taddr++;: ^2 F6 |3 A2 g9 W* W) M1 h1 W
  5.                         tvalue++;; y; j- |  L; e- r7 I6 l/ Y
  6.                 }
    ; Q8 s6 u0 z1 I  b! e" G  R- F$ \1 p
  7.         + }* ^% |/ y& D% B  F
  8.                 NEC_GetValue(&addr,&value);
    + r1 l" ?6 n2 q# Q0 C
  9. ( o$ N( Q$ v( J9 a0 R
  10.                 if((value>>8)){
    - y. v! v: d; y4 w8 n! m5 F1 h  S$ X: L
  11.                         printf(" %04x,%04x \r\n",addr,value);' P4 \% u) q  x. u+ z! k/ u: R2 q6 i8 K
  12.                         printf(" addr: %d,key: %d,cnt: %d\r\n",(addr>>8),(value>>8),RmtCnt);
    $ I  S3 a2 e. D6 P( ]0 h
  13.                         Oled_ShowNum(36,32,(addr>>8),3,8,16);: N" x& p& r- ?0 e# k6 [
  14.                         Oled_ShowNum(96,32,(value>>8),3,8,16);+ Y$ a$ w& R# I. n7 e0 b2 v
  15.                         Oled_ShowNum(36,48,RmtCnt,3,8,16);4 c. k: J& l" {( h3 I9 R# l7 b
  16.                         Oled_RefreshGram();
    6 v  K7 F' t0 i/ w
  17.                 }
    9 R# u5 j3 h% l5 y$ x
  18. 0 J8 h/ G6 p) B/ O# m& R9 x' R
  19.                 Delay_ms(10);
    3 [4 Y5 h; O$ O& e
  20.                 if(++t==50){
    % v) |( T" K. b, l
  21.                         t=0;) E( c& {. ]8 A* v- Q
  22.                         Led_Tog();+ x/ T1 w+ N- {% K( F. n& u% w
  23.                 }
复制代码

! l$ ]! |& C" j: }7 G8 _最后要说一点的就是关于系统的延时,延时部分需要慎重,此处我也是踩了一坑,也一便记录下来。4 X2 d1 Q+ {$ u5 l  ~4 R
# M4 D2 c8 y* a, r# D7 H1 Q
原来的底层:) N+ B9 n- E( X$ t& v1 [2 R  s
1 Q. F* v1 c7 I4 a0 y1 M+ j3 g
  1. //u32 System_ms=0;
    8 n+ a, |; }0 \. q1 F0 B" A

  2. 3 D0 u3 ~( Z: @  ?9 p: Q/ Q
  3. //u32 usTicks;8 a! b" ]# Z$ A6 N
  4. //void Systick_Configuration(void)
    6 V) x4 A. x+ \/ k9 y( `! R6 j
  5. //{
      P$ F4 {0 R1 y6 v7 I6 F7 F
  6. //        RCC_ClocksTypeDef RCC_ClocksStucture;7 N& V  D. {* o- s0 q" i# v4 j
  7. //        RCC_GetClocksFreq(&RCC_ClocksStucture);        $ Y$ V1 \1 b7 G) k- M* p
  8. //        usTicks = RCC_ClocksStucture.SYSCLK_Frequency/1000000;9 y! w' d/ {; m$ p6 }) @  p
  9. //        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);
    + C: T6 ?/ y  X1 e$ M
  10. //        SysTick_Config(RCC_ClocksStucture.SYSCLK_Frequency/1000);2 a8 G, _% W& s% Q! x: @' i; w
  11. //}3 G& @3 K! S4 ~# ]; n* s+ @: e
  12. $ l4 d, S- `* [- E
  13. //u32 GetSystick_us(void)
    8 o& g% t- D1 e6 S) Q
  14. //{6 U8 c' O; S* K+ t0 n* B
  15. //        register u32 ms,cycle_cnt;
    ( h$ [) G3 H/ {  R
  16. //        do{  O/ @. W8 k+ b
  17. //                ms = System_ms;9 ^/ ^& G; I: p( ]. N3 p
  18. //                cycle_cnt = SysTick->VAL;" |; O& {+ E+ |6 o! o9 |
  19. //        }while(ms != System_ms);5 c5 U; L. Y1 q
  20. //        return (System_ms*1000) + (usTicks * 1000 - cycle_cnt) / usTicks;/ M. V( Z$ h( O" |( P
  21. //}/ t7 Y" n6 x& {' x2 Z0 o6 p  {8 [

  22. 3 Q, _& Q- v$ }1 {4 ?
  23. //u32 GetSystick_ms(void)
    0 b7 A) j6 q/ {2 W, y
  24. //{
    4 N7 Z5 @8 K* W8 b- J1 X
  25. //        return System_ms;
    8 D6 Q7 s' Z4 p+ }% [1 [
  26. //}/ D: x1 J  Y5 }1 b% ]2 I- G9 ^7 @
  27. + n: a+ i7 _" q3 V4 n
  28. //void Delay_us(u32 nus)7 \5 I+ ?( y( J6 `- }6 h
  29. //{+ m5 e  t5 y$ d! H4 \
  30. //        u32 now = GetSystick_us();
    ' ~9 k( _, m2 E+ X% u* G; f" }8 [
  31. //        while(GetSystick_us() - now < nus);+ D4 n' m% E) H) I7 [
  32. //}/ w1 n" I3 H, @5 F" X

  33. . ?3 o6 c( q$ J/ E$ c! O
  34. //void Delay_ms(u32 nms)
    3 t! G: j- p$ v. L
  35. //{
    6 t0 U: j$ f  i$ P# Q. T
  36. //        while(nms--)
    ( O! e! R% {2 d( A
  37. //                Delay_us(1000);0 b5 F/ Z5 |% ^
  38. //}  A7 i" ?* j  n. G4 I+ k4 s
  39. 0 n" n3 ^! w2 M& Q' ?& {
  40. void SysTick_Handler(void); A/ d8 @# c( l7 R) ]
  41. {$ r* O+ v: l- G% O5 v# u$ ]
  42. //        System_ms++;
    0 [% l8 r2 ?# d% p  |6 @1 b* C8 Y
  43. }
复制代码
' T8 D5 C! h% l( y( [
这里用到的延时us和ms函数都会调用获取时基函数,这样的话ms还好一点,到us级别就不是很准,再有就是滴答中断里面也有时基去计数,再低级的中断那也是中断,会打断前台程序的运行。
2 \- M5 h, s- {( A& o$ v0 W  e
% p  m# c( @6 G# c' c( r7 m; g改过之后的:
9 n5 g/ A" Q' u. f1 N5 A
0 I! A2 G( ^9 ^: @8 C2 T& [) ~& r( I
  1. void Systick_Configuration(void), ~# }4 n# {6 V1 z2 a( t' e6 A
  2. {
    # b% _; B3 M& g  U2 F
  3.     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //设置时钟源8分频
    . c, L) Q4 ]/ H8 F/ E; e
  4.     SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;            //使能中断
    ! S$ X9 t' k5 j' i- M( U
  5.     SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;             //开定时器0 Y# p0 C5 V  e4 h8 a1 y
  6.     SysTick->LOAD = 9;                                    //随意设置一个重装载值
    5 a" \6 O! L, l/ T7 J" k- s
  7. }5 @9 ~* r% b! Y" _9 z2 t
  8. 5 e# ?0 B- P2 D  \
  9. : Y- v( m% `7 t& F4 P
  10. void Delay_us(u32 xus)7 ]% X! W0 R$ ]* q) g" E
  11. {
    , Q) e; k' Y) @, X% m) F
  12.     SysTick->LOAD = 9 * xus; //计9次为1us,xus则重装载值要*9' V6 o2 Y% l6 v. [# L! Z$ a
  13.     SysTick->VAL = 0;        //计数器归零
    ! L6 Z% ]- I8 h% ~! s% P
  14.     while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待计数完成# S' g# \  _* l4 M' B: J1 {
  15. }$ y+ x1 B( T7 K5 N$ m1 z

  16. * @. O/ N: `0 ?+ P
  17. ; f) `2 L( P: j$ `/ C
  18. void Delay_ms(u32 xms)
    8 h' R" q  Z2 X/ a; l+ T
  19. {
    , \! p8 z3 c' L- q$ h) U, h: Q
  20.     SysTick->LOAD = 9000; //计9次为1us,1000次为1ms
    2 c5 t% E2 W) Y- m; ?  Y9 Q+ N
  21.     SysTick->VAL = 0;     //计数器归零
    : V( v* K4 h# u: h* G
  22.     while (xms--)
    ; B" K( q8 s3 M+ U  r
  23.     {
    & F: x0 e4 i7 {; f4 x& m
  24.         while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待单次计数完成
    / l3 _: B+ {( ^) F8 o: [
  25.     }
    6 i" `7 K" L* b! U* c! U
  26. }
复制代码

5 r; F. g9 T1 }3 I! [- @6 O这样的话中断依然打开,但是中断里是空的,其次延时函数也因为不调用获取时基直接判断寄存器而更加精确!, G- E# k" S: v6 X) h1 }- j1 T5 k1 p
. v- v5 h) f3 U+ Y
4 \% w, D( D' ?5 G
收藏 评论0 发布时间:2022-3-16 10:53

举报

0个回答

所属标签

相似技术帖

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