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