I am trying to implement two independent tasks with the hardware timers.
I need task 1 to repeat after every 100us and the other to repeat 10us. I generated the code from STM32cubemx.
The timer function is as below
static void MX_TIM2_Init(void) {
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 100;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 800;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) {
Error_Handler();
}
}
I am further processing with the function as below :
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim-> Instance == TIM2)
{
ledBlueBlink();
}
}
I have a task ledGreenBlink() which is supposed to run after every 10 times task ledBlueBlink() runs.
Can anyone please let me know how can I implement this?
Not sure about the wording, if you're really looking for a task as I do not see a task "ledBlueBlink()".
If you just want to call "ledGreenBlink()" every ten calls of your callback, I would recommend just doing this by adding a counter variable and triggering for every ten times.
static unsigned counter = 0;
if (counter%10)
ledGreenBlink();
counter++;
Please be aware that the counter variable needs to be static in order to preserve its value over several calls of the callback.
Futhermore it should be an unsigned type in order to safely roll over when it reaches it's maximum value.
In case you really need tasks for something you might want to clarify your code to support the question.
Related
Trying to make simple PWM transmitter i faced with a problem. I have TIM2 with Channel2 (in PWM Generation mode) on board NUCLEO F042K6 and USART1 connected to the board in Async mode (USART works in DMA).
I wanted to make a PWM transmitter that uses a circular buffer that can be filled with one character or several characters at once. The plan was that when the user enters a character (or several characters), the idle line callback is processed, in which a function is called that stores the data in a cyclic buffer. After saving, the data transferred to the buffer is passed to the function PWM_SendChar(...) to convert the data into bits and transfer them over the PWM line. But the problem is when the program goes into PWM_SendChar(...) the callback HAL_TIM_PWM_PulseFinishedCallback(...) isn't called and program stucks in infinite while loop because variable used as a flag for detecting the end of single pulse (PWM_data_sent_flag) wasn't set to value 1.
However, if I use HAL_UART_Recieve(...) func in while(1) loop (located in main(void)) to recieve a char from user program works fine without stucking in while loop in TIM callback. Ofc, for this method I don't use HAL_UARTEx_RxEventCallback(...) and cycle buffer.
Thats's why I think the problem is in USART idle line detection and please help me solve it.
Code Example
Global variable used for waiting the end of pulse:
volatile uint16_t PWM_data_sent_flag = 0;
PWM timer callback:
void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) {
if (htim->Instance == TIM2) {
HAL_TIM_PWM_Stop(&htim2, TIM_CHANNEL_2);
PWM_data_sent_flag = 1;
}
}
Callback for USART idle line:
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) {
if (huart == &huart1) {
// Function_to_save_input_char_in_cycle_buffer() is called
}
}
Function used to send char as a PWM signal (emited after Function_to_save_input_char_in_cycle_buffer() completed):
void PWM_SendChar(char ch) {
for (int i = 0, mv = 7; i <= 7; ++i, --mv) {
uint8_t bit = (ch & (1 << mv)) ? 1 : 0;
// PWM_LOW_PERCENT encodes bit with zero value, PWM_HIGH_PERCENT encodes bit with value one
uint16_t duty_cycle = bit == 0 ? PWM_LOW_PERCENT : PWM_HIGH_PERCENT;
// Change TIM2 CCR2 value according to bit's value
__HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_2,
GET_PERCENT_VALUE(TIM2->ARR, duty_cycle));
HAL_TIM_PWM_Start_IT(&htim2, TIM_CHANNEL_2);
// Program comes to this cycle and doesn't leave it
while (!PWM_data_sent_flag)
;
PWM_data_sent_flag = 0;
}
}
USART receive to idle launch (half data transmitted callback disabled):
int main(void) {
// Init code
// ...
if (HAL_UARTEx_ReceiveToIdle_DMA(&huart1, (uint8_t*) ring_buf.buff,
ARRAY_LEN(ring_buf.buff)) != HAL_OK) {
Error_Handler();
}
// Disable half word transmitted callback
__HAL_DMA_DISABLE_IT(&hdma_usart1_rx, DMA_IT_HT);
while(1) {
}
}
TIM2 settings:
static void MX_TIM2_Init(void) {
/* USER CODE BEGIN TIM2_Init 0 */
/* USER CODE END TIM2_Init 0 */
TIM_ClockConfigTypeDef sClockSourceConfig = { 0 };
TIM_MasterConfigTypeDef sMasterConfig = { 0 };
TIM_OC_InitTypeDef sConfigOC = { 0 };
/* USER CODE BEGIN TIM2_Init 1 */
/* USER CODE END TIM2_Init 1 */
htim2.Instance = TIM2;
htim2.Init.Prescaler = 48000 - 1;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 100 - 1;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK) {
Error_Handler();
}
if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) {
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig)
!= HAL_OK) {
Error_Handler();
}
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = 0;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_2)
!= HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN TIM2_Init 2 */
/* USER CODE END TIM2_Init 2 */
HAL_TIM_MspPostInit(&htim2);
}
USART1 settings:
static void MX_USART1_UART_Init(void) {
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
/* USER CODE BEGIN USART1_Init 1 */
/* USER CODE END USART1_Init 1 */
huart1.Instance = USART1;
huart1.Init.BaudRate = 115200;
huart1.Init.WordLength = UART_WORDLENGTH_8B;
huart1.Init.StopBits = UART_STOPBITS_1;
huart1.Init.Parity = UART_PARITY_NONE;
huart1.Init.Mode = UART_MODE_TX_RX;
huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart1.Init.OverSampling = UART_OVERSAMPLING_16;
huart1.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart1.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart1) != HAL_OK) {
Error_Handler();
}
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
}
DMA settings:
static void MX_DMA_Init(void) {
/* DMA controller clock enable */
__HAL_RCC_DMA1_CLK_ENABLE();
/* DMA interrupt init */
/* DMA1_Channel4_5_IRQn interrupt configuration */
HAL_NVIC_SetPriority(DMA1_Channel4_5_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(DMA1_Channel4_5_IRQn);
}
I am new to interrupt-based programming.
In my current project, I need the interrupt generated exactly at 1us interval.
Below is the screenshot from the Clock Configuration tab in CubeMX.
I am using the TIM3 timer as it can generate the clock frequency of 1us.
Below is the TIM3 configuration code.
static void MX_TIM3_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig;
TIM_MasterConfigTypeDef sMasterConfig;
htim3.Instance = TIM3;
htim3.Init.Prescaler = 1-1 ;//0x00;// 0x36; || 0x00//1-1
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 0xffff-1; //0x64; || 0xd7 //0xffff-1
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim3) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim3, &sClockSourceConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig) != HAL_OK)
{
_Error_Handler(__FILE__, __LINE__);
}
}
I am calling the timer
HAL_TIM_IRQHandler(&htim3);
/* USER CODE BEGIN TIM3_IRQn 1 */
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_6);
I see that the interrupt of duration 1.2ms is generated.
Can anyone let me why is this happening and how can I reduce the interrupt to 1us duration?
Any change required in the timer frequency?
I am also using freeRTOS and other applications are also running on the microcontroller.
Any help in this is highly appreciated.
Thanks in Advance
If your requirement output an accurate 500KHz 1:1 mark/space signal (i.e. 1us high, 1us low), then doing that through interrupts while expecting your system to do other useful work is both impractical an unnecessary. The general purpose timers have a output-compare function that can drive a GPIO pin directly without interrupts or software overhead.
Only certain pins are connected to the Timer OC channels, so to drive PB6 in this case you would need to use TIM4 Channel 1.
Also rather than determining and hard-coding timer reload and pulse, you should use the available HAL RCC clock functions (HAL_RCC_GetPCLK1Freq() in this case) to calculate the values to avoid erros, and so that the code will be portable to other systems or will work correctly if you change your clock configuration.
static void MX_TIM4_Init(void)
{
cost uint32_t PULSE_WIDTH = HAL_RCC_GetPCLK1Freq() * 2 / 1000000 ;
htim4.Instance = TIM4 ;
htim4.Init.Prescaler = 0;
htim4.Init.CounterMode = TIM_COUNTERMODE_UP ;
htim4.Init.Period = PULSE_WIDTH * 2 ;
htim4.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1 ;
HAL_TIM_PWM_Init( &htim4 ) ;
TIM_MasterConfigTypeDef sMasterConfig ;
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig);
TIM_OC_InitTypeDef sConfigOC ;
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = PULSE_WIDTH ;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim4, &sConfigOC, TIM_CHANNEL_1);
}
Then elsewhere you need to configure PB6 as an output and start the timer:
LL_GPIO_InitTypeDef GPIO_InitStruct = {0} ;
GPIO_InitStruct.Pin = GPIO_PIN_6 ;
GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE ;
GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_LOW ;
GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL ;
GPIO_InitStruct.Pull = LL_GPIO_PULL_NO ;
GPIO_InitStruct.Alternate = LL_GPIO_AF_2 ;
LL_AHB2_GRP1_EnableClock(LL_AHB2_GRP1_PERIPH_GPIOB)
LL_GPIO_Init(GPIOB, &GPIO_InitStruct);
HAL_TIM_PWM_Start( &htim4, TIM_CHANNEL_1 ) ;
Thereafter the signal will be maintained indefinitely on PB6 with no GPIO access or interrupt handling.
I'm new in stm32, I recently create a project to make a quick pulse on a GPIO pin every second, just to determine the time accuracy by the logic analyzer, however, I always get the time around 0.999,935s instead of one second, I tried everything I searched on the internet, but nothing work, please give a help.
My code was mostly generated by STM32CubeMX, these are the configs of CubeMX
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM2_Init();
HAL_TIM_Base_Start_IT(&htim2);
while (1) { }
}
.
.
.
static void MX_TIM2_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim2.Instance = TIM2;
htim2.Init.Prescaler = 799;
htim2.Init.CounterMode = TIM_COUNTERMODE_DOWN;
htim2.Init.Period = 9999;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
.
.
.
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
GPIOB->BSRR = (uint32_t)GPIO_PIN_2 << 16u; // reset bit
}
The time between 2 and 3 is always around 0.999935s
how can I fix this error to exactly one second???
I'm having issues setting the timers on the STM32F7 dissovery board to 500 Khz. I seem to top around around 370kHz for some reason. 'm toggling a GPIO pin with a scope to the input and simply changing the Period on the timer to monitor what's happening.
I'm using CubeMX to generate my project files and I initialise my timer:
static void MX_TIM1_Init(void)
{
TIM_ClockConfigTypeDef sClockSourceConfig = {0};
TIM_MasterConfigTypeDef sMasterConfig = {0};
htim1.Instance = TIM1;
htim1.Init.Prescaler = 0;
htim1.Init.CounterMode = TIM_COUNTERMODE_UP;
htim1.Init.Period = 108;
htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim1.Init.RepetitionCounter = 0;
htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim1) != HAL_OK)
{
Error_Handler();
}
sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
if (HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_OC1;
sMasterConfig.MasterOutputTrigger2 = TIM_TRGO2_OC1REF;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
I then start the timer in interrupt mode:
if(HAL_TIM_Base_Start_IT(&htim1) != HAL_OK)
{
Error_Handler();
}
and then toggle a GPIO pin when the period has elapsed:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM1)
{
HAL_GPIO_TogglePin(GPIOG, GPIO_PIN_6);
}
}
The GPIO pin is set as:
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOG, &GPIO_InitStruct);
However, I've played around with the Period on the timer and I've gotten the following results:
539 = 100KHz
239 = 300KHz
215 = 330KHz
108 = 369Khz
I'd expect to get 500Khz with a Period of 215 but this isn't the case.Is there anything wrong with my settings?
The timer settings are right. The interrupt code is too slow.
The HAL library is not suited for timing critical applications. HAL tries (and fails) to handle every possible use case in one-size-fits-all functions, which means lots of unnecessary processing with associated delays. Use a simple interrupt handler instead of the TIM1_IRQHandler() supplied by HAL that just clears the interrupt status and inverts a bit directly in GPIOG->ODR. This should do:
void TIM1_IRQHandler(void) {
TIM1->SR = ~TIM_SR_UIF;
GPIOG->ODR ^= (1 << 6);
}
just 2 lines of code, instead of the 100+ lines of HAL_TIM_IRQHandler(). Should work up to 1 MHz, maybe more.
Toggling output pins in a timer interrupt handler is fine as an embedded programming exercise, but it wastes a significant amount of CPU cycles to achieve what a timer can do alone, delaying and possibly blocking other tasks.
A timer can output a square wave (PWM signal) on its output channels with frequencies up to the half of its source clock. Look for PWM edge-aligned mode in the Reference Manual.
I use SW4STM32 toolchain,I want to bliking led using timer2 when overflow, in STM32103RET,
here is my functions to turn led on and of.
void TurnOnLed(){
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_10,GPIO_PIN_SET);
}
void TurnOffLed()
{
HAL_GPIO_WritePin(GPIOA,GPIO_PIN_10,GPIO_PIN_RESET);
}
the timer2 initilizing has been set by stm32 cube mx, but i do not know which function called when timer2 overflows?
You need to start your timer by function
HAL_TIM_Base_Start_IT(&htimX);
And for the firs time implement you callback function named HAL_TIM_PeriodElapsedCallback:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIMx) {
}
}
If you don't know anything about HAL_Driver you may found a lot of information from:
STM32F0xx HAL_Driver description or for another family just search for HAL Driver on st.com
You can see examples of HAL Driver usage (as you use CubeMX, so you can found it in C:/Users/%USERNAME%/STM32Cube/Repository/ directory)
Just open stm32f?xx_hal_tim.c and see what functions are present, see their comments to uderstand what tey are doing. And see what functions are called from HAL_TIM_IRQHandler to know how are named callbacks.
First,You have to configure your timer:
/* TIM2 init function */
static void MX_TIM2_Init(void)
{
TIM_SlaveConfigTypeDef sSlaveConfig;
TIM_MasterConfigTypeDef sMasterConfig;
htim2.Instance = TIM2;
htim2.Init.Prescaler = 10000;
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 1000;
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
sSlaveConfig.SlaveMode = TIM_SLAVEMODE_RESET;
sSlaveConfig.InputTrigger = TIM_TS_ITR0;
if (HAL_TIM_SlaveConfigSynchronization(&htim2, &sSlaveConfig) != HAL_OK)
{
Error_Handler();
}
sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
{
Error_Handler();
}
}
Second use this function to handle interrupt:
void TIM2_IRQHandler(void)
{
HAL_GPIO_TogglePin(GPIOA,GPIO_PIN_10);
__HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE); //clear interrupt flag
//HAL_Delay(500);
}
Do not forget enable interrupts in main() function:
HAL_NVIC_SetPriority(TIM2_IRQn, 0, 1);
HAL_NVIC_EnableIRQ(TIM2_IRQn);// Enable interrupt from TIM3 (NVIC level)