stm: SPI: revamp pin search code

I discussed with Hierophect on Discord about how to "de-nest" the code
for configuring SPI objects on STM, because the problems with one
nesting level per pin becomes unmanageable with the up to 10 pins of
SDIO.

This code (which is only compile-tested so far) demonstrates the concept
we discussed.

The SCK pin is always required.  Loop over all possibilities of the SCK
pin.  When we are considering a particular item in the mcu_spi_sck_list
we have now become committed to using a particular periph_index.  If all
the other pins can be satisfied by that periph_index, then we have a
working combination.  Once we have a working combination that is not
reserved, we can return that combination.  On reaching the end, we have
checked all the possible possibilities and can give the same errors as
before: One if there was a possibility that worked but was reserved;
and another if no possibility worked.
This commit is contained in:
Jeff Epler 2020-06-24 11:28:28 -05:00 committed by Jeff Epler
parent 9b4b655b03
commit 57ab4f1329
1 changed files with 61 additions and 87 deletions

View File

@ -107,105 +107,79 @@ void spi_reset(void) {
spi_clock_disable(ALL_CLOCKS & ~(never_reset_mask));
}
void common_hal_busio_spi_construct(busio_spi_obj_t *self,
STATIC const mcu_periph_obj_t *find_pin_function(const mcu_periph_obj_t *table, size_t sz, const mcu_pin_obj_t *pin, int periph_index) {
for(size_t i = 0; i<sz; i++, table++) {
if(periph_index == table->periph_index && pin == table->pin ) {
return table;
}
}
return NULL;
}
//match pins to SPI objects
STATIC int check_pins(busio_spi_obj_t *self,
const mcu_pin_obj_t * sck, const mcu_pin_obj_t * mosi,
const mcu_pin_obj_t * miso) {
//match pins to SPI objects
SPI_TypeDef * SPIx;
bool spi_taken = false;
uint8_t sck_len = MP_ARRAY_SIZE(mcu_spi_sck_list);
uint8_t mosi_len = MP_ARRAY_SIZE(mcu_spi_mosi_list);
uint8_t miso_len = MP_ARRAY_SIZE(mcu_spi_miso_list);
bool spi_taken = false;
//SCK is not optional. MOSI and MISO are
for (uint i = 0; i < sck_len; i++) {
if (mcu_spi_sck_list[i].pin == sck) {
//if both MOSI and MISO exist, loop search normally
if ((mosi != NULL) && (miso != NULL)) {
//MOSI
for (uint j = 0; j < mosi_len; j++) {
if (mcu_spi_mosi_list[j].pin == mosi) {
//MISO
for (uint k = 0; k < miso_len; k++) {
if ((mcu_spi_miso_list[k].pin == miso) //everything needs the same index
&& (mcu_spi_sck_list[i].periph_index == mcu_spi_mosi_list[j].periph_index)
&& (mcu_spi_sck_list[i].periph_index == mcu_spi_miso_list[k].periph_index)) {
//keep looking if the SPI is taken, edge case
if (reserved_spi[mcu_spi_sck_list[i].periph_index - 1]) {
spi_taken = true;
continue;
}
//store pins if not
self->sck = &mcu_spi_sck_list[i];
self->mosi = &mcu_spi_mosi_list[j];
self->miso = &mcu_spi_miso_list[k];
break;
}
}
if (self->sck != NULL) {
break; // Multi-level break to pick lowest peripheral
}
}
}
if (self->sck != NULL) {
break;
}
// if just MISO, reduce search
} else if (miso != NULL) {
for (uint j = 0; j < miso_len; j++) {
if ((mcu_spi_miso_list[j].pin == miso) //only SCK and MISO need the same index
&& (mcu_spi_sck_list[i].periph_index == mcu_spi_miso_list[j].periph_index)) {
if (reserved_spi[mcu_spi_sck_list[i].periph_index - 1]) {
spi_taken = true;
continue;
}
self->sck = &mcu_spi_sck_list[i];
self->mosi = NULL;
self->miso = &mcu_spi_miso_list[j];
break;
}
}
if (self->sck != NULL) {
break;
}
// if just MOSI, reduce search
} else if (mosi != NULL) {
for (uint j = 0; j < mosi_len; j++) {
if ((mcu_spi_mosi_list[j].pin == mosi) //only SCK and MOSI need the same index
&& (mcu_spi_sck_list[i].periph_index == mcu_spi_mosi_list[j].periph_index)) {
if (reserved_spi[mcu_spi_sck_list[i].periph_index - 1]) {
spi_taken = true;
continue;
}
self->sck = &mcu_spi_sck_list[i];
self->mosi = &mcu_spi_mosi_list[j];
self->miso = NULL;
break;
}
}
if (self->sck != NULL) {
break;
}
} else {
//throw an error immediately
mp_raise_ValueError(translate("Must provide MISO or MOSI pin"));
}
}
if (!sck) {
mp_raise_ValueError(translate("Must provide SCK pin"));
}
//handle typedef selection, errors
if (self->sck != NULL && (self->mosi != NULL || self->miso != NULL)) {
SPIx = mcu_spi_banks[self->sck->periph_index - 1];
} else {
if (spi_taken) {
mp_raise_ValueError(translate("Hardware busy, try alternative pins"));
} else {
mp_raise_ValueError(translate("Invalid SPI pin selection"));
}
if (!miso && !mosi) {
mp_raise_ValueError(translate("Must provide MISO or MOSI pin"));
}
// Loop over each possibility for SCK. Check whether MISO and/or MOSI can be used on the same peripheral
for (uint i = 0; i < sck_len; i++) {
const mcu_periph_obj_t *mcu_spi_sck = &mcu_spi_sck_list[i];
if (mcu_spi_sck->pin != sck) {
continue;
}
int periph_index = mcu_spi_sck->periph_index;
const mcu_periph_obj_t *mcu_spi_miso = NULL;
if (miso && !(mcu_spi_miso = find_pin_function(mcu_spi_miso_list, miso_len, miso, periph_index))) {
continue;
}
const mcu_periph_obj_t *mcu_spi_mosi = NULL;
if (mosi && !(mcu_spi_mosi = find_pin_function(mcu_spi_mosi_list, mosi_len, mosi, periph_index))) {
continue;
}
if (reserved_spi[periph_index-1]) {
spi_taken = true;
continue;
}
self->sck = mcu_spi_sck;
self->mosi = mcu_spi_mosi;
self->miso = mcu_spi_miso;
return periph_index;
}
if (spi_taken) {
mp_raise_ValueError(translate("Hardware busy, try alternative pins"));
} else {
mp_raise_ValueError(translate("Invalid SPI pin selection"));
}
}
void common_hal_busio_spi_construct(busio_spi_obj_t *self,
const mcu_pin_obj_t * sck, const mcu_pin_obj_t * mosi,
const mcu_pin_obj_t * miso) {
int periph_index = check_pins(self, sck, mosi, miso);
SPI_TypeDef * SPIx = mcu_spi_banks[periph_index - 1];
//Start GPIO for each pin
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = pin_mask(sck->number);