cancel
Showing results for 
Search instead for 
Did you mean: 

fm0 demodulation using stm32f401re

MShah.8
Associate II

I am attempting to demodulate fm0 modulated signal which is received via gpio on stm32f401re.

MShah8_0-1690977825318.png

What should be the approach to this demodulation? please enumerate the steps i can follow. (ps. ~ the symbol period of fm0 modulated data is 4us)

Some insights to this problem would be much appreciated...:smiling_face_with_smiling_eyes:

1 ACCEPTED SOLUTION

Accepted Solutions
  1. Create a new project with STM32CubeMX or ..IDE.
  2. Configure TIM1 clock source to Internal Clock
  3. Set counter mode Up.
  4. Set Channel1 to Input Capture direct mode.on Both Edges.
  5. TIM1 DMA settings: Add TIM1_CH1 DMA in cyclic mode, leave other defaults.
  6. Generate the code for your IDE.
  7. Start the timer in main.c

 

 

  /* USER CODE BEGIN 2 */
  HAL_TIM_IC_Start_DMA(&htim1, TIM_CHANNEL_1, edge_buffer, EDGE_BUFFER_LENGTH);
  /* USER CODE END 2 */

 

 

in main.c at global level add callbacks

 

 

/* USER CODE BEGIN 0 */

#define EDGE_BUFFER_LENGTH  256
uint16_t edge_buffer[EDGE_BUFFER_LENGTH];

void process(uint16_t edges[], size_t len)
{
  // compute edge arrival time differences and decide which FM0 cases you have observed
}

void HAL_TIM_IC_CaptureHalfCpltCallback(TIM_HandleTypeDef *htim)
{
  HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, GPIO_PIN_SET);
  process(&edge_buffer[0], EDGE_BUFFER_LENGTH/2);
}

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
  HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, GPIO_PIN_RESET);
  process(&edge_buffer[EDGE_BUFFER_LENGTH/2], EDGE_BUFFER_LENGTH/2);
}
/* USER CODE END 0 */

 

 

Implement the missing parts. Use a debugger early and often, to better understand what the code does. Tune parameters to your needs (buffer size, prescaler,...).

hth

KnarfB

View solution in original post

7 REPLIES 7
TDK
Guru

It's not easy/straightforward, but it can be done. One way is to create a timer to re-create the clock and then use SPI to receive the data.

To create the clock, hook up the signal to the external reset, set duty cycle to 50%, set interval to be just over the width of the shortest period. Set channel 1 to provide the clock and have it go from low to high at the mid-point of the internal. Hook up that to SPI_SCK.

Hook up your signal to to SPI_MISO. Receive data in SPI mode, then do the conversion after it's received. 1010 -> 00, etc. A 4us period might be pushing things, but I imagine it's doable, barely.

If you feel a post has answered your question, please click "Accept as Solution".
KnarfB
Principal III

Alternative ways:

  • Use a free-running timer and EXTI interrupts. The interrupt handler reads the timer counter register and calculates the output bitwise from the observed counter differences.
  • Use a free-running timer's timer channel in input capture mode with cyclic DMA. The input capture shall fire on every edge and the DMA callbacks can be used to evaluate the edge arrival times in larger chunks.

hth

KnarfB

Thanks a lot. If it's not too much trouble, could you share some resources where I can learn how to implement this? I'm new to embedded systems, working as an intern in my third year of B-tech. Some help in this implementation will help me learn better. 

I wouldn't use interrupts if it has a 4 us period. I have used the second method in the past. Works well.

If you feel a post has answered your question, please click "Accept as Solution".
  1. Create a new project with STM32CubeMX or ..IDE.
  2. Configure TIM1 clock source to Internal Clock
  3. Set counter mode Up.
  4. Set Channel1 to Input Capture direct mode.on Both Edges.
  5. TIM1 DMA settings: Add TIM1_CH1 DMA in cyclic mode, leave other defaults.
  6. Generate the code for your IDE.
  7. Start the timer in main.c

 

 

  /* USER CODE BEGIN 2 */
  HAL_TIM_IC_Start_DMA(&htim1, TIM_CHANNEL_1, edge_buffer, EDGE_BUFFER_LENGTH);
  /* USER CODE END 2 */

 

 

in main.c at global level add callbacks

 

 

/* USER CODE BEGIN 0 */

#define EDGE_BUFFER_LENGTH  256
uint16_t edge_buffer[EDGE_BUFFER_LENGTH];

void process(uint16_t edges[], size_t len)
{
  // compute edge arrival time differences and decide which FM0 cases you have observed
}

void HAL_TIM_IC_CaptureHalfCpltCallback(TIM_HandleTypeDef *htim)
{
  HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, GPIO_PIN_SET);
  process(&edge_buffer[0], EDGE_BUFFER_LENGTH/2);
}

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
  HAL_GPIO_WritePin(LD3_GPIO_Port, LD3_Pin, GPIO_PIN_RESET);
  process(&edge_buffer[EDGE_BUFFER_LENGTH/2], EDGE_BUFFER_LENGTH/2);
}
/* USER CODE END 0 */

 

 

Implement the missing parts. Use a debugger early and often, to better understand what the code does. Tune parameters to your needs (buffer size, prescaler,...).

hth

KnarfB

Hello, I tried implementing your solution. I made a simple PWM signal and fed it to the gpio pin for dma. 

/* USER CODE BEGIN 2 */
  //for PWM
   TIM1->CCR1 = 10;
   HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_1);

   uint16_t edgeTimestamps[100];

   HAL_TIM_IC_Start_DMA(&htim4, TIM_CHANNEL_1, (uint8_t*)edgeTimestamps, 500);

   char buffer[50];
          for (uint8_t i = 0; i < 100; i++) {
              snprintf(buffer, sizeof(buffer), "Time Difference %d: %lu\r\n", i, edgeTimestamps[i]);
              HAL_UART_Transmit(&huart2, (uint8_t *)buffer, strlen(buffer), HAL_MAX_DELAY);
          }
  /* USER CODE END 2 */

I have set up the dma in cyclic mode, and am printing the captured values to serial terminal. Howerver, something seems to be wrong in my configuration of clocks.  Both timer 3 and 4 are run of 40kHz clk. 

TIM3 is generating a pwm signal of 8us with 50% duty cycle. 

How do I set up the TIM 4 to properly monitor the signal? Like I need the time difference between every edge to be stored in a buffer array.  Perhaps I have made a different error? What must the prescaler and counter period of the TIM 4 be to measure in microseconds properly?  What exactly is this function [ HAL_TIM_IC_Start_DMA(&htim4, TIM_CHANNEL_1, (uint8_t*)edgeTimestamps, 500); ] doing when in cyclic mode? Is there a way to reset the counter register value of TIM 4 simultaneously after storing it in array so only the count of the edge difference is stored in array?

 

KnarfB
Principal III

edgeTimestamps are absolute values in timer "ticks" (increment periods), you have to calculate the differences manually. The 500 parameter is wrong, it must be the length of the array passed, 100. Cyclic DMA may be overkill, depending on your overall protocol. Typically, there will be some preamble at the beginning of a transfer and you may have some ideas about the transfer length. Then you can start/stop a linear DMA.

There are more tutorials around for the opposite, generating an arbitrary waveform by a timer & DMA, but the principles are the same.