24 bit value to 8 bits with pointers?

Thread Starter

Gibson486

Joined Jul 20, 2012
360
So, I am using the HAL from STM32 so I can talk SPI to a device.

I have to send a 24 bit word, but the HAL only supports 8 bit transfers.

HAL_SPI_Transmit(&hspi1, &transferValue, num_of_bytes, HAL_MAX_DELAY);

Normally, I would just mask and split the values into three variables and just transfer each new variable one byte at a time. However, there has to be a better way.

is it possible to just point to my 24 bit (or 32 bit) variable so I can have the HAL just increment my pointer?

For example:

uint32_t *32bitValue;

HAL_SPI_Transmit(&hspi1, &32bitValue, 3, HAL_MAX_DELAY);

I tried this, but now my SPI device only partially works. How would I go about just pointing to the 8 bits of the address or am I just not understanding this?
 

danadak

Joined Mar 10, 2018
4,057
HAL call must be 8 bit ptr, so just put in a loop
and inc the pointer so that it points to the next byte.
Pointer of course would be byte pointer.

Regards, Dana.
 

Thread Starter

Gibson486

Joined Jul 20, 2012
360
So what is the point of the number of bytes field if you have to make a loop yourself to do it? Seems like the HAL was trying to do it for you....but if you have to give it an 8 bit ptr, do you just make the 32 bit variable a ptr as well?
 

danadak

Joined Mar 10, 2018
4,057
I do not know what this f() is asking for, its definition.

HAL_SPI_Transmit(&hspi1, &32bitValue, 3, HAL_MAX_DELAY);

But being 8 bit is not the address of the variable it is looking for an 8 bit pointer,
so a loop is necessary ?

Regards, Dana.
 

nsaspook

Joined Aug 27, 2009
16,321
I don't know about this HAL but normally when I write a 8-bit SPI driver I use a void pointer as the IO buffer addresse(s) for byte level access in the function. You can then pass uint_8 pointer(s) to program data and use array index syntax (or a union) to address the individual bytes in the multi-byte variable.
C:
static uint8_t *tx_buff;
static uint8_t *rx_buff;
static bool upd = false;
extern rn4020_appdata_t rn4020_appdata;
static void ads_spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
const void *out, void *in, size_t len)
{
spi_speed_config(bus, 1, SPI_CLK_2MHZ); /* mode , no speed change */
SPI_CS1_0;
timer_shortdelay(75);
spi_transfer_bytes(bus, cs, cont, out, in, len);
tx_buff[0] = ADS1220_CMD_SYNC;
spi_transfer_bytes(SPI_DEV(2), 0, true, tx_buff, rx_buff, 1);
timer_shortdelay(25);
SPI_CS1_1;
}
static void ads_int_setup(void)
{
IEC0CLR = _IEC0_INT2IE_MASK; /* disable interrupt */
INTCONbits.INT2EP = 0;
IFS0CLR = _IFS0_INT2IF_MASK; /* clear flag */
IPC3bits.INT2IP = 1;
IPC3bits.INT2IS = 1;
IEC0SET = _IEC0_INT2IE_MASK; /* enable interrupt */
}
int ads1220_init(void)
{
(* allocate buffer space *) <-- thread comments
tx_buff = __pic32_alloc_coherent(32); /* uncached memory for spi transfers */
rx_buff = __pic32_alloc_coherent(32);
if (!(tx_buff && rx_buff)) {
return false;
}
/*
* setup ads1220 registers
*/
spi_speed_config(SPI_DEV(2), 1, SPI_CLK_2MHZ); /* mode , no speed change */
SPI_CS1_0;
timer_shortdelay(50);
tx_buff[0] = ADS1220_CMD_RESET;
spi_transfer_bytes(SPI_DEV(2), 0, true, tx_buff, rx_buff, 1);
timer_shortdelay(250 * US_TO_CT_TICKS);
(* load 5 data values *)
tx_buff[0] = ADS1220_CMD_WREG + 3;
tx_buff[1] = ads1220_r0;
tx_buff[2] = ads1220_r1 | ADS1220_TEMP_SENSOR;
tx_buff[3] = ads1220_r2;
tx_buff[4] = ads1220_r3;
spi_transfer_bytes(SPI_DEV(2), 0, true, tx_buff, rx_buff, 5);
tx_buff[0] = ADS1220_CMD_RREG + 3;
tx_buff[1] = 0;
tx_buff[2] = 0;
tx_buff[3] = 0;
tx_buff[4] = 0;
spi_transfer_bytes(SPI_DEV(2), 0, true, tx_buff, rx_buff, 5);
timer_shortdelay(10 * US_TO_CT_TICKS);
/*
* Check to be sure we have a device
*/
ads_int_setup();
if ((rx_buff[1] != ads1220_r0) ||
(rx_buff[2] != (ads1220_r1 | ADS1220_TEMP_SENSOR))) { /* checking for sensor flag too */
printf(
"\r\nADS1220 configuration error: %x,%x %x,%x %x %x\r\n",
ads1220_r0, rx_buff[1], ads1220_r1, rx_buff[2],
rx_buff[3], rx_buff[4]);
SPI_CS1_1;
return(-1);
}
tx_buff[0] = ADS1220_CMD_SYNC;
spi_transfer_bytes(SPI_DEV(2), 0, true, tx_buff, rx_buff, 1);
SPI_CS1_1;
return true;
}
static void ads1220_writeregister(int32_t StartAddress, int32_t NumRegs, uint32_t *pData)
{
int32_t i;
tx_buff[0] = ADS1220_CMD_WREG | (((StartAddress << 2) & 0x0c) | ((NumRegs - 1) & 0x03));
for (i = 0; i < NumRegs; i++) {
tx_buff[i + 1] = *pData++;
}
ads_spi_transfer_bytes(SPI_DEV(2), 0, true, tx_buff, rx_buff, NumRegs + 2);
return;
}
https://github.com/nsaspook/RIOT/blob/PIC32MZEF/examples/rn4020_riot/test.X/ads1220.c

