Portapack应用开发教程 (十六) Debug程序 C 声卡芯片wm8731和ak4951

本文主要是介绍Portapack应用开发教程 (十六) Debug程序 C 声卡芯片wm8731和ak4951,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

我前面改了max2875的驱动代码,可以从max2875驱动输出不同内容到debug屏幕上了。验证了之前看的调用关系都是对的。

但是max2875芯片和rffc507x芯片的代码我还看不太懂。无法仿照他们来实现flash芯片的驱动(都是spi的)。

我还是决定从声卡芯片入手,因为他们是iic的,我在调试小四轴飞控时对mpu6050的操作也是经过iic的。另外,前段时间加入内置喇叭和麦克风时我也对声卡芯片比较熟悉了。

其实Portapack声卡芯片有两种,老款和新款板子的声卡芯片是不一样的。对于两种芯片,portapack固件都有支持。

左边是老款的右边是新款的板子,可以看到高亮部分的声卡芯片名称是不一样的。

 

点进去以后如下:

 

左边老款芯片的寄存器只有10个,右边有大约50个。

接下来看程序

firmware/application/portapack.cpp

WM8731 audio_codec_wm8731 { i2c0, 0x1a };
AK4951 audio_codec_ak4951 { i2c0, 0x12 };enum class PortaPackModel {R1_20150901,R2_20170522,
};static PortaPackModel portapack_model() {static Optional<PortaPackModel> model;if( !model.is_valid() ) {if( audio_codec_wm8731.detected() ) {model = PortaPackModel::R1_20150901;} else {model = PortaPackModel::R2_20170522;}}return model.value();
}static audio::Codec* portapack_audio_codec() {return (portapack_model() == PortaPackModel::R2_20170522)? static_cast<audio::Codec*>(&audio_codec_ak4951): static_cast<audio::Codec*>(&audio_codec_wm8731);
}

这个代码开头两行指定了2种芯片的iic地址。

然后判断是哪个声卡芯片,看看能否检测到wm8731来判断portapack板子型号。

接下来可以看看firmware/common/下的wm8731.cpp wm8731.hpp和ak4951.cpp ak4951.hpp

先看wm8731.cpp看看detected函数是如何工作的

bool WM8731::detected() {return reset();
}bool WM8731::reset() {return write(0x0f, 0);
}bool WM8731::write(const Register reg) {return write(toUType(reg), map.w[toUType(reg)]);
}bool WM8731::write(const address_t reg_address, const reg_t value) {const uint16_t word = (reg_address << 9) | value;const std::array<uint8_t, 2> values {static_cast<uint8_t>(word >> 8),static_cast<uint8_t>(word & 0xff),};return bus.transmit(bus_address, values.data(), values.size());
}

detected调用reset,reset调用write,write最终会调用到bus.transmit,第一个参数bus_address就是前面提到的iic地址。

如果声卡芯片指定iic地址能发送成功,代表能找到这个声卡芯片。

 

注意看一下上面代码里有2个write,其实reset里直接调用了下边这个write。上面这个write内部也在调用下边的write。

下面的write有2个参数,一个是寄存器地址,另一个是要写入寄存器的值,从这里可以理解map.w是什么意思。

map.w[toUType(reg)],应该是可以从内存的map变量里把寄存器对应的值读出来,然后从iic总线上发给要控制的芯片。toUType只是类型转换。

在wm8731.hpp里面,有代码在创建这个map变量,代码如下:

