This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

【MSP430 LaunchPad设计心得】水声传感网中的高效调制解调器的设计

Other Parts Discussed in Thread: MSP430F149

 

        本人河海大学常州校区计算机与信息学院学生,2009级,通信工程专业,暑假后升入大四,在实验室老师的指导下申请了一项大学生创新训练计划项目,并成功脱颖而出晋升国家级项目。在项目研究过程中,得到TI公司的支持申请下了一块MSP430的launchpad,感到非常开心,因为此项目名称为“水声传感网中的高效调制解调器的设计”,项目立足于低成本低功耗,TI公司的MSP430系列超低功耗单片机恰好符合项目需求,因此直接为本项目的研究提供了便利。经过近半年的研发,水声MODEM的初步样机已经完成并测试通过,距离可达到200m,通信速率最高2kbps,功耗最大为120mW,基本达到指标,我愿意分享自己的研究成果,以感谢TI公司一直以来对我的支持。

        水声传感网是全球网络化技术普及的产物,它是在一定的水下区域内,通过各种传感器节点获取水下信息,并对水下节点进行声学通信和组网,最终通过特定的节点,重新以无线电和有线的形式把在覆盖区域中所获取的信息纳入岸上的常规网络,并发送给观察者的水下子网。由此可见,水声传感网中的传感节点需要拥有以下两个特点:第一,由于传感节点密度较大,必须降低单个传感节点的成本以节约整个水声传感网的建设成本;第二,传感节点往往在水下需要持续数月在无人维护的情况下工作,必须尽量降低传感节点的功耗以延长水声传感网持续工作的时间。如图1所示,一个传感节点主要由六部分组成。传感器、接口电路、节点控制器、本地存储器、水声调制解调器和供电模块。其中,完成信息收发的关键是水声调制解调器,也是所有部分中功耗最大、成本最高的部分。因此,降低水声调制解调器的功耗和成本可以有效的降低水声传感节点的整体功耗和成本。


        传统的水声调制解调器体积较大、造价昂贵、功耗较高,通信距离可达数公里甚至几十公里。显然,传统的水声调制解调器不能够适应水声传感网的使用要求。为了降低水声传感网的整体成本、保证传感网长时间可靠地进行水下信息收集和传递,非常有必要研制一种具有体积小、成本低、功耗低的高效水声调制解调器。最近几年,已经有研究机构开发出了低功耗、低成本的水声调制解调器,但是这些调制解调器的数据传输速度非常慢,在水下通信时不能够支持介质访问控制层(Medium Access Control :MAC)和路由层的协议,所以能用于水下传感网的节点。在尽量低的功耗下实现尽量远的信息传送距离,就要求水声调制解调器具有很高的效率,因此,在硬件设计上需要遵循尽量以软代硬的原则,首先降低硬件的功耗和成本,在软件设计上使用尽量高效的算法,降低微处理器 (Micro Control Unit:MCU)的运算量,提高抗干扰、抗误码的能力。

         由于海洋环境条件的复杂多变,如多径干扰等时变、频变、空变随机特性,使得声音信号在海洋信道中的传递存在着强烈的畸变和涨落。同时,水下声信道的环境噪声、有限频带、传输时延大等特点又极大地降低了水声通信的有效性。水下通信系统分为非相干通信系统和相干通信系统。非相干方式抗信道起伏能力强,接收端易于解调,且算法稳定性好。相干方式带宽利用率、通信速率都较高,但是相干信号的解调需要载波精确的同步,这在恶劣的水下声信道中是不易实现的。根据水下对讲机系统通信速率低、工作环境随机且复杂的特点,本发明使用了非相干通信系统。非相干通信系统多采用FSK的调制解调方式,基于能量检测的非相干FSK方式的最大优点是通信的可靠性高。由于水声信道的混响与起伏特性,信号的相位与幅度都收到严重的畸变。而基于能量检测的FSK方式对相位的畸变是不敏感的。而且对于非相干方式解调而言,通过正交双通道的处理,随机相位抖动是可以在基带得以消除的。FSK是用不同频率的载波来传递数字消息的。2FSK方式则是用两个不同频率的载波代表数字信号中的两个电平,也就是说,将“1”和“0”这两个不同符号所对应的消息载荷在两个不同的信号频率上。

最终,水声MDOEM的设计框图如下图所示。

