专业: 电气工程及其自动化 姓名: 学号: 实验报告
日期: 2014.11.27 地点: 课程名称: 微机原理及其应用 指导老师: 徐习东 成绩: 实验名称: 时钟控制实验 实验类型: 同组学生姓名:
目录
1 实验内容与要求 …………………………………………………………(2) 2 实验原理 …………………………………………………………………(2)
2.1 数码管显示原理 ………………………………………………………(2)
2.2 键盘接口…………………………………………………………………(3)
3.1主程序设计流程图……………………………………………………… (5) 3.2 时间自增变化程序设计流程图………………………………………… (6) 3.3 调时程序设计流程图…………………………………………………… (6) 3.4 程序段设计与分析……………………………………………………… (7)
3 程序设计与分析 …………………………………………………………(5)
4 实验思考 … ………………………………………………………………(17)
1 / 18
DSP时钟控制实验报告
一、实验内容与要求
1、利用TMS3202812实验目标板上的八段数码管设计显示具有“时、分、秒”功能的数字钟。
2、用按键控制时、分、秒时间的增减。
3、通过实验,掌握TMS2812通用输入/输出管脚直接控制外围设备的方法。 4、了解键盘、发光二极管的控制编程方法。
二、实验原理
1、数码管显示原理
扫描式键盘数码管接口如图:
图1 扫描式键盘数码管接口
DSP的SPI控制串转并芯片如图
2 / 18
DSP时钟控制实验报告
图2 SPI串转并
8个串转并芯片级联在一起,级联多口控制,由锁存信号控制暂态余辉;写入一个字符会引起一串变化。
为控制8个LED,建立8个元素的显示数组;如果要改显示内容,先改显示数组,然后再调用循环传送程序。
2、键盘接口
键盘一端接地,一端上拉高电平;键盘的闭合,断开会产生电平变化;键盘经三态隔离芯片接入DSP;三态芯片的导通由KEYA、KEYB两线控制;键盘信号进入GPIOB(8~15);
3 / 18
DSP时钟控制实验报告
图3 键盘接口
其中两个键盘控制芯片U24和U26分别对应键盘K1-K8和K9-K16,片选控制取决于键盘输入片选信号KEYA与KEYB,通过74LVC138译码器控制,具体定义如下:
功能/名称 键盘输入片选信号KEYA 键盘输入片选信号KEYB 扩展的输入片选LEDA 扩展的输入片选LEDB LCD CSA片选 LCD CSB片选 74LVC138译码器控制信号 GPIOE2 0 0 0 0 1 1 表1 片选控制
外部扩展片选电路如下:
GPIOE1 0 0 1 1 0 0 GPIOE0 0 1 0 1 0 1 4 / 18
DSP时钟控制实验报告
图4 外部扩展片选电路
三、程序设计与分析 1、主程序设计流程图 系统初始化初始化中断向量表关闭中断初始化CPU定时器初始化SPI扫描按键初始化I/O键值转字符数码管清零 5 / 18
显示时钟变化 DSP时钟控制实验报告
2、时间自增变化程序设计流程图 开始显示初始化启动定时器3、调时程序设计流程图
CpuTimer0.InterruptCount++N判断是否CpuTimer0.InterruptCount==10Y秒加1N秒加到60Y,秒清零分加1N分加到60Y,分清零时加1N时加到24Y时清零 6 / 18
DSP时钟控制实验报告 开始 显示初始化启动定时器CpuTimer0.InterruptCount++YY按下K14(D)N秒选定YY按下K11(A)N秒减1按下K15(E)NN分选定按下K11(A)N分减1按下K12(B)按下K16(F)Y按下K12(B)秒加1时选定Y秒加到60时减1按下K11(A)N分加1Y,秒清零按下K12(B)分加到60时加1Y,分清零时加到24时清零 4、程序段设计与分析 1 程序段○
#define K1 0xFEFF #define K2 0xFDFF ……
#define K16 0x7FFF
该程序段运用简单宏定义的方式将一个较长的16位二进制常量用一个较短的标识符Kn来代替(0xFFFE用K1代替、0xFFFD用K2代替等),Kn对应于键盘的K1-K16,具有实际意义。这样在之后程序的编写中便于书写和表达,具体程序见下面的分析。
7 / 18
DSP时钟控制实验报告
2 程序段○
Uint16 KeyReg1; Uint32 i = 0; Uint16 Ctrl; int16 Sec=0; int16 Min=0; int16 Hor=0; Uint16 Light[8];
int LEDCode[30]={0xC000,0xF900,0xA400,0xB000,0x9900,0x9200,0x8200, 0xF800,0x8000,0x9000,0x8800,0x8300,0xC600,0xA100,0x8600,0x8E00, 0x8c00,0xbf00,0xa700,0xff00,0x4000,0x7900,0x2400,0x3000, 0x1900,0x1200,0x0200,0x7800,0x0000,0x1000};
在F2812开发中,为了方便,将常用的数据类型重新定义如下:
int int16 long int32 unsigned int Uint16 unsigned long Uint32 float float32 long double float64
表2 DSP数据类型定义
定义unsigned int型变量 KeyReg1用于读键值;定义unsigned long型变量i 作为后面循环程序中的循环变量;定义unsigned int型变量Ctrl,用于记录按下
的是哪个键,参与控制调时子程序;定义int型变量Sec、Min、Hor分别作为时钟秒、分、时的在调整时的数值,参与调时和时钟显示;定义unsigned int型数组Light[8],用于关联六位时钟和数码关显示屏上的两个‘一’,同时作为时钟秒、分、时新数值的十位与个位数据寄存器,参与调时,具体操作是在LEDCode[30]中寻找到相应的字形码并通过下面叙述的display函数将结果信息传入到SPI数据发送缓冲器SPITXBUF中,在数码管显示屏上进行显示。这些变量的具体作用及其意义相见下面针对各个程序段的叙述。
3 程序段○
void spi_intial() {
SpiaRegs.SPICCR.all = 0x0047; SpiaRegs.SPICTL.all = 0x0006; SpiaRegs.SPIBRR = 0x007F;
SpiaRegs.SPICCR.all = SpiaRegs.SPICCR.all | 0x0080; EALLOW;
8 / 18
DSP时钟控制实验报告
GpioMuxRegs.GPFMUX.all = 0x000F; EDIS; }
void spi_intial()函数用于初始化串行外设接口SPI。 设置SPI配置控制寄存器SPICCR的位3-0为0111,使在一个移位序列中每个字符的字符长度为8位,即串行输入时对应每一个八段数码管的信息;
设置SPI配置控制寄存器SPICCR的位6为1,使数据在始终下降沿输出、上升沿输入,当SPI无数据发送时,SPICLK为高电平;
设置SPI操作控制寄存器SPICTL的位0为0,禁止SPI的接收/发送中断; 设置SPI操作控制寄存器SPICTL的位1为1,使能发送且主控制器的SPISTE引脚置为低电平;
设置SPI操作控制寄存器SPICTL的位2为1,使SPI被配置为主模式; 设置SPI操作控制寄存器SPICTL的位3为0,使SPI时钟相位为正常时钟方式,且时钟边沿有效;
设置SPI波特率设置寄存器SPIBRR的位6-0均为1,设定波特率,使
LSPCLK37.5MHzSPICLK0.29106b/s
SPIBRR1128设置SPI配置控制寄存器SPICCR的位7为1(与‘1’进行或逻辑操作),使SPI退出复位状态,准备接收或发送新的字符。
设置模式寄存器GPFMUX低四位为1,配置为对应SPI的专用外设功能。
void gpio_init() {
EALLOW; GpioMuxRegs.GPAMUX.bit.TDIRA_GPIOA11=0; GpioMuxRegs.GPADIR.bit.GPIOA11=1;
GpioMuxRegs.GPEMUX.all = GpioMuxRegs.GPEMUX.all & 0xfff8; GpioMuxRegs.GPEDIR.all = GpioMuxRegs.GPEDIR.all | 0x0007; GpioMuxRegs.GPBMUX.all = GpioMuxRegs.GPBMUX.all&0x00ff; EDIS;
GpioDataRegs.GPADAT.bit.GPIOA11 = 0; }
void gpio_init()
函数用于初始化GPIO口。
设置模式寄存器GPAMUX的相关位为0,是GPIOA11引脚配置为通用数字I/O模式;
设置方向寄存器GPADIR的相关位为1,使GPIOA11引脚配置为输出; 设置模式寄存器GPEMUX的低3位均为0,使GPIOE低3位引脚配置为通用数字I/O模式;
9 / 18
DSP时钟控制实验报告
设置方向寄存器GPEDIR的低3位均为1,使GPIOE低3位引脚配置为输出;
设置模式寄存器GPBMUX的高8位均为0,使GPIOB的高8位引脚配置为通用数字I/O模式;
设置数据寄存器GPADAT的相关位为0,使GPIOA11引脚为低电平关闭锁存,使74HC595的SPISIMO引脚能够接收SPITXBUF中的信息。
系统初始化、外设时钟初始化、看门狗初始化、PIE初始化等调用DSP281x_SysCtrl.c、DSP281x_PieCtrl.c等文件。
4 程序段○
int Keyscan(void)
{
EALLOW;
GpioMuxRegs.GPBDIR.all = GpioMuxRegs.GPBDIR.all & 0x00ff; EDIS;
GpioDataRegs.GPEDAT.all = 0xfff9; for (i=0; i<100; i++){}
if ((GpioDataRegs.GPBDAT.all | 0x00ff) != 0xffff) {
for (i=0; i<30000; i++){}
if ((GpioDataRegs.GPBDAT.all | 0x00ff) != 0xffff) { KeyReg1 = GpioDataRegs.GPBDAT.all ; while ((GpioDataRegs.GPBDAT.all|0x00ff)!=0xffff)
{
GpioDataRegs.GPDDAT.bit.GPIOD1 =
!GpioDataRegs.GPDDAT.bit.GPIOD1; for (i=0; i<1000; i++){} }
return (1); } }
return (0);
}
int Keyscan (void)函数用于扫描按键K9-K16。
设置方向寄存器GPBDIR的高8位为0,使GPIOB8~GPIOB15被配置为输入;
10 / 18
DSP时钟控制实验报告
设置数据寄存器GPEDAT的低3位为001,选通KEY高8位; if语段:
if ((GpioDataRegs.GPBDAT.all | 0x00ff) != 0xffff) {
for (i=0; i<30000; i++){}
if ((GpioDataRegs.GPBDAT.all | 0x00ff) != 0xffff) { KeyReg1 = GpioDataRegs.GPBDAT.all ; while ((GpioDataRegs.GPBDAT.all|0x00ff)!=0xffff)
{
GpioDataRegs.GPDDAT.bit.GPIOD1 =
!GpioDataRegs.GPDDAT.bit.GPIOD1; for (i=0; i<1000; i++){} }
return (1); }
用于判断K9-K16是否有按键按下,没有按键按下时,GPBDAT高8位均为高电平1,若有按键按下则GPBDAT高8位出现低电平0,与0x00ff进行或逻辑运算后不等于0xffff(GpioDataRegs.GPBDAT.all | 0x00ff) != 0xffff),初步判断有按键按下后,进行去抖动再确认,具体操作为延时一小段时间,再次检测是否有按键按下,如果再次判断有按键按下,则确认有按键按下,记录键值(KeyReg1 = GpioDataRegs.GPBDAT.all),否则视为按键抖动,不进行进一步的操作。
按键去抖动操作流程图如下:
11 / 18
DSP时钟控制实验报告
KeyScan有键按下?NY延时N有键按下?Y记录键值return(1)
5 程序段○
void KeyFunction (unsigned int KeyReg1) {
switch(KeyReg1) { case K14: Ctrl=K14; break; case K15: Ctrl=K15; break; case K16: Ctrl=K16; break; case K11:
if(Ctrl==K14)
{
Sec--; if(Sec==-1) {Sec=59;} Light[7]=LEDCode[Sec%10]; Light[6]=LEDCode[Sec/10]; } if(Ctrl==K15) {
12 / 18
DSP时钟控制实验报告
Min--;
if(Min==-1) {Min=59; }
Light[4]=LEDCode[Min%10]; Light[3]=LEDCode[Min/10];
}
if(Ctrl==K16) {
Hor--; if(Hor==-1) {Hor=23; }
Light[1]=LEDCode[Hor%10]; Light[0]=LEDCode[Hor/10];
} break; case K12: if(Ctrl==K14) { Sec++; if(Sec==60) {Sec=0;} Light[7]=LEDCode[Sec%10]; Light[6]=LEDCode[Sec/10]; } if(Ctrl==K15) { Min++; if(Min==60) {Min=0; }
Light[4]=LEDCode[Min%10]; Light[3]=LEDCode[Min/10];
} if(Ctrl==K16) { Hor++; if(Hor==24) {Hor=0; }
Light[1]=LEDCode[Hor%10]; Light[0]=LEDCode[Hor/10];
13 / 18
DSP时钟控制实验报告
} }
} break; default: break;
void KeyFunction(unsigned int KeyReg1)函数用于控制调整时间。
由按键扫描子程序int Keyscan(void)得到按下的按键值,(KeyReg1 = GpioDataRegs.GPBDAT.all),用switch语句给定每个特定按键所对应实现的调时功能(如按下K14,则选定秒,键值寄存变量Ctrl=K14,接下来若按下K11,则秒加1,若按下K12,则秒减1),调整时间后,由Light[n]与八段数码管关联,在LEDCode[30]中找到对应的字形码,显示在数码管上。
6 程序段○
void display () {
GpioDataRegs.GPADAT.bit.GPIOA11 = 0; for(i=0;i<8;i++) { SpiaRegs.SPITXBUF = Light[i]; while(SpiaRegs.SPISTS.bit.INT_FLAG != 1){} SpiaRegs.SPIRXBUF = SpiaRegs.SPIRXBUF; } GpioDataRegs.GPADAT.bit.GPIOA11 = 1; for(i=0;i<10;i++){} } void display ()用于时钟的八段数码管显示。
设置数据寄存器GPADAT的GPIOA11位为0,给LACK信号一个低电平,为后面的锁存做准备;
SpiaRegs.SPITXBUF = Light[i]用于给数码管送入数据,一旦把数据Light[i]送给SPITXBUF,SPITXBUF就把数据发送出去;
设置数据寄存器GPADAT的GPIOA11位为1,给LACK信号一个高电平,为锁存74HC595。
7 程序段○
interrupt void cpu_timer0_isr(void) {
CpuTimer0.InterruptCount++;
if(CpuTimer0.InterruptCount==10)
14 / 18
DSP时钟控制实验报告
{ Sec++; CpuTimer0.InterruptCount=0; if(Sec==60) { Min++; Sec=0; if(Min==60) { Hor++; Min=0; if(Hor==24) { Hor=0; } Light[1]=LEDCode[Hor%10]; Light[0]=LEDCode[Hor/10]; } Light[4]=LEDCode[Min%10]; Light[3]=LEDCode[Min/10]; } Light[7]=LEDCode[Sec%10]; Light[6]=LEDCode[Sec/10]; display(); }
PieCtrlRegs.PIEACK.all=PIEACK_GROUP1; }
interrupt void cpu_timer0_isr(void)为中断函数,参与时钟无操作时的自加和数码管显示。
定时器开始计时后中断计数寄存器累加CpuTimer0.InterruptCount++;
当中断计数寄存器CpuTimer0.InterruptCount= 10,即0.1s*10=1s,产生一个中断,此时秒加1,若秒溢出(Sec==60)则分加1(Min++),若分溢出(Min==60)则时加1(Hor++),若时溢出(Hor==24)则时分秒清零。时间改变的同时在数码管上显示。
8 程序段○
void main(void) {
InitSysCtrl();
15 / 18
DSP时钟控制实验报告
InitPieVectTable(); EALLOW;
PieVectTable.TINT0=&cpu_timer0_isr; EDIS;
InitCpuTimers();
ConfigCpuTimer(&CpuTimer0,150,100000); StartCpuTimer0(); IER |=M_INT1;
PieCtrlRegs.PIEIER1.bit.INTx7=1; EINT; while(1){ if (Keyscan2() == 1) { KeyFunction2(KeyReg1); display(); } } }
16 / 18
DINT; spi_intial(); gpio_init(); DINT; IER = 0x0000; IFR = 0x0000; for(i=0;i<8;i++) { SpiaRegs.SPITXBUF =LEDCode[0]; while(SpiaRegs.SPISTS.bit.INT_FLAG != 1){} SpiaRegs.SPIRXBUF = SpiaRegs.SPIRXBUF; } GpioDataRegs.GPADAT.bit.GPIOA11=1; for(i=0; i<10; i++){} for(i=0;i<8;i++) { Light[i]=LEDCode[0]; }
Light[2]=LEDCode[17]; Light[5]=LEDCode[17];
DSP时钟控制实验报告
void main(void)为程序主函数。
初始化函数InitSysCtrl()、InitPieVectTable()、InitCpuTimers()、StartCpuTimer0()、ConfigCpuTimer()调用相关C文件,其中ConfigCpuTimer(&CpuTimer0,150,100000)为设置周期寄存器,150MHz ,周期为1*100000us=1s,
IER = 0x0000; IFR = 0x0000;分别表示关闭外围中断和清除中断标志; 开始给数码管送入数据SpiaRegs.SPITXBUF =LEDCode[0],即使时钟时分秒均从0开始显示;
设置数据寄存器GPADAT的GPIOA11位为1,即给LACK信号一个高电平,为锁存74HC595;
启动定时器StartCpuTimer0(),当计数器减到0时产生一个定时器中断信号,(一个中断脉冲)。
CPU定时器0中断流程图如下: 开始初始化DSP时钟初始化GPIO初始化中断向量初始化定时器等待中断产生
四、实验思考
这次实验是按键控制时钟实验,硬件上涉及了键盘和数码管,程序设计用到了CPU定时器、PIE中断、串行外设接口SPI等知识,通过这次实验的练习,我了解了SPI串行外设接口在数据传输方面的知识和软件设置方法,进一步理解了CPU定时器和PIE的工作流程以及成功实现中断的必要步骤等,受益匪浅。
那么要成功实现中断,需要做哪些事呢?要知道CPU接到中断请求,并发
17 / 18
DSP时钟控制实验报告
现可以去响应时,就得暂停正在执行的程序,转而去响应中断程序,但是此时,它必须得做一些准备工作,以便于执行完中断程序后回过头来还能找到原来的地方原来的状态,CPU会将相应的IFR位进行清除,EALLOW也被清除,INTM被置位,即不能响应其他终中断,等于CPU向其他中断发出了通知,现在正在忙,没有时间处理别的请求,得等到处理完手上的中断之后才能再来处理。然后CPU会存储返回地址并自动保存相关信息,例如将正在处理的数据放入堆栈等。做好这些准备工作之后,CPU会从PIE向量表中取出对应的中断向量ISR,从而转去执行中断服务子程序。
另外由于对SPITXBUF和SPIRXBUF区分不清,在编写程序时,多处把SPITXBUF写成SPIRXBUF,导致运行不正确。现将SPI工作在标准SPI模式下(FIFO未使能)时,数据交换过程总结如下。
串行发送缓冲寄存器SPITXBUF用于发送数据。首先,通过程序向发送缓冲寄存器SPITXBUF中写入数据,如果此时SPIDAT寄存器为空,则SPITXBUF将需要发送的完整数据传输给SPIDAT,数据在SPITXBUF寄存器和SPIDAT寄存器中左对齐存放,即从高位开始存储。SPIDAT每经过一个时钟脉冲,完成一位数据的发送或接收。假设在时钟上升沿时,SPIDAT将数据最高位发送出去,然后将剩余数据左移一位,接下来在时钟脉冲下降沿时,SPIDAT锁存一位数据,并保存至其最低位,在发送完指定位数的数据后,SPIDAT寄存器将其内部的数据发送给接收缓冲寄存器SPIRXBUF,等待CPU读取。数据在SPIRXBUF中存放是右对齐的,即从低位开始存储。在标准SPI模式下,接收操作支持双缓冲,也就是在新的接收操作启动时,CPU可以暂时不读取SPIRXBUF中接收到的数据,但是在新的接收操作完成之前,必须读取SPIRXBUF,否则将会覆盖原来接收到的数据。发送操作同样也支持双缓冲功能。
这次程序相比于以前简单地点亮几个发光管的程序来说,无论从程序的大小还是程序设计思想上,都更进了一步。尽管一些重要的程序段比如按键扫描程序、键盘功能设计程序、数码管显示程序还包括一些必要的定义等等都在老师的指导下帮助我们更好地理解,并且给了我们一些相关子程序的范例,但是再深刻理解以及进一步熟悉DSP2812芯片内部结构和开发板结构的基础上,自己编写相关模块功能,完成这个程序使其正确实现目标功能还是需要花费一些时间去思考的。
18 / 18
因篇幅问题不能全部显示,请点此查看更多更全内容