一、基本原理
对于Source-Free RC电路,其电容放电的特性可以描述为:
其中V是电容的初始电压,t是放电时间,R是串接的电阻阻值,C是电容值,v(t)是t时刻电容上的电压。因此,若已知V、R、以及t1时刻的电压V,便可求得C:
二、如何控制和测量
如上图所示,大致步骤为:1)由GPIO通过电阻R给电容C充电至Vcc;2)该GPIO输出0,电容C通过R进行放电,同时Timer开始计时、CA+开启;3)当电容电压放电至参考电压(此处是0.25Vcc)时,比较器CA+输出端出现电平变化;4)中断程序捕获这一变化,并利用Timer的capture mode获得该时刻的时间,最后通过以上方程计算电容值。
上图中R推荐采用1%精度的电阻,以提高测试精度。
三、状态转换图
四、测试代码
main.c程序:
// C meter 2015.9.26
//
// P1.5(TA0.0) --[||||]----------- P1.4(CA3)
// R=10kOhm |
// -----
// cap -----
// |
// GND
// http://zlbg.cnblogs.com
///////////////////////////////////////// #include "io430.h" #define LED1 BIT0 // P1.0, red led
#define LED2 BIT6 // P1.6, green led #define VMEAS BIT4 // P1.4(CA4) for voltage measurement of the cap
#define VCTRL BIT5 // P1.5(TA0.0) for voltage control
#define PUSH2 BIT3 // P1.3, button #define RXD BIT1 //P1.1
#define TXD BIT2 //P1.2 #define READY 0
#define CHARGING 1
#define DISCHARGING 2
#define FINISH_DC 3 #define R_SERIES 10000 //10kOhm
#define LN4 1.3863 //functions for C meter
void P1Init(void);
void TA0Init(void);
void CAInit(void);
37 void setReadyStatus(void); //functions for printf()
void sendByte(unsigned char);
void printf(char *, ...);
void initUART(void); char state = READY;
unsigned char overflowsCharging = ;
unsigned char overflowsDischarging = ;
unsigned char i = ;
float capacitance = ; // unit: nF void main(void)
{
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD; // DCO setup
BCSCTL1 = CALBC1_1MHZ; //running at 1Mhz
DCOCTL = CALDCO_1MHZ; // P1 setup
P1Init(); // Timer0 setup
TA0Init(); // CA setup
CAInit(); // UART setup
initUART(); setReadyStatus(); __enable_interrupt(); // enter LP mode
LPM0; } void P1Init(void)
{
P1OUT = ; // set P1.3 (PUSH2) as input with pullup
P1OUT |= PUSH2;
P1REN |= PUSH2; // set P1.0, P1.6, P1.5 as output
P1DIR |= LED1 + LED2 + VCTRL; // enable P1.3 interrupt
P1IES |= PUSH2; // high -> low transition
P1IFG &= ~PUSH2; // clear the flag
P1IE |= PUSH2;
} void TA0Init(void)
{
// use SMCLK (1MHz), no div, clear, halt
TA0CTL = TASSEL_2 + ID_0 + MC_0 + TACLR; // TA0CCTL0: compare mode, enable interrupt
TA0CCTL0 = CCIE; // TA0CCTL1: capture mode, no capture, CCI1B(CAOUT) input, syn capture
// interrupt enabled
TA0CCTL1 = CCIS_1 + SCS + CAP + CCIE;
} void CAInit(void)
{
//0.25 Vcc ref on V+, halt
CACTL1 = CAREF_1 + CAIES;
// input CA4 (P1.4), remove the jumper) on V-, filter on
CACTL2 = P2CA3 + CAF;
} void setReadyStatus(void)
{
state = READY;
// light led2 and turn off led1 to indicate ready
P1OUT &= ~LED1;
P1OUT |= LED2; //stop and clear timer, stop T0_A1 capture & CA+
TA0CTL = TASSEL_2 + ID_0 + MC_0 + TACLR;
TA0CCTL1 &= ~CM_3;
CACTL1 &= ~CAON; overflowsCharging = ;
} void initUART(void) {
//config P1.1 RXD, P1.2 TXD
P1SEL |= TXD + RXD;
P1SEL2 |= TXD + RXD; //reset UCA0, to be configured
UCA0CTL1 = UCSWRST;
//config
UCA0CTL1 |= UCSSEL_2; //SMCLK
UCA0BR0 = ;
UCA0BR1 = ;//1MHz baut rate = 9600 8-N-1
UCA0MCTL = UCBRS0; // Modulation UCBRSx = 1
//make UCA0 out of reset
UCA0CTL1 &= ~UCSWRST;
} void sendByte(unsigned char byte )
{
while (!(IFG2&UCA0TXIFG)); // USCI_A0 TX buffer ready?
UCA0TXBUF = byte; // TX -> RXed character
} #pragma vector = PORT1_VECTOR
__interrupt void P1_ISR(void)
{
if((P1IFG & PUSH2) == PUSH2)
{
P1IFG &= ~PUSH2; //clear the flag
switch(state)
{
case READY:
state = CHARGING;
// light LED1 and turn off LED2, indicate a busy status
P1OUT |= LED1;
P1OUT &= ~LED2;
//start timer, continuous mode
TACTL |= MC_2;
//start charging
P1OUT |= VCTRL;
break;
default:
break;
} }
else
{
P1IFG = ;
}
} #pragma vector = TIMER0_A0_VECTOR
__interrupt void CCR0_ISR(void)
{
switch(state)
{
case CHARGING:
if (++overflowsCharging == ) // wait 6.5535*50 = 3.28s
{
state = DISCHARGING;
CACTL1 |= CAON; // turn on CA+
TA0CCTL1 |= CM_1; // start TA1 capture on rising edge
P1OUT &= ~VCTRL; // start discharging
overflowsDischarging = ;
}
break;
case DISCHARGING:
overflowsDischarging++;
default:
break; } } #pragma vector = TIMER0_A1_VECTOR
__interrupt void CCR1_ISR(void)
{
TA0CTL &= ~MC_3; //stop timer
TA0CCTL1 &= ~CCIFG; // clear flag
switch(state)
{
case DISCHARGING:
state = FINISH_DC;
capacitance = (overflowsDischarging* + TA0CCR1)* / (R_SERIES*LN4); //nF
//send result to PC
printf("Capatitance: %n", (long unsigned)capacitance);
printf(" nF\r\n"); setReadyStatus();
break;
default:
break;
}
}
printf.c程序:为将电容结果通过UART输出至PC显示,以下这段程序实现了printf()函数,代码来自于NJC's MSP430 LaunchPad Blog博客和oPossum的代码。
/******************************************************************************
* Reusable MSP430 printf()
*
* Description: This printf function was written by oPossum and originally
* posted on the 43oh.com forums. For more information on this
* code, please see the link below.
*
* http://www.43oh.com/forum/viewtopic.php?f=10&t=1732
*
* A big thanks to oPossum for sharing such great code!
*
* Author: oPossum
* Source: http://www.43oh.com/forum/viewtopic.php?f=10&t=1732
* Date: 10-17-11
*
* Note: This comment section was written by Nicholas J. Conn on 06-07-2012
* for use on NJC's MSP430 LaunchPad Blog.
******************************************************************************/ #include "stdarg.h" void putc(unsigned);
void puts(char *); static const unsigned long dv[] = {
// 4294967296 // 32 bit unsigned max
,// +0
, // +1
, // +2
, // +3
, // +4
// 65535 // 16 bit unsigned max
, // +5
, // +6
, // +7
, // +8
, // +9
}; static void xtoa(unsigned long x, const unsigned long *dp) {
char c;
unsigned long d;
if (x) {
while (x < *dp)
++dp;
do {
d = *dp++;
c = '';
while (x >= d)
++c, x -= d;
putc(c);
} while (!(d & ));
} else
putc('');
} static void puth(unsigned n) {
static const char hex[] = { '', '', '', '', '', '', '', '', '',
'', 'A', 'B', 'C', 'D', 'E', 'F' };
putc(hex[n & ]);
} void printf(char *format, ...)
{
char c;
int i;
long n; va_list a;
va_start(a, format);
while(c = *format++) {
if(c == '%') {
switch(c = *format++) {
case 's': // String
puts(va_arg(a, char*));
break;
case 'c':// Char
putc(va_arg(a, char));
break;
case 'i':// 16 bit Integer
case 'u':// 16 bit Unsigned
i = va_arg(a, int);
if(c == 'i' && i < ) i = -i, putc('-');
xtoa((unsigned)i, dv + );
break;
case 'l':// 32 bit Long
case 'n':// 32 bit uNsigned loNg
n = va_arg(a, long);
if(c == 'l' && n < ) n = -n, putc('-');
xtoa((unsigned long)n, dv);
break;
case 'x':// 16 bit heXadecimal
i = va_arg(a, int);
puth(i >> );
puth(i >> );
puth(i >> );
puth(i);
break;
case : return;
default: goto bad_fmt;
}
} else
bad_fmt: putc(c);
}
va_end(a);
} /******************************************************************************
* MSP430G2553 printf() Tests
*
* Description: A modified version of the test code for testing oPossum's
* tiny printf() function. More information on the printf()
* function can be found at the following link.
*
* http://www.43oh.com/forum/viewtopic.php?f=10&t=1732
*
* This specific code tests the printf() function using
* the hardware UART on the MSP430G2553 with a baud rate
* of 9600. Once the character 't' is received, the test
* sequence is started.
*
* This code was originally created for "NJC's MSP430
* LaunchPad Blog".
*
* Author: Nicholas J. Conn - http://msp430launchpad.com
* Email: webmaster at msp430launchpad.com
* Date: 06-07-12
******************************************************************************/ void sendByte(unsigned char); /**
* puts() is used by printf() to display or send a string.. This function
* determines where printf prints to. For this case it sends a string
* out over UART, another option could be to display the string on an
* LCD display.
**/
void puts(char *s) {
char c; // Loops through each character in string 's'
while (c = *s++) {
sendByte(c);
}
}
/**
* puts() is used by printf() to display or send a character. This function
* determines where printf prints to. For this case it sends a character
* out over UART.
**/
void putc(unsigned b) {
sendByte(b);
} /**
* Sends a single byte out through UART
**/
五、测试结果
串口工具推荐使用Realterm,选择MSP430 Launchpad对应的串口号,串口波特率设为9600、8-N-1。电路连接好后,按下S2键开始测量,测量完成后,在Realterm上可以显示测量结果。板上的红、绿LED灯显示了工作状态,绿灯表示空闲(测量结束),红灯表示正在测量。试测了一个标称47uF的电容,结果如下图所示。