开源!使用单片机发送POCSAG编码呼响BP机

禁止转载

需要注意的是,现在BP机工作频段已经被国家收回,所以自己使用手台、车台等大功率无线发射器在这个频段发射无线电波是违法的,所以需要进行发射信号的衰减,无线发射功率控制在0.1W之内是没有问题的。

​ 单片机使用STC公司的IAP15W4K58S4,时钟频率为27MHz,使用串口1来发送数据,串口波特率为115200。支持1200和512比特速率的POCSAG编码,支持正负相位,可以发送数字信息和汉字信息(不支持混输)

​ 对讲机我用的是宝峰UV-5R。单片机的P36接对讲机的压控振荡器输入端,P35接对讲机的PTT控制端,还要和对讲机共地(至于这三个接线端具体接在哪一个位置,网上一搜就有了)。对讲机的发射频率需要调到BP机的工作频率一样。

​ BP机我用摩托罗拉精英王和大顾问实验成功,如果不响可以试试切换一下相位。如果显示乱码就换一个响声/功能位。

​ 如果就是不响,要知道哪有这么容易响?地址码、BP机工作频率、对讲机频率调到和BP机工作频率一致、对讲机不能有频偏。如果就是不响,要考虑BP机是否被改过频率,很多BP机在使用的时候都是改过频率的,或者就是对讲机有频偏,可以使用SDR(软件无线电)测试对讲机的频率。

​ 关于如何准确获得BP机的工作频率和地址码,跳转这里:

如何获得BP机的地址码和工作频率?

​ 开源!单片机源码和上位机源码都开源!讨厌那种一个单片机加个Si4463模块就卖你几百块的商家,虽然我知道写程序画板子也不容易,但是价格是成本的十倍着实太坑。

​ 单片机源码:

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
#include <stc15.h>
#include <intrins.h>

#define TX P36 //数据输出端
#define PTT P35 //PTT控制端
#define HIGH 1 //高电平
#define LOW 0 //低电平
#define NILL_DATA 0x7A89C197 //闲置码
#define SYNC_DATA 0x7CD215D8 //同步码
#define DATA_START 12 //串口中数据开始位置
#define TEXT_OR_NUM 11 //串口中文字或数字标志位置
#define SPEED 10 //串口中速率标志位置
#define UARTBUFF_SIZE 400 //串口接收缓冲区
#define TXBUFF_SIZE 200 //TX发送缓冲区
#define LOWSPEED_TIMER_L 0xE7 //512bit/s时定时器初值,需要微调
#define LOWSPEED_TIMER_H 0x31
#define HIGHTSPEED_TIMER_L 0x1A //1200bit/s时定时器初值,需要微调
#define HIGHTSPEED_TIMER_H 0xA8

unsigned long TxBuff[TXBUFF_SIZE] = {0};//TX发送缓冲区
unsigned char Tx_Num;//地址码发射次序
unsigned char beep;//功能位,1,2,3,4
unsigned char UartBuff[UARTBUFF_SIZE] = "#P12345674HT0001$";//串口缓冲区
unsigned int UartCount = 13;//串口接收计数
unsigned char UartTmp;
bit TM0_FLAG=0;//定时器0标志
bit NewData = 0;//串口是否有新数据收到标志

void UartInit(void);//函数声明
void Delay200ms();
unsigned long calc_bch_and_parity(unsigned long cw_e);
unsigned long calc_addr(unsigned long add,unsigned char fun);
void Timer0Init();
void WaitTF0(void);
void Send_Num(unsigned long s);
void GetAddrNumber();
void calc_NumberData();
void SendTxBuff();
void Empty_Buff();
void calc_TextData();

void UartInit(void) //115200bps@27.000MHz
{
SCON = 0x50; //8位数据,可变波特率
AUXR |= 0x40; //定时器1时钟为Fosc,即1T
AUXR &= 0xFE; //串口1选择定时器1为波特率发生器
TMOD &= 0x0F; //设定定时器1为16位自动重装方式
TL1 = 0xC5; //设定定时初值
TH1 = 0xFF; //设定定时初值
ET1 = 0; //禁止定时器1中断
TR1 = 1; //启动定时器1
ES = 1; //开启接收中断
EA = 1; //开总中断
}

