通信与信息工程学院
2015/2016 学年 第一学期
课程设计 实验报告
模 块 名 称 ARM嵌入式开发
专 业 电子信息工程
学 生 班 级 B120110
学 生 学 号 B******31
学 生 姓 名 钱 晨
指 导 教 师 余雪勇
设计题目 | (1)蜂鸣器测试 (2)流水灯实验 (3)RGB配色原理实验 (4)按键显示6张图片 (5)自动循环显示6张图片 (6)动态图片测试 (7)歌曲测试1 (8)歌曲测试2 |
任务要求 | 基本要求 根据开发板及内容选做TQ2440测试程序中的8个实验。 提高要求 根据学生自己掌握嵌入式系统及linux操作系统知识,设计一个小应用程序 A、B通过UDP发送文件,A发,B收并保存 |
实验设备及软件 | 硬件:TQ2440嵌入式实验平台开发板、Windows系统PC 机、串 口线、网线、电源、USB线。 软件:PC机操作系统、ADS1.2开发环境。red hat enterprise linux 虚拟机环境。 |
同组人员学号及姓名 | B12011032 黄艺 |
参考文献 | 【1】(英)马修,斯通斯,译者:陈健,宋健建,《Linux程序设计(第4版)》,北京:人民邮电出版社,2006 【2】薛园园,《21天学通ARM嵌入式开发》(第二版),电子工业出版社 |
一、基本要求
在基本要求中,需要从11个测试程序中选做8个,以下是对8个程序的实验过程的叙述,包括实验前的硬件连接准备、软件环境配置(串口工具、dnw、ADS、交叉编译环境等)、每个实验的关键代码以及简单分析。
1、硬件连接
用USB线、串口线把开发板连到电脑相应的端口,再将电源线插好。
2、软件环境配置
设置串口工具SecureCRT
解压在“windows 平台开发工具包\”目录下的“SecureCRT.rar”后,即可使用SeureCRT, 双击图标,打开SecureCRT,如下图所示:
点击图中红色方框图标,出现下图的设置窗口:
在 Ptotocol 里面选择Serial,出现如下图所示的对话框,详细设置参考下图,超级终端设置部分,不再重复。
注意:Port 选项部分根据您实际使用的端口进行配置,其他选项请一定配置如下图所示。
配置完毕后,点击上图的“Connect”选项即可连通串口。
DNW 设置
DNW 在这里是我们的.bin 文件下载软件,可实现我们向flash 或者内存当中烧写程序的功能。
直接双击“Windows 平台工具\\DNW”目录下的DNW 软件,出现下图:
(1)点击“Configuration”菜单的“Options”,出现“UART\\USB Options”配置
(2)配置如下图:
3、实验前准备
串口工具和开发板连接成功后,将选择开关打到norflash,并按一下重启键,开发板则自动按照选择从norflash 启动。此时,如果 SecureCRT 界面显示如下,则表示串口工具已经工作正常:
一般出厂光盘里面已经有许多bin 文件了,其中包括我们此处所说的TQ2440_Test 的bin
文件。我们也可以参考以下步骤,使用ADS1.2 生成自己的“*.bin”文件。
(1)、安装ADS1.2(ARM Developer Suite v1.2,一款针对ARM 的开发套件),并使用ADS打开天嵌科技的出厂自带的测试程序。
(2)、点击compile 键进行编译,点击make 键生成我们此处所需要的“*.bin”文件生成自己的 bin 文件之后,就可以使用SecureCRT 配合dnw 来实现对bin 文件的下载了:操作步骤其实和上面烧写出厂程序一样,在此再详细叙述一下:
a,打开串口工具,使开发板从norflash 启动,再串口工具中出现的信息中,选择a,进入等待下载状态;
b,双击打开dnw,然后选择USB Port >>Transmit>>找到相应的需要烧写的bin 文件,双节即可完成烧写。
(3)、按照以上步骤进行操作,则我们此时基本已经完成了一次完整的程序的从编译到烧写的过程了。同时,至此天嵌的出厂测试程序已经被我们烧写到了nandflash 中。此时,我们再将选择开关打到nandflash 中并重启开发板,使开发板进入我们刚刚烧写的出厂程序中,我们会看到在SecureCRT 界面会出现以下信息:
<***************************************>
TQ2440 Test Program
B12011031--钱晨
<***************************************>
Please input 1-11 to select test
1 : BUZZER_PWM_Test,蜂鸣器测试
2 : LED_Test,流水灯测试
3 : RGB_Test,RGB配色测试
4 : Lcd_TFT_Test,按键显示6张图片
5 : Lcd_TFT_Test2,自动循环显示6张图片
6 : Lcd_TFT_Test3,测试动态图片
7 : PWM_Music1_Test,测试歌曲1
8 : PWM_Music2_Test,测试歌曲2
9 : RTC_Display,RTC time display
10 : Test_Adc,Test ADC
11 : KeyScan_Test,Test interrupt and key scan
截图如下:
4、 8个测试程序分析
(1)蜂鸣器测试
按“—”号,蜂鸣器声音频率逐渐减小,最小 Freq = 10;按“+”号,蜂鸣器声音频率逐渐增大,最大Freq = 2000。(加号需要按住“shift”不然是等号)。使用 ADS 打开出厂程序如下:
双击,打开 Main.c 文件,找到while(1)循环,在其中的CmdTip函数上面右键,Go to 一下,就转到了:
struct {
void (*fun)(void);
char *tip;
}CmdTip[] = {
{ Temp_function, "Please input 1-11 to select test" } ,
{ BUZZER_PWM_Test, "Test PWM" } ,
{ RTC_Display, "RTC time display" } ,
{ Test_Adc, "Test ADC" } ,
{ KeyScan_Test, "Test interrupt and key scan" } ,
{ Test_Touchpanel, "Test Touchpanel" } ,
{ Lcd_TFT_Test, "Test TFT LCD" } ,
{ Test_Iic, "Test IIC EEPROM" } ,
{ PlayMusicTest, "UDA1341 play music" } ,
{ RecordTest, "UDA1341 record voice" } ,
{ Test_SDI, "Test SD Card" } ,
{ Camera_Test, "Test CMOS Camera"},
{ 0, 0}};
从下面struct 结构当中去寻找到BUZZER_PWM_Test , 并右键go to , 跳转到: void BUZZER_PWM_Test( void )这个函数。
void BUZZER_PWM_Test( void )
{
U16 freq =800;// lci 1000 ;
Uart_Printf( "\\nBUZZER TEST ( PWM Control )\\n" );
Uart_Printf( "Press +/- to increase/reduce the frequency of BUZZER !\\n" ) ;
Uart_Printf( "Press 'ESC' key to Exit this program !\\n\\n" );
Buzzer_Freq_Set( freq ) ;
while( 1 )
{
U8 key = Uart_Getch();
if( key == '+' )
{
if( freq < 2000 ) //lci 20000
freq += 10 ;
Buzzer_Freq_Set( freq ) ;
}
if( key == '-' )
{
if( freq > 11 )
freq -= 10 ;
Buzzer_Freq_Set( freq ) ;
}
Uart_Printf( "\Freq = %d\\n", freq ) ;
if( key == ESC_KEY )
{
Buzzer_Stop() ;
return ;
}
}
}
我们仔细地分析过这个函数之后不难发现,在我们if( key == '-' )的时候没有问题,是因为此处的减号就是我们键盘上的减号,但是,在我们很多键盘上面是没有if( key == '+' )中的这个加号的,我们要想解决这个漏洞有两个办法:一是,在我们调试出厂程序的时候,使用shfit 加“+”号键来组合实现对于频率控制时候的增加功能;二是,在这里直接把if( key == '=' ),这样就可以是我们在调试程序的时候更加直观了。
其实仅仅是改变按键,包括频率的变化范围、变化步长都可以在这个函数中改变。
(2)按键显示六张照片
我们要实现在原厂程序的 Lcd_TFT_Test 实验当中实现切换六张自己喜欢的图片,我们所需要做的工作如下:
a、在TQ_LOGO.c 文件中添加数组:
unsigned char tu1_320240[] = {};
unsigned char tu2_320240[] = {};
unsigned char tu3_320240[] = {};
unsigned char tu4_320240[] = {};
unsigned char tu5_320240[] = {};
unsigned char tu6_320240[] = {};
添加方法和之前一样,按照之前的方法写在正确的位置就可以了。
b、在LCD_TFT.h 文件中添加对应数组的外部声明:
extern unsigned char tu1_320240[];
extern unsigned char tu2_320240[];
extern unsigned char tu3_320240[];
extern unsigned char tu4_320240[];
extern unsigned char tu5_320240[];
extern unsigned char tu6_320240[];
不添加这些语句的话就不可以在其他文件当中引用这些数组,所以我们当然要这个外部声明。至于有些同学不懂得这个工作怎么做?对于基础比较差的同学,建议大家尽快培养一种思路,多去看别人代码,多去看别人的写法,多去模仿别人的语句来书写自己的语句。添加的位置有之前的数组可以供大家参考,此处具体做法为:
参考原代码的对应的外部数组声明的位置,我们不妨使用以下代码:
“extern unsigned char tu1_320240[];
extern unsigned char tu2_320240[];
extern unsigned char tu3_320240[];
extern unsigned char tu4_320240[];
extern unsigned char tu5_320240[];
extern unsigned char tu6_320240[];”
覆盖掉原代码段:
“#if((LCD_Type == LCDW35) || (LCD_Type == LCDS35)) // 3.5 寸屏
extern unsigned char TQ_LOGO_320240[];//自定义的图片
#elif(LCD_Type == LCDT35) // 3.5 寸屏
extern unsigned char TQ_LOGO_240320[];//自定义的图片
#elif(LCD_Type == LCDW43) // 3.5 寸屏
extern unsigned char TQ_LOGO_480272[];//自定义的图片
#elif(LCD_Type == VGA) //VGA
extern unsigned char TQ_LOGO_0480[];//自定义的图片
#elif(LCD_Type == LCDA70) // 7 寸屏
extern unsigned char TQ_LOGO_800480[];//自定义的图片
#endif”
即可。
c、此处采用在LCD_TFT.c 文件中的void Lcd_TFT_Test( void )函数中的正确位置分别添加以下语句的方法来实现试验功能:
Paint_Bmp(0, 0, 320, 240, tu1_320240);
Paint_Bmp(0, 0, 320, 240, tu2_320240);
Paint_Bmp(0, 0, 320, 240, tu3_320240);
Paint_Bmp(0, 0, 320, 240, tu4_320240);
Paint_Bmp(0, 0, 320, 240, tu5_320240);
Paint_Bmp(0, 0, 320, 240, tu6_320240);
不清楚我们应该添加到哪个位置的同学,请参考随本教程同时发布的参考代码,或者参考以下说明:
此处提供一种修改方式供大家参考,红色的为添加语句,其他语句不变:
void Lcd_TFT_Test( void )
{
Uart_Printf("\\nTest TFT LCD!\\n");
Lcd_ClearScr( (0x00<<11) | (0x00<<5) | (0x00) ) ; //clear screen
Paint_Bmp(0, 0, 320, 240, tu1_320240);
Uart_Printf( "\\nDisplay Black! Press any key to continue!\\n" );
Uart_Getch() ; //wait uart input
Lcd_ClearScr( (0x1f<<11) | (0x3f<<5) | (0x1f) ) ; //clear screen
Paint_Bmp(0, 0, 320, 240, tu2_320240);
Uart_Printf( "Display White! Press any key to continue!\\n" );
Uart_Getch() ; //wait uart input
Lcd_ClearScr( (0x00<<11) | (0x00<<5) | (0x1f) ) ; //clear screen
Paint_Bmp(0, 0, 320, 240, tu3_320240);
Uart_Printf( "Display Blue! Press any key to continue!\\n" );
Uart_Getch() ; //wait uart input
Lcd_ClearScr( (0x00<<11) | (0x3f<<5) | (0x00) ) ; //clear screen
Paint_Bmp(0, 0, 320, 240, tu4_320240);
Uart_Printf( "Display Green! Press any key to continue!\\n" );
Uart_Getch() ; //wait uart input
Lcd_ClearScr( (0x1f<<11) | (0x00<<5) | (0x00) ) ; //clear screen
Paint_Bmp(0, 0, 320, 240, tu5_320240);
Uart_Printf( "Display Red! Press any key to continue!\\n" );
Uart_Getch() ; //wait uart input
Lcd_ClearScr(0xffff); //fill all screen with some color
#define LCD_BLANK 12
#define C_UP ( LCD_XSIZE_TFT - LCD_BLANK*2 )
#define C_RIGHT ( LCD_XSIZE_TFT - LCD_BLANK*2 )
#define V_BLACK ( ( LCD_YSIZE_TFT - LCD_BLANK*4 ) / 6 )
Paint_Bmp(0, 0, 320, 240, tu6_320240);
Uart_Printf("LCD Test Complete!\\n");
Uart_Printf("Press any key to quit!\\n");
Uart_Getch();
}
d.修改好了代码之后去编译和生成bin 文件,并下载到开发板观察效果。
(3)RGB配色原理实验
液晶显示屏通俗点理解就是,TFT液晶显示器的每一个点都是有R.G.B三部分组成,当3个点都通高电平时,液晶融化成液态成透明,所以看到的就是背光颜色白色;当3个点都通低电平时,液晶凝固成晶体状,将背光完全挡住,所以看到的是黑色。
当R的点全为高,而G.B全为低时,显示为红光;
当G的点全为高,而R.B全为低时,显示为绿色;
当B的点全为高,而R,B全为低时,显示为蓝色。
RGB原理:
RGB颜色原理是用三种原色:Red、Green、Blue以不同的比例相加,生成不同颜色的色光。
三原色的原理不是出于物理原因,而是由于生理原因造成的。人的眼睛内有几种辨别颜色的锥形感光细胞,分别对黄绿色、绿色和蓝紫色(或称紫罗 兰色)的光最敏感(波长分别为5、534和420纳米),如果辨别黄绿色的细胞受到的刺激略大于辨别绿色的细胞,人的感觉是黄色;如果辨别黄绿色的细胞受到的刺激大大高于辨别绿色的细胞,人的感觉是红色。虽然三种细胞并不是分别对红色、绿色和蓝色最敏感,但这三种光可以分别对三种锥形细胞产生刺激。
例如,红光与绿光按某种比例复合,对三种锥状细胞刺激后产生的色觉可与眼睛对单纯的黄光的色觉等效。但决不能认为 红光与绿光按某种比例复合后生成黄光,或黄光是由红光和绿光复合而成的。
S3C2440中某些语句的理解:
bpp术语:bits per pixel 每像素多少位。
a、 24bpp,这是最常见的1600万色标准该模式下,R.G.B各使用无符号整数16位来表示红、绿、蓝三颜色的强度,比如:0xFF00FF为品红色,因其最多可以有1600万种组合而得名(2的24次方为16777216)。
b、 16bpp,这是S3C2440所用的6.5万色标准(2的16次方为65536)16比特模式分配给每种原色各5比特,其中Green为6比特,因为人眼对绿光的分辨的更精确(某些地方采用的是各5比特,剩下的1比特不使用),所以我们这里使用的RGB为 5:6:5模式。出厂代码中对整个屏幕显示红色的语句为:LCD_ClearScr((0x1f<<11)|(0x00<<5)|0x00);正是按照5:6:5的模式分配的11111 000000 00000所以显示红色。同理显示出黑色、白色、蓝色、绿色。
编写自己的RGB配色实验:
while(1)
{
for(ii=0;ii<32;ii++){
Glib_Line(ii,0,ii,240,(ii<<11) | (0<<5) | (0));//红色,逐渐加深色
}
for(ii=32;ii<95;ii++){
Glib_Line(ii,0,ii,240,(0<<11) | ((ii-31)<<5) | (0));//绿色,逐渐加深
}
for(ii=95;ii<126;ii++){
Glib_Line(ii,0,ii,240,(0<<11) | (0<<5) | (ii-96));//蓝色,逐渐加深
}
for(ii=126;ii<160;ii++){
Glib_Line(ii,0,ii,240,(0x1f<<11) | (0x3f<<5) | (0));//红色+绿色=黄色
}
for(ii=160;ii<200;ii++){
Glib_Line(ii,0,ii,240,(0<<11) | (0x3f<<5) | (0x1f));//绿色+蓝色=青色
}
for(ii=200;ii<240;ii++){
Glib_Line(ii,0,ii,240,(0x1f<<11) | (0<<5) | (0x1f));//红色+蓝色=品红
}
}
(5)循环显示六张照片
我们要实现在原厂程序的 Lcd_TFT_Test 实验当中实现切换六张自己喜欢的图片,我们所需要做的工作如下:
a、在TQ_LOGO.c 文件中添加数组:
unsigned char tu1_320240[] = {};
unsigned char tu2_320240[] = {};
unsigned char tu3_320240[] = {};
unsigned char tu4_320240[] = {};
unsigned char tu5_320240[] = {};
unsigned char tu6_320240[] = {};
添加方法和之前一样,按照之前的方法写在正确的位置就可以了。
b、在LCD_TFT.h 文件中添加对应数组的外部声明:
extern unsigned char tu1_320240[];
extern unsigned char tu2_320240[];
extern unsigned char tu3_320240[];
extern unsigned char tu4_320240[];
extern unsigned char tu5_320240[];
extern unsigned char tu6_320240[];
不添加这些语句的话就不可以在其他文件当中引用这些数组,所以我们当然要这个外部声明。至于有些同学不懂得这个工作怎么做?对于基础比较差的同学,建议大家尽快培养一种思路,多去看别人代码,多去看别人的写法,多去模仿别人的语句来书写自己的语句。添加的位置有之前的数组可以供大家参考,此处具体做法为:
参考原代码的对应的外部数组声明的位置,我们不妨使用以下代码:
“extern unsigned char tu1_320240[];
extern unsigned char tu2_320240[];
extern unsigned char tu3_320240[];
extern unsigned char tu4_320240[];
extern unsigned char tu5_320240[];
extern unsigned char tu6_320240[];”
覆盖掉原代码段:
“#if((LCD_Type == LCDW35) || (LCD_Type == LCDS35)) // 3.5 寸屏
extern unsigned char TQ_LOGO_320240[];//自定义的图片
#elif(LCD_Type == LCDT35) // 3.5 寸屏
extern unsigned char TQ_LOGO_240320[];//自定义的图片
#elif(LCD_Type == LCDW43) // 3.5 寸屏
extern unsigned char TQ_LOGO_480272[];//自定义的图片
#elif(LCD_Type == VGA) //VGA
extern unsigned char TQ_LOGO_0480[];//自定义的图片
#elif(LCD_Type == LCDA70) // 7 寸屏
extern unsigned char TQ_LOGO_800480[];//自定义的图片
#endif”
即可。
c、此处采用在LCD_TFT.c 文件中的void Lcd_TFT_Test( void )函数中的正确位置分别添加以下语句的方法来实现试验功能:
Paint_Bmp(0, 0, 320, 240, tu1_320240);
Paint_Bmp(0, 0, 320, 240, tu2_320240);
Paint_Bmp(0, 0, 320, 240, tu3_320240);
Paint_Bmp(0, 0, 320, 240, tu4_320240);
Paint_Bmp(0, 0, 320, 240, tu5_320240);
Paint_Bmp(0, 0, 320, 240, tu6_320240);
不清楚我们应该添加到哪个位置的同学,请参考随本教程同时发布的参考代码,或者参考以下说明:
此处提供一种修改方式供大家参考,红色的为添加语句,其他语句不变:
void Lcd_TFT_Test( void )
{
Uart_Printf("\\nTest TFT LCD!\\n");
Lcd_ClearScr( (0x00<<11) | (0x00<<5) | (0x00) ) ; //clear screen
Paint_Bmp(0, 0, 320, 240, tu1_320240);
Uart_Printf( "\\nDisplay Black! Press any key to continue!\\n" );
Uart_Getch() ; //wait uart input
Lcd_ClearScr( (0x1f<<11) | (0x3f<<5) | (0x1f) ) ; //clear screen
Paint_Bmp(0, 0, 320, 240, tu2_320240);
Uart_Printf( "Display White! Press any key to continue!\\n" );
Uart_Getch() ; //wait uart input
Lcd_ClearScr( (0x00<<11) | (0x00<<5) | (0x1f) ) ; //clear screen
Paint_Bmp(0, 0, 320, 240, tu3_320240);
Uart_Printf( "Display Blue! Press any key to continue!\\n" );
Uart_Getch() ; //wait uart input
Lcd_ClearScr( (0x00<<11) | (0x3f<<5) | (0x00) ) ; //clear screen
Paint_Bmp(0, 0, 320, 240, tu4_320240);
Uart_Printf( "Display Green! Press any key to continue!\\n" );
Uart_Getch() ; //wait uart input
Lcd_ClearScr( (0x1f<<11) | (0x00<<5) | (0x00) ) ; //clear screen
Paint_Bmp(0, 0, 320, 240, tu5_320240);
Uart_Printf( "Display Red! Press any key to continue!\\n" );
Uart_Getch() ; //wait uart input
Lcd_ClearScr(0xffff); //fill all screen with some color
#define LCD_BLANK 12
#define C_UP ( LCD_XSIZE_TFT - LCD_BLANK*2 )
#define C_RIGHT ( LCD_XSIZE_TFT - LCD_BLANK*2 )
#define V_BLACK ( ( LCD_YSIZE_TFT - LCD_BLANK*4 ) / 6 )
Paint_Bmp(0, 0, 320, 240, tu6_320240);
Uart_Printf("LCD Test Complete!\\n");
Uart_Printf("Press any key to quit!\\n");
Uart_Getch();
}
d.修改好了代码之后去编译和生成bin 文件,并下载到开发板观察效果。
(6)动态图片测试
我们需要做的工作摘要:
a.在TQ_LOGO.c文件中添加数组:
unsigned char kobeshot_320240[] = {
unsigned char ball_014014[] = {
b.在LCD_TFT.h中添加对应数组的声明:
extern unsigned char kobeshot_320240[];
extern unsigned char ball_014014[];
修补原厂程序中的Paint_Bmp()函数的一个漏洞:
原厂程序的Paint_Bmp()函数是不可以对一张非全屏的小图片实现准确定位显示的。对Paint_Bmp()函数进行了分析和修改,修改之后的使用方法如下:
(a)、屏幕显示的坐标系为左上角为(0,0)点, 屏幕右方为+y方向,屏幕下方为
+x方向;
(b)、 (x0,y0)为需要被显示的图片左上角坐标值,h为图片的高度(在x方向
的长度),l为图片的宽度(在y方向);
*修补这个漏洞的方法:
使用下面这一段代码覆盖原厂程序的Paint_Bmp(){}函数即可。
void Paint_Bmp(int x0,int y0,int h,int l,unsigned char bmp[])
{
int x,y;
U32 c;
Int p = 0;
for( y = 0 ; y < l ; y++ )
{
for( x = 0 ; x < h ; x++ )
{
c = bmp[p+1] | (bmp[p]<<8) ;
if ( ( (x0+x) < SCR_XSIZE_TFT) && ( (y0+y) < SCR_YSIZE_TFT) )
LCD_BUFFER[y0+y][x0+x] = c ;
p = p + 2 ;
}
}
}
修补过之后,就可以实现对小图片起点的准确定位了!!
*我们先在main.c文件中的main函数中的while(1)里面写入如下的程序段,并将
程序烧写进开发板。实现在我们的kobe投篮图上画下距离为20像素点的方格子,方便我们来大概确定篮球走过轨迹的坐标值:
Paint_Bmp(0, 0, 320, 240, kobeshot_320240);
while(1)
{
for(ii=0;ii<16;ii++)
{
Glib_Line(0,20*ii,320,20*ii,0xffff);
Glib_Line(20*ii,0,20*ii,240,0xffff);
if(ii==5||ii==10||ii==15)
{
Glib_Line(0,20*ii,320,20*ii, (0x00<<11) | (0x3f<<5) | (0x00));
Glib_Line(20*ii,0,20*ii,240, (0x00<<11) | (0x3f<<5) | (0x00));
}
}
}
*最终,经过作者的粗略分析暂时采用了以下版本的篮球抛物线运动坐标值来
模拟篮球走过的轨迹,并写了以下版本的代码供大家参考:
while(1)
{
for(ii=0;ii<16;ii++)
{
if(ii<8) iii=60-8*ii;
else iii=(ii-7)*10;
Paint_Bmp(0, 0, 320, 240, kobeshot_320240);
Glib_FilledRectangle(200,40,240,80,0x0000);//
(此处可以使用photoshop把之前那个图涂掉效果更好)
Paint_Bmp(205-10*ii,iii, 14, 14, ball_014014);
Delay(500);
}
}
如此操作之后,编译、生成bin文件并烧写进开发板就可以看到一款科比投
篮的动画啦!!
(7)使用PWM 蜂鸣器唱歌
在网上找的do、re、mi、fa、sol、la、si 的低八度,中八度,高八度对应的声音的频率数值图,如下:
在网络上我们不难可以找到,使用 c51 单片机实现PWM 唱歌的程序,并
在这里作为我们的参考:
#include "reg52.h"
unsigned char Count;
sbit _Speak =P3^2 ; // 蜂鸣器控制脚
unsigned char code SONG[] ={ //祝你平安0x26,0x20,0x20,0x20,0x20,0x20,0x26,0x10,0x20,0x10,0x20,0x80,0x26,0x20,0x30,0x20,
0x30,0x20,0x39,0x10,0x30,0x10,0x30,0x80,0x26,0x20,0x20,0x20,0x20,0x20,0x1c,0x20,
0x20,0x80,0x2b,0x20,0x26,0x20,0x20,0x20,0x2b,0x10,0x26,0x10,0x2b,0x80,0x26,0x20,
0x30,0x20,0x30,0x20,0x39,0x10,0x26,0x10,0x26,0x60,0x40,0x10,0x39,0x10,0x26,0x20,
0x30,0x20,0x30,0x20,0x39,0x10,0x26,0x10,0x26,0x80,0x26,0x20,0x2b,0x10,0x2b,0x10,
0x2b,0x20,0x30,0x10,0x39,0x10,0x26,0x10,0x2b,0x10,0x2b,0x20,0x2b,0x40,0x40,0x20,
0x20,0x10,0x20,0x10,0x2b,0x10,0x26,0x30,0x30,0x80,0x18,0x20,0x18,0x20,0x26,0x20,
0x20,0x20,0x20,0x40,0x26,0x20,0x2b,0x20,0x30,0x20,0x30,0x20,0x1c,0x20,0x20,0x20,
0x20,0x80,0x1c,0x20,0x1c,0x20,0x1c,0x20,0x30,0x20,0x30,0x60,0x39,0x10,0x30,0x10,
0x20,0x20,0x2b,0x10,0x26,0x10,0x2b,0x10,0x26,0x10,0x26,0x10,0x2b,0x10,0x2b,0x80,
0x18,0x20,0x18,0x20,0x26,0x20,0x20,0x20,0x20,0x60,0x26,0x10,0x2b,0x20,0x30,0x20,
0x30,0x20,0x1c,0x20,0x20,0x20,0x20,0x80,0x26,0x20,0x30,0x10,0x30,0x10,0x30,0x20,
0x39,0x20,0x26,0x10,0x2b,0x10,0x2b,0x20,0x2b,0x40,0x40,0x10,0x40,0x10,0x20,0x10,
0x20,0x10,0x2b,0x10,0x26,0x30,0x30,0x80,0x00,
};
void Time0_Init(){
TMOD = 0x01; //工作模式选择
IE = 0x82; //中断设置
TH0 = 0xD8; //装初值
TL0 = 0xEF; //12MZ 晶振,10ms
}
void Time0_Int() interrupt 1{
TH0 = 0xD8;
TL0 = 0xEF;
Count++; //长度加1
}
/*-------------------------------------------------
功能:1MS 延时子程序
-------------------------------------------------*/
void Delay_xMs(unsigned int x){
unsigned int i,j;
for( i =0;i < x;i++ ){
for( j =0;j<3;j++ );
}
}
/*-------------------------------------------------
功能:歌曲播放子程序,i 为播放哪一段曲目-------------------------------------------------*/
void Play_Song(unsigned char i){
unsigned char Temp1,Temp2;
unsigned int Addr;
Count = 0; //中断计数器清0
Addr = i * 217;
while(1){
Temp1 = SONG[Addr++];
if ( Temp1 == 0xFF ) { //休止符
TR0 = 0;
Delay_xMs(100);
}
else if ( Temp1 == 0x00 ){ //歌曲结束符
return;
}
else{
Temp2 = SONG[Addr++];
TR0 = 1; //wang 计时器使能
while(1){
_Speak = ~_Speak; //wang P3^2 取反
Delay_xMs(Temp1); //wang temp1 为单次延时时间
if ( Temp2 == Count ){ //wang temp2 为一个拍子的延时时间
Count = 0;
break;
}
}
}
}
}
/*-------------------------------------------------功能:主程序-------------------------------------------------*/
void main(){
Time0_Init(); //定时器0 中断初始化
while(1){
Play_Song(0); //播放
}
}
首先,将以上代码对应自己的 c51 开发板的硬件原理图进行修改和调整。
(其实就是将“sbit _Speak =P3^2 ;”这一句改成和自己的c51 开发板的连接蜂鸣器的管脚对应就可以了。例如我的开发板连接蜂鸣器的管脚是P3.2,那么我就改成
了这里的“sbit _Speak =P3^2 ;” ,其他语句基本完全不用改动。)
然后,然后将生成的 hex 文件烧写到我们自己的51 开发板就可以听到《祝
你平安》这首歌了。
在 Example4.2 的基础上,在main.c 文件中进行以下操作:
a、定义数组:
unsigned char SONG[] ={ //祝你平安
0x26,0x20,0x20,0x20,0x20,0x20,0x26,0x10,0x20,0x10,0x20,0x80,0x26,0x20,0x30,0x20,
0x30,0x20,0x39,0x10,0x30,0x10,0x30,0x80,0x26,0x20,0x20,0x20,0x20,0x20,0x1c,0x20,
0x20,0x80,0x2b,0x20,0x26,0x20,0x20,0x20,0x2b,0x10,0x26,0x10,0x2b,0x80,0x26,0x20,
0x30,0x20,0x30,0x20,0x39,0x10,0x26,0x10,0x26,0x60,0x40,0x10,0x39,0x10,0x26,0x20,
0x30,0x20,0x30,0x20,0x39,0x10,0x26,0x10,0x26,0x80,0x26,0x20,0x2b,0x10,0x2b,0x10,
0x2b,0x20,0x30,0x10,0x39,0x10,0x26,0x10,0x2b,0x10,0x2b,0x20,0x2b,0x40,0x40,0x20,
0x20,0x10,0x20,0x10,0x2b,0x10,0x26,0x30,0x30,0x80,0x18,0x20,0x18,0x20,0x26,0x20,
0x20,0x20,0x20,0x40,0x26,0x20,0x2b,0x20,0x30,0x20,0x30,0x20,0x1c,0x20,0x20,0x20,
0x20,0x80,0x1c,0x20,0x1c,0x20,0x1c,0x20,0x30,0x20,0x30,0x60,0x39,0x10,0x30,0x10,
0x20,0x20,0x2b,0x10,0x26,0x10,0x2b,0x10,0x26,0x10,0x26,0x10,0x2b,0x10,0x2b,0x80,
0x18,0x20,0x18,0x20,0x26,0x20,0x20,0x20,0x20,0x60,0x26,0x10,0x2b,0x20,0x30,0x20,
0x30,0x20,0x1c,0x20,0x20,0x20,0x20,0x80,0x26,0x20,0x30,0x10,0x30,0x10,0x30,0x20,
0x39,0x20,0x26,0x10,0x2b,0x10,0x2b,0x20,0x2b,0x40,0x40,0x10,0x40,0x10,0x20,0x10,
0x20,0x10,0x2b,0x10,0x26,0x30,0x30,0x80,
};
b、修改main 函数为:
void Main(void){
char *mode;
unsigned char Temp1,Temp2;
unsigned int Addr=0;
U8 key;
U32 mpll_val = 0 ;
U16 freq;// lci 1000
Port_Init();
Isr_Init();
mpll_val = (92<<12)|(1<<4)|(1); //init FCLK=400M, so change MPLL first
ChangeMPllValue((mpll_val>>12)&0xff, (mpll_val>>4)&0x3f, mpll_val&3);
ChangeClockDivider(key, 12);
cal_cpu_bus_clk();
consoleNum = 0; // Uart 1 select for debug.
Uart_Init( 0,115200 );
rMISCCR=rMISCCR&~(1<<3); // USBD is selected instead of USBH1
rMISCCR=rMISCCR&~(1<<13); // USB port 1 is enabled.
rDSC0 = 0x2aa;
rDSC1 = 0x2aaaaaaa;
rCLKCON = 0xfffff0;
MMU_Init(); //
pISR_SWI=(_ISR_STARTADDRESS+0xf0); //for pSOS
Led_Display(0x66);
mode="DMA";
Clk0_Disable();
Clk1_Disable();
mpll_val = rMPLLCON;
Lcd_TFT_Init() ; // LCD initial
download_run=1; //The default menu is the Download & Run mode.
Lcd_ClearScr( (0x00<<11) | (0x00<<5) | (0x1f) );
while(1){
Temp1=SONG[Addr++];
Temp2=SONG[Addr++];
freq=25000/Temp1;
Buzzer_Freq_Set0( freq );
Delay(12*Temp2);
Uart_Printf("freq=%d,",freq);
if(Addr>215){
Addr=0;
}
}
}
c、修改占空比
void Buzzer_Freq_Set0(U32 freq ){
rGPBCON &= ~3; //set GPB0 as tout0, PWM output
rGPBCON |= 2;
rTCFG0 &= ~0xff;
rTCFG0 |= 15; //prescaler = 15+1
rTCFG1 &= ~0xf;
rTCFG1 |= 2; //mux = 1/8
rTCNTB0 = (PCLK>>7)/freq;
rTCMPB0 = rTCNTB0*0.03; //修改这一句,来调整占空比
rTCON &= ~0x1f;
rTCON |= 0xb; //disable deadzone, auto-reload, inv-off, update
TCNTB0&TCMPB0, start timer 0
rTCON &= ~2; //clear manual update bit
}
二、提高要求
A、B通过UDP发送文件,A发,B收并保存
1.实验前准备:
1、硬件连接
用USB线、串口线把开发板连到电脑相应的端口,再将电源线插好。
2、软件环境配置
设置串口工具SecureCRT
同基础内容部分设置相同,无需改动
设置Red Hat Enterprise Linux
在操作系统打开red hat enterprise linux 软件,
1、选文件server.c client.c到共享文件夹
2、在red hat enterprise linux将文件放到主目录root中
输入以下四行文字,如图所示
3、将root中的client文件放到共享盘
3、实验前准备
串口工具和开发板连接成功后,将选择开关打到norflash,并按一下重启键,开发板则自动按照选择从norflash 启动。
4、实验过程
1、在CRT中键入以下文字
cd sbin (路径切换到根目录)
ls (列出文件)
rz (选中client 添加)
rm (选中client 删除)
如下图所示:
3、在red hat enterprise linux中设置IP地址,端口号和保存的文件名
./server 8888 des.txt
./client 192.168.0.2 8888 des.txt
4、查看是否成功:root中的des.txt文件打开
4、具体函数
Client
#include #include #include #include #include #include #include #include #include #include #define FINISH_FLAG "FILE_TRANSPORT_FINISH" #define MAXLINE 1024 void usage(char *command) { printf("usage :%s ipaddr portnum filename\\n", command); exit(0); } int main(int argc,char **argv) { FILE *fp; struct sockaddr_in serv_addr; char buf[MAXLINE]; int sock_id; int read_len; int send_len; int serv_addr_len; int i_ret; int i; if (argc != 4) { usage(argv[0]); } /* open the file to be transported commanted by guoqingbo*/ if ((fp = fopen(argv[3],"r")) == NULL) { perror("Open file failed\\n"); exit(0); } /* create the socket commanted by guoqingbo*/ if ((sock_id = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { perror("Create socket failed"); exit(0); } memset(&serv_addr,0,sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(atoi(argv[2])); inet_pton(AF_INET, argv[1], &serv_addr.sin_addr); serv_addr_len = sizeof(serv_addr); /* connect the server commanted by guoqingbo*/ i_ret = connect(sock_id, (struct sockaddr *)&serv_addr, sizeof(struct sockaddr)); if (-1 == i_ret) { perror("Connect socket failed!\\n"); exit(0); } /* transport the file commented by guoqingbo*/ bzero(buf, MAXLINE); while ( (read_len = fread(buf, sizeof(char), MAXLINE, fp)) > 0 ) { send_len = send(sock_id, buf, read_len, 0); if ( send_len < 0 ) { perror("Send data failed\\n"); exit(0); } bzero(buf, MAXLINE); } fclose(fp); /* send the end_flag commented by guoqingbo*/ bzero(buf, MAXLINE); strcpy(buf, FINISH_FLAG); buf[strlen(buf)] = '\\0'; for (i = 1000; i>0; i--) { send_len = send(sock_id, buf, strlen(buf)+1, 0); if ( send_len < 0 ) { printf("Finish send the end string\\n"); break; } } close(sock_id); printf("Send finish\\n"); return 0; } Server #include #include #include #include #include #include #include #include #include #define FINISH_FLAG "FILE_TRANSPORT_FINISH" #define MAXLINE 1024 void usage(char *command) { printf("usage :%s portnum filename\\n", command); exit(0); } int main(int argc,char **argv) { struct sockaddr_in serv_addr; struct sockaddr_in clie_addr; char buf[MAXLINE]; int sock_id; int recv_len; int clie_addr_len; FILE *fp; if (argc != 3) { usage(argv[0]); } /* Create the the file commented by guoqingbo*/ if ((fp = fopen(argv[2], "w")) == NULL) { perror("Creat file failed"); exit(0); } if ((sock_id = socket(AF_INET,SOCK_DGRAM,0)) < 0) { perror("Create socket failed\\n"); exit(0); } /*fill the server sockaddr_in struct commented by guoqingbo*/ memset(&serv_addr,0,sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(atoi(argv[1])); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sock_id,(struct sockaddr *)&serv_addr,sizeof(serv_addr)) < 0 ) { perror("Bind socket faild\\n"); exit(0); } /* server part commented by guoqingbo*/ clie_addr_len = sizeof(clie_addr); bzero(buf, MAXLINE); while (recv_len = recvfrom(sock_id, buf, MAXLINE, 0,(struct sockaddr *)&clie_addr, &clie_addr_len)) { if(recv_len < 0) { printf("Recieve data from client failed!\\n"); break; } printf("#"); if ( strstr(buf, FINISH_FLAG) != NULL ) { printf("\\nFinish receiver finish_flag\\n"); break; } int write_length = fwrite(buf, sizeof(char), recv_len, fp); if (write_length < recv_len) { printf("File write failed\\n"); break; } bzero(buf, MAXLINE); } printf("Finish recieve\\n"); fclose(fp); close(sock_id); return 0; } 5、调试中遇到的问题 Client.txt文件有时候可以运行,有的时候不可以 经过检查函数发现,在client函数的结尾处有 buf[strlen(buf)] = '\\0' 所以需要到client.txt的原文件中在文段的最后加上回车,client文件即可成功调用。 4、实验体会及总结 通过此次的实验,对于Linux操作系统有了初步的认识。通过实验,感到理论课上的不少知识所学甚浅,在实际运用中会遇到一些意想不到的问题。但是,通过进一步学习与交流,我较好的完成了这次试验。这很好的补充了我在理论课上所学到的知识,同时让我有了很好的认识。 由于我的水平有限和实验的时间较为紧张,有些细节还是了解的十分不是十分详细,对于较为复杂的编程以及Linux下的很多技巧任然知之甚少,希望能多一些这样的实践性的教学环节,让我们更好的了解书本中知识的应用。 设计成绩评定评分内容 具体要求 总分 评分 上机时间 上机时间是否达到要求的学时,按照实际情况给与一定的成绩。 10分 报告审阅结果 报告结构严谨,文字通顺,用语符合技术规范,图表清楚,书写格式规范,不与别人雷同。 30分 验收结果 原理 原理清楚,能较好地理解课题任务并提出实施方案。 20分 完成情况 完成规定设计任务,论证、分析、设计、计算、结构、建模、实验正确合理,有一定的创新。 30分 操作 能熟练操作相关工具软件,并利用工具软件完成设计任务。 10分 总成绩(五分制) 100分 指导教师评阅意见