C:
static inline void _spi_transfer_bytes_async(spi_t bus, spi_cs_t cs, bool cont,
const void *out, void *in, size_t len)
{
const uint8_t *out_buffer = (const uint8_t *) out;
uint8_t dma_able = 8; /* default to NO DMA to trigger default method */
(void) cs;
(void) cont;
/* set input buffer params for the non-dma isr mode */
pic_spi[bus].in = in;
pic_spi[bus].len = len;
pic_spi[bus].complete = false;
/* check of we have both buffers */
if (out && in) {
dma_able = 0;
}
/* Translate a kernel (KSEG) virtual address to a physical address. */
switch (bus + dma_able) {
case 1:
trigger_bus_dma_rx(SPI1_DMA_RX, len, KVA_TO_PA(in));
trigger_bus_dma_tx(SPI1_DMA_TX, len, KVA_TO_PA(out_buffer));
break;
case 2:
trigger_bus_dma_rx(SPI2_DMA_RX, len, KVA_TO_PA(in));
trigger_bus_dma_tx(SPI2_DMA_TX, len, KVA_TO_PA(out_buffer));
break;
default: /* non-dma mode */
while (len--) {
if (out_buffer) {
SPIxBUF(pic_spi[bus]) = *out_buffer++;
/* Wait until TX FIFO is empty */
while ((SPIxSTAT(pic_spi[bus]) & _SPI1STAT_SPITBF_MASK)) {}
}
else {
SPIxBUF(pic_spi[bus]) = 0;
/* Wait until TX FIFO is empty */
while ((SPIxSTAT(pic_spi[bus]) & _SPI1STAT_SPITBF_MASK)) {}
}
}
}
}
void spi_transfer_bytes(spi_t bus, spi_cs_t cs, bool cont,
const void *out, void *in, size_t len)
{
assert(bus != 0 && bus <= SPI_NUMOF_USED);
/* make sure at least one input or one output buffer is given */
assert(out || in);
if (cs != SPI_CS_UNDEF) {
gpio_clear((gpio_t) cs);
}
_spi_transfer_bytes_async(bus, cs, cont, out, in, len);
while (!spi_complete(bus)) {}
if (!cont && cs != SPI_CS_UNDEF) {
gpio_set((gpio_t) cs);
}
}
void spi_transfer_bytes_async(spi_t bus, spi_cs_t cs, bool cont,
const void *out, void *in, size_t len)
{
assert(bus != 0 && bus <= SPI_NUMOF_USED);
/* make sure at least one input or one output buffer is given */
assert(out || in);
if (cs != SPI_CS_UNDEF) {
gpio_clear((gpio_t) cs);
}
_spi_transfer_bytes_async(bus, cs, cont, out, in, len);
/* don't mess with cs on exit */
}
https://github.com/nsaspook/RIOT/blob/PIC32MZEF/cpu/mips_pic32_common/periph/spi.c

