harvardx 发表于 2015-1-8 00:07:37

响应沐紫活动,发个小硬货,关于直接交流采样,计算电压和...

发个代码,是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;
                }
        }
}

采集电路如下.比较简单.

苍天之刃 发表于 2015-1-8 09:38:11

谢谢舵主分享 :lol

harvardx 发表于 2015-1-8 00:12:26

因为整个功能比较简单,就是采集电压和频率. 于是程序结构也是比较简单, 在while循环里面 分别进行这2项工作,不断循环. 希望需要的同学,可以参考

harvardx 发表于 2015-1-8 00:13:22

因为,就用到了定时器和单片机自带adc ,虽然是个51程序,但是可以很方便的移植到其他单片机,比如st的M0 M3

这个可以叫 发表于 2015-1-8 00:21:17

很详细的资料!学习了,顶一个。

harvardx 发表于 2015-1-8 00:22:52

谢谢兄弟们支持.

harvardx 发表于 2015-1-8 00:25:27

其中rms的计算 ,大家可以看看相关的理论书籍,过采样定理.

harvardx 发表于 2015-1-8 00:36:50

有图有真相 .哈哈 喜欢的同学收藏.觉得好 回复点赞啊. 当然电路也有需要斟酌的地方.大家应用的时候可以改进 推翻. 只是抛砖引玉

大石头-349572 发表于 2015-1-8 00:41:58

不会51,帮顶一个!
要是用STM32,可以多顶几个

tsljy2011 发表于 2015-1-8 01:06:23

学习学习,参考一下

oipk 发表于 2015-1-8 02:02:20

群主,我是小张啊,哈哈。
页: [1] 2 3 4
查看完整版本: 响应沐紫活动,发个小硬货,关于直接交流采样,计算电压和...