基于单片机的风速测量仪设计与制作详解

收藏和点赞,您的关注是我创作的动力

文章目录

  • 一、设计介绍
  • 二、设计内容
  • 三、风速风向电路设计
  • 程序设计
  • 风速风向检测子程序设计:
  • 四、主要代码
  • 五、程序截图
  • 六、论文目录

  • 一、设计介绍

        风速、风向的测量在气象预报、环境监测、风力发电、航空航天等领域中有着重要意义。随着传感器技术、微处理器技术和网络通信技术的发展,相比传统的人工观测,数字化、智能化的气象仪器在观测精度、速度和稳定性等方面都有较大优势,因此针对数字化的气象仪器进行设计具有较大意义,尤其是便携式小型化的仪器,虽然通过天气预报可以知道当前的风速风向信息,但是这些都是大范围的,无法感知某一局部区域的风速风向,因此本文围绕风速风向仪进行设计。
    本次基于单片机的风速风向仪设计与实现,整个系统包括单片机最小系统STC89C52,RM-FS-N01风速传感器,RM-FX-N01风向传感器,压力传感器采集的数据变换并传送至单片机进行处理,再由液晶显示屏显示测量的风速值。对于风向系统利用编码器在0~360°范围内进行测量收集信号,在多圈旋转的情况下能够实现单圈自动归零,通过单片机进行数据的转化处理,测量风向也通过液晶显示屏显示。软件部分的设计采用模块化编程方式,方便程序的维护和改进。

    二、设计内容

      本课题为“基于单片机的风速风向仪设计与实现”,在功能上设计如下:
    (1)具有基本测量功能,可以检测风速,风向;(2)具有显示信息的功能,可以显示风速、风向的基本信息;(3)具有语音播报功能,可以播报当前的风速风向大小;(4)可以通过按键设定阈值,当风速风向超过阈值时,进行声音提示(5)具有无线传输功能,可以将检测的数据传输到手机APP;根据上述功能,设计了如图2.1所示的系统总体架构,整个系统包括单片机最小系统STC89C52,RM-FS-N01风速传感器,RM-FX-N01风向传感器,MY1680语音播报模块,OLED液晶,ESP8266无线通信模块,蜂鸣器,ADC0832模数转换器等等,在开发环境上,采用Altium完成系统原理图的设计,通过Keil平台和C语言完成软件程序的编写。

    图2-1系统总体设计框图

    三、风速风向电路设计

      在风速风量测量仪中需要实现对风速和风向的检测,在此出于设计的实际情况选择的是直接通过风速传感器来完成该功能,选用的是RM-FS-N01风速传感器和RM-FX-N01风向传感器,这两种传感器,都支持5V进行供电,并且会将检测到的风速和风向,以模拟量的形式进行输出,因此单片机控制器只需要去检测去模拟量输出的电压大小,就可以得到风速和风向参数,其电路如图所示。

    图3-2风速和风向检测电路图
       在本次设计中,采用的是STC89C52单片机,该单片机并没有内嵌了模数转换器,因此需要选择外部的ADC模数转换器,通过参考网络资源在此选择了ADC0832芯片,该芯片是一款8位精度的低成本,低功耗的模数转换器,其采用串行的数据输出方式,单片机只需要三根线与其CLK,CS,DI进行连接,就可以获取模拟量数据。整个ADC0832芯片一共具有8个引脚,采用5V进行供电,其转换速率高达250KHZ,功耗低至10mW,并且具有CH0和CH1两路模拟数据输入功能,需要注意的是,这两路输入数据的电压大小应当小于ADC0832的供电电源,否则可能造成芯片的损坏,在控制上需要注意的是,单片机读取速度不能太快,否则ADC0832会来不及转换,这样会造成数据的错误。单片机在获得正确数据量后,再将其进行换算就可以得到检测到的模拟量电压值。在此风速输出连接到ADC0832的2脚CH0,风向输出连接到ADC0832的3脚CH1,单片机的P1.2连接到ADC0832的1脚CS,P1.1连接到ADC0832的7脚CLK,P1.0连接到ADC0832的5脚和6脚DI,DO。
       进行A/D转换时,须将CS使能置于低电平并保持到转换结束。当芯片开始转换工作的同时由单片机处理器向芯片的时钟输入端输入CLK时钟脉冲,为ADC0832提供工作频率。DO和DI引脚一同连接单片机的P1.0引脚,根据工作时序当单片机P1.1输出高电位给ADC0832的CLK引脚时,ADC0832的DO引脚向单片机P1.0引脚输出AD转换的数字量结果,由单片机P1.0引脚向ADC0832的DI引脚输出控制信号与通道选择控制命令。具体工作原理如下。在时钟的上升沿,DI端的数据移入ADC0832内部的多路地址移位寄存器。在第一个时钟期间,DI为高,表示启动位,紧接着输入两位配置位。当输入启动位和配置位后,选择输入模拟通道,转换开始。转换开始后,经过一个时钟周期延迟,以使选定的通道稳定。ADC0832接着在第4个时钟下降沿输出转换数据。数据输出时先输出最高位(DATA7-DATA0);输出完转换结果后,又以最低位开始重新输出一遍数据(DATA7-DATA0),两次发送的最低位共用。当CS为高电平时,内部所有寄存器清0,输出变为高阻态。
    风速风向采集模块输出的模拟信号由2脚与3脚输入到ADC0832中,经过转换后将数字信号从6脚输出送入到单片机的P1.0引脚进行处理。

    程序设计

       本文软件设计是以一个主程序实现对各个子程序的协调调用,进而按照要求实现预设计的功能,本系统的风速风量测量仪主程序流程图如图4.1所示,在风速风量测量仪程序启动后,然后进行系统初始化,初始化函数包括:分串口初始化,液晶显示初始化。在系统的初始化工作完成后,首先由单片机发出工作开始信号,启动风速风向仪,进入循环调用ADC083采集程序以读取风向,读取风速风向,并通过显示模块显示,然后根据阈值进行报警判定,接着检测是否有按键按下,如果有则进行按键处理,之后再通过显示模块显示更新后的风速及风向。

    风速风向检测子程序设计:

    在此搭配了ADC0832模数转换器来对风速风向进行检测,其流程如图4.3所示,风向检测:当程序开始后,选择CH1通道,此时在第三个CLK时钟,DAT会给低低电平,然后读前8位数据,再读后8位数据,如果前后8位一致则输出结果,如果不一致则输出0,接着释放ADC0832,然后通过液晶进行显示风向。风速检测采用的是CH0通道,设计思路与风向相同。

    图4-1风速风向检测子程序流程图

    四、主要代码

    主程序:
    void main(void)
    {
    static u8 buff[20];//
    unsigned char i,n;
    uchar shezhi_flag=1,t;//设置变量
    unsigned char Tx_Buf[7];
    uint caiji_time=0;
    int type;
    float val=0,qs_fs=0;//风度
    float qs_fx=0;//风向变量
    InitUART();
    OLED_Init(); //OLED初始化
    sprintf((char*)buff,"fazhi:%2d",data1); 
    22
    OLED_P8x16Str(0,0,buff);
    sprintf((char*)buff,"fengsu:%2d.%1dm/s",(uint)qs_fs/10,(uint)qs_fs%10); 
    OLED_P8x16Str(0,4,buff);
    while(1)
    { 
    delayms(10);
    /*输出值(V) 对应风向
    ≈0 北风
    ≈0.7143 东北风
    ≈1.4286 东风
    ≈2.1429 东南风
    ≈2.8571 南风
    ≈3.5714 西南风
    ≈4.2857 西风
    ≈5 西北风*/
    //读取风向
    qs_fx=adc0832(0)*5/25.5; 
    if(qs_fx>48) 
    {
    type=1;
    OLED_P8x16Str(0,2," xibei feng 315");
    }
    else if(qs_fx>40)
    {
    type=2;
    OLED_P8x16Str(0,2," xi feng 270");
    }
    else if(qs_fx>33)
    {
    type=3;
    OLED_P8x16Str(0,2," xinan feng 225");
    }
    else if(qs_fx>26)
    {
    type=4;
    OLED_P8x16Str(0,2," nan feng 180");
    } 
    else if(qs_fx>19)
    {
    type=5;
    OLED_P8x16Str(0,2,"dongnan feng 135");
    }
    else if(qs_fx>12)
    {
    type=6;
    OLED_P8x16Str(0,2," dong feng 90");
    }
    23
    else if(qs_fx>5) 
    {
    type=7;
    OLED_P8x16Str(0,2," dongbei feng 45");
    }
    else if(qs_fx>=0)
    {
    type=8;
    OLED_P8x16Str(0,2," bei feng 0");
    } 
    //读取风速
    val+=adc0832(1)*50/2.55;
    if(++caiji_time>=15)
    { 
    qs_fs=val/caiji_time;
    qs_fs=qs_fs*0.27;
    val=0;
    caiji_time=0;
    sprintf((char*)buff,"fengsu:%2d.%1dm/s",(uint)qs_fs/10,(uint)qs_fs%10); 
    OLED_P8x16Str(0,4,buff);
    } 
    if((uint)qs_fs>data1*10)
    {
    buzzer=0;
    }
    else
    {
    buzzer=1;
    }
    if(Mode==0){
    delayms(10);
    if(Mode==0){
    while(!Mode);
    n=1;
    while(n){ 
    printf((char*)buff,"fazhi:%2d",data1);
    OLED_P8x16Str(0,0,buff);
    OLED_P8x16Str(80,0,"<");
    if(Mode==0){
    delayms(10);
    if(Mode==0){
    while(!Mode);
    OLED_P8x16Str(0,0," ");
    n=0;
    while(!Mode);
    }
    }
    24
    if(Add==0){
    delayms(10);
    if(Add==0){
    data1++;
    }
    }
    if(Reduc==0){
    delayms(10);
    if(Reduc==0){
    if(data1!=0){
    data1--;
    }
    }
    }
    } 
    }
    }
    sprintf((char*)buff,"fazhi:%2d",data1);
    OLED_P8x16Str(0,0,buff);
    t++;
    if(t>10){
    int a;
    if(buzzer==0){
    a=0;
    }else{
    a=1;
    }
    t=0;
    Tx_Buf[0]='S';
    Tx_Buf[1]=(uint)qs_fs/100%10+0x30;
    Tx_Buf[2]=(uint)qs_fs/10%10+0x30;
    Tx_Buf[3]=(uint)qs_fs%10+0x30;
    Tx_Buf[4]=type%10+0x30;
    Tx_Buf[5]=a%10+0x30;
    Tx_Buf[6]='E';
    SendStr(Tx_Buf,7);
    }
    }
    }
    串口中断程序设计:
    void UARTInterrupt(void) interrupt 4
    { 
    ES=0; //关闭串行口中断
    if(RI) //接收到数据
    {
    Rx_buf[Rxnum]=SBUF; //接收数据进数组
    Rxnum++;
    25
    if(Rx_buf[0]=='S' && Rx_buf[4]=='E'){
    Rxnum=0;
    data1=(Rx_buf[1]%16)*100+(Rx_buf[2]%16)*10+(Rx_buf[3]%16);
    for (ss=0; ss < strlen(Rx_buf); ss++)
    Rx_buf[ss] = '\0' ; 
    }
    if(Rx_buf[Rxnum - 1] == 'E'){
    Rxnum=0;
    for (ss=0; ss < strlen(Rx_buf); ss++)
    Rx_buf[ss] = '\0' ; 
    }
    if(Rxnum>=6){ 
    Rxnum=0;
    for (ss=0; ss < strlen(Rx_buf); ss++)
    Rx_buf[ss] = '\0' ; 
    }
    }
    RI=0;
    ES=1; //启动串行口中断
    }
    风速风向检测程序设计:
    unsigned char adc0832(unsigned char CH)
    {
    unsigned char dat = 0x00; //AD值
    unsigned char i,test,adval;
    adval = 0x00;
    test = 0x00;
    Clk = 0; //初始化
    DATI = 1;
    _nop_();
    CS = 0;
    _nop_();
    Clk = 1;
    _nop_();
    if ( CH == 0x00 ) //通道选择
    {
    Clk = 0;
    DATI = 1; //通道0的第一位
    _nop_();
    Clk = 1;
    _nop_();
    Clk = 0;
    DATI = 0; //通道0的第二位
    _nop_();
    Clk = 1;
    _nop_();
    } 
    26
    else
    {
    Clk = 0;
    DATI = 1; //通道1的第一位
    _nop_();
    Clk = 1;
    _nop_();
    Clk = 0;
    DATI = 1; //通道1的第二位
    _nop_();
    Clk = 1;
    _nop_();
    }
    Clk = 0;
    DATI = 1;
    for( i = 0;i < 8;i++ ) //读取前8位的值
    {
    _nop_();
    adval <<= 1;
    Clk = 1;
    _nop_();
    Clk = 0;
    if (DATO)
    adval |= 0x01;
    else
    adval |= 0x00;
    }
    for (i = 0; i < 8; i++) //读取后8位的值
    {
    test >>= 1;
    if (DATO)
    test |= 0x80;
    else 
    test |= 0x00;
    _nop_();
    Clk = 1;
    _nop_();
    Clk = 0;
    }
    if (adval == test) //比较前8位与后8位的值,如果不相同舍去。若一直出现显示为零,请将该行去掉
    dat = test;
    _nop_();
    CS = 1; //释放ADC0832
    DATO = 1;
    Clk = 1;
    return dat;
    }
    
    显示程序设计:
    void OLED_P8x16Str(unsigned char x, y,unsigned char ch[])
    {
    unsigned char c=0,i=0,j=0;
    while (ch[j]!='\0')
    {
    c =ch[j]-32;
    if(x>120){x=0;y++;}
    OLED_Set_Pos(x,y);
    for(i=0;i<8;i++)
    OLED_WrDat(F8X16[c*16+i]);
    OLED_Set_Pos(x,y+1);
    for(i=0;i<8;i++)
    OLED_WrDat(F8X16[c*16+i+8]);
    x+=8;
    j++;
    }
    }
    风速程序
    PCF8591 AD转化器读程序 
    #include "i2c.h"
    #include "delay.h"
    #define _Nop() _nop_() //定义空指令
    #define AddWr 0x90 //写数据地址 
    #define AddRd 0x91 //读数据地址 
    bit ack; //应答标志位
    sbit SDA=P2^1;
    sbit SCL=P2^0;
    /*------------------------------------------------
    启动总线
    ------------------------------------------------*/
    void Start_I2c()
    {
    SDA=1; //发送起始条件的数据信号
    _Nop();
    SCL=1;
    15
    _Nop(); //起始条件建立时间大于4.7us,延时
    _Nop();
    _Nop();
    _Nop();
    _Nop(); 
    SDA=0; //发送起始信号
    _Nop(); //起始条件锁定时间大于4ì
    _Nop();
    _Nop();
    _Nop();
    _Nop(); 
    SCL=0; //钳住I2C总线,准备发送或接收数据
    _Nop();
    _Nop();
    }
    /*------------------------------------------------
    结束总线
    ------------------------------------------------*/
    void Stop_I2c()
    {
    SDA=0; //发送结束条件的数据信号
    _Nop(); //发送结束条件的时钟信号
    SCL=1; //结束条件建立时间大于4us
    _Nop();
    _Nop();
    _Nop();
    _Nop();
    _Nop();
    SDA=1; //发送I2C总线结束信号
    _Nop();
    _Nop();
    _Nop();
    _Nop();
    }
    /*----------------------------------------------------------------
    字节数据写函数 
    函数原型: void SendByte(unsigned char c);
    功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对
    此状态位进行操作.(不应答或非应答都使ack=0 假) 
    发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
    ------------------------------------------------------------------*/
    void SendByte(unsigned char c)
    {
    unsigned char BitCnt;
    for(BitCnt=0;BitCnt<8;BitCnt++) //要传送的数据长度为8位
    {
    if((c<<BitCnt)&0x80)SDA=1; //判断发送位
    else SDA=0; 
    _Nop();
    SCL=1; //置时钟线为高,通知被控器开始接收数据位
    _Nop(); 
    _Nop(); //保证时钟高电平周期大于4ì
    _Nop();
    _Nop();
    _Nop(); 
    SCL=0; 
    }
    _Nop();
    _Nop();
    SDA=1; //8位发送完后释放数据线,准备接收应答位
    _Nop();
    _Nop(); 
    SCL=1;
    _Nop();
    _Nop();
    _Nop();
    if(SDA==1)ack=0; 
    else ack=1; //判断是否接收到应答信号
    SCL=0;
    _Nop();
    _Nop();
    }
    /*----------------------------------------------------------------
    字节数据读函数 
    函数原型: unsigned char RcvByte();
    功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号),
    发完后请用应答函数。 
    ------------------------------------------------------------------*/
    unsigned char RcvByte()
    {
    unsigned char retc;
    unsigned char BitCnt;
    retc=0; 
    SDA=1; //置数据线为输入方式
    for(BitCnt=0;BitCnt<8;BitCnt++)
    {
    _Nop(); 
    SCL=0; //置时钟线为低,准备接收数据位
    _Nop();
    _Nop(); //时钟低电平周期大于4.7us
    _Nop();
    _Nop();
    _Nop();
    17
    SCL=1; //置时钟线为高使数据线上数据有效
    _Nop();
    _Nop();
    retc=retc<<1;
    if(SDA==1)retc=retc+1; //读数据位,接收的数据位放入retc中
    _Nop();
    _Nop(); 
    }
    SCL=0; 
    _Nop();
    _Nop();
    return(retc);
    }
    /*----------------------------------------------------------------
    应答子函数
    ----------------------------------------------------------------*/
    void Ack_I2c(void)
    {
    SDA=0; 
    _Nop();
    _Nop();
    _Nop(); 
    SCL=1;
    _Nop();
    _Nop(); //时钟低电平周期大于4ì
    _Nop();
    _Nop();
    _Nop(); 
    SCL=0; //清时钟线,钳住I2C总线以便继续接收
    _Nop();
    _Nop(); 
    }
    /*----------------------------------------------------------------
    非应答子函数
    ----------------------------------------------------------------*/
    void NoAck_I2c(void)
    {
    SDA=1;
    _Nop();
    _Nop();
    _Nop(); 
    SCL=1;
    _Nop();
    _Nop(); //时钟低电平周期大于4ì
    _Nop();
    _Nop();
    _Nop(); 
    18
    SCL=0; //清时钟线,钳住I2C总线以便继续接收
    _Nop();
    _Nop(); 
    }
    /*----------------------------------------------------------------
    向有子地址器件读取多字节数据函数
    函数原型: bit ISendStr(unsigned char sla,unsigned char suba,ucahr *s,unsigned char no); 
    功能: 从启动总线到发送地址,子地址,读数据,结束总线的全过程,从器件
    地址sla,子地址suba,读出的内容放入s指向的存储区,读no个字节。
    如果返回1表示操作成功,否则操作有误。
    注意: 使用前必须已结束总线。
    ----------------------------------------------------------------*/
    bit IRcvStr(unsigned char sla,unsigned char suba,unsigned char *s,unsigned char no)
    {
    unsigned char i;
    Start_I2c(); //启动总线
    SendByte(sla); //发送器件地址
    if(ack==0)return(0);
    SendByte(suba); //发送器件子地址
    if(ack==0)return(0);
    Start_I2c();
    SendByte(sla+1);
    if(ack==0)return(0);
    for(i=0;i<no-1;i++)
    { 
    *s=RcvByte(); //发送数据
    Ack_I2c(); //发送就答位 
    s++;
    } 
    *s=RcvByte();
    NoAck_I2c(); //发送非应位
    Stop_I2c(); //结束总线
    return(1);
    }
    /*------------------------------------------------
    读AD转值程序
    输入参数 Chl 表示需要转换的通道,范围从0-3
    返回值范围0-255
    ------------------------------------------------*/
    unsigned char ReadADC(unsigned char Chl)
    {
    unsigned char Val;
    Start_I2c(); //启动总线
    SendByte(AddWr); //发送器件地址
    if(ack==0)return(0);
    SendByte(0x40|Chl); //发送器件子地址
    if(ack==0)return(0);
    19
    Start_I2c();
    SendByte(AddWr+1);
    if(ack==0)return(0);
    Val=RcvByte();
    NoAck_I2c(); //发送非应位
    Stop_I2c(); //结束总线
    return(Val);
    }
    风向程序
    格雷码转化二进制码程序部分:
    Unsigned int Wind_Drct=0 ; //二进制码初始化
    Unsigned int Wind_Gray=0; //格雷码初始化
    if (KBA1==1) 
    Wind_Gray+=0x0001;
    if (KBA2==1)
    Wind_Gray+=0x0002;
    If (KBA3==1)
    Wind_Gray+=0x0004;
    if (KBA4==1)
    Wind_Gray+=0x0008;
    Wind_Gray=Wind_Gray&0x4F;
    Wind_Drct=Wind_Gray; 
    while (Wind_Gray>>1) 
    Wind_Drct^=Wind_Gray;
    Wind_Drct=Wind_TB1[Wind_Drct]; //查表求出风向值
    显示程序
    #include<reg52.h> //包含头文件,一般情况不需要改动,头文件包含特殊功能寄存器的定义
    #include<intrins.h>
    sbit RS = P2^4; //定义端口 
    sbit RW = P2^5;
    sbit EN = P2^6;
    #define RS_CLR RS=0 
    #define RS_SET RS=1
    #define RW_CLR RW=0 
    #define RW_SET RW=1 
    #define EN_CLR EN=0
    #define EN_SET EN=1
    #define DataPort P0
    /*------------------------------------------------
    uS延时函数,含有输入参数 unsigned char t,无返回值
    unsigned char 是定义无符号字符变量,其值的范围是
    0~255 这里使用晶振12M,精确延时请使用汇编,大致延时
    长度如下 T=tx2+5 uS 
    ------------------------------------------------*/
    void DelayUs2x(unsigned char t)
    { 
    while(--t);
    20
    }
    /*------------------------------------------------
    mS延时函数,含有输入参数 unsigned char t,无返回值
    unsigned char 是定义无符号字符变量,其值的范围是
    0~255 这里使用晶振12M,精确延时请使用汇编
    ------------------------------------------------*/
    void DelayMs(unsigned char t)
    { 
    while(t--)
    {
    //大致延时1mS
    DelayUs2x(245);
    DelayUs2x(245);
    }
    }
    /*------------------------------------------------
    判忙函数
    ------------------------------------------------*/
    bit LCD_Check_Busy(void) 
    { 
    DataPort= 0xFF; 
    RS_CLR; 
    RW_SET; 
    EN_CLR; 
    _nop_(); 
    EN_SET;
    return (bit)(DataPort & 0x80);
    }
    
    
    

    五、程序截图


    六、论文目录

    目 录

    摘要…………………………………………………………Ⅰ
    Abstract…………………………………………………………Ⅱ
    1绪论……………………………………………………………… 1
    1.1课题研究背景及意义…………………………………………2
    1.2国内外研究现状………………………………………………2
    1.3本文主要内容…………………………………………………2
    2系统方案设计……………………………………………………4
    2.1系统总体方案设计…………………………………………2
    2.2控制器方案选择………………………………………………2
    2.3风速测量模块和其他模块……………………………………2
    2.4本章小结………………………………………………………2
    3硬件电路设计……………………………………………………4
    3.1单片机最小系统设计…………………………………………2
    3.2风速风向电路设计……………………………………………2
    3.3其他模块电路设计……………………………………………2
    3.4本章小结………………………………………………………2
    4软件设计…………………………………………………………4
    4.1软件设计思路…………………………………………………2
    4.2程序设计………………………………………………………2
    4.3风速风向检测模块设计………………………………………2
    4.4显示模块………………………………………………………2
    4.5本章小结………………………………………………………2
    5系统测试…………………………………………………………4
    5.1系统测试………………………………………………………2
    5.2测试结果………………………………………………………2
    6结论………………………………………………………………60
    参考文献………………………………………………………… 61
    致谢………………………………………………………………62
    附录………………………………………………………………62

    物联沃分享整理
    物联沃-IOTWORD物联网 » 基于单片机的风速测量仪设计与制作详解

    发表评论