正在学习做一个基于MSP430的交流电采集系统,用示波器检测过了输入口的波形,是没有问题的。
问题是LCD只是亮的,上面没有任何数据,怀疑是数据没有传输到LCD上面去,想知道是怎么事,我不知道显示程序哪里出了问题?
MSP430采用的最小系统板,输入信号与连接LCD的部分电路如图所示:
程序如下:
//软件设计介绍:基于MSP430的单相交流电能质量采集模块程序
//***************************************************
//功能介绍:对220V交流电能质量的采集并显示
//对P6.0口电压进行采集 液晶显示(输入的是脉冲波形(去除掉正弦波形的负半周))
//对P6.1口电流进行采集 液晶显示(同上)
//对P2.3口频率进行采集 液晶显示(输入的方波)
/*
LCD16x2
预演效果如下
****************
V:220.0V I:400uA
P:50.00Hz
*/
//****************************************************
#include <msp430x14x.h>
#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<start);
//while(end<start)的话就让其一直等待下去,直到end>start
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)
}
下面是cry1602.C的程序,直接用的网上的
#include <msp430x14x.h>
#include "cry1602.h"
/**************宏定义***************/
#define DataDir P4DIR
#define DataPort P4OUT
#define Busy 0x80
#define CtrlDir P3DIR
#define CLR_RS P3OUT&=~BIT0; //RS = P3.0
#define SET_RS P3OUT|=BIT0;
#define CLR_RW P3OUT&=~BIT1;//RW = P3.1
#define SET_RW P3OUT|=BIT1;
#define CLR_EN P3OUT&=~BIT2;//EN = P3.2
#define SET_EN P3OUT|=BIT2;
/***********************************************
函数名称:DispStr
功 能:让液晶从某个位置起连续显示一个字符串
参 数:x–位置的列坐标
y–位置的行坐标
ptr–指向字符串存放位置的指针
返回值 :无
***********************************************/
void DispStr(uchar x,uchar y,uchar *ptr){
uchar *temp;
uchar i,n = 0;
temp = ptr;
while(*ptr++ != '\0') n++; //计算字符串有效字符的个数
for (i=0;i<n;i++)
{
Disp1Char(x++,y,temp[i]);
if (x == 0x0f)
{
x = 0; y ^= 1;
}
}
}
/*******************************************
函数名称:DispNchar
功 能:让液晶从某个位置起连续显示N个字符
参 数:x–位置的列坐标
y–位置的行坐标
n–字符个数
ptr–指向字符存放位置的指针
返回值 :无
********************************************/
void DispNChar(uchar x,uchar y, uchar n,uchar *ptr){
uchar i;
for (i=0;i<n;i++)
{
Disp1Char(x++,y,ptr[i]);
if (x == 0x0f)
{
x = 0; y ^= 1;
}
}
}
/*******************************************
函数名称:LocateXY
功 能:向液晶输入显示字符位置的坐标信息
参 数:x–位置的列坐标
y–位置的行坐标
返回值 :无
********************************************/
void LocateXY(uchar x,uchar y){
uchar temp;
temp = x&0x0f;
y &= 0x01;
if(y) temp |= 0x40; //如果在第2行
temp |= 0x80;
LcdWriteCommand(temp,1);
}
/*******************************************
函数名称:Disp1Char
功 能:在某个位置显示一个字符
参 数:x–位置的列坐标
y–位置的行坐标
data–显示的字符数据
返回值 :无
********************************************/
void Disp1Char(uchar x,uchar y,uchar data){
LocateXY( x, y );
LcdWriteData( data );
}
/*******************************************
函数名称:LcdReset
功 能:对1602液晶模块进行复位操作
参 数:无
返回值 :无
********************************************/
void LcdReset(void){
CtrlDir |= 0x07; //控制线端口设为输出状态 DataDir = 0xFF; //数据端口设为输出状态
LcdWriteCommand(0x38, 0); //规定的复位操作
Delay5ms();
LcdWriteCommand(0x38, 0);
Delay5ms();
LcdWriteCommand(0x38, 0);
Delay5ms();
LcdWriteCommand(0x38, 1);//显示模式设置
LcdWriteCommand(0x08, 1);//显示关闭
LcdWriteCommand(0x01, 1); //显示清屏
LcdWriteCommand(0x06, 1);//写字符时整体不移动
LcdWriteCommand(0x0c, 1);//显示开,不开游标,不闪烁
}
/*******************************************
函数名称:LcdWriteCommand
功 能:向液晶模块写入命令
参 数:cmd–命令,
chk–是否判忙的标志,1:判忙,0:不判
返回值 :无
********************************************/
void LcdWriteCommand(uchar cmd,uchar chk){
if (chk) WaitForEnable(); // 检测忙信号?
CLR_RS;
CLR_RW; _NOP();
DataPort = cmd; //将命令字写入数据端口 _NOP();
SET_EN; //产生使能脉冲信号
_NOP();
_NOP();
CLR_EN;
}
/*******************************************
函数名称:LcdWriteData
功 能:向液晶显示的当前地址写入显示数据
参 数:data–显示字符数据
返回值 :无
********************************************/
void LcdWriteData( uchar data ){
WaitForEnable(); //等待液晶不忙
SET_RS;
CLR_RW; _NOP();
DataPort = data; //将显示数据写入数据端口
_NOP();
SET_EN; //产生使能脉冲信号
_NOP(); _NOP(); CLR_EN;
}
/*******************************************
函数名称:WaitForEnable
功 能:等待1602液晶完成内部操作
参 数:无
返回值 :无
********************************************/
void WaitForEnable(void){
P4DIR &= 0x00; //将P4口切换为输入状态
CLR_RS;
SET_RW;
_NOP();
SET_EN; _NOP();
_NOP();
while((P4IN & Busy)!=0); //检测忙标志
CLR_EN;
P4DIR |= 0xFF; //将P4口切换为输出状态
}
/*******************************************
函数名称:Delay5ms
功 能:延时约5ms
参 数:无
返回值 :无
********************************************/
void Delay5ms(void)
{
uint i=40000;
while (i != 0)
{
i–;
}
}
//CRY1602.h的声明
void DispNChar(unsigned char x,unsigned char y, unsigned char n,unsigned char *ptr);
void LocateXY(unsigned char x,unsigned char y);
void Disp1Char(unsigned char x,unsigned char y,unsigned char data);
void LcdReset(void);
void LcdWriteCommand(unsigned char cmd,unsigned char chk);
void LcdWriteData( unsigned char data );
void WaitForEnable(void);
void Delay5ms(void);
typedef unsigned char uchar;
typedef unsigned int uint;
灰小子:
建议先单独调试msp430f149驱动led屏的部分,驱动没问题了再和采样部分联合调试
XueSong Ye:
回复 灰小子:
好的,我先试试。谢谢您的回复!
XueSong Ye:
回复 XueSong Ye:
直接Debug的时候,到看门狗的这一步的时候就出现了这几个警告:
Wed May 31, 2017 17:52:36: Illegal register P2IE.P6 in interrupt description: PORT2_VECTOR 0x02 2 P2IE.P6 P2IFG.P6 Wed May 31, 2017 17:52:36: Illegal register P2IE.P7 in interrupt description: PORT2_VECTOR 0x02 2 P2IE.P7 P2IFG.P7 Wed May 31, 2017 17:52:36: Illegal register ADC12IE in interrupt description: ADC12_VECTOR 0x0E 2 ADC12IE ADC12IFG
这些非法寄存器描述是怎么回事,没看懂,求指点。
灰小子:
回复 XueSong Ye:
奇怪的问题。这是否单独调试lcd显示测程序时遇到的问题?
lcd显示程序应该用不到io中断吧?
XueSong Ye:
回复 灰小子:
不是。程序整体跑起来的时候出的这个警告。另外,我的LCD部分的程序是直接用的Cry1602.C里面的函数。不知道该如何调试QAQ
HG:
回复 XueSong Ye:
1602的例程应该很多啊。LCD亮应该只能说明上电没问题,调试思路是:
把中断,ADC,低功耗等等全部去掉,留下单纯的LCD程序。然后用示波器捕捉DATA,CS的信号看是不是跟发出来的一样。