2021年陕西省大学生电子设计竞赛7校联赛题信号相位检测和波形合成系统

一、任务

设计并制作如图1所示的系统。

图1 系统方框图

二、要求

1. 基本要求

(1)设计并制作一个1kHz正弦波产生电路,输出1kHz、2VP-P正弦波电压。

(2)设计并制作一个10°~ 60°可变移相电路,相位移动由单片机键盘输入控制,分辨率为10°。

(3)设计并制作一个相位检测电路,当两个正弦信号相位差≤10°时,输出0V直流电压,当两个正弦信号相位差为60°时,输出5V直流电压。其输出直流电压的误差<1V。

(4)数字实时显示两个正弦信号的相位差。

2. 发挥部分

(1)设计并制作单元电路:整形及谐波产生电路、谐波提取电路、信号调理电路、加法器电路。

(2)将基波和三个谐波合成一个近似的方波,方波重复频率为1kHz,方波的幅度为0~5V。

(3)其他。

基础部分分析:

整个基础部分的难点在于可变移相电路的设计以及相位检测电路的设计。

(1)1kHz的正弦波发生器,我们使用的是文氏桥振荡电路,再接衰减器将幅度调整为2Vpp;

(2)可变移相电路只需要实现10°~60°的移相要求,所以我们设计了一个90°的移相器,使用数字电位器,通过32控制数字电位器的电阻值,即可控制移相的大小。具体电路如下图所示:

X9C系列数字电位器是这样的原理,103代表10K,104代表100K和模拟电位器一样,这个系列电位器都有共同的特点就是一共有100步,103的话1步代表100欧,这样100步就是10K欧,104同理。一次性跳动步数可以设置,直接for循环封装成函数即可。

在使用X9C103时,犯了一个错误是Vw没有和VL接在一起,如果不接就无法正常使用,上面原理图就是x9c系列数字电位器的标准固定用法。

x9c系列电位器驱动代码一样没有任何区别,代码如下:

//延时us子程序

void Delay(unsigned int t)

{

unsigned int i;

for(i=0;i<t;i++);

}

// 数字电位器向上调一步

// 数字电位器共99步

void X9C103_Inc_N_Step(unsigned char N)

{

   unsigned char i=0;

          

        HAL_GPIO_WritePin(GPIOF,CS_Pin,GPIO_PIN_RESET);          // CS  拉低

        HAL_GPIO_WritePin(UD_GPIO_Port,UD_Pin,GPIO_PIN_SET);     // U/D 拉高   

//则下面的INC下沿,执行UP操作  

        Delay(3);           // 延时大约2us

        for(i = N; i>0; i--)

        {

            HAL_GPIO_WritePin(GPIOF,INC_Pin,GPIO_PIN_RESET);  // 因为GPIO初始化时,//INC已经拉高了。INC 拉低;  // 产生一个下沿

            Delay(2);       // 延时2us左右

            HAL_GPIO_WritePin(GPIOF,INC_Pin,GPIO_PIN_SET);    // 拉高INC //保存模式

        }

        HAL_GPIO_WritePin(GPIOF,CS_Pin,GPIO_PIN_SET);         // 完毕CS拉高

}

// 数字电位器向下调一步

// 数字电位器共99步

void X9C103_Dec_N_Step(unsigned char N)

{

    unsigned char i=0;

    HAL_GPIO_WritePin(UD_GPIO_Port,UD_Pin,GPIO_PIN_RESET);               //CLRB_X9C103_UD;   // U/D 清0,  则下面的INC下沿,执行Down操作  

    Delay(3);            // 延时大约2us

    HAL_GPIO_WritePin(GPIOF,CS_Pin,GPIO_PIN_RESET);  

   for(i=N;i>0;i--)

  {

     HAL_GPIO_WritePin(GPIOF,INC_Pin,GPIO_PIN_SET);               // 拉高INC    因为INC的下沿有效

     Delay(2);              // 延时2us左右

     HAL_GPIO_WritePin(GPIOF,INC_Pin,GPIO_PIN_RESET);             // INC 拉低;  //

//产生一个下沿

     Delay(600);            // 延时大约500us, Rw等待输出稳定

  }

     HAL_GPIO_WritePin(GPIOF,INC_Pin,GPIO_PIN_RESET);//保存模式

     HAL_GPIO_WritePin(GPIOF,CS_Pin,GPIO_PIN_SET);     // 完毕CS拉高

}

#define LED1_Pin GPIO_PIN_5

#define LED1_GPIO_Port GPIOE

#define CS_Pin GPIO_PIN_13

#define CS_GPIO_Port GPIOF

#define INC_Pin GPIO_PIN_15

#define INC_GPIO_Port GPIOF

#define UD_Pin GPIO_PIN_1

#define UD_GPIO_Port GPIOG