void Timer0Init()
{
AUXR |= 0x80; //定时器时钟1T模式
TMOD &= 0xF0; //设置定时器模式
if(UartBuff[SPEED] == 'L')//为L表示512bit/s速率,下同
{
TL0 = LOWSPEED_TIMER_L; //设置定时初值
TH0 = LOWSPEED_TIMER_H;
}
if(UartBuff[SPEED] == 'H')
{
TL0 = HIGHTSPEED_TIMER_L; //设置定时初值
TH0 = HIGHTSPEED_TIMER_H;
}
ET0=1; //定时器0充许中断
TR0=0;
EA=1; //开总中断
}

void UartSendString(unsigned char* ch, unsigned char n) //串口发送字符串函数
{
unsigned char i;
for(i = 0; i < n; i++)
{
SBUF = *ch++;
while(TI == 0);
TI = 0;
}
}

void Delay200ms() //@27.000MHz
{
unsigned char i, j, k;

_nop_();
_nop_();
i = 21;
j = 133;
k = 210;
do
{
do
{
while (--k);
} while (--j);
} while (--i);
}


unsigned long calc_bch_and_parity(unsigned long cw_e) //BCH校验和奇偶校验函数
{
unsigned char i;
unsigned char parity = 0; //奇偶校验计数
unsigned long local_cw; //临时存放数
local_cw=cw_e;//保存cw_e参数值
for(i=1;i<=21; i++,cw_e<<=1)
if (cw_e & 0x80000000) cw_e ^= 0xED200000;
cw_e=cw_e&0xFFC00000;//保留前10位,BCH校验值共11位,只保留前10位有效数据
local_cw |= (cw_e >> 21); //BCH校验数移至第22位到31位,BCH共10位,并和原始数据相加
cw_e=local_cw;
for(i=0; i<31; i++, cw_e<<=1) if(cw_e&0x80000000) parity++;
if(parity%2) local_cw+=1;//从1至31位判断为奇数则后面加1补充为偶数
return local_cw;
}

unsigned long calc_addr(unsigned long add,unsigned char fun) //地址转换,第1参数为地址,第2参数为功能
{
unsigned long adr;
unsigned long tem;
Tx_Num=(unsigned char)(add&0x00000007);//获取地址发射的帧位次,111位第7帧,后3位地址数据隐藏不发送,接收按帧位还原
adr=0x00;
adr=add&0xFFFFFFF8; //去掉地址码后3位
adr=adr<<10; //地址左移10位
tem=0x00;
tem=fun; //功能位
tem=tem<<11;//功能位左移11位,功能位为00 01 10 11四种状态,代表4个地址码
adr=adr|tem; //地址码和功能位合成地址码;
return adr;
}


void WaitTF0(void)
{
while(!TM0_FLAG);//等待定时器溢出
TM0_FLAG=0;//清标志位
}

void Send_Num(unsigned long s)//发送数据
{
unsigned char n;
for (n=0;n<32;n++)
{
if(s&0x80000000)
{
if(UartBuff[1] == 'N')//判断正负相位,下同
TX = LOW;
else
TX= HIGH;
}
else
{
if(UartBuff[1] == 'N')
TX = HIGH;
else
TX=LOW;
}
WaitTF0();//等待延时结束 0.833ms
s<<=1;
}
}

void Empty_Buff()//清空缓冲区
{
unsigned int i;
for(i = TEXT_OR_NUM; i < UARTBUFF_SIZE; i++)
{
UartBuff[i] = 0x00000000;
}
for(i = TEXT_OR_NUM; i < TXBUFF_SIZE; i++)
{
TxBuff[i] = 0x00000000;
}
}

void GetAddrNumber()
{
unsigned char i;
unsigned long tem;
unsigned long addr_tmp;

addr_tmp = (UartBuff[2] - '0')*1000000;//提取地址码
addr_tmp += (UartBuff[3] - '0')*100000;
addr_tmp += (UartBuff[4] - '0')*10000;
addr_tmp += (UartBuff[5] - '0')*1000;
addr_tmp += (UartBuff[6] - '0')*100;
addr_tmp += (UartBuff[7] - '0')*10;
addr_tmp += (UartBuff[8] - '0');

beep = UartBuff[9] - '0';//提取功能位

tem=calc_addr(addr_tmp,beep);//前面是地址码,后面是BB机内00 01 10 11 代表0,1,2,3种不同的声音

for(i = 0; i < 8; i++)
{
if(i == Tx_Num)//地址码放入对应的帧中
TxBuff[i*2] = calc_bch_and_parity(tem);//取得BCH校验后的地址码序列
else
{
TxBuff[i*2] = NILL_DATA;//其他帧填充闲置码
TxBuff[i*2+1] = NILL_DATA;
}
}
}

