通过STM32的输入捕获,获取高电平时间,计算BPC电波授时编码,并进行解码与校验,获取时间。
资料下载:待更新。。。。
安卓APP模拟电波发射测试用途:https://www.liqucn.com/rj/701595.shtml
1、BPC介绍
BPC电波钟是一种利用中国国家授时中心发射的BPC(Binary Phase-Shift Coding)低频时码信号进行时间校准的高科技计时设备。其主要特点和技术参数如下:
信号来源与频率
BPC电波钟接收的是中国国家授时中心位于河南商丘的低频时码发播台发射的信号,频率为68.5kHz,属于中长波信号。该信号每天广播21小时,覆盖范围包括地波半径1000公里和天波半径3000公里,基本覆盖中国大部分地区。
信号编码与解码
BPC信号采用方波脉冲秒编码,每分钟发送3帧数据,每帧包含19个有效位,用于表示秒、分、时、星期、日期、月份、年份等信息。解码时,通过检测脉冲宽度和间隔来获取时间信息。脉冲频率为1hz,高电平占0.1s,0.2s,0.3s,0.4s时,对应信息分别为0,1,2,3(也就是采用4进制编码模式,跟PWM舵机有点像,占空比大小,对应角度),通过计算高电平时间可以精确解码出时间。
以下为BPC编码
P0设在每分钟0,20, 40秒,以缺少秒脉冲使帧与帧隔开,同时作为帧起始预告。
P2为预留位。用于需要要扩充信息
P3是校验位,与“午前”,“午后”标志复用。0和2表示“P1”,“P2”,“时”,“分”,“星期”各位码的值转换成二进制表达式后,其“1”的个数为偶数,1和3表示“P1”,“P2”“时”,“分”,“星期”各位码的什转换成二进制表达式后,其“1”的个数为奇数,0和1同时表示午前,2和3同时表示午后。
P4是校验位与“年”的最高位利用,0和2表示“日”“月”“年”的低三位码的值转换成二进制表达式后,其“1”的个数为日奇数,0和1同时表示“年”的最高位的值为0,2和3同时表示“年”的最高位的值为1 。
图中帧状态的时间编码为:0021033021021030101 。表示的时间信息为:2004年3月9日,星期二,午前09时15分。该帧起始时间为:15分01秒
写代码时首先实现获取脉冲低电平时间的功能,用于检测P0,也就是帧间隔,程序里更容易实现起始帧检测。然后就是根据上面的编码方式进行解码,以及纠错和校验
电波超长距离传输很容易受到干扰,所以纠错以及校验是很有必要的
2、硬件
BPC模块 | STM32 |
---|---|
V | 3.3V |
G | GND |
T | PA0 |
P | GND,低电平工作 |
实物
3、代码
STM32代码采用C语言,标准库编写,软件是keil5,关键代码有中文注释,看不懂可以VX问我
#include "led.h"
#include "delay.h"
#include "sys.h"
#include "timer.h"
#include "usart.h"
#include "lcd.h"
extern u8 TIM2CH1_CAPTURE_STA; //输入捕获状态
extern u16 TIM2CH1_CAPTURE_VAL; //输入捕获值
char xiaoshi=0,fenzon=0,xinqi=0,ri=0,yue=0,nian=0,wu=0,miao=0;
int P1;
int i=0;
u8 a[19];
int main(void)
{
double temp=0;
int dat;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);// 设置中断优先级分组2
delay_init(); //延时函数初始化
uart_init(9600); //9600
LED_Init(); //初始化与LED连接的硬件接口
LCD_Init();
TIM2_Cap_Init(0XFFFF,72-1); //以1Mhz的频率计数
POINT_COLOR=RED;
LCD_ShowString(50,130,200,16,16," - - ");
LCD_ShowString(60,80,200,24,24," : : ");
while(1)
{
delay_us(10);
if(TIM2CH1_CAPTURE_STA&0X80)//成功捕获到了一次高电平
{
miao=miao+1;
if(miao==60) miao=0;
temp=TIM2CH1_CAPTURE_STA&0X3F;
temp*=65536; //溢出时间总和
temp+=TIM2CH1_CAPTURE_VAL; //得到总的高电平时间
temp=(double)temp/1000; //ms
if((temp>=850)&&(temp<950)) dat=0;
else if((temp>=750)&&(temp<850)) dat=1;
else if((temp>=650)&&(temp<750)) dat=2;
else if((temp>=550)&&(temp<650)) dat=3;
if(temp>1200)
{
printf("\r\n");
i=0;
miao++;
LCD_ShowNum(128,80,miao,2,24);
}
a[i]=dat;
i++;
if(i==19)
{
xiaoshi=((a[3])*4)+(a[4]);
fenzon=(((a[5])*16)+((a[6])*4)+(a[7]));
xinqi=((a[8])*4)+(a[9]);
ri=((a[11])*16)+((a[12])*4)+a[13]; // 换算方式
yue=((a[14])*4)+a[15];
nian=((a[16])*16)+((a[17])*4)+a[18];
wu=a[10]; //0和1表示上午 2和3表示下午
P1=a[1]; // 0表示1秒 1表示21秒 2表示41秒
if(P1==0)
{
miao=19;
}
if(P1==1)
{
miao=39;
}
if(P1==2)
{
miao=59;
}
if(wu>1)
{
xiaoshi=xiaoshi+12; //时间处理
}
}
TIM2CH1_CAPTURE_STA=0; //开启下一次捕获
printf("%d ",dat);
LCD_ShowNum(50,130,nian,4,16);
LCD_ShowNum(90,130,yue,2,16);
LCD_ShowNum(114,130,ri,2,16);
switch(xinqi)
{
case 0:
LCD_ShowString(160,130,200,16,16,"Sunday ");
break;
case 1:
LCD_ShowString(160,130,200,16,16,"Monday ");
break;
case 2:
LCD_ShowString(160,130,200,16,16,"Tuesday ");
break;
case 3:
LCD_ShowString(160,130,200,16,16,"Wednesday");
break;
case 4:
LCD_ShowString(160,130,200,16,16,"Thursday ");
break;
case 5:
LCD_ShowString(160,130,200,16,16,"Friday ");
break;
case 6:
LCD_ShowString(160,130,200,16,16,"Saturday ");
break;
}
LCD_ShowNum(60,80,xiaoshi,2,24);
LCD_ShowNum(94,80,fenzon,2,24);
LCD_ShowNum(128,80,miao,2,24);
POINT_COLOR=GBLUE;
LCD_ShowxNum(10,10,dat,10,24,0);
POINT_COLOR=RED;
}
}
}