概述
【这是好久以前写的,有点乱,没时间整理,当做记录用的。】
图片粘贴失效,没上传图,想要的直接下载文档吧。
项目目的:通过IIS,触摸屏,LCD模块实现音乐播放器功能(按钮上一首、下一首、播放、暂停的音频控制功能,并实现播放歌曲时显示相应的歌曲图片,不播放时显示hello music图片)
项目设备:windows7(32位),mini2440,uboot(nandflash),ADS1.2开发环境,jlink v8,耳机;
功能模块LCD、触摸屏、IIS 分别介绍:
Main函数中主频设置:
U32 mpll_val = 0; mpll_val = (92<<12)|(1<<4)|(1); //init FCLK=400M, ChangeMPllValue((mpll_val>>12)&0xff, (mpll_val>>4)&0x3f, mpll_val&3); ChangeClockDivider(14, 12); //the result of rCLKDIVN [0:1:0:1] 3-0 bit cal_cpu_bus_clk(); //HCLK=100M PCLK=50M |
一、LCD(显示相应的图片)
我的LCD是统宝3.5 TFT屏,就是类似由240 x 320个会产生不同颜色的LED组成的,我的目的就是让这些点产生与图形相应的颜色。
LCD彩色图片转换工具转换bmp图片过程:将图片的第一个点转换为两个常量,然后一个点一个点转换就转换完一行,一行一行就完成一张图片(一帧)的数组数据转换,所产生的产量就是我们要的bmp图片数组了,这个转换和LCD显示时序是相同的。
LCD的时序显示图解:
从图中也可知道整个屏幕的点亮流程:是从一行的第一个点阵点亮,再一行里一点挨一点点亮,点亮完一行跳转下一行点亮,一行一行点亮完后,就完成了一张图片在LCD里的显示。
1、画图函数主要分析(原型见附录2):
c = bmp[p+1] | (bmp[p]<<8) ; // bmp数组是8位char类型,我采用16bpp[ 5 : 6 : 5 ]来显示,超过8位
LCD_BUFFER[y0+y][x0+x]= c ; // LCD_BUFFER数组存放图片显示数据
p= p + 2 ;
2、调用图片函数实参分析:
Pait_Bmp( X0, Y0, X1, Y1, hellomusic);
X0, Y0分别是图片显示的LCD横、纵坐标,
X1, Y1分别是图片横、纵向的点阵数, hellomusic是一个图片数组的首地址
3、将LCD_BUFFER图片数组写入LCD帧缓冲数据寄存器
#define M5D(n) ((n)&0x1fffff) //取低21位数据 #define LCD_ADDR ((U32)LCD_BUFFER) //宏定义LCD_ADDR为LCD_BUFFER图片数组首地址 // rLCDSADDR1中[20:0]存储的是LCD_ADDR图片数组的起始位置,但在LCD_ADDR中是[21:1],所以采用(M5D(LCD_ADDR>> 1) 运算将LCD_ADDR[21:1]存储于rLCDSADDR1[20:0]中 rLCDSADDR1 = ((LCD_ADDR>> 22) << 21) | (M5D(LCD_ADDR>> 1));
// rLCDSADDR2中[20:0]存储的是LCD_ADDR图片数组的结束位置 rLCDSADDR2 = M5D((LCD_ADDR+ LCD_WIDTH * LCD_HEIGHT * 2) >> 1); |
4、LCD寄存器初始化:
/************************************************************** TFT LCD功能模块初始化//此函数没开启屏蔽功能 **************************************************************/ void LCD_Init(void) { #define M5D(n) ((n)&0x1fffff) #define LCD_ADDR ((U32)LCD_BUFFER) //宏定义LCD_ADDR为LCD_BUFFER图片数组首地址
//Con1 控制寄存器命令在下面解释 //Con2 //Con3 //Con4 //Con5
rLCDSADDR1 = ((LCD_ADDR>> 22) << 21) | ((M5D(LCD_ADDR>> 1)) << 0); rLCDSADDR2 = M5D((LCD_ADDR+ LCD_WIDTH * LCD_HEIGHT * 2) >> 1); rLCDSADDR3 = LCD_WIDTH;
rLCDINTMSK |= 3; //中断屏蔽 rTCONSEL &= (~7);
rTPAL = 0x0; rTCONSEL &= ~((1<<4) | 1); } |
Con1:
#define LCD_PIXCLOCK 4 //con1[17:8]CLKVAL设置为4,根据TFT:VCLK= HCLK / [(CLKVAL + 1) × 2] ,VCLK=10MHz,VCLK就是显示速率 [6:5]显示模式选择为 TFT LCD面板 模式 [4:1]选择为 TFT的16bpp 像素模式 rLCDCON1 = (LCD_PIXCLOCK << 8) | (3 << 5) | (12 << 1); |
Con2
#define LCD_UPPER_MARGIN 1 #define LCD_HEIGHT 320 #define LCD_LOWER_MARGIN 4 //con2[23:14]设置高度320 VSPW[5:0]决定相应脉冲的宽度; VFPD[13:6]、VBPD[31:24]决定了延时时间; rLCDCON2 = (LCD_UPPER_MARGIN << 24) | ((LCD_HEIGHT- 1) << 14) | (LCD_LOWER_MARGIN<< 6) | (LCD_VSYNC_LEN << 0); |
Con3
#define LCD_RIGHT_MARGIN 25 #define LCD_WIDTH 240 #define LCD_LEFT_MARGIN 0 //con3[25:19]设置HBPD为25,[7:0]设置HFPD为0,[18:8]设置高度240;HFPD、HBPD决定了延时时间; rLCDCON3 = (LCD_RIGHT_MARGIN << 19) | ((LCD_WIDTH - 1) << 8) | (LCD_LEFT_MARGIN << 0); |
Con4
#define LCD_HSYNC_LEN 4 //con4 HSPW[7:0]决定相应脉冲的宽度; rLCDCON4 = (13 << 8) | (LCD_HSYNC_LEN << 0); |
Con5
//con5[11] 5:6:5输出格式,[9]、[8]脉冲翻转,[3]允许PWREN信号,[0]半字节交换使能; rLCDCON5 = ((1<<11) | (1 << 9) | (1 << 8) | (1 << 3) | (1 << 0)); |
5、LCD视频输出控制函数与开启电源函数
/************************************************************** LCD视频和控制信号输出或者停止,1开启视频输出 **************************************************************/ void Lcd_EnvidOnOff(int onoff){ if(onoff==1) rLCDCON1|=1;// ENVID=ON else rLCDCON1 =rLCDCON1& 0x3fffe;// ENVID Off } /************************************************************** TFT LCD 电源控制引脚使能 ,Lcd_PowerEnable(0,1)就开启了LCD电源 **************************************************************/ void Lcd_PowerEnable(int invpwren,int pwren){ rGPGUP = rGPGUP|(1<<4);//禁止上拉使能 rGPGCON = rGPGCON|(3<<8);//GPG4=LCD_PWREN //开启LCD电源 rLCDCON5 = rLCDCON5&(~(1<<3))|(pwren<<3); // PWREN rLCDCON5 = rLCDCON5&(~(1<<5))|(invpwren<<5); // INVPWREN } |
LCD总结:
通过调用上面的函数:
LCD_Init(); // TFT LCD功能模块初始化
Lcd_PowerEnable(0,1); // TFT LCD 电源控制引脚使能
Lcd_EnvidOnOff(1); // LCD视频和控制信号输出或者停止,1开启视频输出
就可以使用Pait_Bmp( X0, Y0, X1, Y1, hellomusic);来实现LCD显示图片了
二、触摸屏(点击屏幕时产生中断)
中断是当硬件支持中断时,设置相应的控制寄存器开启中断,当中断发生时CPU跳转到中断程序完成中断操作,以提高操作效率。
这个解释很短,但是在项目试验中才发现中断真的很神奇,而且很省事,不用再写别的逻辑函数以实现相同的功能,也不会有这么高的处理效率,而且写出来的程序也会变很大。触摸屏就支持中断操作。
1、寄存器设置
当使用触摸屏功能时,需将XP,XM,YP,YM设置为触摸屏引脚使能,寄存器设置为:
void Touch_Init(void) { rADCCON=((1<<14)|(9<<6)); //A/D分频时钟有效,其值为9 rADCTSC=0xd3; //光标按下中断信号,YM有效,YP无效,XM有效,XP无效,XP上拉电阻,普通ADC转换,等待中断模式 rADCDLY=50000;//正常转换模式转换延时大约为(1/3.6864M)*50000=13.56ms
rINTSUBMSK &=~(0x1<<9); //TC中断使能 rINTMSK &=~((U32)0x1<<31); //ADC总中断使能
pISR_ADC=(U32)Adc_Tc_Handler; //指向中断向量表
} |
二、中断处理函数
触摸屏中断是有sub寄存器的,通过图14-1可以看出中断发生是:
中断请求中断服务>>次级源中断挂起>>SRCPND>>仲裁>>INTPND>>irq中断>>中断函数入口
中断函数:
/************************************************************** TFT LCD *触摸屏中断函数* **************************************************************/ static void __irq Adc_Tc_Handler(void) {
rADCTSC|=(1<<3)|(1<<2);//XP上拉电阻无效,自动连续测量X坐标和Y坐标. rADCCON|=(1<<0);//ADC转换开始
while(rADCCON&(1<<0));//检测ADC转换是否开始且ADCCON[0]自动清0 while(!(rADCCON&(0x1<<15)));//检测ADCCON[15]是否为1,ADC转换是否结束,(必须) while(!(rINTPND&((U32)0x1<<31)));//检测ADC中断是否已请求
//这里就是将转换的坐标值存储在 xdata、ydata; xdata=rADCDAT0&0x3ff;//读x坐标 >>xdata并不是像素点,而是模拟信号 0-1000 ydata=rADCDAT1&0x3ff;//读y坐标
rSUBSRCPND|=(0x1<<9); //清除中断 rSRCPND|=((U32)0x1<<31); rINTPND|=((U32)0x1<<31);
rADCTSC =0xd3; //ADC等待中断模式 rADCTSC|=(0x1<<8); //ADCTSC[8]=1,设置抬起中断信号
while(!(rSUBSRCPND&(0x1<<9))); //检测触屏抬起中断是否已请求
rADCTSC &=~(0x1<<8);//ADCTSC[8]=0光标按下中断信号
rSUBSRCPND|=(0x1<<9); rSRCPND|=((U32)0x1<<31); rINTPND|=((U32)0x1<<31); } |
触摸屏总结:在开启LCD电源时 Lcd_PowerEnable(0,1); // TFT LCD 电源控制引脚使能
初始化触摸屏中断 Touch_Init();
每次中断产生的坐标值都会存储在xdata、ydata;
三、IIS(实现将音频文件按一定频率传输)
1、IIS预分频值的确定
IIS我设置DMA无效,FIFO输出模式,设置了预分频值,保证传输的速率和采样频率一样。
IISSCLK为串行时钟,每一个时钟信号传送一位音频信号。因此IISSCLK的频率=声道数×采样频率×采样位数,如采样频率fs为44.1kHz,采样的位数为16位,声道数2个(左、右两个声道),则IISSCLK的频率=32fs=1411.2kHz。
1、Fs 2、采样的位数 3、声道数
使用 wav数据提取器 查看
采样频率
右键音乐文件à属性à详细信息
PCLK(50MHZ)经过两个预分频器处理后分别得到IISSCLK、IISLRCK和CDCLK,寄存器IISPSR是IIS预分频器寄存器,5~9位是预分频器A,0~4位是预分频器B,一般来说,这两个预分频器的值N相等,即只要知道一个,另一个也就知道,而这里我们是通过CDCLK来计算预分频器B的值N的,即CDCLK=PCLK / (N+1)。
我设置主时钟频率( CODECLK )选择384fs,由下表,因fs=44.1kHZ,CODECLK=16.9344。
可知N= PCLK(50MHZ)/ CODECLK(16.9344MHZ) -1 = 2 ;
|
IIS初始化
/************************************************************** IIS初始化 **************************************************************/ void iis_init(void){
//配置IIS接口 rGPEUP = rGPEUP& ~(0x1f)| 0x1f; //上拉无效,GPE[4:0] 1 1111 rGPECON = rGPECON& ~(0x3ff)| 0x2aa;
//配置s3c2440的IIS寄存器 //预分频器为2,所以CDCLK=PCLK/(2+1)=16.66666kHz rIISPSR = 2<<5|2; //无效DMA,输入空闲,预分频器有效,开启IIS rIISCON = (0<<5)|(0<<4)|(0<<3)|(1<<2)|(1<<1); //PCLK为时钟源,输出模式,IIS模式,每个声道16位,CODECLK=384fs,SCLK=32fs rIISMOD = (0<<9)|(0<<8)|(2<<6)|(0<<5)|(0<<4)|(1<<3)|(1<<2)|(1<<0); rIISFCON = (0<<15)|(1<<13); //输出FIFO正常模式,输出FIFO使能
} |
UDA1341
在我看来,IIS就是实现将音频(WAV)数据按一定频率发送一定大小的数据给FIFO或者DMA,UDA1341就是将这些音频数据转换成电信号通过音频接口发送出去,而关于uda1341的配置是参考来的
//通过io口模拟L3总线写数据 //mode:1为地址模式,0为数据模式 //关于地址模式和数据模式以及传输时序注意参考数据手册 static void write_UA1341(U8 data, U8 address) { int i,j; if(address== 1) { rGPBDAT = rGPBDAT&(~(L3D| L3M |L3C))|L3C; //地址模式,根据手册L3M为LOW,L3C为high } else { rGPBDAT = rGPBDAT& (~(L3D|L3M |L3C))|(L3M|L3C); //数据模式 L3M为高 } Delay(1); //传输数据 for(i=0;i<8;i++) { if(data& 0x1) // H { rGPBDAT &=~L3C; //L3C=L rGPBDAT |= L3D; //L3D=H for(j=0;j<5;j++) ; //等待一段时间 rGPBDAT |= L3C; //L3C=H rGPBDAT |= L3D; //L3D=H for(j=0;j<5;j++) ; //等待一段时间 } else // L { rGPBDAT &=~L3C; //L3C=L rGPBDAT &=~L3D; //L3D=L for(j=0;j<5;j++) ; //等待一段时间 rGPBDAT |= L3C; //L3C=H rGPBDAT &=~L3D; //L3D=L for(j=0;j<5;j++) ; //等待一段时间 } data >>=1; } rGPBDAT = rGPBDAT& ~(L3D| L3M | L3C)| (L3C | L3M); //L3M=H,L3C=H } |
|
UDA1341初始化
//UDA1341初始化 //配置L3接口总线,GPB2:L3MODE, GPB3:L3DATA, GPB4:L3CLOCK rGPBCON = 0x015550; //输出 rGPBUP = 0x7ff; //上拉无效 rGPBDAT = 0x1e4;
rGPBDAT = rGPBDAT& (~(L3M|L3C |L3D))|(L3M|L3C); //将L3CLOCK和L3MODE置高,准备开始传输 根据UDA1341TS数据手册14页中的操作顺序,首先在地址模式下, //选择操作地址000101xx +10(STATUS)=0X16 write_UA1341(0x16,1); write_UA1341(0x60,0);// 0,1,10,000,0 复位
write_UA1341(0x16,1); write_UA1341(0x10,0); //0,0,01, 000,0 : 状态0, 384fs,IIS,no DC-filtering
write_UA1341(0x16,1); write_UA1341(0xc1,0); //1,0,0,0, 0,0,01:状态1, //Gain of DAC 6 dB,Gain of ADC 0dB,ADC non-inverting, //DAC non-inverting,Single speed playback,ADC-Off DAC-On
|
|
IIS总结:
初始化IIS后 iis_init(); // IIS初始化
UDA1341_init();
就可以通过以下语句实现歌曲的播放
以下语句是在while循环下 buffer=music1; //初始化buffer指向music1音乐数组地址
if((rIISCON& (1<<7))==0) //检查输出FIFO是否为空 { //FIFO中的数据为16位,深度为32 //当输出FIFO为空时,一次性向FIFO写入32个16位数据 for(i=0;i<32;i++){ rIISFIFO=(buffer[2*i+count])+(buffer[2*i+1+count]<<8); //一次循环向FIFO存储16位数据于FIFO } count+=64; // 64为32次循环,每次循环指向两个不同字节的字节总和
|
四、界面素材
第一排图片为按钮图片,较暗的那张为点击后短暂显示的图片
背景图:
五、WAV音频文件的制作
1、 网上下载无损音乐(我下载了WAV格式和APE格式的)
2、 打开格式工厂à音频à->WAVà添加文件à截取片段(无损音乐太大了,需要截取一小部分,20秒一首就可以了)à确定à输出配置(如下图)à确定à点击开始。
3、 打开截取WAV文件夹
4、 使用DataToHex将WAV文件下的WAV转换成音频数据数组文件,修改数组文件名Array[],
5、 将wav音频数据文件改为c文件,请不要改为h,不然debug要好久。
6、 制作完成!
主函数实现功能:
/************************************************************** music *子main函数* **************************************************************/
void music_player(void) { static U32 save_A, save_C, save_CP, save_D, save_DP,save_G, save_GP,save_INTS,save_INT, save_E, save_EP, save_B, save_LCD1, save_LCD2, save_LCD3, save_LCD4, save_LCD5, save_SR, save_S, save_SM, save_SF, save_LCDINT, save_BD;
//**********************保护现场************************** save_SR = rIISPSR; save_S = rIISCON; save_SM = rIISMOD; save_SF = rIISFCON; save_A = rADCCON; save_B = rGPBCON; save_BD = rGPBDAT; save_EP = rGPEUP; save_E = rGPECON; save_G = rGPGCON; save_GP = rGPGUP; save_C = rGPCCON; save_CP = rGPCUP; save_D = rGPDCON; save_DP = rGPDUP; save_LCD1 = rLCDCON1; save_LCD2 = rLCDCON2; save_LCD3 = rLCDCON3; save_LCD4 = rLCDCON4; save_LCD5 = rLCDCON5; save_LCDINT = rLCDINTMSK; save_INTS = rINTSUBMSK; save_INT = rINTMSK; //**********************开启LCD显示功能**********************************
Lcd_Port_Init();//端口初始化 LCD_Init(); //TFT LCD功能模块初始化 Lcd_PowerEnable(0,1); // TFT LCD 电源控制引脚使能 Lcd_EnvidOnOff(1); //LCD视频和控制信号输出或者停止,1开启视频输出
//******************************************************** 显示图片界面 //******************************************************** Pait_Bmp( 0, 0, 240, 320, music_interface); Pait_Bmp( 70, 70, 100, 100, hellomusic); Pait_Bmp( 20, 190, 60, 60, button2); //上一首 Pait_Bmp(160,190,60,60, button3); //下一首 Pait_Bmp( 90, 190, 60, 60, button4); //播放
//******************触摸屏中断初始化,IIS数据流设置************ Touch_Init(); //开触摸屏中断 iis_init(); // IIS初始化 UDA1341_init(); //UDA1341初始化
buffer=music1; //初始化buffer指向music1音乐数组地址
Delay(100); Uart_Printf("nnq:退出n"); while(Uart_GetKey()!= 'q') {
// button1是 播放 图片函数 ,button1_1是 播放 阴影图片函数; // button2是 上一首 图片函数 ,button2_2是 上一首 阴影图片函数; // button3是 下一首 图片函数 ,button3_3是 下一首 阴影图片函数; // button4是 暂停 图片函数 ,button4_4是 暂停 阴影图片函数; // flag 为 1 播放状态 ,flag 为 0 暂停状态 ; // song_num 歌曲序号标志,song_num1 歌曲跳转标志,只有按上一首、下一首song_num1才不为0,一般为0;
if(flag==1){ rIISCON|= 0x1; //如果点击播放(flag==1) 开启IIS song_num1初值为1 播放第一首
//******************播放相应歌曲显示相应的图片 ************ if(flag1==1&& song_num==1){flag1=0, Pait_Bmp(70,70,100,100, music1_bmp);} if(flag1==1&& song_num==2){flag1=0, Pait_Bmp(70,70,100,100, music2_bmp);} if(flag1==1&& song_num==3){flag1=0, Pait_Bmp(70,70,100,100, music3_bmp);}
//***************新一首歌曲处理语句,song_num1一般为0,当为具体数时就为新一首歌曲序号 //*************** buffer音乐播放地址赋初值 length音乐长度重新赋值 并显示音乐图片 //*************** 下一FIFO字节位置count初始化 song_num1归零
if(song_num1==1){ buffer=music1; length=3704572; count=0; Pait_Bmp(70,70,100,100, music1_bmp); song_num1=0;}
if(song_num1==2){ buffer=music2; length=3704552; count=0; Pait_Bmp(70,70,100,100, music2_bmp); song_num1=0;}
if(song_num1==3){ buffer=music3; length=5644880; count=0; Pait_Bmp(70,70,100,100, music3_bmp); song_num1=0;}
//****************播放音频数据语句*************************** if((rIISCON& (1<<7))==0) //检查输出FIFO是否为空 { //FIFO中的数据为16位,深度为32 //当输出FIFO为空时,一次性向FIFO写入32个16位数据 for(i=0;i<32;i++){ rIISFIFO=(buffer[2*i+count])+(buffer[2*i+1+count]<<8); // 一次循环向FIFO存储16位数据于FIFO }
count+=64; // 64为(32次循环x每次循环指向两个不同字节)的字节总和
//**************音乐播放完成后 音乐标志song_num1指向下一首歌 if(count>length){ song_num2=song_num2+1; if(song_num2==4){song_num2=1;} song_num1=song_num2; } } }
//**************暂停时关闭IIS 显示欢迎图片 if(flag==0){ rIISCON |=0x0; Pait_Bmp(70,70,100,100, hellomusic);} //关闭IIS;
}
Lcd_ClearScr( (0x1f<<11)| (0x3f<<5)| (0x1f) ); //白色 //**********************还原现场************************** rIISPSR = save_SR; rIISCON = save_S; rIISMOD = save_SM; rIISFCON = save_SF; rADCCON = save_A; rGPBCON = save_B; rGPBDAT = save_BD; rGPEUP = save_EP; rGPECON = save_E; rGPGCON = save_G; rGPGUP = save_GP; rGPCCON = save_C; rGPCUP = save_CP; rGPDCON = save_D; rGPDUP = save_DP; rLCDCON1 = save_LCD1; rLCDCON2 = save_LCD2; rLCDCON3 = save_LCD3; rLCDCON4 = save_LCD4; rLCDCON5 = save_LCD5; rLCDINTMSK = save_LCDINT; rINTSUBMSK = save_INTS; rINTMSK = save_INT; //******************************************************** } |
遇到的问题与解决
做这个项目的时候遇到过很多问题,LCD是怎么点亮的,颜色是怎么产生的?触摸屏的是怎么实现存储我的坐标地址的?还有IIS的FIFO传输问题问题,IIS的音频文件是什么格式的?如何用代码设计出自己想要的功能?
1、当时音频文件困扰我好久,使用友善之臂提供的音频文件可以播放,但是自己制作的却不行,网上也没找到原因,mp3、wav这些常见的格式音乐一个一个试,都不行,结果发现现在的压缩技术已经将这些音频文件压缩的很小,方便存储而且不失真,但是这些都不是这个项目需要的无损音频文件,找到无损音频文件后,才把我的音频文件问题解决了;
2、而关于IIS的FIFO传输,FIFO一个数据块是有64个字节的,当时我使用过分64次循环,一次传输一个字节出来,结果是沙哑的音质,后面再将FIFO设置成32次,一次传输两个字节,结果奇迹般的放出了自己爱听的歌曲,一次传输两个字节,也就是实现双声道的效果吧
3、还记得当时有过LCD显示图片移位的现象,这是因为自己对 Pait_Bmp(intx0,int y0,int h,int l,const unsigned char *bmp)函数里的参数没设置好
以上都是项目中出现的一些比较困扰的问题,平时还会遇到很多调试问题,这些问题一般博客里都会有人做记录的,自己也就可以学习和解决,但是当遇到一些中断、地址映射、系统加载过程、nandflash和norflash区别、uboot烧写到nandflash后裸机怎么运行的、GPBDAT写入控制命令怎么能控制模块等等问题时,就需要多次使用和运行后才明白。
1、中断:经过多次中断实验终于明白了中断时,CPU的跳转与所带来的方便;
2、地址映射:内存就这么大,为什么加载一块大的存储器时还能能够读取到那么高的地址?这就是地址映射强大;
3、系统加载:裸机下的2440init.S文件,写驱动时需要烧写系统时的bootloader,这些都是为了初始化CPU、
硬件平台,2440init.S还为裸机提供物理上的引脚定义(初始化),bootloader则还还提供Linux所需的接口,加载完Linux内核,之后就是文件系统了
裸机:2440init.S --àMain函数--à 具体函数功能
Linux:bootloader --àlinux内核 à 文件系统
4、nandflash和norflash区别:norflash里面的数据是可以直接执行的,但是容量小;nandflash容量更大,但是需要前4k映射片选空间。
5、uboot烧写到nandflash后裸机的运行, nandflash内的文件不能直接运行,要将前4k映射片选空间才可使用,而且uboot烧写到nandflash后,运行裸机文件并不是在SDRAM的起始位置,被uboot先使用了SDRAM前面的空间,而在编译器下设置的裸机的SDRAM起始位置为0x30000000,所以需要将裸机运行的起始地址在编译器里初始化,初始化文件见附录1,不然裸机会跑飞。
6、而关于GPBDAT写入控制命令能控制模块,是因为这个宏定义:#define GPBDAT (*(volatile unsigned long*)0x56000014),GPBDAT宏定义为0x56000014寄存器地址内的值,而寄存器地址的初始化是在头文件2440init.S文件
很多东西都是自己做项目再做总结, 总结中可能会发现新问题或者找到曾经迷惑的问题,但是平常遇到的问题主要还是通过查博客解决问题,做项目、看文档、查手册、问同学老师、读博客、写博客,这是我学习进步的方法,我也会坚持下去的。
————————————————————项目介绍完—————————————————————
附录
1、
Setmem 0x53000000 0x00000000 32
Setmem 0x4A000008 0xFFFFFFFF 32
Setmem 0x4A00001C 0x000007FF 32
Setmem 0x53000000 0x00000000 32
Setmem 0x56000050 0x000055AA 32
Setmem 0x4C000014 0x00000007 32
Setmem 0x4C000000 0x00FFFFFF 32
Setmem 0x4C000004 0x00061012 32
Setmem 0x4C000008 0x00040042 32
Setmem 0x48000000 0x22111120 32
Setmem 0x48000004 0x00002F50 32
Setmem 0x48000008 0x00000700 32
Setmem 0x4800000C 0x00000700 32
Setmem 0x48000010 0x00000700 32
Setmem 0x48000014 0x00000700 32
Setmem 0x48000018 0x0007FFFC 32
Setmem 0x4800001C 0x00018005 32
Setmem 0x48000020 0x00018005 32
Setmem 0x48000024 0x008E0459 32
Setmem 0x48000028 0x00000032 32
Setmem 0x4800002C 0x00000030 32
Setmem 0x48000030 0x00000030 32
2、
/************************************************************** TFT LCD *在屏幕上画图* **************************************************************/ void Pait_Bmp(int x0,int y0,int h,int l,const 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) && ( (y0+y) < SCR_YSIZE) ) LCD_BUFFER[y0+y][x0+x] = c ;
p = p + 2 ; } } } |
3、项目整个代码
/*********************************** 实现功能 music播放器 ***********************************/
#include "2440lib.h" #include "2440slib.h" #include "LCD_init.h" #include "2440addr.h"
#define L3C (1<<4) //gpb4:L3CLOCK #define L3D (1<<3) //gpb3:L3DATA #define L3M (1<<2) //gpb2:L3MODE #define rIISFIFO (*(volatile unsigned long*)0x55000010)
extern unsigned char music_interface[]; extern unsigned char hellomusic[]; //hellomusic extern unsigned char music1_bmp[]; extern unsigned char music2_bmp[]; extern unsigned char music3_bmp[];
extern unsigned char button1[]; //暂停 extern unsigned char button1_1[]; extern unsigned char button2[]; //下一首 extern unsigned char button2_2[]; extern unsigned char button3[]; //上一首 extern unsigned char button3_3[]; extern unsigned char button4[]; //播放 extern unsigned char button4_4[];
extern unsigned char music1[3704572]; extern unsigned char music2[3704552]; extern unsigned char music3[5644880];
//xdata, ydata用于存储屏幕坐标(不是屏幕像素点阵) volatile static int xdata, ydata; //volatile的作用;作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值. volatile static int botten1=0, flag=0, flag1, song_num=1, song_num1=1; volatile static int length, count, song_num2=1, i; volatile static unsigned char *buffer;
/************************************************************** 键值处理函数 **************************************************************/ static void Button_Handle(void){ // button1是 播放 图片函数 ,button1_1是 播放 阴影图片函数; // button2是 上一首 图片函数 ,button2_2是 上一首 阴影图片函数; // button3是 下一首 图片函数 ,button3_3是 下一首 阴影图片函数; // button4是 暂停 图片函数 ,button4_4是 暂停 阴影图片函数; // flag 为 1 播放状态 ,flag 为 0 暂停状态 ; // song_num 歌曲序号标志,song_num1 歌曲跳转标志,只有按上一首、下一首song_num1才不为0,一般为0;
if( xdata>= 160&& xdata <=360 && ydata>= 280&& ydata <420 ) //上一首并播放 { Beep(2000,100); botten1= 1, song_num--; flag=1; if(song_num==0){ song_num=3;} song_num1=song_num; Pait_Bmp(20,190,60,60, button2_2); Delay(200); //显示 上一首 阴影图片 Pait_Bmp( 90, 190, 60, 60, button1); Pait_Bmp(20,190,60,60, button2);} //显示播放、上一首
if( xdata>= 660&& xdata <=860 && ydata>= 280&& ydata <420 ) //下一首并播放 { Beep(2000,100); botten1= 2, song_num++; flag=1;
if(song_num==4){ song_num=1;} song_num1=song_num; Pait_Bmp(160,190,60,60, button3_3); Delay(200); Pait_Bmp( 90, 190, 60, 60, button1); Pait_Bmp(160,190,60,60, button3);}
if( xdata>= 410&& xdata <=610 && ydata>= 280&& ydata <420 ) //播放 { Beep(2000,100); flag++; //下一段很凑巧,当点击(上一首/下一首) flag赋值为1时,下面这段也会执行 //我想应该是 ydata >= 280 && ydata < 420相与为1后, xdata >= 410 && xdata <= 610 && ydata >= 280 && ydata < 420的值也为1 //刚好实现了我的功能 if(flag==1){botten1= 0; flag1=flag; Pait_Bmp(90,190,60,60, button4_4); Delay(200); Pait_Bmp( 90, 190, 60, 60, button1);} //为1时播放,显示暂停按钮
else {flag=0; botten1= 0; Pait_Bmp(90,190,60,60, button1_1); Delay(200); Pait_Bmp( 90, 190, 60, 60, button4);} //为0时暂停,显示播放按钮 } }
/************************************************************** TFT LCD *触摸屏中断函数* **************************************************************/
static void __irq Adc_Tc_Handler(void) {
rADCTSC|=(1<<3)|(1<<2);//XP上拉电阻无效,自动连续测量X坐标和Y坐标. rADCCON|=(1<<0);//ADC转换开始
while(rADCCON&(1<<0));//检测ADC转换是否开始且ADCCON[0]自动清0 while(!(rADCCON&(0x1<<15)));//检测ADCCON[15]是否为1,ADC转换是否结束,(必须) while(!(rINTPND&((U32)0x1<<31)));//检测ADC中断是否已请求
xdata=rADCDAT0&0x3ff;//读x坐标 >>xdata并不是像素点,而是模拟信号 0-1000 ydata=rADCDAT1&0x3ff;//读y坐标 //Beep(2000, 100); Button_Handle(); //button处理函数 Uart_Printf("n Xdata=%04d, Ydata=%04dn", xdata, ydata); Uart_Printf("n flag=%d, 1:播放 0:暂停", flag); Uart_Printf("n botten1=%d, 1:上一首 2:下一首",botten1); Uart_Printf("n **正在播放第%d首歌曲** nn", song_num);
rSUBSRCPND|=(0x1<<9); //清除中断 rSRCPND|=((U32)0x1<<31); rINTPND|=((U32)0x1<<31);
rADCTSC =0xd3; //ADC等待中断模式 rADCTSC|=(0x1<<8); //ADCTSC[8]=1,设置抬起中断信号
while(!(rSUBSRCPND&(0x1<<9))); //检测触屏抬起中断是否已请求
rADCTSC &=~(0x1<<8);//ADCTSC[8]=0光标按下中断信号
// 由于下面这段代码和上面这段代码是看到大神们都会加上去的,调试得到 // 结论:上面的清除中断实现触屏中断,而下面这段则是加快中断后的响应 // (我的实验结果是:没下面的时候,触屏后的数值显示时间变长,蜂鸣器的 // 响声也变长,有种慢一拍的感觉)
rSUBSRCPND|=(0x1<<9); rSRCPND|=((U32)0x1<<31); rINTPND|=((U32)0x1<<31); }
static void Touch_Init(void) { rADCCON=((1<<14)|(9<<6)); //A/D分频时钟有效,其值为9 rADCTSC=0xd3; //光标按下中断信号,YM有效,YP无效,XM有效,XP无效,XP上拉电阻,普通ADC转换,等待中断模式 rADCDLY=50000;//正常转换模式转换延时大约为(1/3.6864M)*50000=13.56ms
rINTSUBMSK &=~(0x1<<9);//TC中断使能 rINTMSK &=~((U32)0x1<<31);//ADC总中断使能
pISR_ADC=(U32)Adc_Tc_Handler;//指向中断向量表
}
/************************************************************** IIS初始化 **************************************************************/ void iis_init(void){
//配置IIS接口 rGPEUP = rGPEUP& ~(0x1f)| 0x1f; //上拉无效,GPE[4:0] 1 1111 rGPECON = rGPECON& ~(0x3ff)| 0x2aa;
//配置s3c2440的IIS寄存器 //预分频器为2,所以CDCLK=PCLK/(2+1)=16.66666kHz rIISPSR = 2<<5|2; //无效DMA,输入空闲,预分频器有效,开启IIS rIISCON = (0<<5)|(0<<4)|(0<<3)|(1<<2)|(1<<1); //PCLK为时钟源,输出模式,IIS模式,每个声道16位,CODECLK=384fs,SCLK=32fs rIISMOD = (0<<9)|(0<<8)|(2<<6)|(0<<5)|(0<<4)|(1<<3)|(1<<2)|(1<<0); rIISFCON = (0<<15)|(1<<13); //输出FIFO正常模式,输出FIFO使能
}
/************************************************************** UA1341函数 **************************************************************/ //通过io口模拟L3总线写数据 //mode:1为地址模式,0为数据模式 //关于地址模式和数据模式以及传输时序注意参考数据手册 static void write_UA1341(U8 data, U8 address) { int i,j; if(address== 1) { rGPBDAT = rGPBDAT&(~(L3D| L3M |L3C))|L3C; //地址模式,根据手册L3M为LOW,L3C为high
} else {
rGPBDAT = rGPBDAT& (~(L3D|L3M |L3C))|(L3M|L3C); //数据模式 L3M为高
} Delay(1); //传输数据 for(i=0;i<8;i++) { if(data& 0x1) // H { rGPBDAT &=~L3C; //L3C=L rGPBDAT |= L3D; //L3D=H for(j=0;j<5;j++) ; //等待一段时间 rGPBDAT |= L3C; //L3C=H rGPBDAT |= L3D; //L3D=H for(j=0;j<5;j++) ; //等待一段时间 } else // L { rGPBDAT &=~L3C; //L3C=L rGPBDAT &=~L3D; //L3D=L for(j=0;j<5;j++) ; //等待一段时间 rGPBDAT |= L3C; //L3C=H rGPBDAT &=~L3D; //L3D=L for(j=0;j<5;j++) ; //等待一段时间 } data >>=1; } rGPBDAT = rGPBDAT& ~(L3D| L3M | L3C)| (L3C | L3M); //L3M=H,L3C=H
}
/************************************************************** UDA1341初始化 **************************************************************/ void UDA1341_init(void){ //UDA1341初始化 //配置L3接口总线,GPB2:L3MODE, GPB3:L3DATA, GPB4:L3CLOCK rGPBCON = 0x015550; //输出 rGPBUP = 0x7ff; //上拉无效 rGPBDAT = 0x1e4;
rGPBDAT = rGPBDAT& (~(L3M|L3C |L3D))|(L3M|L3C); //将L3CLOCK和L3MODE置高,准备开始传输 根据UDA1341TS数据手册14页中的操作顺序,首先在地址模式下, //选择操作地址000101xx +10(STATUS)=0X16 write_UA1341(0x16,1); write_UA1341(0x60,0);// 0,1,10,000,0 复位
write_UA1341(0x16,1); write_UA1341(0x10,0); //0,0,01, 000,0 : 状态0, 384fs,IIS,no DC-filtering
write_UA1341(0x16,1); write_UA1341(0xc1,0); //1,0,0,0, 0,0,01:状态1, //Gain of DAC 6 dB,Gain of ADC 0dB,ADC non-inverting, //DAC non-inverting,Single speed playback,ADC-Off DAC-On }
/************************************************************** music *子main函数* **************************************************************/
void music_player(void) { static U32 save_A, save_C, save_CP, save_D, save_DP,save_G, save_GP,save_INTS,save_INT, save_E, save_EP, save_B, save_LCD1, save_LCD2, save_LCD3, save_LCD4, save_LCD5, save_SR, save_S, save_SM, save_SF, save_LCDINT, save_BD;
//**********************保护现场************************** save_SR = rIISPSR; save_S = rIISCON; save_SM = rIISMOD; save_SF = rIISFCON; save_A = rADCCON; save_B = rGPBCON; save_BD = rGPBDAT; save_EP = rGPEUP; save_E = rGPECON; save_G = rGPGCON; save_GP = rGPGUP; save_C = rGPCCON; save_CP = rGPCUP; save_D = rGPDCON; save_DP = rGPDUP; save_LCD1 = rLCDCON1; save_LCD2 = rLCDCON2; save_LCD3 = rLCDCON3; save_LCD4 = rLCDCON4; save_LCD5 = rLCDCON5; save_LCDINT = rLCDINTMSK; save_INTS = rINTSUBMSK; save_INT = rINTMSK; //********************************************************
Lcd_Port_Init();//端口初始化 LCD_Init(); //TFT LCD功能模块初始化 Lcd_PowerEnable(0,1); // TFT LCD 电源控制引脚使能 Lcd_EnvidOnOff(1); //LCD视频和控制信号输出或者停止,1开启视频输出
/*红(255:0:0);绿(0:255:0);蓝(0:0:255);黑(0:0:0);白(255,255,255)*/
/*在屏幕上显示三基色*/
Lcd_ClearScr((0x1f<<11)| (0x00<<5)| (0x00)); //red Delay(500);
Lcd_ClearScr((0x00<<11)| (0x3f<<5)| (0x00)); //green Delay(500);
Lcd_ClearScr((0x00<<11)| (0x00<<5)| (0x1f)); //blue Delay(500);
Lcd_ClearScr( (0x1f<<11)| (0x3f<<5)| (0x1f) ) ; //clear screen white Delay(500);
Pait_Bmp( 0, 0, 240, 320, music_interface); Pait_Bmp( 70, 70, 100, 100, hellomusic); Pait_Bmp( 20, 190, 60, 60, button2); //上一首 Pait_Bmp(160,190,60,60, button3); //下一首 Pait_Bmp( 90, 190, 60, 60, button4); //播放
Touch_Init(); //开触摸屏中断 iis_init(); // IIS初始化 UDA1341_init(); //UDA1341初始化
buffer=music1; //初始化buffer指向music1音乐数组地址
//while(1) Delay(100);Uart_Printf("nnq:退出n"); while(Uart_GetKey()!= 'q') { if(flag==1){ rIISCON|= 0x1; //如果点击播放(flag==1) 开启IIS song_num1初值为1 播放第一首
//处理点击播放时音乐图片的显示(点击播放时 flag1=1) if(flag1==1&& song_num==1){flag1=0, Pait_Bmp(70,70,100,100, music1_bmp);} if(flag1==1&& song_num==2){flag1=0, Pait_Bmp(70,70,100,100, music2_bmp);} if(flag1==1&& song_num==3){flag1=0, Pait_Bmp(70,70,100,100, music3_bmp);}
// 音乐标志song_num1不为零时(歌曲播放完song_num1赋值或者通过键值(上一首/下一首)给song_num1赋值) // buffer音乐播放地址赋初值 length音乐长度重新赋值并显示音乐图片 下一FIFO字节位置count初始化 song_num1归零 if(song_num1==1){ buffer=music1; length=3704572; count=0; Pait_Bmp(70,70,100,100, music1_bmp); song_num1=0;}
if(song_num1==2){ buffer=music2; length=3704552; count=0; Pait_Bmp(70,70,100,100, music2_bmp); song_num1=0;}
if(song_num1==3){ buffer=music3; length=5644880; count=0; Pait_Bmp(70,70,100,100, music3_bmp); song_num1=0;}
if((rIISCON& (1<<7))==0) //检查输出FIFO是否为空 { //FIFO中的数据为16位,深度为32 //当输出FIFO为空时,一次性向FIFO写入32个16位数据 for(i=0;i<32;i++){ rIISFIFO=(buffer[2*i+count])+(buffer[2*i+1+count]<<8); // 一次循环向FIFO存储16位数据于FIFO }
count+=64; // 64为32次循环,每次循环指向两个不同字节的字节总和
//音乐播放完成后 音乐标志song_num1指向下一首歌 if(count>length){ song_num2=song_num2+1; if(song_num2==4){song_num2=1;} song_num1=song_num2; } } }
//暂停时关闭IIS 显示欢迎图片 if(flag==0){ rIISCON |=0x0; Pait_Bmp(70,70,100,100, hellomusic);} //关闭IIS;
}
Lcd_ClearScr( (0x1f<<11)| (0x3f<<5)| (0x1f) ); //白色 //**********************还原现场************************** rIISPSR = save_SR; rIISCON = save_S; rIISMOD = save_SM; rIISFCON = save_SF; rADCCON = save_A; rGPBCON = save_B; rGPBDAT = save_BD; rGPEUP = save_EP; rGPECON = save_E; rGPGCON = save_G; rGPGUP = save_GP; rGPCCON = save_C; rGPCUP = save_CP; rGPDCON = save_D; rGPDUP = save_DP; rLCDCON1 = save_LCD1; rLCDCON2 = save_LCD2; rLCDCON3 = save_LCD3; rLCDCON4 = save_LCD4; rLCDCON5 = save_LCD5; rLCDINTMSK = save_LCDINT; rINTSUBMSK = save_INTS; rINTMSK = save_INT; //******************************************************** } |
附录
wav工具下载地址
视频地址
最后
以上就是纯情糖豆为你收集整理的mini2440裸机音乐播放器(很久以前的笔记)功能模块LCD、触摸屏、IIS 分别介绍:主函数实现功能:遇到的问题与解决附录的全部内容,希望文章能够帮你解决mini2440裸机音乐播放器(很久以前的笔记)功能模块LCD、触摸屏、IIS 分别介绍:主函数实现功能:遇到的问题与解决附录所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复