mini2440裸机音乐播放器(很久以前的笔记)

【这是好久以前写的,有点乱,没时间整理,当做记录用的。】

图片粘贴失效,没上传图,想要的直接下载文档吧。

项目目的:通过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
;


//预分频器为2,所以CODECLK=PCLK/(2+1)=16.66666kHz(和表中的16.9344有偏差)

rIISPSR =
2<<5|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("\n\nq:退出\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=%04d\n", xdata,
ydata);

Uart_Printf("\n        flag=%d,  1:播放    0:暂停",
flag);

Uart_Printf("\n     botten1=%d,  1:上一首  2:下一首",botten1);

Uart_Printf("\n         **正在播放第%d首歌曲** 
\n\n", 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("\n\nq:退出\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工具下载地址

视频地址

时间: 2024-10-02 11:09:40

mini2440裸机音乐播放器(很久以前的笔记)的相关文章

mini2440裸机试炼之IIS——音乐播放器

IIS 信号频率设置 IIS(Inter-IC Sound)由飞利浦公司开发,是一种常用的音频设备接口,主要用于CD.MD.MP3等设备. s3c2440一共有5个引脚用于IIS:IISDO.IISDI.IISSCLK.IISLRCK和CDCLK.前两个引脚用于数字音频信号的输出和输入,另外三个引脚都与音频信号的频率有关,可见要用好IIS,就要把信号频率设置正确. IISSCLK为串行时钟,每一个时钟信号传送一位音频信号.因此IISSCLK的频率=声道数×采样频率×采样位数,如采样频率fs为44

HTML5项目笔记4:使用Audio API设计绚丽的HTML5音乐播放器

HTML5 有两个很炫的元素,就是Audio和 Video,可以用他们在页面上创建音频播放器和视频播放器,制作一些效果很不错的应用. 无论是视屏还是音频,都是一个容器文件,包含了一些音频轨道,视频轨道和一些元数据,这些是和你的视频或者音频控件绑定到一块的,这样才形成了一个完整的播放组件. 浏览器支持情况: 浏览器 支持情况 编解码器 Chrome 3.0 Theora . Vorbis .Ogg H.264 . AAC .MPEG4 FireFox 3.5 Theora . Vorbis .Og

安卓第十七天笔记--简易版本音乐播放器

title:简易版本音乐播放器 简易版本音乐播放器 1.布局 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="

Android(java)学习笔记234: 服务(service)之音乐播放器

1.我们播放音乐,希望在后台长期运行,不希望因为内存不足等等原因,从而导致被gc回收,音乐播放终止,所以我们这里使用服务Service创建一个音乐播放器. 2.创建一个音乐播放器项目(使用服务) (1)首先新建一个Android项目,命名为"Mp3音乐播放器",如下: (2)创建服务MusicService,如下: 1 package com.himi.Mp3player; 2 3 import android.app.Service; 4 import android.content

如何写一个正经的Android音乐播放器 一

以前写过很多次音乐播放器,但是总有一些问题出现,例如: 1,音乐长时间播放问题(即便是放在service中去播放,依然会被杀死): 2,音乐的播放进度如何掌握?(如何利用mediaplayer.getCurrentPosition()来有效的通知界面变更进度?): 3,在我以往的经验中,音乐播放完毕下一曲时候,经常出现当前音乐播放还差几秒钟的时候就下一曲了的情况. 从网上找到教程中,通常都是一个播放器的demo,简单的直接把MediaPlayer放在了一个Activity中去操作,稍有良心的教程

Android实现音乐播放器(一)

Graphical User Interface 本篇文章记录了我实现Android简单音乐播放器的过程,(一)中介绍了怎么构建音乐播放器的前端页面.首先大家看一下,界面最后是这样的(界面有点粗陋) 音乐文件列表是从SD卡中读取出来的. 首先我们先在Layout里面创建Music Player的GUI,diagram如下图所示: 根据diagram我们在activity_main.xml写出如下代码: <LinearLayout xmlns:android="http://schemas.

手把手教你做音乐播放器(八)桌面小工具(上)

第8节 桌面小工具 桌面小工具是可以放置在主界面的.快速控制应用的小助手.例如我们的音乐小工具,它可以帮助用户在桌面上就完成音乐的暂停.播放.切换等操作,而不需要启动应用本身. 在安卓系统中,我们也常常叫它App widget. 实现一个App widget要经过以下几个步骤, 创建一个App widget类,让它继承自AppWidgetProvider,例如AnddleMusicAppWidget类: 放在res\layout目录下,为App widget的界面定义一个布局,例如anddle_

毕业设计——基于STM32的音乐播放器设计(一)

基于STM32的音乐播放器设计, 源代码下载地址:http://download.csdn.net/detail/cxp2205455256/8334021      SD卡文件下载地址:http://download.csdn.net/detail/cxp2205455256/8334089 电路图下载地址:文件太大了,上传不了....... 以下是截图: 1.硬件电路 2.软件主界面 3.音乐播放器界面 4.音乐定时播放界面 5.音乐列表界面 6.日历功能界面 9.温度功能界面 10.计算器

Xamarin.Android开发音乐播放器

最近.Net开源着实让C#火了一把,好久就听说Mono for Android一直没静下心来看,上周末找来看看,确实不错,前台界面axml编写跟Java安卓开发毫无区别,后台用C#其实很多windows下的方法都可以用,做一个安卓音乐播放器,主要实现音乐播放,上一曲下一曲,音乐播放列表,随机播放,扫描SD卡内所有音乐. 先show一组界面啊,话说有图有真相啊: 项目结构如图: Lrc是歌词处理方法 MusicService是安卓服务播放音乐,服务Broadcast,以及界面接收Receiver