接下来可以看其它芯片了。
我仍然不打算看max2837和rffc507x,而是观察max5864芯片,它是adc/dac芯片,它在portapack固件里涉及的代码量很少,只有设置传输方向的功能。
可以看看firmware/application/hw/下面的max5864.cpp和max5864.hpp。
max5864.cpp文件里唯一只有这个set_mode函数
void MAX5864::set_mode(const Mode mode) {
std::array<uint8_t, 1> command { toUType(mode) };
_target.transfer(command.data(), command.size());
}
我感觉这个_target就有点类似之前iic芯片的bus的意思,相当于告诉程序你要操纵哪个芯片,只不过这次这个芯片不是iic总线,所以是target。
然后看看max5864.hpp
#include "spi_arbiter.hpp"
class MAX5864 {
public:
constexpr MAX5864(
spi::arbiter::Target& target
) : _target(target)
{
}
}
我摘录了这么一段代码。这次max5864类的初始化函数里要传入的是spi::arbiter::target而不是之前的I2C& bus, I2C::address_t address了。
这个target应该同时包含了bus(主板上的某一个总线)和address(芯片在总线上的地址)。
另外引用的spi_arbiter.hpp也与i2c_pp.hpp对应,里面应该是spi总线的实现。
然后我们点开spi_arbiter.hpp,可以看到它也在引用spi_pp.hpp
#include "spi_pp.hpp"
namespace spi {
namespace arbiter {
class Arbiter {
public:
constexpr Arbiter(
SPI& bus
) : _bus(bus),
_config(nullptr)
{
}
void transfer(const SPIConfig* const config, void* const data, const size_t count) {
if( config != _config ) {
_bus.stop();
_bus.start(*config);
_config = config;
}
_bus.transfer(data, count);
}
private:
SPI& _bus;
const SPIConfig* _config;
};
class Target {
public:
constexpr Target(
Arbiter& arbiter,
const SPIConfig& config
) : _arbiter(arbiter),
_config(config)
{
}
void transfer(void* const data, const size_t count) {
_arbiter.transfer(&_config, data, count);
}
private:
Arbiter& _arbiter;
const SPIConfig _config;
};
} /* arbiter */
} /* spi */
芯片调用的是target的transfer函数,也就是下面的这个,然后这个函数又调用了arbiter的transfer函数(也就是上面那个transfer函数),最终调用了bus里的transfer函数。而现在的这个bus是开头声明的SPI& bus。
所以spi_arbiter.hpp主要只是一层封装,具体实现还是在spi_pp.cpp和spi_pp.hpp里,与iic的实现文件命名方式是一样的。
有一点要注意,arbiter的transfer函数里除了调用指定的bus的transfer函数以外,还根据前面传过来的config,配置了bus。
接下来,打开spi_pp.cpp发现里面没什么东西,主要都在spi_pp.hpp里。
void start(const SPIConfig& config) {
spiStart(_driver, &config);
}
void transfer(void* const data, const size_t count) {
spiAcquireBus(_driver);
spiSelect(_driver);
spiExchange(_driver, count, data, data);
spiUnselect(_driver);
spiReleaseBus(_driver);
}
摘录了一部分代码,这个start函数,可以对程序指定的spi bus做设置,设置完了才调用spiStart启动。下面的transfer函数就是被spi_arbiter调用的最里层的transfer函数,它针对的是芯片初始化时指定的总线来操作的。里面的spiAcquireBus和spiReleaseBus跟之前iic的函数命名方式也是对应的。
那么这个max5864芯片的初始化工作是在哪里实现的呢?
firmware/application/radio.cpp
static constexpr SPIConfig ssp_config_max5864 = {
.end_cb = NULL,
.ssport = gpio_max5864_select.port(),
.sspad = gpio_max5864_select.pad(),
.cr0 =
CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max5864_spi_f))
| CR0_FRFSPI
| CR0_DSS8BIT
,
.cpsr = ssp1_cpsr,
};
static spi::arbiter::Arbiter ssp1_arbiter(portapack::ssp1);
static spi::arbiter::Target ssp1_target_max5864 {
ssp1_arbiter,
ssp_config_max5864
};
static max5864::MAX5864 baseband_codec { ssp1_target_max5864 };
最后一句话就是在初始化这个max5864类的对象,初始化的参数由ssp1_arbiter和ssp_config_max5864指定,它们分别是所选的总线以及芯片的一些设置。