#define LED0_Pin GPIO_PIN_5

#define LED0_GPIO_Port GPIOB

这是管脚定义

(3)相位检测电路:

如图所示,由于我们比较的是两个正弦波的相位差,所以先将两个正弦波经过比较器之后变成两个数字序列,两个方波传入7474双D触发器的时钟输入端,两个D触发器的异步置1端均置1,即异步置1端无效,输入信号D输入1,假设初始输出为0,那么下方的触发器异步清零端有效,即下方的触发器处于清零状态,输出Q为0,Q非为一,那么上方的触发器异步清零端无效,输出Q=D,当上方信号上升沿到来时,Q输出1,那么下方触发器异步清零端无效,在下方信号上升沿到来时,输出Q=1,Q非为0,上方触发器清零,输出Q=0

则输出的高电平持续时间即为两个信号的相位差。

此处要注意:LM393不能使用双电源供电,因为74HC74是数字芯片,输入的信号不能有负,若是393使用双电源,生成的方波信号是有正有负的,不能传入7474芯片。

逻辑图如下图所示:

输出信号的占空比便可以反映出两个信号相位差,相位差相差10°,占空比变化约为2.8%。

再利用PWM波调节输出电压的原理,将生成的信号转换成相应的电压数据,传入32,即可在32上显示两个波形的相位差。

发挥部分:

发挥部分的难点主要在于各次谐波合成时,要确保各次谐波的相位一致,信号幅度分别为,三次谐波幅值为基波的1/3,五次谐波幅值为基波的1/5,七次谐波为基波的1/7(即满足傅里叶级数)

三次谐波、五次谐波、七次谐波由filterpro软件生成,电路做出来后数值进行了一些调整,此处便不再展示电路图。

然后将各次谐波的相位调整一致,电路图如下:

此处使用了超前与滞后结合的180°移相,由于滤波出来的波形已经确定,所以将各次谐波分别与基波进行对照调整,使其初始相位一致,如果滤波电路设计合理,生成的各次谐波的幅值满足合成条件,但是经过移相之后,幅值会发生改变,所以信号调理电路中还需要有一个放大器来调整信号的幅度,实际电路做出来后,需将移相器与放大器结合在一起对谐波进行调整。

各次谐波信号调整好后,将四路信号相加,生成方波,电路如图所示:

由于只有基波、三次谐波、五次谐波和七次谐波,所以最终合成的并不是一个完整的方波,方波形式大致如图所示:

 因为是训练,所以给自己加了点训练量,把原始波形和移相波形显示到lcd上

#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "tftlcd.h"
//#include "picture.h"
#include "adc.h"
u16   buf1[320];
    u16   buf[320];
    u16   table[11]  ={0,25,50,75,100,125,150,175,200,225,250};
    u16        table1[41] ={0,5,10,15,20,30,35,40,45,55,60,65,70,80,85,
                                            90,95,105,110,115,120,130,135,140,145,155,
                                            160,165,170,180,185,190,195,205,210,215,220,
                                            230,235,240,245};
    u16 Ypos1=100,Ypos2=100,Xpos1,Xpos2;
  u16 Ypos11=100,Ypos21=100,Xpos11,Xpos21;                                    
    u16 adcx,dsl;
    float temp1;
    u16  t=0;
    u16  t1=0;                                        
    u16  h=0;
                                            
    u16 d=0;
    u32  bei=1;
    u16 Yinit=150;
    u16 Yinit1=150;                                    
    u8 a;
    u8 len;    
                    
    int T; 
    float F;

                                            
    float VMAX;
    float VMIN;                


void LCD_Clearone(u16 color,u16 a,u16 b)
{
    uint16_t i, j ;

    LCD_Set_Window(0, 0, a-1, b-1);     //作用区域
      for(i=0; i<a; i++)
    {
        for (j=0; j<b; j++)
        {
            LCD_WriteData_Color(color);
        }
    } 
}

void drawgaid() //画网格
{
    u16 x,y;
    for(x=0;x<320;x=x+25)
        for(y=0;y<201;y=y+5)
        {
            LCD_DrawPoint(x,y);
        }
    for(y=0;y<201;y=y+25)
        for(x=0;x<320;x=x+5)
        {
            LCD_DrawPoint(x,y);
        }
}

float get_vpp(void)       //获取峰峰值
{
        
    u32 max_data=buf[0];
    u32 min_data=buf[0];
    u32 n=0;
    float Vpp=0;
    for(n = 1;n<201;n++)
    {
        if(buf[n]>max_data)
        {
            max_data = buf[n];
        }
        if(buf[n]<min_data)
        {
            min_data = buf[n];
        }            
    }     
    VMAX=max_data;
    VMIN=min_data;
    Vpp = (float)(VMAX-VMIN);
    Vpp = Vpp*(3300.0/4095);
    return Vpp;    
        
    }

