单片机C语言程序的结构和设计.docx
《单片机C语言程序的结构和设计.docx》由会员分享,可在线阅读,更多相关《单片机C语言程序的结构和设计.docx(36页珍藏版)》请在冰豆网上搜索。
单片机C语言程序的结构和设计
实验五单片机C语言程序的结构和设计
一、实验目的
1.掌握单片机C语言的程序结构;
2.掌握单片机C语言程序的编写和调试方法;
3.掌握MSP430FG2553基本I/O控制方法;
4.掌握单片机对外部接口电路的控制方法。
二、实验任务
1.练习调试程序
(1)硬件连接图
(2)原程序存在的问题
①实验要求将L1~L8连接到P2OUT端口上,所以程序中的端口应该采用P2OUT;
②由于程序中用到了P2OUT所有端口作为输出,所以应该首先将P2设置为GPIO;
③实验要求八个LED等应该在亮和灭之间进行循环,所以应该在原程序中加入主循环;
④在延时函数delay()中,延时时间不够,所以不能够看出灯的闪烁,应该将循环的时间延长;
(3)修改后的程序:
#include"io430.h"
voiddelay();
intmain(void)
{
WDTCTL=WDTPW+WDTHOLD;//关闭看门狗
P2SEL=0;
P2SEL2=0;
P2DIR=0xff;//设置端口1为输出
P2OUT=0xff;
for(;;)
{P2OUT=~P2OUT;//将端口1的值取反后输出
delay();//调用函数延时
}
}
voiddelay()//延时函数
{unsignedintj;
for(j=0;j<0xffff;j++);}
(3)程序调试方法总结
程序调试时主要使用逐步调试的方法,通过F10和F11使程序逐步运行,有必要时通过view观测register寄存器的相关值的变化,通过观察每一步运行之后相关寄存器的变化,可以初步判断对应程序运行的正确与否,还可以通过设置断点的方式进行局部调试。
对于几个不同调试按钮的使用方法总结如下:
GO属于连续执行,在调试过程中通常与断点同时使用,用于程序的长距离跳转;stepover用于逐步运行程序,但是不会运行到子程序的内部,如本例子中的delay函数,用此按钮时不会运行到delay内部;而stepinto则可以进入子函数内部,通常用来调试子函数,在调试子函数过程中若想跳出子函数可以使用stepout;合理地使用这些指令可以高效的调试程序。
2.控制发光二极管的显示变化
(1)硬件连接图
(2)C语言程序
#include"io430.h"
voiddelay();
voidaction_1();
voidaction_2();
constunsignedcharLED_blink1[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
constunsignedcharLED_blink2[4]={0xdb,0xe7,0x7e,0xbd};
intmain(void)
{
//Stopwatchdogtimertopreventtimeoutreset
WDTCTL=WDTPW+WDTHOLD;
P2SEL=0;//将P2端口设置为GPIO
P2SEL2=0;
P2DIR=0xff;//方向设定
for(;;)//主循环
{
action_1();//子函数调用
action_2();
}
}
voiddelay()//延时函数
{
inti;
for(i=0;i<0xffff;i++);
}
voidaction_1()//规律一动作函数
{
intj;
for(j=0;j<=7;j++)
{
delay();
P2OUT=~LED_blink1[j];//使输出端口按照表中显示
}
}
voidaction_2()//规律二动作函数
{
intk;
for(k=0;k<4;k++)
{
delay();
P2OUT=LED_blink2[k];//使输出端口按照表中显示
}
}
由于本程序比较简单,并且在上面程序后面附有注释,故不再说明程序的运行原理。
(3)思考
若要使用P1端口控制8个发光二极管,则实验板上的连线方面需要将L1~L8分别用跳线连接至P1.0~P1.7,在程序方面只需要将原程序中用到的P2端口均换成P1端口即可。
3.用按键控制发光二极管的显示变化
(1)硬件连接图
(2)程序设计思路
首先由于在上一个任务中已经实现了两种显示方式的循环显示,所以为实现该任务,只需要在上一个任务的程序中加入按键控制即可。
通过两个将两个P1引脚设置成输入引脚并与按键相连用来检测按键,在上个任务中的程序中,由于两个动作方式是循环进行的,所以只需实现当检测到对应的按键时,使得程序跳出执行某一动作的循环而进入执行另一动作的循环即可。
另外由于子函数一个循环是显示完一个动作,所以也需要在子函数中加入按键检测,当到按键时就跳出循环。
对于按键检测,由于当按下键时与地相连,所以需要给其一个上拉电阻,然后检测按键相连的输入引脚是否为低电平即可。
(3)C语言程序
#include"io430.h"
voiddelay();
voidaction_1();
voidaction_2();//函数声明
constunsignedcharLED_blink1[8]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
constunsignedcharLED_blink2[4]={0xdb,0xe7,0x7e,0xbd};
intmain(void)
{
//Stopwatchdogtimertopreventtimeoutreset
WDTCTL=WDTPW+WDTHOLD;
P2SEL=0;
P2SEL2=0;//将P2端口设置为普通IO
P1SEL=0;
P1SEL2=0;//将P1端口设置为普通IO
P2DIR=0xff;//P2设置为输出引脚
P1DIR=0xf0;//P1.0~P1.3设置为输入引脚,其余为输出引脚
P1REN|=BIT0+BIT1+BIT2+BIT3;//P1.0~P1.3上拉电阻使能置为1
P1OUT=0xff;//为P1.0~P1.3接上拉电阻
P1OUT|=BIT7;
for(;;)//主循环
{
for(;;)//动作一执行循环
{
action_1();//调用动作一函数
if((P1IN&BIT3)==0)//当检测到P1IN&BIT3对应按键时执行
{
P1OUT&=~BIT7;//置零P1.7,蜂鸣器工作
delay();//延时一段时间
P1OUT|=BIT7;//置位P1.7,蜂鸣器停止
break;//跳出动作一循环,进入动作二循环
}
}
for(;;)//动作二循环
{
action_2();//调用动作二函数
if((P1IN&BIT0)==0)//当检测到P1IN&BIT0对应按键时执行
{
P1OUT&=~BIT7;//蜂鸣器工作
delay();//延时一段时间
P1OUT|=BIT7;//蜂鸣器停止
break;//跳出动作二循环,进入动作一循环
}
}
}
}
voidaction_1()//动作一子函数
{
intj;
for(j=0;j<=7;j++)
{
delay();
P2OUT=~LED_blink1[j];
if((P1IN&BIT3)==0)break;//当检测到按键时立即跳出
}
}
voidaction_2()//动作二子函数
{
intk;
for(k=0;k<4;k++)
{
delay();
P2OUT=LED_blink2[k];
if((P1IN&BIT0)==0)break;//当检测到按键时立即跳出子函数
}
}
voiddelay()//延时函数
{
inti;
for(i=0;i<0xffff;i++);
}
该任务的代码如上所示,该代码中LED灯工作的基本思想与上一任务中的基本相同,程序的说明见程序后的注释,不再赘述。
4.并行方式控制数码管的显示
(1)数码管工作原理
数码管电路的内部结构如下图所示:
如上图所示,数码管的控制电路由两部分组成,一部分是位选信号S1~S4,控制四个数码管的显示与否,当其为高电平时,对应的数码管点亮。
Sa~Sh为数码管段选信号,用于控制八段数码管的显示内容。
所以若想在某个数码管上显示,首先应该给位选信号一个高电平,然后利用段选信号来控制要显示的内容。
(2)硬件连接图
(3)数码管的轮流显示
(a)硬件连接图
硬件连接图如上图所示。
(b)设计思路
将想要显示的内容翻译成数码管的段选信号,并将其存在数组中。
在较短的时间内,使四个数码管的为选信号依次单独为1,同时给相应的数码管段选信号。
(c)C语言程序
#include"io430.h"
//定义一个数码管显示表对应数码管显示为{,,,0,1,2,3,4,5,6,7,8,9,A,b,C,d,E,F,,,,,G,o,o,d};
unsignedintNUM_tab[27]={0xff,0xff,0xff,0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,
0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xff,0xff,0xff,0x82,0xa3,0xa3,0xa1};
voidprint(intk);//函数声明
intmain(void)
{
//Stopwatchdogtimertopreventtimeoutreset
WDTCTL=WDTPW+WDTHOLD;
P1SEL=0;
P1SEL2=0;
P2SEL=0;
P2SEL2=0;//将P1和P2均设置为GPIO
P2DIR=0xff;//P2设置为输出端口
P1DIR=0xff;//P1设置为输出端口
P1OUT&=~(BIT0+BIT1+BIT2+BIT3);
P1OUT|=BIT0;
intj;
for(j=0;j<20;j++)//数码管上轮流显示上面内容的循环
{
print(j);//调用函数进行扫描显示
}
for(;;){print(23);}//一直显示“Good。
}
voidprint(intk)
{
inta;
for(a=0;a<0x2ff;a++)//循环实现延时
{
if((a%24)==0)
{P2OUT=NUM_tab[k+3];P1OUT|=BIT0;P1OUT&=~BIT1;P1OUT&=~BIT2;P1OUT&=~BIT3;}
//右边第一个数码管显示
elseif((a%24)==6)
{P2OUT=NUM_tab[k+2];P1OUT&=~BIT0;P1OUT|=BIT1;P1OUT&=~BIT2;P1OUT&=~BIT3;}
//右边第二个数码管显示
elseif((a%24)==12)
{P2OUT=NUM_tab[k+1];P1OUT&=~BIT0;P1OUT&=~BIT1;P1OUT|=BIT2;P1OUT&=~BIT3;}
//右边第三个数码管显示
elseif((a%24)==18)
{P2OUT=NUM_tab[k];P1OUT&=~BIT0;P1OUT&=~BIT1;P1OUT&=~BIT2;P1OUT|=BIT3;}
//最左边数码管显示
}
}
(4)设计电子跑表
(a)硬件连接图
硬件连接图与(3)中相同。
(b)设计思路
该任务与上一个任务相似,都是使用四个数码管同时显示数字,由于对于3分钟以内的跑表,每个数码管上显示的内容都是0~9之间,所以同样可以定义数组一次存放0~9,显示时利用循环实现循环显示的功能。
(c)C语言程序
#include"io430.h"
voidseg();
intnum3,num2,num1,num0;
intmain(void)
{
unsignedcharNUM_tab[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
//Stopwatchdogtimertopreventtimeoutreset
WDTCTL=WDTPW+WDTHOLD;
P1SEL=0;
P1SEL2=0;
P2SEL=0;
P2SEL2=0;//P1和P2设置为GPIO
P1DIR=0x1f;//P1.0~P1.4设置为输出,其余设置为输入
P2DIR=0xff;//P2设置为输出
P1REN|=BIT5+BIT6+BIT7;//上拉电阻使能
P2OUT=0x00;
P1OUT|=BIT4+BIT5+BIT6+BIT7;//接上拉电阻
inti,j,k;
for(;;)
{
num3=NUM_tab[0];//时钟最高位一直显示0
for(i=0;i<=3;i++)//循环控制时钟分钟个位
{
num2=NUM_tab[i];//分钟个位赋值
if(i==3)
P1OUT&=~BIT4;//当到三分钟时,蜂鸣器工作
else
P1OUT|=BIT4;
for(j=0;j<=5;j++)//时钟秒十位循环控制
{
num1=NUM_tab[j];//时钟秒十位赋值
for(k=0;k<=9;k++)//秒个位循环控制
{
num0=NUM_tab[k];//秒个位赋值
seg();//调用显示函数进行显示
if(num2==0xb0&&num1==0xc0&&num0==0xc0)
break;//当计时到三分钟时跳出循环
}
if(num2==0xb0&&num1==0xc0&&num0==0xc0)
break;
}
if(num2==0xb0&&num1==0xc0&&num0==0xc0)
break;
}
}
}
voidseg()
{
inta;
for(a=0;a<0xff;a++)
{
if((a%12)==0)
{P1OUT|=BIT0;P1OUT&=~BIT1;P1OUT&=~BIT2;P1OUT&=~BIT3;P2OUT=num0;}
elseif((a%12)==3)
{P1OUT&=~BIT0;P1OUT|=BIT1;P1OUT&=~BIT2;P1OUT&=~BIT3;P2OUT=num1;}
elseif((a%12)==6)
{P1OUT&=~BIT0;P1OUT&=~BIT1;P1OUT|=BIT2;P1OUT&=~BIT3;P2OUT=num2;}
elseif((a%12)==9)
{P1OUT&=~BIT0;P1OUT&=~BIT1;P1OUT&=~BIT2;P1OUT|=BIT3;P2OUT=num3;}
}
}
(d)思考题
控制每个数码管显示的延时时间不能太短也不能太长,这是因为若太长则四个数码管显示存在闪烁;若时间太短则数码管亮度太低。
(5)(提高)增加按键控制功能
(a)硬件电路图
(b)设计思路
与LED的按键控制思路相同,当暂停时使数码管处于死循环中,循环显示同一个时刻,当按下开始或者复位键时跳出该循环,若是开始键则运行与上面相同,当按下复位键时使程序从循环最开始运行,并进入死循环,此时一直显示00:
00.
(c)C语言程序
#include"io430.h"
voidseg();
intnum3,num2,num1,num0;
intmain(void)
{
unsignedcharNUM_tab[10]={0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90};
//Stopwatchdogtimertopreventtimeoutreset
WDTCTL=WDTPW+WDTHOLD;
P1SEL=0;
P1SEL2=0;
P2SEL=0;
P2SEL2=0;
P1DIR=0x1f;
P2DIR=0xff;
P1REN|=BIT5+BIT6+BIT7;
P2OUT=0x00;
P1OUT|=BIT4+BIT5+BIT6+BIT7;
inti,j,k;
intrst,start;
for(;;)
{
num3=NUM_tab[0];
for(i=0;i<=3;i++)
{
num2=NUM_tab[i];
if(i==3)
P1OUT&=~BIT4;
else
P1OUT|=BIT4;
for(j=0;j<=5;j++)
{
num1=NUM_tab[j];
for(k=0;k<=9;k++)
{
num0=NUM_tab[k];
while(!
start)//未按下开始键时执行循环即暂停
{
seg();
if((P1IN&BIT5)==0||(P1IN&BIT7)==0)break;//当按下开始或者清零时跳出循环
}
if((P1IN&BIT7)==0){start=1;rst=0;}//若按下开始键则start=1,rst=0;
if((P1IN&BIT6)==0){start=0;rst=0;}//若按下暂停键则start=0,rst=0;
if((P1IN&BIT5)==0){start=0;rst=1;}//若按下清零键则start=0,rst=1;
seg();
if((num2==0xb0&&num1==0xc0&&num0==0xc0)||rst==1)//当计时结束或者清零键时跳出循环
break;
}
if((num2==0xb0&&num1==0xc0&&num0==0xc0)||rst==1)
break;
}
if((num2==0xb0&&num1==0xc0&&num0==0xc0)||rst==1)
break;
}
}
}
////////////////////数码管显示电路///////////////////
voidseg()
{
inta;
for(a=0;a<0x1ff;a++)
{
if((a%12)==0)
{P1OUT|=BIT0;P1OUT&=~BIT1;P1OUT&=~BIT2;P1OUT&=~BIT3;P2OUT=num0;}
elseif((a%12)==3)
{P1OUT&=~BIT0;P1OUT|=BIT1;P1OUT&=~BIT2;P1OUT&=~BIT3;P2OUT=num1;}
elseif((a%12)==6)
{P1OUT&=~BIT0;P1OUT&=~BIT1;P1OUT|=BIT2;P1OUT&=~BIT3;P2OUT=num2;}
elseif((a%12)==9)
{P1OUT&=~BIT0;P1OUT&=~BIT1;P1OUT&=~BIT2;P1OUT|=BIT3;P2OUT=num3;}
}
}
5.串行方式控制数码管显示
(1)硬件连接图
(2)设计思路
使用74HC595实现的串行方式控制数码管,与并行方式的唯一差别在于对于一个数码管的显示内容的控制。
并行方式较为简单,直接将对应的值赋给连接段选信号的输出端口即可,而串行方式则需要一位一位地输入,即从高位到低位一次从DS端口输入,每准备好一位数据需要给SHCP一个脉冲读入,输入八位之后给STCP一个脉冲使得数码管进行显示,所以在之前任务的基础上,创建子函数实现这个过程即可,其余相同。
(3)C语言程序
#include"io430.h"
voidseg();
voidINPUT();
unsignedcharBIT[8]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01};
//位测试向量,用于检测对应位上的值
unsignedcharNUM_TAB[27]={0xff,0xff,0xff,0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,
0x80,0x90,0x88,0x83,0xc6,0xa1,0x86,0x8e,0xff,0xff,0xff,0xff,0x82,0xa3,0xa3,0xa1};
//显示表
intmain(void)
{
//Stopwatchdogtimertopreventtimeoutreset
WDTCTL=WDTPW+WDTHOLD;
P1SEL=0;
P1SEL2=0;
P2SEL=0;
P2SEL2=0;//设置为GPIO
P2DIR=0xff;
P1DIR=0xff;//设置为输出端口
P1OUT=0x00;
intj;
for(j=0;j<20;j++)//循环实现移动显示
{
seg(j);
}
for(;;){seg(23);}//循环完之后显示Good
}
//********************数码管扫描显示函数***********************//
//由于该部分与之前任务相同不再赘述
voidseg(intk)
{
inta;
for(a=0;a<0x2ff;a++)
{
if((a%12)==0)
{INPUT(k+3);P1OUT|=BIT0;P1OUT&=~BIT1;P1OUT&=~BIT2;P1OUT&=~BIT3;}
elseif((a%12)==3)
{INPUT(k+2);P1OUT&=~BIT0;P1OUT|=BIT1;P1OUT&=~BIT2;P1OUT&=~BIT3;}
elseif((a%12)==6)
{INPUT(k+1);P1OUT&=~BIT0;P1OUT&=~BIT1;P1OUT|=BIT2;P1OUT&=~BIT3;}
elseif((a%12)==9)
{INPUT(k);P1OUT&=~BIT0;P1OUT&=~BIT1;P1OUT&=~BIT2;P1OUT|=BIT3;}