全志V3S嵌入式驱动开发 - 驱动开发合集
-
之前的文章都是教大家怎么搭建环境、看原理图、编译内核和根文件系统、做镜像,直到现在才进入驱动开发的主题。毕竟整个专栏的目的,还是希望大家能够学会驱动外部硬件。驱动好硬件,分成硬件和软件两个部分,硬件的部分比较好理解,就是看相关的信号有没有对接上即可,但是软件对接、甚至于怎么写软件,大家好像接触的比较少,所以这个时候,就可以简单讨论一下,怎么样才能写驱动代码。
目录:
1.多按键输入驱动
2.PWM驱动
3.GPIO驱动
4.音频输出和音频录制1、了解外部驱动芯片手册
所谓驱动,其实就是用soc里面的io控制器来驱动外部电路,或者说是外部芯片。如果是简单的io和uart这些,当然不需要知道驱动芯片的内容。但是如果驱动的是外部芯片,那么这个时候我们就要把对应芯片的资料找过来,好好读一读。里面协议的内容,就是我们将来编程的依据,
2、了解soc芯片手册
了解外部芯片主要是解决发送内容的问题,而soc芯片则会告诉我们怎么发送这些内容。比如,它是iic接口,还是spi接口,还是普通的uart接口等等。这些内容,都可以通过soc的芯片手册找到,比如下面s3c2440中nandflash controller的介绍,
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%的问题。这样遇到难题,也不会受制于人,效率上不打折扣。 -
多按键输入驱动
前面我们说过,荔枝派的开发板上面,有4个按键本身不是通过gpio连接到soc上面的。它是通过ad的方法,连接到主芯片的。这个时候,不同的按键被按下的时候,就会生成不同的电压或者电流,那么完全可以根据对应的电信号,推算出当前是哪一个按键被按下去了。
1、查找电路图
简单看一下电路之后,下面就是去找设备树,对应的信号是什么、在哪里。
2、查找设备树
在sun8i-v3s-licheepi-zero-dock.dts文件当中,我们发现了这样的内容,
&lradc { vref-supply = <®_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
-
PWM驱动
pwm驱动也是常见的一种驱动方式。常见的pwm,其实就是一组方波,方波中的高低电平之比称之为空占比。通过调节这个空占比,可以实现不同的控制目的,比如说呼吸灯、电机控制等等。接下来,正好可以看看如何对v3s的pwm进行设置,以及如何在linux平台下使用pwm。
1、pwm0和pwm1
目前V3S支持两个pwm输出,分别是pwm0和pwm1。如下图所示,
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的效果,最好有一个示波器,这样会比较方便一点,效果也比较明显。
-
gpio输出
不管是对mcu,还是对soc来说,gpio肯定是越多越好。但是一个芯片上,它的引脚总是有限的,特别对于非BGA的soc来说,芯片又要做的小,引脚又要多,这几乎是不可能的。因此,在芯片领域,很多引脚功能都是复用的,至于选用哪一个功能完全看客户自己的选择,很多时候只能2选1,或者3选1。
对于v3s也是一样的,从全志的芯片手册,也就是Allwinner_V3s_Datasheet_V1.0.pdf中的第53页,我们看到v3s中很多的功能也是复用的,
所以,大家如果在荔枝派中看到,给客户的引脚只有那么几个,这其实是误解。有很多的pin,如果不使用,完全可以拿过来直接当成gpio使用。
1、使用spi接口当成gpio口
这张图我们已经使用过很多次。图中有一个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,
接着看Allwinner_V3s_Datasheet_V1.0.pdf中的第54页,获取引脚名称,
找到了这个信号叫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处于高电平和低电平的时候都测量下,这样就知道电压有没有设置对了。
-
音频输出和音频录制
之前在芯片公司的时候,基本没有看过音频这一块,只知道有个alsa框架这么个知识点。要驱动音频,需要两部分,一部分就是底层驱动,一部分就是alsa上层接口,两者缺一不可。对于荔枝派来说,底层的驱动其实都已经包含在linux kernel里面了,客户只要自己port好一个alsa库,或者类alsa库,就可以开始播放音频、录制音频了。
1、电路
电路分成两个部分,一个是mic,也就是麦克风,录制音频用,
另外一个是headphone,也就是耳机,输出音频用,
不管是哪一个电路,通过观察发现,电路中并没有音频电路经常出现的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.
Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号