Union addressing code fragments.
C:
/* for 24-bit transmit and extra status data */
typedef struct mcp_adc_t {
uint32_t dummy12 : 12; // dummy space for adc data
uint32_t nullbits : 2;
uint32_t index : 3; //adc channel select
uint32_t single_diff : 1;
uint32_t start_bit : 1;
uint32_t dummy8 : 8;
uint32_t finish : 1;
uint32_t in_progress : 1;
} mcp_adc_t;
/* upper-> lower bytes to 32 bit word for ADC/DAC, etc ... */
union adc_bytes4 {
uint32_t ld;
uint8_t bd[4];
};
/* upper/lower bytes to 16 bit word for ADC/DAC, etc ... */
union adc_bytes2 {
uint16_t ld;
uint8_t bd[2];
};
/* used to hold 24-bit adc buffer, index and control bits */
union mcp_adc_buf_t {
uint32_t ld;
uint8_t bd[4];
struct mcp_adc_t map;
};
typedef struct {
union mcp_adc_buf_t mcp3208_cmd;
uint16_t potValue;
uint8_t chan;
} mcp3208_data_t;
...
static uint8_t *td;
static uint8_t *rd;
static mcp3208_data_t riot_adc;

int adc_init_internal(adc_t);
/* for RIOT-OS 8 lines, 12-bit resolution only */
int adc_sample(adc_t line, adc_res_t res)
{
assert(line <= 11);
(void) res;
switch (line) {
case 0:
PDEBUG1_ON;
ADCCON3bits.GSWTRG = 1;
while (!ADCDSTAT1bits.ARDY3) {}
riot_adc.potValue = ADCDATA3;
PDEBUG1_OFF;
break;
case 1:
ADCCON3bits.GSWTRG = 1;
while (!ADCDSTAT1bits.ARDY15) {}
riot_adc.potValue = ADCDATA15;
break;
case 2:
ADCCON3bits.GSWTRG = 1;
while (!ADCDSTAT1bits.ARDY26) {}
riot_adc.potValue = ADCDATA26;
break;
case 3:
ADCCON3bits.GSWTRG = 1;
while (!ADCDSTAT1bits.ARDY28) {}
riot_adc.potValue = ADCDATA28;
break;
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
case 11:
riot_adc.mcp3208_cmd.ld = 0; // clear the command word
riot_adc.mcp3208_cmd.map.start_bit = 1;
riot_adc.mcp3208_cmd.map.single_diff = 1;
riot_adc.mcp3208_cmd.map.index = line - ADC_CPU_CHANS;
td[0] = riot_adc.mcp3208_cmd.bd[2];
td[1] = riot_adc.mcp3208_cmd.bd[1];
td[2] = riot_adc.mcp3208_cmd.bd[0];
gpio_clear(Ja10_3); // select the ADC
spi_speed_config(SPI_DEV(2), 0, SPI_CLK_1MHZ); /* mode 0, speed */
spi_transfer_bytes(SPI_DEV(2), 0, true, td, rd, 3);
gpio_set(Ja10_3); // deselect the ADC
/* lsb array index 2 */
riot_adc.potValue = (rd[1] & 0x0f) << 8;
riot_adc.potValue += rd[2];
break;
default:
riot_adc.potValue = 0;
}
return riot_adc.potValue;
}
 
