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

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

[复制链接]
STMCU小助手 发布时间:2022-3-16 10:53
红外接收头很常见,具体就不细说了,这里记录重点:5 t+ Z7 Z& ]; X! {6 A* W
  i" K" u: ]8 I% t. j
NEC的特征& G' i6 Z+ w* ?. e0 c0 T
1:使用38 kHz 载波频率+ T$ t- K2 V4 u7 [& F
2:引导码间隔是9 ms + 4.5 ms  p; ?! P3 G" \4 ~2 K$ t$ L* B7 y3 s# Z
3:使用16 位客户代码- E. \- N) n: ]' U
4:使用8 位数据代码和8 位取反的数据代码
* d) h. ]" [5 n, j: B
* ^, b& b0 v, N4 L. D1 b当发射器按键按下后,即有遥控码发出,所按的键不同遥控编码也不同。这种遥控码具有以下特征:采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”,其波形如图所示。- e9 A8 B( q- q7 Y+ g$ r: u
- }3 S9 p0 ?& \. s4 \; B. I

3 M6 k1 Y+ }$ U+ `1 K; O
. g" i, h6 k) ]9 ?; G AM]YYXBP7@ZX4]66N2G8`DT.png 6 v& y; E+ H2 Z6 d4 M
8 Y1 ]: ^$ U& {" b8 r

$ ?# I" A- q( H这里我采用同一个定时器TIM3的两个通道分别用来做红外的接收和发送,接收用通道3的输入捕获,发送用通道4的输出比较,程序采用分时复用的方式完成收发一体的实验效果。默认处于接收状态,在按键触发时切换成发射状态发送红外数据,发送完成后再次默认切换成接收。
% g: n3 @" m$ p+ ?2 p
- x8 z! _9 x! v- O$ z! V& L上接收部分的代码:& F% a9 J. s4 W/ \7 p: h
1 r6 B0 o- {# ]8 |; v' o4 ^1 D
  1. #define NEC_HEAD                (u16)(4500) & U0 B) o" l) U* \+ n4 e
  2. #define NEC_ZERO                 (u16)(560)0 z  c# [7 ?+ Y: C  [6 t! ~2 K
  3. #define NEC_ONE                        (u16)(1680): E( ?  }* r/ m- ^' H
  4. #define NEC_CONTINUE         (u16)(2500)  ?' O- n, B( I, @, A
  5. ; q6 N# Y, |1 r) B% E& M' J8 O
  6. #define NEC_HEAD_MIN (u16)(NEC_HEAD*0.8f)
    3 V6 Q9 J& i5 y  {* q( K0 P! t, E
  7. #define NEC_HEAD_MAX (u16)(NEC_HEAD*1.2f)
    ( k( C, k* d0 _$ V
  8. 7 u  ]  F  S! a
  9. #define NEC_ZERO_MIN (u16)(NEC_ZERO*0.8f)2 X# v4 t% I5 ], J
  10. #define NEC_ZERO_MAX (u16)(NEC_ZERO*1.2f)4 x: g  F3 y( q$ t
  11. 7 B8 x9 @9 J3 g8 l
  12. #define NEC_ONE_MIN (u16)(NEC_ONE*0.8f)
    ' m) Y1 n9 w  @% f+ Z% Q% [
  13. #define NEC_ONE_MAX (u16)(NEC_ONE*1.2f)
    ' Y+ B  |4 b: r
  14. 5 D7 V* x* _# o' i6 K6 J: ^/ w
  15. #define NEC_CONTINUE_MIN (u16)(NEC_CONTINUE*0.8f)/ B0 J8 b5 ^* c4 a  }3 ?% ]
  16. #define NEC_CONTINUE_MAX (u16)(NEC_CONTINUE*1.2f)+ }4 k( K2 @* t# {

  17. + z+ e4 s& m! N4 O( l

  18. % |: n! c8 d( e/ v- `# L
  19. //红外遥控接收初始化
    / x: N! H- c0 f: A) h/ |
  20. void NEC_RX_Configuration(void)
    4 @+ a6 @+ A. V, H
  21. {6 v0 D1 ?1 s$ w
  22.     GPIO_InitTypeDef GPIO_InitStructure;
    / U9 ~/ p4 \0 Z% q% f  ]& |
  23.     NVIC_InitTypeDef NVIC_InitStructure;
    . H# a0 [3 Y' Z( x7 k4 w/ W
  24.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;; B  X4 f, \2 K0 B1 {' {
  25.     TIM_ICInitTypeDef  TIM_ICInitStructure;//输入捕获
    " M) R0 Y; I5 F& z: {8 v) P3 {
  26.         
      j9 k5 W9 \! J8 v) _; g
  27.     RCC_APB2PeriphClockCmd(NEC_RX_RCC|NEC_TX_RCC,ENABLE); //使能PORT时钟' z8 K/ f! Z( y  s8 P& D
  28.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);         //TIM3 时钟使能
    & q7 j4 {. y2 h' @$ }. p9 B2 q
  29.         
    + Z5 m3 z6 Z8 I/ c" K: s/ m: @! n
  30.     GPIO_InitStructure.GPIO_Pin = NEC_RX_PIN;                        
    4 i6 q/ t' I/ d$ @( t
  31.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;                  // 上拉输入) Q5 }# G. s5 [# J+ ~4 p
  32.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    ! A' y6 Y/ z3 m5 v, I. I1 `4 z' e% p
  33.     GPIO_Init(NEC_RX_PORT, &GPIO_InitStructure);
    : s  z; _2 C+ D) v' x

  34. 9 Z2 N, D, w1 e7 J7 V( s! N
  35.     GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;                        
    ) F) |6 H! }2 c1 y1 I5 }
  36.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;          //复用推挽输出
    . a: ?& h- A9 b. ?; Y& N+ E% N; M" _
  37.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;; a# F2 K1 A- Y' [; m
  38.     GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);2 I3 _' z4 K, Z  p6 G5 t

  39. 2 @3 E# {+ N8 X; `4 W( V& R  S
  40.     TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出$ ^& B4 G4 r2 J; E8 R/ @' v
  41.     TIM_TimeBaseStructure.TIM_Prescaler =71;         //预分频器,1M的计数频率,1us加1.
    ; j# [! U6 J0 f! n
  42.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
    ' _; R: D/ W  \4 Q, e/ f: \  l
  43.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    5 @0 j$ ?1 G6 r4 \. {5 }; `
  44.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx% q; Q1 r( q3 j4 S. ^$ j' u. D

  45. 1 m/ X6 `3 z: ~! |3 D, ^$ \8 g  _5 b
  46.     TIM_ICInitStructure.TIM_Channel = TIM_Channel_3;  // 选择输入端 IC3映射到TI3上' U9 K& J! n" O! N% {+ @
  47.     TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;        //上升沿捕获
    8 j- @" N5 ~. Y4 J* q
  48.     TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;; H' _* w  O+ }; K
  49.     TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;         //配置输入分频,不分频1 S9 W3 _; @3 l3 N4 a* D
  50.     TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波* _9 ?4 p! ^6 {
  51.     TIM_ICInit(TIM3, &TIM_ICInitStructure);//初始化定时器输入捕获通道
    + J7 M! P6 e) {

  52. + h" A& K& ?0 B+ G; ^# ~
  53.     NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断8 b; H, g& }, _' I5 o/ F8 U
  54.     NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;  //先占优先级2级- \/ [( j9 ^3 h# x' a7 R  w* a# k
  55.     NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;  //从优先级/ p' x4 U3 R8 x. V7 e
  56.     NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能  {5 r; q, Y) \$ c
  57.     NVIC_Init(&NVIC_InitStructure);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
    ' O( s% ?: z+ B& e+ Q  I: ]

  58. 6 l9 ~- m5 F% Y9 |7 Z* Z
  59.     TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,ENABLE);//允许更新中断 ,允许CC3IE捕获中断2 M9 }: }( k, b& w9 G0 ^+ F! [
  60.         
    0 ~: f: b+ m/ o: o& K
  61.         TIM_ARRPreloadConfig(TIM3,ENABLE);        //重装载
    % S$ m5 A" E5 I1 Z
  62. & v: L$ o2 B. A2 j+ i: ^7 \
  63.     TIM_Cmd(TIM3,ENABLE );         //使能定时器3
    5 i" k4 \/ L6 C, @9 s
  64. }; x" b- B6 q2 T+ W6 m/ K1 \# B& |
  65. $ X4 k$ {- N5 c: w
  66. //遥控器接收状态6 `3 u% D- ^, }$ ?0 ?  _; _
  67. //[7]:收到了引导码标志, V6 S5 G; o1 A1 s* {* n5 ^
  68. //[6]:得到了一个按键的所有信息) c& m/ g. v# i) Z) L; i
  69. //[5]:保留
    3 v9 I$ F3 D! Y5 p1 q& X0 N
  70. //[4]:标记上升沿是否已经被捕获
    % \/ f( F) B+ j6 T, B' ~; g9 T7 M' q/ M
  71. //[3:0]:溢出计时器
    " f; g5 @3 ]8 T, Y2 Y
  72. u8         RmtSta=0;
    3 s5 d9 ^7 w2 _, Z- l: S# ?" k+ o) F
  73. u16 Dval;                //下降沿时计数器的值
    ' {) u) i" \% y
  74. u32 RmtRec=0;        //红外接收到的数据
    # o- ^/ G6 k9 |
  75. u8  RmtCnt=0;        //按键按下的次数9 ^  u/ K) f4 @$ E4 V1 L1 g
  76. //定时器4中断服务程序0 W1 C9 J7 r" j# `) ?; y
  77. void TIM3_IRQHandler(void)1 k! t1 V0 W* l! ]  v; @
  78. {
    ( f% }& @8 [- h3 u
  79.     if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET)        //更新中断
    - [, r3 J# l* F
  80.     {
    & V1 v% v1 G- o. ], f
  81.         if(RmtSta&(1<<7))                                                                //上次有数据被接收到了
    + l* k* L- r$ `) W
  82.         {* h& x, E* N, {2 t0 _
  83.             RmtSta&=~(1<<4);                                                        //取消上升沿已经被捕获标记1 h( t6 l8 T  M2 |( C
  84.             if((RmtSta&0X0F)==0X00)RmtSta|=1<<6;                //标记已经完成一次按键的键值信息采集' s3 |3 H+ o. k* Z9 w; V' }
  85.             if((RmtSta&0X0F)<14)RmtSta++;2 V1 N' O* |% j. d
  86.             else. T; a! O. K. X! D3 I
  87.             {
    + l2 {9 U1 F/ Z$ L  x" _
  88.                 RmtSta&=~(1<<7);                                        //清空引导标识- B( W( q# ?: b
  89.                 RmtSta&=0XF0;                                                //清空计数器
    9 E6 X' L+ k4 F- J$ ^% A
  90.             }  K0 r* ^- l2 s3 Z* O! n5 G$ F
  91.         }
    4 K3 X  k9 \/ k3 }
  92.     }# g3 K8 f4 k* P$ X7 M
  93.     if(TIM_GetITStatus(TIM3,TIM_IT_CC3)!=RESET)
    ! l( |! ?5 ?) B8 P; ]4 p
  94.     {  s1 G8 o6 `& H0 w
  95.         if(NEC_READ_RX())//上升沿捕获
    4 K  }0 Q7 V* O( \
  96.         {
    1 M6 ~: k" {6 {% j: V
  97.             TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Falling);                //CC3P=1        设置为下降沿捕获1 F3 H4 p1 o. b- n. V$ A! R
  98.             TIM_SetCounter(TIM3,0);                                        //清空定时器值. L0 n: g/ ]- s- S" U
  99.             RmtSta|=(1<<4);                                                        //标记上升沿已经被捕获/ L5 s/ B! I9 A2 b" {2 i5 |
  100.         }
    / ?2 v( y* w# P5 y- A( K. Y
  101.                 else //下降沿捕获, W+ j* [6 _4 x' |& ?
  102.         {
    * A) P: W  S  t( K7 f
  103.             Dval=TIM_GetCapture3(TIM3);                                //读取CCR3也可以清CC3IF标志位2 {/ M6 F  n' T* q3 K
  104.             TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Rising);                                //CC3P=0        设置为上升沿捕获( G' l8 E( s& [, x. k/ d* R
  105.             if(RmtSta&0X10)                                                        //完成一次高电平捕获* l4 |5 E) C5 E0 z$ j
  106.             {
    8 W3 D' X! f5 h& a
  107.                 if(RmtSta&(1<<7))//接收到了引导码( \+ G! O: m! I* q! K# u
  108.                 {
    # J; o6 X' b3 j$ r7 y
  109.                     if(Dval>NEC_ZERO_MIN && Dval<NEC_ZERO_MAX){                        //560为标准值,560us/ ~; [/ |0 T) }1 u$ }

  110. 0 I+ O1 A, B% o+ U0 E1 X: z$ E
  111.                         RmtRec<<=1;                                        //左移一位." n# u/ j2 P9 i5 F: T0 b
  112.                         RmtRec|=0;                                        //接收到0% p; A1 j% N2 n$ P0 G
  113.                     } 6 l: D; V2 ]/ p! j
  114.                                         else if(Dval>NEC_ONE_MIN && Dval<NEC_ONE_MAX){        //1680为标准值,1680us
    ' |5 n) a6 [- B' l0 i) @
  115. 3 N3 L; T1 |, Y$ s$ C
  116.                         RmtRec<<=1;                                        //左移一位.
    5 W5 U6 [. a5 r3 C
  117.                         RmtRec|=1;                                        //接收到1. u' U9 H0 q/ S
  118.                     }
    ( r' T/ ^* f$ }, w) `8 D2 y
  119.                                         else if(Dval>NEC_CONTINUE_MIN && Dval<NEC_CONTINUE_MAX){        //得到按键键值增加的信息 2500为标准值2.5ms9 m5 X1 _" X7 U, J0 P- q' C  H
  120. 2 G- b9 y- E" Y5 T  b" c! B+ d
  121.                         RmtCnt++;                                         //按键次数增加1次5 Y! s4 |; |' U+ {1 i
  122.                         RmtSta&=0XF0;                                //清空计时器
    6 S5 \  @& a, m( ^( x" |
  123.                     }+ l3 n1 o  J2 v  H: R. Q) C/ W
  124.                 } - o1 u; s- P4 ^6 _- F* @& p
  125.                                 else if(Dval>NEC_HEAD_MIN&&Dval<NEC_HEAD_MAX)                //4500为标准值4.5ms
    / T& I2 `% _4 y* ?! L. U' y
  126.                 {) X; Q  D5 {7 |# H; b% [
  127.                     RmtSta|=1<<7;                                        //标记成功接收到了引导码6 Y/ q6 Q+ I3 P* r' K& R5 L
  128.                     RmtCnt=0;                                                //清除按键次数计数器
    ' b; F* G$ q. T% K9 L! s5 u
  129.                 }
    : v! P( q9 p/ A5 k/ f( ?
  130.             }
    ' r8 @( O' ?' v3 ]5 P
  131.             RmtSta&=~(1<<4);. T5 i* X: G1 u+ a0 V3 w3 G; z
  132.         }
    % j, }9 L; E- T/ H( b& U* ?
  133.     }% A$ k6 p% h' u
  134.     TIM_ClearITPendingBit(TIM3,TIM_IT_Update|TIM_IT_CC3);0 {' G" `6 N+ ]  Q2 w+ c
  135. }
    2 a1 ~! l- Q7 Q( t1 B" K  M1 l
  136. 3 u1 e7 u9 @5 P# X9 z! V
  137. void NEC_GetValue(u16 *addr,u16 *value)
    : z8 z+ J% o- c) Q7 b$ k# }  f
  138. {
    . _* p4 ]- r! G$ T
  139.     u8 t1,t2;
    4 _# @9 Q- e+ c
  140.         *addr = 0;4 v, |/ C9 T) ^! |9 W: s' n; T
  141.         *value = 0;3 ~& S  d% _: @: l. }4 Y* h3 @
  142.     if(RmtSta&(1<<6)){                        //得到一个按键的所有信息了  k6 G# D6 N- X) y9 y. X4 c

  143. # w4 P" L7 T$ q# t* [8 i6 r
  144.         t1=RmtRec>>24;                        //得到地址码
    : V. B" U+ @( x" y+ @$ }6 Q' S4 d% w
  145.         t2=(RmtRec>>16)&0xff;        //得到地址反码
    % U' I" g/ \" y9 {0 u* ~( b
  146.         if(t1==(u8)~t2)        {                //检验遥控识别码(ID)及地址
    - Q, ~( D) P# h- b8 P

  147. : X' ~  Q, x7 w$ W3 h
  148.                         *addr = (t1<<8) | (t2);7 t9 B) a7 {$ ]" S, _5 |4 y
  149.             t1=RmtRec>>8;
    0 B$ s6 ]% l# ^/ r/ N
  150.             t2=RmtRec;
    7 {$ U  a/ \6 U% q: A( _; ~/ ?
  151.             if(t1==(u8)~t2){                        
    7 C# i. j1 ]+ p4 b/ E8 @1 n" R
  152.                                 *value = (t1<<8) | (t2);* q( f& u+ D1 O. ^8 Y8 P! R
  153.                         }2 G2 ?: z9 U, q- R
  154.                         else{$ `' ~  X; Q! |+ O# D1 O
  155.                                 *addr = 0;$ z( w* z/ O# f
  156.                                 *value=0;7 H/ k, n' ^; O* F
  157.                         }- _" a* c0 w* N! U/ H: z& r
  158.         }
    + m. a; {: Q. `( Z( V9 p3 b0 Y
  159.         if((*value==0)||((RmtSta&(1<<7))==0)){//按键数据错误/遥控已经没有按下了
    ( t& A* J3 m0 ?4 N* K
  160. 9 S% U5 v+ y# @
  161.             RmtSta&=~(1<<6);//清除接收到有效按键标识
      v" |) o9 r# B* N  Q9 q! L: S
  162.             RmtCnt=0;                //清除按键次数计数器
    # \# X5 A. A' x4 Q* K  t
  163.         }
    . `% t; _+ R, [% [" B
  164.     }. c& ^" `; E" K2 }7 ?" A
  165. }
复制代码

0 O. l5 _/ B  I* S8 N0 e接收部分在配置上比较简单,配置一个1us计数拼了,周期为10000(也就是10ms)的计数器,同时开启更新中断和捕获中断,+ }9 \. ~! g! i! m

6 P3 N6 d- d8 g( z在更新中断,和捕获中断里面分别对接数据进行处理,这里要提一下就是捕获中断分上升沿捕获和下降沿捕获,在捕获到某一个电平后需要更新一下捕获的中断源。在每一次捕获的同时对时间间隔进行判断,判断的结果有引导码、数据码、重复码等,这里就不多解释了。
) C7 @7 q, `- O( f& Z+ R! v  K! e8 `9 Q& s4 c& v
上发射部分代码:4 ~) p8 ^) p# g: e$ E* w' c

& ^, _3 ~  C4 n8 ^8 c+ i
  1. /****************************************************************************************/  w$ H- l4 a* o0 ^6 x" ?
  2. #define CARRIER_38KHz() TIM_SetCompare4(TIM3,9)  c. s% R9 @$ F( m
  3. #define NO_CARRIER()        TIM_SetCompare4(TIM3,0)
    3 h5 U$ O+ R1 n# Y3 W: A
  4. - p. X  `, J3 M' N7 h( W6 E
  5. void NEC_TX_Configuration(void)            //红外传感器接收头引脚初始化
    ( H7 |9 E9 ^; f, Y8 E: k" J3 S% f
  6. {" g. Q; \' S0 a; q
  7.     GPIO_InitTypeDef GPIO_InitStructure;
    - R0 r- E/ {2 A$ w! I; |8 Q- J

  8. 5 V* X" V9 N" e1 e6 \% o
  9.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    . N: A" u) N) b+ h, \
  10.         TIM_OCInitTypeDef  TIM_OCInitStructure;                                //输出比较        
    " R/ K3 B; n; t, q- F* O0 I$ z
  11.         3 x& l2 G2 s8 ?7 T2 q) z
  12.     RCC_APB2PeriphClockCmd(NEC_TX_RCC,ENABLE);                         //使能PORT时钟
    " o! q/ D; y" {" I/ q2 v; E
  13.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);        //TIM3 时钟使能; @8 T4 `7 b1 C1 N0 r$ Y: G

  14. * B  P# C9 Y- N3 |
  15.     GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;                         $ R8 {  h, J, b$ w& T
  16.     GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;         //复用推挽输出
    6 r+ I) D* i8 @' r( o
  17.     GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;, I9 L8 |) Q8 \% P  i) @% A/ v' }
  18.     GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);8 ^( V7 m1 f! ~4 P6 m
  19. : l: g7 X3 X+ ~1 q0 H
  20.         TIM_Cmd(TIM3,DISABLE);
    : M! m1 M" `- Z& V" T
  21.         TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,DISABLE);                        //关闭TIM3中断
    9 ~- _4 e" U9 }9 u

  22. 8 U9 P2 ?/ y' ?, a, c7 T
  23.     TIM_TimeBaseStructure.TIM_Period = 25;                                                         //设定计数器自动重装值 最大10ms溢出
    ) S0 V' U; I8 m& D1 u
  24.     TIM_TimeBaseStructure.TIM_Prescaler =71;                                                 //预分频器,1M的计数频率,1us加1./ n  \2 E! j  @: E
  25.     TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;                 //设置时钟分割:TDTS = Tck_tim& a; c9 I- @9 N. W  h
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;          //TIM向上计数模式1 K( r9 D  r. `7 K/ e5 W
  27.     TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); : I9 u: |6 N1 q5 P* ^- z, m( [" L, D

  28. : {1 f2 O6 w/ k  w& p! b& Q
  29.         TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;                          //设置PWM1模式
    ' W$ l* y0 C1 ^8 C1 `# C- a* b
  30.         TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
    , H, A- C# C2 `4 D' e
  31.         TIM_OCInitStructure.TIM_Pulse = 0;                                                            //设置捕获比较寄存器的值! g- S) Y1 }# B4 r9 G* l8 I
  32.         TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;          //设置有效电平为高电平
    9 ?& x' t: ^; ~" }: g- _
  33.         TIM_OC4Init(TIM3, &TIM_OCInitStructure);                                           //生效初始化设置& G& y4 a$ `& _- y3 i/ @
  34.         TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable);                          //使能输出比较预装载8 [2 g+ b9 ], g
  35.         
    4 w) _# l; A* a" Z& |
  36.         TIM_Cmd(TIM3,ENABLE);
    6 E2 I# e0 H9 Y9 e2 I# j& N  v* I
  37. }0 `/ \" a, L9 d, S/ w9 I
  38. ) G( y5 d! L4 X% J& ]
  39. static void NEC_Send_Head(void){" R4 f9 [9 {* C( P' s
  40.         CARRIER_38KHz();        //比较值为1/3载波1 H: s9 O5 \6 u4 W. [0 ^
  41.         Delay_us(9000);
    , {* p& T( [, g& z2 J) X
  42.         NO_CARRIER();        //不载波
    ( C. \- _9 q9 y% V/ Q) \
  43.         Delay_us(4500);
    5 d) c4 S) T, o, j6 F
  44. }" x$ z. Y7 w4 d7 f
  45. * d, P, L' q. C
  46. static void NEC_Send_BYTE(u8 value)
    8 R2 q- e3 i# Z. R& v
  47. {
    ( v0 n6 p! y2 D) I
  48.         u8 i;( A7 S, a8 m8 o
  49.         $ G& H0 ]" H; Q& z; H' c' k
  50.         for(i=0;i<8;i++)
    ! y1 m/ ^  V: ?# @" v& e
  51.         {% N$ M  F3 K6 v
  52.                 if( value & 0x80 ){
      \1 c  ^  G1 A6 y* ^2 n% {
  53.                         CARRIER_38KHz();        //比较值为1/3载波
    5 j5 `. a9 g3 c4 z; k: H
  54.                         Delay_us(560);+ |7 y7 s. V" V3 J
  55.                         NO_CARRIER();        //不载波  k4 C$ [% Z) t
  56.                         Delay_us(1680);; p- s4 P7 Q# N5 H
  57.                 }
    . Q; F+ }5 h- E/ p! z
  58.                 else{
    . K4 U: H$ J- h" a# T; ^
  59.                         CARRIER_38KHz();        //比较值为1/3载波& b. w& I& `$ x) M2 ?8 k/ k9 q0 o
  60.                         Delay_us(560);( [/ [# L; _  }: t# X
  61.                         NO_CARRIER();        //不载波  E* j  `5 y, I
  62.                         Delay_us(560);. c$ f% u1 E: t* p4 S/ O8 c) H
  63.                 }
    6 h2 C5 k* z) z# h: i) D7 r
  64.                 value<<=1;
    , e9 D- _2 k6 H/ R* W; B
  65.         }6 ^( ^! d# Y( l5 Y
  66. }
    . E5 y7 c3 V- E3 T9 I7 q

  67. / @& {; N8 ]8 g9 Y* K
  68. static void NEC_Send_Repeat(u8 repeatcnt)
    ; g/ t1 i; H; C9 j
  69. {# v3 ?, `. A, g
  70.         u8 i;3 A  m4 M! l9 S% Q2 d( R7 v$ @
  71.         3 ~4 i, [" z' `. s, h8 a# Y
  72.     if(repeatcnt==0)          //如果没有重复码就直接设置无载波,发射管进行空闲状态
    7 l5 E( p. J- m6 b
  73.     {( x. J2 a" E1 u2 T& t9 q
  74.         CARRIER_38KHz();. Q9 g& [! m) x( T9 `% I
  75.         Delay_us(560);
    ; c: k( X8 F: A% K. j( T
  76.         NO_CARRIER();     # F5 l. ]; W) ]4 _! j! _
  77.     }
    , i6 |! g: R. E: O
  78.         else
    4 C# ?1 x. A! i) C$ x9 b/ \
  79.     {/ N5 K8 Z5 z2 D  J9 h
  80.         for(i=0;i<repeatcnt;++i)
    * D  X3 f; S0 X
  81.         {
    * p3 ~- ]! D# Z  K
  82.             CARRIER_38KHz();
    + d. B- V9 R  E$ _$ W2 e6 J8 [
  83.             Delay_us(560);
    9 F6 e: {: Y' ?, z
  84.             NO_CARRIER();
    - O. I$ V; N9 [
  85.             Delay_ms(98);
    1 i5 S: @0 V1 \" W
  86.             CARRIER_38KHz();
    7 k. n# E  T0 `% d5 q7 K
  87.             Delay_us(9000);  
    % Z, @: h/ v7 Y  Y
  88.             NO_CARRIER();
    / w4 z$ w# E: A
  89.             Delay_us(2250);                     0 e+ G5 d. {& p$ Y  h+ L. m  `
  90.         }
    0 k; Q7 g" o1 h8 S, j" P% i
  91. % @( {, i2 p# M7 b
  92.                 CARRIER_38KHz();
    / n3 e, y4 C: w5 w8 U
  93.                 Delay_us(560);
    ! ^  T, a5 c  E, b0 w2 D1 O
  94.                 NO_CARRIER();
    6 V* i, o. H# [/ j& d
  95.     }+ [) s" l/ n7 D) B0 }3 f' ]' z+ n
  96. }
    $ o  W$ B6 j9 \# _& B
  97. 0 A  j2 @7 W0 @$ {+ I
  98. ' Y  z0 a% S, ^: {( E! E* z9 B
  99. void NEC_Send(u8 addr,u8 value,u8 cnt){* i" V$ i! e7 l/ ?6 \2 e3 H# _

  100. 4 Z0 R* l" L6 w) ~/ U
  101.         NEC_TX_Configuration();
    1 T( }4 m" p/ B3 O8 |/ S5 Z/ |
  102.         9 g( Y1 k0 @& T4 N7 L
  103.         NEC_Send_Head();                                //发送起始码
    # P( |; k. a1 X6 ?. y8 H! V
  104.         NEC_Send_BYTE(addr);                        //发送地址码H
    2 x. `+ {, D% P# O
  105.         NEC_Send_BYTE(~addr);                        //发送地址码L
    1 f) Y+ m6 ?) B0 B/ o
  106.         NEC_Send_BYTE(value);                        //发送命令码H! H- o& \+ K0 @1 H; b7 C2 h
  107.         NEC_Send_BYTE(~value);                        //发送命令码L2 ^; O: [0 _- \
  108.         NEC_Send_Repeat(cnt);                        //发送重复码        / I' R8 c, b7 e0 Y4 J1 h
  109. ' u. j) p! I  I! {. v
  110.         NEC_RX_Configuration();/ b4 e* E, }9 y: W
  111. }
复制代码
: X+ ~9 U$ L0 N* 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)/ ]: ^3 c0 V" t5 e7 S2 b/ {( Y6 J& V
  2. #define NO_CARRIER()        TIM_SetCompare4(TIM3,0)
复制代码

% ^2 b6 |* d  D载波的时候比较值设置的是9,没有载波的时候是0,9和26就约等于一个1/3的关系。
5 d! n4 _! r' z" q
$ u0 X' H# B. T% w5 L5 T最后就是发射部分,细节不说了,按照NEC协议进行发送,依次是引导码、地址码、地址反码、数据码、数据反码、重复码等。可以看到发射数据前后加了有NEC发送和NEC接收的配置,这里就是关键的分时部分,因为程序主要出于接收,在触发器(这里使用的按键)触发的时候才会发射红外数据。
/ A  D3 c5 o2 e* v
9 K# W& v. _$ S$ w上述就是NEC底层部分。上层应用也贴出来以供参考
+ h+ T# M9 S* S& q: U9 e' L
/ j' ^7 ^! H6 G2 P+ C3 Y
, e" j  L* z) W
  1.                 keyvalue = Key_Scan();5 M" }% ?2 O) N& u1 M! {# Q1 t) S
  2.                 if(keyvalue != KEY_NONE){8 \: ^# i2 H* C9 o4 k4 r
  3.                         NEC_Send(taddr,tvalue,0);
    ; F  H" t6 @, l# h4 x$ B7 ^
  4.                         taddr++;
    0 _, D/ k( e& d
  5.                         tvalue++;
    & x: k8 n4 h8 X  P% i1 I
  6.                 }
    ; B( I0 G8 Q- f" k7 K
  7.         7 y" `6 F+ ?5 z% [8 ~6 |
  8.                 NEC_GetValue(&addr,&value);
    5 n. {0 q, r; ?

  9. , h4 j# y' U/ V: h
  10.                 if((value>>8)){% x& ~" |9 ~$ ~3 W  a# j- t
  11.                         printf(" %04x,%04x \r\n",addr,value);
      X# ]- R  L4 y8 k, p
  12.                         printf(" addr: %d,key: %d,cnt: %d\r\n",(addr>>8),(value>>8),RmtCnt);8 A. |  j& f! A* C. \3 ]2 n4 M
  13.                         Oled_ShowNum(36,32,(addr>>8),3,8,16);
    " q8 s1 U2 p. i0 ~! F/ Z  y8 b
  14.                         Oled_ShowNum(96,32,(value>>8),3,8,16);
    + }/ A$ ]3 ~; V# d
  15.                         Oled_ShowNum(36,48,RmtCnt,3,8,16);" {- @# X* z4 G4 p. ~" P
  16.                         Oled_RefreshGram();7 `/ A3 g" M+ ^2 a* O. A
  17.                 }
    5 j' d8 S1 i: \( O. [# g- O. ?  m

  18. ) `* w( H- j- o. g  x) O; h* E
  19.                 Delay_ms(10);
    - e- }/ |' R8 P+ d% y5 g
  20.                 if(++t==50){
    8 y" N9 n: A' J) B2 U
  21.                         t=0;
    ! Z" s! f) D7 ~7 t) M/ Q' u
  22.                         Led_Tog();7 u7 I# o9 r/ t0 p
  23.                 }
复制代码
, V* ?5 U/ h3 R+ B0 q: m
最后要说一点的就是关于系统的延时,延时部分需要慎重,此处我也是踩了一坑,也一便记录下来。7 U1 a! l+ \* c9 e- N7 G

7 s3 J# \$ @8 B( p9 z8 E# I9 W+ A" Y原来的底层:
+ ~" t7 z4 k& z9 t3 H5 i: z" B6 |9 Q! g5 X
  1. //u32 System_ms=0;1 u/ j6 J1 q- L0 Y/ y( B

  2. , D9 u3 C* {% [$ T' e0 A
  3. //u32 usTicks;1 p% r, O6 M3 d$ ?  W- z! V2 \: \
  4. //void Systick_Configuration(void)
    8 n' Z! n% f! h* c+ V3 d' `
  5. //{
    # \# _# a8 \, B" s- I7 w4 W" u
  6. //        RCC_ClocksTypeDef RCC_ClocksStucture;, m% ]+ h# {2 k) b% M
  7. //        RCC_GetClocksFreq(&RCC_ClocksStucture);        1 `9 h" U+ n0 l: p
  8. //        usTicks = RCC_ClocksStucture.SYSCLK_Frequency/1000000;
    6 c. Y4 k$ t  S, K
  9. //        SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);3 f( P: o; f. i* O% Z4 ^8 s
  10. //        SysTick_Config(RCC_ClocksStucture.SYSCLK_Frequency/1000);
    : e# u- [+ m, e1 q$ |
  11. //}6 V7 a+ n6 B( c  E2 _. i: u
  12. 4 T3 r: U0 R' F5 s) O/ I- Y) O. c) |
  13. //u32 GetSystick_us(void)- u& S( ?* t1 }! X
  14. //{1 k0 Q" N3 q9 {/ ~; r8 M, O9 x
  15. //        register u32 ms,cycle_cnt;: F. u1 C. ]+ M" O( v
  16. //        do{
      v. A0 h8 @  C$ _; B
  17. //                ms = System_ms;4 M2 r6 ?+ G4 j+ A# e! r- E
  18. //                cycle_cnt = SysTick->VAL;: R; Y6 {6 ~3 M, d' s' p4 o
  19. //        }while(ms != System_ms);0 I- h. a2 {( r
  20. //        return (System_ms*1000) + (usTicks * 1000 - cycle_cnt) / usTicks;! j9 f9 y% C3 x4 c$ I
  21. //}( C( j1 |( Z% b% c5 L" Y/ b* X5 ^
  22. , j7 i) p; {9 D/ m
  23. //u32 GetSystick_ms(void)
    0 b2 r8 o0 U8 z& O' D. z5 L7 k
  24. //{
    , a) _3 R9 J8 ^* |
  25. //        return System_ms;
    0 G! W1 e3 f2 T% K2 s
  26. //}( B) F* N9 W: ?5 R3 M5 A: J8 l

  27. + q' j! |0 p, E: I. U( z. r6 e9 w
  28. //void Delay_us(u32 nus)% H( x9 |, A: v+ E/ e
  29. //{
    * @- F0 F3 t- O3 h5 q
  30. //        u32 now = GetSystick_us();
      P2 N, K* p, }% m- I2 i
  31. //        while(GetSystick_us() - now < nus);
    + ]# U& r% Z- N3 r; B
  32. //}% d2 }2 _8 O& z

  33. ' D) j1 ~7 ~- O5 O: q- r2 u( K
  34. //void Delay_ms(u32 nms)$ G+ S7 r; e( N# O0 ]1 x% n
  35. //{. E/ O0 y* q3 d! s. C9 e
  36. //        while(nms--)
    $ r6 v: G- @! o$ w# `3 t
  37. //                Delay_us(1000);5 A3 `. _) [4 ~! B7 ]" B* k9 @
  38. //}8 `5 c7 K2 Y7 x, d% E7 o
  39. 4 |8 P; b4 o) W2 l& o, K* o
  40. void SysTick_Handler(void)
    / y; l/ ?+ s' W7 J5 j! }% r
  41. {2 A' D4 d% D4 o+ |5 O6 q
  42. //        System_ms++;
    7 `& y. |+ g7 z/ r
  43. }
复制代码

8 [+ K4 C- e0 f) K- \. l这里用到的延时us和ms函数都会调用获取时基函数,这样的话ms还好一点,到us级别就不是很准,再有就是滴答中断里面也有时基去计数,再低级的中断那也是中断,会打断前台程序的运行。( {, h  O; W; a) E) O! q
7 y+ V  J$ p9 Y' o% G
改过之后的:  L, m7 A% Q4 w  U

' y: Z5 @9 \, }9 w# {
  1. void Systick_Configuration(void)
    * w1 I2 S1 j& B. C9 C. q
  2. {# P1 ?9 k, d/ ?1 S; N
  3.     SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //设置时钟源8分频$ J; ~0 M( N6 u7 n+ q: H9 e
  4.     SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk;            //使能中断; @; _- d6 n1 C4 e+ Y
  5.     SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;             //开定时器# E; p- w; b( A0 `$ M. \+ w, d% [
  6.     SysTick->LOAD = 9;                                    //随意设置一个重装载值9 y* |7 W: c9 M8 c7 p& w
  7. }/ P1 S+ X1 A4 q5 o; _; }) h5 N

  8. " G. d: d' j0 T# m8 T% ]5 U
  9. - ~: Q8 r% ?: ?$ N
  10. void Delay_us(u32 xus)
      @) B) k4 |' m5 s/ n' Y
  11. {
    3 |9 I+ E; V; h
  12.     SysTick->LOAD = 9 * xus; //计9次为1us,xus则重装载值要*9
    6 K7 ^# W6 E* ~
  13.     SysTick->VAL = 0;        //计数器归零8 c' P/ H8 Q. G: a# j2 \6 Z
  14.     while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待计数完成
    $ ~- Z+ {. Q+ u% W6 p8 i
  15. }7 t1 e( [. F, ^' W8 B
  16. 7 L0 {1 \7 U3 q% M

  17. 2 D, I$ m7 V! |+ b
  18. void Delay_ms(u32 xms)1 f, s  {& l  o: a
  19. {; q4 U- [3 ]) R) U
  20.     SysTick->LOAD = 9000; //计9次为1us,1000次为1ms
    ! w7 S- z- P" i; m3 r
  21.     SysTick->VAL = 0;     //计数器归零6 ^) ?9 \+ {" S. r0 W: J' `' p% x3 n
  22.     while (xms--)' K3 D! }6 _1 f" w+ A
  23.     {
    9 i+ @  x8 ?* [* z& f  ^/ u  T. t
  24.         while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待单次计数完成
    2 M- P! j  v" T4 Y
  25.     }2 Z& c6 P" Z, B; [4 T
  26. }
复制代码

3 N* p/ G! s7 s# U' i* `这样的话中断依然打开,但是中断里是空的,其次延时函数也因为不调用获取时基直接判断寄存器而更加精确!1 w6 V5 T* `* y0 v  W
* }4 O" |( p/ g2 s

9 R6 `* Q5 W5 P9 J
赞 收藏 评论0 发布时间:2022-3-16 10:53

举报

0个回答

所属标签

相似技术帖

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