嵌入式mp3播放器

分四部分:按键驱动,声卡驱动,Madplay播放器移植,MP3主播放器处理

按键1:播放,按键2:停止,按键3:上一曲,按键4:下一曲
UA1341内核自带声卡驱动
1.解压内核:
tar zxvf linux.2.6.29.tar.gz

2.清理中间件,配置文件:
cd linux-linux2.6.29;make clean
3.选择参考配置文件:
cp config-mp3.config 4.配置内核: make menuconfig ARCH=arm CROSS_COMPILE=arm-linux- 选择声卡驱动:device drivers-->sound card support-->advanced linux sound architecture-->alsa for soc audio support-->UA134x 5.编译内核: make uImage ARCH=arm CROSS_COMPILE=arm-linux- 内核映像uImage位于arch/arm/boot 将其拷贝到tftpboot目录 6.解压rootfs.tar.gz并拷贝到nfsroot 7.按键驱动移植: cd SDK-MP3/driver make clean;make cp mini2440_buttons.ko /nfroot/rootfs/mp3 8.madplay移植
见最后

9.播放处理:
cd SDK-MP3/app;make clean;make
cp app-mp3 /nfsroot/rootfs/mp3

10.测试
采用NFS方式起文件系统,加载按键驱动,运行mp3程序:
insmod mini2440_buttons.ko
./app-mp3
会显示播放列表,播放option,1,2,3,4按键控制播放。
主要程序为mp3播放控制程序:

/*
 *     mp3播放器控制程序
 *       功能:
              k1:播放、暂停
              k2:停止播放
              k3:上一首
              k4:下一首
 *     附加:歌曲自动循环播放
 *
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/select.h>
#include <sys/time.h>
#include <errno.h>
#include <sys/wait.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>

/*共享内存申请标记*/
#define PERM S_IRUSR|S_IWUSR                                                    

/*双向循环列表:存放歌曲名*/
struct song                
{
    char songname[20];
    struct song *prev;
    struct song *next;
};

/*孙子进程id号*/
pid_t gradchild;

/*子进程id号*/
pid_t pid;

/*共享内存描述标记*/
int shmid;

char *p_addr;

/*播放标记*/
int first_key=1;
int play_flag=0;

