2025-01-03 03:36 AM - last edited on 2025-01-03 04:10 AM by SofLit
Hello,
I am trying to send a message using SPI DMA. I also enabled UART DMA. When both of them are executing, SPI DMA does not work. I implemented SEmaphore Mutex as well. Below are my code and SPI and UART drivers. Can anyone help me
---------------------------------------------------------------------main.c---------------------------------------------------------------------------
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2024 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "cmsis_os.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "GPIO.h"
#include "UART.h"
#include "SPI.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* Definitions for defaultTask */
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
void GPIOA_Init(void);
void UART_Init(void);
void UART2_Tx_DMA_Init(void * SrcAddr, uint16_t dataSize);
void SPI_Init(void);
void SPI1_Tx_DMA_Init(void * SrcAddr, uint16_t dataSize);
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
SemaphoreHandle_t xRecursiveMutex;
//void UART_Task(void *pvParameters);
void SPI_Task(void *pvParameters);
void LED_Blinking_Task(void *pvParameters);
/* 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 */
/* USER CODE BEGIN 2 */
xRecursiveMutex = xSemaphoreCreateMutex();
/*xTaskCreate(UART_Task,
"UART&DMA",
128,
NULL,
1,
NULL);
*/
xTaskCreate(SPI_Task,
"SPI&DMA",
128,
NULL,
1,
NULL);
xTaskCreate(LED_Blinking_Task,
"LED_Blinking",
128,
NULL,
1,
NULL);
vTaskStartScheduler();
/* USER CODE END 2 */
/* Init scheduler */
/* USER CODE BEGIN RTOS_MUTEX */
/* add mutexes, ... */
/* USER CODE END RTOS_MUTEX */
/* USER CODE BEGIN RTOS_SEMAPHORES */
/* add semaphores, ... */
/* USER CODE END RTOS_SEMAPHORES */
/* USER CODE BEGIN RTOS_TIMERS */
/* start timers, add new ones, ... */
/* USER CODE END RTOS_TIMERS */
/* USER CODE BEGIN RTOS_QUEUES */
/* add queues, ... */
/* USER CODE END RTOS_QUEUES */
/* Create the thread(s) */
/* creation of defaultTask */
/* USER CODE BEGIN RTOS_THREADS */
/* add threads, ... */
/* USER CODE END RTOS_THREADS */
/* USER CODE BEGIN RTOS_EVENTS */
/* add events, ... */
/* USER CODE END RTOS_EVENTS */
/* Start scheduler */
/* We should never get here as control is now taken by the scheduler */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
__HAL_RCC_PWR_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
---------------------------------------------------------------------SPI.c---------------------------------------------------------------------------
#include "stm32f4xx.h"
#include "SPI.h"
#include "UART.h"
void SPI_Init(void)
{
// Clock GPIOA was enabled in UART driver
// Enable GPIOB clock
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
// Enable SPI1 clock
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
//Enable DMA Clock
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN;
// Configure GPIOA5,6,7 and GPIOB6 as Alternate Function Mode
GPIOB->MODER &= 0x00000000;
GPIOA->MODER |= (2U << 10) | (2U << 12) | (2U << 14); // Set PA5,6,7 to AF mode
GPIOB->MODER |= (1U << 12); // Set PB6 to general output mode as Chip Select
GPIOB->BSRR = (1U << 6); // Set PB6 high (CS idle)
// Configure PA5,6,7 as very High Speed and PB6 as High Speed
GPIOA->OSPEEDR |= (3U << 10) | (3U << 12) | (3U << 14);
GPIOB->OSPEEDR |= (2U << 12);
//Set PB6 as Pull-Down
GPIOB->PUPDR |= (2U << 12);
// Configure AF5 (SPI1) for PA5,6,7
GPIOA->AFR[0] &= ~((0xF << (5 * 4)) | (0xF << (6 * 4)) | (0xF << (7 * 4))); // Clear PA5,6,7 AF bits
GPIOA->AFR[0] |= ((0x5 << (5 * 4)) | (0x5 << (6 * 4)) | (0x5 << (7 * 4))); // Set PA5,6,7 to AF5
// Configure SPI1
SPI1->CR1 = SPI_CR1_MSTR | SPI_CR1_SSM | SPI_CR1_SSI | SPI_CR1_DFF | SPI_CR1_BR_0; // Master mode, software NSS, 16-bit, baud rate
SPI1->CR2 = SPI_CR2_TXDMAEN; // Enable TX DMA
SPI1->CR1 |= SPI_CR1_SPE; // Enable SPI1
}
void SPI1_Tx_DMA_Init(void * SrcAddr, uint16_t dataSize)
{
Print_Message((void *)"Start SPI DMA config\r\n", 30);
//Disable DMA stream for configuration
DMA2_Stream3->CR &= ~DMA_SxCR_EN;
// Wait for stream to be disabled
while (DMA2_Stream3->CR & DMA_SxCR_EN);
//Write o in LIFCR
DMA2->LIFCR &= ~(0xFFFFFFFF);
//Write 1 and clear in LIFCR related to stream3
DMA2->LIFCR = DMA_LIFCR_CDMEIF3 |
DMA_LIFCR_CTEIF3 |
DMA_LIFCR_CHTIF3 |
DMA_LIFCR_CTCIF3;
//Set the peripheral port register address
DMA2_Stream3->PAR = (uint32_t)&SPI1->DR;
// Set DMA stream priority
//DMA2_Stream3->CR |= DMA_SxCR_PL_1;
//Set the memory address.
//Later store ADC values to M0AR
DMA2_Stream3->M0AR = (uint32_t)SrcAddr;
//Total number of data items to be transferred
DMA2_Stream3->NDTR = dataSize;
// Configure DMA stream for SPI1 transmission (DMA2 Stream 3, Channel 3)
DMA2_Stream3->CR = (3U << DMA_SxCR_CHSEL_Pos)| // Select Channel 3 for USART2 TX
DMA_SxCR_MINC | // Enable memory increment mode
DMA_SxCR_DIR_0 | // Set direction: memory to peripheral
DMA_SxCR_TCIE |
DMA_SxCR_CIRC; // Enable transfer complete interrupt (Circular Mode)
//Enable DMA stream
DMA2_Stream3->CR |= DMA_SxCR_EN;
Print_Message((void *)"SPI DMA Enabled...\r\n", 30);
}
---------------------------------------------------------------------UART.c-------------------------------------------------------------------------
#include "stm32f4xx.h"
#include "UART.h"
void UART_Init(void)
{
// Enable GPIOA clock
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
// Enable USART2 clock
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
//Enable DMA Clock
RCC->AHB1ENR |= RCC_AHB1ENR_DMA1EN;
// Configure PA2 (Tx) as Alternate Function Mode
GPIOA->MODER &= 0x00000000; // Clear MODER bits for PA GPIOs
GPIOA->MODER |= (2U << 4); // Set PA2 to AF mode
// Configure PA2 as High Speed
GPIOA->OSPEEDR |= (2U << 4);
// Configure AF7 (USART2) for PA2
GPIOA->AFR[0] &= ~(0xF << 8); // Clear PA2 AF bits
GPIOA->AFR[0] |= (0x7 << 8); // Set PA2 to AF7
// Configure USART2 parameters: 9600 baud, 8 data bits, 1 stop bit
USART2->BRR = 0x0683; // Baud rate 9600 (assuming 16MHz clock)
USART2->CR1 |= USART_CR1_TE; // Enable transmitter
USART2->CR1 |= USART_CR1_UE; // Enable USART
USART2->CR3 |= USART_CR3_DMAT; //Enable UART DMA
}
void UART2_Tx_DMA_Init(void * SrcAddr, uint16_t dataSize)
{
//Disable DMA stream for configuration
DMA1_Stream6->CR &= ~DMA_SxCR_EN;
// Wait for stream to be disabled
while(DMA1_Stream6->CR & DMA_SxCR_EN);
//Write o in HIFCR
DMA1->HIFCR &= ~(0xFFFFFFFF);
//Write 1 in HIFCR related to stream6
DMA1->HIFCR = DMA_HIFCR_CDMEIF6 |
DMA_HIFCR_CTEIF6 |
DMA_HIFCR_CHTIF6 |
DMA_HIFCR_CTCIF6;
//Set the peripheral port register address
DMA1_Stream6->PAR = (uint32_t)&USART2->DR;
//Set the memory address.
//Later store ADC values to M0AR
DMA1_Stream6->M0AR = (uint32_t)SrcAddr;
//Total number of data items to be transferred
DMA1_Stream6->NDTR = dataSize;
// Configure DMA stream for USART2 transmission (DMA1 Stream 6, Channel 4)
DMA1_Stream6->CR = (4U << DMA_SxCR_CHSEL_Pos)| // Select Channel 4 for USART2 TX
DMA_SxCR_MINC | // Enable memory increment mode
DMA_SxCR_DIR_0 | // Set direction: memory to peripheral
DMA_SxCR_TCIE |
DMA_SxCR_CIRC; // Enable transfer complete interrupt (Circular Mode)
//Enable DMA stream
DMA1_Stream6->CR |= DMA_SxCR_EN;
}
void Send_Data(char data)
{
while (!(USART2->SR & USART_SR_TXE)); // Wait until TXE is set
USART2->DR = data; // Transmit data
while (!(USART2->SR & USART_SR_TC)); // Wait until TC is set (transmission complete)
}
void Print_Message(void * SrcAddr, uint16_t dataSize)
{
// Failure message
const char *Failure_Msg = "Memory allocation failed\r\n";
// Define structure for data transmission
typedef struct
{
int Data_Size;
char Data[100];
} Data_Transmission;
// Allocate memory for the data structure
Data_Transmission *First_Data = malloc(sizeof(Data_Transmission));
if (First_Data == NULL)
{
// If memory allocation fails, send failure message
Split_Bits(Failure_Msg);
return;
}
// Initialize and prepare data for transmission
strcpy(First_Data->Data, SrcAddr);
//First_Data->Data_Size = strlen(First_Data->Data);
First_Data->Data_Size = dataSize;
// Send data via UART
Split_Bits(First_Data->Data);
// Free allocated memory
free(First_Data);
}
void Split_Bits(const char *ptr)
{
// Iterate through the string and send each character
while (*ptr != '\0')
{
Send_Data(*ptr++);
}
}
2025-01-03 04:09 AM
Hello @HosseinBaghaei and welcome to the community,
1- In next time please use </> button to share your code. See this guideline. I'm editing your code then.
2- "SPI DMA does not work" does not help. What you did as debug at this stage? you need to describe more the issue. See the tips on posting.
3- What is the relation with the RTOS. Does it work in without RTOS and doesn't work with it?
4- Why are not using HAL to avoid any issue in bit masking and bit shifts?
2025-01-03 04:57 AM - edited 2025-01-03 04:58 AM
Thank you for your message.
I did debugging. UART DMA driver and SPI DMA driver work individually without FreeRTOS. Moreover, they work individually with Mutex.
I already used HAL. I am going to learn working with registers for more insight.
2025-01-03 05:05 AM
To work with registers for study purposes, remove FREERTOS as it's not necessary for this study and (based on your comment) it worked without RTOS.
So need to separate the subjects here?:
1- You are facing an issue with UART config with baremetal (direct access to the registers) without RTOS.
2- Or you get the baremetal working without RTOS but you have an issue with RTOS. In this case the thread will be moved to another forum board.
2025-01-03 08:26 PM
My thoughts:
I cannot see in posted code where and how the xRecursiveMutex is really used (just defined and initialized, never released!).
Why a RecursiveMutex (a regular simple Semaphore should be enough)?
(based on my "understanding": a mutex is used for "critical sections", to avoid that two thread would use the same code in "parallel"; a semaphore is used to send a signal e.g. from an INT to a thread)
It sounds to me more like: how to use RTOS, Semaphores and Interrupts (e.g. to release a semaphore)?
(not a FW or MCU issue, more related to RTOS)
Usually (as I use) it works this way:
You could use debugger:
If not (and potentially): the "semaphore communication" fails, e.g. not the right "...release_from_interrupt" function called in INT Handler for the semaphore.
Or: you have an issue with RTOS thread priorities so that the "waiting thread" will not get the Prio and "event" (you might be able to use also "events" instead of semaphores. There is also an option to raise the Prio...
At the end: an issue with writing code when using an RTOS (and how to sync between INTs and user code (threads).
Good luck with debugging...