1、一种用于水下对讲机的水声MODEM包括:
一个用于发射和接收超声波的超声换能器,
一个用于切换换能器的发射、接收模式的电子切换开关,
用于将已调制信号的电压放大并连接电子切换开关的电压放大电器,
用于将二进制信号转化为2FSK信号的FSK信号调制器,
用于将换能器接收时的电信号放大并连接电子切换开关的信号放大器,
用于将已放大的接收信号滤波的带通滤波器,
用于将已滤波的接收信号解调的FSK信号解调器,
用于处理MODEM数据并与FSK调制器、FSK解调器相连的核心处理器,
用于与其他设备进行数据通信的串行接口。
2、超声换能器的中心频率为32kHz
3、电子切换开关使用MAX4602芯片,公共端口连接超声换能器,两个选择端口分别连接电压放大器和信号放大器。
4、电压放大器使用AD620芯片,将2FSK信号的电压进行放大,以驱动超声换能器。
5FSK信号调制器使用XR2206芯片,将核心处理器输出的二进制信号转换为2FSK信号,并连接电压放大器。
6、信号放大器使用AD620芯片,对接收到的信号进行放大,并连接带通滤波器。
7、带通滤波器使用MAX275芯片,将已放大的接收信号进行滤波,得到比较纯净的2FSK信号,并连接FSK信号解调器。
8、FSK信号解调器使用XR2211芯片,将滤波后的2FSK信号还原为二进制信号,并送入核心处理器。
9、核心处理器使用单片机MSP430F149,用于对数据进行编码解码,数据帧打包、拆包等工作,并拥有与外界通信的串行接口。
10、串行接口使用RS-232标准的串行接口,可以连接PC机对水声MODEM进行调试,也可以连接其他传感器组成水下传感节点。
具体电路图因为版权问题不便于公开,现将部分关键源代码公开,与大家分享。程序是针对温度传感节点设计的,即发送端的调制解调器连接一个DS18B20温度传感器用于测量水下温度,接收端的调制解调器连接一个12864液晶屏用于显示水下温度。
发送端程序:
 
main.c
#include <msp430x14x.h>
#include "DS18B20.h"
#include "delay.h"
#include "fsk_send.h"
//#include "cry12864.h"

#define   uint  unsigned int
#define   uchar unsigned char
#define   PWM   BIT3//BIT2

uint first=0,scound=0,third=0,forth=0,fifth=0,sixth=0,seventh=0,eighth=0;//数字传输
uint temp,temp1,temp2;//温度
uchar m=0;//次数
uchar FLAGled7=0,FLAGled6=0,FLAGled=0,FLAGledcount=0,FLAGled5count=0;
uint time[2]={0};         //上升沿
uint period;

/*************************************************
初始化
*************************************************/
/**********clk*********/
void int_clk()
{
    uchar i;
    //P5DIR |= 0x10;                            // P5.4= output direction
    //P5SEL |= 0x10;                            // P5.4= MCLK 为外围模块作用MCLK信号输出
    BCSCTL1&=~XT2OFF;  //打开XT振荡器     
    do
{
        IFG1 &= ~OFIFG;                 //清除振荡错误标志 
        for(i = 0; i < 100; i++)
 _NOP(); //延时等待
        }
    while ((IFG1 & OFIFG) != 0); //如果标志为1继续循环等待
    //IFG1&=~OFIFG;
    BCSCTL2|=SELM1+SELS+DIVS_3;//MCLK 8M and SMCLK 1M 
}

/**********PWM*********/
void int_pwm()
  P1SEL|=PWM;//选择P12作为PWM输出
  P1SEL|=BIT2;
  P1DIR|=PWM;//方向为输出 
  P1DIR|=BIT2;//方向为输出 
  TACCR0=60000;//243频率32768左右(800 10k)(246 32.4k)
  TACCR1=40000;//122占空比1/2(400)(122)//
  TACCR2=40000;//122占空比1/2(400)(122)//
  TACCTL2=OUTMOD_7; //输出模式选择//
  TACTL|=TASSEL_2+MC_1; //up模式+时钟
}

/**********UART1*********/
void int_uart1(void)
{
  U1CTL|=SWRST;//复位串口
  U1CTL|=CHAR; //8位数据
  U1TCTL|=SSEL1;//select SMCLK AS CLK
  U1BR0=0X09;//0x68
  U1BR1=0X00;
  UMCTL1=0X08;//115200
  ME2|=UTXE1+URXE1;//使能接收和发送 
  U1CTL&=~SWRST;//清除串口复位信号
  IE2|=URXIE1;//使能接收中断
  
  P3SEL|=BIT6;
  P3SEL|=BIT7;//选择I/O口使用扩展功能和方向
  P3DIR|=BIT6;  //3.4发送、3.5接收
}

/**********捕获*********/
void _cap()
{
  P1SEL=0X04;                          //选择P1.2作为捕捉的输入端子
  TACTL |=TASSEL_2 + MC_2 + TAIE + TACLR;// + ID_3 ;               //选择SMCLK时钟作为计数脉冲
  TACCTL1 |=CM_1 + SCS + CAP + CCIE + CCIS_0;  //上升沿触发捕捉,同步模式、使能中断、CCIxA为输入端
}

/*************************************************
功能函数
*************************************************/
/**********延时*********/
void delay() 
{
  unsigned int j;
  for(j=5000;j>0;j--);
}


