//软件设计介绍:基于MSP430的单相交流电能质量采集模块程序
//***************************************************
//功能介绍:对220V交流电能质量的采集并显示
//对P6.0口电压进行采集 液晶显示(输入的是脉冲波形(去除掉正弦波形的负半周))
//对P6.1口电流进行采集 液晶显示(同上)
//对P2.3口频率进行采集 液晶显示(输入的方波)
/*
LCD16x2
预演效果如下
****************
V:220.0V I:400uA
P:50.00Hz
*/
//****************************************************
#include
#include "cry1602.h"
//#include "cry1602.c"
//有一个报错:目前的解决方案是注释掉上面一行的语句,并且把1602.C里面的int,char定义放到.h里面去
#define Num_of_Results 32
uchar shuzi[] = {"0123456789."};//定义数字
uchar tishi1[] = {"V:"};//定义提示电压信息
uchar tishi2[] = {"I:"};//定义提示电流信息
uchar tishi3[] = {"P:"};//定义提示频率信息
uchar tishi4[] = {"uA"};//定义提示电流单位
uchar tishi5[] = {"Hz"};//定义提示频率信息
uint start,end;
uint width; //==用于存放脉宽==
uint period; //==用于存放周期==
uint frequency; //==用于存放频率==
uint fy[4]; //==用于存放频率显示数据==
void process(void); //==函数声明==
void InitSys(); //==初始化时钟==
static uint A0results[Num_of_Results]; //保存ADC转换结果的数组
static uint A1results[Num_of_Results]; //保存ADC转换结果的数组
void Trans_val(uint Hex_Val);
void Trans_val1(uint Hex_Val);
/**********************************************************/
void main(void)
{
WDTCTL = WDTPW+WDTHOLD; //关闭看门狗
LcdReset(); //复位1602液晶
DispNChar(0,0,2,tishi1); //显示电压提示信息
//分配电压信息的坐标值(第1个位置开始,放在LCD的第1行)
DispNChar(9,0,2,tishi2); //显示电流提示信息
DispNChar(0,1,2,tishi3); //显示频率提示信息
Disp1Char(7,0,'V'); //显示电压单位V
DispNChar(14,0,2,tishi4); //显示电流单位uA
DispNChar(7,1,2,tishi5); //显示频率单位Hz
//ADC的初始化
P6SEL |= BIT0; //使能ADC通道A0,即P6.0口(采集电压)0x01
P6SEL |= BIT1; //使能ADC通道A1,即P6.1口(采集电流) 0x02
ADC12CTL0 = ADC12ON+SHT0_8+MSC; //打开ADC,设置采样时间 SHT0_8是64分频
//上面的配置没有打开内部的参考电压
//ADC12MCTLx用来选择通道和参考电压,这里面没有对此寄存器进行配置为默认的值
//默认值是参考电压选择AVCC(3.3V),通道是A0,所以测量范围是0~3.3V
//选择通道与参考电压的被省略: ADC12MCTL0=ADC12INCH_0+ADC12SREF_1;
ADC12CTL1 = CSTARTADD_0+SHP+CONSEQ_3; //使用采样定时器,序列通道多次重复采集
//寄存器配置采样保持触发源选择时ADC12SC,采样信号使用采样时序电路产生的信号
ADC12IE = BIT0; //使能ADC中断 P6.0
ADC12IE = BIT1; //使能ADC中断 P6.1
ADC12CTL0 |= ENC; //使能转换
ADC12CTL0 |= ADC12SC; //开始转换
//定时器的初始化
InitSys(); //==初始化时钟,SMCLK,MCLK均为8M==
P2DIR&=~BIT3;
P2SEL = BIT3; //==设置P1.2端口为功能模块使用,即:做捕获源==
TACTL = TASSEL_2+ID_3+TACLR+TAIE+MC1;//==定时器A时钟信号选择SMCLK,8分频,同时设置定时器A计数模式为连续增计模式==
CCTL1 = CM_1+SCS+CAP+CCIE; //==输入上升沿捕获,CCI0A为捕获信号源==
_EINT(); //使能中断,(开启全局中断)
LPM0; //低功耗模式
}
/**********************************************************/
//ADC中断服务函数
#pragma vector=ADC_VECTOR
__interrupt void ADC12ISR (void)
{
static uint index = 0;
A0results[index++] = ADC12MEM0;// 将转换出来的结果存入数组
A1results[index++] = ADC12MEM1;// 将转换出来的结果存入数组
if(index == Num_of_Results)//如果数组存满
{
uchar i;
unsigned long sum0 = 0;
unsigned long sum1 = 0;
index = 0;//再重新开始存放数组(会导致覆盖原有的数据)
for(i = 0; i < Num_of_Results; i++)//计算数组的和
{
sum0 += A0results[i];
sum1 += A1results[i];
}
sum0 >>= 5; //除以32,得到平均值,因为前面定义了一个32,即采集32次
sum1 >>= 5;
sum0=(sum0<<7)+(sum0<<5)+(sum0<<4);
//采集时电压在前端的处理电路中被放小了多少,
//这里就放大回去,分析得出应该为176倍(变回220V)
sum1=(sum1<<10)+(sum1<<6)+(sum1<<5)+(sum1<<3)+(sum1<<2);
//放大回去,分析得出应该为1132倍
//(这里求电流,1K的采样电阻要/1000,但是单位A变为uA要*10^6,所以是*1000)
Trans_val(sum0);
Trans_val1(sum1);
}
}
/**********************************************************/
/********频率采集处理模块**********/
//时钟初始化函数
void InitSys()
{
unsigned int i;
//--- 使用XT2振荡器,8MHz的那个 ---
BCSCTL1&=~XT2OFF; //==打开XT2振荡器==
do
{
IFG1 &= ~OFIFG; //==清除振荡器失效标志==
for (i = 0xFF; i > 0; i--); //==延时,等待XT2起振==
}
while ((IFG1 & OFIFG) != 0); //==判断XT2是否起振==
BCSCTL2 =SELM_2+SELS; //==选择MCLK、SMCLK为XT2,8M==
}
//中断处理函数
#pragma vector=TIMERA1_VECTOR //==定时器A中断处理==
__interrupt void timer_a(void)
{
switch(TAIV) //==向量查询==
{ case 2: //==捕获中断==
if(CCTL1&CM0) //==捕获到上升沿==
{
CCTL1=(CCTL1&(~CM0))|CM1; //==更变设置为下降沿触发==
start=TAR; //==记录初始时间==
}
else if (CCTL1&CM1) //==捕获到下降沿==
{
CCTL1=(CCTL1&(~CM1))|CM0; //==更变设置为上升沿触发==
end=TAR; //==用start,end,overflow计算脉冲宽度==
}
break;
default:
break;
}
}
//数据处理函数
void process(void)
{
while(end //while(endstart
uchar i;
width = end-start; //==实际脉冲宽度的计算==
period = 2* width;
frequency=10000/period; //这里放大的目的是为了后面便于显示
fy[0]=frequency/10000;
fy[1]=(frequency-10000*fy[0])/1000;
fy[3]=(frequency-10000*fy[0]-1000*fy[1])/100;
fy[4]=(frequency-10000*fy[0]-1000*fy[1]-100*fy[3])/10;
fy[2]=shuzi[10];
for(i = 0;i < 5;i++)
Disp1Char((2 + i),1,shuzi[fy[i]]); //第3个开始显示5位 ,例如 50.00,然后在第8位加上单位Hz)
}
/**********************************************************/
//将16进制的ADC转换数据变为10进制的模拟电压,电流数据并显示到LCD上面
//电压转换部分
void Trans_val(uint Hex_Val)
{
unsigned long caltmp;
uint Curr_Volt;
uchar t1,i;
uchar ptr[5];
caltmp = Hex_Val;
caltmp = (caltmp << 5) + Hex_Val; //caltmp = Hex_Val * 33 扩大10倍便于计算
caltmp = (caltmp << 3) + (caltmp << 1); //caltmp = caltmp * 10 扩大10倍便于计算
Curr_Volt = (caltmp >> 12); //Curr_Volt = caltmp / 2^n
//由于这里是V为单位,要转化为
//参考电压为3.3V,所以计算公式应该为Hex_Val*3.3/2^n
//在程序计算的过程中位移运算是最有效率的
//实际电压值是3.3*(ad采样值)/1024(v)
ptr[0] = Curr_Volt / 10000;//百位上面的数字(Hex到Dec的变换)因为放大了,所以要除以100*100
ptr[1] = (Curr_Volt % 10000)/1000; //十位上面的数字
ptr[2] = (Curr_Volt % 1000)/100; //个位上面的数字
t1 = Curr_Volt - (ptr[0] * 10000)-(ptr[1] * 1000)-(ptr[2] * 100);
ptr[4] = t1 / 10;//相当于小数点后第一位
ptr[3] = shuzi[10];//数字表中第10位对应符号"."
//在液晶上显示变换后的结果
for(i = 0;i < 5;i++)
Disp1Char((2 + i),0,shuzi[ptr[i]]); //(第3个开始显示5位 ,例如 220.0,然后在第8位加上单位V)
}
//电流转换部分
void Trans_val1(uint Hex_Val)
{
unsigned long caltmp;
uint Curr_I;
uchar i;
uchar ptr[3];
caltmp = Hex_Val;
caltmp = (caltmp << 5) + Hex_Val; //caltmp = Hex_Val * 33 扩大10倍便于计算
caltmp = (caltmp << 3) + (caltmp << 1); //caltmp = caltmp * 10 扩大10倍便于计算
Curr_I = caltmp >> 12; //Curr_Volt = caltmp / 2^n
//参考电压为3.3V,所以计算公式应该为Hex_Val*3.3/2^n
//在程序计算的过程中位移运算是最有效率的
//实际电压值是3.3*(ad采样值)/1024(v)
ptr[0] = Curr_I / 10000;//百位上面的数字(Hex到Dec的变换)
ptr[1] = (Curr_I % 10000)/1000; //十位上面的数字
ptr[2] = (Curr_I % 1000)/100; //个位上面的数字
//在液晶上显示变换后的结果
for(i = 0;i < 3;i++)
Disp1Char((11 + i),0,shuzi[ptr[i]]); //(第12个开始显示3位 ,例如 400,然后在第15位加上单位uA)
} |
|