Last edited:

Picbuster

Joined Dec 2, 2013
1,058
So, I am using the HAL from STM32 so I can talk SPI to a device.

I have to send a 24 bit word, but the HAL only supports 8 bit transfers.

HAL_SPI_Transmit(&hspi1, &transferValue, num_of_bytes, HAL_MAX_DELAY);

Normally, I would just mask and split the values into three variables and just transfer each new variable one byte at a time. However, there has to be a better way.

is it possible to just point to my 24 bit (or 32 bit) variable so I can have the HAL just increment my pointer?

For example:

uint32_t *32bitValue;

HAL_SPI_Transmit(&hspi1, &32bitValue, 3, HAL_MAX_DELAY);

I tried this, but now my SPI device only partially works. How would I go about just pointing to the 8 bits of the address or am I just not understanding this?
Use union to convert from long(32) to array[4] and backwards
Wheel out the array.

Picbuster
 

xox

Joined Sep 8, 2017
936
So, I am using the HAL from STM32 so I can talk SPI to a device.

I have to send a 24 bit word, but the HAL only supports 8 bit transfers.

HAL_SPI_Transmit(&hspi1, &transferValue, num_of_bytes, HAL_MAX_DELAY);

Normally, I would just mask and split the values into three variables and just transfer each new variable one byte at a time. However, there has to be a better way.

is it possible to just point to my 24 bit (or 32 bit) variable so I can have the HAL just increment my pointer?

For example:

uint32_t *32bitValue;

HAL_SPI_Transmit(&hspi1, &32bitValue, 3, HAL_MAX_DELAY);

I tried this, but now my SPI device only partially works. How would I go about just pointing to the 8 bits of the address or am I just not understanding this?
I wouldn't recommend sending the value in binary over the wire, you would just be making a lot of assumptions about byte-order, alignment, etc.

If this function call truly is causing a bottleneck (and is it really?) then just shift the bytes into an array:

Code:
    uint32_t data = 0xABCDEF;
    unsigned char bytes[3] = { data, data >> 8, data >> 16 };
    HAL_SPI_Transmit(&hspi1, bytes, 3, HAL_MAX_DELAY);
 
Last edited:

Thread Starter

Gibson486

Joined Jul 20, 2012
360
Hate to bring this back from the dead, but I having trouble with this. I am trying to talk to a different chip that has a 16 bit word to send. When I split the word into two 8 bit variables and do two separate spi send commands, it works. but when I try to do it as an array and one spi command, it does not work. I am kind of stuck. I put both versions of the code and i comment out what is not needed when I run it.

C:
void sendAD5443SpiStream(uint16_t bitStream)
{
    uint16_t bitCount;
    uint8_t LSBbyte;
    uint8_t bytes[2];
    uint8_t MSBbyte;



    bitCount = bitStream;

    //split into array
    bytes[0] = bitCount >> 8;
    bytes[1] = bitCount;

  
    //split into two bytes and two variables
    MSBbyte = bitCount >> 8;
    LSBbyte = bitCount;

    HAL_GPIO_WritePin(AD5443_SS_GPIO_Port, AD5443_SS_Pin, GPIO_PIN_RESET);

    HAL_SPI_Transmit(&hspi1, bytes, 2, 0);    //split into array
      so we just do one spi send command
  
    //split into two bytes and two variables
    //HAL_SPI_Transmit(&hspi1, &MSBbyte, 1, 0);
    //HAL_SPI_Transmit(&hspi1, &LSBbyte, 1, 0);

    HAL_GPIO_WritePin(AD5443_SS_GPIO_Port, AD5443_SS_Pin, GPIO_PIN_SET);
}
 

nsaspook

Joined Aug 27, 2009
16,321
My clairvoyant mode is disabled again.
What exactly do you mean by 'it does not work'? Wrong data sent, no data sent, 16-bit instead of 8-bit data sent, etc ...
 

