接下来可以看max2837的代码了,它也是调用spi_arbiter来实现spi通信的。
firmware/application/hw/max2837.cpp max2837.hpp
但它引入了dirty resigster的概念。
firmware/application/dirty_registers.hpp
这个mask就是一连串bit,每个bit对应一个寄存器。需要配置哪个寄存器,就把它对应的bit设置为1。然后就会把内存里map变量存的内容写到对应寄存器里去。
如果寄存器不需要写入,bit就是0。
dirty_register的几个方法就是在操纵这串bit,建议先百度一下bitset。
class DirtyRegisters {
public:
using mask_t = std::bitset<RegisterCount>;
/* TODO: I feel like I might regret implementing this cast operator... */
operator bool() const {
return mask.any();
}
void set() {
mask.set();
}
void clear() {
mask.reset();
}
void clear(const size_t reg_num) {
mask.reset(reg_num);
}
typename mask_t::reference operator[](const size_t reg_num) {
return mask[reg_num];
}
typename mask_t::reference operator[](const RegisterType reg) {
return mask[toUType(reg)];
}
private:
mask_t mask { };
};
开头mask_t就是一串长度与register count相等的bitset,其它方法都是对bitset函数的封装。
接下来可以看max2837.cpp了,还是先看init函数,一开头就是对内存中的map里的各个变量都设置好
最后是这两行。
_dirty.set();
flush();
_dirty.set()表示把整个bitset都设置为1,也就是说所有寄存器都需要配置。
flush函数如下
void MAX2837::flush() {
if( _dirty ) {
for(size_t n=0; n<reg_count; n++) {
if( _dirty[n] ) {
write(n, _map.w[n]);
}
}
_dirty.clear();
}
}
它的功能就是看所有bitset里哪些bit是1,如果是1,就调用write函数把map里对应的值写入到寄存器里。
全部设置完了以后就调用dirty.clear把整个bitset里的bit全部设置为0。
void MAX2837::write(const address_t reg_num, const reg_t value) {
uint16_t t = (0U << 15) | (reg_num << 10) | (value & 0x3ffU);
_target.transfer(&t, 1);
}
上面是flush函数里调用的write函数,最终调用的也是上一篇文章讲的_target.transfer函数,会从spi把数据发出去。
reg_t MAX2837::read(const address_t reg_num) {
uint16_t t = (1U << 15) | (reg_num << 10);
_target.transfer(&t, 1U);
return t & 0x3ffU;
}
再看看read函数也差不多,也是调用target.transfer。
比较一下write和read,都是调用target.transfer,参数都是16bit的变量t,变量最左边的0或者1代表写入还是读取,中间5个bit存储的是寄存器地址,最右边10个bit存的就是寄存器的。如果是要写入这个值需要事先指定,如果是读取,这个值就先空着,transfer完了就会把寄存器里读到的值存到这里面。
0x3ff对应的就是10个1。与它按位与就可以取出最右边10个bit的数据。
init函数开头结尾还有gpio操作。
具体要看/firmware/common/hackrf_gpio.hpp和/firmware/common/gpio.hpp
hackrf_gpio.hpp里写了不同芯片跟mcu gpio引脚对应关系。
gpio.hpp里的是跟gpio有关系的函数的具体实现。比较底层。