/**********温度转换*********/
void temperature(void)
{
  if(temp&0x8000)//<0°C
  {
    temp=(~temp)+0x0001;
    temp1=temp>>4;//整数部分
    temp2=temp&0x000f;//小数部分
  }
  else//>0°C
  {
    temp1=temp>>4;//整数部分
    temp2=temp&0x000f;//小数部分
  }
  
  temp2=temp2*625;//小数换算
  first=temp2/1000;temp2=temp2%1000;
  scound=temp2/100;temp2=temp2%100;
  third=temp2/10;temp2=temp2%10;
  forth=temp2;
  
  fifth=temp1/100;temp1=temp1%100;//整数换算
  sixth=temp1/10;temp1=temp1%10;
  seventh=temp1;
}

/*************************************************
main
*************************************************/
void main()
{
  //WDTCTL=WDTPW+WDTHOLD;//关看门狗
  WDTCTL = WDT_MDLY_32;
  IE1 |= WDTIE;
  
  int_clk();  //初始化时钟
  //int_pwm();  //初始化PWM
  int_uart1();
  //_cap();             //初始化CAP
  
  TAR;//测试
  P3DIR|=BIT0;//测试口p10output
  P3DIR|=BIT1;//测试口p11output
  P3OUT&=~BIT0;//测试口p10~0
  P3OUT&=~BIT1;//测试口p11~0
  
  P6DIR |= BIT7;//18b20
  P6OUT |= BIT7;//18b20
  
  P2DIR |= 0xFE; //其余的LED指示灯全部开启
  P2OUT |= 0xFE; //熄灭全部LED
  
  
  P2OUT=0x00;
  delay_ms(500); 
  P2OUT=0xFF;
  delay_ms(500);
  P2OUT=0x00;
  delay_ms(500); 
  P2OUT=0xFF;
  delay_ms(500);
  P2OUT=0x00;
  delay_ms(500); 
  P2OUT=0xFF;
  delay_ms(500);
  P2OUT=0x00;
  delay_ms(500); 
  P2OUT=0xFF;
  delay_ms(500);
  P2OUT=0x00;
  delay_ms(500); 
  P2OUT=0xFF;
 // delay_ms(500);
  
  
  
  _EINT();            //开总中断
  
  //P1SEL&=~PWM;//pwm停止
  //P1SEL|=PWM;//pwm开始
  
  while(1)
  {
    //send0();
    //send1();
    send_in(Do1Convert());
    if(FLAGled7==0) {P2OUT &=~BIT7;FLAGled7=1;}
               else {P2OUT |= BIT7;FLAGled7=0;}
   
    //send_in(0x00ff);
   
  //  delay_ms(500);

    //send_in(0xe0e0);
    //delay_ms(500);
    //temp=Do1Convert();
    //temperature();
  }//结束
}


/*************************************************
中断函数
*************************************************/
/**********UART1*********/
#pragma vector=UART1RX_VECTOR
__interrupt  void UART1_RX_ISR(void)
  /*while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束                    //温度传输
  U1TXBUF=fifth+48; //读取接受到的数据
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=sixth+48; //读取接受到的数据
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=seventh+48; //读取接受到的数据
  
  
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF='.'; //读取接受到的数据
  
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=first+48; //读取接受到的数据
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=scound+48; //读取接受到的数据
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=third+48; //读取接受到的数据
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=forth+48; //读取接受到的数据*/
  
  if(period>2000 && period<3000)                                        //脉宽捕获
  {
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF='0'; //读取接受到的数据
  }
  else if(period>4500 && period<5500)
  {
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF='1'; //读取接受到的数据
  }
  else
  {
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF='e'; //读取接受到的数据
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF='r'; //读取接受到的数据
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF='r'; //读取接受到的数据
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF='o'; //读取接受到的数据
  }
}

/**********capA*********/
#pragma vector=TIMERA1_VECTOR
__interrupt void Timer_A(void)
{
  switch(TAIV) //==向量查询==

  { case 2://==捕获中断==
    
    //if((TACTL&~TAIFG)==1)
    //TACTL |= TACLR;
    if(m==1)
    {
      time[m]=TACCR1;
      m=0;
      period = time[1]-time[0];
      TACCTL1 &=~CM_2;
      TACCTL1 |=CM_1;//上升沿
    }
    else if(m==0)
    {
      time[m]=TACCR1;
      m++;
      TACCTL1 &=~CM_1;
      TACCTL1 |=CM_2;//下降沿
    }
    break;
    //case 10:
    //  ***=1;
    // break;
  default:
      break;  
  }
}


#pragma vector=WDT_VECTOR
__interrupt void watchdog_timer(void)
 if(FLAGled5count==3)
 {
  FLAGled5count=0;
  if(FLAGled6==0) {P2OUT &=~BIT6;FLAGled6=1;}
             else {P2OUT |= BIT6;FLAGled6=0;}
 }
 if(FLAGledcount==6)
 {
  FLAGledcount=0;
  if(FLAGled==0) {P2OUT &=~BIT2;P2OUT |= BIT4;FLAGled=1;}
            else {P2OUT |= BIT2;P2OUT &=~BIT4;FLAGled=0;}
 }
 
 FLAGledcount++;
 FLAGled5count++;
 
}
 
fsk_send.c
#include <msp430x14x.h>
#include "delay.h"
 
