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设计心得】+基于launchpad的简易数字万用表

Other Parts Discussed in Thread: MSP430G2231

一:本设计基于MSP430G2231为核心控制系统,结合适量的硬件设计,配合12864液晶,制作出一个自动量程的电压,电阻,温度测试仪表。电压量程在0-11V,电阻档量程:0-100kΩ,温度适于室温测量。此表多适用于电子DIY爱好者使用。 

   二:原理 

 

 

                                                                        

 

  当被测电压VDD时,由上图可知,我们只需测出V值,即可知道VDD值。VDD=V*(R9+R10)/R10.V的值可用430ADC采集得到。

 

2. 电阻测量:   

                       

 

原理和电压测量一样当VDD已知,R10已知,V已知时。R9的阻值:R9=(VDD/V-1)*R10

3:温度测量:基于18B20。DS1820 数字温度计以位数字量的形式反映器件的温度值。

DS1820 通过一个单线接口发送或接收信息,因此在中央微处理器和DS1820 之间仅需一条连

接线(加上地线)。用于读写和温度转换的电源可以从数据线本身获得,无需外部电源。

因为每个 DS1820 都有一个独特的片序列号,所以多只DS1820 可以同时连在一根单线总线上,

这样就可以把温度传感器放在许多不同的地方。这一特性在HVAC 环境控制、探测建筑物、仪

器或机器的温度以及过程监测和控制等方面非常有用。

整体设计思路

 

 

三:电路实现

 

 

1:电阻与电压测量外围硬件电路图,电压测量时,高电位端接红表笔,低电位端接黑表笔。通过运放的电压跟随,解决了数字万用表仅使用于测量恒压源的窘境,但同时也由于运放的限制,测量电压范围变小。当测量电压低于2V时,ADC0采集的电压值有效,作为测量值,而ADC1采样无效。每次测量都是由大量程开始,通过程序判断,该量程是否合适,如量程太大,进行小量程切换。

 

2:电阻的测量是本设计的经典,不借助于模拟开关,手动开关选档。而借助于单片机IO口的上拉输出,高阻态输入完成自动选档。如当P12为高电平时,P13为高阻态时,R5与被测电阻形成通路分IO口电压,然后再经358跟随供AD采集。通过切换P12P13的输入输出状态即可实现档位切换。

2: 

   

 

18B20具有很多优秀的特性,如零待机功耗,独特的单线接口仅需一个端口引脚进行通讯,报警搜索命令识别并标志超过程序限定温度(温度报警条件)的器件。

3:电源模块

    因为用的是MSP430的LAUNCHPAD开发板,故3,5V供电电压不用单独制作。只需产生运放需要要的12V电压,本设计依赖34063的芯片的升压,由5V升到12V。

 

4:液晶显示模块

 

此设计也是经典之举,尽管抬高了此作品的造价,因430总共只有10个可操控IO口,而一般的显示模块都得用8个左右,而用12864的串行模式,同时运用MSP430的SPI通信,既方便又高效的完成了液晶显示的操作。同时采用5分钟定时息屏操作,从根本上解决了低功耗的问题,环保,节约。关于12864这里不再赘述。

 

4:按键模块

     

 

因为设计温度是一直显示,所以就只需对电阻,电压进行选择。所以运用了两个按键作为人际交互手段,而复位按键由MSP430开发板自身提供。

源码:

/*
 * main.c
 */
#include <msp430.h>

typedef struct REG{
 unsigned bit0 : 1;
 unsigned bit1 : 1;
 unsigned bit2 : 1;
 unsigned bit3 : 1;
 unsigned bit4 : 1;
 unsigned bit5 : 1;
 unsigned bit6 : 1;
 unsigned bit7 : 1;
}REG;

typedef enum STATE{
 WELCOME = 1,
 VOLTAGE,
 RESISTANCE
}STATE;

#define uchar unsigned char
#define uint  unsigned int

#define jump_ROM 0xCC
#define start 0x44
#define read_EEROM 0xBE

typedef unsigned char byte;
typedef unsigned int word;

#define N_SAMPLE 12

#define P1 ((REG *) 0x0021)

