响应沐紫活动,发个小硬货,关于直接交流采样,计算电压和...
发个代码,是51单片机的. 通过一个简单的二极管半波电路,可以采集220v交流电压和 计算220v的交流频率./********************************************************************************
* 项目名称: FBC04
* DATE: 20110828
* AUTHOR: Shine
* 要点:
*
* -----------------------------------------------------------------------------*
*********************************************************************************/
#include "inc/config.h"
volatile u08 SYS_hz_err_cnt =0;
volatile u08 SYS_220v_err_cnt =0;
u08 SYS_f_HZ = false; //频率是否报警
u08 SYS_f_220V = false; //电压是否报警
u08 SYS_hz_sample_cnt =0;
u08 SYS_f_1s =false; // 1s时间到标志位
volatile u16 SYS_cnt_1s =0; // 1s时间计数器
volatile u08 SYS_hz =0; // 1s时间计数器
volatile u08 SYS_cnt_hz =0; // 1s时间计数器
/*下列变量用于 定时器2中断 控制ad等间隔采样 */
volatile u08 f_ad_done = NO; //ad一个完整周期结束
volatile u08 ad_index = 0; //ad rms采样的序号
u08 SYS_cnt=0;
u08 SYS_hz_buf={50,50,50,50,50,50,50,50,50,50};//寄存器 用于滤波
/* 定时器1的启动 ,初始化*/
void tim1_init( void )
{
ET1=0;
TR1=0;
TF1=0;
/* 1ms 每次中断溢出 重载计数器 */
TH1 = (65535 - 1000 )/255;
TL1 = (65535 - 1000 )%255;
TR1 = 1;
ET1 = 1;
}
/*1 ---------------------- 频率监控函数 ------------------------- */
void get_hz( void )
{
u16 i =0;
u16 sum =0;
SYS_cnt_hz =0;
/* 设定定时器0的工作方式:
*16位计数器,模式, 外部T0 输入 .
*设置IO的配置,
*/
/* IO 配置
* 将P1.0 口作为T0的脉冲输入,下降沿有效
*/
P1MDIN = 0xff;
P1SKIP =0x0;
P0MDOUT = 0x04;
P1MDOUT = 0x1C;
P2MDOUT = 0x0C;
XBR0 = 0x07;
XBR1 = 0x50;
/*TIM0 配置:T0外部下降沿输入,工作方式1
*/
TMOD =(TMOD&0xF0)|0x05;
TH0=0; TL0=0;
SYS_cnt_1s=0;
SYS_f_1s = false;
TR0=1;
EA =1; //开中断 ,测量完毕后 关中断 .
while(1)
{
wdt_reset ( );
/* 1秒种到 */
if( SYS_f_1s == true )
{
/* 清零标志位*/
SYS_f_1s =false;
/* 停止timer0计数 */
TR0=0;
/* 读取TIMER0*/
SYS_cnt_hz = TL0;//获取一秒采集到的脉冲数,也就是频率
break;
}
}
/* 计算频率 */
SYS_hz = SYS_cnt_hz;
/* 本次采集中 如果超限 ,则报警计数器 +1*/
if( SYS_hz > 55 || SYS_hz < 45 )
{
SYS_hz_err_cnt++;
}
EA =0;
}
/*1 ---------------------- 频率监控函数 ------------------------- */
/*2 ---------------------- 电压监控函数 监控函数----------------- */
u32 SYS_T = 20000; //电压的周期 ;默认20ms 20000us
u32 SYS_delta_T = 20000/32; // 采样间隔
volatile u16 SYS_ad_buf={0,};
u32 SYS_ad_total =0;
u32 SYS_rms_total=0;
void get_220v( void )
{
u08 hz;
u08 i =0;
u08 j =0;
u08 k =0;
/*重新配置io ,p1.0配置为AD输入*/
/* AD通道为P10*/
AMX0N = 0x1F;
ADC0CN = 0x80;
/* VDD为基准*/
REF0CN = 0x0E;
/* 配置P1 .0为adc输入*/
P1MDIN = 0xFE;
P0MDOUT = 0x04;
P1MDOUT = 0x1C;
P2MDOUT = 0x0C;
P1SKIP = 0x01;
XBR0 = 0x07;
XBR1 = 0x40;
/* 连续采集32点 ,取有效值 ,
*注意同步: 周期为 1/SYS_hz
*/
if( SYS_hz >=45 && SYS_hz<=55)
{
hz =SYS_hz;
}
else
{
hz =50;
}
/* 计算周期 */
//SYS_T = 1000000UL/hz;
SYS_T = 1425000UL/hz; //微调延时值
SYS_delta_T = SYS_T/AC220_CNT;
/* 连续采集6秒,若全部超限,则报警 */
///////////////////////////////////////
/*清零错误计数*/
/* 连续 AC220_AVG_CNT RMS采样 ,取平均值*/
SYS_rms_total =0;
/* for循环: 采集rms值,共计AC220_AVG_CNT次*/
for( j=0; j< AC220_AVG_CNT; j++ )
{
SYS_ad_total =0;
/* 等间隔采样 32点 */
/* 启动定时器2 ,设置溢出间隔为SYS_delta_T
*设置ad由定时器2的溢出来启动
*/
/* ******************************定时器2的启动 ,初始化*/
T2SPLIT = 0; //工作在16位自动重装模式
T2XCLK =0; // SYSCLK/12 . 约为1us一个
TR2=0;
TF2H=0;
/* 625us 每次中断溢出 自动重载计数器 */
TMR2H= (65535 - SYS_delta_T )/255;
TMR2L = (65535 - SYS_delta_T )%255;
TMR2RLH = (65535 - SYS_delta_T )/255;
TMR2RLL = (65535 - SYS_delta_T )%255;
TR2 = 1;
ET2 = 1;
EA =1;
/*初始ad队列标记 及指针 */
f_ad_done = 0;
ad_index=0;
/* ******************************定时器2的启动 ,初始化*/
/* ******************************等待一个周期采集完成 */
while(1)
{
wdt_reset ( );
if( f_ad_done ==YES)
{
break;
}
}
/* ******************************等待一个周期采集完成 */
/* ***************************** 挨个处理,计算RMS */
for( i=0; i< AC220_CNT; i++ )
{
SYS_ad_total += (u32)SYS_ad_buf*SYS_ad_buf ;
wdt_reset ( );
}
/* 累加采集到的rms值*/
SYS_rms_total += KitFastSqrt(SYS_ad_total/32);
}
/* for循环: 采集rms值,共计AC220_AVG_CNT次*/
/* AC220_AVG_CNT x 0.02秒内,采集的rms平均值 */
SYS_rms_total = SYS_rms_total/AC220_AVG_CNT;
/* 限值判断 */
if(SYS_rms_total >= SYS_AC220_253|| SYS_rms_total <= SYS_AC220_207 )
{
SYS_220v_err_cnt++;
}
}
void main ( void )
{
u08 cnt=0;
/*1 初始化CPU*/
Init_Device ();
/*2 关闭继电器 */
RELAY =0;
/*3 初始化定时器1*/
tim1_init();
/*4 上电指示灯闪烁 */
LED_HZ =0;LED_220V =0; _delay_100ms();
LED_HZ =1;LED_220V =1; _delay_100ms();
LED_HZ =0;LED_220V =0; _delay_100ms();
LED_HZ =1;LED_220V =1; _delay_100ms();
LED_HZ =0;LED_220V =0; _delay_100ms();
LED_HZ =1;LED_220V =1; _delay_100ms();
EA =0;
cal();
/*高低压报警值到内存加载*/
read_user_data_to_ram();
/*5 总得工作循环 */
while(1)
{
wdt_reset( );
/* 5-1 进入一个判断循环前,首先清零频率和电压的错误计数*/
SYS_hz_err_cnt =0;
SYS_220v_err_cnt =0;
/* 5-2进入一个判断周期 ,连续测量5-6次,耗时 Nx 1.2S*/
for( cnt=0; cnt < AC_ERR_CNT; cnt++ )
{
/*先采集频率 因为涉及到后面的计算
* 耗时约1s;
*/
get_hz( );
/*根据频率再计算电压 耗时约 AC220_AVG_CNT x 0.02s
* 如.10x0.02 =0.2s
*/
get_220v( );
}
/* 5-3 分别判断频率 和电压 是否超限 */
/* 5-3-1 如果连续的测量都超限制*/
// if( SYS_hz_err_cnt >= AC_ERR_CNT )
// {
// /* 则报警 */
// SYS_f_HZ = true;
// LED_HZ = 0;
// }
// else
// {
// /* 则报警 */
// SYS_f_HZ = false;
// LED_HZ = 1;
// }
/* 5-3-2 如果连续的测量都超限制*/
if( SYS_220v_err_cnt >= AC_ERR_CNT )
{
/* 则报警 */
SYS_f_220V = true;
LED_220V = 0;
}
else
{
/* 则报警 */
SYS_f_220V = false;
LED_220V = 1;
}
/* 继电器输出控制 */
if( SYS_f_220V == true || SYS_f_HZ == true )
{
RELAY =1;
}
else
{
RELAY =0;
}
}
}
/*********定时器0的中断,来产生系统工作的基本时序**************/
//定时器0 中断服务函数
void T1_ISR( void ) interrupt 3 //timer1 外部中断3
{
TR1 = 0;
if( SYS_cnt_1s++ > 1350)
{
SYS_cnt_1s =0 ;
SYS_f_1s = true;
}
/* 1ms 每次中断溢出 重载计数器 */
TH1 = (65535 - 1000 )/255;
TL1 = (65535 - 1000 )%255;
TR1 = 1;
}
/* AD定时采集: 自动采集32点数据, 625us间隔,*/
void T2_ISR( void ) interrupt 5
{
/* 软件清除溢出标记 */
if( TF2H )TF2H =0;
if( TF2L )TF2L =0;
if( f_ad_done == 0)
{
if(ad_index<32)
{
SYS_ad_buf[ ad_index ] = ad_read(0);
}
ad_index++ ;
if(ad_index ==32 )
{
/* 设置采集完成标志*/
f_ad_done = YES;
/* 队列指针归0*/
ad_index =0;
/* 暂停定时器2*/
ET2 =0;
TR2 =0;
TMR2H= 0;
TMR2L = 0;
}
}
}
采集电路如下.比较简单.
谢谢舵主分享 :lol 因为整个功能比较简单,就是采集电压和频率. 于是程序结构也是比较简单, 在while循环里面 分别进行这2项工作,不断循环. 希望需要的同学,可以参考 因为,就用到了定时器和单片机自带adc ,虽然是个51程序,但是可以很方便的移植到其他单片机,比如st的M0 M3 很详细的资料!学习了,顶一个。 谢谢兄弟们支持. 其中rms的计算 ,大家可以看看相关的理论书籍,过采样定理. 有图有真相 .哈哈 喜欢的同学收藏.觉得好 回复点赞啊. 当然电路也有需要斟酌的地方.大家应用的时候可以改进 推翻. 只是抛砖引玉 不会51,帮顶一个!
要是用STM32,可以多顶几个 学习学习,参考一下 群主,我是小张啊,哈哈。