I've got a SPI slave device connected to my Laird BL652 (based on the nRF52832). The slave requires the following transaction on the SPI bus:

Note that the total transaction size is 16 bits.
The SPI section of the module's dts file looks like this:
&spi0 {
compatible = "nordic,nrf-spi";
/* Cannot be used together with i2c0. */
/* status = "okay"; */
cs-gpios = <&gpio0 22 GPIO_ACTIVE_LOW>;
pinctrl-0 = <&spi0_default>;
pinctrl-1 = <&spi0_sleep>;
pinctrl-names = "default", "sleep";
};
And I've added an overlay that looks like this in order to enable the device and generate the CS signal on GPIO 18.
&spi0 {
cs-gpios = <&gpio0 18 GPIO_ACTIVE_LOW>;
status = "okay";
};
My test code to use the SPI looks like this:
const struct spi_dt_spec spi_spec = {
.bus = DEVICE_DT_GET(DT_NODELABEL(spi0))
};
if( spi_spec.bus == NULL ) {
printk("SPI not initialized.\n");
return SPI_NOT_INITIALIZED;
} else if( ! device_is_ready(spi_spec.bus) ) {
printk("SPI not ready.\n");
return SPI_NOT_READY;
}
uint8_t spi_bytes[2] = {0};
const struct spi_buf my_spi_buf[1] = {// Buffer to hold one set of SPI data
[0].buf = spi_bytes,// The byte array to send
[0].len = 2// Number of bytes in buffer
};
const struct spi_buf_set tx_buff = {
.buffers = my_spi_buf,
.count = 1
};
int spi_write_return = spi_write_dt(&spi_spec,&tx_buff);
if( spi_write_return >= 0 ) {
return MESSAGE_OK;
} else {
printk("Error number %d in SPI write.\n" , spi_write_return );
return SPI_WRITE_ERROR;
}
When I run the code, I get the following error message: <err> spi_nrfx_spi: configure: Word sizes other than 8 bits are not supported
The error number returned by spi_write_dt is -22.
Now, I'm not setting anything in the "operation" field of the spi_spec structure and I guess I need to. Here's the code from spi.h that describes all the various bit fields in the int16_t operation word.
/**
* @brief SPI controller configuration structure
*
* @param frequency is the bus frequency in Hertz
* @param operation is a bit field with the following parts:
*
* operational mode [ 0 ] - master or slave.
* mode [ 1 : 3 ] - Polarity, phase and loop mode.
* transfer [ 4 ] - LSB or MSB first.
* word_size [ 5 : 10 ] - Size of a data frame in bits.
* duplex [ 11 ] - full/half duplex.
* cs_hold [ 12 ] - Hold on the CS line if possible.
* lock_on [ 13 ] - Keep resource locked for the caller.
* cs_active_high [ 14 ] - Active high CS logic.
* format [ 15 ] - Motorola or TI frame format (optional).
* if @kconfig{CONFIG_SPI_EXTENDED_MODES} is defined:
* lines [ 16 : 17 ] - MISO lines: Single/Dual/Quad/Octal.
* reserved [ 18 : 31 ] - reserved for future use.
* @param slave is the slave number from 0 to host controller slave limit.
* @param cs is a valid pointer on a struct spi_cs_control is CS line is
* emulated through a gpio line, or NULL otherwise.
* @warning Most drivers use pointer comparison to determine whether a
* passed configuration is different from one used in a previous
* transaction. Changes to fields in the structure may not be
* detected.
*/
struct spi_config {
uint32_t frequency;
#if defined(CONFIG_SPI_EXTENDED_MODES)
uint32_t operation;
uint16_t slave;
uint16_t _unused;
#else
uint16_t operation;
uint16_t slave;
#endif /* CONFIG_SPI_EXTENDED_MODES */
const struct spi_cs_control *cs;
};
I would have hoped many of operation bits are set by default but I guess not. So I have the following questions:
- Given the SPI transaction as I showed it at the top of this question, what should be the values of the various operation bits.
- Is there a C bit-field data structure (as I created in the code fragment below) to make it easier to manage the fields?
As an experiment, I created the following code to set the bit fields in the operation word. I'm not sure I got them all correct but it's a start. I'm also setting the frequency to 1MHz.
union op_combo {
struct {
uint16_t op_mode: 1; // [0] - master or slave.
uint16_t mode: 3; // [ 1 : 3 ] - Polarity, phase and loop mode.
uint16_t transfer: 1; // [ 4 ] - LSB or MSB first.
uint16_t word_size: 6; // [ 5 : 10 ] - Size of a data frame in bits.
uint16_t duplex: 1; // [ 11 ] - full/half duplex.
uint16_t cs_hold: 1; // [ 12 ] - Hold on the CS line if possible.
uint16_t lock_on: 1; // [ 13 ] - Keep resource locked for the caller.
uint16_t cs_active_high: 1; // [ 14 ] - Active high CS logic.
uint16_t format: 1; // [ 15 ] - Motorola or TI frame format (optional).
} fields;
uint16_t word;
};
const union op_combo operation = {
.fields.op_mode = 0,
.fields.mode = 0,
.fields.transfer = 0,
.fields.word_size = 8,
.fields.duplex = 0,
.fields.cs_hold = 0,
.fields.lock_on = 0,
.fields.cs_active_high = 0,
.fields.format = 1
};
printk("op = 0x%x\n",operation.word);
const struct spi_dt_spec spi_spec = {
.bus = DEVICE_DT_GET(DT_NODELABEL(spi0)),
.config.operation = operation.word,
.config.frequency = 1000000,
.config.slave = 0
};
Now I'm getting the following runtime error: <err> spi_nrfx_spi: spi_context_wait_for_completion: Timeout waiting for transfer complete
And error code -116 returned from spi_write_dt.
Note that I'm not seeing anything coming out of the SPI bus so I'm guessing the timeout is set to 0 somewhere and the driver is timing out immediately.
From what I understand, the driver is supposed to calculate the timeout based on the transfer size and clock rate, but there appear to be some bugs associated with this, namely bugs #53957 and #53965.
- I'm using toolchain version 2.3.0. Are these bugs fixed in that version? If not, is there a patch I can apply?
Thanks,
Bret