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