#define RS P1->bit7
#define DQ P1->bit4           //DS18B20数据口
void dispnum(unsigned int a,unsigned char x,unsigned char y) ;
void dispuserset(unsigned int a,unsigned char x,unsigned char y);
volatile STATE state = WELCOME;

word adc_value[N_SAMPLE];
unsigned char TMPH,TMPL;

void Key_Init(void)
{
 P2SEL &= ~BIT6 + ~BIT7;
 P2REN |= BIT6 + BIT7;
 P2OUT |= BIT6 + BIT7;
 P2IE |= BIT6 + BIT7;
 P2IES |= BIT6 + BIT7;
 P2IFG &= ~BIT6 + ~BIT7;
}

void TimerA0_Init(void)
{
 TACCR0 = 30000;
 TACCR1 = 20000;
 TACTL = TASSEL_2 + MC_2 + ID_3;
 TACCTL1 = CCIE;
 TACTL |= TACLR;
}

void ADC_Init(void)
{
 ADC10CTL1 = CONSEQ_2;
 ADC10CTL0 = ADC10SHT_2 + MSC + ADC10ON + ADC10IE;
 ADC10DTC1 = N_SAMPLE;
 ADC10SA = (word) adc_value;
}

void ADC_Channel_Select(word channel)
{
 ADC10CTL1 &= 0x0fff;
 ADC10CTL1 |= channel;

 ADC10AE0 &= 0x00;
 ADC10AE0 = (1 << (channel >> 12));
}


inline void SPI_Init(void)
{
 USICTL0 |= USIPE6 + USIPE5 + USIMST + USIOE;
 USICTL1 |= USICKPH + USIIE;
 USICKCTL = USIDIV_4 + USISSEL_2;
 USICTL0 &= ~USISWRST;
 __enable_interrupt();
}

void SPI_Transmit(byte data)
{
 USISRL = data;
 USICNT = 8;
// while (!(USIIFG & USICTL1));
 LPM0;
}
/********************************************************************
* 名称 : delay()
* 功能 : 延时函数
* 输入 : 无
* 输出 : 无
***********************************************************************/
void delay(unsigned int yN)
{
 unsigned int i;
 for(i=0; i<yN; i++)
      ;
}

/********************************************************************
* 名称 : Reset()
* 功能 : 复位DS18B20
* 输入 : 无
* 输出 : 无
***********************************************************************/
uchar Reset(void)
{
 uchar deceive_ready;
 DQ = 0;
 delay(29);
 DQ = 1;
 delay(3);
 deceive_ready = DQ;
 delay(25);
 return(deceive_ready);
}

/********************************************************************
* 名称 : read_bit()
* 功能 : 从DS18B20读一个位值
* 输入 : 无
* 输出 : 从DS18B20读出的一个位值
***********************************************************************/
uchar read_bit(void)
{
 uchar i;
 DQ = 0;
 DQ = 1;
 for(i=0; i<3; i++);
 return(DQ);
}

/********************************************************************
* 名称 : write_bit()
* 功能 : 向DS18B20写一位
* 输入 : bitval(要对DS18B20写入的位值)
* 输出 : 无
***********************************************************************/
void write_bit(uchar bitval)
{
DQ=0;if(bitval==1)
DQ=1;
delay(5);
DQ=1;
}

/********************************************************************
* 名称 : read_byte()
* 功能 : 从DS18B20读一个字节
* 输入 : 无
* 输出 : 从DS18B20读到的值
***********************************************************************/
uchar read_byte(void)
{
 uchar i,m,receive_data;
 m = 1;
 receive_data = 0;
 for(i=0; i<8; i++)
 {
  if(read_bit())
  {
   receive_data = receive_data + (m << i);
  }
  delay(6);
 }
 return(receive_data);
}

/********************************************************************
* 名称 : write_byte()
* 功能 : 向DS18B20写一个字节
* 输入 : val(要对DS18B20写入的命令值)
* 输出 : 无
***********************************************************************/
void write_byte(uchar val)
{
 uchar i,temp;
 for(i=0; i<8; i++)
 {
  temp = val >> i;
  temp = temp & 0x01;
  write_bit(temp);
  delay(5);
 }
}


