需要注意的是,现在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模块就卖你几百块的商家,虽然我知道写程序画板子也不容易,但是价格是成本的十倍着实太坑。
单片机源码:
#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;
}
原作者出了个STM32的,基于STM32的寻呼发射器
项目地址:https://359303267.github.io/STM32_POCSAG_Transmit
视频教程:https://www.bilibili.com/video/BV1sZ4y1u76M?spm_id_from=444.42.0.0
原文地址:https://359303267.github.io/POCSAG/