/*************************************************
Function name: play
Parameter    : struct song *
Description     : 播放函数
Return         : void
Argument     : void
Autor & date : ada 09,12,07
**************************************************/
void play(struct song *currentsong)
{
    pid_t fd;
    char *c_addr;
    char *p;
    int len;
    char my_song[30]="/mp3/song/";
    while(currentsong)
    {
        /*创建子进程,即孙子进程*/
        fd = fork();
        if(fd == -1)
        {    
            perror("fork");
            exit(1);
        }
        else if(fd == 0)
        {
            /*把歌曲名加上根路径*/
            strcat(my_song,currentsong->songname);
            p = my_song;
            len = strlen(p);

            /*去掉文件名最后的'
'*/
            my_song[len-1]='';

            printf("THIS SONG IS %s
",my_song);
            execl("/mp3/madplay","madplay",my_song,NULL);
            printf("


");
        }
        else
        {
            /*内存映射*/
            c_addr = shmat(shmid,0,0);

            /*把孙子进程的id和当前播放歌曲的节点指针传入共享内存*/
            memcpy(c_addr,&fd,sizeof(pid_t));
            memcpy(c_addr + sizeof(pid_t)+1,&currentsong,4);
            /*使用wait阻塞孙子进程,直到孙子进程播放完才能被唤醒;
              当被唤醒时,表示播放MP3期间没有按键按下,则继续顺序播放下一首MP3*/
            if(fd == wait(NULL))
            {
                currentsong = currentsong->next;
                printf("THE NEXT SONG IS %s
",currentsong->songname);
            }
        }
    }
}

/*************************************************
Function name: creat_song_list
Parameter    : void
Description     : 创建歌曲名的双向循环链表
Return         : struct song *
Argument     : void
Autor & date : ada 09.12.07
**************************************************/
struct song *creat_song_list(void)
{    
    FILE *fd;
    size_t size;
    size_t len;
    char *line = NULL;
    struct song *head;
    struct song *p1;
    struct song *p2;
    system("ls /mp3/song >song_list");
    fd = fopen("song_list","r");

    p1 = (struct song *)malloc(sizeof(struct song));

    printf("==================================song list=====================================
");
    system("ls /mp3/song");    
    printf("
");
    printf("================================================================================
");
    size = getline(&line,&len,fd);

    strncpy(p1->songname,line,strlen(line));
    head = p1;
    while((size = getline(&line,&len,fd)) != -1)
    {    
        p2 = p1;
        p1 = (struct song *)malloc(sizeof(struct song));
        strncpy(p1->songname,line,strlen(line));
        p2->next = p1;
        p1->prev = p2;    
    }
    p1->next = head;
    head->prev = p1;
    p1 = NULL;
    p2 = NULL;
    system("rm -rf song_list");
    return head;
}
/*************************************************
Function name: startplay
Parameter    : pid_t *,struct song *
Description     : 开始播放函数
Return         : void
Argument     : void
Autor & date : ada 09.12.07
**************************************************/
void startplay(pid_t *childpid,struct song *my_song)
{
    pid_t pid;
    int ret;
    /*创建子进程*/
    pid = fork();

    if(pid > 0)
    {
        *childpid = pid;
        play_flag = 1;
        sleep(1);
        /*把孙子进程的pid传给父进程*/
        memcpy(&gradchild,p_addr,sizeof(pid_t));
    }
    else if(0 == pid)
    {    
        /*子进程播放MP3函数*/
        play(my_song);
    }
}
/*************************************************
Function name: my_pause
Parameter    : pid_t
Description     : 暂停函数
Return         : void
Argument     : void
Autor & date : ada 09,12,07
**************************************************/
void my_pause(pid_t pid)
{
    printf("=======================PAUSE!PRESS K1 TO CONTINUE===================
");
    kill(pid,SIGSTOP); //对孙子进程发送SKGSTOP信号
    play_flag = 0;
}

/*************************************************
Function name: my_pause
Parameter    : pid_t
Description     : 停止播放函数
Return         : void
Argument     : void
Autor & date : ada 09,12,07
**************************************************/
void my_stop(pid_t g_pid)
{

    printf("=======================STOP!PRESS K1 TO START PLAY===================
");
    kill(g_pid,SIGKILL); //对孙子进程发送SKGKILL信号
    kill(pid,SIGKILL);   //对子进程发送SKGKILL信号
    first_key=1;

}

/*************************************************
Function name: conti_play
Parameter    : pid_t
Description     : 继续函数
Return         : void
Argument     : void
Autor & date : ada 09,12,07
**************************************************/
void conti_play(pid_t pid)
{
    printf("===============================CONTINUE=============================
");
    kill(pid,SIGCONT); //对孙子进程发送SIGCONT信号
    play_flag=1;
}

/*************************************************
Function name: next
Parameter    : pid_t
Description     : 下一首函数
Return         : void
Argument     : void
Autor & date : ada 09.12.07
**************************************************/
void next(pid_t next_pid)
{
    struct song *nextsong;

    printf("===============================NEXT MP3=============================
");
    /*从共享内存获得孙子进程播放歌曲的节点指针*/
    memcpy(&nextsong,p_addr + sizeof(pid_t)+1,4);
    /*指向下首歌曲的节点*/
    nextsong = nextsong->next;
    /*杀死当前歌曲播放的子进程,孙子进程*/
    kill(pid,SIGKILL);
    kill(next_pid,SIGKILL);
    wait(NULL);
    startplay(&pid,nextsong);
}

/*************************************************
Function name: prev
Parameter    : pid_t
Description     : 上一首函数
Return         : void
Argument     : void
Autor & date : yuanhui 09.12.08
**************************************************/
void prev(pid_t prev_pid)
{
    struct song *prevsong;
    /*从共享内存获得孙子进程播放歌曲的节点指针*/
    printf("===============================PRIOR MP3=============================
");
    memcpy(&prevsong,p_addr + sizeof(pid_t)+1,4);
    /*指向上首歌曲的节点*/
    prevsong = prevsong->prev;
    /*杀死当前歌曲播放的子进程,孙子进程*/
    kill(pid,SIGKILL);
    kill(prev_pid,SIGKILL);
    wait(NULL);
    startplay(&pid,prevsong);
}

/*************************************************
Function name: main
Parameter    : void
Description     : 主函数
Return         : int
Argument     : void
Autor & date : ada 09.12.07
**************************************************/
int main(void)
{
    int buttons_fd;
    int key_value;
    struct song *head;
    /*打开设备文件*/
    buttons_fd = open("/dev/buttons", 0);
    if (buttons_fd < 0) {
        perror("open device buttons");
        exit(1);
    }


  /*创建播放列表*/
    head = creat_song_list();
    printf("===================================OPTION=======================================



");
    printf("        K1:START/PAUSE     K2:STOP   K3:NEXT      K4:PRIOR



");
    printf("================================================================================
");


  /*共享内存:用于存放子进程ID,播放列表位置*/
    if((shmid = shmget(IPC_PRIVATE,5,PERM))== -1)
        exit(1);
    p_addr = shmat(shmid,0,0);
    memset(p_addr,'',1024);
    
    
    while(1) 
    {
        fd_set rds;
        int ret;

        FD_ZERO(&rds);
        FD_SET(buttons_fd, &rds);

        /*监听获取键值*/
        ret = select(buttons_fd + 1, &rds, NULL, NULL, NULL);
        if (ret < 0) 
        {
            perror("select");
            exit(1);
        }
        if (ret == 0) 
            printf("Timeout.
");
        else if (FD_ISSET(buttons_fd, &rds))
        {
            int ret = read(buttons_fd, &key_value, sizeof key_value);
            if (ret != sizeof key_value) 
            {
                if (errno != EAGAIN)
                    perror("read buttons
");
                continue;
            } 
            else
            {
                //printf("buttons_value: %d
", key_value+1);
                
                /*首次播放,必须是按键1*/
                if(first_key){
                    switch(key_value)
                    {    
                    case 0:
                        startplay(&pid,head);
                        first_key=0;
                        break;
                    case 1:
                    case 2:
                    case 3:
                        printf("=======================PRESS K1 TO START PLAY===================
");
                        break;
                    default:
                        printf("=======================PRESS K1 TO START PLAY===================
");
                        break;
                    } //end switch
                }//end if(first_key)
                /*若不是首次播放,则根据不同键值处理*/
                else if(!first_key){
                    switch(key_value)
                    {
                    case 0:
                        //printf("play_flag:%d
",play_flag);
                        if(play_flag)
                            my_pause(gradchild);
                        else
                            conti_play(gradchild);
                        break;
                    case 1:
                        my_stop(gradchild);
                        break;
                    case 2:
                        next(gradchild);
                        break;
                    case 3:
                        prev(gradchild);
                        break;
                    } //end switch
             }//end if(!first_key)

            }
                
        }
    }

    close(buttons_fd);
    return 0;
}

all:
arm-linux-gcc -static app.c -o app-mp3

clean:
rm -rf app-mp3

 
mini2440_buttons.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/poll.h>
#include <linux/irq.h>
#include <asm/irq.h>
#include <linux/interrupt.h>
#include <asm/uaccess.h>
#include <mach/regs-gpio.h>
#include <mach/hardware.h>
#include <linux/platform_device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>

#define DEVICE_NAME     "buttons"

//#define DEBUG 
struct button_irq_desc {
    int irq;
    int pin;
    int pin_setting;
    int number;
    char *name;    
};

#if !defined (CONFIG_QQ2440_BUTTONS)
static struct button_irq_desc button_irqs [] = {
    {IRQ_EINT8 , S3C2410_GPG0 ,  S3C2410_GPG0_EINT8  , 0, "KEY0"},
    {IRQ_EINT11, S3C2410_GPG3 ,  S3C2410_GPG3_EINT11 , 1, "KEY1"},
    {IRQ_EINT13, S3C2410_GPG5 ,  S3C2410_GPG5_EINT13 , 2, "KEY2"},
    {IRQ_EINT14, S3C2410_GPG6 ,  S3C2410_GPG6_EINT14 , 3, "KEY3"},
    {IRQ_EINT15, S3C2410_GPG7 ,  S3C2410_GPG7_EINT15 , 4, "KEY4"},
    {IRQ_EINT19, S3C2410_GPG11,  S3C2410_GPG11_EINT19, 5, "KEY5"},
};
#else /* means QQ */
static struct button_irq_desc button_irqs [] = {
    {IRQ_EINT19, S3C2410_GPG11, S3C2410_GPG11_EINT19, 0, "KEY0"},
    {IRQ_EINT11, S3C2410_GPG3,  S3C2410_GPG3_EINT11,  1, "KEY1"},
    {IRQ_EINT2,  S3C2410_GPF2,  S3C2410_GPF2_EINT2,   2, "KEY2"},
    {IRQ_EINT0,  S3C2410_GPF0,  S3C2410_GPF0_EINT0,   3, "KEY3"},
    {       -1,            -1,                 -1,    4, "KEY4"},
    {       -1,            -1,                 -1,    5, "KEY5"},
};
#endif
//static volatile char key_values [] = {'0', '0', '0', '0', '0', '0'};
static int key_values = 0;

static DECLARE_WAIT_QUEUE_HEAD(button_waitq);

static volatile int ev_press = 0;


static irqreturn_t buttons_interrupt(int irq, void *dev_id)
{
    struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
    int down;
    // udelay(0);
    
    /*上升沿触发,GPIO DAT 应该为非0 的数*/
    down = !s3c2410_gpio_getpin(button_irqs->pin);
    if (!down) { 
    //printk("rising
");
    key_values = button_irqs->number;
        ev_press = 1;
        wake_up_interruptible(&button_waitq);
    }
   else {
    //printk("falling
");
    ev_press = 0;
    return 0;
   }
    return IRQ_RETVAL(IRQ_HANDLED);
}


static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
{
    int i;
    int err = 0;
    
    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
    if (button_irqs[i].irq < 0) {
        continue;
    }

     /* 设置中断触发方式 IRQ_TYPE_EDGE_FALLING,IRQ_TYPE_EDGE_RISING,IRQ_TYPE_EDGE_BOTH ;我们这里设置为上升沿触发*/
        //err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, 
        //                  button_irqs[i].name, (void *)&button_irqs[i]);
        err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_RISING, 
                          button_irqs[i].name, (void *)&button_irqs[i]);
        if (err)
            break;
    }

    if (err) {
        i--;
        for (; i >= 0; i--) {
        if (button_irqs[i].irq < 0) {
        continue;
        }
        disable_irq(button_irqs[i].irq);
            free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
        }
        return -EBUSY;
    }

    ev_press = 0;
    
    return 0;
}


static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
{
    int i;
    
    for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
    if (button_irqs[i].irq < 0) {
        continue;
    }
    free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
    }

    return 0;
}


static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
    unsigned long err;
    //int i=0;
    if (!ev_press) {
    if (filp->f_flags & O_NONBLOCK)
        return -EAGAIN;
    else
        wait_event_interruptible(button_waitq, ev_press);
    }
    if(count != sizeof key_values)
    return -EINVAL;
    ev_press = 0;
    err = copy_to_user(buff, &key_values, sizeof(key_values));
    return sizeof(key_values);
}

static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    poll_wait(file, &button_waitq, wait);
    if (ev_press)
        mask |= POLLIN | POLLRDNORM;
    return mask;
}


static struct file_operations dev_fops = {
    .owner   =   THIS_MODULE,
    .open    =   s3c24xx_buttons_open,
    .release =   s3c24xx_buttons_close, 
    .read    =   s3c24xx_buttons_read,
    .poll    =   s3c24xx_buttons_poll,
};

static struct miscdevice misc = {
    .minor = MISC_DYNAMIC_MINOR,
    .name = DEVICE_NAME,
    .fops = &dev_fops,
};

static int __init dev_init(void)
{
    int ret;

    ret = misc_register(&misc);
#ifdef DEBUG
    printk("debug test
");//ykz
#endif
    printk (DEVICE_NAME"	initialized
");

    return ret;
}

static void __exit dev_exit(void)
{
    misc_deregister(&misc);
}

module_init(dev_init);
module_exit(dev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("FriendlyARM Inc.");

ifneq ($(KERNELRELEASE),)

obj-m := mini2440_buttons.o

else
KDIR := /home/project/mp3/SDK-MP3/kernel/linux-2.6.29
all:
make -C $(KDIR) M=$(PWD) modules ARCH=arm CROSS_COMPILE=arm-linux-
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers app-key

endif

Madplay移植说明

一.准备
移植Madplay所需四个软件包分别为libid3tag-0.15.1b.tar.gz,
libmad-0.15.1b.tar.gz,zlib-1.1.4.tar.gz,madplay-0.15.2b.tar.gz

二.解压
   1.mkdir /mp3 建立MP3目录
         2. tar -zxvf libid3tag-0.15.1b.tar.gz -C /mp3
         3. tar -zxvf ibmad-0.15.1b.tar.gz -C /mp3
       4. tar -zxvf zlib-1.1.4.tar.gz -C /mp3
       5. tar -zxvf madplay-0.15.2b.tar.gz -C /mp3
  
三.编译zlib
#cd /mp3/zlib-1.1.4    
#./configure --prefix=/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/lib
    修改Makefile
    AR=/usr/local/arm/4.3.2/bin/arm-linux-ar rcs
    CC=/usr/local/arm/4.3.2/bin/arm-linux-gcc
    RANLIB=/usr/local/arm/4.3.2/bin/arm-linux-ranlib
       
  执行  #make
          #make install

四.编译libid3tag
#cd /mp3/libid3tat-0.15.1d
#./configure --host=arm-linux CC=arm-linux-gcc --disable-debugging --disable-shared --prefix=/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/lib

    #make
    #make install


五.编译libmad
#cd /mp3/libmad-0.15.1b    
#./configure --enable-fpm=arm --host=arm-linux --disable-shared --disable-debugging --prefix=/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/lib
修改 Makefile 129行 去掉 –fforce-mem

    #make
    #make install

六.编译madplay
#cd /mp3/madplay-0.15.2b    
#./configure --host=arm-linux CC=arm-linux-gcc --disable-debugging --disable-shared
    #make
    但是,这样得到的是动态连接的。
    #rm madplay
    拷贝make的最后一个连接的命令,在最后加上-static 和 -lz,然后运行,得到静态连接的程序
        如

arm-linux-gcc -Wall -O2 -fomit-frame-pointer -o madplay madplay.o getopt.o getopt1.o version.o resample.o filter.o tag.o crc.o rgain.o player.o audio.o audio_aiff.o audio_cdda.o audio_hex.o audio_null.o audio_raw.o audio_snd.o audio_wave.o audio_oss.o  -lmad -lid3tag -lm -lz -static
最后把madplay下到板子就可以了.
原文地址:https://www.cnblogs.com/timssd/p/4101742.html