void calc_NumberData() //计算数值数据(数字机)
{
unsigned long Num_Negate[UARTBUFF_SIZE] = {0};
unsigned int i, j, k, n;
unsigned char byte_tmp[10], byte_tmp_negate[10];
float TxCount = 0.0;

for(i = DATA_START; i < UARTBUFF_SIZE; i++) //表意字符替换
{
if(UartBuff[i] == 'A')
UartBuff[i] = 0x0A;
if(UartBuff[i] == 'B')
UartBuff[i] = 0x0B;
if(UartBuff[i] == 'C')
UartBuff[i] = 0x0C;
if(UartBuff[i] == 'D')
UartBuff[i] = 0x0D;
if(UartBuff[i] == 'E')
UartBuff[i] = 0x0E;
if(UartBuff[i] == 'F')
UartBuff[i] = 0x0F;
}

n = DATA_START;
for(i = DATA_START; i < UARTBUFF_SIZE; i++) //数字的ASIIC码只用到第四位,所以将所有数字的低四位合并,高四位丢弃
{
if(i % 2 == 0)
UartBuff[n] = UartBuff[i] << 4;
else
{
UartBuff[n] |= UartBuff[i] & 0x0f;
n++;
}
}

k = Tx_Num * 2 + 1; //定位到地址码后数据所在的位置
for(i = DATA_START; i < UARTBUFF_SIZE; i += 5) //按四位取出并放在高四位准备倒序
{
byte_tmp[0] = (UartBuff[i] & 0xf0);
byte_tmp[1] = (UartBuff[i] & 0x0f) << 4;
byte_tmp[2] = (UartBuff[i + 1] & 0xf0);
byte_tmp[3] = (UartBuff[i + 1] & 0x0f) << 4;
byte_tmp[4] = (UartBuff[i + 2] & 0xf0);
byte_tmp[5] = (UartBuff[i + 2] & 0x0f) << 4;
byte_tmp[6] = (UartBuff[i + 3] & 0xf0);
byte_tmp[7] = (UartBuff[i + 3] & 0x0f) << 4;
byte_tmp[8] = (UartBuff[i + 4] & 0xf0);
byte_tmp[9] = (UartBuff[i + 4] & 0x0f) << 4;

for(j = 0; j < 10; j++) //倒序
{
for(n = 0; n < 8; n++)
{
byte_tmp_negate[j] <<= 1;
byte_tmp_negate[j] |= (byte_tmp[j] >> n) & 0x01;
}
}

Num_Negate[k] = 0x80000000; //重新组合
Num_Negate[k] |= (unsigned long)byte_tmp_negate[0] << 27;
Num_Negate[k] |= (unsigned long)byte_tmp_negate[1] << 23;
Num_Negate[k] |= (unsigned long)byte_tmp_negate[2] << 19;
Num_Negate[k] |= (unsigned long)byte_tmp_negate[3] << 15;
Num_Negate[k] |= (unsigned long)byte_tmp_negate[4] << 11;
Num_Negate[k + 1] = 0x80000000;
Num_Negate[k + 1] |= (unsigned long)byte_tmp_negate[5] << 27;
Num_Negate[k + 1] |= (unsigned long)byte_tmp_negate[6] << 23;
Num_Negate[k + 1] |= (unsigned long)byte_tmp_negate[7] << 19;
Num_Negate[k + 1] |= (unsigned long)byte_tmp_negate[8] << 15;
Num_Negate[k + 1] |= (unsigned long)byte_tmp_negate[9] << 11;
k = k + 2;

}

//UartCount = 25;
n = Tx_Num * 2 + 1; //将数据合并到Tx发送缓冲区并计算校验码
TxCount = 0.0;
for(i = Tx_Num * 2 + 1; i < UARTBUFF_SIZE; i++)
{
if(TxCount >= (UartCount - DATA_START - 1))
break;
if(i % 16 == 0) //每8帧(16个码字)插入一个同步码
TxBuff[n++] = SYNC_DATA;
TxBuff[n++] = calc_bch_and_parity( Num_Negate[i]);
TxCount += 5; //串口接收5个数字相当于POCSAG码中一个码字
if(n >= TXBUFF_SIZE || i >= UARTBUFF_SIZE) //超出缓冲区退出
break;
}


}