//延时长短
#define   del  2500//300//2500//333//2500//频率133.33
 
//2.0收发总线
#define   LineIN  P2DIR&=~BIT0
#define   LineOUT  P2DIR|=BIT0
#define   Line0  P2OUT&=~BIT0
#define   Line1  P2OUT|=BIT0
 
/**********发送0*********/
void send0()//p10
{
  Line1;
  delay_us(del);
  Line0;
  delay_us(del*2);
}
 
/**********发送1*********/
void send1()//p11
{
  Line1;
  delay_us(del*2);
  Line0;
  delay_us(del);
}
 
/**********起始信号*********/
void start_sign()
{
  Line1;
  delay_us(del*4);
  Line0;
  delay_us(del*4);
}
 
/**********终止信号*********/
void stop_sign()
{
  Line1;
  delay_us(del*6);
  Line0;
  delay_us(del);
}
 
/**********发送一个字节*********/
void send_cha(unsigned char data)
{
 
    unsigned char i;
    unsigned char sure=0;//校验
 
    _DINT();//关中断
 
    LineOUT;//设为输出
 
    Line0;//先拉低
    delay_us(10);//等待
    start_sign();//起始信号
    for(i = 0; i < 8;i++)
    {
        if(data & 0X01)    send1();
        else               send0();
        sure^=(data & 0x01);//奇偶校验
        data=data>>1;
    }
 
    //if(sure)    send1();//发送奇偶校验位
    //else               send0();
 
    stop_sign();//终止信号
 
    _EINT();//开中断
}
 
/**********发送一个字*********/
void send_in(unsigned int data)
{
    unsigned char i;
    unsigned char sure=0;//校验
 
    _DINT();//关中断
 
    LineOUT;//设为输出
 
    Line0;//先拉低
    delay_us(10);//等待
    start_sign();//起始信号
    for(i = 0; i < 16;i++)
    {
        if(data & 0X0001)    send1();
        else               send0();
        sure^=(data & 0x0001);//奇偶校验
        data=data>>1;
    }
 
    //if(sure)    send1();//发送奇偶校验位
    //else               send0();
 
    stop_sign();//终止信号
 
    _EINT();//开中断
}
 
/**********发送串*********/
void send_s(unsigned char data[],unsigned char n)//没调好
{
    unsigned char i,j;
    unsigned char sure=0;//校验
 
    _DINT();//关中断
 
    LineOUT;//设为输出
 
    Line0;//先拉低
    delay_us(10);//等待
    start_sign();//起始信号
    for(j=0;j<n;j++)
    {
      for(i = 0; i < 8;i++)
      {
        if(data[n-j-1] & 0X01)    send1();
        else               send0();
        sure^=(data[n-j-1] & 0x01);//奇偶校验
        data[n-j-1]=data[n-j-1]>>1;  
      }
    }
    if(sure)    send1();//发送奇偶校验位
    else               send0();
 
    stop_sign();//终止信号
 
    _EINT();//开中断
}
 
 
 
 
接收端程序:
 
main.c
#include <msp430x14x.h>
#include "DS18B20.h"
#include "delay.h"
#include "fsk_receive.h"
#include "cry12864.h"
#define   uint  unsigned int
#define   uchar unsigned char
#define   PWM   BIT3//BIT2
 
uint first=0,scound=0,third=0,forth=0,fifth=0,sixth=0,seventh=0,eighth=0;//数字传输
uint temp,temp1,temp2;//温度
uchar m=0,j=0,i=0;//次数
uchar FLAGled7=0,FLAGled6=0,FLAGled=0,FLAGledcount=0,FLAGled5count=0;
uint time[2]={0};         //上升沿
uint period;//时间间隔
uchar pulse_flag;//开始标志
uchar _sure;//奇偶校验标志
uchar Sum;//电平读取计数
uchar seat_in;//字接收比特位置
uchar uart_begin;
uint receive_ram[10]; //接收缓冲
uchar tishi0[]={"接收端初始化...."};
uchar tishi1[]={"等待接收数据...."};
uchar tishi2[]={"水下温度      ℃"};
uchar tishi3[]={"传感节点工作正常"};
uint wendu[6];
 
 
/*************************************************
初始化
*************************************************/
/**********clk*********/
void int_clk()
{
    uchar i;
    //P5DIR |= 0x10;                            // P5.4= output direction
    //P5SEL |= 0x10;                            // P5.4= MCLK 为外围模块作用MCLK信号输出
    BCSCTL1&=~XT2OFF;  //打开XT振荡器     
    do
{
        IFG1 &= ~OFIFG;                 //清除振荡错误标志 
        for(i = 0; i < 100; i++)
 _NOP(); //延时等待
        }
    while ((IFG1 & OFIFG) != 0); //如果标志为1继续循环等待
    //IFG1&=~OFIFG;
    BCSCTL2|=SELM1+SELS+DIVS_3;//MCLK 8M and SMCLK 1M 
}
 
