本帖最后由 雷精灵2046 于 2019-6-14 09:28 编辑
. Z4 f6 X: `! M1 b/ {3 V+ i4 Z& _' e: h& R$ U* e* w4 W
没有用复杂的分立元件搭建发射接收电路,直接用的现成的2.4G无线模块jdy-40。
! q5 L# w4 q2 P- o% {; [4 i3 L0 U6 f0 l
6 \0 f; l! _! J: a! l
发射端靠3.7V锂电池供电,用了一片SOP8封装的STC单片机,负责产生连发脉冲,同时负责检测电池电量,电压低于3V左右的时候触发低压中断,低压指示LED就会亮起来告知该充电了。平时发射模块和单片机均处于睡眠状态,有按键按下才会唤醒,从而实现省电。实测睡眠状态下整机电流约1~2uA。板子洗了一下稍微干净了一点。因为对于微功耗设备来说,焊接残余的助焊剂可能会增加漏电流。4 B8 ]1 B& |9 _2 ~ u5 f
, r8 T7 b& F1 h( H* `% m5 b& U, g' ^
买了一个火花的FC山寨手柄,去掉里面的牛屎板子,并修正一下塑料挡板,把锂电池和充电模块装进去。6 c2 ]5 L# ~; R7 k4 s. \
为了减少体积,充电模块裁剪得十分小,并且更换了充电电阻使之适应这个小小的锂电池。这么小容量的电池,也不需要多么大的充电电流是吧?
, i; N$ \$ X6 a/ q然后统统用热熔胶粘上。充电电流比较小,所以发热很少,不会熔化热熔胶。# b6 U- z5 E; ]
/ c m5 _) q2 E
1 S% |- H* {1 { q4 ~$ X/ m7 G! T0 @) h
最后把按键、导电橡胶和电路板装上去。3 c0 Y) p; v* U9 X
精心裁剪了电路板,正好扣上,不会阻碍其他元件,也不会影响后盖和拧螺丝。, K# F/ u8 l% `& P+ d; P
% T$ R9 T4 H6 ~+ N+ B7 ^3 m+ V2 l0 A5 u+ j- P9 W: t: Z
完工!7 ?: F. g* t6 k. i
S" c* ?, r8 i0 c
: G8 L5 t! T) s; D r& j
这是充电的MicroUSB插座。旁边的小孔用于透出低压报警LED的光线。5 U" P1 s% q7 c+ d) C) J
' V: J) @) x5 P; i3 z. n# \+ {1 n; y: P5 ~# t8 T6 p# m- F
接收端用一片DIP16封装的STC单片机模拟CD4021。为啥要用单片机模拟而不是直接用CD4021?因为jdy-40模块接收端在有按键按下的时候输出高电平,无按键按下输出低电平,和普通手柄正好相反。当然我也可以用两片74HC00或者74HC04之类的逻辑门进行反相,但那就增加了芯片的数量。对于FC手柄这种工作频率并不高的设备来说,STC单片机完全可以胜任模拟工作。
4 Z0 s. o: L+ `! r. O! F
) o7 M* H) C7 ~/ p" y% g! i5 s1 `6 l) z9 j0 _, Y
2 ?# E5 U K4 m; K0 R1 e* j
电路设计有点小小的错误,所以有几根飞线。原本设计使用低压差LDO是XC6206,结果在做这个接收模块的时候,买的LDO居然找不到了!无奈只好用了AMS1117。幸好对于接收模块来说是5V降到3.3V,1117可以胜任。要是发射端手柄的话,3.7V降到3.3V,那就非得用6206不可了。2 ? `& h; a8 y+ a
1 F+ T/ D* a2 A+ Q1 i+ ]
实测十分灵敏,延迟极小几乎感觉不出来。可惜这山寨手柄的手感并不佳,尤其是方向键,软塌塌的。看来我得买个剪线手柄改装。' Z1 M& |4 R. C& K0 a/ a S
0 ]4 P: i" j2 a5 U这是电路图,有兴趣的朋友可以参考。3 {+ k. M9 M& T2 H
8 z+ @9 t/ O/ T& L' v- u" M( t! h/ s8 [( ^: _. |. t
我信奉开源主义,十分痛恨把技术藏着掖着。好东西大家分享嘛!所以两个单片机的源代码大公开!3 ]0 I) q( i3 h; y* ^
编译器:Keil 51。3 t. O1 s" P! y/ R1 X9 ]+ J* ]2 O
, E7 t$ ^3 ]+ H发射端:
% d0 x2 g3 q/ y' ?$ y* a0 m- #include <INTRINS.H>' x' _& o; q! P4 h4 P+ |( Q+ v
- #include "STC15W.h"
) v0 w$ S9 b$ M1 G - 8 |- }& W- h, t' K8 I
3 u2 H: Q& O0 P* A& E% Y- /*
9 G+ R" V# a$ B8 n8 _5 k - --------------------------6 x9 R$ B5 L9 H0 Q
- |1 (OUT) \__/ (INT1) 8|
0 q; |9 d2 \2 D) U9 H- J$ @ - | |
9 ^# D; x& k( \9 A" K. o# x - |2 (VCC) (INT0) 7|1 V/ J* G, y9 N0 l4 @
- | STC15W204S |
! q% y% Z0 y* U- ?1 N - |3 (LED) (TXD) 6|. n0 S; a" B. d5 @5 s
- | |
; n4 a3 M) J+ H. R - |4 (GND) (RXD) 5|$ t0 r1 R% W. M4 S* l
- --------------------------1 W7 w% y+ B J! G% H! k
! n; o+ i, ?/ | D9 h, |- LED ---|<|---[===]--- VCC# @( @3 ?" s/ X" S3 P) z I
- Red 330
0 ]$ G$ u( g$ O6 e - , |- H+ Z" w/ v( ] f. \6 P
- Fosc = 6MHz! f% _- u0 I4 O7 [# O$ Y
- */! y/ f6 ?0 e. z. R2 E- L) x
- 8 w/ n" l4 _+ W) j
1 L& H! _( m4 K% _) x4 D9 U+ ]- // 矩形波输出脚
5 ]" G; K1 K; S. _% W- s% G6 W/ u - sbit OUT = P5^4;
7 w9 X0 n H3 t$ \$ G - // 低压指示灯引脚6 l' }$ r* I, {2 C
- sbit LED = P5^5;' s" ]) e% L9 |. X0 D) D9 O8 s' i
6 i: S6 e0 h; t- // 停机标志位
$ _6 v8 O' g' O+ e; T - bit isReadyToHalt = 0;4 ~5 A( y/ G M4 A/ P1 w3 J
- 2 |9 l) \ T x, z" g5 _1 f- e
- // 矩形波次数累计4 X0 x# _; k3 p' D X! b5 n5 E' s
- static volatile unsigned char count = 0;
+ ^# A3 ]9 E4 H7 N3 }/ ` - - `& A5 s# _2 S. C4 C7 t
2 }5 @. S0 Z- }4 ^- r3 n8 L" V) y% U- static void GPIO_Init(){
9 l. p& O& H* Y8 m8 T - // P3口设置为准双向,默认靠内部弱上拉输出高电平
0 A: L0 f+ {2 z - P3M0 = 0;
8 X2 I9 A; ^3 B - P3M1 = 0;% Q; X+ R, [, K$ j
- P3 = 0xFF;. b+ V T0 w1 }' m1 T1 Y
- // P5口设置为准双向,默认靠内部弱上拉输出高电平
+ S3 q6 q+ C& H - P5M0 = 0;
% I- E1 `/ \4 ]) d% [ - P5M1 = 0;
4 i/ `% w7 E0 B; ?* G5 ^) J - P5 = 0xFF;
) T# {. m0 H3 s9 t2 c3 K* ~ - }
# K+ t P- G, |
0 q1 V6 [) E- B* ~* |; ?. Z# U( {- static void Timer0_Init(){& u S6 y* n" \8 g; y6 {. y
- // 16毫秒@6.000MHz
0 q2 `8 ]) r# ~# r1 h/ \ - AUXR &= 0x7F; // 定时器时钟12T模式
5 v& ?/ V* D* T k) n - TMOD &= 0xF0; // 设置定时器模式6 s& \9 q- |7 X9 a( V/ `
- TL0 = 0xC0; // 设置定时初值
( h# U8 r, e: s2 J: P) W$ f% T - TH0 = 0xE0; // 设置定时初值2 y8 }+ F; @, q7 R0 G
- TF0 = 0; // 清除TF0标志& g ?1 x* y( t( Y$ e! b# ] k, C
- TR0 = 1; // 定时器0开始计时
/ G, C. M- L+ u - }
' w x9 r5 U+ ^
) _# a2 V: B' w( x) j" n7 z- static void Interrupt_Init(){
% W' v- g! b! H; m2 h- l! ` - // INT0中断触发类型为下降沿触发
" o, c# h! x& e; E - IT0 = 1;
. _$ T8 F9 `4 ? - // 允许INT0中断/ n4 i9 O+ P. Y3 M7 I; Z
- EX0 = 1;. E3 U8 U! ~* z. ?7 q+ z" n
- // INT1中断触发类型为下降沿触发. X7 _4 S$ g1 @& C+ |0 h
- IT1 = 1;/ _/ Q9 A4 K9 X, x; ~* v
- // 允许INT1中断 a% A4 n0 Z( k' K& \1 Q' d- ~+ j
- EX1 = 1;
1 t: y4 \$ e- p) m( ?- c3 u& J - // 允许定时器0中断$ k d" i2 P; s- d& B& ^7 h
- ET0 = 1;
/ k9 G$ h7 ?, P% s3 q) Z" f" a - // 允许全局中断
5 {1 \' B2 Y }8 w# F - EA = 1;
0 \; `5 j1 n0 P - }
" j7 S4 F3 {2 d! L
o% i* C, Y4 L- static void INT0_Isr() interrupt 0{6 n5 z$ x( n2 |; `0 I+ E
- // 矩形波次数累计归零, U5 L! }/ N4 m; G+ G
- count = 0;% \" X9 C6 U' M% @- T
- // 重开定时器0中断' ]! U( z3 T! P \7 |
- ET0 = 1;) q5 n9 X* x" g0 e
- }! _" x5 }1 G+ \; p F7 _& w
- 8 [6 `3 I$ U6 E: n; J- k1 c
- static void Timer0_Isr() interrupt 1{/ \, @1 U# ?/ Q# i2 M! }; T. e
- // 输出矩形波5 w p6 U. ?! D7 X0 E9 x
- OUT = ~OUT;
' E' C6 w& I8 q' M3 j. q - // 累计矩形波次数1 a6 J/ n1 D ?
- count++;$ X) Y) O' G" Q, i+ W
- // 超过250个矩形波,准备停机5 X! y: s, L7 {6 m" |. Q! {
- if(count > 250){: a% _" k' k0 c3 p( r7 ]4 t! v. N2 ^
- count = 0;
# [- @. _' S1 B% L/ O7 i - isReadyToHalt = 1;0 a. N- F8 L2 |* |5 P
- }$ m2 z- @- M, t+ q F
- }) W3 ]& W' t" G1 q: F5 V
6 v V4 I2 I4 w! b; m4 d# U- static void INT1_Isr() interrupt 2{: f3 l' c' {* e! h- {- {
- // 矩形波次数累计归零
5 F. g, @/ C* I4 V8 P0 X - count = 0;% I6 s0 ~! r* o3 s/ d5 M6 ]) u; D3 E$ R
- // 重开定时器0中断5 v7 Z5 h# }* q- _3 F8 b3 y
- ET0 = 1;% Q% }- V8 M& ^+ G
- }
% R% w! B5 A$ U
. M- S: C7 m, c8 ]/ k2 j2 [/ C. D. s9 ]
9 H, Z* H9 g4 v' r# o- void main(){
* A7 G; m. M6 Q+ U2 _, }: l - // 初始化P3和P5口( [' q' G8 x- G
- GPIO_Init();
2 r, F" r; q" ^/ c. o0 i - // 初始化定时器0
3 U# E; l2 J+ G( D0 ~ - Timer0_Init();; h# H# o0 x- G3 Q! ?. q
- // 初始化中断
" m) l2 {# U7 p+ [# o - Interrupt_Init();1 ~" \& h+ I- O; w9 T
- % N3 W. f0 f# J6 [
- while(1){
8 m, s" U6 D v# A% N7 j - // 检测电量: G. H2 v# A! Y
- if(PCON & (1 << 5)){
( w R+ S$ r0 P4 d& D - // 如果电量低,低压指示灯亮- I0 R# i- j& e: s: L8 f9 h9 B8 H( ~
- LED = 0;" e( V/ N! {# {' q. p
- // 清电量检测硬件标志位! }3 a9 n: T4 p/ j) P' ~
- PCON &= ~(1 << 5);
4 _; A" E, M$ V/ d' x/ m `. y - }else{) A" I3 ^8 k, {3 ]; U1 S8 Y! [
- // 电量正常,低压指示灯灭8 H; }( B6 u" I5 |* T9 b
- LED = 1;
! j9 {. ~! I! H# E1 P1 n - }
8 M; X5 s. X$ s - . k N j5 N0 d4 d
- // 检测到停机标志
! Z3 S( U/ Q' w" y% N0 W - if(isReadyToHalt == 1){
( V9 u7 h& a8 j% n$ m - // 暂时关闭定时器0中断" N3 d( r1 k* U8 Z: H
- ET0 = 0;
. k! i+ s8 f# E: F7 M$ T) N - // 停机之前先把矩形波输出脚置低电平,以方便INT0和INT1唤醒CPU
' _# C4 C" f, ?5 u+ @" ^- p9 Q7 K - OUT = 0;2 J- }3 t F- H1 ^) \& w# z& h2 k
- // 停机之前先把低压指示灯灭掉,以省电
3 D0 T% r- |! g - LED = 1;
4 u) S; U* x$ I4 R! K - // 进入停机模式; A9 Y5 h. q) G4 A7 _) e
- PCON |= 0x02;! d3 U9 f( P3 V X0 a
- _nop_();
6 T2 {* s) q/ Y - _nop_();0 ~6 K; G$ R7 j" F' M
- _nop_();0 J0 B# n9 u0 C3 Z5 v7 a* M( v
- _nop_();
5 {! L5 C9 U, v: ?0 X - // 唤醒,清标志位) i5 a! y- `8 c
- isReadyToHalt = 0;+ G4 |4 u% E6 a% b& G
- // 重开定时器0中断3 |" W/ S t4 h& D/ n: e1 b
- ET0 = 1;
) [( ]' ]) [! w% d6 w - }7 Z* w# h. i* i* I; K
- }; A3 c) }4 H' T0 S
- }$ y# }9 R: y. A
复制代码 / j+ o( v" ?1 f- ]4 p" e+ ^
硬件参数配置:+ d5 ?+ H- z/ M; ]+ X$ O+ g$ j
- w4 Z" `2 C \0 a
; \* H' s% m" ^* o* O; i# a0 e接收端:
* c: \8 H" |- _8 z. T' o0 }- #include <INTRINS.H>
/ o+ G3 N' i+ q4 ?2 C- T, U C! A - #include "STC15W.h"
/ y0 m* q& j% W$ D% J9 K5 u5 E - ! U/ x$ |- o1 `2 c' U
- x5 `4 t, V+ g4 Q/ V9 Q
- /*
" i+ u* g/ \( E8 H7 ?; u' l
0 O- \" ~. t; }- x- q+ S- *---------------------------*
6 K2 g# i8 m6 J/ H; L* D; S - |1 (GPIO2) \__/ (GPIO1)16|
O$ ?! H" T3 _3 E - | |
* K0 ^( U/ }) t: j - |2 (GPIO3) (GPIO0)15|! [5 W3 g3 H2 {, c2 A7 ]" M/ j5 U
- | |8 i4 G( Q( g( \) T' J. V$ T( ]7 _& l
- |3 (GPIO4) 14|9 `2 f' L6 z0 z
- | |5 p8 ~, k" H8 X; \
- |4 (GPIO5) (DATA)13|
8 w; W4 b9 f" _& y) [# w - | STC15W204S |
+ D" _7 k+ Q) s$ h; |" I* A( p - |5 (GPIO6) (LATCH)12|- r2 P A# m5 a# Y6 W
- | |/ w' l- R+ `6 P) j: L
- |6 (VCC) (CLK)11|. C9 E- B( V6 v# k1 V
- | |
7 ^' e- S4 K9 I- _1 X4 B( g; c - |7 (GPIO7) (TXD)10|8 V. S6 Q- p/ i! X3 Q
- | |9 d! j" [5 x7 Z" s7 e
- |8 (GND) (RXD) 9|3 u: L7 E$ \8 A6 K ]+ w
- *---------------------------*
2 B# A0 k& j4 Z# t4 ? - Fosc = 12MHz
7 l# f/ A! l( @( N+ ^! Y3 E
( _& @& ^7 p5 a6 G- P1.0 -> 上
: ]) N+ K2 v2 S4 B Q - P1.1 -> 左
* Y( w: ^% R7 S1 ~( i% h1 T$ M( v - P1.2 -> 下" i" d# ^1 y3 @$ e6 C& i$ l
- P1.3 -> 右1 L, @4 ]; G% \( ?% G, V& }
- P1.4 -> SEL+ u/ r2 E! T. g) Z
- P1.5 -> STA
$ j1 c/ {; C) Q6 p% X - P5.4 -> B5 D( d5 w5 S2 N0 n4 e
- P5.5 -> A+ [3 X6 k# @- s9 Q
( W6 a* t/ ~" {- */
. D) T% ]1 A" X3 N. T
2 ^$ J! D4 h/ Y( R- Z- 7 m9 L4 A* c8 g
- sbit CLK = P3^2;9 x- D. y& M9 T7 V! ?* c4 Y7 `
- sbit LATCH = P3^3;# n9 y! Y. P2 o8 q$ X
- sbit DATA = P3^6;
+ h1 \% f9 U3 f - ' s" m3 D* B( c. ?7 H- M" m
- bit isReady = 0;' ^3 {" R! Q" M1 |/ \. I
- static unsigned char key = 0;
) a6 F: `4 a. P, y) N% N) t - static unsigned char buf = 0; // 双缓冲。这个缓冲区保存从P1和P5组合而来的键值3 z- \& M+ R% e) [) f0 ~
- static unsigned char bufReady = 0; // 双缓冲。这个缓冲区保存上面那个缓冲区的备份" s) z0 P# I( [4 t
- static const unsigned char data mask[] = {0x80, 0x40, 0x10, 0x20, 0x01, 0x04, 0x02, 0x08}; // A B SELECT START UP DOWN LEFT RIGHT。为提升速度,这个表放到RAM中% A# W3 i: Z% H1 F
- static unsigned char idx = 0;
* Q* e5 ^" w0 ~& R |2 m7 D6 T
0 I5 P! z# H9 h2 s7 R- % r* s. w+ I# O" A9 s
- static void GPIO_Init(){1 P' q& l2 l1 _% J0 t' H
- // P1口和P5口用于接收并行信号,全部初始化为准双向,依靠内部弱上拉输出高电平# Q2 e5 t; i" w, K' F' A
- P1M0 = 0;) ~0 L% O+ S6 O- [* m, W
- P1M1 = 0;' n* X* C/ R* C* x9 B7 E
- P1= 0xFF;6 Q" O. a7 Q% z" n1 q1 [8 i" m
- P5M0 = 0;' s( n, [6 P* j+ W9 Y
- P5M1 = 0;
5 [0 O+ t* |0 k2 [ - P5= 0xFF;: D8 m% z9 h+ a. s0 @" e E) l
- // P3口初始化为准双向$ r# L# b$ v2 `+ |
- P3M0 = 0;
+ H5 R/ x1 m; W. q9 ? ?6 x ~! a - P3M1 = 0;6 [$ i5 @; X3 z" z6 H& R; b4 [; Q, F
- P3 = 0xFF; W3 l* S% U) o7 F% n
- }$ e& g# @1 d! o8 H) \8 G
- & |0 Y4 V' I4 {* V
- static void Interrupt_Init(){
, a! R0 y5 y% R1 z - // INT0中断(CLK)触发类型为上升沿+下降沿
" o( `$ V( ?( M; f - IT0 = 0;# a! U* Y: p$ p. L* S0 w8 d# r
- // 允许INT0中断
. n0 B/ j4 B, h) T - EX0 = 1;/ n) J. h i. L- j& V
- // INT1中断(LATCH)触发类型为上升沿+下降沿; o5 |$ p3 u5 f2 x6 u5 `% u$ K7 r5 d
- IT1 = 0;
& k# i7 J! k7 z: `3 l/ d - // 允许INT1中断
. C% y* P; J4 @" D9 }. k$ ^ - EX1 = 1;- t; k# ?+ u5 E8 z9 r2 O
- // 开启全局中断
0 s1 k' r2 d9 v - EA = 1;8 _, ]$ ]3 |* n
- }
0 p) W3 u- D, X& f% v$ p - 0 K. `; d. q) @& D/ D( I n
- static void INT0_Isr() interrupt 0{
! p, j$ B0 H, C, r( K! {$ o" x - // 只有已经成功锁存才允许CLK移位
, g, ~, g5 A! X, c- E, P4 A' ~ - if(isReady == 0)6 `6 k8 h7 v* D7 i
- return;! s/ m! N0 [$ u4 u* E
- // 读当前引脚电平,如果是低电平则说明是下降沿,此时直接忽略该中断
9 E- F- R& B: i7 h - if(CLK == 0)
W( d/ e* E' \ - return;
# ~' S! ~* L8 H& u# t1 A1 W7 D8 ]+ v - // CLK上升沿到来的时候,取锁存值的下一位输出& ^$ ?+ C7 o# ^; R& d5 E9 k
- idx++;5 r) i2 q- M1 R W& E M4 Y
- DATA = key & mask[idx];
! Q" I, e# g, {1 } - // 如果已经完成7次移位,则一轮读取完成2 o: z' e. g0 ?8 S6 V! [! |
- if(idx >= 7)
* y7 I, c# m8 M! R3 k) u - isReady = 0;- X" z8 l: n& R {- b, _4 c
- }- H% N9 z( v8 s- p C
2 t+ e3 v; v: x9 T) M/ a6 c9 p8 M- static void INT1_Isr() interrupt 2{
' J+ Q; w% l ` ?5 l% z - // 读当前引脚电平,如果是低电平则说明是下降沿,此时直接忽略该中断* G3 X! U, X. k; I7 V$ |- D% h
- if(LATCH == 0)
# a+ I: L; }/ m% L! ? - return;
( P& H7 s9 h" C4 Y, B - // 当LATCH上升沿到来的时候,锁存所有按键状态,同时把键值A输出到DATA
, ^' w, f" l' {. f3 }" ]- M+ r# \ - key = bufReady;8 y9 p; J1 ?9 s& ?
- DATA = key & 0x80;//mask[0]; // 为了加速运算,直接取表中的值而不是读表
' G2 ?, ~6 E. e8 ?$ p - idx = 0;
0 }! x5 Z8 R( b3 G - // 允许CLK进行移位
; X! s: t9 r% W) }2 A2 |1 j - isReady = 1;% P4 q, N% s: V5 |& |0 X, O1 o
- }. C$ r" r8 B% k9 k
- 3 j8 v* b- _& A: a/ Y9 H9 v
- void main(){8 L4 F) P4 W/ \% t+ h5 O
- GPIO_Init();
. m4 c9 P3 e6 H- n X# R3 I9 j2 R - Interrupt_Init();( l/ I) a; X- J$ E1 k! P. v
-
/ V4 T/ V5 o" J) ~" [ - while(1){
1 G* c) s' Q6 r% a5 m1 {5 A - //PCON |= 0x01; // 进入省电模式" v( q) B1 ^; L$ {! T
- //_nop_();
) `( v( u$ q6 O( k3 l1 J( N; { - //_nop_();- S4 i6 A" I7 S
- //_nop_();
~2 b; K) ?- t6 B - //_nop_();
9 ~/ F+ ]0 `) x* X8 q/ W1 F - buf = ~((P1 & 0x3F) | ((P5 << 2) & 0xC0));
' L) R* \: t5 F/ U - bufReady = buf;
0 C0 p4 A9 ]0 \ - }# u9 y: G8 |2 e6 L B" b
- }
, I3 S. x) Q" W( }
复制代码
9 J+ |# w- ^# a; T硬件参数配置无特殊要求,晶振频率选择12M即可。* H& }# r2 p; W$ J* D
: b; |+ w z, N& C
这是编译好的固件。 |