通信工程专业课程设计基于SEP3203的电子琴模型文档格式.docx
《通信工程专业课程设计基于SEP3203的电子琴模型文档格式.docx》由会员分享,可在线阅读,更多相关《通信工程专业课程设计基于SEP3203的电子琴模型文档格式.docx(21页珍藏版)》请在冰豆网上搜索。
调试过程中,需要查看按键的状态,需要查看是否正确检测到了按键,是否能够给出正确的键码,所以增加了这一功能。
功能描述:
利用LED数码管,显示按下的按键。
2、总体设计
2.1基本功能使用的模块
第一部分包括两个主要软件模块和两个外围的硬件模块:
2.2加入附加功能之后的模块组成
加入第二部分后的框图如下:
增加了一个新的程序模块和硬件模块。
程序运行的流程如下:
第二部分是对第一部分的完善和改进,但是在程序执行的流程中,仅仅在处理键码信息步骤,增加了“根据键码控制点亮数码管”这一部分。
3、可行性分析
该系统包含了输入、处理、输出三个部分。
其中,输入即为按键,使用矩阵键盘完成,实际的电子琴键盘含有更多的按键,但由于这仅仅是模型,所以,不需要使用那么多的按键。
处理部分是基于SEP3203的程序,通过输入的按键信息,修改寄存器的相应位,使得最终能够输出相应的PWM波,控制蜂鸣器响。
此外,还可以根据输入的按键信息,将输入的按键值显示在LED七段数码管上。
输出即为蜂鸣器和LED数码管,硬件电路已经在实验箱上连接好。
4、详细设计与分工日志
4.1.1器件原理
采用矩阵键盘,矩阵键盘有两种驱动方式:
一种是行扫描法,另一种是高低电平翻转法。
这里采用了行扫描法。
包括两个步骤:
判断键盘中有无键按下:
将全部行线Y0-Y3置低电平,然后检测列线的状态。
只要有一列的电平为低,则表示键盘中有键被按下,而且闭合的键位于低电平线与4根行线相交叉的4个按键之中。
若所有列线均为高电平,则键盘中无键按下。
判断闭合键所在的位置:
在确认有键按下后,即可进入确定具体闭合键的过程。
其方法是:
依次将行线置为低电平,即在置某根行线为低电平时,其它线为高电平。
在确定某根行线位置为低电平后,再逐行检测各列线的电平状态。
若某列为低,则该列线与置为低电平的行线交叉处的按键就是闭合的按键。
4.1.2相关代码
键盘中断的调用:
voidkeyhandler(void)//键盘中断处理函数
{
mask_irq(INT_EXT15);
//屏蔽键盘中断
//找到哪个键被按下,并打印
findkey();
unmask_irq(INT_EXT15);
//打开键盘中断
//这里的键盘中断处理的不仅仅是打印键值,
//还包括了重置PWM波发出的寄存器.
}
寻找被按下的按键:
voidfindkey(void)
U32KEYTIME=0x100000;
U32KEYTIME2=0x10000;
U32dly=0;
U16intstatus;
U8col,row;
//清除garfield中断源
*(RP)PORTH_INTRCLR|=0x8;
//记录下中断状态值,用于判断行地址
intstatus=rucb(0x62);
wucb(0x60,0);
//屏蔽UCB中断
if(!
(intstatus&
KEY_ROW_MASK)){
wucb(0x60,KEY_ROW_MASK);
return;
}
wucb(0x62,KEY_ROW_MASK);
row=intstatus&
KEY_ROW_MASK;
{
inti;
for(i=0;
i<
KEY_ROW_NUM;
i++)
if((row&
(1<
<
i))){
row=i;
break;
}
{
//找到对应得列
U16line=KEY_COL_MSB;
U16tmp;
col=KEY_COL_NUM;
while(col--){
//利用按键拉低相应得中断线
tmp=rucb(0x5a);
tmp=((tmp|KEY_COL_MASK)&
(~line)|KEY_ROW_MASK);
wucb(0x5a,tmp);
if((rucb(0x5a)&
intstatus)==0)
break;
line>
>
=1;
key_code=key_code_map[row][col];
//利用行列取得键码
b=b_code_map[row][col];
l=l_code_map[row][col];
pwm();
led();
if(key_code){//排除无效按键
wucb(0x5a,0);
//所有的列线置0
while(((rucb(0x5a))&
intstatus)==0){
//按键还未抬起
inti;
if(dly++==0){
for(i=0;
KEYTIME;
i++);
//在等待较长时间后,打印键值
DBG_Printf("
key=%c\n"
key_code);
}else{
KEYTIME2;
}
//下一次的判断周期
wucb(0x5a,0x0);
//再次ucb行清除中断状态,因为判断
wucb(0x62,KEY_ROW_MASK);
4.2发声器件
4.2.1PWM波输出原理
发声器件即为蜂鸣器,通过控制输出的PWM波的波形来控制发出的声音。
PWM波包括两个参数:
频率和占空比。
控制频率,可以控制发出声音的频率;
控制占空比,可以控制声音的响度。
在所用开发板GE01中,PWM模块中包含了2个PWM通道,彼此相互独立。
相关寄存器:
地址
寄存器名称
宽度
描述
复位值
0x1000c000
PWMC(TCTL)
32
PWM_1控制寄存器
0000_4000
0x1000c004
PWMS(TPRER)
PWM_1采样寄存器
0000_0000
0x1000c008
PWMP(TCMP)
PWM_1周期寄存器
0000_FFFE
0x1000c00c
PWMCNT(TCR)
PWM_1计数器寄存器
高16位为保留位
bit15——中断请求状态(0=FIFO满、1=FIFO不满)
bit14——中断请求使能(0=不使能(缺省)、1=PWM中断使能)
bit13——FIFO有效状态(0=FIFO满、1=FIFO不满)
bit12——PWM使能(0=PWM不使能、1=PWM使能)
bit11-10——选择playback模式下采样被重放的次数(00=没重复,01=播放两次、10=重复3次(播放4次)、11=重复7次(播放8次))
bit9-0(PRESCALER)——用来提供分频因子的(prescaler+1),对系统时钟进行分频。
缺省值是0。
1=2分频、2=4分频…
Bit15-0——PWMS采样值PWM采样寄存器是FIFO的输入。
此值和一个自由计数的计数器比较。
当大于计数器值时PWM输出为高,反之输出为低。
bit15-0——Period值/定时值在tone模式时,其用于设置计数器的复位值。
计数器计数到此值就复位一次,PWM输出也相应翻转一次。
bit15-0——当前的计数值它保存着自由计数的计数器的当前值。
依据上述寄存器各位的功能,通过合适的赋值便可以实现本实验所要求的基本功能。
主函数:
intmain(void)
{
if(E_OK!
=ModulePwm())
errorinpwmlab\n"
);
successinpwmlab\n"
while
(1);
return1;
STATUS模块初始化
STATUSModulePwm(void)
/*systeminitialized*/
system_init();
/*固定周期发音实验*/
#ifdefFIX_LAB
sample=0X5555DDDD;
if(E_OK!
=set_fix())
returnE_CTX;
#endif
/*设置周期发音实验*/
#ifdefUNFIX_LAB
=set_unfix(0Xf0))
run_pwm();
returnE_OK;
STATUSset_fix(void)
#ifdefGE01MB
*(RP)(PWM2_P)=0X0000FFFE;
#else
*(RP)(PWM1_P)=0X0000FFFE;
#endif
STATUSset_unfix(U8prescaler)
*(RP)(PWM2_C)=0X00001c00|(U32)prescaler;
*(RP)(PWM1_C)=0X00001c00|(U32)prescaler;
voidrun_pwm(void)
#ifdefFIX_LAB
#ifdefGE01MB
*(RP)(PWM2_C)|=0X00001CFF;
while
(1)
*(RP)(PWM2_S)=0X0000FFFF&
sample;
*(RP)(PWM2_S)=sample>
16;
#else
*(RP)(PWM1_C)|=0X00001CFF;
*(RP)(PWM1_S)=0X0000FFFF&
*(RP)(PWM1_S)=sample>
#endif
#ifdefUNFIX_LAB
*(RP)(PWM2_P)=0X00008ffe;
*(RP)(PWM1_P)=0X00008ffe;
return;
4.3LED显示
4.3.1动态显示原理
图十二动态显示LED的连接
由图,六个数码管每个都有一个选择信号,当置“1”时,该数码管的相关段亮。
对于此动态显示电路,有六个数码管,使用的频率由定时器产生,为500Hz,即周期为2ms。
此外,将每个数码管相同的段连在一起到段数据线上。
在第一个2ms,第一个数码管允许点亮,同时,需要点亮的段置“1”,在这2ms内,只有第一个数码管亮。
在第二个2ms,第二个数码管允许点亮,同时,需要点亮的段置“1”,在这2ms内,只有第二个数码管亮……如此,每个2ms只有一个数码管亮,所以,不会相互影响,导致显示不清。
而且,由于每个数码管刷新的周期非常小,本实验中为12ms,小于人眼的分辨时间。
所以,看起来,好像同时亮了。
4.3.2定时器输出
如图,定时器用到的IO端口位置如下:
TIMER是片内集成的32bit定时器,该模块共包括4个通道,每个通道包括一个32位计数器,以及一个加载计数寄存器。
计数器的重新加载、计数使能以及中断的产生受到控制寄存器的各个控制位的控制。
定时器的工作原理是:
首先要设定定时器的初值,然后启动定时器。
一旦启动,定时器就从初值开始累计增加或减小(在实验所用的芯片中是递减)。
每增加或减小一个数需要特定的时间,这个特定的时间是累加指令的机器周期,是由处理器的晶振的频率(本实验中晶振的频率是75MHz)决定的。
计数值从初值计至0时,TIMER模块发出中断,然后计数器跳至用户配置的值,重复递增或递减过程。
在中断寄存器允许的情况下处理器响应中断,通过中断服务程序执行特定的任务。
TIMER中的一个通道的功能结构如下图:
TIMER有两种工作模式:
正常工作模式
TIMER在正常工作模式时,当通道使能后计数器锁存加载计数寄存器的值,然后在系统时钟的驱动下递减计数。
当计数到零时,产生一个计数到零的标志用于设置相应的中断标志位,若中断未被屏蔽则产生中断;
同时,计数器重新锁存加载计数寄存器的值开始一个新的计数周期。
自由工作模式
TIMER在自由工作模式时,当通道使能后计数器锁存加载计数寄存器的值,然后在系统时钟的驱动下递减计数。
同时,计数器加载数据32’HFFFFFFFF开始一个新的计数周期。
TIMER中四个通道的中断都可以通过各自的控制寄存器屏蔽。
任意一个通道计数完毕都会产生一个中断。
TIMER的中断输出是四个中断源的线或。
查询中断源有两种方法:
(一)查询各个通道的中断屏蔽状态寄存器是否被置1;
(二)查询TIMER中断屏蔽状态寄存器,看各个通道相应位是否被置1。
清除中断的方法也有两种:
(一)读各个通道的中断状态清除寄存器,相应通道的中断会被清除;
(二)读TIMER中断状态清除寄存器,所有通道的中断都会被清除。
查询到中断源后,如果不重新配置加载计数寄存器,计数器会按照原来的配置继续计数,直到计数完毕再发出中断。
如果要重新配置,清完中断后应该先将计数器关闭(disable),然后配置新的计数值,再将计数器打开(enable)。
4.3.3相关寄存器
名称
读/写
0x10003000
T1LCR
R/W
通道1加载计数寄存器
32’b0
0x10003004
T1CCR
R
通道1当前计数值寄存器
0x10003008
T1CR
W/R
通道1控制寄存器
0x1000300C
T1ISCR
通道1中断状态清除寄存器
0x10003010
T1IMSR
通道1中断屏蔽状态寄存器
0x10003014
T2LCR
通道2加载计数寄存器
0x10003018
T2CCR
通道2当前计数值寄存器
0x1000301C
T2CR
通道2控制寄存器
0x10003020
T2ISCR
通道2中断状态清除寄存器
0x10003024
T2IMSR
通道2中断屏蔽状态寄存器
0x10003028
T3LCR
通道3加载计数寄存器
0x1000302C
T3CCR
通道3当前计数值寄存器
0x10003030
T3CR
通道3控制寄存器
0x10003034
T3ISCR
通道3中断状态清除寄存器
0x10003038
T3IMSR
通道3中断屏蔽状态寄存器
0x1000303C
T4LCR
通道4加载计数寄存器
0x10003040
T4CCR
通道4当前计数值寄存器
0x10003044
T4CR
通道4控制寄存器
0x10003048
T4ISCR
通道4中断状态清除寄存器
0x1000304C
T4IMSR
通道4中断屏蔽状态寄存器
0x100030A0
TIMSR
TIMER中断屏蔽状态寄存器
0x100030A4
TISCR
TIMER中断状态清除寄存器
0x100030A8
TISR
TIMER中断状态寄存器
调用LED显示:
intled(void)
char*s1,*s2,*s3;
s1="
ThereisnoLEDonGE00\n"
;
s2="
ErrorinLedlab!
!
\n"
s3="
SucceededinLedlab!
#ifdefGE00
DBG_Printf(s1);
return;
#else
=ModuleLed())
//PRINT(s2);
else//PRINT(s3);
DBG_Printf("
//while
(1);
//returnE_OK;
LED显示模块:
STATUSModuleLed(void)
//系统初始化
/*GPIOinitialized*/
GPIO_Init();
/*ledlabbody*/
if(key_code=='
1'
)
{
LedDisPlay(display1);
//显示用户设定的内容
2'
LedDisPlay(display2);
3'
)
{
LedDisPlay(display3);
4'
LedDisPlay(display4);
5'
LedDisPlay(display5);
6'
LedDisPlay(display6);
7'
LedDisPlay(display7);
8'
LedDisPlay(display8);
9'
LedDisPlay(display9);
LED显示程序:
voidLedDisPlay(U8data[])
U8*SegData;
//十六进制数字翻译成7段译码值
Hex2Seg(data,SegData);
LedOut(SegData,10);
//在led上显示
翻译码值
VoidHex2Seg(U8hexdata[],U8*p)
inti;
for(i=0;
i<
9;
i++)
//将要显示的数值,通过查找7段译码表翻译成码值
*p++=SEGMENT[hexdata[i]];
LED输出
voidLedOut(U8data[],U32times)
intSegment_i,Bit_i,i;
U8Segment_Data;
U8tempData;
for(i=0;
times;
for(Bit_i=0;
Bit_i<
8;
Bit_i++)
//依次串行导入八个led的显示内容
{
//获得一个led显示的码值
Segment_Data=*(data+Bit_i);
//将7段译码值依次串行输入到GPIO口中
for(Segment_i=0;
Segment_i<
8;
Segment_i++)
{
//得到一段发光管的译码值
tempData=(Segment_Data>
Segment_i)&
0x1;
//写入74HC595的输入端,同时拉低PD3
*(RP)PORTD_