/********************************************************************
* 名称 : GETDATA()
* 功能 : 获取数据
* 输入 : 无
* 输出 : 温度值,整数
***********************************************************************/
unsigned int GETDATA()
{
 unsigned int temp;
  DQ = 0;
  Reset();
  write_byte(jump_ROM);
  write_byte(start);
  Reset();
  write_byte(jump_ROM);
  write_byte(read_EEROM);
  TMPL = read_byte();
  TMPH = read_byte();
  temp = TMPL>>4 + 4<<TMPH;
  return temp;
}
void LCD_Write_Cmd(byte com)
{
 RS = 1;
 SPI_Transmit(0xf8);
 SPI_Transmit(0xf0 & com);
 SPI_Transmit(0xf0 & (com << 4));
 RS = 0;
}

void LCD_Write_Dat(byte dat)
{
 RS = 1;
 SPI_Transmit(0xfa);
 SPI_Transmit(0xf0 & dat);
 SPI_Transmit(0xf0 & (dat << 4));
 RS = 0;
}

void LCD_Pos(byte x, byte y)
{
 byte pos;
 switch(x)
 {
 case 0:
  x = 0x80;
  break;
 case 1:
  x = 0x90;
  break;
 case 2:
  x = 0x88;
  break;
 case 3:
  x = 0x98;
  break;
 default:
  break;
 }
 pos = x + y;
 LCD_Write_Cmd(pos);
}


void LCD_Init(void)
{
 LCD_Write_Cmd(0x30);
 LCD_Write_Cmd(0x0c);
 LCD_Write_Cmd(0x01);
 LCD_Write_Cmd(0x02);
 LCD_Write_Cmd(0x80);
}

void LCD_Write_Str(byte x, byte y, char *str)
{
 LCD_Pos(x, y);
 while (*str)
 {
  LCD_Write_Dat(*str++);
 }
}

void LCD_Clear_Screen(void)
{
 LCD_Write_Cmd(0x34);
 LCD_Write_Cmd(0x30);
 LCD_Write_Cmd(0x01);
}

void Disp_Welcome_Msg(void)
{
 LCD_Write_Str(0, 1, "自动量程仪表");
 LCD_Write_Str(1, 0, "温度:   °C");

 LCD_Write_Str(2, 0, "1:电阻  2:电压");
 LCD_Write_Str(3, 0, "    欢迎使用");
}

word filter(void)
{
 word ad_val;
 int i;

 for (i = 0; i < N_SAMPLE; i++)
 {
  ad_val += adc_value[i];
 }
 ad_val /= N_SAMPLE;

 return ad_val;
}

 void Measure_Voltage(void)
{
 word ad_val = 0;
 byte flag=0;
 LCD_Write_Str(2, 0, "                ");
 LCD_Write_Str(2, 0, "电压:");
 LCD_Write_Str(3, 0, "                ");

 ADC_Channel_Select(INCH_1);

 ADC10CTL0 &= ~ENC;
 while (ADC10CTL1 & BUSY); // Wait if ADC core is active
 ADC10CTL0 |= ENC + ADC10SC;  // Sampling and conversion start
 LPM0;

 ad_val = filter();
 if (ad_val > 150)
 {
  flag=1;
 }
 else
 {
  flag=0;
  ADC_Channel_Select(INCH_2);
  ADC10CTL0 |= ADC10SC;
  LPM0;
  ad_val = filter();
 }
 if(flag==1)
 {
  ad_val=ad_val*1.05;
  dispuserset(ad_val,3,2);
 }
 else
 {
  ad_val=ad_val*3.60;
  dispuserset(ad_val,3,2);
 }
 LCD_Write_Str(3, 4, "V");
}

