红外接收头很常见,具体就不细说了,这里记录重点: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
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
- #define NEC_HEAD (u16)(4500) & U0 B) o" l) U* \+ n4 e
- #define NEC_ZERO (u16)(560)0 z c# [7 ?+ Y: C [6 t! ~2 K
- #define NEC_ONE (u16)(1680): E( ? }* r/ m- ^' H
- #define NEC_CONTINUE (u16)(2500) ?' O- n, B( I, @, A
- ; q6 N# Y, |1 r) B% E& M' J8 O
- #define NEC_HEAD_MIN (u16)(NEC_HEAD*0.8f)
3 V6 Q9 J& i5 y {* q( K0 P! t, E - #define NEC_HEAD_MAX (u16)(NEC_HEAD*1.2f)
( k( C, k* d0 _$ V - 7 u ] F S! a
- #define NEC_ZERO_MIN (u16)(NEC_ZERO*0.8f)2 X# v4 t% I5 ], J
- #define NEC_ZERO_MAX (u16)(NEC_ZERO*1.2f)4 x: g F3 y( q$ t
- 7 B8 x9 @9 J3 g8 l
- #define NEC_ONE_MIN (u16)(NEC_ONE*0.8f)
' m) Y1 n9 w @% f+ Z% Q% [ - #define NEC_ONE_MAX (u16)(NEC_ONE*1.2f)
' Y+ B |4 b: r - 5 D7 V* x* _# o' i6 K6 J: ^/ w
- #define NEC_CONTINUE_MIN (u16)(NEC_CONTINUE*0.8f)/ B0 J8 b5 ^* c4 a }3 ?% ]
- #define NEC_CONTINUE_MAX (u16)(NEC_CONTINUE*1.2f)+ }4 k( K2 @* t# {
+ z+ e4 s& m! N4 O( l
% |: n! c8 d( e/ v- `# L- //红外遥控接收初始化
/ x: N! H- c0 f: A) h/ | - void NEC_RX_Configuration(void)
4 @+ a6 @+ A. V, H - {6 v0 D1 ?1 s$ w
- GPIO_InitTypeDef GPIO_InitStructure;
/ U9 ~/ p4 \0 Z% q% f ]& | - NVIC_InitTypeDef NVIC_InitStructure;
. H# a0 [3 Y' Z( x7 k4 w/ W - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;; B X4 f, \2 K0 B1 {' {
- TIM_ICInitTypeDef TIM_ICInitStructure;//输入捕获
" M) R0 Y; I5 F& z: {8 v) P3 { -
j9 k5 W9 \! J8 v) _; g - RCC_APB2PeriphClockCmd(NEC_RX_RCC|NEC_TX_RCC,ENABLE); //使能PORT时钟' z8 K/ f! Z( y s8 P& D
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //TIM3 时钟使能
& q7 j4 {. y2 h' @$ }. p9 B2 q -
+ Z5 m3 z6 Z8 I/ c" K: s/ m: @! n - GPIO_InitStructure.GPIO_Pin = NEC_RX_PIN;
4 i6 q/ t' I/ d$ @( t - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD; // 上拉输入) Q5 }# G. s5 [# J+ ~4 p
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
! A' y6 Y/ z3 m5 v, I. I1 `4 z' e% p - GPIO_Init(NEC_RX_PORT, &GPIO_InitStructure);
: s z; _2 C+ D) v' x
9 Z2 N, D, w1 e7 J7 V( s! N- GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN;
) F) |6 H! }2 c1 y1 I5 } - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
. a: ?& h- A9 b. ?; Y& N+ E% N; M" _ - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;; a# F2 K1 A- Y' [; m
- GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);2 I3 _' z4 K, Z p6 G5 t
2 @3 E# {+ N8 X; `4 W( V& R S- TIM_TimeBaseStructure.TIM_Period = 10000; //设定计数器自动重装值 最大10ms溢出$ ^& B4 G4 r2 J; E8 R/ @' v
- TIM_TimeBaseStructure.TIM_Prescaler =71; //预分频器,1M的计数频率,1us加1.
; j# [! U6 J0 f! n - TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
' _; R: D/ W \4 Q, e/ f: \ l - TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式
5 @0 j$ ?1 G6 r4 \. {5 }; ` - TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx% q; Q1 r( q3 j4 S. ^$ j' u. D
1 m/ X6 `3 z: ~! |3 D, ^$ \8 g _5 b- TIM_ICInitStructure.TIM_Channel = TIM_Channel_3; // 选择输入端 IC3映射到TI3上' U9 K& J! n" O! N% {+ @
- TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
8 j- @" N5 ~. Y4 J* q - TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;; H' _* w O+ }; K
- TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频1 S9 W3 _; @3 l3 N4 a* D
- TIM_ICInitStructure.TIM_ICFilter = 0x03;//IC4F=0011 配置输入滤波器 8个定时器时钟周期滤波* _9 ?4 p! ^6 {
- TIM_ICInit(TIM3, &TIM_ICInitStructure);//初始化定时器输入捕获通道
+ J7 M! P6 e) {
+ h" A& K& ?0 B+ G; ^# ~- NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3中断8 b; H, g& }, _' I5 o/ F8 U
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //先占优先级2级- \/ [( j9 ^3 h# x' a7 R w* a# k
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //从优先级/ p' x4 U3 R8 x. V7 e
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能 {5 r; q, Y) \$ c
- NVIC_Init(&NVIC_InitStructure); //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器
' O( s% ?: z+ B& e+ Q I: ]
6 l9 ~- m5 F% Y9 |7 Z* Z- TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,ENABLE);//允许更新中断 ,允许CC3IE捕获中断2 M9 }: }( k, b& w9 G0 ^+ F! [
-
0 ~: f: b+ m/ o: o& K - TIM_ARRPreloadConfig(TIM3,ENABLE); //重装载
% S$ m5 A" E5 I1 Z - & v: L$ o2 B. A2 j+ i: ^7 \
- TIM_Cmd(TIM3,ENABLE ); //使能定时器3
5 i" k4 \/ L6 C, @9 s - }; x" b- B6 q2 T+ W6 m/ K1 \# B& |
- $ X4 k$ {- N5 c: w
- //遥控器接收状态6 `3 u% D- ^, }$ ?0 ? _; _
- //[7]:收到了引导码标志, V6 S5 G; o1 A1 s* {* n5 ^
- //[6]:得到了一个按键的所有信息) c& m/ g. v# i) Z) L; i
- //[5]:保留
3 v9 I$ F3 D! Y5 p1 q& X0 N - //[4]:标记上升沿是否已经被捕获
% \/ f( F) B+ j6 T, B' ~; g9 T7 M' q/ M - //[3:0]:溢出计时器
" f; g5 @3 ]8 T, Y2 Y - u8 RmtSta=0;
3 s5 d9 ^7 w2 _, Z- l: S# ?" k+ o) F - u16 Dval; //下降沿时计数器的值
' {) u) i" \% y - u32 RmtRec=0; //红外接收到的数据
# o- ^/ G6 k9 | - u8 RmtCnt=0; //按键按下的次数9 ^ u/ K) f4 @$ E4 V1 L1 g
- //定时器4中断服务程序0 W1 C9 J7 r" j# `) ?; y
- void TIM3_IRQHandler(void)1 k! t1 V0 W* l! ] v; @
- {
( f% }& @8 [- h3 u - if(TIM_GetITStatus(TIM3,TIM_IT_Update)!=RESET) //更新中断
- [, r3 J# l* F - {
& V1 v% v1 G- o. ], f - if(RmtSta&(1<<7)) //上次有数据被接收到了
+ l* k* L- r$ `) W - {* h& x, E* N, {2 t0 _
- RmtSta&=~(1<<4); //取消上升沿已经被捕获标记1 h( t6 l8 T M2 |( C
- if((RmtSta&0X0F)==0X00)RmtSta|=1<<6; //标记已经完成一次按键的键值信息采集' s3 |3 H+ o. k* Z9 w; V' }
- if((RmtSta&0X0F)<14)RmtSta++;2 V1 N' O* |% j. d
- else. T; a! O. K. X! D3 I
- {
+ l2 {9 U1 F/ Z$ L x" _ - RmtSta&=~(1<<7); //清空引导标识- B( W( q# ?: b
- RmtSta&=0XF0; //清空计数器
9 E6 X' L+ k4 F- J$ ^% A - } K0 r* ^- l2 s3 Z* O! n5 G$ F
- }
4 K3 X k9 \/ k3 } - }# g3 K8 f4 k* P$ X7 M
- if(TIM_GetITStatus(TIM3,TIM_IT_CC3)!=RESET)
! l( |! ?5 ?) B8 P; ]4 p - { s1 G8 o6 `& H0 w
- if(NEC_READ_RX())//上升沿捕获
4 K }0 Q7 V* O( \ - {
1 M6 ~: k" {6 {% j: V - TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Falling); //CC3P=1 设置为下降沿捕获1 F3 H4 p1 o. b- n. V$ A! R
- TIM_SetCounter(TIM3,0); //清空定时器值. L0 n: g/ ]- s- S" U
- RmtSta|=(1<<4); //标记上升沿已经被捕获/ L5 s/ B! I9 A2 b" {2 i5 |
- }
/ ?2 v( y* w# P5 y- A( K. Y - else //下降沿捕获, W+ j* [6 _4 x' |& ?
- {
* A) P: W S t( K7 f - Dval=TIM_GetCapture3(TIM3); //读取CCR3也可以清CC3IF标志位2 {/ M6 F n' T* q3 K
- TIM_OC3PolarityConfig(TIM3,TIM_ICPolarity_Rising); //CC3P=0 设置为上升沿捕获( G' l8 E( s& [, x. k/ d* R
- if(RmtSta&0X10) //完成一次高电平捕获* l4 |5 E) C5 E0 z$ j
- {
8 W3 D' X! f5 h& a - if(RmtSta&(1<<7))//接收到了引导码( \+ G! O: m! I* q! K# u
- {
# J; o6 X' b3 j$ r7 y - if(Dval>NEC_ZERO_MIN && Dval<NEC_ZERO_MAX){ //560为标准值,560us/ ~; [/ |0 T) }1 u$ }
0 I+ O1 A, B% o+ U0 E1 X: z$ E- RmtRec<<=1; //左移一位." n# u/ j2 P9 i5 F: T0 b
- RmtRec|=0; //接收到0% p; A1 j% N2 n$ P0 G
- } 6 l: D; V2 ]/ p! j
- else if(Dval>NEC_ONE_MIN && Dval<NEC_ONE_MAX){ //1680为标准值,1680us
' |5 n) a6 [- B' l0 i) @ - 3 N3 L; T1 |, Y$ s$ C
- RmtRec<<=1; //左移一位.
5 W5 U6 [. a5 r3 C - RmtRec|=1; //接收到1. u' U9 H0 q/ S
- }
( r' T/ ^* f$ }, w) `8 D2 y - else if(Dval>NEC_CONTINUE_MIN && Dval<NEC_CONTINUE_MAX){ //得到按键键值增加的信息 2500为标准值2.5ms9 m5 X1 _" X7 U, J0 P- q' C H
- 2 G- b9 y- E" Y5 T b" c! B+ d
- RmtCnt++; //按键次数增加1次5 Y! s4 |; |' U+ {1 i
- RmtSta&=0XF0; //清空计时器
6 S5 \ @& a, m( ^( x" | - }+ l3 n1 o J2 v H: R. Q) C/ W
- } - o1 u; s- P4 ^6 _- F* @& p
- else if(Dval>NEC_HEAD_MIN&&Dval<NEC_HEAD_MAX) //4500为标准值4.5ms
/ T& I2 `% _4 y* ?! L. U' y - {) X; Q D5 {7 |# H; b% [
- RmtSta|=1<<7; //标记成功接收到了引导码6 Y/ q6 Q+ I3 P* r' K& R5 L
- RmtCnt=0; //清除按键次数计数器
' b; F* G$ q. T% K9 L! s5 u - }
: v! P( q9 p/ A5 k/ f( ? - }
' r8 @( O' ?' v3 ]5 P - RmtSta&=~(1<<4);. T5 i* X: G1 u+ a0 V3 w3 G; z
- }
% j, }9 L; E- T/ H( b& U* ? - }% A$ k6 p% h' u
- TIM_ClearITPendingBit(TIM3,TIM_IT_Update|TIM_IT_CC3);0 {' G" `6 N+ ] Q2 w+ c
- }
2 a1 ~! l- Q7 Q( t1 B" K M1 l - 3 u1 e7 u9 @5 P# X9 z! V
- void NEC_GetValue(u16 *addr,u16 *value)
: z8 z+ J% o- c) Q7 b$ k# } f - {
. _* p4 ]- r! G$ T - u8 t1,t2;
4 _# @9 Q- e+ c - *addr = 0;4 v, |/ C9 T) ^! |9 W: s' n; T
- *value = 0;3 ~& S d% _: @: l. }4 Y* h3 @
- if(RmtSta&(1<<6)){ //得到一个按键的所有信息了 k6 G# D6 N- X) y9 y. X4 c
# w4 P" L7 T$ q# t* [8 i6 r- t1=RmtRec>>24; //得到地址码
: V. B" U+ @( x" y+ @$ }6 Q' S4 d% w - t2=(RmtRec>>16)&0xff; //得到地址反码
% U' I" g/ \" y9 {0 u* ~( b - if(t1==(u8)~t2) { //检验遥控识别码(ID)及地址
- Q, ~( D) P# h- b8 P
: X' ~ Q, x7 w$ W3 h- *addr = (t1<<8) | (t2);7 t9 B) a7 {$ ]" S, _5 |4 y
- t1=RmtRec>>8;
0 B$ s6 ]% l# ^/ r/ N - t2=RmtRec;
7 {$ U a/ \6 U% q: A( _; ~/ ? - if(t1==(u8)~t2){
7 C# i. j1 ]+ p4 b/ E8 @1 n" R - *value = (t1<<8) | (t2);* q( f& u+ D1 O. ^8 Y8 P! R
- }2 G2 ?: z9 U, q- R
- else{$ `' ~ X; Q! |+ O# D1 O
- *addr = 0;$ z( w* z/ O# f
- *value=0;7 H/ k, n' ^; O* F
- }- _" a* c0 w* N! U/ H: z& r
- }
+ m. a; {: Q. `( Z( V9 p3 b0 Y - if((*value==0)||((RmtSta&(1<<7))==0)){//按键数据错误/遥控已经没有按下了
( t& A* J3 m0 ?4 N* K - 9 S% U5 v+ y# @
- RmtSta&=~(1<<6);//清除接收到有效按键标识
v" |) o9 r# B* N Q9 q! L: S - RmtCnt=0; //清除按键次数计数器
# \# X5 A. A' x4 Q* K t - }
. `% t; _+ R, [% [" B - }. c& ^" `; E" K2 }7 ?" A
- }
复制代码
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- /****************************************************************************************/ w$ H- l4 a* o0 ^6 x" ?
- #define CARRIER_38KHz() TIM_SetCompare4(TIM3,9) c. s% R9 @$ F( m
- #define NO_CARRIER() TIM_SetCompare4(TIM3,0)
3 h5 U$ O+ R1 n# Y3 W: A - - p. X `, J3 M' N7 h( W6 E
- void NEC_TX_Configuration(void) //红外传感器接收头引脚初始化
( H7 |9 E9 ^; f, Y8 E: k" J3 S% f - {" g. Q; \' S0 a; q
- GPIO_InitTypeDef GPIO_InitStructure;
- R0 r- E/ {2 A$ w! I; |8 Q- J
5 V* X" V9 N" e1 e6 \% o- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
. N: A" u) N) b+ h, \ - TIM_OCInitTypeDef TIM_OCInitStructure; //输出比较
" R/ K3 B; n; t, q- F* O0 I$ z - 3 x& l2 G2 s8 ?7 T2 q) z
- RCC_APB2PeriphClockCmd(NEC_TX_RCC,ENABLE); //使能PORT时钟
" o! q/ D; y" {" I/ q2 v; E - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); //TIM3 时钟使能; @8 T4 `7 b1 C1 N0 r$ Y: G
* B P# C9 Y- N3 |- GPIO_InitStructure.GPIO_Pin = NEC_TX_PIN; $ R8 { h, J, b$ w& T
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
6 r+ I) D* i8 @' r( o - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;, I9 L8 |) Q8 \% P i) @% A/ v' }
- GPIO_Init(NEC_TX_PORT, &GPIO_InitStructure);8 ^( V7 m1 f! ~4 P6 m
- : l: g7 X3 X+ ~1 q0 H
- TIM_Cmd(TIM3,DISABLE);
: M! m1 M" `- Z& V" T - TIM_ITConfig( TIM3,TIM_IT_Update|TIM_IT_CC3,DISABLE); //关闭TIM3中断
9 ~- _4 e" U9 }9 u
8 U9 P2 ?/ y' ?, a, c7 T- TIM_TimeBaseStructure.TIM_Period = 25; //设定计数器自动重装值 最大10ms溢出
) S0 V' U; I8 m& D1 u - TIM_TimeBaseStructure.TIM_Prescaler =71; //预分频器,1M的计数频率,1us加1./ n \2 E! j @: E
- TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim& a; c9 I- @9 N. W h
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM向上计数模式1 K( r9 D r. `7 K/ e5 W
- TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); : I9 u: |6 N1 q5 P* ^- z, m( [" L, D
: {1 f2 O6 w/ k w& p! b& Q- TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1; //设置PWM1模式
' W$ l* y0 C1 ^8 C1 `# C- a* b - TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //输出使能
, H, A- C# C2 `4 D' e - TIM_OCInitStructure.TIM_Pulse = 0; //设置捕获比较寄存器的值! g- S) Y1 }# B4 r9 G* l8 I
- TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //设置有效电平为高电平
9 ?& x' t: ^; ~" }: g- _ - TIM_OC4Init(TIM3, &TIM_OCInitStructure); //生效初始化设置& G& y4 a$ `& _- y3 i/ @
- TIM_OC4PreloadConfig(TIM3, TIM_OCPreload_Enable); //使能输出比较预装载8 [2 g+ b9 ], g
-
4 w) _# l; A* a" Z& | - TIM_Cmd(TIM3,ENABLE);
6 E2 I# e0 H9 Y9 e2 I# j& N v* I - }0 `/ \" a, L9 d, S/ w9 I
- ) G( y5 d! L4 X% J& ]
- static void NEC_Send_Head(void){" R4 f9 [9 {* C( P' s
- CARRIER_38KHz(); //比较值为1/3载波1 H: s9 O5 \6 u4 W. [0 ^
- Delay_us(9000);
, {* p& T( [, g& z2 J) X - NO_CARRIER(); //不载波
( C. \- _9 q9 y% V/ Q) \ - Delay_us(4500);
5 d) c4 S) T, o, j6 F - }" x$ z. Y7 w4 d7 f
- * d, P, L' q. C
- static void NEC_Send_BYTE(u8 value)
8 R2 q- e3 i# Z. R& v - {
( v0 n6 p! y2 D) I - u8 i;( A7 S, a8 m8 o
- $ G& H0 ]" H; Q& z; H' c' k
- for(i=0;i<8;i++)
! y1 m/ ^ V: ?# @" v& e - {% N$ M F3 K6 v
- if( value & 0x80 ){
\1 c ^ G1 A6 y* ^2 n% { - CARRIER_38KHz(); //比较值为1/3载波
5 j5 `. a9 g3 c4 z; k: H - Delay_us(560);+ |7 y7 s. V" V3 J
- NO_CARRIER(); //不载波 k4 C$ [% Z) t
- Delay_us(1680);; p- s4 P7 Q# N5 H
- }
. Q; F+ }5 h- E/ p! z - else{
. K4 U: H$ J- h" a# T; ^ - CARRIER_38KHz(); //比较值为1/3载波& b. w& I& `$ x) M2 ?8 k/ k9 q0 o
- Delay_us(560);( [/ [# L; _ }: t# X
- NO_CARRIER(); //不载波 E* j `5 y, I
- Delay_us(560);. c$ f% u1 E: t* p4 S/ O8 c) H
- }
6 h2 C5 k* z) z# h: i) D7 r - value<<=1;
, e9 D- _2 k6 H/ R* W; B - }6 ^( ^! d# Y( l5 Y
- }
. E5 y7 c3 V- E3 T9 I7 q
/ @& {; N8 ]8 g9 Y* K- static void NEC_Send_Repeat(u8 repeatcnt)
; g/ t1 i; H; C9 j - {# v3 ?, `. A, g
- u8 i;3 A m4 M! l9 S% Q2 d( R7 v$ @
- 3 ~4 i, [" z' `. s, h8 a# Y
- if(repeatcnt==0) //如果没有重复码就直接设置无载波,发射管进行空闲状态
7 l5 E( p. J- m6 b - {( x. J2 a" E1 u2 T& t9 q
- CARRIER_38KHz();. Q9 g& [! m) x( T9 `% I
- Delay_us(560);
; c: k( X8 F: A% K. j( T - NO_CARRIER(); # F5 l. ]; W) ]4 _! j! _
- }
, i6 |! g: R. E: O - else
4 C# ?1 x. A! i) C$ x9 b/ \ - {/ N5 K8 Z5 z2 D J9 h
- for(i=0;i<repeatcnt;++i)
* D X3 f; S0 X - {
* p3 ~- ]! D# Z K - CARRIER_38KHz();
+ d. B- V9 R E$ _$ W2 e6 J8 [ - Delay_us(560);
9 F6 e: {: Y' ?, z - NO_CARRIER();
- O. I$ V; N9 [ - Delay_ms(98);
1 i5 S: @0 V1 \" W - CARRIER_38KHz();
7 k. n# E T0 `% d5 q7 K - Delay_us(9000);
% Z, @: h/ v7 Y Y - NO_CARRIER();
/ w4 z$ w# E: A - Delay_us(2250); 0 e+ G5 d. {& p$ Y h+ L. m `
- }
0 k; Q7 g" o1 h8 S, j" P% i - % @( {, i2 p# M7 b
- CARRIER_38KHz();
/ n3 e, y4 C: w5 w8 U - Delay_us(560);
! ^ T, a5 c E, b0 w2 D1 O - NO_CARRIER();
6 V* i, o. H# [/ j& d - }+ [) s" l/ n7 D) B0 }3 f' ]' z+ n
- }
$ o W$ B6 j9 \# _& B - 0 A j2 @7 W0 @$ {+ I
- ' Y z0 a% S, ^: {( E! E* z9 B
- void NEC_Send(u8 addr,u8 value,u8 cnt){* i" V$ i! e7 l/ ?6 \2 e3 H# _
4 Z0 R* l" L6 w) ~/ U- NEC_TX_Configuration();
1 T( }4 m" p/ B3 O8 |/ S5 Z/ | - 9 g( Y1 k0 @& T4 N7 L
- NEC_Send_Head(); //发送起始码
# P( |; k. a1 X6 ?. y8 H! V - NEC_Send_BYTE(addr); //发送地址码H
2 x. `+ {, D% P# O - NEC_Send_BYTE(~addr); //发送地址码L
1 f) Y+ m6 ?) B0 B/ o - NEC_Send_BYTE(value); //发送命令码H! H- o& \+ K0 @1 H; b7 C2 h
- NEC_Send_BYTE(~value); //发送命令码L2 ^; O: [0 _- \
- NEC_Send_Repeat(cnt); //发送重复码 / I' R8 c, b7 e0 Y4 J1 h
- ' u. j) p! I I! {. v
- NEC_RX_Configuration();/ b4 e* E, }9 y: W
- }
复制代码 : X+ ~9 U$ L0 N* g
发送部分首先是重新配置定时器3,在配置里面计数频率依然是1us,也就是1MHz,计数周期修改成了25,也就是26个周期(0也算一个),这样算下来的话1MHz / 26 ≈ 38.4615KHz,这个跟红外接收差不多能匹配,当然红外接收头这里用的是38K的,所以配置成38K的,市面上也有不是38K的,比如36K,40K等。然后是载波,载波这里用的是差不多1/3的载波,从哪里可以体现呢?可以看到有这样一个宏定义:- #define CARRIER_38KHz() TIM_SetCompare4(TIM3,9)/ ]: ^3 c0 V" t5 e7 S2 b/ {( Y6 J& V
- #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- keyvalue = Key_Scan();5 M" }% ?2 O) N& u1 M! {# Q1 t) S
- if(keyvalue != KEY_NONE){8 \: ^# i2 H* C9 o4 k4 r
- NEC_Send(taddr,tvalue,0);
; F H" t6 @, l# h4 x$ B7 ^ - taddr++;
0 _, D/ k( e& d - tvalue++;
& x: k8 n4 h8 X P% i1 I - }
; B( I0 G8 Q- f" k7 K - 7 y" `6 F+ ?5 z% [8 ~6 |
- NEC_GetValue(&addr,&value);
5 n. {0 q, r; ?
, h4 j# y' U/ V: h- if((value>>8)){% x& ~" |9 ~$ ~3 W a# j- t
- printf(" %04x,%04x \r\n",addr,value);
X# ]- R L4 y8 k, p - printf(" addr: %d,key: %d,cnt: %d\r\n",(addr>>8),(value>>8),RmtCnt);8 A. | j& f! A* C. \3 ]2 n4 M
- Oled_ShowNum(36,32,(addr>>8),3,8,16);
" q8 s1 U2 p. i0 ~! F/ Z y8 b - Oled_ShowNum(96,32,(value>>8),3,8,16);
+ }/ A$ ]3 ~; V# d - Oled_ShowNum(36,48,RmtCnt,3,8,16);" {- @# X* z4 G4 p. ~" P
- Oled_RefreshGram();7 `/ A3 g" M+ ^2 a* O. A
- }
5 j' d8 S1 i: \( O. [# g- O. ? m
) `* w( H- j- o. g x) O; h* E- Delay_ms(10);
- e- }/ |' R8 P+ d% y5 g - if(++t==50){
8 y" N9 n: A' J) B2 U - t=0;
! Z" s! f) D7 ~7 t) M/ Q' u - Led_Tog();7 u7 I# o9 r/ t0 p
- }
复制代码 , 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
- //u32 System_ms=0;1 u/ j6 J1 q- L0 Y/ y( B
, D9 u3 C* {% [$ T' e0 A- //u32 usTicks;1 p% r, O6 M3 d$ ? W- z! V2 \: \
- //void Systick_Configuration(void)
8 n' Z! n% f! h* c+ V3 d' ` - //{
# \# _# a8 \, B" s- I7 w4 W" u - // RCC_ClocksTypeDef RCC_ClocksStucture;, m% ]+ h# {2 k) b% M
- // RCC_GetClocksFreq(&RCC_ClocksStucture); 1 `9 h" U+ n0 l: p
- // usTicks = RCC_ClocksStucture.SYSCLK_Frequency/1000000;
6 c. Y4 k$ t S, K - // SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK);3 f( P: o; f. i* O% Z4 ^8 s
- // SysTick_Config(RCC_ClocksStucture.SYSCLK_Frequency/1000);
: e# u- [+ m, e1 q$ | - //}6 V7 a+ n6 B( c E2 _. i: u
- 4 T3 r: U0 R' F5 s) O/ I- Y) O. c) |
- //u32 GetSystick_us(void)- u& S( ?* t1 }! X
- //{1 k0 Q" N3 q9 {/ ~; r8 M, O9 x
- // register u32 ms,cycle_cnt;: F. u1 C. ]+ M" O( v
- // do{
v. A0 h8 @ C$ _; B - // ms = System_ms;4 M2 r6 ?+ G4 j+ A# e! r- E
- // cycle_cnt = SysTick->VAL;: R; Y6 {6 ~3 M, d' s' p4 o
- // }while(ms != System_ms);0 I- h. a2 {( r
- // return (System_ms*1000) + (usTicks * 1000 - cycle_cnt) / usTicks;! j9 f9 y% C3 x4 c$ I
- //}( C( j1 |( Z% b% c5 L" Y/ b* X5 ^
- , j7 i) p; {9 D/ m
- //u32 GetSystick_ms(void)
0 b2 r8 o0 U8 z& O' D. z5 L7 k - //{
, a) _3 R9 J8 ^* | - // return System_ms;
0 G! W1 e3 f2 T% K2 s - //}( B) F* N9 W: ?5 R3 M5 A: J8 l
+ q' j! |0 p, E: I. U( z. r6 e9 w- //void Delay_us(u32 nus)% H( x9 |, A: v+ E/ e
- //{
* @- F0 F3 t- O3 h5 q - // u32 now = GetSystick_us();
P2 N, K* p, }% m- I2 i - // while(GetSystick_us() - now < nus);
+ ]# U& r% Z- N3 r; B - //}% d2 }2 _8 O& z
' D) j1 ~7 ~- O5 O: q- r2 u( K- //void Delay_ms(u32 nms)$ G+ S7 r; e( N# O0 ]1 x% n
- //{. E/ O0 y* q3 d! s. C9 e
- // while(nms--)
$ r6 v: G- @! o$ w# `3 t - // Delay_us(1000);5 A3 `. _) [4 ~! B7 ]" B* k9 @
- //}8 `5 c7 K2 Y7 x, d% E7 o
- 4 |8 P; b4 o) W2 l& o, K* o
- void SysTick_Handler(void)
/ y; l/ ?+ s' W7 J5 j! }% r - {2 A' D4 d% D4 o+ |5 O6 q
- // System_ms++;
7 `& y. |+ g7 z/ r - }
复制代码
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# {- void Systick_Configuration(void)
* w1 I2 S1 j& B. C9 C. q - {# P1 ?9 k, d/ ?1 S; N
- SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK_Div8); //设置时钟源8分频$ J; ~0 M( N6 u7 n+ q: H9 e
- SysTick->CTRL |= SysTick_CTRL_TICKINT_Msk; //使能中断; @; _- d6 n1 C4 e+ Y
- SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; //开定时器# E; p- w; b( A0 `$ M. \+ w, d% [
- SysTick->LOAD = 9; //随意设置一个重装载值9 y* |7 W: c9 M8 c7 p& w
- }/ P1 S+ X1 A4 q5 o; _; }) h5 N
" G. d: d' j0 T# m8 T% ]5 U- - ~: Q8 r% ?: ?$ N
- void Delay_us(u32 xus)
@) B) k4 |' m5 s/ n' Y - {
3 |9 I+ E; V; h - SysTick->LOAD = 9 * xus; //计9次为1us,xus则重装载值要*9
6 K7 ^# W6 E* ~ - SysTick->VAL = 0; //计数器归零8 c' P/ H8 Q. G: a# j2 \6 Z
- while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待计数完成
$ ~- Z+ {. Q+ u% W6 p8 i - }7 t1 e( [. F, ^' W8 B
- 7 L0 {1 \7 U3 q% M
2 D, I$ m7 V! |+ b- void Delay_ms(u32 xms)1 f, s {& l o: a
- {; q4 U- [3 ]) R) U
- SysTick->LOAD = 9000; //计9次为1us,1000次为1ms
! w7 S- z- P" i; m3 r - SysTick->VAL = 0; //计数器归零6 ^) ?9 \+ {" S. r0 W: J' `' p% x3 n
- while (xms--)' K3 D! }6 _1 f" w+ A
- {
9 i+ @ x8 ?* [* z& f ^/ u T. t - while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); //等待单次计数完成
2 M- P! j v" T4 Y - }2 Z& c6 P" Z, B; [4 T
- }
复制代码
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 |