2021-09-29 01:29 AM
Hello,
Our project uses an STM32H743VIT and we are building the project on Cube rev. 1.7.0.
Currently we use the HAL function HAL_SRAM_Write_8b to control a display on address 0xC0000000 on bank 1. (the display has an onboard controller SSD1351 setup for 8bit parallel mode) and it works perfectly fine.
To offload the CPU we would like to use DMA. Calling the function HAL_SRAM_Write_DMA revealed that hsram->hmdma was not configured so I tried to connect these together using the Configuration tool. Usually, there is a DMA settings tab in the configuration but it is not visible for the FMC. Some googling later it seems we need to use the MDMA module so I have enabled it with a software request.
I was expecting to find a way to have the configuration tool generate the code for me to link the DMA to the FMC but found no way so I changed xx_hal_msp.c to
void HAL_SRAM_MspInit(SRAM_HandleTypeDef* hsram){
/* USER CODE BEGIN SRAM_MspInit 0 */
/* USER CODE END SRAM_MspInit 0 */
HAL_FMC_MspInit();
/* USER CODE BEGIN SRAM_MspInit 1 */
__HAL_LINKDMA(hsram,hmdma,hmdma_mdma_channel40_sw_0);
/* USER CODE END SRAM_MspInit 1 */
}
When I call HAL_SRAM_Write_DMA now, it passes the first time without error but I see no data on the output pins and SRAM_DMACplt is never called, hence the second time I try to write something, hsram->State == HAL_SRAM_STATE_BUSY. I must be missing something obvious.
For reference this is a part of my code. Obviously calling a DMA function with a single byte is not efficient, but I wanted to get the basics working first.
#define LCD_Ax_PIN 18u
#define LCD_BASE_ADDR 0xC0000000
#define LCD_REG ((uint32_t *) LCD_BASE_ADDR) /* RS = 0 */
#define LCD_RAM ((uint32_t *) (LCD_BASE_ADDR | (1<<LCD_Ax_PIN))) /* RS = 1 */
extern SRAM_HandleTypeDef hsram1;
void oledData(uint8_t data){
HAL_StatusTypeDef ret;
ret = HAL_SRAM_Write_DMA(&hsram1, LCD_RAM, (uint32_t *)&data, 1);
// ret = HAL_SRAM_Write_8b(&hsram1, LCD_RAM, &data, 1);
if(ret != HAL_OK){
printf("error writing oled data\n");
}
}
void oledCommand(uint8_t data){
HAL_StatusTypeDef ret;
ret = HAL_SRAM_Write_DMA(&hsram1, LCD_REG, (uint32_t *)&data, 1);
// ret = HAL_SRAM_Write_8b(&hsram1, LCD_REG, &data, 1);
if(ret != HAL_OK){
printf("error writing oled command\n");
}
}
I have searched for reference manuals regarding MDMA in combination with the FMC but so far I have no luck and only found AN5001 which did not get me further.
Hopefully I have presented enough information to start. Any insight or tip is highly appreciated!
Bart
2021-09-29 03:58 AM
After some more digging I found that:
The MX_DMA_Init routine does not set the initialize Request type to MDMA_REQUEST_SW although I would expect that because of the Config tool setting. Maybe it's a wrong assumption. If I put that in manually, like so (inside the also previously non existing and therefore automatically deleted USER CODE block):
/**
* Enable MDMA controller clock
* Configure MDMA for global transfers
* hmdma_mdma_channel40_sw_0
*/
static void MX_MDMA_Init(void)
{
/* MDMA controller clock enable */
__HAL_RCC_MDMA_CLK_ENABLE();
/* Local variables */
/* USER CODE BEGIN MDMA_Init 0 */
hmdma_mdma_channel40_sw_0.Init.Request = MDMA_REQUEST_SW;
/* USER CODE END MDMA_Init 0 */
/* Configure MDMA channel MDMA_Channel0 */
/* Configure MDMA request hmdma_mdma_channel40_sw_0 on MDMA_Channel0 */
hmdma_mdma_channel40_sw_0.Instance = MDMA_Channel0;
hmdma_mdma_channel40_sw_0.Init.TransferTriggerMode = MDMA_BUFFER_TRANSFER;
hmdma_mdma_channel40_sw_0.Init.Priority = MDMA_PRIORITY_LOW;
hmdma_mdma_channel40_sw_0.Init.Endianness = MDMA_LITTLE_ENDIANNESS_PRESERVE;
hmdma_mdma_channel40_sw_0.Init.SourceInc = MDMA_SRC_INC_BYTE;
hmdma_mdma_channel40_sw_0.Init.DestinationInc = MDMA_DEST_INC_BYTE;
hmdma_mdma_channel40_sw_0.Init.SourceDataSize = MDMA_SRC_DATASIZE_BYTE;
hmdma_mdma_channel40_sw_0.Init.DestDataSize = MDMA_DEST_DATASIZE_BYTE;
hmdma_mdma_channel40_sw_0.Init.DataAlignment = MDMA_DATAALIGN_PACKENABLE;
hmdma_mdma_channel40_sw_0.Init.BufferTransferLength = 1;
hmdma_mdma_channel40_sw_0.Init.SourceBurst = MDMA_SOURCE_BURST_SINGLE;
hmdma_mdma_channel40_sw_0.Init.DestBurst = MDMA_DEST_BURST_SINGLE;
hmdma_mdma_channel40_sw_0.Init.SourceBlockAddressOffset = 0;
hmdma_mdma_channel40_sw_0.Init.DestBlockAddressOffset = 0;
if (HAL_MDMA_Init(&hmdma_mdma_channel40_sw_0) != HAL_OK)
{
Error_Handler();
}
/* MDMA interrupt initialization */
/* MDMA_IRQn interrupt configuration */
HAL_NVIC_SetPriority(MDMA_IRQn, 5, 0);
HAL_NVIC_EnableIRQ(MDMA_IRQn);
}
The hardware pushes out one byte. Also, after that, the TCIF bit is set. The HAL_MDMA_IRQHandler is still not triggered.
If I change the trigger mode to:
hmdma_mdma_channel40_sw_0.Init.TransferTriggerMode = MDMA_FULL_TRANSFER;
4 bytes (?) are pushed out, and the IRQ is triggered. The HAL_SRAM_DMA_XferCpltCallback is also triggered and I'm free to call for a new transfer.
Although it looks like progress it does not yet make me confident I have set everything up correctly to continue.
Hopefully it helps to identify where I'm going wrong, or point me in the right direction.
Another potential wrong assumption is that because I have set the Buffer Transfer length to 1, and increment with bytes, it's allowed to only send out one byte and expect normal behaviour. Is 4 bytes the minimum?
2021-10-03 06:39 AM
Some more progress. Function HAL_SRAM_Write_DMA has a line multiplying the amount of BufferSize by 4 before calling HAL_MDMA_Start_IT.
If I remove the 4* multiplication, my code executes and the CPU time spent on writing an image to the display is decreased by 25 times. Nice!
Can someone explain why this is multiplied, and if it is not necessary, maybe get it changed in the sourcecode from HAL? If I recompile the configuration, this change obviously get's overwritten.