struct Register_Type {LeftLineIn					left_line_in;RightLineIn					right_line_in;LeftHeadphoneOut			left_headphone_out;RightHeadphoneOut			right_headphone_out;AnalogAudioPathControl		analog_audio_path_control;DigitalAudioPathControl		digital_audio_path_control;PowerDownControl			power_down_control;DigitalAudioInterfaceFormat	digital_audio_interface_format;SamplingControl				sampling_control;ActiveControl				active_control;
};static_assert(sizeof(Register_Type) == reg_count * sizeof(reg_t), "Register_Type wrong size");struct RegisterMap {constexpr RegisterMap(Register_Type values) : r(values){}union {Register_Type r;std::array<reg_t, reg_count> w;};
};static_assert(sizeof(RegisterMap) == reg_count * sizeof(reg_t), "RegisterMap type wrong size");constexpr RegisterMap default_after_reset { Register_Type {.left_line_in = {.linvol		= 0b10111,.reserved0	= 0b00,.linmute	= 0b1,.lrinboth	= 0b0,.reserved1 	= 0,},.right_line_in = {.rinvol		= 0b10111,.reserved0	= 0b00,.rinmute	= 0b1,.rlinboth	= 0b0,.reserved1 	= 0,},.left_headphone_out = {.lhpvol		= 0b1111001,.lzcen		= 0b0,.lrhpboth	= 0b0,.reserved0	= 0,},.right_headphone_out = {.rhpvol		= 0b1111001,.rzcen		= 0b0,.rlhpboth	= 0b0,.reserved0	= 0,},.analog_audio_path_control = {.micboost	= 0b0,.mutemic	= 0b1,.insel		= 0b0,.bypass		= 0b1,.dacsel		= 0b0,.sidetone	= 0b0,.sideatt	= 0b00,.reserved0	= 0,},.digital_audio_path_control = {.adchpd		= 0b0,.deemp		= 0b00,.dacmu		= 0b1,.hpor		= 0b0,.reserved0	= 0,},.power_down_control = {.lineinpd	= 0b1,.micpd		= 0b1,.adcpd		= 0b1,.dacpd		= 0b1,.outpd		= 0b1,.oscpd		= 0b0,.clkoutpd	= 0b0,.poweroff	= 0b1,.reserved0	= 0,},.digital_audio_interface_format = {.format		= 0b10,.iwl		= 0b10,.lrp		= 0b0,.lrswap		= 0b0,.ms			= 0b0,.bclkinv	= 0b0,.reserved0	= 0,},.sampling_control = {.usb_normal	= 0b0,.bosr		= 0b0,.sr			= 0b0000,.clkidiv2	= 0b0,.clkodiv2	= 0b0,.reserved0	= 0,},.active_control = {.active		= 0b0,.reserved0	= 0,},
} };RegisterMap map { default_after_reset };

这种做法其实有点奇怪的,map.w是从map变量里读取而map.r是往map变量里写入。跟常人理解是反的。

接下来看看wm8731.cpp代码

代码不多,先看init也就是初始化函数,是在设置芯片的各部分。结构都类似,我找出其中一段,看看怎样调用

write(PowerDownControl {.lineinpd = 1,	.micpd = 0,.adcpd = 0,.dacpd = 0,.outpd = 0,.oscpd = 1,.clkoutpd = 1,.poweroff = 0,.reserved0 = 0,});

这个init里调用的write函数其实不是前面提到的那2个。

而是下面这段代码第一个void WM8731::write(const PowerDownControl value) 函数,然后第一个函数再调用第二个write函数,最后再调用第三个wriite函数。

void WM8731::write(const PowerDownControl value) {map.r.power_down_control = value;write(Register::PowerDownControl);
}bool WM8731::write(const Register reg) {return write(toUType(reg), map.w[toUType(reg)]);
}bool WM8731::write(const address_t reg_address, const reg_t value) {const uint16_t word = (reg_address << 9) | value;const std::array<uint8_t, 2> values {static_cast<uint8_t>(word >> 8),static_cast<uint8_t>(word & 0xff),};return bus.transmit(bus_address, values.data(), values.size());
}

而且第一个write函数,先要把值往内存里的map变量里写,到第二个write函数时再从map里读出来,发给第三个write函数。这样的实现好奇怪,不知道为什么一定要经过map这一步,而不是直接在iic总线上发给芯片。

uint32_t WM8731::reg_read(const size_t reg_address) {return map.w[reg_address];
}

这里还设有个读取寄存器的函数,它其实也是从内存里的map变量中读出来的,而不是直接在iic总线上访问对应芯片读出来的。

我估计map变量的作用应该还是为了读以前的配置用的,毕竟直接从mcu的内存里读比从别的芯片里读方便多了。

接下来看看第二种声卡芯片,打开ak4951.hpp

可以看到它比第一种芯片(wm8731)复杂一些,但是芯片设置部分的代码实现思路是一样的。

ak4951的init函数里直接在设置map.r,所以相当于不需要wm8731的第一行的write函数了,然后ak芯片里的update函数,相当于wm芯片里的第二行write函数。

而ak芯片里的write函数和wm芯片的第三行write函数是一样的。

void AK4951::write(const address_t reg_address, const reg_t value) {const std::array<uint8_t, 2> tx { reg_address, value };bus.transmit(bus_address, tx.data(), tx.size());
}

tx变量就相当于之前的values变量,只是写法更简洁。

不过ak芯片的寄存器读取功能的实现方式区别就比较大了。

