• 智能家居项目


    1、需求分析
      1)LED灯的控制
      2)视频功能
      3)音频功能
         mp3音乐
      4)监控是否有人闯入(红外)
         拿按键模拟 实现报警
      5)g-sensor 实现计步器
      6)在线升级
     
       产出物: 需求分析文档
    2、概要设计
      硬件选型:
           cpu: s5p6818
       开放板:x6818
           操作系统: linux
     
      设计软件模块 ,以及模块之间的通信方式
    3、详细设计
      每个模块中有哪些函数
      函数的参数、返回值 、出错处理
      函数的执行流程图 (viso)
     
    4、编码


    5、整合测试


    6、项目的发布

    1、开发环境的搭建
      cd /home/tarena
      mkdir project
      cd project/
         kernel
         rootfs
     
      安装交叉编译工具
     
      烧写到开发板 uImage 并运行
      nfs方式挂载根文件系统
         /home/tarena/project/rootfs  *(rw,sync,no_root_squash)
         sudo /etc/init.d/nfs-kernel-server restart
         
         setenv bootargs root=/dev/nfs nfsroot=192.168.1.8:/home/tarena/project/rootfs ip=192.168.1.6:192.168.1.8:192.168.1.1:255.255.255.0 console=ttySAC0,115200 maxcpus=1 lcd=wy070ml tp=gslx680-linux cam=OV5645
         saveenv
     


    2、QT程序的移植
      2.1PC机上的QT开发环境
         cd workdir/qt/qt_dev
         
         ./qt-opensource-linux-x64-android-5.4.1.run
         
         安装路径 /opt/Qt5.4.1
         
         sudo vi /home/tarena/.bashrc
         PATH=$PATH:/opt/arm-cortex_a9-eabi-4.7-eglibc-2.18/bin/:/opt/Qt5.4.1/5.4/gcc_64/bin:/opt/Qt5.4.1/Tools/QtCreator/bin
         source /home/tarena/.bashrc
         
         使用qtcreator创建一个test工程
         在其中增加了确定按钮
         readelf -d test
            libQt5Widgets.so.5
            libQt5Core.so.5
      2.2 编译ARM版本的QT库
          http://doc.qt.io/qt-t/embedded-linux.html
     
          1)拿到QT源码  官方网站
          2)通过交叉编译工具编译ARM版本的QT库
             cd project/
             cp /home/tarena/workdir/qt/qt_src/qtbase-opensource-src-5.4.1.tar.xz ./
             tar xf qtbase-opensource-src-5.4.1.tar.xz
             cd qtbase-opensource-src-5.4.1
             vi mkspecs/linux-arm-gnueabi-g++/qmake.conf
            14 QMAKE_CC                = arm-cortex_a9-linux-gnueabi-gcc
            15 QMAKE_CXX               = arm-cortex_a9-linux-gnueabi-g++
            16 QMAKE_LINK              = arm-cortex_a9-linux-gnueabi-g++
            17 QMAKE_LINK_SHLIB        = arm-cortex_a9-linux-gnueabi-g++
            18
            19 # modifications to linux.conf
            20 QMAKE_AR                = arm-cortex_a9-linux-gnueabi-ar cqs
            21 QMAKE_OBJCOPY           = arm-cortex_a9-linux-gnueabi-objcopy
            22 QMAKE_NM                = arm-cortex_a9-linux-gnueabi-nm -P
            23 QMAKE_STRIP             = arm-cortex_a9-linux-gnueabi-strip
         ./configure --help 查看QT源码编译前的配置选项
        
             ./configure -prefix /home/tarena/project/qtlib -release -opensource -qt-libpng -qt-libjpeg -plugin-sql-sqlite -widgets -qt-sql-sqlite     -make libs -no-cups -no-nis -no-iconv -no-dbus -no-openssl -no-iconv -no-accessibility -no-sse2 -silent -xplatform linux-arm-gnueabi-g++ -nomake tools -nomake examples -nomake tests -qt-freetype -no-glib -strip -linuxfb -plugindir /home/tarena/project/qtlib/plugins
                   执行./configure会根据配置生成合适的Makefile
                   -prefix: 编译后的安装路径
                            make install
                   -plugindir: 插件库的安装路径
                   -xplatform linux-arm-gnueabi-g++
                   //ls mkspecs/linux-arm-gnueabi-g++       
            make  -j4
            make install
      2.3 将QT程序移植到开发板运行
         2.3.1 编译ARM版本的可执行程序
            0)cd ......test/
            1)/home/tarena/project/qtlib/bin/qmake //生成合适的Makefile
            2) make clean
            3) make
         2.3.2 将可执行程序及库文件部署到开发板

    注意:建议接下来的所有内容都拷贝rootfs/home
            1)mkdir /home/tarena/project/rootfs/home/bin -p
            2)cp test /home/tarena/project/rootfs/home/bin/
            3)mkdir /home/tarena/project/rootfs/home/qt
            4)cp /home/tarena/project/qtlib/lib/ /home/tarena/project/rootfs/home/qt/ -a
            5)mkdir /home/tarena/project/rootfs/home/lib
            6)cp /opt/arm-cortex_a9-eabi-4.7-eglibc-2.18/arm-cortex_a9-linux-gnueabi/sysroot/lib/*.so* /home/tarena/project/rootfs/home/lib/ -a
            7)cp /opt/arm-cortex_a9-eabi-4.7-eglibc-2.18/arm-cortex_a9-linux-gnueabi/sysroot/usr/lib/*.so* /home/tarena/project/rootfs/home/lib/ -a
         2.3.3 部署QT程序运行时使用的插件
            1)cp /home/tarena/project/qtlib/plugins/ rootfs/home/qt/ -a
       
         2.3.4 关于QT的配置文件
            1)mkdir rootfs/home/etc -p    
            2)cp /mnt/hgfs/project/env/qt.zip/profile rootfs/home/etc/
            3)关于profile
            #插件库路径

            1)mkdir rootfs/home/etc -p    
            
            2)vi rootfs/home/etc/profile
            export PATH=$PATH:/home/bin
    	export LD_LIBRARY_PATH=/home/qt/lib/:/home/lib/:$LD_LIBRARY_PATH
    	#插件库路径
    	export QT_QPA_PLATFORM_PLUGIN_PATH=/home/qt/plugins/
    	#指定LCD设备 经常修改的就是分辨率
    	export QT_QPA_PLATFORM=linuxfb:fb=/dev/fb0:size=1024x600:tty=/dev/fb0
    	#指定字体库的存放位置 
    	export QT_QPA_FONTDIR=/home/qt/lib/fonts
    	#指定的触摸屏对应的设备文件
    	#确定触摸屏设备文件的方式
    	#hexdump /dev/input/eventX
    	#用手敲击屏幕看是否有数据输出
    	export QT_QPA_GENERIC_PLUGINS=evdevkeyboard,evdevmouse,evdevtouch:/dev/input/event1
            #如果接了USB键盘 使用判断触摸屏设备文件的方式修改event7
            #export QWS_USB_KEYBOARD=/dev/input/event7



            4) 使配置生效
                source /home/etc/profile
          2.3.5 运行程序
                /home/bin/test
            
     关于开发板GUI界面程序中中文显示乱码的问题:
         env/font.zip
         1)rm /home/tarena/project/rootfs/home/qt/lib/fonts/* -rf
         1)cp /mnt/hgfs/project/env/font/DroidSansFallback.ttf /home/tarena/project/rootfs/home/qt/lib/fonts/   
         

    练习: 将QT课程中的小游戏移植到开发板运行
    问题:移植qt程序到开发板运行需要哪几步?
         
    参考代码:ehome_final.tar.gz

    3、LED的控制
      3.1 LED驱动程序
         实质就是一个linux字符设备驱动
         
         cd project
         mkdir drivers
         cd drivers
         mkdir leds
         cd leds
         vi led_drv.c
         vi Makefile
         make
         mkdir ../../rootfs/home/drivers
         cp leds_drv.ko  ../../rootfs/home/drivers/
         vi test.c
         
         编译test.c,测试驱动程序是否好用
         
      3.2 编写LED的控制应用程序
          有两种方式
          3.2.1 简单的方式
               当点击亮灯按钮时,完成该信号的槽函数
               在槽函数中
               open("/dev/leds", ...)
               ioctl(fd, CMD_LED_ON, &i);
               修改图片
               
               再次点击时灭灯
                  open("/dev/leds", ...)
               ioctl(fd, CMD_LED_OFF, &i);
               修改图片
               
               GUI界面程序一定部署到开发板才能有效果
               
          3.2.2复杂方式
               希望GUI界面程序不管是运行在PC
               或者运行在开发板,都能控制开发板的LED状态
               
               client (GUI 开发板/PC)        Server (UDP 运行在开发板)
                点击按钮发送命令LED_ON         接收命令,根据命令open设备 ioctl亮灯
            
             1)界面客户端程序
               mkdir gui_client
               cd gui_client
               qtcreator
                  建立工程 完成界面编程
                  
                  先将mainwindow窗口的大小调整文1024*600
                  
                  添加按钮
                      槽函数中给UDP服务器发送命令
            2)服务器程序
            
               cd project
               mkdir server
               cd server
               vi server.c  //接收命令 分发命令
                  //udp server
                  
                  recvfrom(cmd)
                  
                  if(cmd == LED_ON)
                     open("/dev/leds",...)
                     ioctl(fd, LED_ON, 0)  
              vi leds.c: 根据命令不同调用操作灯的函数
              vi leds_hw.c:
                          open
                          ioctl
               
              arm-cor.....gcc leds.c leds_hw.c server.c -o server
              cp server rootfs/home/bin/
              启动server (开发板)
              启动client  点击按钮实验是否亮灯
        
        前三项在板子上运行
        insmod /home/drivers/leds_drv.ko
        source /home/etc/profile
        /home/bin/server &
        
        可以运行在开发板上页可以运行在PC
        /home/bin/client
        
       注意编程过程中的调试技巧
    #define DEBUG
    #ifdef DEBUG
        /*##表示如果可变参数被忽略或为空,将使预处理器( preprocessor )去除掉它前面的那个逗号。*/
        #define pr_debug(fmt, ...) printf(fmt, ##__VA_ARGS__)
    #else
        #define pr_debug(fmt, ...)
    #endif  


       BUG的调试,顺着数据流分析该调用的函数是否调用到
       
       gui界面程序点击按钮 发送命令函数是否被调用
          qWarning
       命令被正常接收?
          在server中打印收到的命令
       该命令被正常处理了吗?
          leds_operations
              leds
                  ioctl
                      ----------
                        led_ioctl
       
       ehome_v1.tar.gz
       
       
    4、视频服务器
       4.1摄像头的驱动
          uvc子系统: usb video class
                    内核中自带了满足uvc格式的摄像头驱动
          如果你手中的摄像头满足uvc规范,该摄像头就是免驱
          只需要对内核进行配置,将uvc模块对应的代码
          编译到uImage
          
          如何判断摄像头满足uvc格式规范?
              lsusb
          再将摄像头插入开发板
              lsusb
              Bus 001 Device 003: ID 046d:0825  
              
              网络搜索 uvc官网 有一个的页面:
              列出了uvc框架支持的usb摄像头ID        
        配置内核,将uvc子系统编译进内核
            Device Drivers  --->
                 <*> Multimedia support  --->
                      [*]   Video capture adapters  --->
                           [*]   V4L USB devices  --->  
                                 <*>   USB Video Class (UVC)  
        make uImage
        让开发板加载包含uvc模块的新的内核
        
        ls /dev/video*
        再次插入摄像头
        发现多了一个video9 ,就是插入摄像头的设备文件
        4.2 应用程序
           4.2.1操作摄像头,抓取图像数据
               v4l2: video for linux ver2
                     
                     它属于摄像头软件的中间层
                     向下统一摄像头驱动的格式
                     向上为应用软件访问控制摄像头提供统一的接口
                     简化应用层软件控制摄像头的编程工作
               v4l2用户态编程怎么玩?
                
               v4l2提供的有小程序:v4l_demo.zip/capture.c
                   open设备
                   
                   ioctl设置工作参数
                   
                   ioctl(fd, VIDIOC_STREAMON, &type);//开始摄像头开始工作
                   
                   // 获取图像数据
                   ioctl(fd, VIDIOC_DQBUF, &buf);
                   ioctl(fd, VIDIOC_QBUF, &buf);
             mjpeg-streamer包含了按照v4l2框架去操作摄像头的代码
                   而且其中也包含了按照http协议向客户端发送图像数据的代码
                   
               重点:移植部署运行mjpeg-streamer
           4.2.2mjpeg-streamer的移植:mjpg-streamer.tar.bz2
                cd project
                mkdir video
                cd video
                cp /mnt/hgfs/project/env/mjpg-streamer.tar.bz2 ./
                tar xf mjpg-streamer.tar.bz2
                cd mjpg-streamer/
                vi README
                   make clean all
                   ./mjpg-streamer ....
                   start.sh
                vi Makefile
                   CC = gcc
                find ./ -name "Makefile" -exec sed -i "s/CC = gcc/CC = arm-cortex_a9-linux-gnueabi-gcc/g" {} ;
                    
                    sed: 文件文件
                    awk: 行处理  
                        结合正则表达式 功能非常强大
                make
           4.2.3 部署到开发板
                cp mjpg_streamer ../../rootfs/home/bin/
                cp *.so ../../rootfs/home/lib/ -a
                cp www/ ../../rootfs/home/ -r
           4.2.4 运行
                 /home/bin/mjpg_streamer --help
                 /home/bin/mjpg_streamer -i "input_uvc.so --help"
                /home/bin/mjpg_streamer -i "/home/lib/input_uvc.so -d /dev/video9 -y -r 320x240 -f 30" -o "/home/lib/output_http.so -w /home/www"        
                   -i: 指定输入插件
                      -d: 指定访问的摄像头设备文件
                      -y: 采集图像的格式为YUYV
                      -r: 采集图像的大小
                      -f: 帧频率                  
                   
                   -o: 指定输出插件
                      -w: 网页资源文件所在目录         
                   
                打开浏览器,输入“http://192.168.1.6:8080/”
               
               如果手里没有摄像头,可以使用如下方案
               /home/bin/mjpg_streamer -i "/home/lib/input_testpicture.so -r 320x240 -d 500" -o "/home/lib/output_http.so -w /home/www"
               input_testpicture.so不是采集摄像头数据
                   自身有两张图片
                   将这两张图片交替的发送给客户端
               打开浏览器,输入“http://192.168.1.6:8080/”
     
       4.3 mjpg-streamer 源码分析
          4.3.1 mjpg_streamer.c  高内聚 低耦合
                ctags -R *
             int main(int argc, char *argv[])
             {
                 /*共享库的运行阶段加载有两种方式
                   gcc xxx -o a.out -lpthread
                   ./a.out 操作系统加载共享库
                   
                   程序中自主主动加载共享库(插件库)
                 */
                                              "input_uvc.so"
                 global.in[i].handle = dlopen(global.in[i].plugin, RTLD_LAZY)
                      /*找到input_uvc.so中的input_init函数对应代码在内存中的位置*/
                 global.in[i].init = dlsym(global.in[i].handle, "input_init");
                 global.in[i].run = dlsym(global.in[i].handle, "input_run");
                 /*执行input_uvc.so中的input_init函数*/
                 global.in[i].init(&global.in[i].param, i)
                 
                                               /*"output_http.so"*/
                 global.out[i].handle = dlopen(global.out[i].plugin, RTLD_LAZY);
                 global.out[i].init = dlsym(global.out[i].handle, "output_init");
                 global.out[i].run = dlsym(global.out[i].handle, "output_run");
                 global.out[i].init(&global.out[i].param, i)
                 
                 global.in[i].run(i)
                 global.out[i].run(global.out[i].param.id);
                 
                 pause();
             return 0;    
        
             }   
        4.3.2 输入插件plugins/input_uvc/
              按照v4l2编程步骤去操作uvc格式的摄像头
              官方例程:capture.c
              Video for linux 2 example (v4l2 demo) - MetalSeed - 博客频道 - CSDN.NET.png
              
              vi plugins/input_uvc/input_uvc.c
                 /*打开摄像头 设置工作参数*/
                 int input_init(input_parameter *param, int id)
                 {
                     init_videoIn(cams[id].videoIn, dev, width, he    ight, fps, format, 1, cams[id].pglobal, id)
                     {
                         init_v4l2(vd)
                         {
                            /*打开"/dev/video9"设备文件*/
                            vd->fd = OPEN_VIDEO(vd->videodevice, O_RDWR)
                            /*查询当前硬件的工作能力*/
                            xioctl(vd->fd, VIDIOC_QUERYCAP, &vd->cap)
                            /*图像格式设置*/
                            ret = xioctl(vd->fd, VIDIOC_S_FMT, &vd->fmt);
                            ...
                         }
                     }
                 }
                 int input_run(int id)
                 {
                     pthread_create(&(cams[id].threadID), NULL, cam_thread, &(cams[id]));   
                 }
                 void *cam_thread(void *arg)
                 {
                     while(!pglobal->stop)
                     {
                         uvcGrab(pcontext->videoIn)
                         {
                             video_enable(vd)
                             {
                                 /*VIDIOC_STREAMON:让摄像头开始工作*/
                                 ret = xioctl(vd->fd, VIDIOC_STREAMON, &type);
                             }
                             /*获取一帧图像*/
                             xioctl(vd->fd, VIDIOC_DQBUF, &vd->buf)
                             
                         }
                         /* copy JPG picture to global buffer */
                          memcpy_picture(pglobal->in[pcontext->id].buf, pcontext->videoIn->tmpbuffer, pcontext->videoIn->buf.bytesused);
                     }
                 }

    BUG修改:input_uvc/v4l2uvc.c
    428     do{
    429     memset(&vd->buf, 0, sizeof(struct v4l2_buffer));
    430     vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    431     vd->buf.memory = V4L2_MEMORY_MMAP;
    432
    433     ret = xioctl(vd->fd, VIDIOC_DQBUF, &vd->buf);
    434     if(ret < 0) {
    435         perror("Unable to dequeue buffer");
    436        // goto err;
    437     }
    438     }while(ret < 0);

          4.3.3 输出插件 plugins/output_http/
               将图像数据封装成http数据包
               通过TCP方式下客户端发送
               
               vi plugins/output_http/output_http.c
                  int output_init(output_parameter *param, int id)
                  {
                      port = htons(8080);
                      ...
                  }
              int output_run(int id)
              {
              pthread_create(&(servers[id].threadID), NULL, server_thread, &(servers[id]));
              }
            void *server_thread(void *arg)
            {
                socket(aip2->ai_family, aip2->ai_socktype, 0)
                bind(pcontext->sd[i], aip2->ai_addr, aip2->ai_addrlen)
                listen(pcontext->sd[i], 10)
                while(!pglobal->stop)
                {
                    accept(pcontext->sd[i], (struct sockaddr *)&client_addr, &addr_len)
                    pthread_create(&client, NULL, &client_thread, pcfd)
                }
            
            }
            void *client_thread(void *arg)
                    {
                      _readline(lcfd.fd, &iobuf, buffer, sizeof(buffer) - 1, 5)
                      {
                         _read(fd, iobuf, &c, 1, timeout)
                         {
                             read(fd, &iobuf->buffer, IO_BUFFER)
                         }
                      }
                      
                      else if(strstr(buffer, "GET /?action=stream") != NULL)
                      {
                          req.type = A_STREAM;//确定客户端请求类型
                      }
                      
                       switch(req.type) {
                              case A_STREAM:
                                   send_stream(lcfd.fd, input_number)
                                   {
                                        while(!pglobal->stop)
                                        {
                                           /**/
                                           write(http头信息)
                                           /*将pglobal->buf数据拷贝到 frame缓冲区*/
                                            memcpy(frame, pglobal->in[input_number].buf, frame_size);
                                            write(fd, frame, frame_size)
                                            write(http尾信息)
                                        }
                                   }
                                   break;
                  }


    练习:
         1)在开发板上假设好视频服务器
         2)编程实现一个tcp client
            向服务器发送 "GET /?action=stream HTTP/1.1 "
            recv(sd, buf, 1024)
            printf("%s ",buf) ;
            
            结束            
                  
                  
                  
                  
                                
    5、视频客户端
      5.1 HTTP协议:http.zip
          超文本传输协议
          
          建立TCP传输的基础上
          
          客户端要给服务器端发送请求request
          
          服务器端根据客户端的请求回送响应response
      5.2 对mjpg-streamer的分析得到以下内容
          如果客户端发送的请求中包含"GET /?action=stream"
          服务器就会将视频数据封装HTTP格式数据帧
          不断发送给客户端
          按照http 协议 request的数据格式
          
          就是给服务器发送"GET /?action=stream HTTP/1.1 "
      5.3 编写一个tcp客户端程序
          code_for_mjpgstreamer.rar
         1)保证客户端和服务器能够联通
          serverip : 开发板IP
          端口号:   8080
          给服务器发送请求 "GET /?action=stream HTTP/1.1 "
          读取一次服务器返回的数据,并将数据打印
         2)在1)的基础上,不断地读取数据
            把读到的数据保存/tmp/test.jpg文件
            保存1M数据后程序退出
         3)能不能把http头信息过滤掉 把http尾信息过滤掉
            只将图像信息内容保存到/tmp/test.jpg文件去
            
            hexdump -C /tmp/test.jpeg |less
            
            服务器返回的图像是jpeg格式的
            jpeg图像帧在存储时是有固定的格式
            JPEG是一种有损的图像压缩算法
            JPEG文件由两部分组成: 标记码 压缩数据
            
            FF D8 .... ... .... FF D9

            参考code_for_mjpgstreamer.rar/03代码
        4) 编写GUI客户端显示图像数据
           
           camer:该类负责和HTTP server进行通信
                  连接服务器
                      connectToHost
                  给服务器发送请求"GET /?action=stream HTTP/1.1 "
                      requestImage
                  接收server返回的数据
                  并从中过滤出"ff d8 .... ff d9"
                      void CamClient::readImage()
                      {
                          接收数据
                          过滤出图像
                          发送信号 newImageReady(image)给mainwindow
                      }
                  void MainWindow::showNewImage(QImage img)
              {
             /*显示图片*/
             ui->imageLabel->setPixmap(QPixmap::fromImage(img));
              }
                  通信过程中使用了QTcpSocket
                  使用QBtyeArray 缓存图像数据
                  显示图像使用了QImage
                  
                      注意1:SIGNAL(readyRead())
                      
                      当收到readyRead()信号时,去调用对应的槽函数接收数据,过滤数据
                  
                      camer::camer(QObject *parent) :
     
                    QObject(parent)
            {
                connect(&tcpSocket,SIGNAL(readyRead()),this,SLOT(readImage()));
            }
            
            注意2: 自定义的信号
              CamClient 当过滤到一个完整的图像帧发出的
              MainWindow负责处理该信号  
         
         
         视频数据的压缩 H264
         参考代码: 04 视频客户端
                   ehome_v2.tar.gz : LED + 视频客户端
                 
         
    6、红外报警功能
      常见的人体红外感应模块HC-SR501
      用按键来模拟红外
      当按键按下,有人闯入,需要报警
      报警:
           beep响 (选择)
           发一个短信 (gsm)
      6.1驱动程序
         按键驱动
             drivers/buttons
             
         蜂鸣器驱动
             建议按照混杂设备的架构完成
             drivers/beep
             
      6.2应用程序
         实时监控,满足条件就报警
         使用多线程/Qtimer
           去轮询红外(按键)是否有数据,有数据报警
           
         使用定时器间隔一段时间就给server发命令,读是否有按键值  
         如果有键值,给server发蜂鸣器响的命令
         客户端程序: client/
         服务器程序: server/beep.c
                             beep_hw.c
                             leds.c
                             leds_hw.c
             
         
         . /home/etc/profile           
         如果看视频数据
           /home/bin/start.sh &
         安装设备驱动
           insmod home/drivers/beep_drv.ko
           insmod home/drivers/leds_drv.ko
           insmod home/drivers/btn_dev.ko
           insmod home/drivers/btn_drv.ko
           
           按键的驱动:
               1)内核中原有的按键驱动裁剪
               2)对应的设备文件
                  cat /proc/bus/input/devices
                  
                   open("/dev/input/eventX",O_RDONLY|O_NONBLOCK);

         启动udp server 接收客户端发送的命令
             /home/bin/server &   
         
         ehome_v3.tar.gz           
    7、MP3功能的实现  
      完成mp3文件的解码工作,
         MP3_format_parse.doc
      将解码之后的数据写入声卡设备
         Linux_sound_guide.doc
         mp3/ALSA Audio API 使用指南(中英版) - 幻雪神界 - 博客频道 - CSDN.NET.html
            
      7.1基本概念
         PCM: 脉冲编码调制
              声音是模拟量
              计算机能处理的是数字量,涉及模拟量和数字量的相互转换
              录音时是模拟量转数字量
              播放时是数字量转模拟量
         采样频率:
              每秒钟抽取声波幅度样本的次数
              当采样频率应该在40kHz左右,保证声音不失真
         量化位数:
              每次采样,模拟音频信号的振幅用多少个bit来表示
              一般使用16bit       
         声道数:
              有单声道和双声道之分,双声道又称为立体声  
         MP3:
              音频数据的压缩算法
              有损压缩(还原后是有失真的)
              压缩比可以接近10:1-12:1
              使压
              缩后的文件在回放时能够达到比较接近原音源的声音效果  
      7.2 MP3文件的解码工作    
          MP3_format_parse.doc
          其存储格式:
              TAGV2
                  {
                    。。。
                  }
              FRAME
                  {
                      帧头
                      {
                          。。。。
                      }
                      数据实体
                  }
              FRAME
              ...
              TAGV1
                      {
                          。。。
                      }
          mp3文件的解码工作,可以使用开源的库完成
             mp3.rar/libmad-0.15.1b.tar.gz
          libmad库的使用步骤:
             1)cd project/
             2)mkdir mp3
                cd mp3
             3)cp /mnt/hgfs/project/env/mp3/libmad-0.15.1b.tar.gz ./
             4)tar xf libmad-0.15.1b.tar.gz
             5) ./configure --help
                ./configure CC=arm-cortex_a9-linux-gnueabi-gcc --host=arm-linux --prefix=/home/tarena/project/mp3/install
                    --host: 运行目标平台
                    --prefix: 安装路径 make install
                用来生成合适的Makefile
             6) make && make install
             7) minimad.c
                libmad API使用的演示demo
                   从标准输入获取要解码的数据
                   进行解码
                   解码后的数据打印到标准输出设备
               arm-cortex_a9-linux-gnueabi-gcc minimad.c -o player -L ../install/lib/ -lmad
                  如果出现“../install/lib//libmad.so:对‘III_imdct_l’未定义的引用”
                  
                  解决方式:
                      make clean
                      make
                      make install
                      arm-cortex_a9-linux-gnueabi-gcc minimad.c -o player -L ../install/lib/ -lmad
                          
                   
                   
                   
                   计划将解码之后的数据输出到声卡设备                     
        7.3 声卡的驱动程序
            linux下的声卡驱动分两种:
                 OSS: Open Sound System
                      最早出现的
                      非开源程序
                      /dev/dsp
                      /dev/mixer
                      
                      open("/dev/dsp",...)
                      ioctl()设置采样频率 量化位数 声道数
                      write(解码后的数据)
                      
                 ALSA: 开源    
           make menuconfig
                Device Drivers  --->
                     <*> Sound card support  --->
                         <*>   Advanced Linux Sound Architecture  --->
          ls /dev/snd/
             controlC0  
             controlC1  
             pcmC0D0c   
             pcmC0D0p   
             pcmC1D0p   
             timer
        7.4 alsa库的使用
         
         如果是alsa架构,提供了一套用户空间访问以上设备文件
         的库函数alsa-lib-1.1.1.tar.bz2
         
         alsa库封装了对/dev/snd下设备文件的操作
         编译步骤:
             1) cd mp3/
             2) cp /mnt/hgfs/project/env/mp3/alsa-lib-1.1.1.tar.bz2 ./
                 tar xf alsa-lib-1.1.1.tar.bz2
                 cd alsa-lib-1.1.1/
             3) ./configure CC=arm-cortex_a9-linux-gnueabi-gcc --host=arm-linux --prefix=/home/tarena/project/mp3/install  --with-configdir=/opt/alsa --with-plugindir=/opt/alsa_lib
              4) make && make install
             
          以上库函数的使用可以参考
             alsa使用官网:http://www.alsa-project.org/alsa-doc/alsa-lib/index.html
                     http://www.alsa-project.org/main/index.php/Download
             或者参考:
                 ALSA Audio API 使用指南(中英版) - 幻雪神界 - 博客频道 - CSDN.NET.html
          
        以上两个库libmad alsa库的使用实例mp3_player.c
        
        mp3_player.c和minimad.c相比修改以下内容
           ./player xxx.mp3
           
           1)原来从标准输入读取解码数据
             现在从xxx.mp3文件中读取解码数据
           2)设置声卡的工作属性 44.1KHz 16bit 双声道
              set_pcm() //参考文档中“一个最简单的回放程序”
                   {
                      /*打开声卡设备*/
                      rc=snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
                      //硬件设置为交错模式
                      snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
                       //设置16位采样精度
                      rc=snd_pcm_hw_params_set_format(handle, params, SND_PCM _FORMAT_S16_LE);  
                      //设置声道,1表示单声>道,2表示立体声
                      rc=snd_pcm_hw_params_set_channels(handle, params, channels);
                      //设置频率  
                 snd_pcm_hw_params_set_rate_near(handle, params, &rate, &dir);  
                   }
           
           3)修改了output
              将解码的音频数据输出到声卡设备
           注意:2) 3) 参考 ALSA Audio API 使用指南(中英版) - 幻雪神界 - 博客频道 - CSDN.NET.html
              
              /*libmad解码的音频帧数据写入声卡设备*/
              snd_pcm_writei(handle, OutputPtr, n);
          
          编译 部署 运行 player程序
             1)arm-cortex_a9-linux-gnueabi-gcc mp3_player.c  -lmad -lasound  -L install/lib/ -I install/include/ -o player
                出现了以下错误:
                      libmad.so : III_....
                解决方法:
                      cd libmad 源码目录
                      make clean
                      make
                      make install
                      arm-cortex...... mp3_player.c      
                      
         2)cp player ../rootfs/home/bin/
         3)cp  install/lib/* ../rootfs/home/lib/ -a
         4)配置文件和插件库文件
           注意,配置时指定了路径
           --with-configdir=/opt/alsa --with-plugindir=/opt/alsa_lib
           部署到开发板上时要保持一致
           cp /opt/alsa/ ../rootfs/opt/ -a
           cp /opt/alsa_lib/ ../rootfs/opt/ -a
         5)  mkdir ../rootfs/home/songs
            cp /mnt/hgfs/project/env/mp3/*.mp3 ../rootfs/home/songs/
         6) 运行
            . /home/etc/profile
            /home/bin/player /home/songs/I Will Always Love You Always Love You.mp3 1>/dev/null 2>&1  
            
         练习:
           1)看懂mp3_player.c
           2)编译 部署到开发板运行
           3)/home/bin/player /home/songs/xxx.mp3
              
              //将/home/songs目录下的所有.mp3文件播放一遍
              /home/bin/player /home/songs
              
              参考ehome_final.tar.gz/server/ myplayer*.c
                  有目录操作
       7.5 加一个简单的GUI界面
           
           播放:  
                 1)开启新线程开始播放
                     或者
                 2) 唤醒被暂停的播放线程
                     pthread_cond_signal(&conti);  
           暂停:stat=1
                 修改了播放线程中的output
                 
                 //alsa声卡的暂停,将缓冲区中提提交声卡播放数据全部提交给声卡
                  snd_pcm_drop(handle);

                 //让播放线程进入睡眠状态, 暂停了播放
                  pthread_cond_wait(&conti, &lock);
                 
                 当再次收到播放命令 会执行到“ pthread_cond_signal(&conti); ”
                 //设置声卡处于就绪状态
                 snd_pcm_prepare(handle);
                  
           上一首
                 MUSIC_NEXT  music_stat =2
                 播放线程
                 music_play()
                 {
                     while(遍历歌曲)
                     {
                       decode()
                       {
                                  out_put
                                 {
                                    return MAD_FLOW_STOP//导致decode()执行完毕
                                 }
                               }
                               munmap
                               改变cur_pos //标识要播放的目录中的第几首歌
                               
                           }
                     }
                 
                 
           下一首            
                MUSIC_PREV  music_stat =3
                播放线程
                 music_play()
                 {
                     while(遍历歌曲)
                     {
                       decode()
                       {
                                  out_put
                                 {
                                    return MAD_FLOW_STOP//导致decode()执行完毕
                                 }
                               }
                               munmap
                               cur_pos = cur_pos?(cur_pos-1):0; //标识要播放的目录中的第几首歌
                               
                           }
                     }
                     
         步骤:
              . /home/etc/profile
              /home/bin/start.sh  &
              insmod /home/drivers/beep_drv.ko
              insmod /home/drivers/btn_drv.ko
              insmod /home/drivers/btn_dev.ko
              insmod /home/drivers/leds_drv.ko
              /home/bin/server &
    8、计步器的功能实现
       使用g-sensor实现
       
       如何算一步:
           只要xyz任意一轴和上个周期比有了一个比较大的变化
           
       8.1 g-sensor的驱动程序
       
       8.2 应用程序 C/S架构
          c/s: client/server  : qq
          b/s  browser/server : 网页版游戏
          
          
          client: qt gui
                  周期性(博尔特)的给服务器发送命令
                  让服务器不断的读取g-sensor的数据
                  客户端如果收到1,步数+1 ,并显示
                  可以使用Qtimer
          server:
                  收到相应的命令,读取g-sensor的数据
                  上次读到的结果进行比较
                  如果变化比较大(人为定义),
                  给客户端返回1,否则返回0        
                  
                  数据的比较可以放在服务器上完成
                  其实也可以放在客户端完成
          
        步骤:  
          source /home/etc/profile
          insmod  /home/drivers/beep_drv.ko
          insmod  /home/drivers/btn_dev.ko
          insmod  /home/drivers/btn_drv.ko
          insmod  /home/drivers/leds_drv.ko
          insmod  /home/drivers/mma8653_drv.ko
          /home/bin/start.sh &
          /home/bin/server &     
         
    9、项目的发布
      9.1 配置脚本,实现自启动
          /etc/init.d/rcS: 开机自启动的程序可以放入该脚本
          /etc/profile: 全局对所有用户有效的环境变量
          
          开机自启动:
              vi rootfs/etc/init.d/rcS
                 最后加入
                 
                 exec /home/etc/rcS
              vi rootfs/home/etc/rcS
                  #配置开发板IP
            ifconfig eth0 192.168.1.6
            ifconfig lo up
            #加载内核模块
            find /home/drivers/ -name *ko -exec insmod {} ;
            
            
            source /home/etc/profile
            
            #启动视频服务器
            /home/bin/start.sh &
            
            #启动UDP服务器
            /home/bin/server &
            #启动GUI界面程序
            /home/bin/client &
              chmod +x rootfs/home/etc/rcS
          环境变量的设置
              vi rootfs/etc/profile
                   . /home/etc/profile
       9.2 分区的规划
           前提条件,该分区能够存储下要存储文件
           
           0--------1M---------------17M--------------------273M---------------->剩余
    扇区号        0x800            0x8800                  0x88800
             uboot        kernel                rootfs                 userdata
           fdisk 2 3 0x100000:0x1000000  0x1100000:0x10000000  0x11100000:0
          9.2.1 制作uImage 并完成烧写
                tftp 48000000 uImage
                mmc write 48000000 0x800 0x3000
          9.2.2 制作ext4类型个文件系统 并烧写
                
                #创建256M大小的rootfs.ext4文件
                dd if=/dev/zero of=rootfs.ext4 bs=1k count=262144      
                du -h rootfs.ext4
                sudo mkfs.ext4  rootfs.ext4
                sudo mkdir /mnt/ext4
                sudo mount -t ext4 -o loop rootfs.ext4 /mnt/ext4
                sudo cp rootfs/* /mnt/ext4/ -a
                sudo umount /mnt/ext4
                
                cp rootfs.ext4 /tftpboot/
                tftp 0x48000000 rootfs.ext4
                mmc write 48000000 0x8800 0x80000
          9.2.3 启动参数的修改
               setenv bootargs root=/dev/mmcblk0p2 init=/linuxrc console=ttySAC0,115200 rootfstype=ext4  lcd=wy070ml tp=gslx680-linux cam=OV5645  maxcpus=1
               saveenv
              
          系统启动后:
              ls /dev/mmcblk0*
              cat /proc/partitions
              mkfs.vfat /dev/mmcblk0p3
              mount /dev/mmcblk0p3 /mnt/   
                

    10、系统功能升级
       通过网络进行升级
       
       通过U盘来实现升级功能
       10.1 U盘的手工挂载
           ls /dev/sd*
           插入U盘
           ls /dev/sd*
           
            mount /dev/sda1 /mnt
            umount /mnt
       10.2 u盘自动挂载
            env/usb.rar
            
            /dev/sda1设备文件自动创建
            是由于热插拔事件产生,导致mdev程序被执行
            由mdev来去创建的设备文件
            其实可以通过设置让mdev在去创建/dev/sda1设备文件
            的同时,可以完成u盘的自动挂载
            问题1:如何配置让mdev既可以自动创建设备文件/dev/sda1
                   又可以创建后自动挂载u盘?
                mdev.conf
            问题2: 如何修改mdev.conf ,语法格式?
                 vi busybox-1.23.2/docs/mdev.txt
                     sd[a-z][0-9] 0:0 666 @/home/usb/usb_insert.sh /dev/$MDEV
                     
                         sd[a-z][0-9], 设备文件的规则,满足该规则的
                                       sda1 sdb2
                                       sda 不满足
                         0:0 , uid:gid
                         666,  权限
                         @, 创建设备文件
                         /home/usb/usb_insert.sh /dev/$MDEV,
                            创建sda1/sdb2...设备文件时执行/home/usb/usb_insert.sh脚本,
                            并且传递参数/dev/sda1(sdb2)
                    sd[a-z] 0:0 666 $/home/usb/usb_remove.sh
                       $, 销毁设备文件之前                
                     
                     
                 
                 
            实验步骤:env/usb.rar
                1)在rootfs/etc/mdev.conf          
                   cp /mnt/hgfs/project/env/usb/usb/mdev.conf  rootfs/etc/
                   
                2) mkdir rootfs/home/usb -p
                   cp /mnt/hgfs/project/env/usb/usb/usb_insert.sh rootfs/home/usb/
                   创建挂载点
                   mkdir rootfs/mnt/usb
                3)cp /mnt/hgfs/project/env/usb/usb/usb_remove.sh rootfs/home/usb/
      10.3手工操作将u盘中uImage 更新到
          dd if=/mnt/usb/uImage of=/dev/mmcblk0p1
             输入文件 if指定
             输出到哪去 of
      10.4当点击按钮时自动更新
          system("dd if=/mnt/usb/uImage of=/dev/mmcblk0p1");

          GUI: 给服务器发送命令 m
          server: 收到命令m
                执行system("dd if=/mnt/usb/uImage of=/dev/mmcblk0p1");
       参考:ehome_v7.tar.gz

  • 相关阅读:
    源码学习之Yii-去掉magic_quote里的反斜线
    PHP中传递回调函数的方法
    mac里的terminal环境下如何跳转行首和行末
    mac下切换输入法
    nginx上配置vhosts
    MySQL学习之查询优化(一)
    MySQL学习之索引(三)
    在LINUX下为自己加上sudo权限的方法
    MySQL学习之索引(二)
    MySQL学习之索引(一)
  • 原文地址:https://www.cnblogs.com/DXGG-Bond/p/11965928.html
Copyright © 2020-2023  润新知