资源描述
通信工程专业课程设计南理工
44
2020年4月19日
文档仅供参考,不当之处,请联系改正。
通信工程专业课程设计
实验报告
——带有LED指示的图片播放器
指导教师:程风雷
完成日期: .9.20
目录
一、 实验目的......................................3
二、 实验原理......................................3
三、 实验设备......................................3
四、 设计思想与功能简介............................3
五、 总体设计方案..................................4
六、 主要模块实现方案..............................4
1.矩阵键盘模块.................................4
2.LCD显示模块.................................11
3.LED显示模块.................................16
七、 整体效果图...................................20
八、 实验结论.....................................23
九、 实验总结.....................................23
十、 参考文献.....................................24
一、 实验目的
随着信息化技术的发展,嵌入式系统已经成为当前IT产业界一个非常热门的话题。嵌入式系统主要由嵌入式处理器、相关支撑硬件和嵌入式软件系统组成,它是集软硬件于一体的可独立工作的“器件”。总体看来,嵌入式系统具有便利灵活、性能价格比高、嵌入性强等特点,能够嵌入到现有任何信息家电和工业控制系统中。从软件角度来看,嵌入式系统具有不可修改性、系统所需配置要求较低、系统专业性和实时性较强等特点。为此我们开展基于SEP3203的通信工程专业课程设计以实现以下目的:
1、经过实验,接触目标开发板、集成开发环境的构建方式和作业方式;
2、经过实验,了解嵌入式系统的硬件和软件、JTAG调试方法,学会如何从头开始着手开发一个嵌入式系统;
3、增加交叉编译、目标板程序调试和加载的真知;积累嵌入式系统开
发流程、开发方法和开发技巧的经验。
经过整个实验流程最终增强实践能力,达到对所学知识的巩固,在实践中
加深对嵌入式软件开发的体会。
二、 实验原理
1. 经过键盘按键判断的工作原理和方法,利用UCB1400的控制方法,实现按键控制切换;
2. 基于LCD的物理特性和工作原理及ARM7TDMI内核LCD控制器的工作原理,设计液晶显示文本及图形的方法与程序;
3.基于LED的工作原理和控制方法及74HC595移位寄存器的使用,实现键控与LED指示的结合;
4.掌握SEP3203 GPIO口的配置和读写方法。
三、实验设备
1.硬件:HOST机一台、调试器一台、ARM实验平台一套。
2.软件:WIN98或 操作系统、ADS或SDT开发环境、调试器驱动程序ICE、键控LCD与LED范例源程序。
四、设计思想与功能简介
设计思想的最初来源是对于图片和幻灯片的播放,而且利用LED动态的显示与图片匹配的日期或编号或其它信息。
经过对实验功能的整合,现取以下两个功能实例展示程序的功能:
1、 经过键盘按键控制LCD液晶显示屏切换图片,实现播放。
2、 达到播放同时在LED数码管上实时显示图片编号。
3、 文字提示信息:当播放成功时会同时显示LCD、LED功能实现成功,在信息提示框中同时显示按键号码、图片号码, 当所按按键无图片时会提示“无图片”信息。
4、 程序采用模块并列模式,易于扩展图片数量与LED指示,便于动态添加,这正是播放设备的基本需要。
五、 总体设计方案
本系统基于SEP3203微处理器,ARM7TDMI内核,主要由矩阵键盘模块、LCD模块、LED模块等部分组成。
系统的总体框图如下:
矩阵键盘模块
LED指示模块
LCD播放模块
六、 主要模块实现方案
1.矩阵键盘模块
1.1 按键位置的确定
本系统采用矩阵键盘,矩阵键盘有两种驱动方式,一种是行扫描法,另一种是高低电平翻转法。本系统采用了行扫描法。包括两个步骤:
判断键盘中是否有键按下:将全部行线置为低电平,然后检测列线的状态。只要有一列的电平为低,则表示键盘中有键被按下,而且闭合的键位于低电平与4根行线相交叉的4个按键之中。若所有列线均为高电平,则键盘中无键按下。
判断闭合键所在的位置:在确认有键按下后,即可进入确定具体闭合键的过程。方法是:依次将行线置为低电平,即在置某根行线为低电平时,其它线为高电平。在确定某根行线位置为低电平后,再逐行检测各列线的电平状态。若某列为低,则该列线与置为低电平的行线交叉处的按键就是闭合的按键。
1.2 键盘中断
键盘实验的主函数流程如下图所示:
上述的循环等待过程可由按键中断打断,执行相应的键盘中断函数。键盘中断服务流程能够由下图表示:
1.3 实验代码
实验代码主要由三个部分的函数组成:顶层实现函数,硬件设置函数,中断服务函数。
1.3.1 顶层实现函数
void KeyBoardModule(void);
键盘实验的顶层函数,实现系统初始化及键盘初始化。
1.3.2 硬件设置函数
void keyboardinit(void);
该函数实现了键盘的初始化。主要调用InitAC97()和int15init(),完成UCB1400初始化及GPIO 口的配置。
void int15init(void);
GPIO 口的配置函数。
void InitAC97(void);
UCB1400 的初始化配置函数。
void wucb(U32 addr,U32 val);
经过向AC97 CRC 寄存器中写入,配置UCB1400 的各寄存器值,该函数用来被查找按键函数调用。
U32 rucb(U32 addr);
经过读出AC97 CRC 寄存器值,获得UCB1400 的各寄存器值,该函数用来被查找按键函数调用。
1.3.3 中断服务函数
void keyhandler(void);
键盘中断处理函数,调用findkey()函数来查找并显示键号。中断处理函数的流程如下图所示。
void findkey(void);
查找按键的函数,用于求出被按下按键的键号并打印出来,该函数被键盘处理中断调用。
1.4 键盘中断相关代码如下:
void key_handler(void)
{
U32 tmp_int_status;
U32 i;
U8 key;
char *s;
*(RP)INTC_IMSK = 0xffffffff;
tmp_int_status = *(RP)INTC_ISTAT;
for(i=0;i<10000;i++);
if(tmp_int_status == 0x20)
{ //int4
/************* set Y3,Y2,Y1 = {1,1,0},config GPIO*************/
*(RP)PORTE_DATA |= 0x00e0;
*(RP)PORTE_INTRCLR = 0xffff;
*(RP)PORTE_DATA &= 0xffc3;
for(i=0;i<5000;i++);
if(*(RP)INTC_ISTAT == 0x20)
{
key = 1;
*(RP)PORTE_DATA &= 0xff1f;
for(i=0;i<100;i++);
*(RP)PORTE_INTRCLR |= 0x10;
*(RP)INTC_IMSK = 0;
}
/************* set Y3,Y2,Y1 = {1,0,1},config GPIO*************/
*(RP)PORTE_DATA |= 0x00e0;
*(RP)PORTE_INTRCLR = 0xffff;
*(RP)PORTE_DATA &= 0xffa3;
for(i=0;i<5000;i++);
if(*(RP)INTC_ISTAT == 0x20)
{
key = 4;
*(RP)PORTE_DATA = 0x0;
for(i=0;i<100;i++);
*(RP)PORTE_INTRCLR |= 0x8;
*(RP)INTC_IMSK = 0;
}
/************* set Y3,Y2,Y1 = {0,1,1}},config GPIO*************/
*(RP)PORTE_DATA |= 0x00e0;
*(RP)PORTE_INTRCLR = 0xffff;
*(RP)PORTE_DATA &= 0xff63;
for(i=0;i<5000;i++);
if(*(RP)INTC_ISTAT == 0x20)
{
key = 7;
*(RP)PORTE_DATA = 0x0;
for(i=0;i<100;i++);
*(RP)PORTE_INTRCLR |= 0x4;
*(RP)INTC_IMSK = 0;
}
}
else if(tmp_int_status == 0x10)
{
/************* set Y3,Y2,Y1 = {1,1,0},config GPIO*************/
*(RP)PORTE_DATA |= 0xe0;
*(RP)PORTE_INTRCLR = 0xffff;
*(RP)PORTE_DATA &= 0xffc3;
for(i=0;i<5000;i++);
if(*(RP)INTC_ISTAT == 0x10)
{
key = 2;
*(RP)PORTE_DATA = 0x0;
for(i=0;i<10000;i++);
*(RP)PORTE_INTRCLR |= 0x10;
*(RP)INTC_IMSK = 0;
}
/************* set Y3,Y2,Y1 = {1,0,1},config GPIO*************/
*(RP)PORTE_DATA |= 0x00e0;
*(RP)PORTE_INTRCLR = 0xffff;
*(RP)PORTE_DATA &= 0xffa3;
for(i=0;i<5000;i++);
if(*(RP)INTC_ISTAT == 0x10)
{
key = 5;
*(RP)PORTE_DATA = 0x0;
for(i=0;i<100;i++);
*(RP)PORTE_INTRCLR |= 0x8;
*(RP)INTC_IMSK = 0;
}
/************* set Y3,Y2,Y1 = {0,1,1}},config GPIO*************/
*(RP)PORTE_DATA |= 0x00e0;
*(RP)PORTE_INTRCLR = 0xffff;
*(RP)PORTE_DATA &= 0xff63;
for(i=0;i<5000;i++);
if(*(RP)INTC_ISTAT == 0x10)
{
key = 8;
*(RP)PORTE_DATA = 0x0;
for(i=0;i<100;i++);
*(RP)PORTE_INTRCLR |= 0x4;
*(RP)INTC_IMSK = 0;
}
}
else if(tmp_int_status == 0x8)
{
/************* set Y3,Y2,Y1 = {1,1,0},config GPIO*************/
*(RP)PORTE_DATA |= 0x00e0;
*(RP)PORTE_INTRCLR = 0xffff;
*(RP)PORTE_DATA &= 0xffc3;
for(i=0;i<5000;i++);
if(*(RP)INTC_ISTAT == 0x8)
{
key = 3;
*(RP)PORTE_DATA = 0x0;
for(i=0;i<100;i++);
*(RP)PORTE_INTRCLR |= 0x10;
*(RP)INTC_IMSK = 0;
}
/************* set Y3,Y2,Y1 = {1,0,1},config GPIO*************/
*(RP)PORTE_DATA |= 0x00e0;
*(RP)PORTE_INTRCLR = 0xffff;
*(RP)PORTE_DATA &= 0xffa3;
for(i=0;i<5000;i++);
if(*(RP)INTC_ISTAT == 0x8)
{
key = 6;
*(RP)PORTE_DATA = 0x0;
for(i=0;i<100;i++);
*(RP)PORTE_INTRCLR |= 0x8;
*(RP)INTC_IMSK = 0;
}
/************* set Y3,Y2,Y1 = {0,1,1}},config GPIO*************/
*(RP)PORTE_DATA |= 0x00e0;
*(RP)PORTE_INTRCLR = 0xffff;
*(RP)PORTE_DATA &= 0xff63;
for(i=0;i<5000;i++);
if(*(RP)INTC_ISTAT == 0x8)
{
key = 9;
*(RP)PORTE_DATA = 0x0;
for(i=0;i<100;i++);
*(RP)PORTE_INTRCLR |= 0x4;
*(RP)INTC_IMSK = 0;
}
}
*(RP)INTC_IMSK = 0x0;
*(RP)PORTE_DATA &= 0xff1f;
for(i=0;i<100;i++);
*(RP)PORTE_INTRCLR = 0xffff;
DBG_Printf("key=%x\n ", key);
}
void findkey(void)
{
U32 intstatus = 0;
U32 i = 0;
U16 j = 0;
U16 k = 0;
U16 l = 0;
U16 m = 0;
U16 n = 0;
U32 datareg = 0;
U16 key = 0;
char *s;
datareg = rucb(0x5a); // 记录下数据寄存器原始值,用于判断列地址
intstatus = rucb(0x62); // 记录下中断状态值,用于判断行地址
if(intstatus != 0)
{
for(j=0; j<4; j++) // 确定列位置。
{
k = (0x08>>j) | datareg; // 依次写入 1000 0100 0010 0001
wucb(0x5a,k); // 进行列扫描动作
for(n=1000;n>1;n--);
l = rucb(0x5a);
if( (l & 0x1e0) == 0x1e0) // 如果数据寄存器值变化,则输入为"1"的那列号就为按键所在列
break; // 此时,j代表的循环数即为列号
}
n = intstatus >> 4; // 确定行位置。
for(m=0; m<4; m++)
{
n = n >> 1;
if( (n & 0x1) == 0x1 )
break;
}
}
// 下面判断按键是否抬起
n = 0;
wucb(0x5a, 0x1e0); // 向I/O 0-3写入全"0"
l = rucb(0x5a);
while( (l & 0x1e0) != 0x1e0) // 按键如果已经抬起了
{
key = (U16)((m*4 + j)%16); // 计算得到的键值
if ( key < 9 )
{
key +=1;
}
else if ( key == 9 )
{
key = 0;
}
if(n++ == 0) // 如果是第一次按键抬起了
{DBG_Printf("keey=%x\n ", key);
}
else if ( n>= 10000 )
{ // 如果在较长时间后,按键依然没有抬起,则快速打印键值
DBG_Printf("keey=%x\n ", key);
}
wucb(0x5a,0x1e0); // 下一次的判断周期
l = rucb(0x5a);
}
*(RP)PORTH_INTRCLR = 0x8; // 清除garfield中断源
wucb(0x62,0x1e0); // ucb I/O 5-8 清除中断状态
wucb(0x5c, 0x1f); // 配置UCB的I/O口功能,0-3为数据输出,5-8为中断输入
wucb(0x5a, 0x1e0); // 配置UCB的I/O口初始数据值
wucb(0x60, 0x1e0);
}
2. LCD显示模块
2.1 LCDC控制器的可编程控制寄存器
本系统中,LCD屏幕用于显示切换图片。该模块需要完成的主要工作是LCD的驱动,在LCD的基本驱动中,需要配置以下寄存器:
寄存器
地址
复位值
SSA
0x1100 (BASE_LCDC)
0x31f00000
SIZE
BASE_LCDC+0x04
0x014000F0
PCR
BASE_LCDC+0x08
0x22080009
HCR
BASE_LCDC+0x0c
0x 4040
VCR
BASE_LCDC+0x10
0x14008586
PWMR
BASE_LCDC+0x14
0x00000114
DMACR
BASE_LCDC+0x1c
0x80070003
LCDICR
BASE_LCDC+0x20
0x00000000
SEP3203中具有内置的LCD控制器,支持灰度LCD和彩色LCD。控制器负责将显存(在系统存储器中)中的数据送到LCD驱动电路。在灰度LCD上,使用基于时间的抖动算法(time-based dithering algorithm)和FRC (Frame RateControl) 方法,能够支持单色、4级灰度和16级灰度模式的灰度LCD;在彩色LCD上,最多可支持65535(16 位)级彩色。能够经过编程修改相应的LCD控制器寄存器的值,适配不同大小、象素的LCD。
2.2 LCD的总体流程图
2.3 实验代码
实验代码主要由三部分组成:顶层实现函数,硬件设置函数,及两个画图函数。
2.3.1 顶层实现函数
STATUS ModuleLcdc(void)
该函数是Lcdc的顶层函数。它实现了系统供电的初始化及Lcdc模块的初始化,而且根据Lcdc.h中的宏定义决定是否清屏,最后调用画图函数,画出一个矩形并显示出来。
2.3.2 硬件设置函数
ER lcdc_init(void)
该函数实现的是对Lcdc的初始化。其主要功能包括:设置LCD数据帧的起始地址、屏幕尺寸、控制方式选择、灰度级别等。(LCDC初始化的流程见下图)。
ER lcdc_clear(void)
该函数用于清除屏幕上所显示的内容。
2.3.3 画图函数
ER lcd_draw2bpp(U8 x1, U8 y1, U8 x2, U8 y2, U8 color)
该函数用来在屏幕上画一个由坐标(x1,y1)和(x2,y2)所确定的矩形,参数U8 color表示所画图形的灰度级别。
void pic_display(void)
该函数经过写屏幕起始地址寄存器,来显示一幅指定的图画。
2.4 相关代码如下:
STATUS ModuleLcdc(void)
{
U32 i;
init_lcdc();
#ifndef LCD_640_480_Color
#ifdef LCDC_CLEAR
if(E_OK != lcdc_clear())
return E_CTX;
#endif
pic_display();
#else
ShowPics();
#endif
}
ER init_lcdc(void)
{
U32 i = 4;
U32 k = 0;
volatile char tempDot;
*(RP)LECR = 0x00000000; //禁用LCDC
//*(RP)PORTE_SEL = 0X1<<11; //GPIO to select lcd_backlight
//*(RP)PORTE_DATA =( 0X1 << 11);
/* config the register of lcdc */
#ifdef LCD_V //竖屏时的初始化
*(RP)SSA = VS_BASE; //lcd数据帧的起始地址
*(RP)SIZE = 0x00F00140; //屏幕尺寸设定为240*320
*(RP)PCR = 0x22080009;//LCD被动模式,单色显示,4bit总线宽度,2bpp,象素极性高有效,
//小印第安格式,最后刷新率配置必须写奇数这里是9,十分之一的
//AMBA频率
*(RP)HCR = 0xc80008b4;//;0xc80008ab;
*(RP)VCR = 0x14000304;//0x14000304; //V_WAIT_1=4, V_WAIT_1=4
*(RP)PWMR = 0x00000113;//0x00000114; //选取象素时钟,关对比度,最后7位设置输出脉冲数目
*(RP)DMACR = 0x80070003;//0x80070003; //burst=1设置DMA高标志7和低标志3,对于固定长度的burst,每次请求
//burst长度为高标志减一,这里是7-1=6。当buffer中的字数小于低标志
//时触发DMA请求
#endif
#ifdef LCD_H //横屏时的初始化
*(RP)SSA = VS_BASE; //lcd数据帧的起始地址
*(RP)SIZE = 0x014000F0; // 320*240
*(RP)PCR = 0x22080009;
*(RP)HCR = 0x 4040;
*(RP)VCR = 0x14008586;
*(RP)PWMR = 0x00000114;
*(RP)DMACR = 0x80070003;
*(RP)LCDICR = 0x00000000;
#endif
#ifdef LCD_640_480_Color //彩屏时的初始化
*(RP)SSA = VS_BASE; //lcd数据帧的起始地址
*(RP)SIZE = YMAX | XMAX;
*(RP)PCR = TFT|COLOR|PBSIZE|BPIX|PIXPOL|FLMPOL|LPPOL|CLKPOL|OEPOL|END_SEL|ACD_SEL|ACD|PCD;
*(RP)HCR = H_WIDTH|H_WAIT_1|H_WAIT_2;
*(RP)VCR = V_WIDTH|PASS_FRAME_WAIT|V_WAIT_1|V_WAIT_2;
*(RP)PWMR = SCR|CC_EN|PW;
*(RP)DMACR = BL|HM|TM;
#endif
*(RP)LECR = 0x00000001; //使能LCDC
*(RP)LCDICR = 0x00000000; //中断在加载帧的最后一个或第一个数据时设置,到LCD之间会有一个延时
#ifndef LCD_640_480_Color //黑白屏时的四级灰度显示
#ifdef grey4
{
*(RP)LGPMR = 0x00000000; //灰度寄存器组的使用应根据PCR中bpp的选定,这里PCR设定为2bpp,即四级灰度。
*(RP)(LGPMR+1*i) = 0x00000008;
*(RP)(LGPMR+2*i) = 0x0000000d;
*(RP)(LGPMR+3*i) = 0x0000000f;
}
//to config the grey,16 levels:
#else
for(i=0;i<16;i++)
*(RP)(LGPMR+4*i) = i;
#endif
*(RP)LECR = 0x0000001; //使能LCDC
for (k = 0; k < 4; k++ )
{
tempDot = k;
tempDot <<= 2;
tempDot |= k;
tempDot <<= 2;
tempDot |= k;
tempDot <<= 2;
tempDot |= k;
for (i = 0; i < (320 * 60 / 4); i++)
{
*(char*)(VS_BASE + (k * 60 * 320 / 4) + i) = tempDot;
}
for (i = 0; i < 1000000; i++);
}
#endif
}
3. LED显示模块
本系统经过LED数码管实时显示图片编号,显示时,首先将要显示的数值经过查表的方式译码,再送至LED上显示。
3.1 七段译码LED模块
采用的七段式的共阴极数码管,它有8个数据输入段,每一个对应于数码管上的一段或者小数点。也就是说,输入一个8位二进制数,就能够得到相应的LED显示的组合。下表是8位二进制数各位和七段码的对应关系。
3.2 实验代码
实验代码主要由两部分组成:顶层实现函数,硬件设置函数。
3.2.1 顶层实现函数
STATUS ModuleLed(void);
功能描述:LED实验的顶层函数,依次调用其它函数实现实验内容。
3.2.2 硬件设置函数
void LedDisPlay(U8 data[]);
功能描述:LED显示函数,将data数组里的内容显示在LED上。
参数说明:
名 称:data[]
类 型:U8数组
取值范围:0x0~0xF
说 明:需要显示在LED上的数据
void GPIO_Init(void);
功能描述:将GPIO PortD的PD0/1/2设置为通用输出口;
void LedOut(U8 data[], U32 times);
功能描述:根据74HC595的工作时序,向PD0/1/2送显示过程的具体实现部分;
参数说明:
名 称:data[]
类 型:U8数组
取值范围:0x0~0xF
说 明:需要显示在LED上的数据
名 称:time
类 型:U32
取值范围:0x00000000~0xFFFFFFFF
说 明:重复显示的次数
void LedUpdate(void);
功能描述:将74HC595的移位寄存器值锁存到输出引脚,驱动LED显示。
3.3 该部分的相关代码如下:
U8 SEGMENT[16] = { (U8)(sect2 + sect3 + sect4 + sect5 + sect6 + sect7), //0
(U8)(sect5 + sect6), //1
(U8)(sect1 + sect3 + sect4 + sect6 + sect7), //2
(U8)(sect1 + sect4 + sect5 + sect6 + sect7), //3
(U8)(sect1 + sect2 + sect5 + sect6), //4
(U8)(sect1 + sect2 + sect4 + sect5 + sect7), //5
(U8)(sect1 + sect2 + sect3 + sect4 + sect5 + sect7), //6
(U8)(sect5 + sect6 + sect7), //7
(U8)(sect1 + sect2 + sect3 + sect4 + sect5 + sect6 + sect7), //8
(U8)(sect2 + sect3 + sect4 + sect5 + sect6 + sect7), //9
(U8)(sect1 + sect2 + sect3 + sect5 + sect6 + sect7), //a
(U8)(sect1 + sect2 + sect3 + sect4 + sect5), //b
(U8)(sect2 + sect3 + sect4 + sect7), //c
(U8)(sect1 + sect3 + sect4 + sect5 + sect6), //d
(U8)(sect1 + sect2 + sect3 + sect4 + sect7), //e
(U8)(sect1 + sect2 + sect3 + sect7) //f
};
U8 prochip[8] = { 0xce, 0x60, 0x6e, 0x9c, 0xfc, 0xee, 0xce }; // prochip LOGO
U8 display[8] = {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
U8 display1[8] = {0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
U8 display2[8] = {0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
U8 display3[8] = {0x04,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
// 实验中显示的数据(0-0xf)
//U8 display[8] = {0x08,0x08,0x08,0x08,0x08,0x08,0x08,0x08}; // 实验中显示的数据(0-0xf)
//#define ROLL; // 是否以滚屏方式显示: no define: 不滚屏
// define: 滚屏方式
STATUS ModuleLed(void)
{
/* system initialized */
system_init(); // 系统初始化
/* GPIO initialized */
GPIO_Init();
/* led lab body */
// LedOut(prochip,5); // 在LED上显示“PROCHIP〉的logo
LedDisPlay(display); // 显示用户设定的内容
return E_OK;
}
STATUS ModuleLed1(void)
{
/* system initialized */
//system_init(); // 系统初始化
/* GPIO initialized */
GPIO_Init();
/* led lab body */
// LedOut(prochip,5); // 在LED上显示“PROCHIP〉的logo
LedDisPlay(display1); // 显示用户设定的内容
return E_OK;
}
STATUS ModuleLed2(void)
{
/* system initialized */
//system_init(); // 系统初始化
/* GPIO initialized */
GPIO_Init();
/* led lab body */
// LedOut(prochip,5); // 在LED上显示“PROCHIP〉的logo
LedDisPlay(display2); // 显示用户设定的内容
return E_OK;
}
STATUS ModuleLed3(void)
{
展开阅读全文