/**********PWM*********/
void int_pwm()
  P1SEL|=PWM;//选择P12作为PWM输出
  P1SEL|=BIT2;
  P1DIR|=PWM;//方向为输出 
  P1DIR|=BIT2;//方向为输出 
  TACCR0=60000;//243频率32768左右(800 10k)(246 32.4k)
  TACCR1=40000;//122占空比1/2(400)(122)//
  TACCR2=40000;//122占空比1/2(400)(122)//
  TACCTL2=OUTMOD_7; //输出模式选择//
  TACTL|=TASSEL_2+MC_1; //up模式+时钟
}
 
/**********timeb*********/
void int_timeB(void)
{
  TBCCTL0 = CCIE;                           // CCR0 interrupt enabled
  TBCCR0 = 50000;
  TBCTL = TBSSEL_2 + MC_2;                  // SMCLK, contmode
}
 
/**********UART1*********/
void int_uart1(void)
{
  U1CTL|=SWRST;//复位串口
  U1CTL|=CHAR; //8位数据
  U1TCTL|=SSEL1;//select SMCLK AS CLK
  U1BR0=0x09;//0X69;//0x68
  U1BR1=0x00;//0X03;
  UMCTL1=0x08;//0Xff;//1200//115200
  ME2|=UTXE1+URXE1;//使能接收和发送 
  U1CTL&=~SWRST;//清除串口复位信号
  IE2|=URXIE1;//使能接收中断
 
  P3SEL|=BIT6;
  P3SEL|=BIT7;//选择I/O口使用扩展功能和方向
  P3DIR|=BIT6;  //3.4发送、3.5接收
}
 
/**********捕获*********/
void _cap()
{
  P1SEL=0X04;                          //选择P1.2作为捕捉的输入端子
  TACTL |=TASSEL_2 + MC_2 + TAIE + TACLR;// + ID_3 ;               //选择SMCLK时钟作为计数脉冲
  TACCTL1 |=CM_1 + SCS + CAP + CCIE + CCIS_0;  //上升沿触发捕捉,同步模式、使能中断、CCIxA为输入端
}
 
/*************************************************
功能函数
*************************************************/
/**********延时*********/
void delay() 
{
  unsigned int j;
  for(j=5000;j>0;j--);
}
 
/**********发送0*********/
void send0()//p10
{
  P3OUT|=BIT0;
  delay_us(2500);//delay();
  P3OUT&=~BIT0;
  delay_us(5000);//delay();delay();
}
 
/**********发送1*********/
void send1()//p11
{
  P3OUT|=BIT1;
  delay_us(5000);//delay();delay();
  P3OUT&=~BIT1;
  delay_us(2500);//delay();
}
 
/**********温度转换*********/
void temperature(void)
{
  if(temp&0x8000)//<0°C
  {
    temp=(~temp)+0x0001;
    temp1=temp>>4;//整数部分
    temp2=temp&0x000f;//小数部分
  }
  else//>0°C
  {
    temp1=temp>>4;//整数部分
    temp2=temp&0x000f;//小数部分
  }
 
  temp2=temp2*625;//小数换算
  first=temp2/1000;temp2=temp2%1000;
  scound=temp2/100;temp2=temp2%100;
  third=temp2/10;temp2=temp2%10;
  forth=temp2;
 
  fifth=temp1/100;temp1=temp1%100;//整数换算
  sixth=temp1/10;temp1=temp1%10;
  seventh=temp1;
 
 
 
 
  //温度发送
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束                    //温度传输
  U1TXBUF=fifth+48; //读取接受到的数据
  wendu[0]=':';
 
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=sixth+48; //读取接受到的数据
  wendu[1]=sixth+48;
 
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=seventh+48; //读取接受到的数据
  wendu[2]=seventh+48;
 
 
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF='.'; //读取接受到的数据
  wendu[3]='.';
 
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=first+48; //读取接受到的数据
  wendu[4]=first+48;
 
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=scound+48; //读取接受到的数据
  wendu[5]=scound+48;
 
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=third+48; //读取接受到的数据
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=forth+48; //读取接受到的数据
 
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=' '; //读取接受到的数据
}
 
