Navigation

    全志在线开发者论坛

    • Register
    • Login
    • Search
    • Categories
    • Tags
    • 在线文档
    • 社区主页

    全志V3S嵌入式驱动开发 - 驱动开发合集

    爱搞机专区
    1
    5
    4357
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • bayche
      bayche LV 6 last edited by q1215200171

      之前的文章都是教大家怎么搭建环境、看原理图、编译内核和根文件系统、做镜像,直到现在才进入驱动开发的主题。毕竟整个专栏的目的,还是希望大家能够学会驱动外部硬件。驱动好硬件,分成硬件和软件两个部分,硬件的部分比较好理解,就是看相关的信号有没有对接上即可,但是软件对接、甚至于怎么写软件,大家好像接触的比较少,所以这个时候,就可以简单讨论一下,怎么样才能写驱动代码。

      目录:

      1.多按键输入驱动
      2.PWM驱动
      3.GPIO驱动
      4.音频输出和音频录制

      1、了解外部驱动芯片手册

      所谓驱动,其实就是用soc里面的io控制器来驱动外部电路,或者说是外部芯片。如果是简单的io和uart这些,当然不需要知道驱动芯片的内容。但是如果驱动的是外部芯片,那么这个时候我们就要把对应芯片的资料找过来,好好读一读。里面协议的内容,就是我们将来编程的依据,

      274644966316418f8cadf388bb9760e0.png

      2、了解soc芯片手册

      了解外部芯片主要是解决发送内容的问题,而soc芯片则会告诉我们怎么发送这些内容。比如,它是iic接口,还是spi接口,还是普通的uart接口等等。这些内容,都可以通过soc的芯片手册找到,比如下面s3c2440中nandflash controller的介绍,

      191066c1ada246a986f8d1212940ee24.png

      3、os驱动框架的学习

      不同的os,它的驱动框架其实是不一样的。比如说,windows有windows的驱动框架,linux有linux的驱动框架,android系统甚至于把一部分驱动代码放到了应用层,这都是有可能的。所以,对于驱动工程师来说,如果需要编写驱动,那么要做的就是熟悉对应系统的驱动框架。一般来说,大部分驱动代码都是差不多的,没有必要从0开始写,可以找一个差不多的模板代码,根据芯片手册改一下内容,基本就可以用了。

      当然,除了框架之外,还有一些特殊的配置文件需要注意,以linux驱动为例,除了驱动框架之外,还需要学习一下dts文件,也就是设备树,

      https://github.com/Lichee-Pi/linux/tree/zero-4.10.y/arch/arm/boot/dts
      

      4、熟悉os kernel API

      一般来说,编写驱动的时候,我们也会用到内核的API。比如说,需要申请内存,需要申请中断,需要访问io地址等等,这些操作都需要小心、谨慎地去做。因为,如果是上层去做开发,最多也就是程序闪退,出不了大错,但是底层驱动如果写错,会直接重启硬件,或者程序死锁,这些都是很常见的现象。所以在真正使用API的时候,一定要理解了之后再使用,驱动的一些故障、特别是一些低概率的故障还是比较难调试的。

      5、适当编写上层代码

      驱动写好了,是需要搭配上层代码进行配套测试的。如果公司不是很大,这部分也是需要驱动工程师自己去完成的。另外从提高工作效率的角度来说,自己会写上层应用,也可以更好地发现问题、解决问题,省的拖到最后解决,只会让自己搞得措手不及。

      注:
      面说的都是软件的工作,有时候驱动工程师还需要自己量电压、测信号。不需要很懂,但是可以解决最低级的故障。根据2/8原则来说,我们会的硬件知识也许不多,但是掌握了常见的套路之后,它已经足够可以解决80%的问题。这样遇到难题,也不会受制于人,效率上不打折扣。

      1 Reply Last reply Reply Quote Share 3
      • bayche
        bayche LV 6 last edited by

        多按键输入驱动

        前面我们说过,荔枝派的开发板上面,有4个按键本身不是通过gpio连接到soc上面的。它是通过ad的方法,连接到主芯片的。这个时候,不同的按键被按下的时候,就会生成不同的电压或者电流,那么完全可以根据对应的电信号,推算出当前是哪一个按键被按下去了。

        1、查找电路图

        04019fb44a2242199e6d5070eb68a44b.png

        简单看一下电路之后,下面就是去找设备树,对应的信号是什么、在哪里。

        2、查找设备树

        在sun8i-v3s-licheepi-zero-dock.dts文件当中,我们发现了这样的内容,

        &lradc {
        	vref-supply = <&reg_vcc3v0>;
        	status = "okay";
         
        	button@200 {
        		label = "Volume Up";
        		linux,code = <KEY_VOLUMEUP>;
        		channel = <0>;
        		voltage = <200000>;
        	};
         
        	button@400 {
        		label = "Volume Down";
        		linux,code = <KEY_VOLUMEDOWN>;
        		channel = <0>;
        		voltage = <400000>;
        	};
         
        	button@600 {
        		label = "Select";
        		linux,code = <KEY_SELECT>;
        		channel = <0>;
        		voltage = <600000>;
        	};
         
        	button@800 {
        		label = "Start";
        		linux,code = <KEY_OK>;
        		channel = <0>;
        		voltage = <800000>;
        	};
        };
        

        很明显,每一个button都是和电路中的按键是一一对应的,这个没有问题。那么,我们不禁还有一个疑问,既然是ad转换得到的结果,那么肯定要知道ad相关的设备配置是恶还那么。仔细找了一下,可以在sun8i-v3s.dtsi文件发现这样的内容,

        lradc: lradc@01c22800 {
        			compatible = "allwinner,sun4i-a10-lradc-keys";
        			reg = <0x01c22800 0x400>;
        			interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
        			status = "disabled";
        		};
        

        看到这里,大家应该放心了,确实是有这么一个ad的驱动。兼容的设备是sun4i-a10-lradc-keys,寄存器地址空间是0x01c22800,长度是0x400,中断是GIC_SPI类型,状态关闭。有了设备树,还有了兼容设备号,接下来的一步就是根据设备号sun4i-a10-lradc-keys找到对应的驱动文件。

        3、查找驱动代码,准备测试程序

        通过工具查找一下,不难发现,文件在这,即sun4i-lradc-keys.c,

        static const struct of_device_id sun4i_lradc_of_match[] = {
        	{ .compatible = "allwinner,sun4i-a10-lradc-keys", },
        	{ /* sentinel */ }
        };
        MODULE_DEVICE_TABLE(of, sun4i_lradc_of_match);
         
        static struct platform_driver sun4i_lradc_driver = {
        	.driver = {
        		.name	= "sun4i-a10-lradc-keys",
        		.of_match_table = of_match_ptr(sun4i_lradc_of_match),
        	},
        	.probe	= sun4i_lradc_probe,
        };
         
        module_platform_driver(sun4i_lradc_driver);
         
        MODULE_DESCRIPTION("Allwinner sun4i low res adc attached tablet keys driver");
        MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
        MODULE_LICENSE("GPL");
        

        大致看一下最后一段的代码,内容方面应该错不了。一般来说,如果按键ok的话,会在设备启动的时候生成个/dev/input/event0节点,此时,如果编写一个应用程序,读写这些节点,就完全可以获取相关的按键信息。所以,我们还得准备一个input.c的读写程序,

        #include <sys/types.h>
        #include <sys/stat.h>
        #include <fcntl.h>
        #include <unistd.h>
        #include <stdio.h>
        #include <string.h>
        #include <stdlib.h>
        #include <errno.h>
        #include <linux/input.h>
        #include <linux/input-event-codes.h>
         
        const char * path = "/dev/input/event0";
         
        int main(char argc,char *argv[])
        {
           int ret;
         
           int fd;
           struct input_event event;
         
           fd = open(path,O_RDONLY);
           if(fd < 0)
           {
              perror(path);
              exit(-1);
           }
         
           while(1)
           {
              ret = read(fd,&event,sizeof(struct input_event));
              if(ret == sizeof(struct input_event))
              {
                    if(event.type != EV_SYN)
                    {
                       printf("Event: time %ld.%ld,",event.time.tv_sec,event.time.tv_usec);
                       printf("type:%d,code:%d,value:%d\n", event.type,event.code,event.value);
                    }
              }
           }
         
           close(fd);
         
           return 0;
        }
        

        上面这段代码是从其他地方copy而来,谢谢了。准备好了程序之后,下面就是交叉编译,下载到开发板上面。但是实际运行的时候,发现按键被按下的时候,有三个按键的数值居然是一样的,都是352,另外一个是114。这就非常蹊跷了。

        4、发现问题、解决问题

        一般情况下,遇到这种情况,第一个怀疑的就是电阻坏了,R24、R25、R26、R27当中肯定有三个被击穿了,不然这种情况是说不过去的。于是,我们拿掉sd卡,让v3s继续跑之前norflash里面的嵌入式系统,输入key程序,也就是按键测试程序,没想到结果居然是正常的。这说明,硬件,没问题。问题出在上层应用或者驱动程序。

        回过头查看sun4i-lradc-keys.c,惊讶地发现电压判断标准是根据sun8i-v3s-licheepi-zero-dock.dts中的voltage来验证的,这并不符合实际的情况。我们通过printk&dmesg打印,也验证了这一想法,所以如果需要得到正确的按键数值,只需要修正一下sun4i-lradc-keys.c中的判断逻辑就可以了,修改方法如下,具体的标定数值可以做实验来解决,

        #if 0 // by feixiaoxing
        		voltage = val * lradc->vref / 63;
         
        		for (i = 0; i < lradc->chan0_map_count; i++) {
        			diff = abs(lradc->chan0_map[i].voltage - voltage);
        			if (diff < closest) {
        				closest = diff;
        				keycode = lradc->chan0_map[i].keycode;
        			}
        		}
        #else
        		printk("val = %d\n", val);
        		if(val >=9 && val <= 13)
        			keycode = lradc->chan0_map[0].keycode;
        		else if(val >=24 && val <= 29)
        			keycode = lradc->chan0_map[1].keycode;
        		else if(val >= 35 && val <= 40)
        			keycode = lradc->chan0_map[2].keycode;
        		else
        			keycode = lradc->chan0_map[3].keycode;
        #endif
        

        经过这一次修改,我们重新编译kernel内核,烧入zImage,启动后输入key程序,这样就得到了我们想要的最终结果,即稳定地输出按键值,

        # ./input 
        [    8.623196] val = 10
        Event: time 47.807318,type:1,code:115,value:1
        Event: time 48.78567,type:1,code:115,value:0
        [   10.920313] val = 26
        Event: time 50.104437,type:1,code:114,value:1
        Event: time 50.340531,type:1,code:114,value:0
        [   12.709584] val = 37
        Event: time 51.893708,type:1,code:353,value:1
        Event: time 52.141508,type:1,code:353,value:0
        [   14.549618] val = 45
        Event: time 53.733726,type:1,code:352,value:1
        Event: time 53.969827,type:1,code:352,value:0
        [   16.186513] val = 11
        Event: time 55.370623,type:1,code:115,value:1
        Event: time 55.645799,type:1,code:115,value:0
        [   17.514789] val = 27
        Event: time 56.698907,type:1,code:114,value:1
        Event: time 56.950631,type:1,code:114,value:0
        [   20.823740] val = 39
        Event: time 60.7851,type:1,code:353,value:1
        Event: time 60.279112,type:1,code:353,value:0
        [   21.987936] val = 44
        Event: time 61.172044,type:1,code:352,value:1
        Event: time 61.435486,type:1,code:352,value:0
        
        1 Reply Last reply Reply Quote Share 1
        • bayche
          bayche LV 6 last edited by

          PWM驱动

          pwm驱动也是常见的一种驱动方式。常见的pwm,其实就是一组方波,方波中的高低电平之比称之为空占比。通过调节这个空占比,可以实现不同的控制目的,比如说呼吸灯、电机控制等等。接下来,正好可以看看如何对v3s的pwm进行设置,以及如何在linux平台下使用pwm。

          1、pwm0和pwm1

          目前V3S支持两个pwm输出,分别是pwm0和pwm1。如下图所示,

          b4983719e5b14f359afa9c0e74f2167d.png

          2、要使能pwm功能,最主要就是修改设备树配置文件,

          第一,在sun8i-v3s.dtsi中,添加pwm0和pwm1节点,

          pwm0_pins: pwm0 {
          				pins = "PB4";
          				function = "pwm0";
          			};
           
          			pwm1_pins: pwm1 {
            			      	pins = "PB5";
               				function = "pwm1";
          			};
          

          第二,在sun8i-v3s-licheepi-zero.dts中使能pwm,

          &pwm {
                          pinctrl-names = "default";
                          pinctrl-0 = <&pwm0_pins>, <&pwm1_pins>;
                          status = "okay";
                  };
          

          修改了这两个文件,下面要做的就是把他们编译成dtb,下载到sd卡里面,等待重启即可。注意,拷贝的dtb文件是un8i-v3s-licheepi-zero-dock.dtb。

          3、pwm驱动文件

          如果对驱动代码有兴趣,可以通过sun8i-v3s-pwm这个关键字去查找一下。查找后发现,相关的驱动文件名是drivers/pwm/pwm-sun4i.c,不想下载的朋友也可以在网上直接查看代码,链接地址如下所示,

          https://github.com/Lichee-Pi/linux/blob/zero-4.14.y/drivers/pwm/pwm-sun4i.c

          此外,之前这份驱动已经包含在了zImage里面,所以不需要重新编译内核。

          4、开始调试

          前面如果大家做过实验,就可以发现,如果我们没有修改设备树文件,那么发现在/sys/class/pwm节点下什么也没有。但是修改了之后,就会发现/sys/class/pwm一下子多了很多的内容,

          # cd /sys/class
          # cd pwm/
          # ls
          pwmchip0
          # cd pwmchip0/
          # ls
          device     export     npwm       power      subsystem  uevent     unexport
          # ls -l
          total 0
          lrwxrwxrwx    1 root     root             0 Jan  1 00:31 device -> ../../../1c21400.pwm
          --w-------    1 root     root          4096 Jan  1 00:31 export
          -r--r--r--    1 root     root          4096 Jan  1 00:31 npwm
          drwxr-xr-x    2 root     root             0 Jan  1 00:31 power
          lrwxrwxrwx    1 root     root             0 Jan  1 00:31 subsystem -> ../../../../../../class/pwm
          -rw-r--r--    1 root     root          4096 Jan  1 00:31 uevent
          --w-------    1 root     root          4096 Jan  1 00:31 unexport
          

          首先,我们可以通过export来使能通道,输入0就可以创建通道0,输入1就可以创建通道1,根据具体情况而定。

          # echo 0 > /sys/class/pwm/pwmchip0/export
          # ls
          device     npwm       pwm0       uevent
          export     power      subsystem  unexport
          # ls -l
          total 0
          lrwxrwxrwx    1 root     root             0 Jan  1 00:31 device -> ../../../1c21400.pwm
          --w-------    1 root     root          4096 Jan  1 00:31 export
          -r--r--r--    1 root     root          4096 Jan  1 00:31 npwm
          drwxr-xr-x    2 root     root             0 Jan  1 00:31 power
          drwxr-xr-x    3 root     root             0 Jan  1 00:31 pwm0
          lrwxrwxrwx    1 root     root             0 Jan  1 00:31 subsystem -> ../../../../../../class/pwm
          -rw-r--r--    1 root     root          4096 Jan  1 00:31 uevent
          --w-------    1 root     root          4096 Jan  1 00:31 unexport
          # echo 1 > /sys/class/pwm/pwmchip0/export
          # ls -l
          total 0
          lrwxrwxrwx    1 root     root             0 Jan  1 00:31 device -> ../../../1c21400.pwm
          --w-------    1 root     root          4096 Jan  1 00:31 export
          -r--r--r--    1 root     root          4096 Jan  1 00:31 npwm
          drwxr-xr-x    2 root     root             0 Jan  1 00:31 power
          drwxr-xr-x    3 root     root             0 Jan  1 00:31 pwm0
          drwxr-xr-x    3 root     root             0 Jan  1 00:31 pwm1
          lrwxrwxrwx    1 root     root             0 Jan  1 00:31 subsystem -> ../../../../../../class/pwm
          -rw-r--r--    1 root     root          4096 Jan  1 00:31 uevent
          --w-------    1 root     root          4096 Jan  1 00:31 unexport
          

          通道创建好了,就可以进入到通道里面,看看有哪些配置。以通道0为例,

          # cd pwm0
          # ls
          capture     enable      polarity    uevent
          duty_cycle  period      power
          # ls -l
          total 0
          -r--r--r--    1 root     root          4096 Jan  1 00:32 capture
          -rw-r--r--    1 root     root          4096 Jan  1 00:32 duty_cycle
          -rw-r--r--    1 root     root          4096 Jan  1 00:32 enable
          -rw-r--r--    1 root     root          4096 Jan  1 00:32 period
          -rw-r--r--    1 root     root          4096 Jan  1 00:32 polarity
          drwxr-xr-x    2 root     root             0 Jan  1 00:32 power
          

          简单来说,可以通过三个数值就可以实现最基本的pwm功能。其中period代表频率,duty_cycle代表空占比,enable代表使能开关,

          echo 1000000 > /sys/class/pwm/pwmchip0/pwm0/period
          echo 500000 > /sys/class/pwm/pwmchip0/pwm0/duty_cycle
          echo 1 > /sys/class/pwm/pwmchip0/pwm0/enable
          echo 0 > /sys/class/pwm/pwmchip0/pwm0/enable
          

          假设cpu频率是1GHz,而我们希望得到的pwm频率是1000,那么这里的period就是1G/1000,而duty_cycle被设置成了500000,代表空占比是50%,enable为1代表打开,0则代表关闭。通道1也是这个道理,用同样的方法配置一下即可。

          5、实际测量和验证

          如果需要实际验证pwm的效果,最好有一个示波器,这样会比较方便一点,效果也比较明显。

          81dd38bc3e8f49c68202b988a76eadff.png

          1 Reply Last reply Reply Quote Share 1
          • bayche
            bayche LV 6 last edited by

            gpio输出

            不管是对mcu,还是对soc来说,gpio肯定是越多越好。但是一个芯片上,它的引脚总是有限的,特别对于非BGA的soc来说,芯片又要做的小,引脚又要多,这几乎是不可能的。因此,在芯片领域,很多引脚功能都是复用的,至于选用哪一个功能完全看客户自己的选择,很多时候只能2选1,或者3选1。

            对于v3s也是一样的,从全志的芯片手册,也就是Allwinner_V3s_Datasheet_V1.0.pdf中的第53页,我们看到v3s中很多的功能也是复用的,

            3a0f61c49bb34afa8aca44af71f1e12c.png

            所以,大家如果在荔枝派中看到,给客户的引脚只有那么几个,这其实是误解。有很多的pin,如果不使用,完全可以拿过来直接当成gpio使用。

            1、使用spi接口当成gpio口

            070ef1ec2e34476bba6a9c4fd5d63418.png

            这张图我们已经使用过很多次。图中有一个spi接口,之前主要是用作norflash访问使用的。现在因为所有系统都保存在sd卡里面,因此完全可以用这个当成gpio使用。

            2、修改sun8i-v3s.dtsi文件

            首先注释掉之前spi0_pins这个部分,

            /*spi0_pins: spi0 {
            				pins = "PC0", "PC1", "PC2", "PC3";
            				function = "spi0";
            };*/
            

            接着注释掉spi0,

            /*spi0: spi@1c68000 {
            			compatible = "allwinner,sun8i-h3-spi";
            			reg = <0x01c68000 0x1000>;
            			interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
            			clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>;
            			clock-names = "ahb", "mod";
            			pinctrl-names = "default";
            			pinctrl-0 = <&spi0_pins>;
            			resets = <&ccu RST_BUS_SPI0>;
            			status = "disabled";
            			#address-cells = <1>;
            			#size-cells = <0>;
            		};*/
            

            注释掉这两部分呢,重新编译成sun8i-v3s-licheepi-zero-dock.dtb文件就可以了。细心的同学也许会看到sun8i-v3s-licheepi-zero.dts和sun8i-v3s-licheepi-zero-dock.dts这两个文件中均有leds的配置,是不是status设置为okay就好了?要注意它们的状态都是写死的,后期不能通过命令和配置的方法来解决,虽然启动后也可以在/sys/kernel/debug/gpio下面看到映射关系,这个需要注意下。

            3、重启开发板

            重启开发板之后,首先需要查看一下端口使用情况,没有debug信息,先要mount debugfs系统,

            mount -t debugfs debugfs /sys/kernel/debug
            

            加载好了之后,就可以看看端口的使用情况了,

            # cat /sys/kernel/debug/gpio
            gpiochip0: GPIOs 0-223, parent: platform/1c20800.pinctrl, 1c20800.pinctrl:
             gpio-166 (                    |usb0_id_det         ) in  lo
            

            看上去gpio情况还算正常。

            4、创建通道,开始设备外设

            看过上面一篇文章的同学,对于/sys/class/pwm里面的export不会陌生。但是pwm只有两个,分别是0和1,gpio这么多,我们怎么把这些pin和通道bind在一起呢?其实这里面是有规律的。首先我们找到一个pin,但不知道它的序号是多少,那可以先找到名称,比如SPI_CS,

            8de808b8d2c54627a8afcef1acace1ca.png

            接着看Allwinner_V3s_Datasheet_V1.0.pdf中的第54页,获取引脚名称,

            0da71c1c51604cb894cf905798405d85.png

            找到了这个信号叫PC2,下面就好办了。所有的端口一般都是channel = 32*x+y来实现的。PA、PB、PC...,这些代表x,分别是0、1、2...。而PC2中的2就代表y,如果是PB9,那么y就是9。所以对于PC2来说,channel = 32 * 2+ 2,也就是66,就是这么简单。那么,刚才说的PB9呢,它的channel = 32 * 1 + 9,应该就是41。

            说了这么多,下面开始实验,

            echo 66 > /sys/class/gpio/export
            echo out > /sys/class/gpio/gpio66/direction
            echo 1 > /sys/class/gpio/gpio66/value
            echo 0 > /sys/class/gpio/gpio66/value
            

            四条命令依次解释下,第一条创建channel 66。第二条呢,设定chanel 66的方向为输出。第三条,设置高电平,与此相对的,第四条就是设置低电平。

            为了验证设置的电平是不是正确,一个靠谱的办法就是在spi_cs处于高电平和低电平的时候都测量下,这样就知道电压有没有设置对了。

            71b7fa7e0c4d41cca98b88f726708f28.png

            1 Reply Last reply Reply Quote Share 0
            • bayche
              bayche LV 6 last edited by

              音频输出和音频录制

              之前在芯片公司的时候,基本没有看过音频这一块,只知道有个alsa框架这么个知识点。要驱动音频,需要两部分,一部分就是底层驱动,一部分就是alsa上层接口,两者缺一不可。对于荔枝派来说,底层的驱动其实都已经包含在linux kernel里面了,客户只要自己port好一个alsa库,或者类alsa库,就可以开始播放音频、录制音频了。

              1、电路

              电路分成两个部分,一个是mic,也就是麦克风,录制音频用,

              27c4b9919eb34cc98650b6ea2db670ef.png

              另外一个是headphone,也就是耳机,输出音频用,

              fd7814995c2a4cd2be5c48b1a36c5e08.png

              不管是哪一个电路,通过观察发现,电路中并没有音频电路经常出现的iis接口。这就说明,v3s和网卡一样,本身已经集成了数模转换和功放功能了,不需要额外芯片了。遇到这种情况,一般soc厂商都会自己默默把驱动代码准备好,省着使用者去二次开发了。毕竟都是自己的东西,驱动写起来也得心应手,不用外人劳神劳力了。

              2、设备树

              本次使用的内核依然是linux-zero-4.14.y,顶层设备树是 sun8i-v3s-licheepi-zero-dock.dts。查看一下设备树的内容,可以发现声卡驱动已经集成到里面了,

              &codec {
              	allwinner,audio-routing =
              		"Headphone", "HP",
              		"Headphone", "HPCOM",
              		"MIC1", "Mic",
              		"Mic",  "HBIAS";
              	status = "okay";
              };
              

              当然仅仅有这些还是不够的,进一步阅读sun8i-v3s.dtsi文件,可以看到codec的具体实现细节,

              codec: codec@01c22c00 {
              			#sound-dai-cells = <0>;
              			compatible = "allwinner,sun8i-v3s-codec";
              			reg = <0x01c22c00 0x400>;
              			interrupts = <GIC_SPI 29 IRQ_TYPE_LEVEL_HIGH>;
              			clocks = <&ccu CLK_BUS_CODEC>, <&ccu CLK_AC_DIG>;
              			clock-names = "apb", "codec";
              			resets = <&ccu RST_BUS_CODEC>;
              			dmas = <&dma 15>, <&dma 15>;
              			dma-names = "rx", "tx";
              			allwinner,codec-analog-controls = <&codec_analog>;
              			status = "disabled";
              		};
               
              		codec_analog: codec-analog@01c23000 {
              			compatible = "allwinner,sun8i-v3s-codec-analog";
              			reg = <0x01c23000 0x4>;
              		};
              

              有了这两个配置,基本上声卡驱动就有保证了。好在这些都是默认配置,对于用户来说,就是什么都不需要做,系统上电后,声卡就已经加载好了。

              3、判断声卡是否正确加载

              判断声卡有没有加载好,主要有两个办法。第一,就是看内核启动日志;第二,就是看/dev/snd下面的节点有没有正确生成。查看日志的话,如果启动ok,应该是可以看到这些打印的,

              [    1.380188] sun4i-codec 1c22c00.codec: Codec <-> 1c22c00.codec mapping ok
              [    1.388822] sun6i-rtc 1c20400.rtc: setting system clock to 1970-01-01 00:00:40 UTC (40)
              [    1.397167] vcc5v0: disabling
              [    1.400142] ALSA device list:
              [    1.403106]   #0: V3s Audio Codec
              

              不放心日志的话,可以输入ls -l /dev/snd进一步确认下,

              # ls -l /dev/snd
              total 0
              crw-------    1 root     root      116,   0 Jan  1 00:00 controlC0
              crw-------    1 root     root      116,  24 Jan  1 00:00 pcmC0D0c
              crw-------    1 root     root      116,  16 Jan  1 00:00 pcmC0D0p
              crw-------    1 root     root      116,  33 Jan  1 00:00 timer
              

              另外,也可以查看下/sys/class/sound下面的节点,也可以算是一种方法,

              # cd /sys/class/sound/
              # ls
              card0      controlC0  pcmC0D0c   pcmC0D0p   timer
              

              不管是哪一种方法,基本上看到这些打印或者节点信息,就可以认为声卡被系统正确加载了。

              4、编译安装TinyAlsa

              前面我们说过,如果需要让声卡驱动起来,除了驱动本身之外,还需要一个alsa的库。TinyAlsa就是比较适合使用的那个库,下载位置在这,

              https://github.com/tinyalsa/tinyalsa
              

              下载好压缩包之后,拷贝到ubuntu虚拟机,直接用arm-linux-gnueabihf-交叉编译器来编译。解压一下,接着cd到解压目录后,直接输入这个命令,

              make CROSS_COMPILE=arm-linux-gnueabihf-
              

              不出意外的话,在utils子目录下面,就可以看到四个编译好的工具,

              tinycap
              tinymix
              tinypcminfo
              tinyplay
              

              通过观察编译日志,发现这四个程序都是静态链接libtinyalsa.a,所以直接拷贝四个文件即可,不需要拷贝其他动态库。

              make -C src
              make[1]: Entering directory '/home/feixiaoxing/Desktop/tinyalsa-master/src'
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include -fPIC    -c -o limits.o limits.c
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include -fPIC    -c -o mixer.o mixer.c
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include -fPIC    -c -o pcm.o pcm.c
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include -fPIC    -c -o pcm_plugin.o pcm_plugin.c
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include -fPIC    -c -o pcm_hw.o pcm_hw.c
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include -fPIC    -c -o snd_card_plugin.o snd_card_plugin.c
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include -fPIC    -c -o mixer_plugin.o mixer_plugin.c
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include -fPIC    -c -o mixer_hw.o mixer_hw.c
              arm-linux-gnueabihf-ar rv libtinyalsa.a limits.o mixer.o pcm.o pcm_plugin.o pcm_hw.o snd_card_plugin.o mixer_plugin.o mixer_hw.o
              arm-linux-gnueabihf-ar: creating libtinyalsa.a
              a - limits.o
              a - mixer.o
              a - pcm.o
              a - pcm_plugin.o
              a - pcm_hw.o
              a - snd_card_plugin.o
              a - mixer_plugin.o
              a - mixer_hw.o
              arm-linux-gnueabihf-gcc  -shared -Wl,-soname,libtinyalsa.so.2 limits.o mixer.o pcm.o pcm_plugin.o pcm_hw.o snd_card_plugin.o mixer_plugin.o mixer_hw.o -o libtinyalsa.so.2.0.0
              ln -sf libtinyalsa.so.2.0.0 libtinyalsa.so.2
              ln -sf libtinyalsa.so.2 libtinyalsa.so
              make[1]: Leaving directory '/home/feixiaoxing/Desktop/tinyalsa-master/src'
              make -C utils
              make[1]: Entering directory '/home/feixiaoxing/Desktop/tinyalsa-master/utils'
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include -fPIC -O2   -c -o tinyplay.o tinyplay.c
              arm-linux-gnueabihf-gcc -L ../src -pie  tinyplay.o ../src/libtinyalsa.a  -ldl -o tinyplay
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include -fPIC -O2   -c -o tinycap.o tinycap.c
              arm-linux-gnueabihf-gcc -L ../src -pie  tinycap.o ../src/libtinyalsa.a  -ldl -o tinycap
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include -fPIC -O2   -c -o tinymix.o tinymix.c
              arm-linux-gnueabihf-gcc -L ../src -pie  tinymix.o ../src/libtinyalsa.a  -ldl -o tinymix
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include -fPIC -O2   -c -o tinypcminfo.o tinypcminfo.c
              arm-linux-gnueabihf-gcc -L ../src -pie  tinypcminfo.o ../src/libtinyalsa.a  -ldl -o tinypcminfo
              make[1]: Leaving directory '/home/feixiaoxing/Desktop/tinyalsa-master/utils'
              make -C doxygen
              make[1]: Entering directory '/home/feixiaoxing/Desktop/tinyalsa-master/doxygen'
              Makefile:11: "doxygen is not available please install it"
              make[1]: Nothing to be done for 'all'.
              make[1]: Leaving directory '/home/feixiaoxing/Desktop/tinyalsa-master/doxygen'
              make -C examples
              make[1]: Entering directory '/home/feixiaoxing/Desktop/tinyalsa-master/examples'
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include    pcm-readi.c ../src/libtinyalsa.so  -ldl -o pcm-readi
              arm-linux-gnueabihf-gcc -Wall -Wextra -Werror -Wfatal-errors -I ../include    pcm-writei.c ../src/libtinyalsa.so  -ldl -o pcm-writei
              make[1]: Leaving directory '/home/feixiaoxing/Desktop/tinyalsa-master/examples'
              

              如何把四个文件拷贝到开发板,这个就不再赘述了。主要还是使用python http库和开发板上的wget命令来共同完成的。

              5、寻找音频文件

              目前,tinyalsa的工具只能播放wav文件,所以我们还需要找一个可以下载wav文件的网站。这里推荐一个网站链接给大家,

              https://www.xmwav.com/
              

              下载好音频文件之后,还是用同样的方法下载到开发板上面。

              6、播放音频和录制音频

              6.1 播放音频

              播放音频的时候,除了正常插入耳机,还有三个地方需要注意下,不然听不到音频输出。第一,就是打开播放开关;第二,设置音量;第三,确认下前面的设置有没有对。这三个步骤都做完了,就可以播放音频了。设置命令主要是tinymix,播放命令是tinyplay。

              # ./tinymix set 1 40
              # ./tinymix set 2 1
              # ./tinymix contents
              Number of controls: 13
              ctl	type	num	name                                    value
              0	INT	1	DAC Playback Volume                     63 (range 0->63)
              1	INT	1	Headphone Playback Volume               40 (range 0->63)
              2	BOOL	2	Headphone Playback Switch               On, Off
              3	INT	1	Mic1 Playback Volume                    3 (range 0->7)
              4	INT	1	Mic1 Boost Volume                       4 (range 0->7)
              5	INT	1	ADC Gain Capture Volume                 3 (range 0->7)
              6	BOOL	2	DAC Playback Switch                     Off, Off
              7	BOOL	2	DAC Reversed Playback Switch            Off, Off
              8	BOOL	2	Mic1 Playback Switch                    Off, Off
              9	BOOL	2	Mixer Capture Switch                    Off, Off
              10	BOOL	2	Mixer Reversed Capture Switch           Off, Off
              11	BOOL	2	Mic1 Capture Switch                     Off, Off
              12	ENUM	2	Headphone Source Playback Route         > DAC, Mixer, , > DAC, Mixer,
              

              播放刚才下载的音频,

              # ./tinyplay  test.wav 
              playing 'test.wav': 2 ch, 44100 hz, 16-bit signed PCM
              

              6.2 录制音频

              录制音频和播放音频差不多。第一步打开录制开关,第二步确认配置ok。前两步都做好了之后,就可以用tinycap录制音频了,结束录制用ctrl+c,

              # ./tinymix set 11 1
              # ./tinymix contents
              Number of controls: 13
              ctl	type	num	name                                    value
              0	INT	1	DAC Playback Volume                     63 (range 0->63)
              1	INT	1	Headphone Playback Volume               40 (range 0->63)
              2	BOOL	2	Headphone Playback Switch               On, Off
              3	INT	1	Mic1 Playback Volume                    3 (range 0->7)
              4	INT	1	Mic1 Boost Volume                       4 (range 0->7)
              5	INT	1	ADC Gain Capture Volume                 3 (range 0->7)
              6	BOOL	2	DAC Playback Switch                     Off, Off
              7	BOOL	2	DAC Reversed Playback Switch            Off, Off
              8	BOOL	2	Mic1 Playback Switch                    Off, Off
              9	BOOL	2	Mixer Capture Switch                    Off, Off
              10	BOOL	2	Mixer Reversed Capture Switch           Off, Off
              11	BOOL	2	Mic1 Capture Switch                     On, Off
              12	ENUM	2	Headphone Source Playback Route         > DAC, Mixer, , > DAC, Mixer, 
              # ./tinycap record.wav
              Capturing sample: 2 ch, 48000 hz, 16 bit
              

              如果想要确认录制的音频有没有问题,那么直接用tinyplay播放下即可,

              # ./tinyplay record.wav 
              playing 'record.wav': 2 ch, 48000 hz, 16-bit signed PCM
              Played 1601536 bytes. Remains 0 bytes.
              
              1 Reply Last reply Reply Quote Share 0
              • 1 / 1
              • First post
                Last post

              Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号

              行为准则 | 用户协议 | 隐私权政策