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