/*************************************************
main
*************************************************/
void main()
{
 // WDTCTL=WDTPW+WDTHOLD;//关看门狗
  WDTCTL = WDT_MDLY_32;
  IE1 |= WDTIE;
 
  int_clk();  //初始化时钟
  //int_pwm();  //初始化PWM
  int_uart1();
  //_cap();             //初始化CAP
  int_timeB();//开定时器b,做为电平采集
 
  TAR;//测试
  P3DIR|=BIT0;//测试口p10output
  P3DIR|=BIT1;//测试口p11output
  P3OUT&=~BIT0;//测试口p10~0
  P3OUT&=~BIT1;//测试口p11~0
 
  P6DIR |= BIT7;//18b20
  P6OUT |= BIT7;//18b20
 
  P5DIR|=BIT0;
  P5OUT|=BIT0;
 
 
  P2DIR |= 0xFF; //其余的LED指示灯全部开启
  P2OUT |= 0xFF; //熄灭全部LED
 
 
 
  //P1SEL&=~PWM;//pwm停止
  //P1SEL|=PWM;//pwm开始
  Init_Lcd12864();
  Lcd_ClearGDRAM();
 
  Lcd_DispHZ(0x80,tishi0,8);  //80 90 88 98
 
   P2OUT=0x00;
  delay_ms(500); 
  P2OUT=0xFF;
  delay_ms(500);
  P2OUT=0x00;
  delay_ms(500); 
  P2OUT=0xFF;
  delay_ms(500);
  P2OUT=0x00;
  delay_ms(500); 
  P2OUT=0xFF;
  Lcd_WriteCmd(0x80); for(j=0;j<16;j++) Lcd_WriteData(0x20);//清除本行
  Lcd_DispHZ(0x80,tishi1,8);  //80 90 88 98
  delay_ms(500);
  P2OUT=0x00;
  delay_ms(500); 
  P2OUT=0xFF;
  delay_ms(500);
  P2OUT=0x00;
  delay_ms(500); 
  P2OUT=0xFF;
  Lcd_WriteCmd(0x80); for(j=0;j<16;j++) Lcd_WriteData(0x20);//清除本行
  Lcd_DispHZ(0x80,tishi3,8);  //80 90 88 98
  Lcd_DispHZ(0x88,tishi2,8);  //80 90 88 98
 // delay_ms(500);
 
  _EINT();            //开总中断
 
 
  while(1)
  {
    //send0();
 
    //_sure=receive_val_2(receive_ram,_sure);
 
 
 
    if(_sure)//uart_begin)
    {  
      while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
      U1TXBUF=(' '); //读取接受到的数据
      temp=receive_ram[1];//赋值
      temperature();//转换发送
      while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
      U1TXBUF=('C'); //读取接受到的数据
      while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
      U1TXBUF=(';'); //读取接受到的数据
 
 
 
 
       Lcd_WriteCmd(0x8C);   //80 90 88 98
       for(i = 0;i < 6;i++)
       Lcd_WriteData(wendu[i]);    //字符
 
       if(FLAGled7==0) {P2OUT &=~BIT7;P2OUT &=~BIT0;FLAGled7=1;}
                  else {P2OUT |= BIT7;P2OUT |= BIT0;FLAGled7=0;}
 
 
 
 
 
 
      //uart_begin=0;
      _sure=0;
    }
    //temp=Do1Convert();
    //temperature();
  }//结束
}
 
 
/*************************************************
中断函数
*************************************************/
/**********UART1*********/
#pragma vector=UART1RX_VECTOR
__interrupt  void UART1_RX_ISR(void)
  /*while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束                    //温度传输
  U1TXBUF=fifth+48; //读取接受到的数据
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=sixth+48; //读取接受到的数据
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=seventh+48; //读取接受到的数据
 
 
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF='.'; //读取接受到的数据
 
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=first+48; //读取接受到的数据
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=scound+48; //读取接受到的数据
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=third+48; //读取接受到的数据
  while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
  U1TXBUF=forth+48; //读取接受到的数据*/
 
  if(period>2000 && period<3000)                                        //脉宽捕获
  {
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF='0'; //读取接受到的数据
  }
  else if(period>4500 && period<5500)
  {
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF='1'; //读取接受到的数据
  }
  else
  {
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF='e'; //读取接受到的数据
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF='r'; //读取接受到的数据
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF='r'; //读取接受到的数据
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF='o'; //读取接受到的数据
  }
}
 
/**********capA*********/
#pragma vector=TIMERA1_VECTOR
__interrupt void Timer_A(void)
{
  switch(TAIV) //==向量查询==
 
  { case 2://==捕获中断==
 
    //if((TACTL&~TAIFG)==1)
    //TACTL |= TACLR;
    if(m==1)//捕获到下降沿
    {
      time[m]=TACCR1;
      m=0;
      period = time[1]-time[0];
      TACCTL1 &=~CM_2;
      TACCTL1 |=CM_1;//上升沿
 
      /**识别脉宽**/
      //sendperiod(period);//脉宽发给串口
 
      if(pulse_distinguish(period))//起始识别
      {
         pulse_flag=1;//采集标志
 
        _DINT();
        _sure = receive_val_in(receive_ram,_sure,&seat_in);//采集
 
        if(_sure)//奇偶校验正确
        {
          if(receive_ram[0])//小偷懒把错的温度虑掉
          {
            receive_ram[1]=receive_ram[0];
          }
            uart_begin=1;//发送标志
        }
        else//错误
        {
          uart_begin=0;
        }
 
       //初始化
        _sure = 0;//清零校验位
        seat_in = 16;//重置位置值
        receive_ram[0]=0;        
        pulse_flag=0;//采集标志
 
        _EINT();
        pulse_flag=0;
      }  
    }
    else if(m==0)//捕获到上升沿
    {
      time[m]=TACCR1;
      m++;
      TACCTL1 &=~CM_1;
      TACCTL1 |=CM_2;//下降沿
    }
    break;
    //case 10:
    //  ***=1;
    // break;
  default:
      break;  
  }
}
 
