本帖最后由 雷精灵2046 于 2019-6-14 09:28 编辑
i0 ]/ T J" J2 Q
% K# l1 M/ z# L& q B* e& O8 Y没有用复杂的分立元件搭建发射接收电路,直接用的现成的2.4G无线模块jdy-40。
* H. C" y9 p( A. }' q3 ]% ^: A
( G e4 ]" B% {+ o3 s: A- b5 B7 s发射端靠3.7V锂电池供电,用了一片SOP8封装的STC单片机,负责产生连发脉冲,同时负责检测电池电量,电压低于3V左右的时候触发低压中断,低压指示LED就会亮起来告知该充电了。平时发射模块和单片机均处于睡眠状态,有按键按下才会唤醒,从而实现省电。实测睡眠状态下整机电流约1~2uA。板子洗了一下稍微干净了一点。因为对于微功耗设备来说,焊接残余的助焊剂可能会增加漏电流。; U' }5 s/ M* o0 W2 [
' C1 m) A' Z! I; g% p7 J, \2 j买了一个火花的FC山寨手柄,去掉里面的牛屎板子,并修正一下塑料挡板,把锂电池和充电模块装进去。
7 U: C7 h+ |9 {2 ~0 G# X为了减少体积,充电模块裁剪得十分小,并且更换了充电电阻使之适应这个小小的锂电池。这么小容量的电池,也不需要多么大的充电电流是吧?5 u: f: m+ F# T# A% [
然后统统用热熔胶粘上。充电电流比较小,所以发热很少,不会熔化热熔胶。# G9 ~, a$ w7 O( x0 w
+ r e. |* ?/ r$ P
* o* b, }. b) k$ r1 j" H" F
1 ]/ ]2 E$ O4 L) s/ r7 `! I. w7 _最后把按键、导电橡胶和电路板装上去。
( a- e$ h( O9 L) c; I) q8 e: @精心裁剪了电路板,正好扣上,不会阻碍其他元件,也不会影响后盖和拧螺丝。# K9 p1 j' v$ f
) {0 O' c3 c: q0 _9 N
* v. Q9 C- \8 Q/ ^: Z$ a& p完工!! o, _0 s% N* _9 ^" z9 ~3 y
/ C" ]% B% |, b- R" _, ?2 W/ s1 X- |
这是充电的MicroUSB插座。旁边的小孔用于透出低压报警LED的光线。
5 M* P- r0 Z% p. ` ]
% @0 b) f: N5 N- e
( A5 a, ?5 r; o! W1 A4 s. ?接收端用一片DIP16封装的STC单片机模拟CD4021。为啥要用单片机模拟而不是直接用CD4021?因为jdy-40模块接收端在有按键按下的时候输出高电平,无按键按下输出低电平,和普通手柄正好相反。当然我也可以用两片74HC00或者74HC04之类的逻辑门进行反相,但那就增加了芯片的数量。对于FC手柄这种工作频率并不高的设备来说,STC单片机完全可以胜任模拟工作。. S* m# O5 l9 _/ k; W0 {
% Y( \* U3 j" O& V$ @$ \( w
$ |0 ~+ P! F% x" n1 J
. N6 q' P. V0 ?3 G4 C$ w电路设计有点小小的错误,所以有几根飞线。原本设计使用低压差LDO是XC6206,结果在做这个接收模块的时候,买的LDO居然找不到了!无奈只好用了AMS1117。幸好对于接收模块来说是5V降到3.3V,1117可以胜任。要是发射端手柄的话,3.7V降到3.3V,那就非得用6206不可了。/ p' t* }# o$ B( U4 a6 @+ ^; L, V4 e
: J, k, N* x4 i4 w: _6 J* L- S ^实测十分灵敏,延迟极小几乎感觉不出来。可惜这山寨手柄的手感并不佳,尤其是方向键,软塌塌的。看来我得买个剪线手柄改装。* r6 j2 p$ Z% I3 Q& B1 o" t7 ]$ F2 ?
# Y9 ~# g3 L) j4 P- Z0 d! G5 o6 _6 B* x
这是电路图,有兴趣的朋友可以参考。" I1 A- v- Y) q0 Z
2 d( }1 M& _1 \! o j! R
" K2 ^4 e6 c6 x$ L* M( g& t0 y我信奉开源主义,十分痛恨把技术藏着掖着。好东西大家分享嘛!所以两个单片机的源代码大公开!
) C4 |$ T) {4 O! Z编译器:Keil 51。
?/ [2 e8 c. o/ c6 n3 j' S+ x5 L5 ~! w8 p C
发射端:
a9 a' g2 d: r4 z. V; ?- #include <INTRINS.H>
+ E6 u8 H& q0 \$ I0 `" t$ T - #include "STC15W.h"8 p& Y2 J8 i/ Y4 h
% q, L; ~3 t. T; t' h0 [3 a7 v
5 X7 Q! N4 R# N, H- d- /*
- u; B# h/ A3 Q0 }; H - --------------------------! y" b. z0 ~, M& |" I
- |1 (OUT) \__/ (INT1) 8|
4 F6 Z% x+ g) I+ p0 P: d - | |4 ]1 }" S/ [2 q1 e
- |2 (VCC) (INT0) 7|; |- h; ?, n" o3 p3 P
- | STC15W204S |, T/ n+ x+ x- [, a/ J+ ~; `# v z6 M
- |3 (LED) (TXD) 6|5 a J* ]5 J: i. a) q$ `$ x: D
- | |
. z& Q4 ^7 f9 [. q) Y - |4 (GND) (RXD) 5|
6 N8 K" }0 M" q% @5 N- ` - --------------------------
7 x6 h) n# w" A w! l( b; c5 h* l) {
6 ^6 e( `6 E: c) k- LED ---|<|---[===]--- VCC$ V# a8 s9 y! A
- Red 330% x" [. X' e: o' \, I* D
- 5 m9 G8 ^* Y1 w0 O* Y( S. ` D
- Fosc = 6MHz. |3 W8 ^1 K6 Y% }( ]
- */1 d h0 x2 Q( [* `
- 8 I- P& z; A" F j9 |
- $ s2 j4 a8 i. [3 x+ k& O: o3 l. H5 J
- // 矩形波输出脚
. D+ ]* x& e6 b# x# e: f' r( y - sbit OUT = P5^4;1 ~& Z ^7 n5 e, K4 X- d
- // 低压指示灯引脚
( h( `, q3 D2 V6 n! L9 }3 ]& ~ - sbit LED = P5^5;
4 Q) H( w. T/ u3 W
9 H' `7 u9 ]9 N; t- // 停机标志位& @" ?4 n1 g* ^& _
- bit isReadyToHalt = 0;
* Z7 N! |: r7 x' D, _ - : U8 j4 H, t; d5 j# V# @
- // 矩形波次数累计
3 [! J& l$ r, e; f" D( e - static volatile unsigned char count = 0;
! k) J3 P! {. N% {/ I
% J- ~ D) D' Q) m. R) d- t
9 N2 t5 W4 k2 c3 G- static void GPIO_Init(){5 } i7 s3 `% B9 q8 v6 b+ U
- // P3口设置为准双向,默认靠内部弱上拉输出高电平* C) g- U' u$ a1 e
- P3M0 = 0;) x7 Z6 O; Q: g
- P3M1 = 0;5 x8 x" l7 y" [4 }
- P3 = 0xFF;* j. x P3 W( W
- // P5口设置为准双向,默认靠内部弱上拉输出高电平; A: _8 \5 y7 }3 S
- P5M0 = 0;
- V/ z1 F* O7 O, S1 ? - P5M1 = 0;
, ~7 _. ]( o/ F% T* R - P5 = 0xFF;
& q1 X% e$ Q' ^4 `& Y$ v - }/ V4 U6 B9 W6 J9 G: z5 G
' X1 @' Z7 q7 B+ ^% S" B- `- static void Timer0_Init(){3 U% v& F/ R! Y$ B6 D9 v7 J: r' N
- // 16毫秒@6.000MHz
8 ^* \ g1 D* G( X4 @8 Q, C - AUXR &= 0x7F; // 定时器时钟12T模式
4 F' D4 \1 }6 P - TMOD &= 0xF0; // 设置定时器模式& U- y: D1 Y& X7 b1 ^1 ~& k
- TL0 = 0xC0; // 设置定时初值# c, a+ H9 H3 [0 d2 m- q
- TH0 = 0xE0; // 设置定时初值
7 ]+ G1 |% }) P - TF0 = 0; // 清除TF0标志
d3 q$ f7 _! U4 j/ d; `8 x0 Z - TR0 = 1; // 定时器0开始计时8 Y( \) G$ c! |+ W4 I
- }
( Y" _8 q7 h3 I1 o7 Q
0 y& S2 r1 V& Z e- static void Interrupt_Init(){9 _9 E$ T0 t- k4 b8 v3 }
- // INT0中断触发类型为下降沿触发, ?% g$ G3 z+ `* {+ M
- IT0 = 1;
3 }% t0 M% A! Z e - // 允许INT0中断. m- U# ^% O$ `& m8 S" V
- EX0 = 1;9 x! p/ X5 w& Y1 \6 m6 ~& u
- // INT1中断触发类型为下降沿触发
+ {( y* Y3 Z7 I" S3 j - IT1 = 1;
* w) i* f3 L- H/ V* q - // 允许INT1中断7 U9 b+ ~* V. [" r; n, U/ y
- EX1 = 1;
2 m( y7 P5 _+ Y% A5 C - // 允许定时器0中断" q. T+ r B& _1 M, j5 }
- ET0 = 1;
- L& F! k% Y3 s3 p0 ~# e5 u) N/ R - // 允许全局中断. q+ X1 f" n! E$ H U
- EA = 1;% f, X8 Q4 O# s" _+ F
- }
* ]! d5 x/ \& l
5 s6 }) Z. M- I- K' C: @- static void INT0_Isr() interrupt 0{- V0 A6 y5 u& z2 f6 G H
- // 矩形波次数累计归零+ V/ o# \7 ^4 a6 v7 ?( S
- count = 0;
' h4 ?* L8 u7 m$ S2 ~" A B - // 重开定时器0中断& m& R" E6 x" A1 ~6 G# X( y
- ET0 = 1;9 j+ \4 X' }% a) z5 m) s& o* _
- }; J; u6 `2 E! @9 H. K7 f* ^
* N5 g# n" o1 M2 O- static void Timer0_Isr() interrupt 1{
1 j; \2 r: Z& o1 I% d - // 输出矩形波
e: e% r! c7 V% ]6 D" M - OUT = ~OUT;
s8 E6 M! {: j9 A3 P - // 累计矩形波次数
5 }9 N/ M R3 P - count++;) Y4 [0 a" M l7 X, u
- // 超过250个矩形波,准备停机) r. F0 o! g3 _' `4 p
- if(count > 250){# H. ~ B3 A# X7 o6 Y6 Y9 Y
- count = 0;3 h# ]3 h Y+ d9 M2 p
- isReadyToHalt = 1;
% B" W, R8 I. J, J+ o/ c3 X5 w - }* ?1 l5 ~" L% G' T% l" d/ |
- }
/ ^: W( Y( F' Z. a; y! Y* u
: Z/ t" y) A. u- static void INT1_Isr() interrupt 2{7 y5 X& B2 o+ o$ d5 [
- // 矩形波次数累计归零
# M6 K( u6 R6 R- t - count = 0;
- a) W# H! e* j, h1 K$ o6 i' R9 C - // 重开定时器0中断1 V) B# |: H- F6 L3 I" D
- ET0 = 1;/ B1 D& g: C, n$ u1 u, y
- }
3 w: s' ^: G, e2 B+ ?7 M
- p O1 ~7 t, u" R0 p; |: X5 x$ r
" H# g* D! y9 x9 G0 y- void main(){" r- R: ~, |5 y/ G4 g0 b3 A4 P
- // 初始化P3和P5口- N4 ?, z$ w1 C5 b% M* g
- GPIO_Init();1 g4 X( O& U+ }2 f
- // 初始化定时器0
# q" T4 G) c% g/ R - Timer0_Init();$ `6 r* a& ^: T( ~
- // 初始化中断
$ U) U) |0 B) J( O$ y' q! s S - Interrupt_Init();- g6 s6 g7 V3 d3 g( r- T9 O
-
5 Q1 q5 ~" q2 Z! p4 h: G F; f - while(1){
* m2 H, t' n1 W& h - // 检测电量- W( m7 v" z9 a( Q! |) O
- if(PCON & (1 << 5)){4 I! O/ Y% j* p* e
- // 如果电量低,低压指示灯亮
# B. P; a& n$ R - LED = 0;5 }. u7 A7 S7 ^0 q
- // 清电量检测硬件标志位) X- g0 O8 E6 s6 Z4 D e
- PCON &= ~(1 << 5);; ^2 K: m# I8 z2 t
- }else{" ^0 I; s) B4 ^9 X! K! ~
- // 电量正常,低压指示灯灭- h. u# Z& o: V* j5 R9 `
- LED = 1;
& Z9 f: {6 U4 k+ L c1 p5 A! b7 T X$ H0 c - }* _; [" ]# d) a6 P4 X/ g8 q- b6 u9 G1 H
% j. t. Z7 f0 W; q- // 检测到停机标志
: y& u. H& |! S$ Z# s' v/ k - if(isReadyToHalt == 1){5 j2 w$ q# B2 z+ {2 b
- // 暂时关闭定时器0中断
( u4 b9 j, o$ p, V X - ET0 = 0;' F; }# F% l/ i$ h4 G& e& U0 _
- // 停机之前先把矩形波输出脚置低电平,以方便INT0和INT1唤醒CPU* T1 b/ H+ P3 t5 q
- OUT = 0;& X1 K- h# B/ |. p! L9 l# h- \
- // 停机之前先把低压指示灯灭掉,以省电5 ^, j+ Z! a$ ^ @' M3 P, f
- LED = 1;; P7 y i) h! Q
- // 进入停机模式: g' o2 V6 {+ G& w4 }1 w
- PCON |= 0x02;
- Q7 `% }( Q# ?" h& F$ S - _nop_();: l7 h' s! a" z; \
- _nop_();
/ ]# ?& b: L7 H) W8 E - _nop_();
9 R2 y4 h: g: c' \/ P - _nop_();
" d! L8 h7 V/ b0 E - // 唤醒,清标志位
' E# a1 u+ I9 _ j" m) \5 e. \ - isReadyToHalt = 0;
7 t# D! G2 F2 ]% Y, j - // 重开定时器0中断
0 w$ v' e1 t4 ~5 S1 R1 g3 o - ET0 = 1;
r) `2 D' `& |: I+ O6 x: y# t - }' ]# R+ @+ q x/ c
- }+ d2 r! r# I8 L# T8 r# b/ K9 t
- }5 G; t" G( ^" N
复制代码 2 j) D' r: ] {1 t K$ D
硬件参数配置:1 ^: o) L: O5 z4 X6 M9 ]' ^
5 c1 J2 S! T$ p- s9 c G& ]
! ?" z1 V, u, q; h/ V7 @! T$ U接收端:
4 R( w4 ?8 r( u% W" I- #include <INTRINS.H>, I, h( B, d2 ^
- #include "STC15W.h" x0 C+ W, e% s
- 6 R5 n \+ I9 k! D
- . i( z [/ J/ m: h9 M7 ?7 H" Y+ V9 W
- /*' J8 n: V( a6 H3 Y
- 1 p4 F4 Q9 A3 O" Y5 q3 h2 T
- *---------------------------*
% x3 {8 ]- b1 {$ r( d4 J3 O - |1 (GPIO2) \__/ (GPIO1)16|+ S; R1 C8 d: h
- | |3 B7 g" F7 X D0 E
- |2 (GPIO3) (GPIO0)15|
+ I4 d6 S' \, G2 N9 ^0 R - | |9 c1 n* w2 m: H6 h Z9 K
- |3 (GPIO4) 14|! D+ H) v! A `
- | |
, e' E4 C5 B) i$ ^- V9 x R - |4 (GPIO5) (DATA)13|) K4 l. ^4 D/ L2 B3 L
- | STC15W204S |1 w- G( H, x* f% h! W2 A8 E4 ^: A1 E
- |5 (GPIO6) (LATCH)12|& c5 u& d* q' X. L7 ^" q; Y0 g7 q8 L
- | |
$ c) d% I0 q* q! q$ E) l | - |6 (VCC) (CLK)11|
1 e! M' O) Y* O7 D* b) U - | |
! `- g5 Q T. p+ Q1 e5 p* B* k% U - |7 (GPIO7) (TXD)10|- H! G( J2 D7 C2 W: e& j
- | |9 o8 Y3 S% x7 V7 v9 j- @- a
- |8 (GND) (RXD) 9|
1 r% A7 j/ G) L/ x( m* S) f# n7 T. w - *---------------------------*
! { s6 _8 U+ @2 @ - Fosc = 12MHz% }- L' Z3 N" H6 Z
- 0 l9 @0 L) u4 w* P/ o. Q; K
- P1.0 -> 上, n+ T+ \) C9 i( A8 B
- P1.1 -> 左
! f9 F' E% N( Y& D" i - P1.2 -> 下
% i5 s/ C, @1 }" c W( J - P1.3 -> 右9 k$ r; v$ F4 O
- P1.4 -> SEL5 R9 r: v( S1 i; w& K% K' v
- P1.5 -> STA
3 m3 F9 c4 u/ M - P5.4 -> B
* U" x' q! j; g" i' W - P5.5 -> A
7 c {) t w, V1 u( d9 ^/ `) p
, @% Q4 _$ o) F/ F' V; a; ~) r- */
# p5 H# F4 F B0 c3 O0 c - & ^6 }8 U+ C# A* E0 k) ]
& u$ l: T* S( D- A/ }- sbit CLK = P3^2;. d+ n4 d$ ?& J; h1 d
- sbit LATCH = P3^3;' S) h, ^: Q) a4 D
- sbit DATA = P3^6;% g' X S3 C7 h; g+ B1 R2 q
# r/ Q$ _4 | f. s$ d6 g. }- bit isReady = 0;% S% l+ I0 C& s3 l
- static unsigned char key = 0;* k! }4 Q+ p* I+ f# D. t) Q
- static unsigned char buf = 0; // 双缓冲。这个缓冲区保存从P1和P5组合而来的键值
# K, [* V# l8 [* C$ M5 f - static unsigned char bufReady = 0; // 双缓冲。这个缓冲区保存上面那个缓冲区的备份8 G5 ~4 m. {0 c2 q3 G& q7 d6 V
- static const unsigned char data mask[] = {0x80, 0x40, 0x10, 0x20, 0x01, 0x04, 0x02, 0x08}; // A B SELECT START UP DOWN LEFT RIGHT。为提升速度,这个表放到RAM中
! p7 v7 @- ?+ ~' _$ X U - static unsigned char idx = 0;
8 t/ H* T6 t+ q7 k+ u! U4 G: g
) K/ a9 Y: d2 k* ~
! p/ B! h) w' y# o- static void GPIO_Init(){7 b) f ~0 E5 M7 t7 z6 z2 \
- // P1口和P5口用于接收并行信号,全部初始化为准双向,依靠内部弱上拉输出高电平
! V! F f9 u2 ]7 Q# q: ` - P1M0 = 0;( o5 w# O* u* A6 E% Z& q4 c
- P1M1 = 0;
% o. B( y" b8 R% }# h - P1= 0xFF;
( S* ` q) w. A) ~ - P5M0 = 0;9 d0 @1 b' y6 ?' M
- P5M1 = 0;
% R- S6 }" B4 v; I5 v& H5 ] - P5= 0xFF;! Y+ w# ?6 Q& b) E: H+ l7 t
- // P3口初始化为准双向
. |8 ]' _8 N& [* \ - P3M0 = 0;: @2 j" f0 P% W
- P3M1 = 0;" G8 l" J! @0 C; H$ L" j7 o! J
- P3 = 0xFF;
# W! Y- K {6 m y) [% k# k3 H - }( K8 S; p, ?/ c$ k
- - |1 |1 R& l* X h3 f& G
- static void Interrupt_Init(){
; U8 v# ^- G0 ^- m' { - // INT0中断(CLK)触发类型为上升沿+下降沿
1 ^; Q6 e) }$ Y% u& h, N - IT0 = 0;
+ ]5 Y7 {8 {' L, ]* l - // 允许INT0中断
4 N( e/ m! N, L - EX0 = 1;
: X7 X4 n' N: S* ] - // INT1中断(LATCH)触发类型为上升沿+下降沿
1 u- V# D) `8 i; s! M - IT1 = 0;1 i9 S( N0 }. Z$ S( m6 U" k
- // 允许INT1中断0 j5 H0 r! I7 o) t% x& B
- EX1 = 1;: e( T( _+ Q. ]. C3 i% B
- // 开启全局中断
% l" q* L5 g" j+ O - EA = 1;
& _9 ~9 m. E# |4 }6 b& T/ V - }) w( f5 B% B! [. @3 r
- 2 X9 @( N, d$ ]( Y4 ]4 j
- static void INT0_Isr() interrupt 0{, U( S. u' w: A1 _# w
- // 只有已经成功锁存才允许CLK移位* x# O- V- x3 g) i1 b/ U3 p6 h
- if(isReady == 0)5 k* H* }$ A- g9 \4 ?
- return;! K: v1 a" ?! f7 O1 a/ m4 _
- // 读当前引脚电平,如果是低电平则说明是下降沿,此时直接忽略该中断
" k$ |9 b, m: E - if(CLK == 0)3 b2 j! I, S, a% d" C
- return;
$ R* x& I! T* ?* d* L' B' { - // CLK上升沿到来的时候,取锁存值的下一位输出) r3 K" B4 c$ d$ Z
- idx++;
! g1 Y1 Q+ G9 n, A C% e - DATA = key & mask[idx];
1 g4 E0 m9 S, X0 [& i- S1 Z - // 如果已经完成7次移位,则一轮读取完成
- ]5 n% ^9 F* I# z' p6 [% V - if(idx >= 7)
; ^8 |# V" d# @ - isReady = 0;
5 ^$ a. E s0 E2 t [ - }
' H( O- b! i0 d! }2 ~9 @! [' Q [
$ p- {' X) A k8 ?- J. P, M- static void INT1_Isr() interrupt 2{
) L0 \" E2 ^ f# j1 F& u - // 读当前引脚电平,如果是低电平则说明是下降沿,此时直接忽略该中断3 W) h; }* V- D! @; e1 O3 }% z t
- if(LATCH == 0)- k: S) G3 x! l
- return;/ g6 X2 x- @0 k5 s2 d
- // 当LATCH上升沿到来的时候,锁存所有按键状态,同时把键值A输出到DATA
( d0 `0 V; ~, G8 A - key = bufReady;2 Z$ b: B/ K9 y* ~9 m' F2 @% b3 |3 A
- DATA = key & 0x80;//mask[0]; // 为了加速运算,直接取表中的值而不是读表
- R$ X0 `2 j h. }; z$ z+ { - idx = 0;
4 k- {( `) P9 E3 t - // 允许CLK进行移位
3 m+ a7 X! X8 o, I - isReady = 1;( L- @) k; |( O- T3 x
- }
' P# `3 `: @, T& e
5 X0 x' ~" H6 y _+ i5 c1 j; k: }- void main(){6 f% _6 t7 L3 }+ G; {. F* R
- GPIO_Init();
E6 u; V7 Z/ ^5 c% \ - Interrupt_Init();1 h7 V5 d5 n" L, c* I% Z. g# \
-
% }9 |5 [/ [) ~ - while(1){
* {+ g3 d- B" R9 j2 b: } - //PCON |= 0x01; // 进入省电模式- @: F$ ?0 U! P# ~; {$ E
- //_nop_();
2 b* W9 u9 s/ U6 i/ o1 D - //_nop_();
+ a* ]& I/ x9 e* C - //_nop_();
( h1 _/ P$ H9 _' H3 m# Y! e - //_nop_();. ^" L2 U o; Q6 g$ K* p
- buf = ~((P1 & 0x3F) | ((P5 << 2) & 0xC0));6 p. E) {" g Y1 j$ X6 Y3 n) R
- bufReady = buf;6 P3 z, u. A8 o+ J2 ^
- }( \0 e8 g# ]# R& l- s" k, R' ?
- }
' u) [0 _7 J- R' c7 N
复制代码
2 c4 O6 W+ b8 M2 M$ S硬件参数配置无特殊要求,晶振频率选择12M即可。& y6 i4 t* V9 ?3 h# A
/ I" [( w% e- E# ^7 I: k2 Z A0 n
这是编译好的固件。 |