reg_t AK4951::read(const address_t reg_address) {const std::array<uint8_t, 1> tx { reg_address };std::array<uint8_t, 1> rx { 0x00 };bus.transmit(bus_address, tx.data(), tx.size());bus.receive(bus_address, rx.data(), rx.size());return rx[0];
}

这回是在iic总线上先发出要查询的寄存器地址,然后再从iic总线上接收来自ak芯片的回答。而不是直接从内存里的map变量得到结果。

芯片初始化的时候就已经有了bus_address的数据。然后是在bus.transmit(bus_address,...,...)函数调用的时候,把这个iic的bus变量与bus_address联系在了一起,bus.receive同理。

这样声卡芯片的驱动代码就看得差不多了,接下去可以看看spi总线是不是也用差不多方式实现通信的。

这篇关于Portapack应用开发教程 (十六) Debug程序 C 声卡芯片wm8731和ak4951的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/483604

相关文章

Python结合PyWebView库打造跨平台桌面应用

《Python结合PyWebView库打造跨平台桌面应用》随着Web技术的发展,将HTML/CSS/JavaScript与Python结合构建桌面应用成为可能,本文将系统讲解如何使用PyWebView... 目录一、技术原理与优势分析1.1 架构原理1.2 核心优势二、开发环境搭建2.1 安装依赖2.2 验

Java字符串操作技巧之语法、示例与应用场景分析

《Java字符串操作技巧之语法、示例与应用场景分析》在Java算法题和日常开发中,字符串处理是必备的核心技能,本文全面梳理Java中字符串的常用操作语法,结合代码示例、应用场景和避坑指南,可快速掌握字... 目录引言1. 基础操作1.1 创建字符串1.2 获取长度1.3 访问字符2. 字符串处理2.1 子字

IDEA自动生成注释模板的配置教程

《IDEA自动生成注释模板的配置教程》本文介绍了如何在IntelliJIDEA中配置类和方法的注释模板,包括自动生成项目名称、包名、日期和时间等内容,以及如何定制参数和返回值的注释格式,需要的朋友可以... 目录项目场景配置方法类注释模板定义类开头的注释步骤类注释效果方法注释模板定义方法开头的注释步骤方法注

使用Python开发一个带EPUB转换功能的Markdown编辑器

《使用Python开发一个带EPUB转换功能的Markdown编辑器》Markdown因其简单易用和强大的格式支持,成为了写作者、开发者及内容创作者的首选格式,本文将通过Python开发一个Markd... 目录应用概览代码结构与核心组件1. 初始化与布局 (__init__)2. 工具栏 (setup_t

SpringShell命令行之交互式Shell应用开发方式

《SpringShell命令行之交互式Shell应用开发方式》本文将深入探讨SpringShell的核心特性、实现方式及应用场景,帮助开发者掌握这一强大工具,具有很好的参考价值,希望对大家有所帮助,如... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定

SpringBoot应用中出现的Full GC问题的场景与解决

《SpringBoot应用中出现的FullGC问题的场景与解决》这篇文章主要为大家详细介绍了SpringBoot应用中出现的FullGC问题的场景与解决方法,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录Full GC的原理与触发条件原理触发条件对Spring Boot应用的影响示例代码优化建议结论F

Python虚拟环境终极(含PyCharm的使用教程)

《Python虚拟环境终极(含PyCharm的使用教程)》:本文主要介绍Python虚拟环境终极(含PyCharm的使用教程),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录一、为什么需要虚拟环境?二、虚拟环境创建方式对比三、命令行创建虚拟环境(venv)3.1 基础命令3

使用Node.js制作图片上传服务的详细教程

《使用Node.js制作图片上传服务的详细教程》在现代Web应用开发中,图片上传是一项常见且重要的功能,借助Node.js强大的生态系统,我们可以轻松搭建高效的图片上传服务,本文将深入探讨如何使用No... 目录准备工作搭建 Express 服务器配置 multer 进行图片上传处理图片上传请求完整代码示例

MySQL 分区与分库分表策略应用小结

《MySQL分区与分库分表策略应用小结》在大数据量、复杂查询和高并发的应用场景下,单一数据库往往难以满足性能和扩展性的要求,本文将详细介绍这两种策略的基本概念、实现方法及优缺点,并通过实际案例展示如... 目录mysql 分区与分库分表策略1. 数据库水平拆分的背景2. MySQL 分区策略2.1 分区概念

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S