/* Timer B0 interrupt service routine*/
#pragma vector=TIMERB0_VECTOR
__interrupt void Timer_B (void)
{
  period=pinline(period);//切换输入并设置采集速率
  _sure=level_distinguish(&period,_sure,receive_ram,&seat_in);//电平识别 
}
 
 
 
#pragma vector=WDT_VECTOR
__interrupt void watchdog_timer(void)
 if(FLAGled5count==3)
 {
  FLAGled5count=0;
  if(FLAGled6==0) {P2OUT &=~BIT6;FLAGled6=1;}
             else {P2OUT |= BIT6;FLAGled6=0;}
 }
 if(FLAGledcount==6)
 {
  FLAGledcount=0;
  if(FLAGled==0) {P2OUT &=~BIT2;P2OUT |= BIT4;FLAGled=1;}
            else {P2OUT |= BIT2;P2OUT &=~BIT4;FLAGled=0;}
 }
 
 FLAGledcount++;
 FLAGled5count++;
 
}
 
 
fsk_receive.c
#include <msp430x14x.h>
#include "delay.h"
 
#define  del_  2500//300//2500//333//2500
#define  range 1500//180//1500//67//500
#define  num_del 25//2794
#define  num_range 10//500
#define  rate 100//100
 
#define   Line     BIT2
 
#define   LineIO   P1SEL&=~Line
#define   LineSEL  P1SEL|=Line
#define   LineIN   P1DIR&=~Line
#define   LineOUT  P1DIR|=Line
 
 
/********脉宽判别*********/
char pulse_distinguish(unsigned int period)
{
  if(period>((del_*4)-range) && period<((del_*4)+range))//起始位    
    return 1;
  return 0;//
}
 
 
/********一个字接收赋值*********/
unsigned char receive_val_in(unsigned int receive_ram[],unsigned char _sure,unsigned char *seat_in)//接收方式1
{
  char i;
  LineIO;//设为IO
  LineIN;//设为输入
  delay_us(del_*3);//等待起始位结束
 
  for(i=0;i<*seat_in;i++)//读16位数据
  {
    delay_us(del_*2);//中点读取
    receive_ram[0]>>=1;
    if(P1IN&Line)
    {
      receive_ram[0]|=0x8000;
      _sure^=0x01;
    }
  }
 
 
  delay_us(del_);//中点读取
  if((P1IN&Line) && _sure);//读奇偶校验位
  else if(!(P1IN&Line) && !_sure)
    _sure = 0x01;
  else
    _sure = 0;
 
  LineSEL;//设置为捕获
 
  //return _sure;
  return 1;
}
 
void sendperiod(unsigned int period)//温度发给串口
{
  char a,b,c,d,e;
  a=period/10000;
  b=(period-a*10000)/1000;
  c=(period-a*10000-b*1000)/100;
  d=(period-a*10000-b*1000-c*100)/10;
  e=((period-a*10000-b*1000-c*100)%10);
  while((IFG2&UTXIFG1)==0);//                                  判断发送缓冲区是否结束
  U1TXBUF='0'+a;
  while((IFG2&UTXIFG1)==0);//                                  判断发送缓冲区是否结束
  U1TXBUF='0'+b;
  while((IFG2&UTXIFG1)==0);//                                  判断发送缓冲区是否结束
  U1TXBUF='0'+c;
  while((IFG2&UTXIFG1)==0);//                                  判断发送缓冲区是否结束
  U1TXBUF='0'+d;
  while((IFG2&UTXIFG1)==0);//                                  判断发送缓冲区是否结束
  U1TXBUF='0'+e;
  while((IFG2&UTXIFG1)==0);//                                  判断发送缓冲区是否结束
  U1TXBUF=' ';
}
 
 
unsigned char receive_val_2(unsigned int receive_ram[],unsigned char _sure)
{
  unsigned int Sum=0;
 
  /*起始位*/
  if(P1IN&Line)
  {
    Sum++;
    while(P1IN&Line)
    {
      Sum++;
    }
  }
  if((!(P1IN&Line)) && Sum)// && (num>(num_del+num_range)) &&(num>(num_del-num_range)))
  {
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF=Sum>>8; //读取接受到的数据
    while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
    U1TXBUF=Sum; //读取接受到的数据
 
    Sum=0;
  }
  return _sure;
}
 
unsigned int pinline(unsigned int period)
{
  TBCCR0 += rate;//每100微秒采一次 
  LineIN;//设为输入
  if(P1IN&Line)    //若端口为高电平
  {
    period++;
  }
  return period;
}
 
