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