软件开发必要条件

STC 51单片机开发环境

常用的单片机开发环境 Keil、IAR。将STC头文件导入到软件文件夹内。利用STC-ISP中的范例程序进行高效率的开发。

单片机C语言

定义变量的方法
算数逻辑运算符的使用
if、while、for、switch/case、do-while语句
头文件的用途与调用,头文件是硬件和软件关联的关键。

详情见板块“C语言基础”。

无线门磁探测器软件逻辑

门磁探测器软件共可分为四块:IO口初始化配置、门磁开关检测程序设计、低压检测程序设计、射频电路程序设计。
其中,IO口初始化配置并非放在第一步直接完成,应根据模块需求对接口进行配置。
STC15W10X系列芯片如下图所示。

P2.1-STC15W10X系列芯片.png

IO口初始化配置

P2.2-IO口模式初始化配置.png

以MCU为核心,看MCU的中英文资料,根据每个接口的用途,确定各个管脚的工作模式,编写初始化代码。

关于IO口的各种模式,此博客中有详细的介绍: GPIO输入输出各种模式详解

1
2
推挽输出能输出大电流,能点亮LED灯。
开漏模式省电,可用于接收外部电平变化并进入中断。

P2.3-MCU电路.png

所有接口都可利用宏定义进行命名,如下图。

P2.4-宏定义命名芯片接口.png

LED驱动引脚(P3.0、P3.1)

