本帖最后由 雷精灵2046 于 2019-6-14 09:28 编辑 : u' {! S& ^: s8 q* F
. `5 V4 F% t6 Q: w- }
没有用复杂的分立元件搭建发射接收电路,直接用的现成的2.4G无线模块jdy-40。
, L- E" Z& |$ f9 \/ e# ]
0 z: a: L$ |! Q- _5 f6 u3 y
- b$ d0 \; `$ r& F$ e, e: c$ v发射端靠3.7V锂电池供电,用了一片SOP8封装的STC单片机,负责产生连发脉冲,同时负责检测电池电量,电压低于3V左右的时候触发低压中断,低压指示LED就会亮起来告知该充电了。平时发射模块和单片机均处于睡眠状态,有按键按下才会唤醒,从而实现省电。实测睡眠状态下整机电流约1~2uA。板子洗了一下稍微干净了一点。因为对于微功耗设备来说,焊接残余的助焊剂可能会增加漏电流。3 U& ?3 ^% i' @8 O
, A" T9 k3 |% f# g: C买了一个火花的FC山寨手柄,去掉里面的牛屎板子,并修正一下塑料挡板,把锂电池和充电模块装进去。* S. F+ p8 {9 ]) Q& _" }3 M
为了减少体积,充电模块裁剪得十分小,并且更换了充电电阻使之适应这个小小的锂电池。这么小容量的电池,也不需要多么大的充电电流是吧?
- L. z# U7 T( l& f- @9 l然后统统用热熔胶粘上。充电电流比较小,所以发热很少,不会熔化热熔胶。' e; Z' D& ?! ^. ~
, {9 E. ^$ y7 o6 o0 t0 I
* j7 \# p. Q: U0 S6 e+ p, }- N! x% b
最后把按键、导电橡胶和电路板装上去。2 G( W t! y6 T- _! z/ @9 s
精心裁剪了电路板,正好扣上,不会阻碍其他元件,也不会影响后盖和拧螺丝。
: l4 m+ E) C0 w3 }1 o7 {5 J5 O" M4 q; J- Z! |0 l% P
w6 b' f7 [3 \- `4 [% i
完工!
3 L! @" j9 w1 N0 {% h& q( c, |8 M% }" L9 ^2 [
5 g5 f. Y! M5 R0 Q" V这是充电的MicroUSB插座。旁边的小孔用于透出低压报警LED的光线。
& P8 V8 \8 q) J, ?2 _) A( c. Q7 Z3 Z, T1 [* s
+ Q1 c; D3 p+ } |7 k- l; R
接收端用一片DIP16封装的STC单片机模拟CD4021。为啥要用单片机模拟而不是直接用CD4021?因为jdy-40模块接收端在有按键按下的时候输出高电平,无按键按下输出低电平,和普通手柄正好相反。当然我也可以用两片74HC00或者74HC04之类的逻辑门进行反相,但那就增加了芯片的数量。对于FC手柄这种工作频率并不高的设备来说,STC单片机完全可以胜任模拟工作。9 c3 z$ R8 R7 Y# `
% {" s' V& x8 h; e( |6 A9 c4 K" b* q6 f/ C
8 E, e0 X/ c" n2 Q' ]$ y
电路设计有点小小的错误,所以有几根飞线。原本设计使用低压差LDO是XC6206,结果在做这个接收模块的时候,买的LDO居然找不到了!无奈只好用了AMS1117。幸好对于接收模块来说是5V降到3.3V,1117可以胜任。要是发射端手柄的话,3.7V降到3.3V,那就非得用6206不可了。
2 S6 B2 ~' n' f. R8 G& ~$ w% W9 @7 N' x' b% H% l
实测十分灵敏,延迟极小几乎感觉不出来。可惜这山寨手柄的手感并不佳,尤其是方向键,软塌塌的。看来我得买个剪线手柄改装。+ h0 \4 }% r, P% P" _/ R# t. ^
2 L4 ]+ z- N5 `7 W
这是电路图,有兴趣的朋友可以参考。
* Y/ |9 [6 P4 U' A
. f3 e' K# C/ |- X) y& o ]4 G$ ?0 _4 x2 u8 _5 m
我信奉开源主义,十分痛恨把技术藏着掖着。好东西大家分享嘛!所以两个单片机的源代码大公开!
5 c: E4 Z: [4 N+ c/ E; X0 \3 d4 V编译器:Keil 51。
6 H( i( e( P" N$ U% N5 `% ?4 V
1 R% C, Q7 G) D" \- E发射端:* c* P4 i. u1 `5 N: O
- #include <INTRINS.H>0 o6 J0 I- {2 `" ]# Y- o: s
- #include "STC15W.h"
( |. i3 j# ?9 a& \* y - . C: X* [& f& d* o8 \; c+ B
+ W/ S0 P' n5 n+ C1 u' z: S9 V7 B1 m- /*
U2 \1 B9 P( Q& y' c; c9 T2 \+ U - --------------------------
6 j @! c4 t& s( E - |1 (OUT) \__/ (INT1) 8|
. q+ K& e/ K9 ?5 p( y& s - | |$ ~9 X) g# F, K4 I
- |2 (VCC) (INT0) 7|, f2 n0 ?# z, S0 a7 F8 E
- | STC15W204S |7 @, O/ \1 j5 w, W
- |3 (LED) (TXD) 6|
+ C2 T4 W: {3 l+ F" D - | |
; W5 }! r7 M+ F; `6 X3 T - |4 (GND) (RXD) 5|
& h3 g2 H* B7 ` u. B - --------------------------
8 l Y& X+ ~! |9 I& Q1 B6 F
4 V2 P2 V `! O! ?) M" C) [- LED ---|<|---[===]--- VCC
. D% T% I: k5 A# S4 P - Red 330& a6 ?- C9 W8 P# T, u4 d
4 |& l& ?" ^/ B0 @- Fosc = 6MHz: Z4 i; ^% a; ~+ [7 Q( S& y
- */3 l7 ?# a" ^/ q' h
, H8 S3 {* ]6 P0 {- 6 r* \+ Q0 s+ Z* a1 b" H5 v
- // 矩形波输出脚$ g6 f) x0 o/ t' q/ L
- sbit OUT = P5^4;
9 @3 W! N/ m7 }, o; A8 ] - // 低压指示灯引脚7 ~" Z) T; T: {/ a: M
- sbit LED = P5^5;
$ ]1 ~/ K6 n) q7 C3 j( y. {9 r! B0 ^
% @' K8 ?" z; w5 ]# {- // 停机标志位
0 m6 H7 g) z% j5 m6 W4 Q8 s9 B - bit isReadyToHalt = 0;3 \! t: B( F; U+ Q6 u3 L' i4 k
- 5 P- I! y. A) k
- // 矩形波次数累计
3 ^: [ r G f- ? - static volatile unsigned char count = 0;
6 l7 x- U. ~: L5 ?/ e7 |
/ r* S1 q! M) v% M
: ]7 z9 c9 f7 ?# a T) V- static void GPIO_Init(){
. b: F3 D+ _/ Q6 ^$ h' q - // P3口设置为准双向,默认靠内部弱上拉输出高电平
+ ]# ^2 R1 E7 Y! w" w4 p3 a - P3M0 = 0;
7 A& { `/ S( @8 N: N" P( | - P3M1 = 0;
! d8 E& `* u& S2 u, k( i - P3 = 0xFF;
. B" k A9 E0 t0 }) P0 `! `0 Y# Z' Z - // P5口设置为准双向,默认靠内部弱上拉输出高电平, y5 y9 ~% d0 G% n- P [
- P5M0 = 0;
2 [7 q! a: m, ]% r6 L' P - P5M1 = 0;
( z9 M( s$ |" d# | - P5 = 0xFF;5 J1 x. B4 P- o5 W0 K
- }
1 n* O1 P: }) z V$ \: ?* E! X - " [1 I1 I! r1 l, V0 f9 s, ^: Y O1 k
- static void Timer0_Init(){
- l& w+ U1 G# b. } - // 16毫秒@6.000MHz1 I9 U/ Y& w+ n8 X
- AUXR &= 0x7F; // 定时器时钟12T模式
9 R0 o- `2 ~( \* \% U, j - TMOD &= 0xF0; // 设置定时器模式
' ?& T- k$ V7 ?: }8 n7 ]2 \ - TL0 = 0xC0; // 设置定时初值9 t, f% S$ J, J1 \
- TH0 = 0xE0; // 设置定时初值
' B# N4 L0 y. }- q0 V$ w - TF0 = 0; // 清除TF0标志
- Q+ {( x% E# q1 d2 _( k - TR0 = 1; // 定时器0开始计时
3 ~/ A+ j, L$ C! k - }
7 J! \1 m) l* y$ E+ h: y/ b - 3 H( U# j* P% p& Q3 Y
- static void Interrupt_Init(){
! Z! n) x. W( A/ H2 k9 ^$ w) O - // INT0中断触发类型为下降沿触发+ z; _4 z+ F9 N* @' b
- IT0 = 1;# X* [; H' O" O: b" _
- // 允许INT0中断
+ O$ F o: y. e$ ] - EX0 = 1;
! `2 l, u: A0 h- e g: ^ g; T) | - // INT1中断触发类型为下降沿触发1 G, Q2 b3 A8 h. I! ?! o9 G
- IT1 = 1;
9 I) x d" w6 `3 ?$ [% A9 H( p - // 允许INT1中断! H6 u0 ~1 \" U" M
- EX1 = 1;
# [9 Z0 W+ y Z - // 允许定时器0中断: o* f# P, n2 r+ b1 _, |. f0 r
- ET0 = 1;
6 N7 v/ Y+ ^5 Q& y2 h - // 允许全局中断
- I4 A" F+ E& s! | - EA = 1;6 R6 ~) B( f3 y- y
- }
' I9 \' }9 y; j$ K - 5 c- S7 L) p1 V9 I8 i
- static void INT0_Isr() interrupt 0{
+ N j# Z1 W" Z4 C/ i. \/ J - // 矩形波次数累计归零0 X: d" Z, T s9 O
- count = 0;
8 ~( r4 _! z$ ~; |6 U( e - // 重开定时器0中断. E) S0 M) M) Q- S! i
- ET0 = 1;8 q9 J& D, D1 t2 i; ]0 F
- }
9 [* b* Y. N Z2 l5 j ^
& Z( ^& v1 T" p+ Z5 f% `! x- static void Timer0_Isr() interrupt 1{
9 N; \1 u7 T/ s) t - // 输出矩形波
5 H4 z2 B: B7 ]0 p - OUT = ~OUT;$ p! W/ k. U, v
- // 累计矩形波次数4 {2 T# }; B# w/ ~
- count++;
9 ^0 R$ E/ Z$ i1 l8 y4 t - // 超过250个矩形波,准备停机
% V9 h3 p* c& b8 T' m7 \1 a3 Q - if(count > 250){
[- G# R& J' h: h" d& x2 t9 r - count = 0;& K( @2 |" k9 W0 k, E
- isReadyToHalt = 1;. m3 ~( X+ J. K( q' Y
- }/ O- `/ T5 I) j; M* O
- }
- A* L/ I( T$ p5 P5 h2 ~) Q
% c' }% s" v0 n6 y3 J; E- static void INT1_Isr() interrupt 2{/ g2 c3 d7 r! S, I
- // 矩形波次数累计归零; G3 e" d- ?4 h- Q* I3 Z7 ?# F
- count = 0;
z) U. B# v% A/ ^ - // 重开定时器0中断: g( ~4 U) {/ M+ G1 D! ^1 K
- ET0 = 1;8 {7 ]/ u! P! Z5 W6 N
- }
$ |: e' G6 P r! K' b& n - " p9 d, R" p3 ?/ K7 I5 @" d3 P
- 5 u, p* E/ }2 e" M; \% t
- void main(){8 ]! a6 v7 {3 o! Q3 N- W) p
- // 初始化P3和P5口4 k/ c2 \) {. M3 ^! w8 K3 _
- GPIO_Init();+ w# P, b" L! Z' s z) z( _
- // 初始化定时器0# T! ]4 _' d- \( d
- Timer0_Init();3 H3 F2 X8 J% j3 D
- // 初始化中断+ Q; W& f0 K: ?5 `3 L
- Interrupt_Init();5 L0 Q, z6 z1 s- j* K
-
. I4 @- U' W# k% A4 B0 t - while(1){- K7 M# @0 n7 G' ]: D$ _# k0 Q
- // 检测电量
6 g. C5 I0 n4 ? - if(PCON & (1 << 5)){
! T& L+ Q* M0 q2 X - // 如果电量低,低压指示灯亮1 f# u% P4 X6 ]) E/ q: V. l
- LED = 0;
2 l0 T8 e; _$ B+ L, z' U3 `0 T - // 清电量检测硬件标志位
) U3 q5 C& m; k, F. V% z% T - PCON &= ~(1 << 5);/ b# Y2 E1 P& ]( w3 \
- }else{
~: \" O5 @8 ]9 { Z) l$ G - // 电量正常,低压指示灯灭+ W7 R1 v- @2 c# {5 e5 }: @
- LED = 1;
. _" B9 P' C7 O! K& g/ x% g% M) Y; t - }
( ^- x# I- }8 q9 U, ~' X
: x+ O# H8 l( l& z8 H; ^5 K- // 检测到停机标志; \8 Z( j# X. y' a. s7 b. L
- if(isReadyToHalt == 1){ @2 \- h+ N9 O: S0 E- g
- // 暂时关闭定时器0中断
3 b# t# {( L1 {$ U* F! m - ET0 = 0;7 P; Z: ^" q3 n, h
- // 停机之前先把矩形波输出脚置低电平,以方便INT0和INT1唤醒CPU6 B* B3 t+ \& v4 R4 V( _
- OUT = 0;' z; k1 {; [6 F8 U! r
- // 停机之前先把低压指示灯灭掉,以省电
! G: U) w2 c# g! I - LED = 1;8 S% R6 Y. k0 `2 Y0 A. k3 S
- // 进入停机模式1 | q/ I7 y0 e/ A/ \" z
- PCON |= 0x02;9 ~# j z W6 U6 S! L: K% l
- _nop_();' E5 x* k: Y* D3 R
- _nop_();) {8 X, x0 C3 Q! ^- P1 I
- _nop_();
( A" B) k. S' A: H, d. E W5 t' Y - _nop_();
5 M* T7 _, q% b7 Z - // 唤醒,清标志位, `( c! ~# ^. h7 e9 m
- isReadyToHalt = 0;
' [1 i }: P/ M6 m8 U8 x9 u - // 重开定时器0中断+ {- ]' h: a& ~! @
- ET0 = 1;2 E4 C3 [$ y% L
- }9 S: J) k; J5 c" `# b. p
- }: S) J7 f- Z: l
- }
3 @% Y+ A# j: b% ~8 t3 n
复制代码 2 e9 ]) P4 C% Q6 p% ], m
硬件参数配置:
7 }5 R5 h* T {& y, i: s2 S1 L2 z1 ^+ ?" G
/ u. G1 q% b5 M9 X# m接收端:& |% a8 h# v; v. R" {% m5 ]$ n
- #include <INTRINS.H>
1 X3 X* z f6 S3 `$ Z5 o% x - #include "STC15W.h") x' e( }" q5 ^' `( S. o
- 4 z6 M0 R7 c! b: q5 C" j* t
- 4 w, @) \: K# q& {& @
- /*
( I$ r g6 M* o3 e1 s - 1 Z! t/ v1 `1 t0 \ c3 n
- *---------------------------*4 c9 `( h/ ~& z% w) C
- |1 (GPIO2) \__/ (GPIO1)16|
4 J' a5 b- |" }& A! r! _1 ~/ Y4 I - | |, F# U7 a( j9 t6 {: J& E9 S6 E
- |2 (GPIO3) (GPIO0)15|
/ R r, _! \* m3 u! }; c# U, v" ? - | |7 K* z! x3 o7 ?7 [; h8 [/ }
- |3 (GPIO4) 14|
: T' b: y6 a j! H - | |' f3 n% I/ C0 ~1 W7 M8 o
- |4 (GPIO5) (DATA)13|# C5 h) l) |: u
- | STC15W204S |/ S1 \8 ~" I- W7 q4 y& Z) ^
- |5 (GPIO6) (LATCH)12|
, F! O/ \; [# i' b! _( w/ a# D% E - | |8 T- c: t$ f9 K7 C, G
- |6 (VCC) (CLK)11|$ u% L7 f( v5 q+ n3 s+ s+ q
- | |) h0 Q i& e5 o% s! l
- |7 (GPIO7) (TXD)10|
# u" Z9 f( m+ f - | |* B3 F6 M' L" o5 \% O" }5 w; X
- |8 (GND) (RXD) 9|" p) _: {* ?& g/ m4 ~
- *---------------------------*8 I; x2 T7 B( p" W: }
- Fosc = 12MHz, J1 x# H0 b5 y
" {0 Y% v5 m/ i; c- P1.0 -> 上) V+ i) Q* c- u; G, v* _
- P1.1 -> 左
- f9 m0 ~. Z0 _' o - P1.2 -> 下) M; M Q4 l' M4 y [* {8 d8 r
- P1.3 -> 右
6 m) D1 G! M& y+ {, z - P1.4 -> SEL3 j c& o; ~. L- p# f& d6 X
- P1.5 -> STA
. Z- p% g o' r7 G9 @6 m - P5.4 -> B* J3 t! P, L9 @* M* N' s2 Y! o
- P5.5 -> A, \/ d: S7 b! u% q3 `' F, ]
; K$ V, ?9 ^+ r# I" U: w- */
6 m s2 g9 K! N4 [2 R
( o8 {5 W/ Z3 `4 t
' A& l, G6 K7 ^% z$ u- sbit CLK = P3^2;% l& w- k0 f. W$ S! {% V; p! h9 i
- sbit LATCH = P3^3;% @& _ O' @ z5 ~: O
- sbit DATA = P3^6;
9 x" s, `6 C; q/ D0 H
" p9 r& s3 F. R* \" Q- bit isReady = 0;8 s! A2 G2 {' f" f) R) Z
- static unsigned char key = 0;
" m# e; X& k: W - static unsigned char buf = 0; // 双缓冲。这个缓冲区保存从P1和P5组合而来的键值5 ]2 @* p( ^ T' T
- static unsigned char bufReady = 0; // 双缓冲。这个缓冲区保存上面那个缓冲区的备份1 K5 _' P/ [0 |: P: i. q
- static const unsigned char data mask[] = {0x80, 0x40, 0x10, 0x20, 0x01, 0x04, 0x02, 0x08}; // A B SELECT START UP DOWN LEFT RIGHT。为提升速度,这个表放到RAM中
# d% I( [0 ?: y: @ - static unsigned char idx = 0;
" r: @: E2 }* f2 o% [
1 a; `* {1 L3 O+ n) k# ?% t
* F5 m3 p, ^4 _6 P8 o8 x; R- static void GPIO_Init(){2 x {$ l3 A5 z7 K( w8 b
- // P1口和P5口用于接收并行信号,全部初始化为准双向,依靠内部弱上拉输出高电平) E0 V" X$ ~: h- L- L* O9 T
- P1M0 = 0;1 |' ?& \# x6 a
- P1M1 = 0;. @/ j* q$ y9 S
- P1= 0xFF;
( d; s5 r f ]$ f" \8 Z - P5M0 = 0;
" L4 C+ Y9 }& i- R4 a. i u. T1 X - P5M1 = 0;- {# b. W1 S" u2 o. X( \
- P5= 0xFF;
) K# J2 N5 i& n& j$ N( P$ i - // P3口初始化为准双向
7 u! L) g5 q# y" s ] - P3M0 = 0;
3 U+ W9 ~8 T+ @: e- Q% s - P3M1 = 0;' q- U- |+ J) r u7 d
- P3 = 0xFF;# _: N4 S+ t) S. M" O* b
- }
1 Q' `* Q8 k0 S) w3 L8 E0 S - % s5 f1 D! h# y% B3 ~) ~
- static void Interrupt_Init(){+ z) B5 [: w- g9 }% ?& g( L
- // INT0中断(CLK)触发类型为上升沿+下降沿
9 W& v, b( ~% `9 Z4 [' z E1 f( T - IT0 = 0;; ?) P# [" }- ~/ Z5 q
- // 允许INT0中断
( f; H; z; l6 u' E, O/ B - EX0 = 1;5 C) m6 J$ P& a/ t/ w! W/ [4 B% b
- // INT1中断(LATCH)触发类型为上升沿+下降沿& S% F& a& x7 x
- IT1 = 0;
. B/ F8 t) |4 u% E' ]/ x8 Y. N0 @, M - // 允许INT1中断) Q: d. f8 Q- G
- EX1 = 1;
! q, b% e# c6 X& `$ l, K - // 开启全局中断
0 o+ q0 |9 T+ n7 ~8 a& g - EA = 1;
' k" o5 p& e. u' e - }% \' R9 [" z; h) o
- - V' }( s/ D3 c6 q$ j0 p4 @: p
- static void INT0_Isr() interrupt 0{: O7 d/ D ?. d3 x
- // 只有已经成功锁存才允许CLK移位9 I6 |: q" i- A# z
- if(isReady == 0)/ M' q7 i3 k1 h# ~$ d
- return;
3 Z& H( r9 s, X9 U) `* d - // 读当前引脚电平,如果是低电平则说明是下降沿,此时直接忽略该中断
# N& a, @, E( j/ w6 G$ x8 ^ - if(CLK == 0)
3 A( T8 c2 \: C5 y - return;/ K4 Q$ X8 l% w& {
- // CLK上升沿到来的时候,取锁存值的下一位输出
, d2 H2 _1 t; R O5 |- R - idx++;' J0 w8 N2 I" N% Q
- DATA = key & mask[idx];
- ~# t* k" p1 I6 l l2 s" x6 C - // 如果已经完成7次移位,则一轮读取完成1 \4 J" I$ i* c4 i4 @, ?" \
- if(idx >= 7)' b: Y" s+ J' I }4 _2 M+ }
- isReady = 0;5 ~) f/ p( j4 K
- }8 i, L! a p9 `$ {* Z
8 p, g8 r- g" z0 C3 C" I. p- static void INT1_Isr() interrupt 2{) h6 |* T2 S/ l2 H. E# t! w
- // 读当前引脚电平,如果是低电平则说明是下降沿,此时直接忽略该中断 n3 ?, o; \$ F
- if(LATCH == 0)- s7 _% B- f" a; K- L
- return;
0 w% p4 d7 ~; l8 { - // 当LATCH上升沿到来的时候,锁存所有按键状态,同时把键值A输出到DATA
" n3 e2 ~& ?0 h0 ^; @1 n# h - key = bufReady;8 m' s- ?+ r$ H4 o; X0 C
- DATA = key & 0x80;//mask[0]; // 为了加速运算,直接取表中的值而不是读表
3 z; K7 v: I$ k/ j - idx = 0;
' U* y8 _7 N9 q9 H" i - // 允许CLK进行移位5 O/ P T% t7 K6 H
- isReady = 1;
6 {+ S! f" B1 U1 L' a. Y - }
9 Q2 L2 Q1 @; {* {# v" n - / v( o9 @: r) o. D
- void main(){4 F6 u! H6 S$ V0 e& e/ e* E
- GPIO_Init();
8 R$ r0 W( y3 F8 M( { - Interrupt_Init();
; [' f9 P* S( k4 M# F9 z -
$ n( D: Q1 ?; V/ G! ?: s - while(1){
) k( q( _ e/ W" L" l' k - //PCON |= 0x01; // 进入省电模式! b$ k( ?8 h+ \1 q5 r% c) x* X2 h
- //_nop_();
$ d8 Q4 Y5 K2 w) X) C2 d - //_nop_();
# u1 U) a5 ]9 {6 E* n - //_nop_();; f# o% x2 f% L: |8 o
- //_nop_(); n1 e) W) p9 v' _
- buf = ~((P1 & 0x3F) | ((P5 << 2) & 0xC0));* H P5 @0 Y' o& L1 m0 R+ E X
- bufReady = buf;2 W# R/ I* h# j
- }6 X7 j: n# |/ [! Y% V
- }
4 L" ]5 L, x# I* L9 i9 R: Q
复制代码
- m/ J* M4 t v7 X" K硬件参数配置无特殊要求,晶振频率选择12M即可。% _, y+ C H' t( Q u3 t
7 c8 R" r+ O7 T, W这是编译好的固件。 |