红外接收头很常见,具体就不细说了,这里记录重点:
" ]4 I) F& t6 h/ r6 J$ s4 A! S$ G7 p; ^# n$ u% u ~( x
NEC的特征
; x8 |( @% f, F0 P1:使用38 kHz 载波频率' _5 J: b+ ]$ f4 G
2:引导码间隔是9 ms + 4.5 ms
, ~0 a" m' g) E3:使用16 位客户代码9 o! t V- D8 q4 J$ B3 N# F
4:使用8 位数据代码和8 位取反的数据代码
/ O& e! U/ Q* ^6 Z( N9 C0 Z
, F' B8 w! _1 L+ y$ Z6 F当发射器按键按下后,即有遥控码发出,所按的键不同遥控编码也不同。这种遥控码具有以下特征:采用脉宽调制的串行码,以脉宽为0.565ms、间隔0.56ms、周期为1.125ms的组合表示二进制的“0”;以脉宽为0.565ms、间隔1.685ms、周期为2.25ms的组合表示二进制的“1”,其波形如图所示。
7 @2 ]2 l0 t F8 K& W! _' l, o* x4 x+ k7 M( G
( _9 ?/ D' Z6 @/ F
7 f$ k; O4 `1 e7 f, O
5 c& X& d" \* J
% {+ a& I5 {' A! S' i) m {2 U, d) p! K& E: X* ~' Q- N
这里我采用同一个定时器TIM3的两个通道分别用来做红外的接收和发送,接收用通道3的输入捕获,发送用通道4的输出比较,程序采用分时复用的方式完成收发一体的实验效果。默认处于接收状态,在按键触发时切换成发射状态发送红外数据,发送完成后再次默认切换成接收。+ I8 Q6 {( S4 v8 a
; Q) y1 X+ C2 Y( O/ r
上接收部分的代码:
, k2 J/ \3 W2 c ]5 L8 p( `9 z7 U0 Q. |6 e7 F
- #define NEC_HEAD (u16)(4500) ! v' g1 Y: q6 p; A
- #define NEC_ZERO (u16)(560)$ R# A' u( n! s- w( e$ C3 A( W
- #define NEC_ONE (u16)(1680)
. q r$ [6 ?7 j; X* ^7 @1 K/ ]& y - #define NEC_CONTINUE (u16)(2500)
+ L A8 Y% E; u, z& I
. [% T% t3 l: N( N1 w- #define NEC_HEAD_MIN (u16)(NEC_HEAD*0.8f)' _. c$ y2 Y5 [+ K/ k! j
- #define NEC_HEAD_MAX (u16)(NEC_HEAD*1.2f)8 ~7 g. o# q4 I
- 2 o& b! K# w' {6 E2 z8 q1 p
- #define NEC_ZERO_MIN (u16)(NEC_ZERO*0.8f)+ A) D0 \( ~& _# s
- #define NEC_ZERO_MAX (u16)(NEC_ZERO*1.2f)
4 W1 X8 E( j. G; P. K9 p- u, j& e - 8 l/ s- `! C( t1 U3 i/ O
- #define NEC_ONE_MIN (u16)(NEC_ONE*0.8f)
7 H2 k5 u; t3 h) T/ G& e - #define NEC_ONE_MAX (u16)(NEC_ONE*1.2f)2 P& T2 i' N2 I
- ) N# {: `- Z4 X) r" t0 y/ c
- #define NEC_CONTINUE_MIN (u16)(NEC_CONTINUE*0.8f)& w7 D2 l; L+ t* C6 I
- #define NEC_CONTINUE_MAX (u16)(NEC_CONTINUE*1.2f)
* Y! N. w: v9 ~) M4 _' M4 s
6 [& x/ ~' E+ y5 O. y, C- # }- ?, t( T& P: s6 m/ X
- //红外遥控接收初始化
" c- E( I) V6 e2 f( l- ]6 X8 L - void NEC_RX_Configuration(void)
3 E( P0 l, X6 b4 @' K) s$ B# k - {8 _& w" B! g5 x0 ]+ s# p
- GPIO_InitTypeDef GPIO_InitStructure;4 S# P) `1 V. A; r; X7 M* F
- NVIC_InitTypeDef NVIC_InitStructure;
8 U, }" O0 ^, }; b - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
+ D- P4 v0 t) W( a - TIM_ICInitTypeDef TIM_ICInitStructure;//输入捕获
- o: d: X# @8 e. n% X - - P# v+ n& h( q. j4 }7 \
- RCC_APB2PeriphClockCmd(NEC_RX_RCC|NEC_TX_RCC,ENABLE); //使能PORT时钟3 W7 x* P9 G+ `4 q& K2 A# T
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //TIM3 时钟使能
0 G" V0 [. c7 u -
/ e6 n+ n; M2 p% G% y$ L - GPIO_InitStructure.GPIO_Pin = NEC_RX_PIN; ' S0 v- e# N* ^2 S1 ~6 s- C1 t, _
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 上拉输入
% ], N2 n' t0 \) n2 v# i: h: E - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;2 a8 \2 h! e, n
- GPIO_Init(NEC_RX_PORT, &GPIO_InitStructure);2 A" b' q6 U6 \
3 o/ s$ k; w6 m. _! X- GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;
3 N$ m0 G4 b- _, S - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
7 M- O. F8 O4 O - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;6 e- {- F( R v3 D$ r: }
- GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);
3 Z7 F1 X, t; y/ T. `7 @ - ' r; l J1 s/ S% v& O
- TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出9 z! _7 v0 R1 C* D2 `) M$ |+ o
- TIM_TimeBaseStructure.TIM_Prescaler =71; //预分频器,1M的计数频率,1us加1.( V; a& }' F0 ^& g; b# S
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim* n0 t6 l1 s; J0 H" ~; Y$ V4 y9 V6 w3 h
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
1 q4 b" ^4 {4 G* m( Y) E - TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx
7 N. {0 O7 I& v9 _' J - # j& C/ j1 t& z: A S
- TIM_ICInitStructure.TIM_Channel = TIM_Channel_3; // 选择输入端 IC3映射到TI3上. m, |9 ~/ _$ c n
- TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获 S) m- [# d, R0 g% w: o) `/ h2 \
- TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;, @( j1 S5 f2 t, P' p
- TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频& L9 O6 H" ^% b* J4 R1 A
- TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波
1 ?5 t6 r4 j" q9 A, ~6 L - TIM_ICInit(TIM3, &TIM_ICInitStructure);//初始化定时器输入捕获通道
* o5 Z- Y/ j. t1 p Z - & |8 h. ~( L4 g7 e. j( ]
- NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断+ ~7 r4 `% {- u
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级2级
! D9 G+ ]' Q, W6 x. @, }* m0 [ - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级" x% H4 J: I0 D6 ?- h4 o
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
4 _+ Y/ r P) ^4 Q7 K3 {( N - NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
" y, M5 x" _9 { - & ?$ u# C0 N# b- a/ T9 X! L
- TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,ENABLE);//允许更新中断 ,允许CC3IE捕获中断1 G: `: l' O2 ]) o) k5 o
-
6 T k& {! s5 i - TIM_ARRPreloadConfig(TIM3,ENABLE); //重装载$ ~ y, b9 w" a4 R
- 1 a( b) H6 r: t/ f/ j9 [0 w
- TIM_Cmd(TIM3,ENABLE ); //使能定时器38 c6 V. n$ [. J" e# X& @
- }
% n' _$ s) l9 R$ ^" ]3 |
2 D6 L. R: H+ Q+ U# Z, `- //遥控器接收状态' N) z( z& i/ N R0 N' c8 `
- //[7]:收到了引导码标志/ f, \6 g) S, k: R5 F( i
- //[6]:得到了一个按键的所有信息
/ L9 L0 D% y3 ?; U - //[5]:保留! |) z6 a! z: E5 o0 [
- //[4]:标记上升沿是否已经被捕获0 `% G% V3 J# l7 D5 t/ `( i$ F
- //[3:0]:溢出计时器( V: A, v5 p* Q" N- k
- u8 RmtSta=0;/ N- c- p- y( d- x7 H1 W: R
- u16 Dval; //下降沿时计数器的值7 d4 i8 j2 n8 _2 d7 s- y
- u32 RmtRec=0; //红外接收到的数据
4 [9 ]- [5 e a/ K - u8 RmtCnt=0; //按键按下的次数$ {2 `7 z5 V2 G8 d% Y- q, `
- //定时器4中断服务程序9 k9 t: D% h. {4 E; O, i2 a* U- I; W
- void TIM3_IRQHandler(void)
7 J" ~+ y3 j5 `& x+ z, J - {) Q5 n6 j8 U4 |9 h. E! o8 E- o* ]
- if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET) //更新中断
# O- k4 T4 F' Y, O% M7 C! I1 o - {7 F9 C7 q7 |; G
- if(RmtSta&(1<<7)) //上次有数据被接收到了$ o6 I8 D. D9 N' _# { T" }& a& b
- {; R5 q- S5 U$ V, F3 o5 k% }4 X
- RmtSta&=~(1<<4); //取消上升沿已经被捕获标记
1 ~/ O) O/ w( v$ Z - if((RmtSta&0X0F)==0X00)RmtSta|=1<<6; //标记已经完成一次按键的键值信息采集8 P0 P1 I, E8 u' \" i
- if((RmtSta&0X0F)<14)RmtSta++;; l/ D8 P$ }, |5 p f; _
- else) Y& s$ I8 p* g |9 _3 g' }" A
- {
2 S; J: Y2 I1 r. }" H% A" g/ E - RmtSta&=~(1<<7); //清空引导标识 j4 E' i) G; D
- RmtSta&=0XF0; //清空计数器1 {3 P8 k. r& X
- }
$ ~( {" I3 u/ x- w1 A$ F9 W - }
C+ F3 g/ B& y/ K6 v' A' U# w% @ - }
8 B# h+ z2 v$ A - if(TIM_GetITStatus(TIM3,TIM_IT_CC3)!=RESET)5 Y* @4 m& [% C d
- {
9 |6 K/ a( r3 k; }) }, k! k+ }6 b: r6 { - if(NEC_READ_RX())//上升沿捕获
/ T% f3 S: M, e! L+ N - {% A& o8 r* M0 X
- TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Falling); //CC3P=1 设置为下降沿捕获
/ e% P8 S# o, P6 K4 R5 s3 G7 ` - TIM_SetCounter(TIM3,0); //清空定时器值* d# c! `5 y4 j" B1 m% U
- RmtSta|=(1<<4); //标记上升沿已经被捕获4 @( _3 }" e9 \, p
- } 7 I' v9 J& u0 d0 M* Y# H* o( Y$ ~
- else //下降沿捕获/ J) h+ j9 f+ E5 O# H8 u
- {. L! c( O% f0 u2 ?/ m0 w" f4 j
- Dval=TIM_GetCapture3(TIM3); //读取CCR3也可以清CC3IF标志位
6 h8 b9 ?$ S+ n1 C- A2 r - TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Rising); //CC3P=0 设置为上升沿捕获
n; l, `8 o- b- x/ F6 R) G - if(RmtSta&0X10) //完成一次高电平捕获
1 K* e2 m, z% D5 @ - {
; e2 G7 f% M' c0 ]: F - if(RmtSta&(1<<7))//接收到了引导码. \* s4 I, W' U [% D( K
- {# a" o I6 c! f0 o: Z( q2 J
- if(Dval>NEC_ZERO_MIN && Dval<NEC_ZERO_MAX){ //560为标准值,560us$ n7 J/ Y& `5 Y ]! I& `0 ?- @: H
- # s4 s z8 n1 V$ a- t; ^. ^
- RmtRec<<=1; //左移一位.
: b! u4 x% g# S4 P/ ~/ k - RmtRec|=0; //接收到05 M+ j# Q" r0 f& Q# @! Y _
- } ( b) q ]/ {: g' z) Z
- else if(Dval>NEC_ONE_MIN && Dval<NEC_ONE_MAX){ //1680为标准值,1680us
9 _0 t2 q D% l: r - 2 O$ S) k7 s) X, G# h" ?8 E1 q
- RmtRec<<=1; //左移一位. p. |8 U7 w* J1 h: @
- RmtRec|=1; //接收到1. M& w- E, O8 E4 h# j
- } + D& s0 z% A0 [$ F
- else if(Dval>NEC_CONTINUE_MIN && Dval<NEC_CONTINUE_MAX){ //得到按键键值增加的信息 2500为标准值2.5ms0 S5 R) Q5 d# H" N5 `& m; v
- . w1 ]# G) A7 E% q7 a. l: h
- RmtCnt++; //按键次数增加1次
! \. ^ ^( V5 D: G. |+ S/ C- A' }7 J - RmtSta&=0XF0; //清空计时器5 Y: K3 u" i# P; [$ B4 _6 D6 |! ]
- }
5 L, ^" Q( X Z - } 9 F( [& u: C g% i3 N
- else if(Dval>NEC_HEAD_MIN&&Dval<NEC_HEAD_MAX) //4500为标准值4.5ms
1 h+ j3 n/ l3 u - {3 D3 X( h( S/ H8 `; E+ F0 M$ Z
- RmtSta|=1<<7; //标记成功接收到了引导码
, Y) m- k6 \3 J: ~4 \ - RmtCnt=0; //清除按键次数计数器
$ v m' L; R/ N& }0 g - }7 ]# ?3 |$ I9 Y9 G, n
- }# f( A9 O9 N; i0 ^; z, V4 Y1 R: D) `
- RmtSta&=~(1<<4);3 Q2 `5 v' C% ^' i) f: {) @
- }
+ L F$ X' U# \1 k$ g$ j, t* n/ ~6 m$ K - }& z4 ?- b1 C/ ?" m4 p2 W
- TIM_ClearITPendingBit(TIM3,TIM_IT_Update|TIM_IT_CC3);6 Z- ]3 P; @! ~ U2 ] B8 T
- }
4 j4 I: A* a! e/ z - ) B& R% U# M$ q) q) o6 N% l2 w8 u3 i: G
- void NEC_GetValue(u16 *addr,u16 *value)
* e7 ^# u$ g) [- G8 m5 A4 q - {
, P/ Z# j0 T; ~5 b- i% q - u8 t1,t2;
: `# ` j9 I) S% [, s! N: O. o - *addr = 0;* w6 o6 x+ E7 K+ t" r, r
- *value = 0;
9 r0 M5 }' u' q - if(RmtSta&(1<<6)){ //得到一个按键的所有信息了
8 |# i5 ]+ L/ P6 b; p( V8 v+ V$ } - $ B8 d2 B) L X7 d% _
- t1=RmtRec>>24; //得到地址码 M2 ], r; c7 L: |+ ?, ?
- t2=(RmtRec>>16)&0xff; //得到地址反码
2 J0 ~9 j+ C6 Y - if(t1==(u8)~t2) { //检验遥控识别码(ID)及地址5 D* s. J( C, e0 n
0 t3 T: J' w4 k- *addr = (t1<<8) | (t2);/ ]6 ~& d, x! j2 E/ V, q
- t1=RmtRec>>8;3 X9 x4 W+ ~- h1 a# v( p
- t2=RmtRec;' T1 q e3 g9 Z; V5 T2 h& D; r
- if(t1==(u8)~t2){
+ T* Y& E/ g0 m+ S - *value = (t1<<8) | (t2);
2 j. ~ f4 q# d, S, c& W& ] - }% n" ~* P2 [+ t+ L2 R: p
- else{/ ^- R ?4 U) ^+ w2 V; E% l5 ~* k
- *addr = 0;7 `: r8 D- n5 [" `- z9 f: p) e. O
- *value=0;1 P: s1 i# ^8 G& p( o# ^8 T
- }
7 O N6 h9 ]: e. \7 M6 H - }
/ ]$ \; C& E( a9 X. u- @ - if((*value==0)||((RmtSta&(1<<7))==0)){//按键数据错误/遥控已经没有按下了/ `: [- t3 E* ?- d
- 2 B5 R1 I: T7 E/ A
- RmtSta&=~(1<<6);//清除接收到有效按键标识
2 k+ B( j5 U& ^; d8 Q1 D( G - RmtCnt=0; //清除按键次数计数器
0 S+ s" M2 @8 p4 H* O0 [ - }
4 {5 N8 O' L; G, ?' ]! b- H0 c - }& r- A. o* b3 ?, i# H
- }
复制代码 5 {* [" ]/ ]1 ~) |
接收部分在配置上比较简单,配置一个1us计数拼了,周期为10000(也就是10ms)的计数器,同时开启更新中断和捕获中断,4 D5 W. Y* V* t6 d+ b
1 O7 N- ~# [, Z* }. _# j9 v
在更新中断,和捕获中断里面分别对接数据进行处理,这里要提一下就是捕获中断分上升沿捕获和下降沿捕获,在捕获到某一个电平后需要更新一下捕获的中断源。在每一次捕获的同时对时间间隔进行判断,判断的结果有引导码、数据码、重复码等,这里就不多解释了。1 }2 f% i" x8 O7 `! F
* U; l, O: u2 Q8 ~& _% D) ^8 J- n上发射部分代码:6 W- ^( p4 r: x* o) T) H# G, \+ M
) m. ]8 a7 U: K4 b8 K
- /****************************************************************************************/& |' ~1 ]$ ?% A% i6 V4 F
- #define CARRIER_38KHz() TIM_SetCompare4(TIM3,9)% v0 {& r( f) P
- #define NO_CARRIER() TIM_SetCompare4(TIM3,0)
8 J" q4 m; e& O4 I+ J - 0 `3 _8 R: ^' N/ Q& _1 {1 G
- void NEC_TX_Configuration(void) //红外传感器接收头引脚初始化" U' J& S/ W' ~- l) Q
- {1 }; l5 r0 x9 f8 k( h: g
- GPIO_InitTypeDef GPIO_InitStructure;
% Q8 V( n& d5 U3 R; U
8 V" }. p. R1 ~9 ^3 Z( D, O- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;( b9 w0 ^/ V: f9 w5 C5 ^
- TIM_OCInitTypeDef TIM_OCInitStructure; //输出比较
! d) q. a* ] U* _- M - * @4 C4 S& y+ c' A
- RCC_APB2PeriphClockCmd(NEC_TX_RCC,ENABLE); //使能PORT时钟& Y7 b6 s+ {. d4 u
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //TIM3 时钟使能1 W( f ^6 ]" [; }: p1 ~
" b+ X, B: z3 @- GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN; ' f- O4 p: r7 ^ J' ^( }
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出, V% a/ e/ h3 H
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
+ _) M# e! ~8 H - GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);
4 A6 o) K1 j. E' p1 ]7 @* d+ H
% Y5 A! ?' n! R2 S% Z0 y- TIM_Cmd(TIM3,DISABLE);
, z! e' H4 p$ ] - TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,DISABLE); //关闭TIM3中断
. D% }$ b. H6 T5 m0 H& n$ @ - + D% I1 A8 H$ V( H; `- D$ L1 `" e
- TIM_TimeBaseStructure.TIM_Period = 25; //设定计数器自动重装值 最大10ms溢出
" A& D P3 d1 F. V* E - TIM_TimeBaseStructure.TIM_Prescaler =71; //预分频器,1M的计数频率,1us加1.
# d( ] W8 U5 O T& ` - TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
$ v6 b7 k1 ^! k% y+ @7 i/ \3 n& h - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式9 q9 e* C+ V, D* G+ y
- TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); * T$ l8 T0 l5 i5 T* l/ N+ y
- s+ R. N! A0 M7 G5 C, i1 ~
- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //设置PWM1模式
( J3 b% z8 }+ u( ` - TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能5 ]4 l- o% O1 s# Z. u9 Q/ r
- TIM_OCInitStructure.TIM_Pulse = 0; //设置捕获比较寄存器的值8 X: \9 I% {" y4 p/ X$ s
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //设置有效电平为高电平4 X4 v$ G$ i1 G0 { A& F6 B/ K
- TIM_OC4Init(TIM3, &TIM_OCInitStructure); //生效初始化设置0 J, b2 I/ U0 {5 E
- TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能输出比较预装载1 B+ `) g6 x4 ]5 T
- 5 W7 t: h/ g( j j" u3 `
- TIM_Cmd(TIM3,ENABLE);9 b4 X2 w$ D7 i. |- C% C
- }2 `- d$ j R2 e7 M7 z' ~; W
# E. l' c& T" K1 y( R1 V5 W0 {9 \& r- static void NEC_Send_Head(void){. T! Q0 K, o7 c" a t+ g. F% Q
- CARRIER_38KHz(); //比较值为1/3载波. P% ~! | m, p5 k. @
- Delay_us(9000);
# D5 H7 n. T9 w" `' Y- A - NO_CARRIER(); //不载波4 d3 N! P7 H' i" R. t
- Delay_us(4500);! g6 e8 n% P0 q, L6 y
- }
6 @. t+ o: b: y. v! y - ! S I5 G6 s+ J) N
- static void NEC_Send_BYTE(u8 value)
/ t4 d2 ]. F' N5 m7 ~ - {3 X9 M' A0 K1 ]# f8 ?4 \2 {' l
- u8 i;# ]; z3 t( g4 T ^7 h8 V( d/ {* |
- 2 i, h( B7 }/ w6 h# z$ S8 }
- for(i=0;i<8;i++)% |6 f$ x( W$ [3 B0 X3 S( t
- {; o( ? d. b X/ V
- if( value & 0x80 ){ @, |0 @3 m* J# q% a
- CARRIER_38KHz(); //比较值为1/3载波" Y8 E+ H# R$ \9 b/ P8 o
- Delay_us(560);
9 J" x2 e i' Q+ D* p - NO_CARRIER(); //不载波1 c/ [, I2 U9 j. i- j
- Delay_us(1680);
9 }# [$ W7 x: H4 g - }
. s1 y6 r" w. `3 m$ b) h - else{2 A: Q; Z8 Q9 ?
- CARRIER_38KHz(); //比较值为1/3载波4 v8 J: m8 K. L, Y, d. u5 q
- Delay_us(560);
# x' J& m, T! @2 Z0 ^4 j5 C# J - NO_CARRIER(); //不载波
' E2 T6 j& b+ E5 @ - Delay_us(560);. Z# c: w8 I& @% E/ Y0 _# H: B! {
- }
5 `2 u/ ?: A2 W6 ~6 C( [ - value<<=1;
# e' p+ V# u/ f; C$ Z - }
- ]4 ?' I) n" ^ n( p, V$ [( q - }
0 E" M1 Y4 X$ L7 X1 B& C3 j - 4 A$ H' V; P9 t
- static void NEC_Send_Repeat(u8 repeatcnt), a9 T3 w0 {2 d- Z
- {
. g1 F4 U! B6 w, m+ Q- \ - u8 i;9 J# ]) v5 m% a' x
- * H* _$ T8 E1 h/ O4 o
- if(repeatcnt==0) //如果没有重复码就直接设置无载波,发射管进行空闲状态
( j$ s: y) P: W, {1 o& |3 F) |3 c - {
2 u8 p: _; v5 v: C4 e: X - CARRIER_38KHz();; W9 I8 D/ U& d! I9 R
- Delay_us(560); i" R4 D4 b2 X3 G4 [7 H
- NO_CARRIER(); \0 |) q3 }! K9 ~* K+ z
- }
& @0 m% p- g% c6 E - else - F3 G5 Z. I% s
- {: B/ S' b. o2 q- `1 C. T
- for(i=0;i<repeatcnt;++i)
* a Q. @) D. M' p1 D - {& l; f( H' y# j
- CARRIER_38KHz();, ]0 a! P* D, D& ]7 U
- Delay_us(560);
@7 Y w6 U/ f$ Q/ m! ~1 Z - NO_CARRIER();
8 j& Z4 o0 Q/ }7 T3 e7 U8 | - Delay_ms(98);
, y6 c! G5 a- k, x+ n) H - CARRIER_38KHz();( K' P( \/ c" M$ V% t4 b7 u0 X _
- Delay_us(9000); 1 T) e: k) [* y- _
- NO_CARRIER();
. J: o7 E! V6 q4 C# p4 Y - Delay_us(2250); $ u) ~( U5 p7 b+ C1 d
- }
. j5 S2 K- q% Z' d& I0 V
6 ^$ q U- o+ c8 `- CARRIER_38KHz();
# k" ?( S+ v. Z" E$ _' H& I; G- V - Delay_us(560);0 w" E& r4 F. h3 {
- NO_CARRIER();
8 v) N* I' z/ o7 N7 E8 D$ |4 i - }: O) ]: y' i5 h0 }" `0 _
- }3 x5 g3 N# ~- o- c7 \+ K1 }* F
% l% C" p# _& F5 R r
3 D; l+ M' r' Z# t$ @- void NEC_Send(u8 addr,u8 value,u8 cnt){/ p7 n# x2 U2 K5 j6 F0 x
1 ~- P0 J8 h9 D- NEC_TX_Configuration();
# @2 L" m# W! ]3 l: |- t3 e - ( R* }6 Z: N5 S8 B6 l4 I& w `7 }
- NEC_Send_Head(); //发送起始码' {5 x5 X6 v5 T6 r3 w) Z8 x
- NEC_Send_BYTE(addr); //发送地址码H
0 K) w" o3 B) z - NEC_Send_BYTE(~addr); //发送地址码L# ]/ @, p( P8 d2 E& h2 n
- NEC_Send_BYTE(value); //发送命令码H! V( a: W+ D" k# ?% s' n! K
- NEC_Send_BYTE(~value); //发送命令码L6 h }" [) \) x8 f- m$ c% [5 G- w
- NEC_Send_Repeat(cnt); //发送重复码
6 R7 d4 ~- \" L2 L, p
D& U0 p+ _5 p1 Z" W9 V! T- NEC_RX_Configuration();( _* ~( w0 ?$ Z. |
- }
复制代码
0 d* { e) d8 u9 ]8 d, B/ a发送部分首先是重新配置定时器3,在配置里面计数频率依然是1us,也就是1MHz,计数周期修改成了25,也就是26个周期(0也算一个),这样算下来的话1MHz / 26 ≈ 38.4615KHz,这个跟红外接收差不多能匹配,当然红外接收头这里用的是38K的,所以配置成38K的,市面上也有不是38K的,比如36K,40K等。然后是载波,载波这里用的是差不多1/3的载波,从哪里可以体现呢?可以看到有这样一个宏定义:- #define CARRIER_38KHz() TIM_SetCompare4(TIM3,9)5 h6 w7 d' l3 [
- #define NO_CARRIER() TIM_SetCompare4(TIM3,0)
复制代码 3 V, y7 S6 J7 S
载波的时候比较值设置的是9,没有载波的时候是0,9和26就约等于一个1/3的关系。! e% j7 v2 d9 q2 q' x
% j, n" x& i4 U" E5 F5 Y3 h" t# E
最后就是发射部分,细节不说了,按照NEC协议进行发送,依次是引导码、地址码、地址反码、数据码、数据反码、重复码等。可以看到发射数据前后加了有NEC发送和NEC接收的配置,这里就是关键的分时部分,因为程序主要出于接收,在触发器(这里使用的按键)触发的时候才会发射红外数据。
+ M3 Q4 A& a1 l/ \2 p0 F. m
" T4 s; i9 C4 y/ g6 @上述就是NEC底层部分。上层应用也贴出来以供参考5 L, R7 ^1 g$ Z1 R! f$ ?4 B' q6 L
" x5 g7 z2 M9 C9 M# i
- X. K% o/ P% I5 U6 S& x$ O. @
- keyvalue = Key_Scan();
7 m3 d! K% K [9 @: r0 `2 Q7 z& } - if(keyvalue != KEY_NONE){. B0 i0 J* t: a, V, k; k4 T
- NEC_Send(taddr,tvalue,0);
_% `$ f# `. S! @' d8 F) F6 O6 G9 v - taddr++;# Z+ q8 D( c7 O! R
- tvalue++;
0 j+ H4 E6 T4 u" n; n1 b, Z - }3 _: L9 S" h3 `4 O( j5 \+ \' t
- 9 t) X5 C" V8 k* N& S, D& U
- NEC_GetValue(&addr,&value);
8 B1 W3 _! Q3 X: y% s1 W" T% M
2 L, U; T4 f9 c; r) j' K- if((value>>8)){" s# ^' S& ^$ z5 M! ~2 g4 s
- printf(" %04x,%04x \r\n",addr,value);
$ U: g0 Z2 X1 M' B1 W( W8 f - printf(" addr: %d,key: %d,cnt: %d\r\n",(addr>>8),(value>>8),RmtCnt);& f2 A7 Y! g2 v' j* I2 ?
- Oled_ShowNum(36,32,(addr>>8),3,8,16);+ F5 l1 K8 n: D
- Oled_ShowNum(96,32,(value>>8),3,8,16);
% I8 g) D& l5 r" |. Q, Y5 T - Oled_ShowNum(36,48,RmtCnt,3,8,16);
% [/ n6 t# t+ d - Oled_RefreshGram();! M1 I$ C& R4 b8 q2 `* v0 m
- }/ ?+ x8 M4 F- m" m' }' q* s: t
4 W$ v& ^" ~+ R- Delay_ms(10);! @5 w+ n3 V. K. R/ Y
- if(++t==50){: l% X: r4 \5 P1 I4 I
- t=0;, r( b8 u1 K( n( d' L( l9 `
- Led_Tog();" O" m4 Z( q! q% T! w- U+ w) V, X
- }
复制代码 & Z7 p t& P! l4 K9 N
最后要说一点的就是关于系统的延时,延时部分需要慎重,此处我也是踩了一坑,也一便记录下来。8 R4 K& y' w4 A# \
4 e% j+ [: `, w原来的底层:* x2 E7 M& n% u- F
# \0 g2 [% a( w
- //u32 System_ms=0;7 e/ S( h% s$ U2 K: k& X6 z
- ' i b3 i- x! k% l3 k
- //u32 usTicks;
( M# Q+ ?1 Y- d( x# v' Q - //void Systick_Configuration(void)
* {8 b0 b! A; e. G9 ]) H - //{
$ j+ P2 @; s& X+ Y - // RCC_ClocksTypeDef RCC_ClocksStucture;
4 n0 [$ K# T9 ?# Y' D9 p - // RCC_GetClocksFreq(&RCC_ClocksStucture);
* @+ S6 C g$ v - // usTicks = RCC_ClocksStucture.SYSCLK_Frequency/1000000;' A2 s3 W7 e2 E( m8 n( m
- // SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);4 s- m5 r& T- r: X) |" e- s
- // SysTick_Config(RCC_ClocksStucture.SYSCLK_Frequency/1000);
( j2 S/ {8 ^$ q - //}1 |( v1 d1 Z' v9 ~9 T9 w7 `
- . n+ I' i( x7 |/ i
- //u32 GetSystick_us(void)7 l9 R3 ^2 R7 ~, @; K" `( D+ I
- //{
N8 U/ _) ?& B2 i" E - // register u32 ms,cycle_cnt;
1 }% w1 K" ~. ? - // do{
; q, s9 N0 S5 g9 h4 d" o - // ms = System_ms;
) q- j) p- o- \ - // cycle_cnt = SysTick->VAL;4 l- N7 Z, A Y/ X' a! V
- // }while(ms != System_ms);9 S. d) A- r7 g5 C U6 I' Y
- // return (System_ms*1000) + (usTicks * 1000 - cycle_cnt) / usTicks;
8 [1 ^5 s/ d5 F# T0 w - //}, L) g8 g5 o9 C. ?9 T7 u
- 6 O8 c. Q) J- I& G6 K
- //u32 GetSystick_ms(void)
~! Y. g, T/ ~. V# |1 B - //{
1 Z- z" u. ]0 D2 m6 ^ - // return System_ms;
5 p" n" c# V2 I3 Z0 T: e - //}& H! V. ^5 F! }" [& X0 n
- . j% x, U! o" P3 W. P0 L
- //void Delay_us(u32 nus)
, x4 l" u+ D; o, o - //{
0 i: G1 R6 Z( q8 h, \ - // u32 now = GetSystick_us();! g' [9 `% H% C$ J4 ?; a! `' @
- // while(GetSystick_us() - now < nus);& G9 i" H* f* }
- //}; P1 Q* M r9 k
% M7 X8 X: [$ G/ y# V- a- //void Delay_ms(u32 nms)! z$ T( _$ I( X9 a2 q1 {5 ~5 t
- //{5 `% r' B; Y6 m4 k3 x
- // while(nms--)- H- S( ~, I- S6 J6 l/ C
- // Delay_us(1000);! m" _. e+ Z* u5 V( ?
- //}1 {$ l, t9 r9 w2 U& A2 v# l) v
9 W j+ S& e/ w0 O- void SysTick_Handler(void)
+ _8 z7 Q5 v/ ]0 J - {
1 B9 s8 O( f- x1 p0 H2 X6 e - // System_ms++;
9 v) w+ p) E' k - }
复制代码 - U( k/ }/ q! c0 c
这里用到的延时us和ms函数都会调用获取时基函数,这样的话ms还好一点,到us级别就不是很准,再有就是滴答中断里面也有时基去计数,再低级的中断那也是中断,会打断前台程序的运行。
. l! J' Q' V+ A$ V V0 r
6 @1 l/ @6 A% W' O4 W- P5 ]改过之后的:. m9 E$ ], J+ _( i- ]
( i' U1 v! U8 i2 _- V- void Systick_Configuration(void)7 w6 O ^9 _* Z0 \/ C& o$ W
- {0 K N3 {' Y. x& v/ o
- SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //设置时钟源8分频7 o4 D3 x( p4 K6 K. l
- SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; //使能中断
) t, p0 b) v g ]# G - SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开定时器5 R* `$ \2 ]2 Z8 p
- SysTick->LOAD = 9; //随意设置一个重装载值 m: Q7 }6 j9 _5 R5 r/ Q- S' o
- }
5 A7 q- V; q! s; O+ C
+ z0 X3 m$ H* Z8 ^: r
. J* D- p* _- w+ [6 V; F5 [7 J* N- void Delay_us(u32 xus)
) b+ n6 x% `; q4 Q! w- ^ - {
O4 d& }0 Z* X8 [1 \ - SysTick->LOAD = 9 * xus; //计9次为1us,xus则重装载值要*98 g5 _/ @9 r* F& K$ X
- SysTick->VAL = 0; //计数器归零
* s2 e4 B$ R9 z( y6 `" t - while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待计数完成
0 {2 F& f, |$ m - }( p$ Q1 o5 t! J6 p9 k) X
6 @) x; p' _( c8 ^: ? B# f* ^- 9 ^ c1 ] [6 W- l
- void Delay_ms(u32 xms)
; l/ I" G& g; N. @( }( J' l; \$ n8 v# e - {* K. l* w1 U5 ]$ v! J* b3 N
- SysTick->LOAD = 9000; //计9次为1us,1000次为1ms
" r5 o' Y$ X- h5 r2 u/ ] - SysTick->VAL = 0; //计数器归零
8 K1 A3 x3 Q$ {6 Q4 e4 I - while (xms--)' O! w0 c) d( q
- { N% J, D3 ^2 }2 P3 z1 L, t
- while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待单次计数完成0 K7 I; K6 {: A
- } v) w/ i3 ~2 r; J
- }
复制代码
0 `, d, H( L* O& J3 H: c1 b这样的话中断依然打开,但是中断里是空的,其次延时函数也因为不调用获取时基直接判断寄存器而更加精确!
5 u! q* K) }2 z0 e( M& G6 e' c- P7 h7 ?* i' B
' T1 Y6 F! c* V! e+ z0 h. C# y
|