如 P2.3 电路图所示,P3.0和P3.1需要实现推挽输出,推挽输出驱动能力比开漏输出更高,可点亮LED灯。(后续应当根据小灯泡亮度实际情况对IO口工作模式做出调整

P3.0口配置:P3M0.0置1;P3M1.0置0
P3.1口配置:P3M0.1置1;P3M1.1置0

1
2
3
// P3.0和P3.1为推挽输出口,应选择推挽输出模式
P3M0 |= 0x03; // P3M0后两位置1,其他位保留
P3M1 &= (~0x03); // P3M0后两位置0,其他位保留

寄存器配置方法:

1
2
3
4
某几位置1,其他位不变:“|=(0x??)”;
某几位置0,其他位不变:“&=(~0x??)”。
其中 0x?? 由需要操作的位来决定,例如需要对第1位和第4位操作,那么此时0x??=0x09=b00001001
或生1,与生0

开关门状态反馈引脚(P3.4、P3.5)

分析开关门检测口P3.4和P3.5,这两个口都是输入性质的口,可以直接检测到外部的开关门信号,但是这种方法需要单片机一直处于工作状态,耗电量大,因此使用中断服务程序INT2和INT3,查数据表可知这两个接口均只支持下降沿中断。编写好中断服务程序使能和中断服务程序入口备用。将两个中断口设置为开漏模式,开漏模式待机状态更加省电。

电池低压状态引脚(P3.3)

开漏模式

射频电路信号输出引脚(P3.2)

准双向口

门磁开关检测程序设计(指示灯P3.0、开门上升沿P3.4、关门上升沿P3.5)

开门和关门操作都应该进入中断服务程序,对开关门flag起作用,程序再根据门的当前状态,对操作定性,来确定是否点亮工作指示灯。
门磁开关检测:INT2下降沿检测关门,INT3下降沿检测开门,因此定义变量flag_Door,关门时进入INT2使其为1,开门时进入INT3使其为2。

低压检测程序设计(指示灯P3.1、低压下降沿P3.3)

当电池低压检测电路产生下降沿输出时,进入中断服务程序,对低压flag起作用,程序再根据电池当前状态,对状态定性,来确定是否点亮低压指示灯。
低压检测电路:电池低压时,进入INT1

射频电路程序设计(信号输出端口P3.2)

本项目射频电路编码方式参考编码器EV1527,无线发射模块中没有EV1527,为节约成本,编程实现EV1527的编码功能,其中文资料如下:

EV1527百万组编码芯片规格书.pdf

如下图所示,在EV1527编码方式中,每次会发送3byte(24bit)的信号,前20bit(C0-C19)代表信号的地址,是接收器识别信号的依据,后4bit(D0-D3)为功能码,可表示15种功能。

P2.5-EV1527编码格式.png

载波信号的OOK编码规则如下图所示。

P2.6-载波信号OOK编码规则.png

因此需要利用单片机中的定时器,对信号地址和功能码进行处理,使之编码为如下图中的信号,再通过震荡电路产生无线信号,从而被WIFI报警主机接收到。

P2.7实际输出信号.jpg

查找EV1527数据手册可得,在3V工作电压,330kΩ外加电阻的情况下,16CLK=1.69ms,1CLK=0.1ms,因此可得到以下的结论:

信号类型 高电平时钟数 低电平时钟数 低电平时长 低电平时长
同步 4CLK 124CLK -> 0.4ms 12.8ms
0 4CLK 12CLK -> 0.4ms 1.2ms
1 12CLK 4CLK -> 1.2ms 0.4ms

12T和1T单片机的区别和认识:
1T的速度更快,最小间隔是一个晶振周期。

计算nT单片机的指令周期公式为:

T = 1fn\frac{1}{f}*n
f: 晶振频率

例如: 使用12M晶振的1T单片机的指令周期为:T = 1121\frac{1}{12}*1 = 112\frac{1}{12} us。

定时器本质上是一个加法器,单片机的位数 n 、晶振频率 f 和 12T or 1T 共同决定了定时器计时的最大时长。12T单片机最大时长 12T=1f2n1212T = \frac{1}{f}*2^n*12 ,1T单片机最大时长 1T=1f2n11T = \frac{1}{f}*2^n*1
设置TH0和TL0为计时初始值,加法器运行到溢出时停止计时。

此处用的是12T非自动重载16位定时器,f = 5.5296MHz

时长 TH0 TL0
12.8ms 0xE8 0xF5
1.2ms 0xFD 0xD6
0.4ms 0xFF 0x47

自动重载定时器:溢出时将自动重新设置TH0和TL0。

STC单片机程序开发

  1. 保存头文件到项目文件夹。
  2. 对接口进行初始化配置。
  3. 分析各个模块,从而设置各个接口的工作模式(开漏、准双向口、推挽输出、高阻输入)。功耗:推挽输出>准双向口>开漏>高阻输入。
  4. 根据各个模块的功能编写相应的程序。
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
/*-------------------------------------------------------------------------
项目说明:
MCU 型号: STC15F104W 单片机
开发环境: Keil C91 版本: V9.59.0.0
主要功能包括: 开门检测 关门检测 OOK无线编码 设备休眠
///主机要求 开门发射数据4 关门5 电池低压 6
单片机晶振: 内部5.5296M
主要使用单片机资源:
>> IO 输出
>> 外部中断1
>> 外部中断2
>> 外部中断3
>> 定时器 0
LED灯状态说明:
休 眠:灭
电池低压: 每次门状态改变后 黄灯闪烁一次 (黄色灯)
设备状态改变:设备LED闪烁一次 (蓝色灯)
---------------------------------------------------------------------------*/

#include "STC15WXX.H"
#include "absacc.h"
#include "intrins.h"

void System_Init(void);
void OOKDataSent(unsigned char SentDat);
void Hard_Pro(void);
void SystmPro(void);
void Sleep_Pro(void);

#define LED_WORK_STA P31
#define YELLOW_BAT P30

#define STA_DOOR_OPEN P35
#define STA_DOOR_CLOSE P34
#define BAT_LOW P33

sbit OOK_DAT_OUT = P3^2;

unsigned char flag_Door; // =0 无动作 1关门 2 开门
unsigned char DoorSta; // =0 无动作 1关门 2 开门
unsigned char flag_VlotLow; // =0 电池正常 1电池低压 2电池低压中

//12.8毫秒 TH0 TL0 的数据 寄存器计数 = (12.8ms * 1000)/ A = 5899 TH0 TL0 = 65535 - 5899 = 59636 E8F4
#define OOK_TONG_H 0xE8 // 同步头高位
#define OOK_TONG_L 0xF4 // 同步头低位

//0.4毫秒 TH0 TL0 的数据 寄存器计数 = (0.4ms * 1000)/ A = 184 TH0 TL0 = 65535 - 184 = 65351 FF47
#define OOK_SMALL_H 0xFF // 小脉冲高位
#define OOK_SMALL_L 0x47 // 小脉冲低位

//1.2毫秒 TH0 TL0 的数据 寄存器计数 = (1.2ms * 1000) / A = 553 TH0 TL0 = 65535 - 553 = 64982 FDD6
#define OOK_LONG_H 0xFD // 大脉冲高位
#define OOK_LONG_L 0xD6 // 大脉冲低位

unsigned char coding[100]; // 数组

void Delay10ms() //@5.5296MHz
{
unsigned char i, j;
i = 54;
j = 199;
do
{
while (--j);
} while (--i);
}

void Delay100ms() //@5.5296MHz
{
unsigned char i, j, k;
// _nop_();
// _nop_();
i = 3;
j = 26;
k = 223;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}

void Delay3000ms() //@5.5296MHz
{
unsigned char i, j, k;
_nop_();
_nop_();
i = 64;
j = 9;
k = 179;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}

////// 主程序
void main(void)
{
System_Init(); // 系统初始化程序
while(1)
{
Hard_Pro(); // 硬件检测
SystmPro(); // LED灯亮 OOK 数据发送
Sleep_Pro();
}
}
/*-------------------------------------------------------------------------
函数说明:
函数名称:SystmPro(void)
功 能: OOK/ASK 无线数据编码程序
参 数: 系统LED灯闪烁 OOK 数据发送
---------------------------------------------------------------------------*/
void Sleep_Pro(void)
{
_nop_(); //掉电模式被唤醒后,首先执行此语句,然后再进入中断服务程序
_nop_();
PCON = 0x02; //MCU进入掉电模式
_nop_(); //掉电模式被唤醒后,首先执行此语句,然后再进入中断服务程序
_nop_();
}

void Hard_Pro(void)
{
if(flag_Door == 1)
{//门关闭
flag_Door = 0;
Delay10ms();
if(STA_DOOR_CLOSE == 0)
{//门确定关闭
DoorSta = 1;
}
}
else if(flag_Door == 2)
{//门打开
flag_Door = 0;
Delay10ms();
if(STA_DOOR_OPEN == 0)
{//门确定打开
DoorSta = 2;
}
}
else
{
flag_Door = 0;
}
if(flag_VlotLow == 1)
{////电池低压
Delay10ms();
if(BAT_LOW == 0)
{////电池第一次低压
flag_VlotLow = 2;
YELLOW_BAT = 1; //工作状态灯打开
OOKDataSent(0x06);
YELLOW_BAT = 0; //工作状态灯关闭
}
else
{
flag_VlotLow = 0;
}
}
}
/*-------------------------------------------------------------------------
函数说明:
函数名称:SystmPro(void)
功 能: OOK/ASK 无线数据编码程序
参 数: 系统LED灯闪烁 OOK 数据发送
---------------------------------------------------------------------------*/
void SystmPro(void)
{
if(DoorSta)
{
// LED_WORK_STA = 1; ///工作状态灯打开
Delay10ms();
if(DoorSta == 1)
{ ////关门无线数据发送
OOKDataSent(0x0E);
}
else ///if(DoorSta == 2)
{ ////开门无线数据发送
OOKDataSent(0x0A);
}
// LED_WORK_STA = 0; ///工作状态灯关闭
DoorSta = 0;
}
if(flag_VlotLow == 2)
{////电池低压提示
YELLOW_BAT = 1; ///工作状态灯打开
Delay100ms();
YELLOW_BAT = 0; ///工作状态灯关闭
}
}


void System_Init(void)
{///// 端口初始化
P0M0 = 0x00;
P0M1 = 0x00;
P1M0 = 0x00;
P1M1 = 0x00;
P2M0 = 0x00;
P2M1 = 0x00;
P3M0 = 0x00;
P3M1 = 0x00;
P4M0 = 0x00;
P4M1 = 0x00;
P5M0 = 0x00;
P5M1 = 0x00;
P6M0 = 0x00;
P6M1 = 0x00;
P7M0 = 0x00;
P7M1 = 0x00;

STA_DOOR_OPEN = 1;
STA_DOOR_CLOSE = 1;

P3M1 |= 0x20;
P3M0 |= 0x20;

// P3M1 |= 0x30;
// P3M0 |= 0x30;

OOK_DAT_OUT = 0;
// INT0 = 1;
IT1 = 1; //设置INT0的中断类型 (1:仅下降沿 0:上升沿和下降沿)
EX1 = 1; //使能INT0中断
EA = 1;
INT_CLKO |= 0x10; //(EX2 = 1)使能INT2中断
INT_CLKO |= 0x20; //(EX3 = 1)使能INT3中断
EA = 1;

// AUXR |= 0x80; //定时器0为1T模式
AUXR &= 0x7f; //定时器0为12T模式
TMOD = 0x00; //设置定时器为模式0(16位自动重装载)
// TL0 = T1MS; //初始化计时值
// TH0 = T1MS >> 8;
TR0 = 0; //定时器0开始计时
ET0 = 0; //使能定时器0中断
EA = 1;

LED_WORK_STA = 1; ///开机 蓝灯 黄灯 长亮 3秒
YELLOW_BAT = 1;
Delay3000ms();
LED_WORK_STA = 0;
YELLOW_BAT = 0;

flag_Door = 0; // =0 无动作 1关门 2 开门
DoorSta = 0; // =0 无动作 1关门 2 开门
flag_VlotLow = 0; // =0 电池正常 1电池低压 2电池低压中
}


/////////////////////////////////////////////////////////////////////////////
/*-------------------------------------------------------------------------
函数说明:
函数名称:void OOKDataSent(unsigned char SentDat
功 能: OOK/ASK 无线数据编码程序
参 数: SentDat 无线数据 功能码
有效数据范围:00H-0FH
详细函数说明:
编码方式: EV1527 兼容
无线数据地址:
从单片机Flash FF6-FF8 获取
Flash地址烧录方式: 通过烧录器滚码烧录 共三个字节 FF6高位4位无效
工作原理:
单片机内部定时器0中断触发后 不断的修改内部的定时器时间 最终达到规则的定时器
规则。
将定时器的定时时间存入 coding[100]中。每帧数据共25数据。每个数据一个正负脉冲
共需要50个字节 每个定时器时间需要高位和低位两个字节 共100个字节
---------------------------------------------------------------------------*/
void OOKDataSent(unsigned char SentDat)
{
unsigned char i,j,dat;
unsigned char data2,data1,data0;
///
// data2=CBYTE[0xFF6];
data1=CBYTE[0xFF7];
data0=CBYTE[0xFF8];
// ////将OOK 数据的功能码添加到发射数据中
// data2 &= 0xf0; //低4位清0
// SentDat &= 0x0f; //将功能码整合到数据中
// data2 |= SentDat;

data2 = SentDat;

////同步信号
////0.4毫秒高电平
coding[0] = OOK_SMALL_H;
coding[1] = OOK_SMALL_L;
////12.8毫秒低电平
coding[2] = OOK_TONG_H;
coding[3] = OOK_TONG_L;

for(i=4;i<100;i++)
{
if(i==4)
{
dat = data0;
}
else if(i==36)
{
dat = data1;
}
else if(i==68)
{
dat = data2;
}
for(j=0;j<8;j++)
{
if(dat & 0x80)
{///
////0.4毫秒高电平
/////1.2毫秒 高电平
coding[i] = OOK_LONG_H;
i++;
coding[i] = OOK_LONG_L;
i++;

////0.4毫秒低电平
coding[i] = OOK_SMALL_H;
i++;
coding[i] = OOK_SMALL_L; //i++;
}
else
{
coding[i] = OOK_SMALL_H;
i++;
coding[i] = OOK_SMALL_L;
i++;
//////1.2毫秒 低电平
coding[i] = OOK_LONG_H;
i++;
coding[i] = OOK_LONG_L;
//i++;
}
i++;
dat <<= 1;
//// 65432187
}
i--;
}
TR0 = 1; //定时器0开始计时
ET0 = 0; //使能定时器0中断

LED_WORK_STA = 1;

P3M0 |= 0X04;
OOK_DAT_OUT = 1;
for(j=0;j<23;j++)////发射数据的帧数
{
for(i=0;i<100;i++)
{////一帧数据
TF0 = 0;
OOK_DAT_OUT = !OOK_DAT_OUT;// = !P_RFSENT;
TH0 = coding[i];
i++;
TL0 = coding[i];
while(!TF0)
{

}
////检测定时器0 的中断标志位 TF0 =1 表示延时完成
// TF0 =0;
}
}

TF0 = 0;
TR0 = 0;
OOK_DAT_OUT = 0;
LED_WORK_STA = 0;
P3M0 &= ~0X04;

}


//-----------------------------------------
//中断服务程序
void exint0() interrupt 0 //INT0中断入口
{//
}

/* Timer0 interrupt routine */
void exint1() interrupt 2
{//电池低压
flag_VlotLow = 1;
// P10 = ! P10; //将测试口取反
}

//-----------------------------------------------
//中断服务程序 开门检测
void exint3() interrupt 11 //INT3中断入口
{
flag_Door = 2;
}

//中断服务程序 关门检测
void exint2() interrupt 10 //INT2中断入口
{
flag_Door = 1;
}