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