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