void getT()
{
    int i=0;int j=0;
    for(i=0;i<320;i++)
    {
        if(buf[i+1]>buf[i]&&buf[i+1]>buf[i+2])
            j=i+1;
        if(buf[i+1]<buf[i]&&buf[i+1]<buf[i+2])
            T=(i+1-j)*14;
    }
}
    

void DrawOscillogram1()
    {
            for(t=0;t<320;t++)//存储AD
            {
            
                buf[t] =  Get_Adc_Average(ADC_Channel_1,1);
                //buf1[t] = Get_Adc_Average(ADC_Channel_2,1);
            }
            //delay_us(500);
            for(t=0;t<320;t++)//存储AD数值
            {
                //buf[t] =  Get_Adc_Average(ADC_Channel_1,1);
                buf1[t] = Get_Adc_Average(ADC_Channel_2,1);
            }
            //clear_point(t1);
            LCD_Clearone(BLACK,320,201);  //区域清屏
                for(t=0;t<320;t++)
            {
            
                //clear_point(t );    
                FRONT_COLOR=WHITE;  //通道2波形颜色
                Ypos21=Yinit1-buf1[t]*100/4096;//转换坐标
                Ypos21=(int)Ypos21*1;
                LCD_DrawLine (t ,Ypos11 , t+1 ,Ypos21  );
                Ypos11 =Ypos21 ;
            
                Ypos2=Yinit-buf[t]*100/4096;//转换坐标
                Ypos2=(int)Ypos2*1;
                FRONT_COLOR=BRRED;  //通道1波形颜色
                LCD_DrawLine (t ,Ypos1 , t+1 ,Ypos2  );
                Ypos1 =Ypos2 ;
                
            }    
            /*for(t1=0;t1<240;t1++)
            {
                clear_point(t1 );    
                Ypos21=Yinit1-buf1[t1]*100/4096;//转换坐标
                Ypos21=(int)Ypos21*1;
                LCD_DrawLine (t1 ,Ypos11 , t1+1 ,Ypos21  );
                Ypos11 =Ypos21 ;
            }    */
            
    }

int main()
{
    u8 i=0;
    u16 color=0;
    u16 adc_data;
    float temp;
    
    SysTick_Init(72);
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //中断优先级分组 分2组
    LED_Init();
    USART1_Init(115200);
    TFTLCD_Init();            //LCD初始化
    ADCx_Init();
    
//    FRONT_COLOR=BLACK;
//    LCD_ShowString(10,10,tftlcd_data.width,tftlcd_data.height,12,"Hello World!");
//    LCD_ShowString(10,30,tftlcd_data.width,tftlcd_data.height,16,"Hello World!");
//    LCD_ShowString(10,50,tftlcd_data.width,tftlcd_data.height,24,"Hello World!");
//    LCD_ShowFontHZ(10, 80,"普中科技");
//    LCD_ShowString(10,120,tftlcd_data.width,tftlcd_data.height,24,"www.prechin.cn");
//    
    LCD_Fill(10,150,60,180,GRAY);
    color=LCD_ReadPoint(20,160);
    LCD_Fill(100,150,150,180,color);
//    printf("color=%x\r\n",color);
    
    //LCD_ShowPicture(20,220,200,112,(u8 *)gImage_picture);
    drawgaid();
    FRONT_COLOR=BLUE;
    LCD_ShowString(10,205,tftlcd_data.width,tftlcd_data.height,16,"Vout:2.000V");
    
    //LCD_Color_Fill(10,400,40,430,BLACK);
    while(1)
    {
        DrawOscillogram1();
        
        //    
            temp1=get_vpp();//峰峰值mv        
            getT();
            F=(float)1000000/T;
            //LCD_DisplayNum(49,210,temp1,4,24,0);    //显示峰峰值(adc3度数)
            //LCD_DisplayNum(49,235,(float)(VMAX+VMIN)/2.85*(3300.0/4095),4,24,0);    //显示有效值(写字初相和移相颜色)
            
            adc_data=Get_Adc_Average(ADC_Channel_3,10);//获取通道1的转换值,10次取平均
        //adc_data=Get_Adc(ADC_Channel_1);
        //LCD_DisplayNum(134,130,adc_data,4,16,0);    //显示ADCC采样后的原始值
        temp=(float)adc_data*(3.3/4096);
    
            //获取计算后的带小数的实际电压值,比如5.1141
        adc_data=temp;            //赋值整数部分给adc_data变量,因为adc_data为u16整形
            
        
        ///
            FRONT_COLOR=BLUE;
            LCD_ShowString(184,240,tftlcd_data.width,tftlcd_data.height,16,"o");

            if(temp>=0&&temp<=0.1)
            {
                LCD_ShowString(100,240,tftlcd_data.width,tftlcd_data.height,24,"XWYD:00");
            
            }
            else if(temp>0.1&&temp<=0.2)
            {
                LCD_ShowString(100,240,tftlcd_data.width,tftlcd_data.height,24,"XWYD:10");
            
            }
            else if(temp>0.2&&temp<=0.3)
            {
                LCD_ShowString(100,240,tftlcd_data.width,tftlcd_data.height,24,"XWYD:20");
            
            }
            else if(temp>=0.31&&temp<=0.4)
            {
                LCD_ShowString(100,240,tftlcd_data.width,tftlcd_data.height,24,"XWYD:30");
            
            }
            else if(temp>0.4&&temp<=0.5)
            {
                LCD_ShowString(100,240,tftlcd_data.width,tftlcd_data.height,24,"XWYD:40");
            
            }
            else if(temp>0.5&&temp<=0.6)
            {
                LCD_ShowString(100,240,tftlcd_data.width,tftlcd_data.height,24,"XWYD:50");
            
            }
            else if(temp>0.6&&temp<=1.3)
            {
                LCD_ShowString(100,240,tftlcd_data.width,tftlcd_data.height,24,"XWYD:60");
            
            }
            else 
            {
                LCD_ShowString(100,240,tftlcd_data.width,tftlcd_data.height,24,"^*--*^");
            
            }
            //
            //LCD_DisplayString(100,230,24,"000000");
                FRONT_COLOR=RED;
            LCD_ShowNum(160,210,adc_data,1,24);                 //显示电压值的整数部分,
            
        temp-=adc_data;                             //把已经显示的整数部分去掉,留下小数部分,比如5.1141-5=0.1141
        temp*=1000;          
        LCD_ShowNum(184,210,temp,3,24);     //小数部分乘以1000,例如:0.1141就转换为114.1,相当于保留三位小数。
        //显示小数部分(前面转换为了整形显示),这里显示的就是114.
            //LCD_DisplayNum(49,260,F,5,24,0);    //TEST
            //LCD_DisplayNum(49,285,T,4,24,0);    //TEST
            //LED1=!LED1;//判断程序是否运行
            
        
        i++;
        if(i%20==0)
        {
            LED1=!LED1;
        }
        
        delay_ms(10);        
    }
}

