2025-06-06 12:55 PM
Hello at all
To do this, I use STM32CubeIDE and STMCubeMX to configure my I/O interfaces and serial buses, including two SPI buses to control my converters.
Only the hardware configuration of an SPI bus is limited to between 8 and 16 data buses, whereas my converters operate with between 24 and 32 bits of data...
However, in hardware SPI, my sync (CS) goes back after an 8- to 16-bit frame, whereas I need it to remain at 0 to meet the TI requirement.
So I tried to manage the sync pin in IO mode, but it still goes back...
I tried using DMA, but the CS signal delay I'm managing is too long before the start of my frame and ends long after it; I must be at about sixty microseconds, too long.
Note that I'm running my MCU at 24 MHz and I want to be able to retrieve my converted data in about 10 µS.
Currently, I'm using SPI bit bang, or I've configured my SPI port as IO, and I'm retrieving my values in 212 µS, which is too long...
So, should I try assembler?...
Do you have any advice on this? A recommendation that could guide me to the best solution.
Thank you for your invaluable help.
Have a good day everyone.
2025-06-06 2:54 PM
Use I2S, 24 or 32-bits with DMA
2025-06-06 11:20 PM
Thanks for your reply, it's not I2S but SPI... and I've already done DMA and the exchange times were very long too.
By managing the CS (Sync) independently, it dropped a few dozen μs before the exchanges and a few dozen after...
2025-06-07 1:07 AM
Than switch to I2S.
I have successfully interface dac80501 & ad5689 (both 16-bit plus 8-bit control, total 24-bits) to F446.
2025-06-07 1:56 AM
@LIFER.1 wrote:the hardware configuration of an SPI bus is limited to between 8 and 16 data buses (sic?), whereas my converters operate with between 24 and 32 bits of data....
Did you mean, "between 8 and 16 data bits" ?
So 24 bits is just 8 bits three times;
32 bits is either 16 bits twice, or 8 bits four times.
No?
2025-06-07 2:55 AM
Yes, exactly, and during this time, the CS (or sync) signal must be low and not high before the 24- or 32-bit frame passes...
Which I can't do with the MCU's hardware SPI.
2025-06-07 3:00 AM
- Consider using clock with higher frequency.
- You can use HW SPI (not bit bang) in simple polling mode and manage CS manualy in IO mode.
- If you need speed or more efficient code consider using LL API or "bare metal" approach instead HAL
- You can also use Timer to generate DMA requests to start SPI transfers and timer hardware output to generate CS signal
2025-06-07 5:59 AM
You certainly should be able to.
Please show your code.
2025-06-07 8:13 AM - edited 2025-06-07 8:19 AM
hi
In fact, in bit bang, I get this:
With this code :
void SPI_Write32_Manual(uint32_t data) {
for (int i = 31; i >= 0; i--) {
if (data & (1UL << i)) SDO_HIGH();
else SDO_LOW();
SCK_HIGH(); // Front montant
// __NOP(); __NOP(); // Très court délai (~80-100ns)
SCK_LOW(); // Front descendant
}
SCK_LOW();
}
void ADS8691_WriteRegister(uint8_t adc_num, uint8_t address, uint16_t data) {
uint32_t cmd = 0x40000000
| ((uint32_t)(address & 0x3F) << 16)
| (uint32_t)(data & 0xFFFF);
if (adc_num == 1) SYNC1_LOW();
else SYNC2_LOW();
SPI_Write32_Manual(cmd);
if (adc_num == 1) {
SYNC1_HIGH();
SCK_LOW(); // <== ajout ici
} else {
SYNC2_HIGH();
SCK_LOW(); // <== ajout ici
}
// CLK_DELAY_US(1);
}
void ADS8691_Init(void) {
// INPUT RANGE register (0x02) :
// Code 0x0003 = ±10.24V range (bipolaire, full-scale)
ADS8691_WriteRegister(1, 0x02, 0x0003); // ADC1
ADS8691_WriteRegister(2, 0x02, 0x0003); // ADC2
}
uint16_t SPI_Read16_Manual(void) {
uint16_t val = 0;
for (int i = 15; i >= 0; i--) {
SCK_HIGH();
//CLK_DELAY_US(1);
if (READ_SDI()) val |= (1 << i);
SCK_LOW();
// CLK_DELAY_US(1);
}
return val;
}
uint32_t SPI_Read24_Manual(void) {
uint32_t val = 0;
for (int i = 23; i >= 0; i--) {
SCK_HIGH();
//CLK_DELAY_US(1);
if (READ_SDI()) val |= (1 << i);
SCK_LOW();
//CLK_DELAY_US(1);
}
return val;
}
float ADS8691_ReadVoltageFast(uint8_t adc_num, float offset_correction) {
// 1. Déclenche la conversion
if (adc_num == 1) SYNC1_LOW();
else SYNC2_LOW();
SPI_Write32_Manual(0xC0000000); // NOP
if (adc_num == 1) {
SYNC1_HIGH();
SCK_LOW();
} else {
SYNC2_HIGH();
SCK_LOW();
}
// 2. Lecture du résultat précédent
if (adc_num == 1) SYNC1_LOW();
else SYNC2_LOW();
SPI_Write32_Manual(0xC0000000); // NOP
if (adc_num == 1) {
SYNC1_HIGH();
SCK_LOW();
} else {
SYNC2_HIGH();
SCK_LOW();
}
// 3. Récupération des 24 bits
if (adc_num == 1) SYNC1_LOW();
else SYNC2_LOW();
uint32_t raw24 = SPI_Read24_Manual();
if (adc_num == 1) {
SYNC1_HIGH();
SCK_LOW();
} else {
SYNC2_HIGH();
SCK_LOW();
}
// Traitement : 18 bits signés dans les 24 bits
int32_t signed_code = ((int32_t)(raw24 << 8)) >> 14;
float voltage = signed_code * (10.24f / 131072.0f); // 2^17 = 131072
return voltage + offset_correction;
//return ADS8691_CodeToVoltage_Calibrated(signed_code, offset_correction);
}
/*float ADS8691_CodeToVoltage_Calibrated(uint16_t code, float offset_correction)
{
float voltage = ((float)code - 32768.0f) * (10.24f / 32768.0f);
return voltage + offset_correction;
}*/
float ADS8691_CodeToVoltage_Calibrated(uint16_t code, float offset_correction)
{
float adc_voltage = ((float)code - 32768.0f) * (10.24f / 32768.0f);
// Ajustement linéaire calibré à partir de mesures
return (-1.1984f * adc_voltage - 8.42f)+offset_correction;
}
about 200us ...
2025-06-07 8:15 AM
void SPI_Write32_Manual(uint32_t data) {
for (int i = 31; i >= 0; i--) {
if (data & (1UL << i)) SDO_HIGH();
else SDO_LOW();
SCK_HIGH(); // Front montant
// __NOP(); __NOP(); // Très court délai (~80-100ns)
SCK_LOW(); // Front descendant
}
SCK_LOW();
}
void ADS8691_WriteRegister(uint8_t adc_num, uint8_t address, uint16_t data) {
uint32_t cmd = 0x40000000
| ((uint32_t)(address & 0x3F) << 16)
| (uint32_t)(data & 0xFFFF);
if (adc_num == 1) SYNC1_LOW();
else SYNC2_LOW();
SPI_Write32_Manual(cmd);
if (adc_num == 1) {
SYNC1_HIGH();
SCK_LOW(); // <== ajout ici
} else {
SYNC2_HIGH();
SCK_LOW(); // <== ajout ici
}
// CLK_DELAY_US(1);
}
void ADS8691_Init(void) {
// INPUT RANGE register (0x02) :
// Code 0x0003 = ±10.24V range (bipolaire, full-scale)
ADS8691_WriteRegister(1, 0x02, 0x0003); // ADC1
ADS8691_WriteRegister(2, 0x02, 0x0003); // ADC2
}
uint16_t SPI_Read16_Manual(void) {
uint16_t val = 0;
for (int i = 15; i >= 0; i--) {
SCK_HIGH();
//CLK_DELAY_US(1);
if (READ_SDI()) val |= (1 << i);
SCK_LOW();
// CLK_DELAY_US(1);
}
return val;
}
uint32_t SPI_Read24_Manual(void) {
uint32_t val = 0;
for (int i = 23; i >= 0; i--) {
SCK_HIGH();
//CLK_DELAY_US(1);
if (READ_SDI()) val |= (1 << i);
SCK_LOW();
//CLK_DELAY_US(1);
}
return val;
}
float ADS8691_ReadVoltageFast(uint8_t adc_num, float offset_correction) {
// 1. Déclenche la conversion
if (adc_num == 1) SYNC1_LOW();
else SYNC2_LOW();
SPI_Write32_Manual(0xC0000000); // NOP
if (adc_num == 1) {
SYNC1_HIGH();
SCK_LOW();
} else {
SYNC2_HIGH();
SCK_LOW();
}
// 2. Lecture du résultat précédent
if (adc_num == 1) SYNC1_LOW();
else SYNC2_LOW();
SPI_Write32_Manual(0xC0000000); // NOP
if (adc_num == 1) {
SYNC1_HIGH();
SCK_LOW();
} else {
SYNC2_HIGH();
SCK_LOW();
}
// 3. Récupération des 24 bits
if (adc_num == 1) SYNC1_LOW();
else SYNC2_LOW();
uint32_t raw24 = SPI_Read24_Manual();
if (adc_num == 1) {
SYNC1_HIGH();
SCK_LOW();
} else {
SYNC2_HIGH();
SCK_LOW();
}
// Traitement : 18 bits signés dans les 24 bits
int32_t signed_code = ((int32_t)(raw24 << 8)) >> 14;
float voltage = signed_code * (10.24f / 131072.0f); // 2^17 = 131072
return voltage + offset_correction;
//return ADS8691_CodeToVoltage_Calibrated(signed_code, offset_correction);
}
/*float ADS8691_CodeToVoltage_Calibrated(uint16_t code, float offset_correction)
{
float voltage = ((float)code - 32768.0f) * (10.24f / 32768.0f);
return voltage + offset_correction;
}*/
float ADS8691_CodeToVoltage_Calibrated(uint16_t code, float offset_correction)
{
float adc_voltage = ((float)code - 32768.0f) * (10.24f / 32768.0f);
// Ajustement linéaire calibré à partir de mesures
return (-1.1984f * adc_voltage - 8.42f)+offset_correction;
}