stm: Add sdioio support for feather_stm32f405_express

Currently, only the bus specs of the stm32f405xx have been coded.
Other stm-family chips need (at a minimum) the specs added in their
periph.[ch] files.
This commit is contained in:
Jeff Epler 2020-06-26 10:13:02 -05:00
parent d30f11163c
commit 4adbd23b75
10 changed files with 463 additions and 1 deletions

View File

@ -1,5 +1,17 @@
#include "py/objtuple.h"
#include "shared-bindings/board/__init__.h" #include "shared-bindings/board/__init__.h"
const mp_rom_obj_tuple_t sdio_data_tuple = {
{&mp_type_tuple},
4,
{
MP_ROM_PTR(&pin_PC08),
MP_ROM_PTR(&pin_PC09),
MP_ROM_PTR(&pin_PC10),
MP_ROM_PTR(&pin_PC11),
}
};
STATIC const mp_rom_map_elem_t board_module_globals_table[] = { STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_PA04) }, { MP_ROM_QSTR(MP_QSTR_A0), MP_ROM_PTR(&pin_PA04) },
{ MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_PA05) }, { MP_ROM_QSTR(MP_QSTR_A1), MP_ROM_PTR(&pin_PA05) },
@ -31,5 +43,9 @@ STATIC const mp_rom_map_elem_t board_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) },
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) },
{ MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) },
{ MP_ROM_QSTR(MP_QSTR_SDIO_CLOCK), MP_ROM_PTR(&pin_PC12) },
{ MP_ROM_QSTR(MP_QSTR_SDIO_COMMAND), MP_ROM_PTR(&pin_PD02) },
{ MP_ROM_QSTR(MP_QSTR_SDIO_DATA), MP_ROM_PTR(&sdio_data_tuple) },
}; };
MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);

View File

@ -169,7 +169,7 @@ STATIC int check_pins(busio_spi_obj_t *self,
if (spi_taken) { if (spi_taken) {
mp_raise_ValueError(translate("Hardware busy, try alternative pins")); mp_raise_ValueError(translate("Hardware busy, try alternative pins"));
} else { } else {
mp_raise_ValueError(translate("Invalid SPI pin selection")); mp_raise_ValueError_varg(translate("Invalid %q pin selection"), MP_QSTR_SPI);
} }
} }

View File

@ -0,0 +1,356 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdbool.h>
#include "shared-bindings/sdioio/SDCard.h"
#include "py/mperrno.h"
#include "py/runtime.h"
#include "shared-bindings/microcontroller/__init__.h"
#include "shared-bindings/util.h"
#include "boards/board.h"
#include "supervisor/shared/translate.h"
#include "common-hal/microcontroller/Pin.h"
#ifndef DEBUG_SDIO
#define DEBUG_SDIO (0)
#endif
#if DEBUG_SDIO
#define DEBUG_PRINT(...) ((void)mp_printf(&mp_plat_print, __VA_ARGS__))
#else
#define DEBUG_PRINT(...) ((void)0)
#endif
STATIC bool reserved_sdio[MP_ARRAY_SIZE(mcu_sdio_banks)];
STATIC bool never_reset_sdio[MP_ARRAY_SIZE(mcu_sdio_banks)];
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 SDIO objects
STATIC int check_pins(sdioio_sdcard_obj_t *self,
const mcu_pin_obj_t * clock, const mcu_pin_obj_t * command,
uint8_t num_data, mcu_pin_obj_t ** data) {
bool sdio_taken = false;
const uint8_t sdio_clock_len = MP_ARRAY_SIZE(mcu_sdio_clock_list);
const uint8_t sdio_command_len = MP_ARRAY_SIZE(mcu_sdio_command_list);
const uint8_t sdio_data0_len = MP_ARRAY_SIZE(mcu_sdio_data0_list);
const uint8_t sdio_data1_len = MP_ARRAY_SIZE(mcu_sdio_data1_list);
const uint8_t sdio_data2_len = MP_ARRAY_SIZE(mcu_sdio_data2_list);
const uint8_t sdio_data3_len = MP_ARRAY_SIZE(mcu_sdio_data3_list);
// Loop over each possibility for clock. Check whether all other pins can
// be used on the same peripheral
for (uint i = 0; i < sdio_clock_len; i++) {
const mcu_periph_obj_t *mcu_sdio_clock = &mcu_sdio_clock_list[i];
if (mcu_sdio_clock->pin != clock) {
continue;
}
int periph_index = mcu_sdio_clock->periph_index;
const mcu_periph_obj_t *mcu_sdio_command = NULL;
if (!(mcu_sdio_command = find_pin_function(mcu_sdio_command_list, sdio_command_len, command, periph_index))) {
continue;
}
const mcu_periph_obj_t *mcu_sdio_data0 = NULL;
if(!(mcu_sdio_data0 = find_pin_function(mcu_sdio_data0_list, sdio_data0_len, data[0], periph_index))) {
continue;
}
const mcu_periph_obj_t *mcu_sdio_data1 = NULL;
if(num_data > 1 && !(mcu_sdio_data1 = find_pin_function(mcu_sdio_data1_list, sdio_data1_len, data[1], periph_index))) {
continue;
}
const mcu_periph_obj_t *mcu_sdio_data2 = NULL;
if(num_data > 2 && !(mcu_sdio_data2 = find_pin_function(mcu_sdio_data2_list, sdio_data2_len, data[2], periph_index))) {
continue;
}
const mcu_periph_obj_t *mcu_sdio_data3 = NULL;
if(num_data > 3 && !(mcu_sdio_data3 = find_pin_function(mcu_sdio_data3_list, sdio_data3_len, data[3], periph_index))) {
continue;
}
if (reserved_sdio[periph_index-1]) {
sdio_taken = true;
continue;
}
self->clock = mcu_sdio_clock;
self->command = mcu_sdio_command;
self->data[0] = mcu_sdio_data0;
self->data[1] = mcu_sdio_data1;
self->data[2] = mcu_sdio_data2;
self->data[3] = mcu_sdio_data3;
return periph_index;
}
if (sdio_taken) {
mp_raise_ValueError(translate("Hardware busy, try alternative pins"));
} else {
mp_raise_ValueError_varg(translate("Invalid %q pin selection"), MP_QSTR_SDIO);
}
}
void common_hal_sdioio_sdcard_construct(sdioio_sdcard_obj_t *self,
const mcu_pin_obj_t * clock, const mcu_pin_obj_t * command,
uint8_t num_data, mcu_pin_obj_t ** data, uint32_t frequency) {
int periph_index = check_pins(self, clock, command, num_data, data);
SDIO_TypeDef * SDIOx = mcu_sdio_banks[periph_index - 1];
GPIO_InitTypeDef GPIO_InitStruct = {0};
// /* GPIOC and GPIOD Periph clock enable */
// RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC | RCC_AHB1Periph_GPIOD | SD_DETECT_GPIO_CLK, ENABLE);
/* Configure data PC.08, PC.09, PC.10, PC.11 pins: D0, D1, D2, D3 pins */
for (int i=0; i<num_data; i++) {
GPIO_InitStruct.Pin = pin_mask(data[i]->number);
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Alternate = self->data[i]->altfn_index;
HAL_GPIO_Init(pin_port(data[i]->port), &GPIO_InitStruct);
}
/* Configure PD.02 CMD line */
GPIO_InitStruct.Alternate = self->command->altfn_index;
GPIO_InitStruct.Pin = pin_mask(command->number);
HAL_GPIO_Init(pin_port(command->port), &GPIO_InitStruct);
/* Configure PC.12 pin: CLK pin */
GPIO_InitStruct.Alternate = self->clock->altfn_index;
GPIO_InitStruct.Pin = pin_mask(clock->number);
HAL_GPIO_Init(pin_port(clock->port), &GPIO_InitStruct);
// XXX hard coded pin
#define SD_DETECT_PIN GPIO_PIN_12
#define SD_DETECT_GPIO_PORT GPIOB
/*!< Configure SD_SPI_DETECT_PIN pin: SD Card detect pin */
GPIO_InitStruct.Pin = SD_DETECT_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(SD_DETECT_GPIO_PORT, &GPIO_InitStruct);
__HAL_RCC_SDIO_CLK_ENABLE();
self->handle.Init.ClockDiv = SDIO_TRANSFER_CLK_DIV;
self->handle.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
self->handle.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
self->handle.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
self->handle.Init.BusWide = SDIO_BUS_WIDE_1B;
self->handle.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
self->handle.Instance = SDIOx;
HAL_StatusTypeDef r = HAL_SD_Init(&self->handle);
if (r != HAL_OK) {
mp_raise_ValueError_varg(translate("SDIO Init Error %d"), (int)r);
}
HAL_SD_CardInfoTypeDef info;
r = HAL_SD_GetCardInfo(&self->handle, &info);
if (r != HAL_OK) {
mp_raise_ValueError_varg(translate("SDIO GetCardInfo Error %d"), (int)r);
}
self->num_data = 1;
if (num_data == 4) {
if ((r = HAL_SD_ConfigWideBusOperation(&self->handle, SDIO_BUS_WIDE_4B)) == HAL_SD_ERROR_NONE) {
DEBUG_PRINT("Switched bus to 4B mode\n");
self->handle.Init.BusWide = SDIO_BUS_WIDE_4B;
self->num_data = 4;
} else {
DEBUG_PRINT("WideBus_Enable returned %r, leaving at 1B mode\n", (int)r);
}
}
self->capacity = info.BlockNbr * (info.BlockSize / 512);
self->frequency = 25000000;
reserved_sdio[periph_index - 1] = true;
return;
}
uint32_t common_hal_sdioio_sdcard_get_count(sdioio_sdcard_obj_t *self) {
return self->capacity;
}
uint32_t common_hal_sdioio_sdcard_get_frequency(sdioio_sdcard_obj_t *self) {
return self->frequency; // self->frequency;
}
uint8_t common_hal_sdioio_sdcard_get_width(sdioio_sdcard_obj_t *self) {
return self->num_data; // self->width;
}
STATIC void check_whole_block(mp_buffer_info_t *bufinfo) {
if (bufinfo->len % 512) {
mp_raise_ValueError(translate("Buffer must be a multiple of 512 bytes"));
}
}
STATIC void wait_write_complete(sdioio_sdcard_obj_t *self) {
if (self->state_programming) {
HAL_SD_CardStateTypedef st = HAL_SD_CARD_PROGRAMMING;
// This waits up to 60s for programming to complete. This seems like
// an extremely long time, but this is the timeout that micropython's
// implementation uses
for (int i=0; i < 60000 && st == HAL_SD_CARD_PROGRAMMING; i++) {
st = HAL_SD_GetCardState(&self->handle);
HAL_Delay(1);
};
self->state_programming = false;
}
}
STATIC void debug_print_state(sdioio_sdcard_obj_t *self, const char *what) {
#if DEBUG_SDIO
HAL_SD_CardStateTypedef st = HAL_SD_GetCardState(&self->handle);
DEBUG_PRINT("%s, st=0x%x State=0x%x ErrorCode=0x%x\n", what, (int)st, self->handle.State, self->handle.ErrorCode);
#endif
}
STATIC void check_for_deinit(sdioio_sdcard_obj_t *self) {
if (common_hal_sdioio_sdcard_deinited(self)) {
raise_deinited_error();
}
}
int common_hal_sdioio_sdcard_writeblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) {
check_for_deinit(self);
check_whole_block(bufinfo);
wait_write_complete(self);
self->state_programming = true;
common_hal_mcu_disable_interrupts();
HAL_StatusTypeDef r = HAL_SD_WriteBlocks(&self->handle, bufinfo->buf, start_block, bufinfo->len / 512, 1000);
common_hal_mcu_enable_interrupts();
if (r != HAL_OK) {
debug_print_state(self, "after writeblocks error");
return -EIO;
}
// debug_print_state(self, "after writeblocks OK");
// debug_print_state(self, "after writeblocks complete");
return 0;
}
int common_hal_sdioio_sdcard_readblocks(sdioio_sdcard_obj_t *self, uint32_t start_block, mp_buffer_info_t *bufinfo) {
check_for_deinit(self);
check_whole_block(bufinfo);
wait_write_complete(self);
common_hal_mcu_disable_interrupts();
HAL_StatusTypeDef r = HAL_SD_ReadBlocks(&self->handle, bufinfo->buf, start_block, bufinfo->len / 512, 1000);
common_hal_mcu_enable_interrupts();
if (r != HAL_OK) {
debug_print_state(self, "after readblocks error");
return -EIO;
}
return 0;
}
bool common_hal_sdioio_sdcard_configure(sdioio_sdcard_obj_t *self, uint32_t frequency, uint8_t bits) {
check_for_deinit(self);
return true;
}
bool common_hal_sdioio_sdcard_deinited(sdioio_sdcard_obj_t *self) {
return self->command == NULL;
}
STATIC void never_reset_mcu_periph(const mcu_periph_obj_t *periph) {
if (periph) {
never_reset_pin_number(periph->pin->port,periph->pin->number);
}
}
STATIC void reset_mcu_periph(const mcu_periph_obj_t *periph) {
if (periph) {
reset_pin_number(periph->pin->port,periph->pin->number);
}
}
void common_hal_sdioio_sdcard_deinit(sdioio_sdcard_obj_t *self) {
if (common_hal_sdioio_sdcard_deinited(self)) {
return;
}
reserved_sdio[self->command->periph_index - 1] = false;
never_reset_sdio[self->command->periph_index - 1] = false;
reset_mcu_periph(self->command);
self->command = NULL;
reset_mcu_periph(self->clock);
self->command = NULL;
for (size_t i=0; i<MP_ARRAY_SIZE(self->data); i++) {
reset_mcu_periph(self->data[i]);
self->data[i] = NULL;
}
}
void common_hal_sdioio_sdcard_never_reset(sdioio_sdcard_obj_t *self) {
if (common_hal_sdioio_sdcard_deinited(self)) {
return;
}
if (never_reset_sdio[self->command->periph_index] - 1) {
return;
}
never_reset_sdio[self->command->periph_index - 1] = true;
never_reset_mcu_periph(self->command);
never_reset_mcu_periph(self->clock);
for (size_t i=0; i<MP_ARRAY_SIZE(self->data); i++) {
never_reset_mcu_periph(self->data[i]);
}
}
void sdioio_reset() {
for (size_t i=0; i<MP_ARRAY_SIZE(reserved_sdio); i++) {
if (!never_reset_sdio[i]) {
reserved_sdio[i] = false;
}
}
}

View File

@ -0,0 +1,50 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2020 Jeff Epler for Adafruit Industries
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_STM32_COMMON_HAL_BUSIO_SDIO_H
#define MICROPY_INCLUDED_STM32_COMMON_HAL_BUSIO_SDIO_H
#include "common-hal/microcontroller/Pin.h"
#include "peripherals/periph.h"
#include "py/obj.h"
typedef struct {
mp_obj_base_t base;
SD_HandleTypeDef handle;
uint8_t num_data:3, state_programming:1;
bool has_lock;
const mcu_periph_obj_t *command;
const mcu_periph_obj_t *clock;
const mcu_periph_obj_t *data[4];
uint32_t frequency;
uint32_t capacity;
} sdioio_sdcard_obj_t;
void sdioio_reset(void);
#endif // MICROPY_INCLUDED_STM32_COMMON_HAL_BUSIO_SDIO_H

View File

View File

View File

@ -6,6 +6,7 @@ USB_SERIAL_NUMBER_LENGTH ?= 24
ifeq ($(MCU_VARIANT),STM32F405xx) ifeq ($(MCU_VARIANT),STM32F405xx)
CIRCUITPY_FRAMEBUFFERIO ?= 1 CIRCUITPY_FRAMEBUFFERIO ?= 1
CIRCUITPY_RGBMATRIX ?= 1 CIRCUITPY_RGBMATRIX ?= 1
CIRCUITPY_SDIOIO ?= 1
endif endif
ifeq ($(MCU_SERIES),F4) ifeq ($(MCU_SERIES),F4)

View File

@ -194,3 +194,25 @@ const mcu_tim_pin_obj_t mcu_tim_pin_list[TIM_PIN_ARRAY_LEN] = {
TIM(8, 3, 2, &pin_PI06), TIM(8, 3, 2, &pin_PI06),
TIM(8, 3, 3, &pin_PI07), TIM(8, 3, 3, &pin_PI07),
}; };
//SDIO
SDIO_TypeDef * mcu_sdio_banks[1] = {SDIO};
const mcu_periph_obj_t mcu_sdio_clock_list[1] = {
PERIPH(1, 12, &pin_PC12),
};
const mcu_periph_obj_t mcu_sdio_command_list[1] = {
PERIPH(1, 12, &pin_PD02),
};
const mcu_periph_obj_t mcu_sdio_data0_list[1] = {
PERIPH(1, 12, &pin_PC08),
};
const mcu_periph_obj_t mcu_sdio_data1_list[1] = {
PERIPH(1, 12, &pin_PC09),
};
const mcu_periph_obj_t mcu_sdio_data2_list[1] = {
PERIPH(1, 12, &pin_PC10),
};
const mcu_periph_obj_t mcu_sdio_data3_list[1] = {
PERIPH(1, 12, &pin_PC11),
};

View File

@ -61,4 +61,15 @@ extern const mcu_periph_obj_t mcu_uart_rx_list[UART_RX_ARRAY_LEN];
TIM_TypeDef * mcu_tim_banks[TIM_BANK_ARRAY_LEN]; TIM_TypeDef * mcu_tim_banks[TIM_BANK_ARRAY_LEN];
const mcu_tim_pin_obj_t mcu_tim_pin_list[TIM_PIN_ARRAY_LEN]; const mcu_tim_pin_obj_t mcu_tim_pin_list[TIM_PIN_ARRAY_LEN];
//SDIO
extern SDIO_TypeDef * mcu_sdio_banks[1];
extern const mcu_periph_obj_t mcu_sdio_clock_list[1];
extern const mcu_periph_obj_t mcu_sdio_command_list[1];
extern const mcu_periph_obj_t mcu_sdio_data0_list[1];
extern const mcu_periph_obj_t mcu_sdio_data1_list[1];
extern const mcu_periph_obj_t mcu_sdio_data2_list[1];
extern const mcu_periph_obj_t mcu_sdio_data3_list[1];
#endif // MICROPY_INCLUDED_STM32_PERIPHERALS_STM32F405XX_PERIPH_H #endif // MICROPY_INCLUDED_STM32_PERIPHERALS_STM32F405XX_PERIPH_H

View File

@ -43,6 +43,9 @@
#include "common-hal/pulseio/PulseIn.h" #include "common-hal/pulseio/PulseIn.h"
#include "timers.h" #include "timers.h"
#endif #endif
#if CIRCUITPY_SDIOIO
#include "common-hal/sdioio/SDCard.h"
#endif
#include "clocks.h" #include "clocks.h"
#include "gpio.h" #include "gpio.h"
@ -224,6 +227,9 @@ void reset_port(void) {
spi_reset(); spi_reset();
uart_reset(); uart_reset();
#endif #endif
#ifdef CIRCUITPY_SDIOIO
sdioio_reset();
#endif
#if CIRCUITPY_PULSEIO #if CIRCUITPY_PULSEIO
timers_reset(); timers_reset();
pwmout_reset(); pwmout_reset();