The advantage I have here is that, with a bit of connivance, the initialization for each driver family is the same, or nearly so.
for I2C:
uint32_t create (
char which = 0,
int IN_QUEUE_SIZE = 0,
int OUT_QUEUE_SIZE = 0,
I2C_TRANSFER_MODE IN_MODE = I2C_BLOCKING,
I2C_TRANSFER_MODE OUT_MODE = I2C_BLOCKING,
int Timeout = 100);
for SPI
uint32_t create (
char which = 0, // interface
char* label = nullptr, // ID label
int IN_QUEUE_SIZE = 0, // if used
int OUT_QUEUE_SIZE = 0, // if used
SPI_TRANSFER_MODE IN_MODE = SPI_BLOCKING, // input mode
SPI_TRANSFER_MODE OUT_MODE = SPI_BLOCKING, // output mode
int Timeout = 100, // timeout
uint16_t CS_PIN = 0, // CS
GPIO_TypeDef* CS_PORT = nullptr,
uint16_t A0_PIN = 0, // A0 (DISPLAYS)
GPIO_TypeDef* A0_PORT = nullptr,
uint16_t RESET_PIN = 0, // RESET (DISPLAYS)
GPIO_TypeDef* RESET_PORT = nullptr,
uint16_t RW_PIN = 0, // RARE DISPLAY
GPIO_TypeDef* RW_PORT = nullptr,
uint16_t READ_PIN = 0, // RARE DISPLAY
GPIO_TypeDef* READ_PORT = nullptr,
uint16_t WRITE_PIN = 0, // RARE DISPLAY
GPIO_TypeDef* WRITE_PORT = nullptr
and for USARTS
uint32_t create (
char which = 0, // interface
char* label = nullptr, // ID label
int IN_QUEUE_SIZE = 0, // if used
int OUT_QUEUE_SIZE = 0, // if used
USART_TRANSFER_MODE IN_MODE = USART_BLOCKING, // input mode
USART_TRANSFER_MODE OUT_MODE = USART_BLOCKING, // output mode
int Timeout = 100 // timeout
);
Which are, for the most part, the same. The label is used to identify semaphores and queues, and can be added to the I2C should I wish.
Other differences have to do with the manner in which each driver is used, the architecture of the hardware, and the usefulness of various modes. Queues don’t do well on I2C and SPI, but do very well on USARTS. Interrupt modes (to a user provided buffer) make sense in I2C and SPI, but not in USARTS. Queues may be added to I2C (generally receive only) if I implement packets over I2C, but that’s only processor to processor.
In this I2C example, you can see the locations needed to support a different processor family:
uint32_t HAL_I2C::Send(uint16_t DevAddress, uint8_t* buffer, uint32_t Size, enum I2C_TRANSFER_MODE Override)
{
enum I2C_TRANSFER_MODE NEW_mode = OUT_mode;
// ********************************** check input parameters***********************************
// need to check overrides
switch (Override)
{
case I2C_IRQ: // TRANSMIT IS IRQ DRIVEN
case I2C_DMA: // TRANSMIT IS DMA FROM BUFFER, RECEIVE IS IRQ QUEUE (MAY CHANGE)
case I2C_BLOCKING: // TRANSMIT AND RECEIVE ARE BYTE BLOCKING
{
NEW_mode = Override;
break;
}
default:
{
NEW_mode = OUT_mode;
// leave mode as is, cannot override
}
}
// must shift left before calling master transmit
DevAddress <<= 1; // address shifted
switch (NEW_mode)
{
case I2C_BLOCKING: // TRANSMIT AND RECEIVE ARE BYTE BLOCKING
{
time_start = htim2.Instance->CNT;
// always semaphore protected
xSemaphoreTake(semaphore, portMAX_DELAY);
// direct call to STMICRO driver, blocking mode
if (DevAddress == 0)
{
result = HAL_I2C_Slave_Transmit(hi2c, (uint8_t*) buffer, Size, timeout);
}
else
{
result = HAL_I2C_Master_Transmit(hi2c,DevAddress, (uint8_t*) buffer,Size, timeout);
}
// error reporting
ERROR_CODE = hi2c->ErrorCode;
ERROR_REASON = (enum COMM_ERROR_TYPE) ERROR_CODE;
operations++;
// record errors if needed
switch (result)
{
case HAL_ERROR: //= 0x01U,
{
fail++;
break;
}
case HAL_BUSY: //= 0x02U,
{
fail++;
break;
}
case HAL_TIMEOUT: //= 0x03U
{
fail++;
break;
}
case HAL_OK:
{
// was OK
succeed++;
break;
}
}
// return semaphore for next operation
xSemaphoreGive(semaphore);
time_stop = htim2.Instance->CNT;
delta_time = time_stop - time_start;
break;
}
case I2C_QUEUE: // TRANSMIT QUEUE SENDS
{
uint16_t buf;
// always semaphore protected
xSemaphoreTake(semaphore, portMAX_DELAY);
for (uint32_t i = 0; i < Size; i++)
{
// build address = MSB, data = LSB
buf = (*buffer & 0xFF) + ((DevAddress & 0xFF) << 8);
// send data
xQueueSend(send_queue, &buf, portMAX_DELAY);
buffer++;
}
operations++;
// return semaphore for next operation
xSemaphoreGive(semaphore);
break;
}
case I2C_IRQ: //
{
time_start = htim2.Instance->CNT;
xSemaphoreTake(I2C_done[interface], portMAX_DELAY);
// always semaphore protected
xSemaphoreTake(semaphore, portMAX_DELAY);
// direct call to STMICRO driver, blocking mode
if (DevAddress == 0)
{
result = HAL_I2C_Slave_Transmit_IT(hi2c, (uint8_t*) buffer, Size);
}
else
{
result = HAL_I2C_Master_Transmit_IT(hi2c,DevAddress, (uint8_t*) buffer,Size);
}
// error reporting
ERROR_CODE = hi2c->ErrorCode;
ERROR_REASON = (enum COMM_ERROR_TYPE) ERROR_CODE;
xSemaphoreTake(I2C_done[interface], portMAX_DELAY);
operations++;
hal_state = HAL_I2C_GetState(hi2c);
switch (hal_state)
{
//// case HAL_I2C_STATE_TIMEOUT:
//// {
//// break;
//// }
case HAL_I2C_STATE_BUSY_TX:
case HAL_I2C_STATE_BUSY_RX:
case HAL_I2C_STATE_BUSY:
{
fail++;
break;
}
case HAL_I2C_STATE_READY:
{
// was OK
succeed++;
break;
}
default:
{
// do nothing
}
}
// return semaphore for next operation
xSemaphoreGive(semaphore);
xSemaphoreGive(I2C_done[interface]);
time_stop = htim2.Instance->CNT;
delta_time = time_stop - time_start;
break;
}
case I2C_DMA: // TRANSMIT IS DMA FROM BUFFER, RECEIVE IS IRQ QUEUE (MAY CHANGE)
{
time_start = htim2.Instance->CNT;
xSemaphoreTake(I2C_done[interface], portMAX_DELAY); // if can take, then no active DMA
// always semaphore protected
xSemaphoreTake(semaphore, portMAX_DELAY);
// direct call to STMICRO driver, blocking mode
if (DevAddress == 0)
{
result = HAL_I2C_Slave_Transmit_DMA(hi2c, (uint8_t*) buffer, Size);
}
else
{
result = HAL_I2C_Master_Transmit_DMA(hi2c,DevAddress, (uint8_t*) buffer,Size);
}
// error reporting
ERROR_CODE = hi2c->ErrorCode;
ERROR_REASON = (enum COMM_ERROR_TYPE) ERROR_CODE;
xSemaphoreTake(I2C_done[interface], portMAX_DELAY);
operations++;
hal_state = HAL_I2C_GetState(hi2c);
switch (hal_state)
{
//// case HAL_I2C_STATE_TIMEOUT:
//// {
//// break;
//// }
case HAL_I2C_STATE_BUSY_TX:
case HAL_I2C_STATE_BUSY_RX:
case HAL_I2C_STATE_BUSY:
{
fail++;
break;
}
case HAL_I2C_STATE_READY:
{
// was OK
succeed++;
break;
}
default:
{
// do nothing
}
}
// return semaphore for next operation
xSemaphoreGive(semaphore);
xSemaphoreGive(I2C_done[interface]);
time_stop = htim2.Instance->CNT;
delta_time = time_stop - time_start;
break;
}
default:
{
// do nothing
}
} // end out mode switch
return HAL_OK;
}
The structure is, to me, the more important feature.