importdow
importdow

Reputation: 1

Generating waveform using STM32 timer compare - STM32F3Discovery

I am attempting to generate a waveform with a period of 33ms. In-between each period, I need to toggle the timer output 5 times with a varying timescale. My initial thought is to do this using a timer in output compare mode and then update the CRR register with the 6 pulses from the DMA in a circular mode.

  ___________          ___________          ___________
  |   5700  |___3000___|   4700  |___3000___|   10000 |_________________________

  <------------------------------------33ms------------------------------------>

So far, the pin toggles but at intervals that are over the values in the array. Below is the waveform as seen on a logic analyser. I am certain that the input clock is correct at 36MHz and the period should be 33ms.

waveform

I have included the following functions to the CubeMX generated code to ensure the timer is started with DMA. I also set the timer 2 UDE bit(enable dma) and DMA request when CCx event occurs.

// update timer 17 ude bit
TIM17->DIER |= TIM_DIER_UDE; 
// dma request sent wehn CC1 event occurs
TIM17->CR2 |= TIM_CR2_CCDS;
HAL_TIM_OC_DelayElapsedCallback(&htim17);
//enable dma
HAL_TIM_OC_Start_DMA(&htim17, TIM_CHANNEL_1, (uint32_t*)wave_buffer, 5);

At this point, I'm not sure what I'm missing as I've followed the steps in the reference manual and application notes to be on par with what I need. I appreciate any help with what I may be missing to get this to work, thank you.

main.c file

#include "main.h"
/* Private variables ---------------------------------------------------------*/
TIM_HandleTypeDef htim17;
DMA_HandleTypeDef hdma_tim17_ch1_up;

/* USER CODE BEGIN PV */
uint16_t wave_buffer[6] = {5700, 3000, 4700, 3000, 10000, 0};

/* 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_TIM17_Init(void);

int main(void)
{

    /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
    HAL_Init();

    /* Configure the system clock */
    SystemClock_Config();

    /* Initialize all configured peripherals */
    MX_GPIO_Init();
    MX_DMA_Init();
    MX_TIM17_Init();
    /* USER CODE BEGIN 2 */
    // update timer 17 ude bit
    TIM17->DIER |= TIM_DIER_UDE;
    // dma request sent wehn CC1 event occurs
    TIM17->CR2 |= TIM_CR2_CCDS;
    // HAL_TIM_OC_DelayElapsedCallback(&htim15);
    HAL_TIM_OC_DelayElapsedCallback(&htim17);
    // enable dma
    HAL_TIM_OC_Start_DMA(&htim17, TIM_CHANNEL_1, (uint32_t *)wave_buffer, 5);

    while (1)
    {
    }
}

/**
 * @brief System Clock Configuration
 * @retval None
 */
