第一篇:北邮数字逻辑小学期交通灯控制器
实验一交通灯控制器
一、实验目的
a)熟练掌握 VHDL语言和QuartusII软件的使用。b)用VHDL进行较复杂逻辑电路的设计和调试。c)熟练掌握isp期间的下载方法。d)理解状态机的工作原理和设计方法。
二、实验所用设备和环境
a)实验设备:TEC-8实验箱一个、EPM7128SLC84-15芯片一个
b)实验环境:WinXP+QuartusII
三、实验任务及功能介绍
在十字路口,每条道路各有一组红、黄、绿灯,用以指挥车辆和行人有序的通行。其中红灯亮表示禁止通行,黄灯亮表示停车,绿灯亮表示可以通行。设计一交通灯控制器用于自动控制十字路口的交通灯,指挥各种车辆和行人安全通行。a)东西和南北方向各有一组绿、黄、红等用于指挥交通,绿灯、黄灯和红灯的持续时间分别为5s,1s和6s。其中初始状态为东西南北四个方向红黄绿灯全亮,持续1秒。后东西方向绿灯,南北方向红灯,持5秒。后东西方向黄灯闪烁,南北方向持续红灯,维持1秒。后东西、南北方向交换。b)有紧急状况时,如有消防车、救护车或者其他需要优先放行的车辆时,各方向上均是红灯亮。当紧急状况解除时,回复原来的状态,继续正常运行。
c)当复位开关置1时,切换到初始状态,即四个方向红黄绿灯全亮。
四、课题分析及流程图:
一共六个状态:S0, S1, S2, S3, S4, 紧急状态。
五、设计思路
a)本题主要是红黄绿灯时间的计时(即分频),和状态的切换。由于外部时钟信号CLK信号为1kHz,而我们需要1Hz,因此需要分频。
b)对于计时,采用计数器方式,根据外部时钟信号CLK,在上升沿时,将计数器加一,分别定义T1,T1,T3,T4的整型数,范围分别是0~999,0~1999,0~4999和0~199,分别表示1秒、2秒、5秒和0.2秒,其中0.2秒用于黄灯闪烁。引入TIMER数组来表示该进行何种时间的计数,每一位依次表示红黄绿灯,为分频后信号。
c)设计六个状态:S0, S1, S2, S3, S4和紧急状态。对于状态切换,引入currentSTATE和nextSTATE两个状态变量,取值在STATE中选择:TYPE STATE IS(S0, S1, S2, S3, S4);d)东西南北方向灯的表示:有三个4位二进制数,RED、YELLOW和GREEN进行输出,从高位到地位依次表示东西南北四个方向的灯的状态,1亮,0灭。
e)为了得知什么时候切换状态,需引入数组SWITCH,4位二进制数,每一位依次表示红黄绿灯计时器是否计时结束,若结束则置1,各位相或,得到CTRL信号若有效,则需要切换状态,反之则不用。
f)对于紧急状态信号URGENT和复位信号CLR,则由开关控制,若置1,则有效,立即切换到紧急状态或初始状态S0。
六、详细设计
a)交通灯控制器实体
b)arc中信号
c)分频进程,以计数器形式得出T1,T2,T3,T4四种时间。
d)判断何时切换状态
e)控制黄灯闪烁进程,每0.2s闪烁一次
f)六种状态的表现形式及紧急状态的切换
七、心得体会
这次虽然小学期有两周,但时间仍然是非常紧张。我由于身体原因在上学期期末申请了缓考,所以在小学期的同时还要准备6门缓考考试,显得非常忙碌,能够分给小学期的时间也不多。所以在第一天老师布置课题后,我们小组经过讨论,让我选择了简单一些的交通灯控制器。
在最开始的时候,显得无从下手。虽然我们编写过LED灯显示和各种译码器,但都只是很简单的一个进程,如今要自己设计整个控制器,觉得有点不知所措。后来用编写C++等程序的经验,我仔细分析题目,将它分解成更小的模块,来分别实现,最后再整合在一起。
状态切换和显示部分的编码还比较简单,但是分频部分又有点不知所云,查阅了网上的例子和书籍后,感觉看不太懂他们的分频方式,询问同学,同学们也还不太清楚。后来借到第一天老师课上说的参考书,发现上面的分频方式是以计数器的形式,利用外部时钟,来一个上升沿计数器加一,简单容易理解,所以采用了。
每一次编写大作业的时候我都喜欢全部编写完毕后再编译、测试,但是这样就造成错误过多而引起不知道从何下手。因此我这一次采取的是逐部分编译、测试的方式。但是由于对VHDL已经一个多学期没有碰过,生疏了很多,语法错误非常多。而且编译通过后还经常不能运行。再反复完善代码却不能运行的时候,询问下同学,发现自己的管脚接的大有问题。是我自己忽略了有特殊指向的管脚,导致我的外部时钟CLK没有输入进去,程序无法继续进行。并且也发生了没有改代码,但是换了个实验箱就运行不了的情况,检查了很久发现是模式控制没有打到硬布线模式,影响了整体程序的运行,这些都是不通过自己调试、检验无法学习到的。
这两周的时间,我将自己的效率放大到最大,但是仍然因时间问题无法完成交通灯倒计时显示部分,也是小学期的一个遗憾。不过这两周,我不但又一次加深理解了自己动手操作的重要性,也对于芯片的设计思路有了更好的理解。由于交通灯控制器是一个简单一些的程序,因此我没有分模块实现,没有用到原理图,也没有遇到芯片的逻辑块溢出的问题,但是在每天的最后,小组讨论时,我们都会讨论解决这些问题,也让我从中学到了很多。总体来说,这一次的小学期算是收获颇丰。附件一:VHDL源代码 LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;USE IEEE.STD_LOGIC_ARITH.ALL;USE IEEE.STD_LOGIC_UNSIGNED.ALL;ENTITY trafficLight IS
PORT(CLR, CLK, URGENT : IN STD_LOGIC;GREEN : OUT STD_LOGIC_VECTOR(3 DOWNTO 0);YELLOW : OUT STD_LOGIC_VECTOR(3 DOWNTO 0);RED : OUT STD_LOGIC_VECTOR(3 DOWNTO 0));
--CLOCK : OUT STD_LOGIC_VECTOR(3 DOWNTO 0));END trafficLight;ARCHITECTURE lightARC OF trafficLight IS TYPE STATE IS(S0, S1, S2, S3, S4);SIGNAL currentSTATE, nextSTATE : STATE;SIGNAL CTRL : STD_LOGIC;SIGNAL SWITCH : STD_LOGIC_VECTOR(3 DOWNTO 0);
--切换状态
SIGNAL TIMER : STD_LOGIC_VECTOR(3 DOWNTO 0);
--时钟计时 SIGNAL T1 : INTEGER RANGE 0 TO 999;
--1s SIGNAL T2 : INTEGER RANGE 0 TO 1999;
--2s SIGNAL T3 : INTEGER RANGE 0 TO 4999;
--5s SIGNAL T4 : INTEGER RANGE 0 TO 199;
--0.2s
--SIGNAL T5 : INTEGER RANGE 0 TO 999;
BEGIN PROCESS(CLK, SWITCH)
BEGIN
IF(CLK'event AND CLK = '1')THEN
IF(SWITCH(0)= '0')THEN TIMER(0)<= '0';
T1 <= 0;
ELSE IF(T1 = 999 AND URGENT = '0')THEN
T1 <= 0;
TIMER(0)<= '1';
ELSE IF(URGENT = '0')THEN
T1 <= T1 + 1;TIMER(0)<= '0';
END IF;END IF;END IF;
IF(SWITCH(1)= '0')THEN TIMER(1)<= '0';
T2 <= 0;
ELSE IF(T2 = 1999 AND URGENT = '0')THEN
T2 <= 0;
TIMER(1)<= '1';
ELSE IF(URGENT = '0')THEN
T2 <= T2 + 1;
TIMER(1)<= '0';
END IF;
END IF;
END IF;
IF(SWITCH(2)= '0')THEN
T3 <= 0;TIMER(2)<= '0';
ELSE IF(T3 = 4999 AND URGENT = '0')THEN
T3 <= 0;TIMER(2)<= '1';
ELSE IF(URGENT = '0')THEN
T3 <= T3 + 1;
TIMER(2)<= '0';
END IF;
END IF;
END IF;
END IF;END PROCESS;
CTRL <= TIMER(0)OR TIMER(1)OR TIMER(2);切换状态
PROCESS(CLK, SWITCH(3))
BEGIN
IF(CLK'event AND CLK = '1')THEN
IF(T4 = 199 AND SWITCH(3)= '1')THEN
T4 <= 0;
TIMER(3)<= NOT TIMER(3);
ELSIF(SWITCH(3)= '1')THEN
T4 <= T4 + 1;
END IF;
END IF;
END PROCESS;
PROCESS(CTRL, CLR)
BEGIN
--倒计时完成IF(CLR = '1')THEN currentSTATE<= s0;
ELSIF(CTRL'event AND CTRL = '0')THEN currentSTATE<= nextSTATE;
END IF;
END PROCESS;
PROCESS(currentSTATE, URGENT)
BEGIN
IF(URGENT = '1')THEN
--紧急状态,所有方向点亮红灯
GREEN <= “0000”;YELLOW <= “0000”;RED <= “1111”;
ELSE
CASE currentSTATE IS
WHEN s0 => SWITCH <= “0001”;
GREEN <= “1111”;
YELLOW <= “1111”;
RED <= “1111”;
--CLOCK <= “0000”;nextSTATE<= s1;
WHEN s1 => SWITCH <= “0100”;
GREEN <= “1100”;
YELLOW <= “0000”;
RED <= “0011”;
--CLOCK <= “0110”;nextSTATE<= s2;WHEN s2 => SWITCH <= “1010”;
GREEN <= “0000”;
YELLOW(3)<= TIMER(3);--闪烁 YELLOW(2)<= TIMER(3);YELLOW(1 DOWNTO 0)<= “00”;nextSTATE<= s3;WHEN s3 => SWITCH <= “0100”;
GREEN <= “0011”;
YELLOW <= “0000”;
RED <= “1100”;
--CLOCK <= “0110”;nextSTATE<= s4;
WHEN s4 => SWITCH <= “1010”;
GREEN <= “0000”;
YELLOW(3 DOWNTO 2)<= “00”;
YELLOW(1)<= TIMER(3);--闪烁 YELLOW(0)<= TIMER(3);
RED <= “1100”;nextSTATE<= s1;
END CASE;
END IF;
END PROCESS;
--
PROCESS(CLK, CLOCK)
--
BEGIN----------IF(CLK'event AND CLK = '1')THEN
IF(CLOCK = “0000”)THEN CLOCK <= “0110”;ELSE CLOCK <= CLOCK-1;END IF;IF(T5 = 999)THEN T5 <= 0;--
ELSE T5 <= T5 + 1;--
END IF;--
END IF;
--
END PROCESS;END lightARC;
附件二:调试日志 2015/9/8周二:
复习VHDL基础知识及语法,了解交通灯大致编写思路。2015/9/9周三:
分解交通灯控制器:分频部分,状态切换部分,显示部分
今日计划:初步完成分频部分,考虑程序流程和各个进程间的配合。学习时钟分频的方式:途径:去图书馆借书查阅《VHDL数字电路设计与应用实践教程》,采用计数器的方式,针对外部时钟所给的1kHz分成1Hz,从而得到想要的5s、1s、0.2s等时间。
遇到问题:能够得到时间,但不知道该怎么将时间运用到状态切换中。解决方案:上网查阅资料。2015/9/10周四:
今日计划:完成状态切换部分和显示部分,考虑如何将分频好的时钟信号用在状态切换部分。
遇到问题:切换状态部分和显示部分比较简单,只是需要注意if-else语句使用,因为VHDL语法不熟悉导致语法报错很多。定义的状态太多,重复性大。
解决方案:开始在状态切换时,没想好要几个状态,零零散散分了很多个,导致很多状态其实根本是重合的,删减后得到最精简的6个状态。对于分频时钟,引入TIMER数组记录时钟是否计时完毕,若完毕则需要切换状态,将分频部分与其他部分串接在一起。2015/9/11周五:
今日计划:完成程序,在TEC-8上进行初步调试。
遇到问题:忘记如何下载芯片,询问同学后解决。成功下载芯片后发现不能运行,灯的显示也是乱的。
解决方案:回头看代码,发现在TIMER信号表示出计时完毕后,并没有给激励进行状态切换,引入CTRL信号给予激励,当CTRL有效时切换状态,为了方便观察CTRL信号的变化,将其连接到灯上。遇到问题:下载芯片后仍旧不能继续运行,但CTRL信号有改变。2015/9/14周一:
今日计划:完善程序,在TEC-8上运行成功。
遇到问题:下载芯片后仍旧不能继续运行,但CTRL信号有改变。解决方案:能够判断计时结束,能够给激励改变状态,却没有改变状态。发现没有信号表示红黄绿灯的切换,用S0~S4来判断状态并切换,但发现红黄绿灯有对称,而S2和S4、S1和S3对于计时器来说没有任何区别,不需要重复判断,因此引入SWITCH数组,每一位代表一种灯,使得能够判断计时结束后切换到何种状态,减少重复。修改编译报错。2015/9/15周二:
今日计划:完成程序,并在TEC-8上正确运行。
遇到问题:下载芯片后仍旧不能运行。发现管脚配置有问题。解决方案:按照老师给的ppt上的管脚配置,CLK接57管脚,CLR、URGENT分别接81,80开关上。RED,YELLOW,GREEN按顺序接到20到34的红黄绿灯上。(开始将CLR接到1号管脚上,发现必须一直按着CLR才可以运行程序,后将CLR改接开关上。)
遇到问题:在上午运行成功后,下午换了一台电脑,发现下载芯片后程序不动了。
解决方案:在完全没有改代码的情况下只能检查实验箱是否有误碰情况。排除灯显示问题后,确定是外部时钟没有走,发现开关置于独立而非硬布线模式,导致程序无法运行。拨回独立后运行正常。2015/9/16周三:
完成交通灯控制器,改正红绿灯显示不合理的地方,在考虑加入倒计时模块未果后,接受验收。
第二篇:数字系统课程设计交通灯控制器
东南大学
《数字系统课程设计》
设计报告
项目名称: 交通灯控制器
姓
名:
学
号:
专
业:
实 验 室: 电工电子实验中心
组
别:
无
同组人员:
无
设计时间: 2016 年月 26 日
——
2016 年 9 月 20日 评定成绩:
审阅教师:
目录
一.设计方案及论证……………………………………………… 3 二.模块设计……………………………………………………… 5 三.总体设计与仿真……………………………………………… 10 四.总结…………………………………………………………… 12 一.设计方案及论证
1.设计使用环境
本交通灯控制系统设计利用Verilog HDL语言进行设计编程,利用Cyclone EP1C6Q240C8芯片和一些外围器件组成硬件电路,利用Quartus II软件将编写好的程序进行编译和仿真,并将调试完成的程序下载到Cyclone EP1C6Q240C8芯片上,通过观测电路板上的红绿信号灯以及数码管显示来分析系统的性能。
2.设计任务分析
主干道与乡村公路十字交叉路口在现代化的农村星罗棋布,为确保车辆安全、迅速地通过,在交叉路口的每个入口处设置了红、绿、黄三色信号灯。红灯禁止通行;绿灯允许通行;黄灯亮则给行驶中的车辆有时间行驶到禁行线之外。主干道和乡村公路都安装了传感器,检测车辆通行情况,用于主干道的优先权控制。
设计要求:
1)当乡村公路无车时,始终保持乡村公路红灯亮,主干道绿灯亮。2)当乡村公路有车时,而主干道通车时间已经超过它的最短通车时间时,禁止主干道通行,让乡村公路通行。主干道最短通车时间为25s。
3)当乡村公路和主干道都有车时,按主干道通车25s,乡村公路通车16s交替进行。4)不论主干道情况如何,乡村公路通车最长时间为16s。
5)在每次由绿灯亮变成红灯亮的转换过程中间,要亮5s时间的黄灯作为过渡。6)用开关代替传感器作为检测车辆是否到来的信号。用红、绿、黄三种颜色的发光二极管作交通灯。7)数码管倒计时显示
3.测量控制原理
1)通过乒乓开关来控制FPGA输入信号的电平,从而控制交通灯工作。
2)利用FPGA输出的电平信号去驱动静态数码管及三色小灯来模拟交通灯。
4.顶层设计方案框图及说明
1)交通灯控制器框图
C表示乡村道路是否有车到来,1表示有,0表示无;SET用来控制系统的开始及停止;RST是复位信号,高电平有效,当RST为1时,恢复到初始设置;CLK是外加时钟信号;MR、MY、MG分别表示主干道的红灯、黄灯和绿灯;CR、CY、CG分别表示乡村道路的红灯、黄灯和绿灯,1表示亮,0表示灭。
2)流程图
MGCR表示主干道绿灯,乡村道路红灯;MYCR表示主干道黄灯,乡村道路红灯;MRCG表示主干道红灯,乡村道路绿灯;MRCY表示主干道红灯,乡村道路黄灯;T0=1表示主干道最短通车时间到,T1=1表示5秒黄灯时间到,T2=1表示乡村道路最长通车时间到。二.模块设计
1.模块功能及端口说明
1)分频模块
输入端为clk_in,即实验箱自带脉冲输入信号,输出端为clk,即想得到的频率。2)主控制模块
输入端为CLK、RST、C。其中c为乡村道路开关,为1时表示乡村道路有车;rst为初始化开关,为1时表示初始化为主干道绿灯,乡村道路红灯的状态。
输出端为MG、MY、MR、CG、CY、CR分别表示主干道和乡村道路的红黄绿灯,与LED灯相连;mh、ml、ch、cl分别表示主干道和乡村道路倒计时显示的高低位,与数码管相连。
3)数码管显示模块
输入端为clk和count,输出端为LED。
2.主要功能的设计方法
1)分频模块
试验箱可选晶振有2M和50M,选择使用2M后,设置分频系数为2000000,每计数到1000000,则输出取反,最终可得到1HZ的时钟信号。
2)主控制模块
设置两个外部控制条件:初始化(RST);乡村干道是否有车(C);
设置一个内部计数变量:NUM,通过相关运算取余取整得到数码管显示高低位;
通过有限状态机实现四个状态的循环切换。
3.Verilog设计程序及说明
1)分频模块
module fre(clk_in,clk);input clk_in;output clk;reg clk;reg [31:0]k;always @(negedge clk_in)begin
if(k>=1000000)//1000000分频 begin clk<=~clk;//取反
k<=0;end else
k<=k+1;//计数
end endmodule 2)主控制模块
module traffic(CLK,RST,C,MG,MY,MR,CG,CY,CR,mh,ml,ch,cl);input CLK,RST,C;output [3:0]mh,ml,ch,cl;output MG,MY,MR,CG,CY,CR;reg [3:0] mh,ml,ch,cl;reg MG,MY,MR,CG,CY,CR;reg [31:0] COUNT;reg [5:0]state;parameter s1=6'b100001,s2=6'b010001,s3=6'b001100,s4=6'b001010;always @(posedge CLK)if(RST)//初始化
begin
state = s1;//最初状态,主通行,乡村不通行 MG=1;MY=0;MR=0;CG=0;CY=0;CR=1;COUNT = 0;
mh=2;//主干道绿灯25s,乡村道路红灯5s
ml=5;
ch=3;
cl=0;
end else case(state)s1: begin COUNT = COUNT+1;
if((COUNT>=25)&&(C==1))//25s已计完且乡村道路来车,跳转到s2状态
begin
state = s2;//主干道黄灯,乡村道路红灯
MG=0;MY=1;MR=0;CG=0;CY=0;CR=1;COUNT = 0;mh=0;ml=5;ch=0;cl=5;end else if(COUNT<25)//25s没有计完,保持s1状态
begin
state = s1;
MG=1;MY=0;MR=0;CG=0;CY=0;CR=1;
mh=(25-COUNT)/10;//取整取余换算,倒计时显示
ml=(25-COUNT)%10;ch=(30-COUNT)/10;cl=(30-COUNT)%10;end else if(COUNT >= 25 && C == 0)//25s计完,乡村道路仍然没有车
begin
state = s1;//保持s1 MG=1;MY=0;MR=0;CG=0;CY=0;CR=1;mh=0;//数码管显示0
ml=0;
ch=0;
cl=0;
end
end s2: begin COUNT = COUNT+1;
if(COUNT==5)//5s黄灯已计完
begin
state = s3;//主干道红灯,乡村道路绿灯 MG=0;MY=0;MR=1;CG=1;CY=0;CR=0;COUNT = 0;
mh=2;//主干道21s红灯,乡村道路16s绿灯
ml=1;
ch=1;
cl=6;
end else
begin
state = s2;//5s黄灯未计完时,保持s2状态
MG=0;MY=1;MR=0;CG=0;CY=0;CR=1;
mh=0;
ml=5-COUNT;
ch=0;
cl=5-COUNT;
end end s3: begin COUNT = COUNT+1;
if(((COUNT>=16)&&(C==1))||(C==0))//乡村道路16s通行时间已结束,不管有无来车,均跳转s4状态
begin
state = s4;//主干道红灯,乡村道路黄灯
MG=0;MY=0;MR=1;CG=0;CY=1;CR=0;
COUNT = 0;
mh=0;
ml=5;
ch=0;
cl=5;
end else
begin
state = s3;//16s未结束,仍保持s3状态
MG=0;MY=0;MR=1;CG=1;CY=0;CR=0;
mh=(21-COUNT)/10;
ml=(21-COUNT)%10;
ch=(16-COUNT)/10;
cl=(16-COUNT)%10;
end end s4: begin COUNT = COUNT+1;if(COUNT==5)//5s黄灯时间结束
begin
state = s1;//回到s1状态
MG=1;MY=0;MR=0;CG=0;CY=0;CR=1;
COUNT = 0;
mh=2;
ml=5;
ch=3;
cl=0;
end else
begin
state = s4;//否则保持s4状态
MG=0;MY=0;MR=1;CG=0;CY=1;CR=0;
mh=0;
ml=5-COUNT;
ch=0;
cl=5-COUNT;
end end default:
begin
state = s1;
MG=1;MY=0;MR=0;CG=0;CY=0;CR=1;
COUNT = 0;
mh=0;
ml=5-COUNT;
ch=0;
cl=5-COUNT;
end endcase endmodule 3)数码管显示模块 module led(clk,count,LED);input clk;input [3:0]count;output [7:0]LED;reg [7:0]LED;always @(posedge clk)begin case(count)
4'b0000:LED=8'b00000011;
4'b0001:LED=8'b10011111;
4'b0010:LED=8'b00100101;
4'b0011:LED=8'b00001101;
4'b0100:LED=8'b10011001;
4'b0101:LED=8'b01001001;
4'b0110:LED=8'b01000001;
4'b0111:LED=8'b00011111;
4'b1000:LED=8'b00000001;
4'b1001:LED=8'b00001001;
default:LED=8'b00000001;endcase end endmodule 4.仿真图及说明
(1)分频模块
由于实际应用中分频较大,仿真时为方便观察,将分频频数设置为20。输入为clk_in,周期为10ns;输出为clk,其周期为200ns,与理论值相符。(2)主控制模块
与总体仿真相同,在此不再赘述。
三.总体设计与仿真
1.顶层设计图及说明
fre为分频模块,traffic为主控制模块,led为数码管显示模块。输入端有clk_in、c和rst,输出端有MG、MY、MR、CG、CY、CR和mh、ml、ch、cl。
2.仿真图及说明
输入有:C、CLK和RST 输出有:CG、CR、CY、MG、MR、MY、ch、cl、mh和ml C为乡村道路是否来车,1表示来车,0表示无车;CLK为时钟信号;RST为初始化功能,1有效;CG、CR、CY、MG、MR、MY分别表示乡村道路绿灯、红灯、黄灯,主干道绿灯、红灯、黄灯;ch、cl、mh、ml分别表示乡村道路和主干道红绿灯倒数显示高低位。
3.实验结果
(1)乡村道路无车时
乡村道路无车时,主干道25s倒数,乡村道路30s倒数结束后,保持0,且主干道绿灯亮,乡村道路红灯亮。(2)乡村道路有车时
若乡村道路一直有车,主干道25s(即S1状态)倒计时结束后,主干道切换黄灯,乡村道路保持红灯(即S2状态);5s黄灯倒计时结束后,主干道切换红灯,时间21s,乡村道路切换绿灯,时间16s(即S3状态);乡村道路16s绿灯结束后,切换黄灯,主干道保持红灯(即S4状态),5s黄灯结束后,回到S1状态,即主干道25s绿灯,乡村道路30s红灯,若一直有车,则循环进行。
四.总结
1.实验结果分析
(1)输入与输出
两个开关:一个初始化控制开关,一个乡村道路开关。初始化开关打开后复位,交通灯开始工作,乡村道路打开表示乡村公路上有车。
输出:四个数码管,两个显示主干道交通灯时间,两个显示乡村道路时间;六个led灯,两红两黄两绿分别表示主干道和乡村公路的红黄绿灯。(2)运行过程
1)初始状态(S1)
左侧为主干道倒计时,右侧为乡村道路倒计时;主干道绿灯亮,乡村道路红灯亮。K1为初始化按键,K2为乡村道路有无来车。
2)主干道25s绿灯结束后切换黄灯,乡村道路红灯(S2)
3)主干道5s黄灯结束,切换红灯21s,乡村道路切换绿灯16s(S3)
4)乡村道路16s绿灯结束,切换黄灯,主干道红灯(S4)
5s黄灯倒计时结束,回到S1状态,若一直有车,则循环S1-S2-S3-S4-S1。
2.问题解决方法
问题1:数码管显示与红绿灯切换不同时。
解决方法:红绿灯输出后面增加一延时模块,延时一个CLK,使其与数码管显示同步。问题2:理解错题意,在S3状态(即主干道红灯,乡村道路绿灯)时,此时若乡村道 路无车通过,应立即切换为S4状态(即主干道红灯,乡村道路黄灯),而不是等当前计数结束再切换。
解决方法:将代码修改为if(((COUNT>=16)&&(C==1))||(C==0)),修改后符合要求,解决了问题。
3.心得体会
通过此次系统设计,我对verilog HDL语言有了初步了解,并对利用quartus来进行系统设计有了更加深入的理解,操作也更加熟练。在设计过程中应该先设计好总体架构,再进行模块的具体设计,通过分析每个模块要实现的功能来写代码,并注意编写注释,便于以后的理解修改。编译时要注意设置顶层文件,先进行仿真观察结果是否正确,对代码进行修改,仿真结果正确后再下载到硬件,测试系统功能。
参考书目: [1] 夏宇闻,《Verilog数字系统设计教程》,北京,北京航空航天大学出版社,2013年 [2] 王金明,《数字系统设计与Verilog HDL》,北京,电子工业出版社,2011年
第三篇:北邮小学期c++文档
1.++程序设计实验报告
姓名: 班级: 学号: C
要求:
猜价格游戏
编写C++程序完成以下功能:
(1)假定有一件商品,程序用随机数指定该商品的价格(1-1000的整数);
(2)提示用户猜价格,并输入:若用户猜的价格比商品价格高或低,对用户作出相应的提示;(3)直到猜对为止,并给出提示。
算法:
int main(){ int price,input;srand(time(NULL));price=rand()%1000;cout<<“请输入数字,1到1000之间n”;cin>>input;while(input!=price){ if(input>price)cout<<“错误,大了,请重新输入n”;else cout<<“错误,小了,请重新输入n”;cin>>input;} cout<<“答对了n”;system(“pause”);} 思路:
使用srand()函数产生随机数,调用time.h的time()函数读取系统时间作为产生随机数的种子。采用循环结构,使函数运行至猜对价格,用cout输出,用cin输入。
2.要求:
矩形
编写C++程序完成以下功能:
(1)定义一个Point类,其属性包括点的坐标,提供计算两点之间距离的方法;(2)定义一个矩形类,其属性包括左上角和右下角两个点,提供计算面积的方法;
(3)创建一个矩形对象,提示用户输入矩形左上角和右下角的坐标;
(4)观察矩形对象以及Point类成员的构造函数与析构函数的调用;(5)计算其面积,并输出。
算法:
class point { private: float x1,y1,x2,y2;public:
};
class rectangle { private: point a;public: rectangle(){ point(){
} x1=0;y1=0;x2=0;y2-0;float dis(){ return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));} float getx(){ return x2-x1;} float gety(){ return y1-y2;} void set(float a1,float b1,float a2,float b2){
} x1=a1;y1=b1;x2=a2;y2=b2;
};
} float x1,y1,x2,y2;cout<<“请输入左上角点的横坐标”<
本题的关键在于创建point和rectangle两个类,在point中声明四个private成员分别是两个点的横坐标和纵坐标。声明返回private值的函数。Rectangle中将point类作为其private成员,在public中声明构造函数,读入两个点的横坐标和纵坐标,传给point。并声明计算面积的函数,利用point中返回x,y的函数得到长方形的x,y值计算其面积。
3.要求:
友元
编写C++程序完成以下功能:
(1)定义一个Boat和Car两个类,他们都具有私用属性——重量;(2)编写一个函数,计算两者的重量和。
double TotalWeight(Boat& b, Car& c);
算法:
class boat;class car { private: double w;public:
car(){
cout<<“请输入车的重量:n”;
};
cin>>w;} friend double TotalWeight(boat& b,car& c);class boat { private: double w;public:
boat(){
cout<<“请输入船的重量:n”;
cin>>w;} friend double TotalWeight(boat& b,car& c);};
double TotalWeight(boat& b,car& c){ return(c.w+b.w);}
void main(){
} car c;boat b;cout<<“总重量是:n”< 声明car,boat两个类,两者中都包含w这个private成员,用来记录重量,在car和boat的public中都声明友元函数TotalWeight()。这样TotalWeight()就能同时访问到car和boat的private成员了。 4.要求: 矩形 编写C++程序完成以下功能: (6)定义一个Point类,其属性包括点的坐标,提供计算两点之间距离的方法;(7)定义一个矩形类,其属性包括左上角和右下角两个点,提供计算面积的方法; (8)创建一个矩形对象,提示用户输入矩形左上角和右下角的坐标;(9)观察矩形对象以及Point类成员的构造函数与析构函数的调用;(10)计算其面积,并输出。 算法: void init(int a[][5]){ } for(int i=0;i<=3;i++){ } cin>>a[i][0]>>a[i][1]>>a[i][2]>>a[i][3]>>a[i][4]; void print(int a[][5]){ } void add(int a1[][5],int a2[][5],int a3[][5]){ for(int i=0;i<=3;i++) } void subtract(int a1[][5],int a2[][5],int a3[][5]){ } for(int i=0;i<=3;i++){ } for(int n=0;n<=4;n++)a3[i][n]=a1[i][n]-a2[i][n];{ } for(int n=0;n<=4;n++)a3[i][n]=a1[i][n]+a2[i][n];for(int i=0;i<=3;i++){ } for(int n=0;n<=4;n++)cout< void main(){ int a1[4][5],a2[4][5],a3[4][5];cout<<“请输入第一个矩阵:n”; init(a1);getchar();cout<<“请输入第二个矩阵:n”;init(a2);cout<<“和为:n”;add(a1,a2,a3);print(a3);cout<<“差为:n”;subtract(a1,a2,a3);print(a3);system(“pause”);思路: 声明三个4行5列的数组,读入矩阵的元素存入数组,用二重递归的方式遍历数组,执行加,减,输出操作。 5.要求: 编写C++程序完成以下功能: (1)假定矩阵大小为4×5(整型); (2)矩阵空间采用new动态申请,保存在指针中; (3)定义矩阵初始化函数,可以从cin中输入矩阵元素;(4)定义矩阵输出函数,将矩阵格式化输出到cout; (5)定义矩阵相加的函数,实现两个矩阵相加的功能,结果保存在另一个矩阵中;(6)定义矩阵相减的函数,实现两个矩阵相减的功能,结果保存在另一个矩阵中;(7)动态申请三个矩阵:A1、A2、A3;(8)初始化A1、A2; (9)计算并输出A3 = A1加A2,A3 = A1减A2;(10)释放矩阵空间。 算法: void main(){ int **a1,**a2,**a3;a1=new int*[4];for(int i=0;i<=4;i++) } a1[i]=new int[5];a2=new int*[4];for(int i=0;i<=4;i++)a2[i]=new int[5];a3=new int*[4];for(int i=0;i<=4;i++)a3[i]=new int[5];cout<<“请输入第一个矩阵:n”;init(a1);//getchar();cout<<“请输入第二个矩阵:n”;init(a2);cout<<“和为:n”;add(a1,a2,a3);print(a3);cout<<“差为:n”;subtract(a1,a2,a3);print(a3);ifree(a1);ifree(a2);ifree(a3);system(“pause”);思路: 声明a为指向指针的指针,先申请一个四个元素的数组,类型为int**,用来储存每行第一个元素的指针。如此声明之后就可以用a[i][j]来访问某个元素了。 6.要求: 编写C++程序完成以下功能: (1)用类来实现矩阵,定义一个矩阵的类,属性包括: 矩阵大小,用 lines, rows(行、列来表示); 存贮矩阵的数组指针,根据矩阵大小动态申请(new)。 (2)矩阵类的方法包括: 构造函数,参数是矩阵大小,需要动态申请存贮矩阵的数组; 析构函数,需要释放矩阵的数组指针; 拷贝构造函数,需要申请和复制数组; 输入,可以从cin中输入矩阵元素; 输出,将矩阵格式化输出到cout; 矩阵相加的函数,实现两个矩阵相加的功能,结果保存在另一个矩阵类,但必须矩阵大小相同; 矩阵相减的函数,实现两个矩阵相减的功能,结果保存在另一个矩阵 类,但必须矩阵大小相同。 (3)(4)(5)(6)定义三个矩阵:A1、A2、A3; 初始化A1、A2; 计算并输出A3 = A1加A2,A3=A1减A2; 用new动态创建三个矩阵类的对象:pA1、pA1、pA3; (7)初始化pA1、pA2; (8)计算并输出pA3=pA1加pA2,pA3=pA1减pA2;(9)释放pA1、pA1、pA3。 算法: class matric{ private: int lines,rows;int **a;public: void init(){ cin>>lines>>rows;a=new int*[lines];for(int n=0;n<=lines-1;n++) a[n]=new int[rows]; } void input(){ } for(int i=0;i<=lines-1;i++)for(int n=0;n<=rows-1;n++)cin>>a[i][n];void output(){ } for(int i=0;i<=lines-1;i++){ } for(int n=0;n<=rows-1;n++)cout< int getlines(){ return lines;} void copy(int** &b) for(int i=0;i<=lines-1;i++)for(int n=0;n<=rows-1;n++)b[i][n]=a[i][n];b=new int*[lines];for(int i=0;i<=lines-1;i++)b[i]=new int[rows]; { } void add(matric A1,matric A2) { int**a1,**a2;lines=A1.getlines();rows=A1.getrows();A1.copy(a1);A2.copy(a2); a=new int*[lines];for(int i=0;i<=lines-1;i++) a[i]=new int[rows]; for(int i=0;i<=lines-1;i++){ } for(int n=0;n<=rows-1;n++)a[i][n]=a1[i][n]+a2[i][n];} void Free() { int**temp=a; for(int i=0;i<=lines-1;i++){ delete a[i]; } } void sub(matric A1,matric A2){ int**a1,**a2;lines=A1.getlines();rows=A1.getrows(); };A1.copy(a1);A2.copy(a2); a=new int*[lines];for(int i=0;i<=lines-1;i++) a[i]=new int[rows]; } for(int i=0;i<=lines-1;i++){ } for(int n=0;n<=rows-1;n++)a[i][n]=a1[i][n]-a2[i][n];思路: 首先需要读入数组的行列,用上题动态申请数组的方法来动态申请。本题的关键在于编写copy函数,从private中传出数组的值,只是将上题4,5这两个常量改成了lines,rows这两个变量。使用copy函数的好处在与只允许单向传出数组的元素,而不允许指令来修改数组的元素从而保护了数据的安全。执行加减法时可以声明两个int**类型元素用来指向数组,用矩阵类中的copy函数,分别从A1,A2中拷贝出数组的元素,拷贝出值之后,加减操作与上题一样,用a[i][j]来访问数组元素即可。 7.要求: 形状 (一)编写C++程序完成以下功能: (1)声明一个基类Shape(形状),其中包含一个方法来计算面积;(2)从Shape派生两个类矩形和圆形;(3)从矩形派生正方形; (4)分别实现派生类构造函数、析构函数和其他方法; (5)创建派生类的对象,观察构造函数、析构函数调用次序;(6)不同对象计算面积。 算法: class shape { public: shape() { cout<<“shape 的构造函数”< };cout<<“shape 的析构函数”< rectangle(float a,float b){ x=a;y=b;cout<<“rectangle的构造函数”< { } };class circle:public shape { private: float r;public: circle(float a){ } r=a;cout<<“circle的构造函数”< { };} return 3.1416*r*r;class square:public rectangle { private: float a;public: };square(float x):rectangle(x,x){ a=x;cout<<“square的构造函数”< 长方形类和圆类继承shape类之后,只要重载area()函数即可正确地计算出面积,正方形类继承了长方形类,不需要重载长方形类的area()函数,只要给长方形类的x,y赋相同的值,然后就可以直接调用从长方形类中继承的area()函数,直接计算面积。 8.要求: 形状 (二)——虚函数 (1)将【形状 (一)】中的基类计算面积的方法定义为虚函数,比较与【形状 (一)】程序的差异;(2)将【形状 (一)】中的基类定义抽象类,比较与【形状 (一)】程序的差异。 算法: 算法与上题大致相同,只需要在area()函数前加上virtual即可。 思路: 算法与上题大致相同,只需要在area()函数前加上virtual即可,即将area()函数声明为虚函数。 将shape()类声明抽象类,只要把shape()中的area()函数声明成如下形式: Virtual float area()=0;纯虚函数即可。这样shape类就是一个抽象类,抽象类可以作 为基类被继承,但是不实例化,例如,shape a;这样声明一个a就是错误的。 9.要求: 对Point类重载++和――运算符 编写C++程序完成以下功能: (1)Point类的属性包括点的坐标(x,y);(2)实现 Point类重载++和――运算符: ++p,--p,p++,p--。 ++和――分别表示x,y增加或减少1。 算法: class point { private: int x,y;public: point operator++(int) { } point a=*this;x++;y++;return a;point operator++(){ x++;y++;return *this;} point operator--(int){ point a=*this;x--;y--;return a;} point operator--(){ x--;y--; };return *this;} void ini(){ cin>>x;} cin>>y;point(){ } cout<<“请输入x:”< 本题的关键在于表示出a++和++a的不同,a++是先引用a的值然后再执行++,++a是先执行++然后再引用a的值。在对++的重载时可以在()中加入int加以区分a++和++a,如下point operator++(int),point operator++()。要做出a++的效果就需要申请point temp,这个中间变量,temp=this,把当前对象赋给a,然后再对当前对象执行++操作,最后返回temp,因为temp储存的是未执行++之前的值,所以能够做出先引用a的值在执行++的效果。——的过程与++完全相同。要求: 流式IO (一)编写C++程序完成以下功能: (1)使用ofstream 向一个文本文件中输出各种类型的数据,并打开文件观察结果: 整数、无符号整型、长整型、浮点型、字符串、„„ (2)用十进制、八进制、十六进制方式向文本文件中输出整数;(3)使用控制符和成员函数来控制输出的格式: set()precision()...算法: void main(){ } ofstream outfile(“xx.txt”);int integer;unsigned int uint;long int lint;float f;string s;cout<<“输入整数:n”;cin>>integer;cout<<“输入无符号整型:n”;cin>>uint;cout<<“输入长整型:n”;cin>>lint;cout<<“输入浮点型:n”;cin>>f;cout<<“输入字符串:n”;cin>>s;outfile<<“十进制:”< .进行对文件的操作需要声明头文件#include 11.要求: 流式IO (三)编写C++程序完成以下功能:(1)输入一个文本文件名; (2)打开文件名,在该文件的每一行前面加上一个行号,保存在另外一个文本文件中。 算法: void main(){ } string a;cout<<“请输入文件名:n”;cin>>a;ifstream in(a);ofstream out(“out.txt”);string s;int i=1;while(getline(in,s)){ out< 本实验比较简单。只需要声明一个字符串a用来储存读入的文件名,再用ifstream类来声明一个对象,打开a这个文件。利用getline()函数,geiline函数能够自动从文件中读入一行数据,每读入一行数据就在其前面加上行号,行号可以用变量i来计数,每读一行就加1,并将这个就上行号的一行数据写入out.txt中,知道无法读入,就结束程序。 12.要求: 电话本 编写C++程序完成以下功能: (1)实现简单电话本功能,用姓名来搜索电话号码;(2)用户输入姓名,程序查找并输出结果;(3)用户可以通过输入,添加姓名和电话号码;(4)用户可以删除姓名和电话号码;(5)电话本可以保存在指定文件中; (6)电话可被从指定文件中读入到内存。 算法: #include class record { private: string name[50],number[50];public: record(){ int i=0; string na,nu; ifstream file(“phonenumber.txt”); file>>na>>nu; while(na.compare(“..”)) { name[i]=na; number[i]=nu; file>>na>>nu; i++; } for(i;i<=49;i++) { name[i]=“..”; number[i]=“..”; } } void search(){ int i=0; string na; cout<<“请输入名字:”< cin>>na; while(name[i].compare(“..”)) { if(!name[i].compare(na)) { cout<<“结果是:”< return;} i++;} cout<<“无该记录”;} void add(){ string na,nu;cout<<“请输入名字:”< if(!name[i].compare(“..”)){ //cout< return;} } cout<<“已满,无法插入”< } ofstream file(“phonenumber.txt”);for(int i=0;i<=49;i++){ } if(!name[i].compare(“..”))break;if(name[i].compare(“,”)) file< };void del(){ } int i=0;string na;cout<<“请输入要删除的名字:”< if(!name[i].compare(na)){ } i++;name[i]=“,”;number[i]=“,”;cout<<“删除成功。”< for(int i=0;i<=49;i++){ cout< ”< } for(int i=0;i<=49;i++){ } if(name[i].compare(“..”)&&name[i].compare(“,”))cout< number:”< void main(){ int choice; int i=0;record temp;while(1) { cout<<“nnn功能列表:n 1.代表用姓名来搜索电话号码n 2.代表添加姓名和号码n 3.代表删除一条记录n 4.代表储存电话本n 5.输出所有记录nn请选择你要的功能:”< cin>>choice; } switch(choice){ } case 1: temp.search();break;case 2: temp.add();break;case 3: temp.del();break;case 4: temp.store();break;case 5: temp.show();break;} system(“pause”);思路: 程序的大体思路是:声明电话本类,private成员是一个50个元素的string型数组,运行程序时就将文件中的电话记录读入到内存中(string数组),方便操作。从内存中读入记录的语句写在电话本类的构造函数中,使得声明类时就可以自动读入记录。文件中最后一行用”....”来标记记录的结束。对于所有操作都写进了电话本类中,本程序可以执行五个基本操作:1.按名字查找记录,查找成功返回该条记录,查找失败输出“无该记录” 2.添加记录,添加记录会要求输入名字与电话,如果记录数小于50条会显示“成功插入”,如果记录已经等于50条会显示“插入失败” 3.按名字删除某条记录,执行删除操作时会先查询该记录,如果存在该记录就将该记录赋为“,,”表示删除,如果不存在该记录返回”不存在该记录” 4.存储命令,可以将内存中的记录再次存入到文件当中 5.输出命令,输出当前内存中的所有记录。命令的选择可以用switch语句实现。 感想: 经过一学期的学习,我已经对c++有了初步的掌握,了解了类这 个全新的概念。类的应用可以方便管理函数,不需要像c那样凌乱得写一大堆函数,要调用时需要向上找到这个这个函数,产看它的功能与传入传出的要求。我认为类更大作用是可以有效地保护数据,不被随意地更改,也方便了他人的二次开发,只需要了解头文件中包含的类,类的方法,就可以方便地实现他人早已实现的功能,方便后人开发。比如试验中用到的 面向对象程序设计与实践 c++实验总结报告 ——网络工程14班饶思哲 ——学号:2013211574 实验一简单C++程序设计 1.猜价格游戏 编写C++程序完成以下功能: (1)假定有一件商品,程序用随机数指定该商品的价格(1-1000的整数); (2)提示用户猜价格,并输入:若用户猜的价格比商品价格高或低,对用户作出相应的提示; (3)直到猜对为止,并给出提示。 题目1-1总结: 1)本题需要随机生成整数,我开始只使用rand(),即price=rand();来生成随机整数 但这样只是一个伪随机函数,每一次重新打开程序生成的数都是一致的。因此加入时间随机种子:srand((unsigned)time(NULL))2)个人改进1:定义最大值最小值,在每次猜测数字时显示应猜的数字范围,作为提示。 想到这个是因为有一次猜了很多遍都没猜到,一时突然忘记猜到什么范围,然后往前翻猜过的数字和大小感觉相当麻烦,所以就添上了应猜范围,方便再一次猜数。3)个人改进2:本来有一个判断条件判断生成的随机数是不是1~1000范围内,而后更进为随机生成整数对1000取余得到0~999整数,再+1得到1~1000的整数。 实验二类与对象 1.矩形 编写C++程序完成以下功能: (1)定义一个Point类,其属性包括点的坐标,提供计算两点之间距离的方法;(2)定义一个矩形类,其属性包括左上角和右下角两个点,提供计算面积的方法;(3)创建一个矩形对象,提示用户输入矩形左上角和右下角的坐标;(4)观察矩形对象以及Point类成员的构造函数与析构函数的调用;(5)计算其面积,并输出。 题目2-1总结: 1)这一题是第一次用到class类的题目,开始并不明白为何要定义class,然后还需要区分public和private。而后来去图书馆借了书看到c++最大特色就是可以封装,定义私有属性和公有函数,以确保有些函数和参数不会被轻易访问到,降低错误率。2)class类在最起初定义时总是在class Rectangle那一行报错,经查书发现class定义最后一个大括号后有分号,开始并没有加上。 3)起初在调用class中函数时用c调用的方式,没有跟面向的对象结合,导致编译出错,而后从distance()改为p.distance()就正确了。 4)计算两点距离和面积运用开方和绝对值函数,前面若没有加头文件math.h则会报错。5)输入左上角右下角坐标时,若输入不当,可能会出现面积为负值的情况,所以加上绝对值函数保证面积非负。 6)起初不知道构造函数和析构函数的定义和用法,经翻阅书籍和运行程序得知构造函数在创建对象时调用,可以有多个。而析构函数则在释放对象时调用,一般每一个class中都只有一个默认析构函数。且构造函数与类名称一致,析构函数则在类名称前加~。 2.友元 编写C++程序完成以下功能: (1)定义一个Boat和Car两个类,他们都具有私用属性——重量; (2)编写一个函数,计算两者的重量和。double TotalWeight(Boat& b, Car& c); 题目2-3总结: 1)友元函数:在两个对象中都使用到时,可以使用友元函数,并在类外单独定义。 友元函数是允许在类外访问类中的任何成员的。开始在类外单独定义时跟类的成员函数单独定义混淆,写成了double Boat::TotalWeight(),导致编译错误,而后发现友元函数直接用函数名和函数返回值类型定义即可,不需要加上class类的名称。 2)起初定义完class Boat和class Car后发现编译错误,在友元函数的声明那一行出错,而后发现在这行之前没有定义Car类,于是将其在最开头声明出来,通过。 3)起初并没有加上boat和car类的构造函数和析构函数,可是经上网查阅,默认构造函数和析构函数可以系统自动生成,但析构函数只能删除成员指针,并不能释放指针指向的空间,所以若没有申请动态内存,析构函数可不写出,若申请,则需自行在析构函数中delete。 实验三数组与指针 1.矩阵 (一)编写C++程序完成以下功能: (1)假定矩阵大小为4×5(整型数组表示); (2)定义矩阵初始化函数,可以从cin中输入矩阵元素;(3)定义矩阵输出函数,将矩阵格式化输出到cout; (4)定义矩阵相加的函数,实现两个矩阵相加的功能,结果保存在另一个矩阵中;(5)定义矩阵相减的函数,实现两个矩阵相减的功能,结果保存在另一个矩阵中;(6)定义三个矩阵:A1、A2、A3;(7)初始化A1、A2; (8)计算并输出:A3 = A1加A2,A3 = A1减A2。 题目3-1总结: 1)起初在矩阵相加相减的赋值中所用语句为:m.matrix[i][j]=a.matrix[i][j]+b.matrix[i][j] 但是运行程序发现m矩阵所有元素都是0。经单步调试,发现并没有赋值成功。经查阅书籍,了解到this指针是指向类的对象的地址,便改用this->matrix[i][j]作为赋值对象,最后程序正确。 2)个人改进:将行数列数在文件开头用define定义,可以随时更改。 3)经多次调试后,程序运行结果正确,但矩阵看起来非常混乱,因为并没有行列对齐,于是在打印矩阵中每列直接用table空格隔开,保证美观。2.矩阵 (二)编写C++程序完成以下功能: (1)假定矩阵大小为4×5(整型); (2)矩阵空间采用new动态申请,保存在指针中; (3)定义矩阵初始化函数,可以从cin中输入矩阵元素;(4)定义矩阵输出函数,将矩阵格式化输出到cout; (5)定义矩阵相加的函数,实现两个矩阵相加的功能,结果保存在另一个矩阵中;(6)定义矩阵相减的函数,实现两个矩阵相减的功能,结果保存在另一个矩阵中;(7)动态申请三个矩阵:A1、A2、A3;(8)初始化A1、A2; (9)计算并输出A3 = A1加A2,A3 = A1减A2;(10)释放矩阵空间。 题目3-2总结: 1)与3-1题目的区别在int main中用new函数动态申请内存,然后析构函数需要释放申请的空间而不只是自动删除指向空间的指针。析构函数如图。 一开始并不知道该怎么动态申请内存,在c中使用malloc可是c++中并不一样。经查阅书籍,发现c++中申请释放内存用new和delete非常简便。2)指针调用类成员函数一开始不知道该怎么写,写成A1.input()结果编译错误。经查阅书籍,得知指针调用成员函数需写成A1->input()的形式。 3)在释放动态内存时,用到delete函数。但开始的时候写delete matrix时候会出问题。上网查询后得到以下delete用法。 Delete用法:当释放内部类型,如int double型时,直接delete a 即可。若是释放自己定义的class类型,需用delete[]matrix来释放内存。3.矩阵 (三)编写C++程序完成以下功能: (1)用类来实现矩阵,定义一个矩阵的类,属性包括: 矩阵大小,用 lines, rows(行、列来表示); 存贮矩阵的数组指针,根据矩阵大小动态申请(new)。(2)矩阵类的方法包括: 构造函数,参数是矩阵大小,需要动态申请存贮矩阵的数组; 析构函数,需要释放矩阵的数组指针; 拷贝构造函数,需要申请和复制数组; 输入,可以从cin中输入矩阵元素; 输出,将矩阵格式化输出到cout; 矩阵相加的函数,实现两个矩阵相加的功能,结果保存在另一个矩阵类,但必须矩阵大小相同; 矩阵相减的函数,实现两个矩阵相减的功能,结果保存在另一个矩阵类,但必须矩阵大小相同。 (3)定义三个矩阵:A1、A2、A3;(4)初始化A1、A2; (5)计算并输出A3 = A1加A2,A3=A1减A2; (6)用new动态创建三个矩阵类的对象:pA1、pA1、pA3;(7)初始化pA1、pA2; (8)计算并输出pA3=pA1加pA2,pA3=pA1减pA2;(9)释放pA1、pA1、pA3。 题目3-3总结: 1)拿到这道题我认为要定义一个矩阵类对象,其中包括3-1和3-2一样的input函数,print函数,plus函数,subtract函数,还有新增的构造函数析构函数。一上来我认为class中属性不再是之前的某行某列的数值,而是行数和列数还有矩阵的头指针。根据输入的行数列数来申请内存,再将数值存入不同的位置。然后在主函数中分为两个部分,一个是直接创建对象,另一个是创建矩阵数组指针。 2)第一部分跟前两题类似,所以很快就调试完成并且得到了正确的结果,但是指针部分一直出现问题。最开始是三个矩阵分别申请内存,这样的后果就是要输入三次行数和列数的数值,有可能出现行列数不相等的情况,给之后的加减操作带来麻烦。因此想到让A1矩阵申请内存,而让A2、A3矩阵都与A1相等,就可以避免多次输入行列数,且保证了矩阵大小相等。但这样输入第一个矩阵数值后,矩阵元素获取正常。可当输入第二个矩阵的数值以后,A1和A2矩阵的元素都变为第二个矩阵的元素,即第一个矩阵的元素被覆盖掉。导致和矩阵为第二个矩阵的2倍,差矩阵都是零。经过反复调试,曾经试过将含参构造函数中输入行列数改到主函数中,再给A1申请动态内存。但是因为A2和A3还是初始化跟A1相等,结果并没有改变,还是错误的。而后想到用含参构造函数来申请动态内存,输入固定的行列数后,用确定的行列数来new Matrix(x,y),这样一来不仅矩阵大小相等,而且也不会存在前一个矩阵的值被后一个覆盖掉的问题。经过调试,终于得到了正确的结果。 3)调试过程中我了解到指针初始化和赋值过程中是容易发生树脂覆盖的,所以尽量不要将指针初始化成跟某个指针相等。 实验四继承与派生 1、形状 (一)编写C++程序完成以下功能: (1)声明一个基类Shape(形状),其中包含一个方法来计算面积;(2)从Shape派生两个类矩形和圆形;(3)从矩形派生正方形; (4)分别实现派生类构造函数、析构函数和其他方法; (5)创建派生类的对象,观察构造函数、析构函数调用次序;(6)不同对象计算面积。 题目4-1总结: 1)这一题主要是函数的派生方法使用,经过查阅书籍,得知在class定义后加上:: public(基类名称)便是作为一个派生类的定义,可以沿用基类中的成员函数。 2)并且经过对构造函数和析构函数调用,得知是先调用基类的构造函数,再调用派生类的构造函数创建派生类对象,在程序结束时,先调用派生类的析构函数释放派生类对象,再调用基类的析构函数释放基类对象。 2、形状 (二)——虚函数(1)将【形状 (一)】中的基类计算面积的方法定义为虚函数,比较与【形状 (一)】程序的差异; (2)将【形状 (一)】中的基类定义抽象类,比较与【形状 (一)】程序的差异。 题目4-2总结: 1)起初并不知道虚函数的作用,后来经过查阅书籍得知在基类中定义虚函数,是为了在派生类中定义这一函数的不同操作方式。并且起初只改变了基类中area函数,在其前面加了virtual,但并没有体现出虚函数的运用。而后看到书上虚函数的应用,发现虚函数是可以让一个基类指针调用派生类相应函数的。于是经过改进,在主函数中定义了3个基类指针,分别指向派生矩形类对象、派生圆形类对象、派生正方形对象,然后并非通过对象名称调用成员函数,而是用基类指针调用派生类中的area函数,充分体现出对虚函数的应用。 2)这一题虽然跟上一题相似,但却因为运用了虚函数而变得不同。这一题让我对虚函数有了更加深入的了解。也了解到调用类中的成员函数不一定非要用对象名或者对象指针调用,也可以用其基类的指针通过虚函数调用。 实验五多态性 1、对Point类重载++和――运算符 编写C++程序完成以下功能: (1)Point类的属性包括点的坐标(x,y);(2)实现 Point类重载++和――运算符: ++p,--p,p++,p--。 ++和――分别表示x,y增加或减少1。 题目5-1总结: 1)对于符号的重载开始并不理解什么意思,后来得知是重新定义一下++、--操作的过程,包括自加自减和赋值。 2)通过这一道题,对于自加符号的前后置的区别又有了更深的印象。起初在—p和++p的时候都只是将x和y加一,并没有赋值,导致在主函数中写q=++p和q=--p时q保持原值,造成结果错误。单步调试后发现这一错误,及时改正过来。3)程序结果正确后编写菜单界面,使其操作步骤更加清晰明了。实验六流式IO 1、流式IO (一)编写C++程序完成以下功能: (1)使用ofstream向一个文本文件中输出各种类型的数据,并打开文件观察结果: 整数、无符号整型、长整型、浮点型、字符串、…… (2)用十进制、八进制、十六进制方式向文本文件中输出整数;(3)使用控制符和成员函数来控制输出的格式: set()precision()...题目6-1总结: 1)文件流主要是向文件中输入信息和从文件读取信息。而跟c语言不同的是c需要fopen来打开一个文件,而c++中只需要用fstream函数。且文件输入方式也比c语言简便很多。2)转换进制开始想到的是用数学的方法算出不同进制下的数值,再输入文件中,但非常的麻烦。后经查阅书籍,看到有指定进制的函数,可以用来修改默认的十进制:oct和hex。3)最后程序正确后,编辑menu显示菜单,使操作更加清晰明了。 2、流式 (三)编写C++程序完成以下功能:(1)输入一个文本文件名; (2)打开文件名,在该文件的每一行前面加上一个行号,保存在另外一个文本文件中。 题目6-3总结: 1)因为第一题流式已经尝试过向文件中输入信息,而经过翻阅书籍得知从文件中可以用getline整行读取字符串,便可以轻易的将文件内容加上行数,并输出到另一个文本文件中。 2)本题我觉得最大的困难在于输入文本文件名,并打开文件名。书上也并没有关于打开文件名的相关资料,于是上网查询,发现可以用string.h中的c_str()函数来获取键盘输入的文件名字符串,并打开文件名。 3)因为文件处理并没有在屏幕上有所显示,为了让每一步操作更加明了,在复制完一行后会在屏幕上打出某某行复制成功,使程序运行过程更清晰。实验七 C++程序设计应用 1、电话本 编写C++程序完成以下功能: (1)实现简单电话本功能,用姓名来搜索电话号码;(2)用户输入姓名,程序查找并输出结果;(3)用户可以通过输入,添加姓名和电话号码;(4)用户可以删除姓名和电话号码;(5)电话本可以保存在指定文件中;(6)电话可被从指定文件中读入到内存。 题目7-1总结: 1)电话本分为几大部分:读到内存、输出到文件、新增联系人、删除联系人、查找联系人。因为是读到内存,就想到了可以用数组或者链表来构成电话本的框架。而这两种方式在c语言用过,所以逻辑方便很熟悉,于是选择了比较简便的数组,这样不易出现指针错误的情况。 2)个人改进1: 新增联系人函数中,就是将联系人信息从键盘逐项读入,然后选择将联系人插入到什么位置。若一共6个联系人,插入位置输入3,则插入第三个位置,后面的联系人依次向后移一位。但若是一共6个联系人,插入位置输入8,则会提醒输入位置不当,直接将联系人插入到最后一个位置,即第七。 3)删除、查找、修改函数中都用到了用姓名查找,于是将其单独摘出来定义findposition,找到联系人位置。起初写的是将输入的字符串逐个跟name比较,即a == name,但却发现这样程序查找出来联系人永远不存在。经上网查阅,发现字符串相等不能直接像字符一样用==来判断,而是用><来判断,于是将其改为>=&&<=。但一开始由于疏忽,将&&且关系写成||或关系,导致查找出来永远是第一个联系人。后来单步调试发现错误,改正过来。 4)个人改进2: 删除、查找、修改中,若是联系人不存在,需要输出提示。而我另外又加了一句,并不是说不存在就直接返回,而是让用户自己选择是继续输入姓名还是返回主菜单。也让手误的时候不再把前面的操作进行一遍,留有手误的余地。5)修改联系人模块,一开始class类中只有setPhone函数,并没有setname,setmobile等等,而是直接将所有属性值组合起来构建联系人。这样的后果就是在修改联系人的时候,只修改了一项,但是并不能赋值进去。表面上虽然修改了,可查找后发现联系人的信息并没有改变。后来将每一项信息都分别set,经过调试最终结果正确。 6)从指定文件将电话本读入内存,就跟实验6第三题的流式很相似,都是逐行读取信息,只是读取后是存入数组的不同属性值中。开始不知道该怎么确定文本文件中不再有联系人信息,而后上网查资料发现当文件中不再有信息可以读的时候,infile>>name>>mobile…的值就是0,便可停止操作。而姓名等信息也可以逐个赋值到phone类数组里。 7)个人改进3: 电话本不需要手动保存在指定文件中,主菜单也不再显示这一操作,而是当选择退出电话本时自动保存到输出的output.txt中。 8)最后程序执行结果正确,编写menu菜单界面,美化界面并且让操作编号更加清晰。 北京邮电大学信息与通信工程学院 小学期AVR单片机实验报告 实验题目: 基于ATmega16L单片机的电子琴设计 学生姓名:学渣 班 级:2012XXXXXX 班内序号:XX 学 号:2012XXXXXX 日 期:2014年9月30日 同组同学:学渣 第1页 北京邮电大学信息与通信工程学院 目录: 一、实验介绍......................................3 1.1实验课题名称.................................3 1.2实验平台.....................................3 1.3实验课题关键字 ..............................3 1.4实验摘要.....................................3 二、小组分工......................................3 三、基本题目训练——流水灯与数码管秒表计时器.......3 3.1实现功能......................................3 3.2程序代码分析..................................4 3.3实验结果图片...................................7 四、有关发声的基础知识............................7 五、电子琴的设计与测试..............................9 5.1设计过程......................................9 5.2实验所需元器件.................................9 5.3实验程序主要流程图.............................10 5.4实验原理及原理图...............................10 5.4.1实验原理....................................10 5.4.2原理图.....................................11 5.5各个模块的设计与讲解............................11 5.6程序源代码及程序分析..........................14 5.7实验结果.......................................24 六、排错过程.........................................26 七、心得体会..........................................29 八、参考文献..........................................32 九、意见与建议........................................33 第2页 北京邮电大学信息与通信工程学院 一、实验介绍: 1.1实验课题名称:基于ATmega16L单片机的电子琴设计 1.2实验平台:本实验所用平台为AVR Studio 4 1.3实验课题关键字: ATmega16L型单片机 电子琴 键盘按键 LCD液晶显示屏 1.4实验摘要: 本实验设计的电子琴拥有可视化操作界面,能实现即时弹奏音乐、音乐播放、音乐变速、音乐变调,并可以进行任意长度录音(通过按键记录音阶)等功能 二、小组分工: XXX负责电路硬件的连接和报告的撰写 XXX负责程序代码的编写 XXX负责资料的收集整理和查阅 三、基本题目训练——流水灯与数码管秒表计时器 3.1功能:八盏LED二极管按顺序依次循环点亮,实现流水灯的效果,同时两只数码管分别代表秒和十分之一秒,进行秒表计时,配有 第3页 北京邮电大学信息与通信工程学院 两个按键,实现计时过程中的暂停和继续,同时在按下暂停键的时候蜂鸣器会响一声。利用ATmega16的寄存器中断功能,实现流水灯和数码管秒表计时器在实验板上同时工作,并且互相独立不影响。 3.2程序代码分析: #include char b[10] = { 0b11111010,//0 0b00110000,//1 0b11011001,//2 0b01111001,//3 0b00110011,//4 0b01101011,//5 0b11101011,//6 0b00111000,//7 0b11111011,//8 0b01111011,//9 };char a[10] = { 0b11111010,//0 0b00110000,//1 0b11011001,//2 0b01111001,//3 0b00110011,//4 0b01101011,//5 0b11101011,//6 0b00111000,//7 0b11111011,//8 0b01111011,//9 }; volatile char temp; int main(void){ DDRA = 0xff;PORTA = 0b10000000;DDRC = 0xff; 第4页 北京邮电大学信息与通信工程学院 DDRD = 0b11111011;DDRB = 0b11111011; TCNT0 = 55;PORTB = b[0];PORTD = a[0];PORTC |=(1 << 0);TCCR0 |=(1 << CS01);int count1 = 0,count2 = 0,i; MCUCR |=(1 << ISC00)|(1 << ISC01);//INT0上升沿触发 GICR |=(1 << INT0);//使能INT0,INT1 sei();//使能全局中断 while(1){ for(i = 0;i<9000;i++) { while(!(TIFR &(1< TCNT0=55; } count1++; if(count1!=10) PORTD = a[count1]; else { count2++; count1 = 0; if(count2 == 10) count2 = 0; temp = PORTA; PORTA = PORTA >> 1; if(temp & 0b00000001) { PORTA = PORTA | 0b10000000; } PORTB = b[count2]; PORTD = a[0]; } } } SIGNAL(SIG_INTERRUPT0)//INT0中断服务程序 { int count3 = 0,count4=0; 第5页 北京邮电大学信息与通信工程学院 while(!(PINB &(1<<2))) { count3++; if(count3 == 10) { count4++; count3 = 0; temp = PORTA; PORTA = PORTA >> 1; if(temp & 0b00000001) { PORTA = PORTA | 0b10000000; } } for(int i=0;i<9000;i++) { while(!(TIFR &(1< TCNT0=55; } } } 第6页 北京邮电大学信息与通信工程学院 3.3实验结果: 四、有关发声的基础知识: 声波是振动产生的。频率即表示每秒钟振动的次数,采用CTC方式时AVR单片机通过特定的端口(PD4及PD5)输出一定频率的方波,TCCR1A设为比较匹配时OC1A/OC1B电平取反,TCCR1B的计数上限为 第7页 北京邮电大学信息与通信工程学院 OC1A,根据公式OCnA=f/2N(1+OCRnA)计算出7个频率音阶所需的OCR1A,则只需将喇叭接在PD4或PD5,通过程序控制端口输出特定频率的方波波形(发声使用正弦波最好,方波效果稍次但影响不大),喇叭就会发出七种不同的声音,依照人听觉分辨7个音阶分为三组,分别为高中,低音阶频率,经计算可得,当OCR1A=(500000/musicmem[i]-1)时,{131,147,165,175,196,220,247}存放低音阶频率,{262,294,330,349,392,440,494}存放中音阶频率,{524,588,660,698,784,880,988}则存放高音阶频率,所以需要定义三个数组存放各音阶的频率值。除了音符频率以外还需要音长,所以定义1个2位数组表示一段音乐,第一个表示频率,第二个表示音长,播放时先访问频率数组,使喇叭发声,之后访问音长数组,确定喇叭发声时间。而有了音符频率数组,只要再得到任意一首歌的简谱,就可以将其转化为两个数组的形式,由音符对应的频率得出频率数组,然后再根据每个音符的音长,将其通过乐曲的节拍和音符的拍数计算出音符持续时间即可得出音长数字。 第8页 北京邮电大学信息与通信工程学院 五、电子琴的设计过程: 5.1设计过程: 5.2 实验所需元器件: ATmega16L型单片机,JTAG下载器,扬声器,4*4矩阵键盘,液晶屏,LM386,实验盒(内装剪刀、镊子、导线等用品)等 第9页 1602LCD北京邮电大学信息与通信工程学院 5.3实验程序主要流程图: 5.4实验原理及原理图 : 5.4.1实验原理 : 以ATmega16单片机作为整个系统的控制中心,外加琴键控制模块、播放模块、显示模块,使制作的电子琴完成设想的功能。琴键控制模块为4*4矩阵键盘,可以通过按下不同的琴键弹奏出不同的音阶,每个音阶对应着不同的频率,一段音乐是由许多不同的音阶组成,这样我们就可以根据不同的频率组合得到我们想要的音乐,同时在录制模式下,还可以通过按不同的按键记录下不同的音阶,由此记录一段音乐。播放模块接收对应频率的方波,由此播放琴键弹奏的音阶以 第10页 北京邮电大学信息与通信工程学院 及播放预先存放在单片机里的音乐。显示模块显示出当前所处的模式。 5.4.2 原理图: 5.5各模块的设计与详解: 中央处理器——ATmega16: 第11页 北京邮电大学信息与通信工程学院 实验中,PB0~PB7全部设置为输出,分配给LCD液晶显示屏D0~D7管脚;PA0~PA7连接4*4矩阵键盘的八个引脚;PD4、PD6和PD7设置为输出状态,分别连接到LCD显示屏的RS、R/W和E端口上;PD5置为输出状态,接到扬声器的一个管脚,扬声器的另一管脚接地;VCC为电源,向LCD显示屏供电;GND为公共接地。 琴键控制模块——4*4矩阵键盘: 工作原理:按键设置在行、列线交点上,行、列线分别连接到按键开关的两端。行线通过上拉电阻接到+5V 电源上。无按键按下时,行线处于高电平的状态,而当有按键按下时,行线电平由与此行线相连的列线的电平决定。 显示模块——LCD液晶显示屏: 第12页 北京邮电大学信息与通信工程学院 引脚详解: 第1脚:VSS为地电源。第2脚:VDD接5V正电源。 第3脚:VL为液晶显示器对比度调整端,接正电源时对比度最弱,接地时对比度最高,对比度过高时会产生“鬼影”,使用时可以通过一个10K的电位器调整对比度。 第4脚:RS为寄存器选择,高电平时选择数据寄存器、低电平时选择指令寄存器。 第5脚:R/W为读写信号线,高电平时进行读操作,低电平时进行写操作。当RS和R/W共同为低电平时可以写入指令或者显示地址,当RS为低电平R/W为高电平时可以读忙信号,当RS为高电平R/W为低电平时可以写入数据。 第6脚:E端为使能端,当E端由高电平跳变成低电平时,液晶模块执行命令。 第7~14脚:D0~D7为8位双向数据线。 第13页 北京邮电大学信息与通信工程学院 第15脚:背光源正极。第16脚:背光源负极。 播放模块——扬声器: 原先以为单片机本身的电源电压不足以驱动扬声器工作,所以设计了有LM386的功放模块,后来在实际操作的过程中测试发现,单片机本身的电源足以驱动扬声器播放音乐音调,因此舍弃了LM386功放模块的设置。 5.6程序源代码及程序分析: #include #define uchar unsigned char #define uint unsigned int const uchar table[]=“HELLO WELCOME!”; volatile uint num=0,count=0;volatile uint Mode=0,list=2,pause=0,aim=0,sure=0,del=0;volatile int state=0, S=1; //定义全局变量,S为变速变量 const uint Mode_Data[16]={0,440,494,523,587,659,698,784,880,998,1046,1156,1318,1396,1568};//存放声音的频率 //音高对应定时器初始化数值(低la~高la+休止符),cpu频率1MHz,用8分频 const uchar tone[]={0x00,0x8E,0x7E,0x77,0x6A,0x5E,0x59,0x4F,0x47,0x3F,0x3B,0x35,0x2F,0x2C,0x27,0x23,0X19,0X15}; uint Ssong[10][2]={{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}}; //******************************************** void delay_us(int n) //微妙级延时函数; { 第14页 北京邮电大学信息与通信工程学院 while(n--){ asm(“nop”);//自动延时一个时钟周期什么也不做 } } void delay_ms(int n) //毫秒级延时函数; { int m=14500*n;while(m--){ asm(“nop”);} } //********************************************** /************************ 屏幕显示函数 ************************/ void write_com(uchar com){ PORTD&=~(1<<4); //低电平指令模式。高电平数据; PORTD&=~(1<<6); //低电平写; PORTB=com;PORTD|=(1<<7); //高电平使能 delay_ms(1);PORTD&=~(1<<7); } void write_dat(uchar dat){ PORTD|=(1<<4);PORTD&=~(1<<6);PORTB=dat;PORTD|=(1<<7);delay_ms(1);PORTD&=~(1<<7);} /*********************************** * 音乐产生函数 * * 功能:输出频率为x的方波* * 范围:x:100-20000Hz,0:不发声 * * ***********************************/ 第15页 北京邮电大学信息与通信工程学院 void sound(int i) { TIMSK|=(1<<2);sei(); if(i&&i<17) //按了音符键了,且在0到F键这16个发音键上,才发声 { OCR1A=tone[i]; TCCR1A=0X50; //控制寄存器,选择可翻转功能,产生方波; TCCR1B=0X0a; PORTD=(1<<5);} else { TCCR1A = 0x00; //频率为0,休止符,不发声 TCCR1B=0X00; OCR1A=0; } } /************************************ 弹奏函数 ************************************/ void play(int n) //参数为键盘上的数字; { write_com(0X80+0X40+n);write_dat(16);sound(n); //弹奏音符; delay_ms(1);write_dat(20); TCCR1A=0x00; TCCR1B=0X00; //中断,为下一个节拍做准备; PORTD|=(1<<5); //PD5作为输出管脚,接扬声器; } //___________________________________________ void INI_POTR(void){ DDRD|=0xff;DDRB=0xff; PORTD=(1<<5);PORTD&=~(1<<7);DDRA=0xff; //A口作为键盘输入口; DDRB=0xff; } //_____________________________________ 第16页 北京邮电大学信息与通信工程学院 void INN_DEVICE(void){ MCUCR=0X00; // 中断寄存器置零; } //____________________________________ //检测键盘是否被按下的函数 uint isKeyPress(){ DDRA=0xff; //初始设置端口均为输出 PORTA=0xf0; //设置端口的初值为11110000 DDRA=0xf0; //将低四位设为输入 if(PINA == 0xf0) //当没有按下时,PINA仍然为11110000 { DDRA=0xff; return 0; //返回0,代表未按下 } else //当按下后,PINA不再是11110000 { DDRA=0xff; return 1; //返回1,代表按下了 } } //键盘输入检测函数; //_______________________________ int key_in(void) { DDRA = 0xff; //先全部设置为输出,再将后四位置为输入 PORTA = 0xf0; //设置高低电平 DDRA = 0xf0;uint i = 0,j = 16,Key = 17; unsigned char temp1,temp2; if(isKeyPress()){ temp1 = PINA; temp1 &= 0x0f; //只关心低四位的情况 switch(temp1) { case 0b00000001: j = 0; break; case 0b00000010: j = 1; 第17页 北京邮电大学信息与通信工程学院 break; case 0b00000100: j = 2; break; case 0b00001000: j = 3; break; } temp2 = PINA; temp2 &= 0x0f; if(temp1!= temp2) return 17; DDRA = 0xff; PORTA= 0x0f; DDRA = 0x0f; temp1 = PINA; temp1 &= 0xf0; switch(temp1) { case 0b00010000: i = 0; break; case 0b00100000: i = 1; break; case 0b01000000: i = 2; break; case 0b10000000: i = 3; break; } temp2 = PINA; temp2 &= 0xf0; if(temp1!= temp2) return 17; Key = i*4 + j+1;// return Key; } } /***************************** //去抖动 //只关心高四位的情况第18页 北京邮电大学信息与通信工程学院 音乐播放函数; *****************************/ void m_sound(uint a){ int m=(62500/a)-1; //发声原理 OCR1A=m; TCCR1A=0X50; //控制寄存器,选择可翻转功能,产生方波; TCCR1B=0X0a;} //**********************************播放函数; void music_play(uint a[][2]){ const char Mtable[]=“Music Mode”; write_com(0X01); //清屏; for(int i=0;i<5;i++) { write_dat(Mtable[i]); } int i=0; char tem=aim,T=1; while((a[i][1]!=0)&&(tem==aim)&&state) { //A 返回 B 暂停 F 模式 C 上一曲你 D 播放 E 下一曲; num=key_in(); switch(num) { case 16: state=0; // F STATE = 0弹奏 break; case 15: aim=1; //下一首E break; case 14: pause=1; //播放D break; case 13: aim=0; ///上一首C break; case 12: pause=0; ///暂停B break; case 11: 第19页 北京邮电大学信息与通信工程学院 T=2; break;case 10: T=1; break;case 9: T=0;break;case 5: //变速,1代表正常速度A //慢速0 //快速9 //5 降调 S=0.5; break; case 6: S=1; break; default: break; } while(a[i][0]&&pause) { num=key_in(); if(num==12) { pause=0; } else { if(S == 0.5) m_sound(a[i][0]*S); else m_sound(a[i][0]); if(T==2) delay_ms(a[i][1]-100); else if(T==0) delay_ms(a[i][1]+100); else delay_ms(a[i][1]); i++; TCCR1A=0x00; TCCR1B=0X00; } } pause=0; //6 正常调 //加减速 //下一个音符; 第20页 北京邮电大学信息与通信工程学院 TCCR1A=0x00; TCCR1B=0X00; //中断,为下一个节拍做准备; } if(a[i][1]==0){ aim =(++aim)% list; } } /***************** 录制音乐函数; *****************/ void record(void){ write_com(0X01); const uchar R_table[]=“Recording Mode”; for(int i=0;i<9;i++){ write_dat(R_table[i]);} pause=1;while(pause){ uint Skey = key_in(); if((Skey>0)&&(Skey<11)) { int i=0;//count=0; while(i<10) { Skey=key_in(); OCR1A=0; TCCR1A=0x00; TCCR1B=0X00; if((Skey>0)&&(Skey<11)) { play(Skey); Ssong[i][0]=tone[Skey]; Ssong[i][1]=600; i++; delay_ms(20); } if(Skey==14) 第21页 //清屏;//开始录制 //先清零计数器 //录制完成,北京邮电大学信息与通信工程学院 按D键结束录制 { i=10; } } pause=0; //借助 PAUSE 来标记一下什么时候退出while } } } //***************************歌曲数据 uint music_data[][2]= { {440,400},{440,400},{659,400},{659,400},{740,400},{740,400},{659,800}, {587,400},{587,400},{554,400},{554,400},{494,400},{494,400},{440,800}, {659,400},{659,400},{587,400},{587,400},{554,400},{554,400},{497,800}, {659,400},{659,400},{587,400},{587,400},{554,400},{554,400},{497,800}, {440,400},{440,400},{659,400},{659,400},{740,400},{740,400},{659,800}, {587,400},{587,400},{554,400},{554,400},{494,400},{494,400},{440,800}, {0,0} }; // abcdefg uint music_1[][2]= {{262,400},{294,400},{330,400},{262,400},{262,400}, //乐谱 {294,400},{330,400},{262,400},{330,400},{349,400}, {392,800},{330,400},{349,400},{392,800},{392,300}, {440,100},{392,300},{349,100},{262,400},{392,400}, {440,300},{392,100},{349,300},{330,100},{262,400}, {294,400},{196,400},{262,400},{294,800},{196,400}, {262,400},{294,800},{0,0}};//music_1 int main(void){ INI_POTR(); //初始化 write_com(0X38); //显示光标等; write_com(0X01); //清屏; write_com(0X0f); //打开光标; write_com(0X06); write_com(0X80+0X02); for(int i=0;i<8;i++) 第22页 北京邮电大学信息与通信工程学院 { write_dat(table[i]); } write_com(0X80+0X11); delay_ms(5); while(1) { num = key_in(); if(num==16) //模式选择 { if(state==1) state=0; else state=1; } 放模式 if(state==1) { (state==1) { switchaim() { case 0: music_play(music_data); break; case 1: music_play(Ssong); break; default: break; }while delay_ms(10); } } else if(num==11) //录制模式; { record(); delay_ms(10); } else { write_com(0X01); 第23页 //播 北京邮电大学信息与通信工程学院 } const uchar Ptable[]=“playing Mode”; for(int i=0;i<7;i++) { write_dat(Ptable[i]); } while((num!=16)&&(num!=11)) { play(num); num=key_in(); } } } //弹奏模式 5.7实验结果: 实现了最初设想的功能,所设计的电子琴可以进行即兴弹奏、录制音乐、播放音乐以及音乐变速。测试图片如下: 第24页 北京邮电大学信息与通信工程学院 第25页 北京邮电大学信息与通信工程学院 接通电源后,显示屏以每次显示一个字符的形式显示出“HELLO WELCOM!”的欢迎文字,接着自动进入弹奏模式,并在LCD的第一行显示出“Playing Mode”的文字,用户每次按下按键都会在LCD的第二行的相应位置出现对应按键顺序的光标闪烁。在弹奏模式下,按下F键会进入播放模式,LCD显示出“Music Mode”字样,此时按下D键是播放/继续,按下B键是暂停,C键和E键分别代表上一首和下一首,9键则是将音乐速度调整为慢速,再次按下F键会回到弹奏模式。在弹奏模式下,按下A键会进入录制模式,此时LCD显示“Recording Mode”字样,可以使用1~0十个按键进行录音,想要听听录制好的音乐,可以切换到播放模式下进行欣赏。 六、排错过程: 基础题目训练阶段: 1.硬件连接好之后,在进行编程的过程中发现端口不够用,因为PB3和PD3这两个能产生中断的端口必须空出来作为暂停和开 第26页 北京邮电大学信息与通信工程学院 始按键的接口,于是我重新布线,将数码管显示小数点的右下角的引脚改接到PC0,同时在代码中将此端口设置为高电平输出,这样就解决了该问题。 2.当我们简单地把流水灯和计时器代码合并在一起然后编译运行时,却发现流水灯在工作,计时器却停止了工作。经过仔细排查,发现简单地将代码合并会导致单片机陷入流水灯的工作循环,而没有进入计时器的工作进程。经过一番思考,我们将流水灯的代码加以修改写入计时器的工作循环中,解决了计时器不工作的问题。 3.但是新问题又出现了,我们发现按下计时器的暂停按钮后,会导致流水灯也停止工作。经过小组的讨论,我们决定在中断程序函数里面加上流水灯工作的代码并加以修改使其能够在中断函数里面运行。经过多次调试,终于实现了计时器的计时、暂停、继续功能,并且流水灯能够一直工作不受计时器的暂停影响。 4.在调整计时器计时精读的过程中,我们发现本实验中使用的ATmega16频率不是16MHz,也不是1MHz,为了尽可能的实现秒表计时,试验了很多的数字组合,最后达到了30秒误差1秒的精确度,当然还可以达到更高的精确度。 电子琴设计与实现阶段: 1.在硬件连接的时候,没想到LCD液晶显示屏的每根管脚都需要 第27页 北京邮电大学信息与通信工程学院 连接,导致在进行程序下载验证的时候,显示屏总是不能显示出应有的文字,后来参考了相关文档资料发现是V0这个管脚也必须接地才可以,解决了问题。 2.在安插矩阵键盘的时候,费了很多功夫,首先,不知道哪根管脚是行线哪根管脚是列线,其次不知道所连接的端口的输出输入模式应该怎么设置,导致设计过程受阻,后来观看了视频并查阅相关资料解决了此问题。 3.在初步完成电子琴的弹奏功能程序时,我们开始对单片机进行测试,发现在按下数次按钮后单片机就会卡死,我们仔细检查了弹奏功能的代码,经过多次排查,我们小组最终将键盘检测函数key_in()和弹奏函数play()两条语句的执行顺序交换,以达到比较好的逻辑顺序效果,提高了程序的稳定性,把多次按下按钮会导致单片机卡死的问题给基本解决了。 4.播放音乐功能也是我们设想的一个重要功能。刚开始,按照我们的思路去实现该功能时,在测试时播放音乐是实现了,但是暂停和继续功能都没能够实现。我们小组对代码进行重新检查,检查出几个比较严重的逻辑错误。我们重新调整播放音乐函数的判断语句和循环结构,并加入一个pause变量来标志暂停状态,在按下暂停按钮时将pause标志为0,使程序退出播放循环。经过一番努力,我们顺利完成了暂停播放和继续播放的功能。 5.录制音乐功能的实现是整个实验过程里面最为困难的一环。我第28页 北京邮电大学信息与通信工程学院 们预先设想的实现方法是每按下一个按键,将对应音阶的数字和节拍存入数组中,然后录制完成后自动播放录制的音乐。但是,我们实际测试时发现录制的音乐并没有能自动播放。经过小组讨论,我们决定将录制好的音乐在播放音乐模式里面播放,根据这个思路,我们将录制的音乐对应的数组用播放音乐函数进行播放,成功地实现了录制音乐并播放的功能。 七、心得体会: 为期9天的单片机小学期圆满结束,不得不说,我从中学到了很多知识,从一个对单片机一窍不通的小白,蜕变成了一个熟悉单片机各个端口的作用和使用方法、懂得如何编程实现相应功能的技术人员。 在这九天中,我们通过观看老师给的视频资料和文档,加上查阅的资料,独立完成了所布置的任务。由于有C++的基础,加上有硬件的端口以及相应设备的使用说明文档的帮助,因此在我看来,在AVR Studio上进行C语言的编程从逻辑上比C++更容易理解,所需要的只是在编程时将端口的配置和逻辑函数结合到一起,实现编写的函数对单片机端口以及内部中断的控制。 第一天刚接到流水灯和数码管秒表计时器的任务的时候,内心真的十分激动,心想:实现了这个不就实现了街边广告牌上滚动的文字了么!但是一看到ATmega16芯片时还是傻了眼,心想:这么多的管脚都该怎么用啊?“视频里有”,同学告诉我,于是我就开始认真地 第29页 北京邮电大学信息与通信工程学院 研究起老师给的单片机教学视频和它的技术文档。由此发现,只需要将单片机的端口设置好,再套上一个循环结构,就可以实现流水灯的功能了。说做就做,于是最初的流水灯就实现了,可以一个一个依次循环点亮。那时真是好高兴!在做数码管的时候,由于不知道数码管的显示与管脚之间的关系,于是我先创建了一个数组,数组中的元素都是8位二进制数,其中只有一位是1,其余位都是0,如下: char a[8] = { 0b00000001,0b00000010,0b00000100,0b00001000,0b00010000,0b00100000,0b01000000,0b10000000 }; 然后我采用一个循环,将端口PORTX每次等于一个a[i],单步运行,找出了数码管的显示与管脚之间的对应关系。这样,之后的工作就轻松许多,只需要在该亮的位置将对应的PORTX端口置为1,就可以显示出自己想要的数字了。流水灯和数码管秒表计时器都分别完成了,最后只需将它们合在一起共同工作即可。不过这个过程也不是一帆风顺的,因为中断的概念我刚开始觉得很难理解,也不知道该将中断的有关函数放在代码的哪个位置,因此反复纠结反复研究视频和技术文档,持续了一段时间。后来我和同学决定将两份流水灯的程序,一份放在主程序main中,和秒表计时器的程序放在一起,即实现两者同时工作,且流水灯的闪烁间隔是一秒,另一份放在中断服务程序中SIGNAL()中,即实现按下按钮进入中断服务程序后,秒表计时器暂停 第30页 北京邮电大学信息与通信工程学院 工作,而流水依旧在依次循环闪烁。由此实现了流水灯和秒表计时器的独立工作,互不影响。 完成了基础题目的训练任务,我信心满满,迎来了我的下一个任务——电子琴。刚开始选题的时候觉得电子琴蛮有趣的,可以弹奏可以录音可以播放,而且原理看起来也比较简单。但是其实不是这样的,拿到元器件的时候,我发现,LCD液晶显示屏有16个管脚!矩阵键盘有8个管脚!但是有了前面对付ATmega16芯片的经验,我沉住气,查阅了有关LCD液晶显示屏和矩阵键盘的技术文档,以及一些经验之作,初步定下了键盘接到A端口,LCD的8个数据口接到B端口,D端口用于LCD的读写控制使能端以及扬声器的输入端。至此,硬件的连接工作就完成了,根据硬件的连接,我们开始了程序的编写。键盘检测函数、播放函数、液晶显示函数,弹奏功能,录制功能„„一步一步,一个完整的系统渐渐完善。由于有硬件,因此我们编写的程序可以马上下载到板子里验证效果,并及时修改,大大方便了我们排错和系统优化。期间由于JTAG下载器和AVR Studio总是出现连接失败的情况,浪费了我们许多宝贵的时间,个人总结原因是驱动可能用了一段时间会失效,解决办法是重新启动计算机,并将WINAVR这个环境卸载后再次装上,并且是装到与上一次不同的盘中,同时编译AVR程序的时候先接上JTAG下载器,试验了许多次,这个方法很管用,之后就没有出现该问题了。最终,电子琴的功能基本完成,并且达标,能实现弹奏、播放、录制、变速等最初设想的功能,但是可能还有一些小BUG没有调出来,导致电子琴在运行的过程中有不稳定的现象,第31页 北京邮电大学信息与通信工程学院 也不能排除是板子的问题。 最终,单片机小学期圆满结束了!9天的时间,完全的自己动手实验,不仅增加了自己对单片机的理解,提高了自己的编程能力,而且在这个过程中,我觉得团队的合作至关重要,自己连接的电路或者自己编写的程序,在自己看来都是对的,但是旁观者清,队友能很容易地帮你找出其中的错误;在和队友的讨论中,我能了解对方的想法和思路,多种想法和思路的碰撞能让我收获更多新的东西;组内的分工能让每个人都尽到自己的责任,发挥自己所长。 总之,这次单片机小学期是一次难忘的过程,一次丰富知识、提升技能的历程,也激起了我对单片机的兴趣,我今后会多多研究单片机,争取更上一层楼! 八、参考文献: 1.AVR C库函数介绍.pdf 2.AVR C语言开发入门指导.pdf 3.AVR单片机原理及应用.pdf 4.AVR高速嵌入式单片机原理及应用(修订版).pdf 5.深入浅出AVR单片机.pdf 6.1602液晶说明.pdf 7.AVR系统板说明.doc 8.cn_mega16-16L.pdf 9.EN_TC1602.pdf 第32页 北京邮电大学信息与通信工程学院 10.字符手册.pdf 11.百度百科《乐理》 12.AVR单片机软硬件设计视频教程-入门篇-第二讲-AVR硬件电路设计教程 13.AVR单片机软硬件设计视频教程-入门篇-第三讲-AVR开发基础知识 14.AVR单片机软硬件设计视频教程-入门篇-第四讲-C语言的流水灯验证 15.AVR单片机软硬件设计视频教程-入门篇-第五讲-按键与数码管的程序设计 16.AVR单片机软硬件设计视频教程-入门篇-第六讲-中断与定时器 九、意见与建议: 1.建议老师能稍微讲解一下实验室AVR软件的用法(视频中讲的是没有JTAG下载器的,与实验室不同,刚开始还以为是板子有问题); 2.实验室的元器件老旧,个别元器件特别是单片机芯片有问题之后,调BUG好久都不知道错在哪里,建议更新。 第33页第四篇:北邮小学期c++实验报告
第五篇:北邮小学期AVR单片机电子琴实验报告