adc配置

#include "adc.h"
#include "SysTick.h"


void ADCx_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量    
    ADC_InitTypeDef       ADC_InitStructure;
    
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_ADC1,ENABLE);
    
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);//设置ADC分频因子6 72M/6=12,ADC最大时间不能超过14M
    
    GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;//ADC
    GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AIN;    //模拟输入
    GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
    GPIO_Init(GPIOA,&GPIO_InitStructure);
    
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;//非扫描模式    
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//关闭连续转换
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//禁止触发检测,使用软件触发
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右对齐    
    ADC_InitStructure.ADC_NbrOfChannel = 1;//1个转换在规则序列中 也就是只转换规则序列1 
    ADC_Init(ADC1, &ADC_InitStructure);//ADC初始化
    
    ADC_Cmd(ADC1, ENABLE);//开启AD转换器
    
    ADC_ResetCalibration(ADC1);//重置指定的ADC的校准寄存器
    while(ADC_GetResetCalibrationStatus(ADC1));//获取ADC重置校准寄存器的状态
    
    ADC_StartCalibration(ADC1);//开始指定ADC的校准状态
    while(ADC_GetCalibrationStatus(ADC1));//获取指定ADC的校准程序

    ADC_SoftwareStartConvCmd(ADC1, ENABLE);//使能或者失能指定的ADC的软件转换启动功能
}


u16 Get_Adc(u8 ch)   
{
      
    ADC_RegularChannelConfig(ADC1, ch, 1, ADC_SampleTime_239Cycles5 );    //18us,ADC1,ADC??,?????239.5??                      
  
    ADC_SoftwareStartConvCmd(ADC1, ENABLE);        使能指定的ADC1的软件转换启动功能    
     
    while(!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC ));//等待转换结束

    return ADC_GetConversionValue(ADC1);    //返回最近一次ADC1规则组的转换结果
    /
    
    
}

u16 Get_Adc_Average(u8 ch,u8 times)
{
    u32 temp_val=0;
    u8 t;
    for(t=0;t<times;t++)
    {
        temp_val+=Get_Adc(ch);
        delay_us(1);
    }
    return temp_val/times;
}
这是代码是普中科技的32板子适用,并且显示波形可以体现出一个问题是,采集双通道1KHZ正弦波时候,32是顺序执行,不是并行,所以通道1和通道会有时间差,通道1延时500ms等待通道采集完毕再一起显示,在lcd屏幕上可以看到双通道波形是重合的。

这个题目非常的基础,用来入门训练最合适不过。

  • 8
    点赞
  • 55
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FLipped杰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值