Thread Starter

Gibson486

Joined Jul 20, 2012
360
Looks like wrong data sent. It also looks like it is a bug when you try to send 2 bytes and only 2 bytes. I tried it with the interrupt based version and it worked. It generated the same waveform.
 

MrChips

Joined Oct 2, 2009
34,809
Here is the source code for HAL_SPI_Transmit( ):
C:
==============================================================================
                      ##### IO operation functions #####
 ===============================================================================
 [..]
    This subsection provides a set of functions allowing to manage the SPI
    data transfers.

    [..] The SPI supports master and slave mode :

    (#) There are two modes of transfer:
       (++) Blocking mode: The communication is performed in polling mode.
            The HAL status of all data processing is returned by the same function
            after finishing transfer.
       (++) No-Blocking mode: The communication is performed using Interrupts
            or DMA, These APIs return the HAL status.
            The end of the data processing will be indicated through the
            dedicated SPI IRQ when using Interrupt mode or the DMA IRQ when
            using DMA mode.
            The HAL_SPI_TxCpltCallback(), HAL_SPI_RxCpltCallback() and HAL_SPI_TxRxCpltCallback() user callbacks
            will be executed respectively at the end of the transmit or Receive process
            The HAL_SPI_ErrorCallback()user callback will be executed when a communication error is detected

    (#) APIs provided for these 2 transfer modes (Blocking mode or Non blocking mode using either Interrupt or DMA)
        exist for 1Line (simplex) and 2Lines (full duplex) modes.

@endverbatim
  * @{
  */

/**
  * @brief  Transmit an amount of data in blocking mode.
  * @param  hspi: pointer to a SPI_HandleTypeDef structure that contains
  *               the configuration information for SPI module.
  * @param  pData: pointer to data buffer
  * @param  Size: amount of data to be sent
  * @param  Timeout: Timeout duration
  * @retval HAL status
  */
HAL_StatusTypeDef HAL_SPI_Transmit(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size, uint32_t Timeout)
{
  uint32_t tickstart = 0U;
  HAL_StatusTypeDef errorcode = HAL_OK;


  /* Check Direction parameter */
  assert_param(IS_SPI_DIRECTION_2LINES_OR_1LINE(hspi->Init.Direction));

  /* Process Locked */
  __HAL_LOCK(hspi);

  /* Init tickstart for timeout management*/
  tickstart = HAL_GetTick();

  if (hspi->State != HAL_SPI_STATE_READY)
  {
    errorcode = HAL_BUSY;
    goto error;
  }

  if ((pData == NULL) || (Size == 0U))
  {
    errorcode = HAL_ERROR;
    goto error;
  }

  /* Set the transaction information */
  hspi->State       = HAL_SPI_STATE_BUSY_TX;
  hspi->ErrorCode   = HAL_SPI_ERROR_NONE;
  hspi->pTxBuffPtr  = (uint8_t *)pData;
  hspi->TxXferSize  = Size;
  hspi->TxXferCount = Size;

  /*Init field not used in handle to zero */
  hspi->pRxBuffPtr  = (uint8_t *)NULL;
  hspi->RxXferSize  = 0U;
  hspi->RxXferCount = 0U;
  hspi->TxISR       = NULL;
  hspi->RxISR       = NULL;

  /* Configure communication direction : 1Line */
  if (hspi->Init.Direction == SPI_DIRECTION_1LINE)
  {
    SPI_1LINE_TX(hspi);
  }

#if (USE_SPI_CRC != 0U)
  /* Reset CRC Calculation */
  if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
  {
    SPI_RESET_CRC(hspi);
  }
#endif /* USE_SPI_CRC */

  /* Check if the SPI is already enabled */
  if ((hspi->Instance->CR1 & SPI_CR1_SPE) != SPI_CR1_SPE)
  {
    /* Enable SPI peripheral */
    __HAL_SPI_ENABLE(hspi);
  }

  /* Transmit data in 16 Bit mode */
  if (hspi->Init.DataSize > SPI_DATASIZE_8BIT)
  {
    if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (hspi->TxXferCount == 0x01U))
    {
      hspi->Instance->DR = *((uint16_t *)pData);
      pData += sizeof(uint16_t);
      hspi->TxXferCount--;
    }
    /* Transmit data in 16 Bit mode */
    while (hspi->TxXferCount > 0U)
    {
      /* Wait until TXE flag is set to send data */
      if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
      {
        hspi->Instance->DR = *((uint16_t *)pData);
        pData += sizeof(uint16_t);
        hspi->TxXferCount--;
      }
      else
      {
        /* Timeout management */
        if ((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >=  Timeout)))
        {
          errorcode = HAL_TIMEOUT;
          goto error;
        }
      }
    }
  }
  /* Transmit data in 8 Bit mode */
  else
  {
    if ((hspi->Init.Mode == SPI_MODE_SLAVE) || (hspi->TxXferCount == 0x01U))
    {
      if (hspi->TxXferCount > 1U)
      {
        /* write on the data register in packing mode */
        hspi->Instance->DR = *((uint16_t *)pData);
        pData += sizeof(uint16_t);
        hspi->TxXferCount -= 2U;
      }
      else
      {
        *((__IO uint8_t *)&hspi->Instance->DR) = (*pData++);
        hspi->TxXferCount--;
      }
    }
    while (hspi->TxXferCount > 0U)
    {
      /* Wait until TXE flag is set to send data */
      if (__HAL_SPI_GET_FLAG(hspi, SPI_FLAG_TXE))
      {
        if (hspi->TxXferCount > 1U)
        {
          /* write on the data register in packing mode */
          hspi->Instance->DR = *((uint16_t *)pData);
          pData += sizeof(uint16_t);
          hspi->TxXferCount -= 2U;
        }
        else
        {
          *((__IO uint8_t *)&hspi->Instance->DR) = (*pData++);
          hspi->TxXferCount--;
        }
      }
      else
      {
        /* Timeout management */
        if ((Timeout == 0U) || ((Timeout != HAL_MAX_DELAY) && ((HAL_GetTick() - tickstart) >=  Timeout)))
        {
          errorcode = HAL_TIMEOUT;
          goto error;
        }
      }
    }
  }
#if (USE_SPI_CRC != 0U)
  /* Enable CRC Transmission */
  if (hspi->Init.CRCCalculation == SPI_CRCCALCULATION_ENABLE)
  {
    SET_BIT(hspi->Instance->CR1, SPI_CR1_CRCNEXT);
  }
#endif /* USE_SPI_CRC */

  /* Check the end of the transaction */
  if (SPI_EndRxTxTransaction(hspi, Timeout, tickstart) != HAL_OK)
  {
    hspi->ErrorCode = HAL_SPI_ERROR_FLAG;
  }

  /* Clear overrun flag in 2 Lines communication mode because received is not read */
  if (hspi->Init.Direction == SPI_DIRECTION_2LINES)
  {
    __HAL_SPI_CLEAR_OVRFLAG(hspi);
  }

  if (hspi->ErrorCode != HAL_SPI_ERROR_NONE)
  {
    errorcode = HAL_ERROR;
  }

error:
  hspi->State = HAL_SPI_STATE_READY;
  /* Process Unlocked */
  __HAL_UNLOCK(hspi);
  return errorcode;
}
 

BobaMosfet

Joined Jul 1, 2009
2,211
Use union to convert from long(32) to array[4] and backwards
Wheel out the array.

Picbuster
Not efficient. You're using an unnecessary code construct who's only purpose is to tell the compiler how to address the memory block containing the value(s). instead of adding another symbol to the symbol table, just use type-coercion to cause the compiler to address the block differently.
 

Thread Starter

Gibson486

Joined Jul 20, 2012
360
I am going by what the code says. Even if you update to 16 bit transfers, the function still only accepts an 8 bit pointer. Regardless, I am done with this. It works when I use the interrupt driven spi write, but not with the blocking version.
 
Top