unsigned char level_distinguish(unsigned int *period,unsigned char _sure,unsigned int receive_ram[],unsigned char *seat_in)
{
  if((!(P1IN&Line)) && *period)// && (num>(num_del+num_range)) &&(num>(num_del-num_range)))
  { /*while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
      U1TXBUF=(*period)>>8; //读取接受到的数据
      while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
      U1TXBUF=(*period); //读取接受到的数据
      */
    if((*period<(num_del+num_range)) && (*period>(num_del-num_range)))//识别为0
    {
      receive_ram[0]>>=1;
      (*seat_in)--;
      //while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
      //U1TXBUF=('0'); //读取接受到的数据
    }
    else if((*period<((num_del*2)+num_range)) && (*period>((num_del*2)-num_range)))//识别为一
    {
      receive_ram[0]>>=1;
      receive_ram[0]|=0x8000;
      //_sure^=0x01;//奇偶校验
      (*seat_in)--;
      //while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
      //U1TXBUF=('1'); //读取接受到的数据
    }
    else if((*period<(num_del*4+num_range*2)) && (*period>(num_del*4-num_range*2)))//起始
    {
      (*seat_in)=16;
      receive_ram[0]=0;
    }
    else if((*period<(num_del*6+num_range*2)) && (*period>(num_del*6-num_range*2)))//终止
    {
     // while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
     // U1TXBUF=receive_ram[0]>>8; //读取接受到的数据
      //while((IFG2&UTXIFG1)==0);//判断发送缓冲区是否结束
      //U1TXBUF=receive_ram[0]; //读取接受到的数据
      receive_ram[1]=receive_ram[0];
      *period=0;
      return 1;
    }
    (*period)=0;
 
  }
 
  return 0;
}
 
 
 

 

 

 

 

最后附几张实物测试图,由于拍照时条件有限,就在一个小水盆里演示了下现象,实际通信距离可以到200m。
 

 
 

 

  • 请问传感器都是什么类型的呀?主要用来监测什么呀?

    再打听一下,这个水声调制解调器可不可以一端在水里一端在水上甚至岸边?

  • 忘了写了,有鱼之类的挡住了会有影响么?

  • 您好,我来回答您的问题:

    首先,传感器可以选用任何用于监测环境参数的传感器,比如温度传感器,PH值传感器,甚至摄像头,本项目的水声MDOEM是透明传输传感器所采集的数据的,因此只要选用的传感器能跟MODEM进行数据通信,就可以选用。本项目主要研发的是MDOEM部分,也就是数据传输部分,对于MODEM到底连接什么传感器未作具体研究,只要符合条件即可。

    下一个问题,本项目所用的超声波换能器探头原本用于水下测距,这是一种只能用于水下的超声探头,也就是说,探头接触到空气就不能使用了,因此一端在水里,另一端在空气中,是无法传送超声波的。经实验,这种超声探头只能在水里用,一到空气里一点信号都收不到。具体原因与换能器的原理有关,你可以搜一下“超声换能器”,换能器是压电陶瓷做的,不同的换能器,因使用场合不同,结构也不相同。空气里用的就只能在空气里用,水里用的就只能在水里用。

    第三个问题,如果有体型较小的鱼挡在超声探头前方,是可以继续通信的,因为此超声探头的发送区域是扇形的,大约100度的角度,因此即使正前方有物体阻隔,超声波一样可以通过绕射、反射等方式传播到接收端。但是如果体积过大并且离探头非常近,几乎堵住了,那样肯定不能通信。所以一般情况下有障碍物在前方不会影响通信。在水声通信中,通信失败或错误的原因往往是由于声波多路反射造成的多径效应,而形成码间干扰。

  • 谢谢回答,

    主要是向您随便请教一些问题,谢谢不吝回复

    请问这种超声波换能器探头能用于探鱼么?这种探头大概多少钱一个?

    如果您有比较长期使用的信箱并且现在方便的话能不能给我的信箱发个Email,我想保留一下您的信箱,也许以后想到什么问题可以向您请教,,您这里的用户信息看不到您的联系方式。我的信箱:wangfuchong@hotmail.com

    谢谢

  • 请教一个问题,您对这方面应用的可能性可能了解

    我看到有网上介绍那种超声波探鱼的,由于时间问题,我未仔细查阅,我想知道的是,这种探鱼能够探测到鱼离探头的距离,能不能探测到与探头的角度呢?能实现既探测鱼(群)的距离和大小又能探测到方向么?如果能那么实现测方向角度的大致实现原理是什么?

    能不能麻烦指点一下呀

  •        您好。本项目使用的换能器探头原本用于水下超声测距,灵敏度和功率远不及探鱼用换能器,本项目的换能器探头价格仅为20元一个,体积约为2*2*2CM。探鱼用换能器价格均在百元级别,体积也至少五倍以上。

           探鱼的原理与潜艇的声纳系统十分类似,声纳系统起到的效果与雷达差不多。雷达能看出前方单位的大小、方位、方向等,声纳系统同样可以,因此探鱼的换能器也可以。实现测量方向的原理是,换能器发出一束超声波,遇到鱼群的部分声波被反射回来,因为换能器的灵敏度高,波形反射后的变化完全可以采集到,然后交由信号处理系统进行分析,显示在屏幕上。其实说白了,就是把雷达中的电磁波替换为超声波,其原理大同小异。

           我的联系方式:248897406@qq.com,是长期使用的。