void calc_TextData() //计算汉字数据
{
unsigned long Text_Negate[UARTBUFF_SIZE] = {0};
unsigned int n, i, k, byte_tmp;
float TxCount = 0.0;
for(k = DATA_START; k < UARTBUFF_SIZE; k++) //字节按位倒序
{
byte_tmp = UartBuff[k];
UartBuff[k] = 0;
for(n = 0; n < 8; n++) //倒序
{
UartBuff[k] <<= 1;
UartBuff[k] |= (byte_tmp >> n) & 0x01;
}
UartBuff[k] &= 0xFE; //倒序后丢弃最低位
}

k = Tx_Num * 2 + 1;
for(n = DATA_START; n < UARTBUFF_SIZE; n += 20) //移位,20个字节一循环
{
Text_Negate[k] = 0x80000000;
Text_Negate[k] |= (unsigned long)UartBuff[n] << 23;
Text_Negate[k] |= (unsigned long)UartBuff[n + 1] << 16;
Text_Negate[k] |= (unsigned long)UartBuff[n + 2] << 9;
Text_Negate[k] &= 0xfffff800;

Text_Negate[k + 1] = 0x80000000;
Text_Negate[k + 1] |= (unsigned long)UartBuff[n + 2] << 29;
Text_Negate[k + 1] &= 0xC0000000;
Text_Negate[k + 1] |= (unsigned long)UartBuff[n + 3] << 22;
Text_Negate[k + 1] |= (unsigned long)UartBuff[n + 4] << 15;
Text_Negate[k + 1] |= (unsigned long)UartBuff[n + 5] << 8;
Text_Negate[k + 1] &= 0xfffff800;

Text_Negate[k + 2] = 0x80000000;
Text_Negate[k + 2] |= (unsigned long)UartBuff[n + 5] << 28;
Text_Negate[k + 2] &= 0xE0000000;
Text_Negate[k + 2] |= (unsigned long)UartBuff[n + 6] << 21;
Text_Negate[k + 2] |= (unsigned long)UartBuff[n + 7] << 14;
Text_Negate[k + 2] |= (unsigned long)UartBuff[n + 8] << 7;
Text_Negate[k + 2] &= 0xfffff800;

Text_Negate[k + 3] = 0x80000000;
Text_Negate[k + 3] |= (unsigned long)UartBuff[n + 8] << 27;
Text_Negate[k + 3] &= 0xF0000000;
Text_Negate[k + 3] |= (unsigned long)UartBuff[n + 9] << 20;
Text_Negate[k + 3] |= (unsigned long)UartBuff[n + 10] << 13;
Text_Negate[k + 3] |= (unsigned long)UartBuff[n + 11] << 6;
Text_Negate[k + 3] &= 0xfffff800;

Text_Negate[k + 4] = 0x80000000;
Text_Negate[k + 4] |= (unsigned long)UartBuff[n + 11] << 26;
Text_Negate[k + 4] &= 0xF8000000;
Text_Negate[k + 4] |= (unsigned long)UartBuff[n + 12] << 19;
Text_Negate[k + 4] |= (unsigned long)UartBuff[n + 13] << 12;
Text_Negate[k + 4] |= (unsigned long)UartBuff[n + 14] << 5;
Text_Negate[k + 4] &= 0xfffff800;

Text_Negate[k + 5] = 0x80000000;
Text_Negate[k + 5] |= (unsigned long)UartBuff[n + 14] << 25;
Text_Negate[k + 5] &= 0xFC000000;
Text_Negate[k + 5] |= (unsigned long)UartBuff[n + 15] << 18;
Text_Negate[k + 5] |= (unsigned long)UartBuff[n + 16] << 11;
Text_Negate[k + 5] |= (unsigned long)UartBuff[n + 17] << 4;
Text_Negate[k + 5] &= 0xfffff800;

Text_Negate[k + 6] = 0x80000000;
Text_Negate[k + 6] |= (unsigned long)UartBuff[n + 17] << 24;
Text_Negate[k + 6] &= 0xFE000000;
Text_Negate[k + 6] |= (unsigned long)UartBuff[n + 18] << 17;
Text_Negate[k + 6] |= (unsigned long)UartBuff[n + 19] << 10;
Text_Negate[k + 6] &= 0xfffff800;

k += 7;
}

n = Tx_Num * 2 + 1;
TxCount = 0.0;
for(i = Tx_Num * 2 + 1; i < UARTBUFF_SIZE; i++) //将数据合并到Tx发送缓冲区并计算校验码
{
if(TxCount >= (UartCount - DATA_START - 1))
break;
if(i % 16 == 0) //每8帧(16个码字)插入一个同步码
TxBuff[n++] = SYNC_DATA;
TxBuff[n++] = calc_bch_and_parity( Text_Negate[i]);
TxCount += 2.8571428571; //串口接收2.8571428571个字节相当于POCSAG码中一个码字
if(n >= TXBUFF_SIZE || i >= UARTBUFF_SIZE)
break;
}
}