void SystemClock_Config(void)
{
    RCC_OscInitTypeDef RCC_OscInitStruct = {0};
    RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

    /** 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_ON;
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
    RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
    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_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK;
    RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2;
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;

    if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_1) != HAL_OK)
    {
        Error_Handler();
    }
}

/**
 * @brief TIM17 Initialization Function
 * @PAram None
 * @retval None
 */
static void MX_TIM17_Init(void)
{

    /* USER CODE BEGIN TIM17_Init 0 */

    /* USER CODE END TIM17_Init 0 */

    TIM_OC_InitTypeDef sConfigOC = {0};
    TIM_BreakDeadTimeConfigTypeDef sBreakDeadTimeConfig = {0};

    /* USER CODE BEGIN TIM17_Init 1 */

    /* USER CODE END TIM17_Init 1 */
    htim17.Instance = TIM17;
    htim17.Init.Prescaler = 36 - 1;
    htim17.Init.CounterMode = TIM_COUNTERMODE_UP;
    htim17.Init.Period = 33000 - 1;
    htim17.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
    htim17.Init.RepetitionCounter = 0;
    htim17.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
    if (HAL_TIM_Base_Init(&htim17) != HAL_OK)
    {
        Error_Handler();
    }
    if (HAL_TIM_OC_Init(&htim17) != HAL_OK)
    {
        Error_Handler();
    }
    sConfigOC.OCMode = TIM_OCMODE_TOGGLE;
    sConfigOC.Pulse = 0;
    sConfigOC.OCPolarity = TIM_OCPOLARITY_LOW;
    sConfigOC.OCNPolarity = TIM_OCNPOLARITY_HIGH;
    sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
    sConfigOC.OCIdleState = TIM_OCIDLESTATE_RESET;
    sConfigOC.OCNIdleState = TIM_OCNIDLESTATE_RESET;
    if (HAL_TIM_OC_ConfigChannel(&htim17, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
    {
        Error_Handler();
    }
    __HAL_TIM_ENABLE_OCxPRELOAD(&htim17, TIM_CHANNEL_1);
    sBreakDeadTimeConfig.OffStateRunMode = TIM_OSSR_DISABLE;
    sBreakDeadTimeConfig.OffStateIDLEMode = TIM_OSSI_DISABLE;
    sBreakDeadTimeConfig.LockLevel = TIM_LOCKLEVEL_OFF;
    sBreakDeadTimeConfig.DeadTime = 0;
    sBreakDeadTimeConfig.BreakState = TIM_BREAK_DISABLE;
    sBreakDeadTimeConfig.BreakPolarity = TIM_BREAKPOLARITY_HIGH;
    sBreakDeadTimeConfig.BreakFilter = 0;
    sBreakDeadTimeConfig.AutomaticOutput = TIM_AUTOMATICOUTPUT_DISABLE;
    if (HAL_TIMEx_ConfigBreakDeadTime(&htim17, &sBreakDeadTimeConfig) != HAL_OK)
    {
        Error_Handler();
    }
    /* USER CODE BEGIN TIM17_Init 2 */

    /* USER CODE END TIM17_Init 2 */
    HAL_TIM_MspPostInit(&htim17);
}

/**
 * Enable DMA controller clock
 */
static void MX_DMA_Init(void)
{

    /* DMA controller clock enable */
    __HAL_RCC_DMA1_CLK_ENABLE();

    /* DMA interrupt init */
    /* DMA1_Channel1_IRQn interrupt configuration */
    HAL_NVIC_SetPriority(DMA1_Channel1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(DMA1_Channel1_IRQn);
}

/**
 * @brief GPIO Initialization Function
 * @PAram None
 * @retval None
 */
static void MX_GPIO_Init(void)
{
    /* USER CODE BEGIN MX_GPIO_Init_1 */
    /* USER CODE END MX_GPIO_Init_1 */

    /* GPIO Ports Clock Enable */
    __HAL_RCC_GPIOF_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();

    /* USER CODE BEGIN MX_GPIO_Init_2 */
    /* USER CODE END MX_GPIO_Init_2 */
}

/**
 * @brief  This function is executed in case of error occurrence.
 * @retval None
 */
void Error_Handler(void)
{
    /* USER CODE BEGIN Error_Handler_Debug */
    /* User can add his own implementation to report the HAL error return state */
    __disable_irq();
    while (1)
    {
    }
    /* USER CODE END Error_Handler_Debug */
}

#ifdef USE_FULL_ASSERT
/**
 * @brief  Reports the name of the source file and the source line number
 *         where the assert_param error has occurred.
 * @PAram  file: pointer to the source file name
 * @PAram  line: assert_param error line source number
 * @retval None
 */
void assert_failed(uint8_t *file, uint32_t line)
{
    /* USER CODE BEGIN 6 */
    /* User can add his own implementation to report the file name and line number,
        ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
    /* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */

hal msp file


/**
 ******************************************************************************
 * @file         stm32f3xx_hal_msp.c
 * @brief        This file provides code for the MSP Initialization
 *               and de-Initialization codes.
 ******************************************************************************
 */
/* Includes ------------------------------------------------------------------*/
#include "main.h"

extern DMA_HandleTypeDef hdma_tim17_ch1_up;

void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);
/**
 * Initializes the Global MSP.
 */
void HAL_MspInit(void)
{

    /* USER CODE BEGIN MspInit 0 */

    /* USER CODE END MspInit 0 */

    __HAL_RCC_SYSCFG_CLK_ENABLE();
    __HAL_RCC_PWR_CLK_ENABLE();

    /* System interrupt init*/

    /* USER CODE BEGIN MspInit 1 */

    /* USER CODE END MspInit 1 */
}

/**
 * @brief TIM_Base MSP Initialization
 * This function configures the hardware resources used in this example
 * @PAram htim_base: TIM_Base handle pointer
 * @retval None
 */
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim_base)
{
    if (htim_base->Instance == TIM17)
    {
        /* USER CODE BEGIN TIM17_MspInit 0 */

        /* USER CODE END TIM17_MspInit 0 */
        /* Peripheral clock enable */
        __HAL_RCC_TIM17_CLK_ENABLE();

        /* TIM17 DMA Init */
        /* TIM17_CH1_UP Init */
        hdma_tim17_ch1_up.Instance = DMA1_Channel1;
        hdma_tim17_ch1_up.Init.Direction = DMA_MEMORY_TO_PERIPH;
        hdma_tim17_ch1_up.Init.PeriphInc = DMA_PINC_DISABLE;
        hdma_tim17_ch1_up.Init.MemInc = DMA_MINC_ENABLE;
        hdma_tim17_ch1_up.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD;
        hdma_tim17_ch1_up.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD;
        hdma_tim17_ch1_up.Init.Mode = DMA_CIRCULAR;
        hdma_tim17_ch1_up.Init.Priority = DMA_PRIORITY_HIGH;
        if (HAL_DMA_Init(&hdma_tim17_ch1_up) != HAL_OK)
        {
            Error_Handler();
        }

        /* Several peripheral DMA handle pointers point to the same DMA handle.
        Be aware that there is only one channel to perform all the requested DMAs. */
        __HAL_LINKDMA(htim_base, hdma[TIM_DMA_ID_CC1], hdma_tim17_ch1_up);
        __HAL_LINKDMA(htim_base, hdma[TIM_DMA_ID_UPDATE], hdma_tim17_ch1_up);

        /* TIM17 interrupt Init */
        HAL_NVIC_SetPriority(TIM1_TRG_COM_TIM17_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(TIM1_TRG_COM_TIM17_IRQn);
        /* USER CODE BEGIN TIM17_MspInit 1 */

        /* USER CODE END TIM17_MspInit 1 */
    }
}

void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim)
{
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    if (htim->Instance == TIM17)
    {
        /* USER CODE BEGIN TIM17_MspPostInit 0 */

        /* USER CODE END TIM17_MspPostInit 0 */

        __HAL_RCC_GPIOA_CLK_ENABLE();
        /**TIM17 GPIO Configuration
        PA7     ------> TIM17_CH1
        */
        GPIO_InitStruct.Pin = GPIO_PIN_7;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
        GPIO_InitStruct.Alternate = GPIO_AF1_TIM17;
        HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

        /* USER CODE BEGIN TIM17_MspPostInit 1 */

        /* USER CODE END TIM17_MspPostInit 1 */
    }
}
/**
 * @brief TIM_Base MSP De-Initialization
 * This function freeze the hardware resources used in this example
 * @PAram htim_base: TIM_Base handle pointer
 * @retval None
 */
void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef *htim_base)
{
    if (htim_base->Instance == TIM17)
    {
        /* USER CODE BEGIN TIM17_MspDeInit 0 */

        /* USER CODE END TIM17_MspDeInit 0 */
        /* Peripheral clock disable */
        __HAL_RCC_TIM17_CLK_DISABLE();

        /* TIM17 DMA DeInit */
        HAL_DMA_DeInit(htim_base->hdma[TIM_DMA_ID_CC1]);
        HAL_DMA_DeInit(htim_base->hdma[TIM_DMA_ID_UPDATE]);

        /* TIM17 interrupt DeInit */
        HAL_NVIC_DisableIRQ(TIM1_TRG_COM_TIM17_IRQn);
        /* USER CODE BEGIN TIM17_MspDeInit 1 */

        /* USER CODE END TIM17_MspDeInit 1 */
    }
}

EDIT Thanks to wek and Sid Pethe for pointing out some errors, I've made corrections and can now confirm that I have 6 pulses. But the pulses are all equal and measured to be the ARR of 33ms. My thinking is that the DMA is not transferring values from the array to the timer compare register (CRR), some DMA transfer flags are not properly setup or either the CRR has a value of 0 or 33000. Still scratching my head reading the reference manual to find something, will post any update if I can figure this out... new waveform with 6 pulses

Upvotes: 0

Views: 549

Answers (3)

Sid Pethe
Sid Pethe

Reputation: 59

So I tried implementing the same thing and I noticed under timer config -> DMA settings STMCube will default the DMA stream direction to "Peripheral to Memory". You will need to swap that around to "Memory to Peripheral". That should let you see the actual changes based on your wave_buffer values. Also, based on DMA_HandleTypeDef hdma_tim17_ch1_up; you've chosen the wrong DMA request. You want TIM17_CH1 not TIM17_CH1_UP.

Secondly, remember the counter does not reset to 0 when ouput compare matches and the output will toggle every time a match occurs. i.e.

match at 5000 (toggle), wait for overflow (~33ms), match at 3000(toggle), match at 4700(toggle), wait for overflow(~33ms)... etc.

I set a period of 4294967 -> (2^32/1000) and wrote these 2 lines in addition to the autogenerated code and it worked.

u32 tims[] = {400000,600000,1200000,2400000};

HAL_TIM_OC_Start_DMA(&htim2, TIM_CHANNEL_1, tims, sizeof(tims)/sizeof(uint32_t));

Output waveform. Notice the doubling time between edges

Caption: Output waveform - Notice the doubling time between edges

Upvotes: 0

wek
wek

Reputation: 1213

I see you've expanded the table as I've told you at the STM32 forum, but you did not reflect that now you have 6 values to transfer, in the number of DMA transfers:

HAL_TIM_OC_Start_DMA(&htim17, TIM_CHANNEL_1, (uint32_t *)wave_buffer,5);

JW

Upvotes: 0

Sid Pethe
Sid Pethe

Reputation: 59

I think the problem might be in your explicit cast from a uint16_t array

uint16_t wave_buffer[6] = {5700, 3000, 4700, 3000, 10000, 0};

to a uint32_t value.

HAL_TIM_OC_Start_DMA(&htim17, TIM_CHANNEL_1, (uint32_t *)wave_buffer, 5);

A uint16_t data is always stored as a contiguous array and reading a uint32_t value from that address will make the value be {wave_buffer[1],wave_buffer[0]}

A simple test to see if this is true would be to check if the output period is

196,613,700 => (3000<<16 + 5700)

Upvotes: 0

Related Questions