1 | #include <stc15f2k60s2.h> |
仅于河南工业大学莲花街校区测试通过,其它使用Srun深澜作为登陆器的学校可以参考修改脚本中的URL变量,如有问题请提issue。
本脚本适用于Bash/BusyBox解释器,可以运行在获取到SSH/Telnet登录权限的路由器设备上。通过Crontab命令的配合,实现校园网自动登录,断线重连。本人使用的是斐讯K2 PDCN固件,29.9购于拼多多,您可以参考购买,后文也全部基于PDCN固件。
在PDCN的高级设置->系统管理->服务->终端服务中开启启用Telnet服务:
使用PowerShell(Windows)/Terminal(Mac)进行登录,在命令提示框内输入telnet 192.168.123.1
,随后输入账号密码(密码默认为后台管理员密码),当出现Linux命令提示符时即可。
注意:Windows可能需要在启用或关闭Windows功能
页面开启Telnet客户端
。
由于PDCN在重启后会清除缓存,请将工作目录切换到/etc/storage
后再下载。在国内我推荐您下载我位于OSS上的镜像:wget https://imgs.raincorn.top/file/srun.sh
。
直接运行bash srun.sh username passwd
,username与passwd需要修改为您的账号与密码,例如bash srun.sh 201916660212 123456
,如果在校园网离线状态下出现login_ok即为测试成功。
在PDCN的高级设置->自定义设置->脚本->自定义Crontab定时任务配置中加入*/1 * * * * bash /etc/storage/srun.sh username passwd
,请注意修改账号与密码。
关于其他路由器如何使用,请尝试使用百度搜索路由器型号+SSH
,大部分能获取SSH的路由器均可运行该脚本,计划任务直接使用crontab -e
进行编辑。
项目位于https://github.com/rainvalley/Srun_Linux ,欢迎Star/Fork,也欢迎提交任何issue(逃
]]>数据手册可与此处下载。
1 | void sr04_measure() |
数据手册可于此处下载。
WS2812的通信使用单线通信协议,DIN为数据输入,DOUT可以连接下一个像素点的DIN做到数据转发,如图:
在通信中,规定的0/1时序如下。(当然我找了十来份数据手册,其中的时序参考时间均不同。)
在STC15 12M的频率下一个_nop_();对应的时间为1/12us。但是请注意在C51中语句的跳转仍然占用大量时间,使用较低频率的晶振会使得时序误差的扩大,直接表现为无法驱动WS2812,这也是很多性能较低的单片机无法驱动WS2812的原因。因此我使用了24M晶振,定义NOP为6个_nop_();,即0.25us。无论如何请注意延时的问题,否则WS2812无法被正常驱动。
1 |
|
1 | //拉低DIN保持50us以上 |
1 | //高电平0.25us,低电平1us |
1 | //高电平1us,低电0.25us |
1 | void ws2812_write_byte(uchar dat) |
1 | void ws2812_write_rgb(uchar r,uchar g,uchar b) |
1 | void ws2812_write_24bits(ulong dat) |
在主函数中:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17void main()
{
init_sys();
ws2812_init();
ws2812_write_24bits(0x000000);
ws2812_write_24bits(0xff0000);
ws2812_write_24bits(0x00ff00);
ws2812_write_24bits(0x0000ff);
ws2812_write_24bits(0xffff00);
ws2812_write_24bits(0xff00ff);
ws2812_write_24bits(0x00ffff);
ws2812_write_24bits(0xffffff);
while(1)
{
}
}
效果如图:
单片机的运行需要依靠固定的节奏,在PC的CPU上表现为主频,在单片机上表现为晶振频率。如使用一个12M的晶振,时钟周期固定为1/12us。在传统8051单片机上,晶振均为外置,在STC15上可以选用内部晶振,下文均使用12M晶振的配置。
在单片机运行的过程中,一条指令需要拆分为多个步骤,完成一个基本操作的时间被称为机器周期。在传统的8051上,一个基本的的机器周期由6个S周期构成,每个S周期又有2个时钟周期构成。因此,一个机器周期=6个S周期=12个时钟周期,即1us。
完成一条指令所需要的机器周期,根据指令不同其周期长度也不同。例如nop指令:
其它不同的指令中,指令周期的长度会有所不同,可见STC-ISP的指令表。一般来说STC15的12T模式速度是传统8051的8~12倍,
频率测量与电压控制,题目可于此处下载。
1 | buff_tube[5]=dig_code[volta/100]+0x80; //将原段码加上0x80即可的得到小数点 |
1 | buff_tube[0]=dig_code[10]; |
1 | IIC_Start(); |
1 | if(volta<150) |
1 | if(count_2ms>=2) |
1 | if(flag_20ms) |
1 | void init_timer0(void) |
1 | #include <stc15f2k60s2.h> |
模拟智能灌溉系统,题目可于此处下载。
注意在手动模式下湿度只会影响蜂鸣器的工作,灌溉是否进行需要手动操作。
1 | #include<stc15f2k60s2.h> |
简易温度温度采集与控制装置,题目可于此处下载。
1 | //矩阵按键状态与备份 |
1 | uchar buff[4]={11,11,11,11}; |
1 | void ser_timer0() interrupt 1 |
1 | #include<stc15f2k60s2.h> |
每次中断运行一次显示函数,根据索引(index)来按位进行位选。同时设置一个数码管显示的缓冲数组,根据索引读取缓存数组内的段码。根据位选与段选即可动态显示数码管,避免delay函数的使用。
1 | #include<stc15f2k60s2.h> |
1 | #include<stc15f2k60s2.h> |
模拟风扇控制系统,题目可与此处下载。
1 | #include<stc15f2k60s2.h> |
基于单片机的电子钟,完整题目可于此处下载。
1 | if(flag_200ms) |
1 | f(flag_200ms!=flag_backup) //仅在变化时累加计数,避免多次循环计数 |
1 | void modify_ds1302() //修改时间是停止读取 |
1 | char time[]={58,59,23}; |
1 | //单总线延时函数 |
1 | #include<stc15f2k60s2.h> |
彩灯控制器,完整题目可于此处下载。
实例代码如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519#include<stc15f2k60s2.h>
#include"iic.h"
#include<intrins.h>
typedef unsigned char uchar;
typedef unsigned int uint;
uchar code dig_code[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90,0xbf,0xff};
//按键状态与,按键状态备份,默认流转时间
uchar keystat[4]={1,1,1,1};
uchar keybackup[4]={1,1,1,1};
uchar time[4]={4,4,4,4};
//变量依次为:800ms闪烁标志位,模式标志位,已选择的模式标志位,pwm占空比,LED运行的模式,LED运行时P0对应值,at24c02读写读写标志。
uchar flag_800ms=0,mode=0,select_mode=1,pwm=25,stat=0,data_led=0,flag_write=0;
sbit S4 = P3^3;
sbit S5 = P3^2;
sbit S6 = P3^1;
sbit S7 = P3^0;
void set_pwm();
uchar read_at24c02(uchar address);
void display_data();
void led_run();
void select(uchar channel)
{
switch(channel)
{
case 4:
P2=(P2&0x1f)|0x80;
break;
case 5:
P2=(P2&0x1f)|0xa0;
break;
case 6:
P2=(P2&0x1f)|0xc0;
break;
case 7:
P2=(P2&0x1f)|0xe0;
break;
default:
P2=(P2&0x1f);
break;
}
}
void init_sys()
{
uchar i=0;
select(4);
P0=0xff;
select(5);
P0=0x00;
select(0);
//初始化时从at24c02中读取数据
time[0]=read_at24c02(0x00);
time[1]=read_at24c02(0x01);
time[2]=read_at24c02(0x02);
time[3]=read_at24c02(0x03);
}
void delay(uint t)
{
while(t--);
}
void display(uchar pos,uchar num)
{
select(6);
P0=0x01<<pos;
select(7);
P0=dig_code[num];
delay(1500);
P0=0xff;
select(0);
}
uchar read_pcf8591(uchar ain)
{
uchar volta;
IIC_Start();
IIC_SendByte(0x90);
IIC_WaitAck();
IIC_SendByte(ain);
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0x91);
IIC_WaitAck();
volta=IIC_RecByte();
IIC_Ack(0);
IIC_Stop();
return volta;
}
void write_at24c02(uchar address,uchar dat)
{
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(address);
IIC_WaitAck();
IIC_SendByte(dat);
IIC_WaitAck();
IIC_Stop();
}
uchar read_at24c02(uchar address)
{
uchar dat;
IIC_Start();
IIC_SendByte(0xa0);
IIC_WaitAck();
IIC_SendByte(address);
IIC_WaitAck();
IIC_Stop();
IIC_Start();
IIC_SendByte(0xa1);
IIC_WaitAck();
dat=IIC_RecByte();
IIC_Ack(0);
IIC_Stop();
return dat;
}
void scan_key()
{
uchar i;
static uchar keybuff[4]={0xff,0xff,0xff,0xff};
keybuff[0]=(keybuff[0]<<1)|S4;
keybuff[1]=(keybuff[1]<<1)|S5;
keybuff[2]=(keybuff[2]<<1)|S6;
keybuff[3]=(keybuff[3]<<1)|S7;
for(i=0;i<4;i++)
{
if(keybuff[i]==0xff)
{
keystat[i]=1;
}
if(keybuff[i]==0x00)
{
keystat[i]=0;
}
}
}
void init_timer0(void)//1毫秒@11.0592MHz
{
AUXR |= 0x80;//定时器时钟1T模式
TMOD &= 0xF0;//设置定时器模式
TL0 = 0xCD;//设置定时初值
TH0 = 0xD4;//设置定时初值
TF0 = 0;//清除TF0标志
TR0 = 1;//定时器0开始计时
ET0 = 1;
EA = 1;
}
void ser_timer0() interrupt 1
{
static uchar update_count=0;
static uint display_count=0;
display_count++;
update_count++;
scan_key(); //每1ms扫描一次键盘
if(display_count==800) //每800ms闪烁一次数码管
{
flag_800ms=~flag_800ms;
display_count=0;
}
if(update_count==100) //每100ms更新一次at24c02与pcf8591
{
flag_write=~flag_write;
update_count=0;
set_pwm(); //每100ms更新一次pwm占空比
}
}
void key_fun(uchar key)
{
if(key==3) //停止TR1的计数
{
if(TR1==0)
{
TR1=1;
}
else
{
TR1=0;
}
}
if(key==2) //改变数码管工作模式:0——熄灭,1——运行模式,2——流转间隔
{
mode++;
if(mode==3)
{
mode=0;
}
}
if(key==1&&mode==1) //增加运行模式
{
select_mode++;
if(select_mode==5)
{
select_mode=1;
}
}
if(key==0&&mode==1) //减少运行模式
{
select_mode--;
if(select_mode==0)
{
select_mode=4;
}
}
if(key==1&&mode==2) //增加流转间隔
{
time[select_mode-1]++;
if(time[select_mode-1]>=13)
{
time[select_mode-1]=4;
}
}
if(key==0&&mode==2) //减少流转间隔
{
time[select_mode-1]--;
if(time[select_mode-1]<=3)
{
time[select_mode-1]=12;
}
}
}
void key_press()
{
int i;
for(i=0;i<4;i++)
{
if(keystat[i]!=keybackup[i])
{
if(keybackup[i]!=0)
{
key_fun(i);
}
keybackup[i]=keystat[i];
}
}
}
void display_data()
{
if(mode==0) //如果数码管处于熄灭状态,长按显示亮度状态
{
if(keystat[0]==0&&mode==0)
{
while(keystat[0]==0)
{
display(6,10);
display(7,pwm/25);
}
}
else
{
select(6);
P0=0xff;
select(7);
P0=0xff;
}
}
if(mode==1) //如果选中运行模式
{
if(flag_800ms) //800ms闪烁数码管
{
display(0,10);
display(1,select_mode);
display(2,10);
}
else
{
display(0,11);
display(1,11);
display(2,11);
}
if(time[select_mode-1]>=10) //流转间隔>=1000时显示第四位
{
display(4,time[select_mode-1]/10);
display(5,time[select_mode-1]%10);
}
else
{
display(5,time[select_mode-1]);
}
display(6,0);
display(7,0);
}
if(mode==2) //如果选中流转间隔
{
display(0,10);
display(1,select_mode);
display(2,10);
if(flag_800ms) //800ms数码管闪烁间隔
{
if(time[select_mode-1]>=10) //如果流转间隔>=1000则显示第四位
{
display(4,time[select_mode-1]/10);
display(5,time[select_mode-1]%10);
}
else
{
display(5,time[select_mode-1]);
}
display(6,0);
display(7,0);
}
else
{
display(4,11);
display(5,11);
display(6,11);
display(7,11);
}
}
}
void set_pwm()
{
uchar dat;
dat=read_pcf8591(3);
if(dat<=64)
{
pwm=25;
}
else if(dat<=128)
{
pwm=50;
}
else if(dat<=192)
{
pwm=75;
}
else
{
pwm=100;
}
}
void init_timer1(void)//100微秒@11.0592MHz
{
AUXR |= 0x40;//定时器时钟1T模式
TMOD &= 0x0F;//设置定时器模式
TL1 = 0xAE;//设置定时初值
TH1 = 0xFB;//设置定时初值
TF1 = 0;//清除TF1标志
TR1 = 1;//定时器1开始计时
ET1 = 1;
EA = 1;
}
void led_run()
{
switch(stat)
{
case 0:
data_led=0xff;
break;
case 1:
data_led=0xfe;
break;
case 2:
data_led=0xfc;
break;
case 3:
data_led=0xf8;
break;
case 4:
data_led=0xf0;
break;
case 5:
data_led=0xe0;
break;
case 6:
data_led=0xc0;
break;
case 7:
data_led=0x80;
break;
case 8:
data_led=0x00;
break;
case 9:
data_led=0x7f;
break;
case 10:
data_led=0x3f;
break;
case 11:
data_led=0x1f;
break;
case 12:
data_led=0x0f;
break;
case 13:
data_led=0x07;
break;
case 14:
data_led=0x03;
break;
case 15:
data_led=0x01;
break;
case 16:
data_led=0x00;
break;
case 17:
data_led=0x7e;
break;
case 18:
data_led=0xbd;
break;
case 19:
data_led=0xdb;
break;
case 20:
data_led=0xe7;
break;
case 21:
data_led=0xe7;
break;
case 22:
data_led=0xdb;
break;
case 23:
data_led=0xbd;
break;
case 24:
data_led=0x7e;
break;
}
}
void ser_timer1() interrupt 3
{
static uint count=0,pwm_count=0;
static uchar temp_mode=1;
count++;
pwm_count++;
if(count==time[temp_mode-1]*1000)
{
count=0;
led_run();
if(stat==24)
{
stat=0;
}
stat++;
if(stat==0)
{
temp_mode=1;
}
else if(stat==9)
{
temp_mode=2;
}
else if(stat==17)
{
temp_mode=3;
}
else if(stat==21)
{
temp_mode=4;
}
}
if(pwm_count<=pwm)
{
P0=0xff;
select(4);
P0=data_led;
}
else if(pwm_count<100)
{
P0=0xff;
select(4);
P0=0xff;
}
else if(pwm_count==100)
{
pwm_count=0;
}
}
void write_data()
{
if(flag_write)
{
write_at24c02(0x00,time[0]);
write_at24c02(0x01,time[1]);
write_at24c02(0x02,time[2]);
write_at24c02(0x03,time[3]);
}
}
int main()
{
init_sys();
init_timer0();
init_timer1();
while(1)
{
key_press(); //松开时的按键处理
display_data(); //数据显示
write_data(); //数据写入
}
}
/data/data/com.tencent.mm/MicroMsg/fbae4541c49a001215028083eb1e9f25/appbrand/pkg/
,随机码会随用户而变化。注意,体积较小的为主包,较大的为依赖包,我们反编译主包。1 | npm install esprima |
node wuWxapkg.js app.wxapkg
即可对app.wxapkg进行反编译。比如当我们想要了解一个东西定义和评价标准,那我们就可以找对应的国家标准文档,如果我们想了解内部结构,那我们就可以找到专利文件,如果我们想了解行业数据,那么可以去查公司财报和投行研报,这些资料都有对应的专业网站以便搜索。——回形针PaperClip
Tips:在陌生领域搜索时,可以直接加上关键字“数据”,以得到最能量化行业发展的内容。
由于年代久远,部分内容可能无法访问,也可能不具有任何参考价值,采取某些措施可以得到这些失效内容。
1 | typedef unsigned char uchar; |
1 | void Init_INT0() |
1 | void Init_Timer() |
1 | uchar code dig_code[]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; |
1 | void Timer0Init(void)//5毫秒@11.0592MHz |
1 | sbit KEY_IN_1 = P4^4; |
1 | void Init_Uart(void)//9600bps@11.0592MHz |
1 | void init_timer0(void)//100us@11.0592MHz |
1 | uchar ADC(uchar ain) |
1 | void write_eeprom(uchar addr,uchar dat) |
1 | uint get_temp() |
1 | uchar time[7]={0x45,0x58,0x23,0x01,0x01,0x03,0x20}; |
1 | void send_wave() |
1 | void init_int0() |
1-3 2-4
用于选择到超声波,5-3 6-4
选择到红外。超声波发射模块发射超声波,在发送的同时开始计时,当接收端接收到超声波时,停止计时。由于超声波在空气中传播的速率固定,因此可以根据传播时间字节计算距离。假定室温20°,距离S = 344*T/2 (m)。
1-3 2-4
1 | void send_wave() |
CT107D上有发送与接收模块,使用前需要连接5-3 4-6
引脚,使用杜邦线连接P1^1与P3^2。在红外中,载波部分为低电平,闲置部分为低电平。常用NEC通信协议,整个过程分为引导码,用户码,数据码。
上图中,Logic 1为逻辑高电平,Logic 0为逻辑低电平。其中,阴影部分为载波低电平,空白部分为高电平。
在NEC标准中,引导码用于标志通信的起始。对应上图中9ms的低电平,4.5ms的高电平。
用户码用于区分不同设备,避免不同红外遥控器之间的干扰。在通信的过程中,为了避免误码,需要对用户码与用户反码进行判定。
1 | void display_ir() |
PCF8591拥有16个引脚,其中:
第一字节为器件地址字节:
在CT107D上,A0~A2均接地,又由于AD/DA的类型地址为1001,故其在IIC总线上的地址为:
第二字节为控制字节:
在CT107D上:
AIN0连接到外接排针;AIN1连接到光敏电阻RD1;AIN2连接到放大器输出;AIN3连接到电位器Rb2。
注意如需ADC,DAC一起工作,则需要将控制字节的第7为置为1,允许模拟量输出。
1 | uchar ADC_AIN(uchar ain) |
1 | void DAC(float volta) |
例如EEPROM器件片选信号0000,则:
IIC总线上,SDA SCL默认上拉。在SCL高电平时,SDA上的数据变化会被判定为开始/结束标志。仅在SCL低电平时,允许SDA上数据的传输(电平变化)。
应答信号用于数据接收,发送时的二次确认。主器件数据发送时,从器件产生应答;主器件数据接收时,主器件产生应答;主器件数据接收完成时,主器件产生非应答。
数据交换时,SCL必须保证低电平,否则将会被判定为开始/结束标志。
下示意图中,阴影部分为主器件发送,白色部分为从器件发送。S——开始,A——应答,P——停止。
1 | //总线启动条件,SCL为高时拉低SDA |
1 | //总线停止条件,SCL为高时释放SDA |
1 | //等待应答,在SCL为高时读取SDA上的电平变化,为高时非应答,为低时应答 |
1 | //发送应答,在SCL为高时,由主机发送 |
1 | //通过I2C总线发送数据,注意SCL高电平时不允许SDA数据变化 |
1 | //从I2C总线上接收数据,在SCL高电平时读取到SDA数据 |
对于AT24C02(2Kb)进行读写操作,注意AT24C02写入时需要延时(最大5ms),同时读取时需要进行伪操作:
1 | void write_eeprom(uchar addr,uchar dat) |
通信流程:
协议起始,发送控制字节0xA0,写入地址0xFF,写入数据0x05,协议终止。
协议起始,发送控制字节0xA0,写入地址0xFF,协议终止;协议起始,发送控制字节0xA1,从从器件读取数据0x05,主器件发送非应答信号,协议终止。
temp=(MSB<<8)|LSB
,temp=temp>>4
。实际温度与二进制输出如下图(上电默认+85°):
DS18B20内部有64位的ROM,9字节高速SCRATCHPAD,3字节EEPROM。其中64位ROM用于储存OneWire总线上设备的唯一ID号,SCRATCHPAD用于暂存数据,EEPROM则用于掉电重加载数据。
SCRATCHPAD分配
DS18B20的通信分为三步:复位,写入,读取。注意,由于单总线对时序要求严格,所以尽量在通信时关闭中断。
1 | //DS18B20设备初始化 |
1 | //通过单总线向DS18B20写一个字节 |
1 | //从DS18B20读取一个字节 |
1 | uint get_temp() |
DS1302典型电路:
DS1302引脚定义:
DS1302在写入时,需要先写入命令字节,再写入具体数据,即满足格式Write_DS1302(uchar command,uchar dat)
。
此处RTC寄存器的定义是由Command格式而来,例如写秒寄存器(80H):Command字节从高位到低位以此为1000 0000
。
使用传统的逐字节读取寄存器模式来获取数据,各个寄存器的数据读取存在时间差,可能会在进位的时候出现偏差。例如在读取23:59:59
时,由于从秒开始读,可能会存在读取到分钟的时候已经发生了进位,即读取到00:00:59
。
使用BRUST模式读取数据,能够一次性将寄存器数据读取到缓冲区,再供单片机一次性读取。如需使用BRUST模式,A0-A4地址全部置为1,需要向0xbe写入7个字节,从0xbf读取7个字节。
对DS1302,上升沿采样,下降沿输出。
DS1302的通信协议为变种三线SPI,与标准SPI协议略有不同。注意:
单字节上升沿写入
单字节下降沿读取
在DS1302的读取写入中,均使用BCD码,即使用16进制表达十进制。例如0x12,在16进制表达中为18(十进制),在BCD码表达中为12(十进制)。
如需将BCD转为十进制,只需要对16除/取整:1
2
3
4
5
6
7uchar bcd_to_num(uchar bcd)
{
uchar a,b;
a = bcd / 16;
b = bcd % 16;
return (10*a+b);
}
如需将十进制转化为BCD码,只需要对16做乘法:1
2
3
4
5
6
7uchar num_to_bcd(uchar num)
{
uchar a,b;
a = num / 10;
b = num % 10;
return (16*a+b);
}
底层驱动
1 | #include <reg52.h> |
时钟实现
1 | void init_ds1302() |
]]>写入
Write_Ds1302_Byte(0x8e,0x00);
:
- SCK低电平时将RST拉高,才能开始读写DS1302。
- SCK时序保持一定,每次在上升沿时DS1302采样一次,即MCU写入。
- 单字节写入需要写入控制字节与内容,
Write_Ds1302_Byte(0x8e,0x00);
意为向0x8e写入0x00,即允许写入。读取
Read_Ds1302_Byte(0x81);
:
- SCK低电平时将RST拉高,才能开始读写DS1302。
- SCK时序保持一定,每次在下降沿时DS1302输出一次,即MCU读取。
- 单字节读取需要写入一个控制字节并读取一个字节数据。