void SendTxBuff()
{
unsigned int i;

PTT = 0; //触发PTT
Delay200ms(); //PTT延时,根据对讲机不同进行调整
if(UartBuff[SPEED] == 'L')
{
TL0 = LOWSPEED_TIMER_L; //设置定时初值
TH0 = LOWSPEED_TIMER_H;
}
if(UartBuff[SPEED] == 'H')
{
TL0 = HIGHTSPEED_TIMER_L; //设置定时初值
TH0 = HIGHTSPEED_TIMER_H;
}
TR0 = 1; //启动定时器

for(i = 0; i < 18; i++)//至少发送576个前导码,只能多,不能少
{
Send_Num(0xAAAAAAAA);
}

Send_Num(SYNC_DATA);//发送同步码

for(i = 0; i < TXBUFF_SIZE; i++)//将TX发送缓冲区中的数据发出
{
if(TxBuff[i] == 0x00000000 || TxBuff[i] == 0xffffffff)//如果缓冲区中数据为全0或全1则表示发送完毕
break;
Send_Num(TxBuff[i]);
}

TX=LOW;
TR0 = 0;
Delay200ms(); //发送完毕PTT延时,根据对讲机不同进行调整
Delay200ms();
Delay200ms();
PTT = 1;

UartSendString("OK\r\n",4); //串口输出调试信息
UartSendString("UartCount=",10);
SBUF = UartCount / 100 + '0';
while(TI == 0);
TI = 0;
SBUF = (UartCount / 10) % 10 + '0';
while(TI == 0);
TI = 0;
SBUF = UartCount % 10 + '0';
while(TI == 0);
TI = 0;
UartSendString("\r\n",2);
}

void main()
{
UartInit();//串口初始化
Timer0Init();//定时器0初始化

TX = 0; //初始化IO引脚
PTT= 1;

while(1)
{
//NewData = 1;
if(NewData == 1 && (UartBuff[TEXT_OR_NUM] == 'N' || UartBuff[TEXT_OR_NUM] == 'T'))
{
GetAddrNumber();//获取地址码及功能位
if(UartBuff[TEXT_OR_NUM] == 'N')
calc_NumberData();//计算数字数据
if(UartBuff[TEXT_OR_NUM] == 'T')
calc_TextData();//计算文字数据
SendTxBuff();//发送数据
Empty_Buff();//清空TX发送缓冲区和串口接收缓冲区
NewData = 0;
}
}
}

void UART_RxData() interrupt 4 //串口接收中断函数
{
if(RI == 1)
{
RI = 0;
UartTmp = SBUF;
if(UartTmp == '#') //串口开头
UartCount = 0;
if(UartTmp == '$') //串口结尾
{
UartTmp = 0x00;
NewData = 1;
}
UartBuff[UartCount] = UartTmp;
UartCount++; //串口计数
if(UartCount >= UARTBUFF_SIZE) //超出缓冲区归零
UartCount = 0;
}
}

void IntTimer0() interrupt 1 //定时器0中断函数
{
if(UartBuff[SPEED] == 'L')
{
TL0 = LOWSPEED_TIMER_L; //设置定时初值
TH0 = LOWSPEED_TIMER_H;
}
if(UartBuff[SPEED] == 'H')
{
TL0 = HIGHTSPEED_TIMER_L; //设置定时初值
TH0 = HIGHTSPEED_TIMER_H;
}
TM0_FLAG=1;
}

上位机源码(易语言):
上位机源码

上位机可执行二进制文件:
上位机可执行二进制文件

上位机截图:
01

如果你用的单片机型号和我的一样,并且对讲机没有频差的话,应该是直接可以使用的。

搞定了POCSAG编码的问题,剩下的加入GSM模块,蓝牙模块,接入公网什么的就容易多了。

PS?我是不是应该在github建一个仓库?我没用过啊~

还有,希望某些人不要太过自信

02

03

04

坚持原创技术分享,您的支持是我前进的动力!