cancel
Showing results for 
Search instead for 
Did you mean: 

STM32F4 parallel input using HAL and DMA [SOLVED]

LOliv.2
Associate II

I have a very basic 8-bit parallel line on pins PC0-PC7 with its clock on PB6. The MCU is a 411RE, I'm using STM32CubeIDE and HAL libraries.

I've read AN4666 but the examples provided are written using an obsolete library, not HAL. But I understood that I have to use a Timer in Input Capture mode to activate the DMA transfer. To do this, in CubeMX I configured TIM4 as follows:

  • Slave Mode: Reset Mode
  • Trigger Source: TI1FP1
  • Internal Clock
  • Channel1: Input Capture direct mode

Then in the DMA Settings tab I added one line:

  • DMA Request: TIM4_CH1
  • Stream: DMA1 Stream0
  • Direction: Peripheral To Memory
  • Priority: Low
  • Mode: Normal
  • Increment Address: Memory
  • Use FIFO: yes
  • Word to Byte
  • Burst: Single

Does this configuration make any sense? My idea is to transfer the contents of register GPIOC->IDR (dimension: Word 32bit) to a uint8_t buffer (dimension Byte 8bit).

I also need an interrupt to count how many transfers were done or simply when the destination buffer is full. This is because I have to receive N 8bit frames before I can do anything with the data.

If the configuration is correct, I don't have any idea on how to use the HAL library to configure the source and destination registers and interrupts.

I have prepared a minimal setup with a button and the parallel lines connected to DIP switches.

Any help or hint is appreciated, it's a couple of days I am messing around trying to adapt stuff from examples written with the old library and from examples which use other DMA configurations, like moving data from ADC. But I the moment I did reach any kind of success.

Thanks

13 REPLIES 13

Look at Fig.1 in the RM.

Even if the two DMAs are almost identical in their working and have identical registers, their connection within the system is not the same: the Peripheral port of DMA1 has no connection into the busmatrix, but is connected directly to the APB1 bus.

Even the Memory port of DMA is not connected to all resources on the busmatrix, only to the memories.

So to access GPIO, you have to use DMA2, thus TIM1 (or TIM8 if your variant of 'F4 contains it).

JW

Nice, makes sense thanks. Would never occurred me to take a look at first figure if the manual. Now I guess that Figure 23 could also be valid to understand this fact.

I will now share my configuration for a sample demo that captures 8-bit data from GPIOC pins 0-7 when a button connected to TIM1 channel 1 in Input Capture mode (also called Compare in TIM1) is pressed. The program sends through UART the data nicely formatted. When the serial transfer is complete a UART TransferComplete interrupt enables again DMA for a new transfer.

A couple of quirks: if the timer is used just as a mean to trigger DMA or a IC interrupt, it doesn't need to have the clock enabled. Another maybe strange thing is that DMA is configured to move bytes, while the GPIOC IDR register is 32 bit, although 16bits are reserved. The motivation of this choice is that in my application I can use the first 8 contiguous pins as in input. This allows me to use a receiving buffer of bytes, saving memory.

/* USER CODE BEGIN PD */
#define LEN 10
#define TXLEN (LEN * 9) + 2
/* USER CODE END PD */
 
 
/* USER CODE BEGIN PV */
uint8_t buff[LEN];
uint8_t txBuff[TXLEN] = { };
uint8_t transferComplete = 0;
/* USER CODE END PV */
 
 
/* USER CODE BEGIN PFP */
static void TransferComplete(DMA_HandleTypeDef *DmaHandle);
static void TransferError(DMA_HandleTypeDef *DmaHandle);
static void HalfTransferComplete(DMA_HandleTypeDef *DmaHandle);
/* USER CODE END PFP */
 
 
/* USER CODE BEGIN 2 */
 
	// Attach DMA callback functions
	htim1.hdma[TIM_DMA_ID_CC1]->XferHalfCpltCallback = HalfTransferComplete;
	htim1.hdma[TIM_DMA_ID_CC1]->XferCpltCallback = TransferComplete;
	htim1.hdma[TIM_DMA_ID_CC1]->XferErrorCallback = TransferError;
 
	// Start DMA in interrupt mode, specify source and destination
	HAL_DMA_Start_IT(htim1.hdma[TIM_DMA_ID_CC1], (uint32_t) &GPIOC->IDR, (uint32_t) buff, LEN);
 
	// Enable timer to trigger DMA transfer - CC1DE bit
	__HAL_TIM_ENABLE_DMA(&htim1, TIM_DMA_CC1);
 
	// Enable timer input capture
	HAL_TIM_IC_Start(&htim1, TIM_CHANNEL_1);
 
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	while (1) {
		if (transferComplete) {
			transferComplete = 0;
 
			// Transmit buffer to UART
			// Prepare data into buffer
			txBuff[0] = '#';
			txBuff[1] = '\n';
			for (int i = 0; i < LEN; i++) {
				for (int k = 0; k < 8; k++) {
					uint32_t val = buff[i] & (1u << k);
					txBuff[(i * 9) + k + 2] = val == 0 ? '0' : '1';
				}
				txBuff[(i * 9) + 10] = '\n';
			}
 
			HAL_UART_Transmit_IT(&huart2, txBuff, TXLEN);
		}
 
    /* USER CODE END WHILE */
 
    /* USER CODE BEGIN 3 */
	}
  /* USER CODE END 3 */
 
// ################################################
// ################################################
 
/* USER CODE BEGIN 4 */
 
static void HalfTransferComplete(DMA_HandleTypeDef *DmaHandle) {
	HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
}
 
static void TransferComplete(DMA_HandleTypeDef *DmaHandle) {
	HAL_GPIO_TogglePin(LED2_GPIO_Port, LED2_Pin);
 
	transferComplete = 1;
}
 
static void TransferError(DMA_HandleTypeDef *DmaHandle) {
	HAL_GPIO_TogglePin(LED3_GPIO_Port, LED3_Pin);
}
 
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) {
	// Enable again DMA for a new transfer
	HAL_DMA_Start_IT(htim1.hdma[TIM_DMA_ID_CC1], (uint32_t) &GPIOC->IDR, (uint32_t) buff, LEN);
}
 
/* USER CODE END 4 */

0693W000005CFhxQAG.png0693W000005CFhsQAG.png

Hi LOliv.2, Thank you for sharing your configuration. I searched a lot and tried for couple of days too about same question (Parallel Input using DMA and Timer). I have some questions and I appreciate if you help:

  1. You shared a code and I think it's piece of main.c. Did you add these codes to main.c which is created by cubemx? Because I don't see Timer Init DAM Init etc.
  2. Did you share your project somewhere i.e. in GitHub? And if Not can I ask to share it?

Thank you.

Hi, sadly I've stopped working on this project long ago. The codebase cannot by shared because it was part of a company R&D. I'm sorry, all the available info is here.

The code shared above is a snipped added to autogenerated code. Follow the guard comments to understand how to use it.

To be completely honest I don't even remember what I was trying to do. I'm sorry.