AP_HAL_ChibiOS: add support for saving and restoring SCK pin state

when SPI goes into undefined state during reset
This commit is contained in:
bugobliterator 2023-10-13 15:18:35 +11:00 committed by Andrew Tridgell
parent 6a97817fa6
commit 829fcf5f33
3 changed files with 71 additions and 22 deletions

View File

@ -69,6 +69,7 @@ static const struct SPIDriverInfo {
uint8_t busid; // used for device IDs in parameters uint8_t busid; // used for device IDs in parameters
uint8_t dma_channel_rx; uint8_t dma_channel_rx;
uint8_t dma_channel_tx; uint8_t dma_channel_tx;
ioline_t sck_line;
} spi_devices[] = { HAL_SPI_BUS_LIST }; } spi_devices[] = { HAL_SPI_BUS_LIST };
// device list comes from hwdef.dat // device list comes from hwdef.dat
@ -86,6 +87,8 @@ SPIBus::SPIBus(uint8_t _bus) :
FUNCTOR_BIND_MEMBER(&SPIBus::dma_allocate, void, Shared_DMA *), FUNCTOR_BIND_MEMBER(&SPIBus::dma_allocate, void, Shared_DMA *),
FUNCTOR_BIND_MEMBER(&SPIBus::dma_deallocate, void, Shared_DMA *)); FUNCTOR_BIND_MEMBER(&SPIBus::dma_deallocate, void, Shared_DMA *));
// remember the SCK line for stop_peripheral()/start_peripheral()
sck_mode = palReadLineMode(spi_devices[bus].sck_line);
} }
/* /*
@ -103,10 +106,7 @@ void SPIBus::dma_deallocate(Shared_DMA *ctx)
{ {
chMtxLock(&dma_lock); chMtxLock(&dma_lock);
// another non-SPI peripheral wants one of our DMA channels // another non-SPI peripheral wants one of our DMA channels
if (spi_started) { stop_peripheral();
spiStop(spi_devices[bus].driver);
spi_started = false;
}
chMtxUnlock(&dma_lock); chMtxUnlock(&dma_lock);
} }
@ -339,6 +339,46 @@ bool SPIDevice::adjust_periodic_callback(AP_HAL::Device::PeriodicHandle h, uint3
return bus.adjust_timer(h, period_usec); return bus.adjust_timer(h, period_usec);
} }
/*
stop the SPI peripheral and set the SCK line as a GPIO to prevent the clock
line floating while we are waiting for the next spiStart()
*/
void SPIBus::stop_peripheral(void)
{
if (!spi_started) {
return;
}
const auto &sbus = spi_devices[bus];
if (spi_mode == SPIDEV_MODE0 || spi_mode == SPIDEV_MODE1) {
// Clock polarity is 0, so we need to set the clock line low before spi reset
palClearLine(sbus.sck_line);
} else {
// Clock polarity is 1, so we need to set the clock line high before spi reset
palSetLine(sbus.sck_line);
}
palSetLineMode(sbus.sck_line, PAL_MODE_OUTPUT_PUSHPULL);
spiStop(sbus.driver);
spi_started = false;
}
/*
start the SPI peripheral and restore the IO mode of the SCK line
*/
void SPIBus::start_peripheral(void)
{
if (spi_started) {
return;
}
/* start driver and setup transfer parameters */
spiStart(spi_devices[bus].driver, &spicfg);
// restore sck pin mode from stop_peripheral()
palSetLineMode(spi_devices[bus].sck_line, sck_mode);
spi_started = true;
}
/* /*
used to acquire bus and (optionally) assert cs used to acquire bus and (optionally) assert cs
*/ */
@ -380,12 +420,9 @@ bool SPIDevice::acquire_bus(bool set, bool skip_cs)
bus.spicfg.cr1 = (uint16_t)(freq_flag | device_desc.mode); bus.spicfg.cr1 = (uint16_t)(freq_flag | device_desc.mode);
bus.spicfg.cr2 = 0; bus.spicfg.cr2 = 0;
#endif #endif
if (bus.spi_started) { bus.spi_mode = device_desc.mode;
spiStop(spi_devices[device_desc.bus].driver); bus.stop_peripheral();
bus.spi_started = false; bus.start_peripheral();
}
spiStart(spi_devices[device_desc.bus].driver, &bus.spicfg); /* Setup transfer parameters. */
bus.spi_started = true;
if(!skip_cs) { if(!skip_cs) {
spiSelectI(spi_devices[device_desc.bus].driver); /* Slave Select assertion. */ spiSelectI(spi_devices[device_desc.bus].driver); /* Slave Select assertion. */
} }

View File

@ -35,7 +35,6 @@ public:
SPIConfig spicfg; SPIConfig spicfg;
void dma_allocate(Shared_DMA *ctx); void dma_allocate(Shared_DMA *ctx);
void dma_deallocate(Shared_DMA *ctx); void dma_deallocate(Shared_DMA *ctx);
bool spi_started;
uint8_t slowdown; uint8_t slowdown;
// we need an additional lock in the dma_allocate and // we need an additional lock in the dma_allocate and
@ -43,6 +42,19 @@ public:
// have two DMA channels that we are handling with the shared_dma // have two DMA channels that we are handling with the shared_dma
// code // code
mutex_t dma_lock; mutex_t dma_lock;
// store the last spi mode for stop_peripheral()
uint32_t spi_mode;
// start and stop the hardware peripheral
void start_peripheral(void);
void stop_peripheral(void);
private:
bool spi_started;
// mode line for SCK pin
iomode_t sck_mode;
}; };
struct SPIDesc { struct SPIDesc {

View File

@ -1403,26 +1403,26 @@ def write_SPI_table(f):
f.write("\n") f.write("\n")
def write_SPI_config(f): def write_SPI_config(self, f):
'''write SPI config defines''' '''write SPI config defines'''
global spi_list for t in list(self.bytype.keys()) + list(self.alttype.keys()):
for t in list(bytype.keys()) + list(alttype.keys()):
if t.startswith('SPI'): if t.startswith('SPI'):
spi_list.append(t) self.spi_list.append(t)
spi_list = sorted(spi_list) self.spi_list = sorted(self.spi_list)
if len(spi_list) == 0: if len(self.spi_list) == 0:
f.write('#define HAL_USE_SPI FALSE\n') f.write('#define HAL_USE_SPI FALSE\n')
return return
devlist = [] devlist = []
for dev in spi_list: for dev in self.spi_list:
n = int(dev[3:]) n = int(dev[3:])
devlist.append('HAL_SPI%u_CONFIG' % n) devlist.append('HAL_SPI%u_CONFIG' % n)
sck_pin = self.bylabel['SPI%s_SCK' % n]
sck_line = 'PAL_LINE(GPIO%s,%uU)' % (sck_pin.port, sck_pin.pin)
f.write( f.write(
'#define HAL_SPI%u_CONFIG { &SPID%u, %u, STM32_SPI_SPI%u_DMA_STREAMS }\n' '#define HAL_SPI%u_CONFIG { &SPID%u, %u, STM32_SPI_SPI%u_DMA_STREAMS, %s }\n'
% (n, n, n, n)) % (n, n, n, n, sck_line))
f.write('#define HAL_SPI_BUS_LIST %s\n\n' % ','.join(devlist)) f.write('#define HAL_SPI_BUS_LIST %s\n\n' % ','.join(devlist))
write_SPI_table(f) self.write_SPI_table(f)
def write_QSPI_table(f): def write_QSPI_table(f):
'''write SPI device table''' '''write SPI device table'''