portapack固件的芯片驱动看得差不多了。我们知道了这些芯片分为3类,iic通信、软件spi、硬件spi。
接下来我们打开hackrf固件,看看flash芯片是哪类。然后看看它与同类别的其它芯片有哪些细微差别,一步步把这些差别改到portapack固件里去。
打开hackrf/firmware/common/
可以看到这几个文件max2837.c max2837_target.c | max5864.c max5864_target.c | rffc5071.c rffc5071_spi.c | w25q80bv.c w25q80bv_target.c | si5351c.c
光从命名方式来看我们要找的flash芯片——w25q80bv跟max2837 max5864是一样的。说明flash芯片也是硬件spi总线连接的。
接下来我们具体看看之前已经比较熟悉的3个芯片 max2837 max5864与rffc5071是如何用不同方式实现spi通信的。
我一开始有点看不明白,如果你打开下面3个芯片驱动:
firmware/common/rffc5071.c max2837.c max5864.c
static uint16_t rffc5071_spi_read(rffc5071_driver_t* const drv, uint8_t r) {
(void)drv;
uint16_t data[] = { 0x80 | (r & 0x7f), 0xffff };
spi_bus_transfer(drv->bus, data, 2);
return data[1];
}
static void rffc5071_spi_write(rffc5071_driver_t* const drv, uint8_t r, uint16_t v) {
(void)drv;
uint16_t data[] = { 0x00 | (r & 0x7f), v };
spi_bus_transfer(drv->bus, data, 2);
}
static uint16_t max2837_read(max2837_driver_t* const drv, uint8_t r) {
uint16_t value = (1 << 15) | (r << 10);
spi_bus_transfer(drv->bus, &value, 1);
return value & 0x3ff;
}
static void max2837_write(max2837_driver_t* const drv, uint8_t r, uint16_t v) {
uint16_t value = (r << 10) | (v & 0x3ff);
spi_bus_transfer(drv->bus, &value, 1);
}
static void max5864_write(max5864_driver_t* const drv, uint8_t value) {
spi_bus_transfer(drv->bus, &value, 1);
}
会发现凡是跟读写寄存器有关的代码,最终都在调用spi_bus_transfer。乍看之下调用了同一个函数,这是为什么?
后来我找到spi_bus_transfer函数的实现,在firmware/common/spi_bus.c
void spi_bus_transfer(spi_bus_t* const bus, void* const data, const size_t count) {
bus->transfer(bus, data, count);
}
可以看到这个函数最终调用的下一级transfer函数取决于传入的bus参数。
而不同的芯片传入的参数drv->bus实际上是不一样的,因为drv的类型不一样。分别是rffc5071_driver_t max2837_driver_t max5864_driver_t
因此,实际transfer函数的实现,取决于芯片驱动。
对于rffc5071,实际上这个drv是在rffc5071_setup函数调用的时候传进来的。
我搜索了一下rffc5071_setup,是在mixer.c中调用的。
我摘录了mixer.c里的相关代码:
const rffc5071_spi_config_t rffc5071_spi_config = {
.gpio_select = &gpio_rffc5072_select,
.gpio_clock = &gpio_rffc5072_clock,
.gpio_data = &gpio_rffc5072_data,
};
spi_bus_t spi_bus_rffc5071 = {
.config = &rffc5071_spi_config,
.start = rffc5071_spi_start,
.stop = rffc5071_spi_stop,
.transfer = rffc5071_spi_transfer,
.transfer_gather = rffc5071_spi_transfer_gather,
};
mixer_driver_t mixer = {
.bus = &spi_bus_rffc5071,
.gpio_reset = &gpio_rffc5072_reset,
};
void mixer_setup(mixer_driver_t* const mixer)
{
rffc5071_setup(mixer);
}
可以看到mixer变量会被作为drv参数传下去,而mixer对应的bus是spi_bus_rffc5071,这个bus对应的transfer函数是rffc5071_spi_transfer。
如果你搜索rffc5071_spi_transfer函数,会在firmware/common/rffc5071_spi.c中找到它
static void rffc5071_spi_data_out(spi_bus_t* const bus, const bool bit) {
const rffc5071_spi_config_t* const config = bus->config;
gpio_write(config->gpio_data, bit);
}
static bool rffc5071_spi_data_in(spi_bus_t* const bus) {
const rffc5071_spi_config_t* const config = bus->config;
return gpio_read(config->gpio_data);
}
static uint32_t rffc5071_spi_exchange_bit(spi_bus_t* const bus, const uint32_t bit) {
rffc5071_spi_data_out(bus, bit);
rffc5071_spi_sck(bus);
return rffc5071_spi_data_in(bus) ? 1 : 0;
}
static uint32_t rffc5071_spi_exchange_word(spi_bus_t* const bus, const uint32_t data, const size_t count) {
size_t bits = count;
const uint32_t msb = 1UL << (count - 1);
uint32_t t = data;
while (bits--) {
t = (t << 1) | rffc5071_spi_exchange_bit(bus, t & msb);
}
return t & ((1UL << count) - 1);
}
void rffc5071_spi_transfer(spi_bus_t* const bus, void* const _data, const size_t count) {
if( count != 2 ) {
return;
}
uint16_t* const data = _data;
const bool direction_read = (data[0] >> 7) & 1;
/*
* The device requires two clocks while ENX is high before a serial
* transaction. This is not clearly documented.
*/
rffc5071_spi_sck(bus);
rffc5071_spi_sck(bus);
rffc5071_spi_target_select(bus);
data[0] = rffc5071_spi_exchange_word(bus, data[0], 9);
if( direction_read ) {
rffc5071_spi_direction_in(bus);
rffc5071_spi_sck(bus);
}
data[1] = rffc5071_spi_exchange_word(bus, data[1], 16);
rffc5071_spi_serial_delay(bus);
rffc5071_spi_target_unselect(bus);
rffc5071_spi_direction_out(bus);
/*
* The device requires a clock while ENX is high after a serial
* transaction. This is not clearly documented.
*/
rffc5071_spi_sck(bus);
}
通过上面的代码可以看到它最终在调用gpio的read和write函数。
同样道理,以max2837为例,我搜索了max2837_setup函数,它在firmware/common/rf_path.c中被调用。(其实这个文件也调用了mixer_setup,再调用前面说的rffc5072_setup,只不过max2837芯片和max5864芯片中间少封装了一层)
void rf_path_init(rf_path_t* const rf_path) {
ssp1_set_mode_max5864();
max5864_setup(&max5864);
max5864_shutdown(&max5864);
ssp1_set_mode_max2837();
max2837_setup(&max2837);
max2837_start(&max2837);
mixer_setup(&mixer);
switchctrl_set(rf_path, switchctrl);
}
对于max2837_setup函数传入的参数drv实际上是&max2837。这里的max2837是在hackrf_core.h里声明的。
我们现在打开hackrf_core.c,可以看到下面这些代码
const ssp_config_t ssp_config_max2837 = {
// Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz
.data_bits = SSP_DATA_16BITS,
.serial_clock_rate = 21,
.clock_prescale_rate = 2,
.gpio_select = &gpio_max2837_select,
};
spi_bus_t spi_bus_ssp1 = {
.obj = (void*)SSP1_BASE,
.config = &ssp_config_max2837,
.start = spi_ssp_start,
.stop = spi_ssp_stop,
.transfer = spi_ssp_transfer,
.transfer_gather = spi_ssp_transfer_gather,
};
max2837_driver_t max2837 = {
.bus = &spi_bus_ssp1,
.gpio_enable = &gpio_max2837_enable,
.gpio_rx_enable = &gpio_max2837_rx_enable,
.gpio_tx_enable = &gpio_max2837_tx_enable,
.target_init = max2837_target_init,
.set_mode = max2837_target_set_mode,
};
所以对于这个芯片而言transfer函数是spi_ssp_transfer,如果我们搜索spi_ssp_transfer,它在spi_ssp.c中
static uint32_t spi_ssp_transfer_word(spi_bus_t* const bus, const uint32_t data) {
spi_ssp_wait_for_tx_fifo_not_full(bus);
SSP_DR(bus->obj) = data;
spi_ssp_wait_for_not_busy(bus);
spi_ssp_wait_for_rx_fifo_not_empty(bus);
return SSP_DR(bus->obj);
}
void spi_ssp_transfer_gather(spi_bus_t* const bus, const spi_transfer_t* const transfers, const size_t count) {
const ssp_config_t* const config = bus->config;
const bool word_size_u16 = (SSP_CR0(bus->obj) & 0xf) > SSP_DATA_8BITS;
gpio_clear(config->gpio_select);
for(size_t i=0; i<count; i++) {
const size_t data_count = transfers[i].count;
if( word_size_u16 ) {
uint16_t* const data = transfers[i].data;
for(size_t j=0; j<data_count; j++) {
data[j] = spi_ssp_transfer_word(bus, data[j]);
}
} else {
uint8_t* const data = transfers[i].data;
for(size_t j=0; j<data_count; j++) {
data[j] = spi_ssp_transfer_word(bus, data[j]);
}
}
}
gpio_set(config->gpio_select);
}
void spi_ssp_transfer(spi_bus_t* const bus, void* const data, const size_t count) {
const spi_transfer_t transfers[] = {
{ data, count },
};
spi_ssp_transfer_gather(bus, transfers, 1);
}
最终操作的是SSP_DR。说明与rffc5071确实不同。
至于max5864它与max2837是差不多的。
const ssp_config_t ssp_config_max5864 = {
// Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz
.data_bits = SSP_DATA_8BITS,
.serial_clock_rate = 21,
.clock_prescale_rate = 2,
.gpio_select = &gpio_max5864_select,
};
spi_bus_t spi_bus_ssp1 = {
.obj = (void*)SSP1_BASE,
.config = &ssp_config_max2837,
.start = spi_ssp_start,
.stop = spi_ssp_stop,
.transfer = spi_ssp_transfer,
.transfer_gather = spi_ssp_transfer_gather,
};
max5864_driver_t max5864 = {
.bus = &spi_bus_ssp1,
.target_init = max5864_target_init,
};
只是target_init不一样。还有一点很奇怪,它用的bus里的config还是在用ssp_config_max2837,而没有用ssp_config_max5864。照道理gpio_select是不一样的,不知道为啥不用切过去。
接下来就到了重点部分了w25q80bv这颗flash芯片的配置:
const ssp_config_t ssp_config_w25q80bv = {
.data_bits = SSP_DATA_8BITS,
.serial_clock_rate = 2,
.clock_prescale_rate = 2,
.gpio_select = &gpio_w25q80bv_select,
};
spi_bus_t spi_bus_ssp0 = {
.obj = (void*)SSP0_BASE,
.config = &ssp_config_w25q80bv,
.start = spi_ssp_start,
.stop = spi_ssp_stop,
.transfer = spi_ssp_transfer,
.transfer_gather = spi_ssp_transfer_gather,
};
w25q80bv_driver_t spi_flash = {
.bus = &spi_bus_ssp0,
.gpio_hold = &gpio_w25q80bv_hold,
.gpio_wp = &gpio_w25q80bv_wp,
.target_init = w25q80bv_target_init,
};
它的transfer函数是spi_ssp_transfer说明它确实使用硬件实现spi,与之前根据名字判断的结果一致。但它的总线不同是spi_bus_ssp0而不是之前那两颗max芯片的spi_bus_ssp1.
另外它的config里的serial_clock_rate区别比较大,我算了一下204MHz/2/2=52MHz,它的通信速度要比两颗max芯片高很多。
还有它的target_init函数要好好看看。
void w25q80bv_target_init(w25q80bv_driver_t* const drv) {
(void)drv;
/* Init SPIFI GPIO to Normal GPIO */
scu_pinmux(P3_3, (SCU_SSP_IO | SCU_CONF_FUNCTION2)); // P3_3 SPIFI_SCK => SSP0_SCK
scu_pinmux(P3_4, (SCU_GPIO_FAST | SCU_CONF_FUNCTION0)); // P3_4 SPIFI SPIFI_SIO3 IO3 => GPIO1[14]
scu_pinmux(P3_5, (SCU_GPIO_FAST | SCU_CONF_FUNCTION0)); // P3_5 SPIFI SPIFI_SIO2 IO2 => GPIO1[15]
scu_pinmux(P3_6, (SCU_GPIO_FAST | SCU_CONF_FUNCTION0)); // P3_6 SPIFI SPIFI_CIPO IO1 => GPIO0[6]
scu_pinmux(P3_7, (SCU_GPIO_FAST | SCU_CONF_FUNCTION4)); // P3_7 SPIFI SPIFI_COPI IO0 => GPIO5[10]
scu_pinmux(P3_8, (SCU_GPIO_FAST | SCU_CONF_FUNCTION4)); // P3_8 SPIFI SPIFI_CS => GPIO5[11]
/* configure SSP pins */
scu_pinmux(SCU_SSP0_CIPO, (SCU_SSP_IO | SCU_CONF_FUNCTION5));
scu_pinmux(SCU_SSP0_COPI, (SCU_SSP_IO | SCU_CONF_FUNCTION5));
scu_pinmux(SCU_SSP0_SCK, (SCU_SSP_IO | SCU_CONF_FUNCTION2));
/* configure GPIO pins */
scu_pinmux(SCU_FLASH_HOLD, SCU_GPIO_FAST);
scu_pinmux(SCU_FLASH_WP, SCU_GPIO_FAST);
scu_pinmux(SCU_SSP0_CS, (SCU_GPIO_FAST | SCU_CONF_FUNCTION4));
/* drive CS, HOLD, and WP pins high */
gpio_set(drv->gpio_hold);
gpio_set(drv->gpio_wp);
/* Set GPIO pins as outputs. */
gpio_output(drv->gpio_hold);
gpio_output(drv->gpio_wp);
}
它的配置分两类scu_pinmux主要都在配置下面的这些脚
hackrf_core.h
/* SPI flash */
#define SCU_SSP0_CIPO (P3_6)
#define SCU_SSP0_COPI (P3_7)
#define SCU_SSP0_SCK (P3_3)
#define SCU_SSP0_CS (P3_8) /* GPIO5[11] on P3_8 */
#define SCU_FLASH_HOLD (P3_4) /* GPIO1[14] on P3_4 */
#define SCU_FLASH_WP (P3_5) /* GPIO1[15] on P3_5 */
max2837 max5864对应是这些引脚
/* SSP1 Peripheral PinMux */
#define SCU_SSP1_CIPO (P1_3) /* P1_3 */
#define SCU_SSP1_COPI (P1_4) /* P1_4 */
#define SCU_SSP1_SCK (P1_19) /* P1_19 */
#define SCU_SSP1_CS (P1_20) /* P1_20 */
/* MAX2837 GPIO (XCVR_CTL) PinMux */
#define SCU_XCVR_ENABLE (P4_6) /* GPIO2[6] on P4_6 */
#define SCU_XCVR_RXENABLE (P4_5) /* GPIO2[5] on P4_5 */
#define SCU_XCVR_TXENABLE (P4_4) /* GPIO2[4] on P4_4 */
#define SCU_XCVR_CS (P1_20) /* GPIO0[15] on P1_20 */
/* MAX5864 SPI chip select (AD_CS) GPIO PinMux */
#define SCU_AD_CS (P5_7) /* GPIO2[7] on P5_7 */
另外target_init里还有其它gpio的设置,对应的引脚,参考下面的代码:
hackrf_core.c
/* MAX2837 GPIO (XCVR_CTL) PinMux */
static struct gpio_t gpio_max2837_select = GPIO(0, 15);
static struct gpio_t gpio_max2837_enable = GPIO(2, 6);
static struct gpio_t gpio_max2837_rx_enable = GPIO(2, 5);
static struct gpio_t gpio_max2837_tx_enable = GPIO(2, 4);
/* MAX5864 SPI chip select (AD_CS) GPIO PinMux */
static struct gpio_t gpio_max5864_select = GPIO(2, 7);
static struct gpio_t gpio_w25q80bv_hold = GPIO(1, 14);
static struct gpio_t gpio_w25q80bv_wp = GPIO(1, 15);
static struct gpio_t gpio_w25q80bv_select = GPIO(5, 11);
只要把flash芯片的这些配置搞定,其它逻辑操作都很简单。
firmware/common/pins.hpp hackrf_gpio.hpp hackrf_hal.hpp
firmware/application/portapack.cpp portapack.hpp radio.cpp CMakeLists.txt