需要注意的是,现在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/