inline void Measure_Resistance(void)
{
 byte flag=0;
 word ad_val,temp_adc;
 LCD_Write_Str(2, 0, "                ");
 LCD_Write_Str(2, 0, "电阻:");
 LCD_Write_Str(3, 0, "                ");
 P1DIR |= BIT2;
 P1DIR &= ~BIT3;
 P1OUT |= BIT2;

 ADC_Channel_Select(INCH_0);
 ADC10CTL0 |= ENC + ADC10SC;  // Sampling and conversion start
 LPM0;

 ad_val = filter();

 if (ad_val > 150)
  {
   flag=1;
  }
  else
  {
   flag=0;
   P1DIR |= BIT3;
   P1DIR &= ~BIT2;
   P1OUT |= BIT3;
   ADC_Channel_Select(INCH_0);
   ADC10CTL0 |= ENC + ADC10SC;  // Sampling and conversion start
   LPM0;

   ad_val = filter();
  }
 if(flag==1)
 {
   temp_adc=1024-ad_val;  //(带小数显示)
   temp_adc=ad_val*10/temp_adc;
   temp_adc=50*temp_adc;
   dispuserset(temp_adc,3,2);
   LCD_Write_Str(3, 4, "KOH");
 }
 else
 {
   temp_adc=1024-ad_val;  //(整数显示)
   temp_adc=ad_val*10/temp_adc;
   temp_adc=100*temp_adc;
   dispnum(temp_adc,3,2);
   LCD_Write_Str(3, 4, "OH");
 }
}

void main(void) {
 WDTCTL = WDTPW + WDTHOLD;
 P1DIR |= BIT7 + BIT4;
 SPI_Init();
 LCD_Init();
 ADC_Init();
 Key_Init();
 TimerA0_Init();
 P1DIR |= BIT0;
// Disp_Welcome_Msg();
 for(;;)
 {
  switch(state)
  {
  case WELCOME:
   Disp_Welcome_Msg();
   //dispnum(GETDATA(),1,2);
   break;
  case RESISTANCE:
   Measure_Resistance();
   break;
  case VOLTAGE:
   Measure_Voltage();
   break;
  }
  LPM0;
 }
}

#pragma vector = USI_VECTOR
__interrupt void USI_ISR(void)
{
 USICTL1 &= ~USIIFG;
 LPM0_EXIT;
}

#pragma vector = TIMERA1_VECTOR
__interrupt void TIMERA1_ISR(void)
{
 switch(TAIV)
 {
 case 2:
  TACCR1 += 20000;
  LPM0_EXIT;
  break;
 }
}

#pragma vector = ADC10_VECTOR
__interrupt void ADC10_ISR(void)
{
 LPM0_EXIT;
}

#pragma vector = PORT2_VECTOR
__interrupt void PORT2_ISR(void)
{

 if (TACCTL0 & CCIFG)
 {
  if (P2IFG & BIT6)
  {
   if (state == VOLTAGE)
   {
    state = WELCOME;
   }
   else
   {
    state = VOLTAGE;
   }
  }
  if (P2IFG & BIT7)
  {
   if (state == RESISTANCE)
   {
    state = WELCOME;
   }
   else
   {
    state = RESISTANCE;
   }
  }
  TACCTL0 &= ~CCIFG;
  TACCR0 += 30000;
 }
 P2IFG &= ~BIT6 + ~BIT7;
}
/********************************************************************************/

void dispnum(unsigned int a,unsigned char x,unsigned char y)    //整数显示(4位数-9999)
{
 unsigned char flag,i=0;
 unsigned char out[5];
 out[0]=' ';

 out[1]=48+(a/1000);
 a=a%1000;

 out[2]=48+(a/100);
 a=a%100;

 out[3]=48+(a/10);
 out[4]=48+(a%10);

 flag=0;
 for(i=1;(i<4)&&(flag==0);i++)
 {
  if(out[i]==48)
  {
   out[i]=32;
  }
  else
  {
   flag=1;
  }
 }
 LCD_Pos( x, y);

 i=0;
 while (i<5)
 {
  LCD_Write_Dat(out[i]);
  i++;
 }
}
void dispuserset(unsigned int a,unsigned char x,unsigned char y)//小数显示(2位数+2位小数-99.99)
{
 unsigned char flag,i=0;
 unsigned char out[5];

 out[0]=48+(a/1000);
 a=a%1000;

 out[1]=48+(a/1000);
 a=a%100;
 out[2]='.';
 out[3]=48+(a/10);
    a=a%10;
 out[4]=48+a;

 flag=0;
 for(i=0;(i<2)&&(flag==0);i++)
 {
  if(out[i]==48)
  {
   out[i]=32;
  }
  else
  {
   flag=1;
  }
 }
 LCD_Pos( x, y);

 i=0;
 while (i<5)
 {
  LCD_Write_Dat(out[i]);
  i++;
 }
}

 

 

 

 

1. 电压测量: