Продолжаем развивать тему STM32CubeMx, и речь в этой статье пойдет о совместном использовании модулей SPI и DMA. Создадим базовый проект, в котором осуществим деятельность по передаче данных через интерфейс SPI при помощи DMA. Я буду использовать отладочную плату STM32F429Discovery в этот раз, но никаких особенностей к процессу это не добавит, для любого другого контроллера суть будет точно такой же.
Создаем проект и первым делом активируем модуль SPI, пусть будет SPI1, по классике:
Режим выбираем "Full-Duplex Master", и поскольку наша задача - создать простой, базовый проект - то остальные настройки можем оставить дефолтными.
CubeMx сразу же "забронировал" 3 вывода контроллера для сигналов SPI:
- SPI1_SCK
- SPI1_MOSI
- SPI1_MISO
Хотя настройки SPI мы и оставили в покое, тем не менее необходимо вернуться в соответствующий раздел и произвести настройку DMA на вкладке "DMA Settings". Нажав на "Add" добавляем канал DMA, для которого задаем:
- DMA Request - SPI1_TX, поскольку будем использовать его для передачи данных
Результат выглядит следующим образом:
Кликнув на добавленный канал, получаем доступ к его настройкам:
Соответственно, здесь у нас выбрано:
- режим работы - Normal
- инкрементирование адреса в периферии отключено, поскольку адрес регистра данных SPI1 всегда один и тот же
- а для памяти инкрементирование включено
- размер данных - один байт
Прерывания DMA будут включены автоматически, можно просто для надежности проверить выполнение данного пункта на вкладке "NVIC Settings". На этом с настройкой заканчиваем, генерируем проект и открываем его в выбранной IDE. Вся инициализация, как обычно, была сгенерирована автоматически, можно сразу переходить к сути дела.
Для того, чтобы отследить, когда передача данных завершена, используем соответствующую callback-функцию:
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
Переопределяем данную функцию в main.c:
/* USER CODE BEGIN 4 */ void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi) { if (hspi->Instance == SPI1) { // Передача завершена } } /* USER CODE END 4 */
HAL_SPI_TxCpltCallback()
будет вызвана автоматически по окончанию процесса передачи. Далее проверяем, что событие произошло именно для нашего модуля SPI1 и в случае необходимости добавляем некие действия, которые надо выполнить после передачи.
Итак, остается только непосредственно осуществить многократно упомянутую отправку данных. Для этого объявим массив тестовых данных (transmitBuffer[]
) и зададим его размер (BUFFER_SIZE
), передачу производим при помощи:
HAL_SPI_Transmit_DMA()
И итогом будет следующий код:
/* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define BUFFER_SIZE 32 /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ SPI_HandleTypeDef hspi1; DMA_HandleTypeDef hdma_spi1_tx; /* USER CODE BEGIN PV */ uint8_t transmitBuffer[BUFFER_SIZE]; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); static void MX_SPI1_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_SPI1_Init(); /* USER CODE BEGIN 2 */ for (uint8_t i = 0; i < BUFFER_SIZE; i++) { transmitBuffer[i] = i + 1; } /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ HAL_SPI_Transmit_DMA(&hspi1, transmitBuffer, BUFFER_SIZE); HAL_Delay(1000); } /* USER CODE END 3 */ }
Отправляем в данном случае циклически, раз в секунду. По окончанию процесса, как уже обсудили, будет вызван callback, в котором можно выставить, к примеру, флаг готовности к следующей передаче. Это уже зависит от задачи. А создание базового примера на данном этапе завершаем 👍