Portapack应用开发教程 (十六)Debug程序 H hackrf固件比较

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

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 设计师:老邵的开源世界 返回首页