0
\$\begingroup\$

I'm working on a project where I need to generate a pulse with a desired frequency, which can be adjusted through three different modes.

The modes are as follows:

  • Frequency remains the same.
  • Frequency is divided by 2.
  • Frequency is multiplied by 2.

The mode is selected using a switch (configured for external interrupt callback), and the pulse is generated using Timer 4 (clock 48 MHz, prescaler 4800) in Output Compare mode with the "toggle on match" feature. The ARR (Auto-Reload Register) and CCR (Capture/Compare Register) values are set based on the output from a rotary encoder.

The pulse generation itself works as expected, but when switching between modes, the signal goes high for a fixed period of time (8 seconds) before returning to the expected pulse pattern.

Has anyone encountered a similar issue? What could be the possible reasons for this behavior, and how can I address it?

    while (1)
    {
        Rotary_Enc_Chk();

        if(rotChk == TRUE)
        {
            rotChk = FALSE;

            sprintf(txBuffer, "Speed: %d\n\r", speed);
            HAL_UART_Transmit(&huart1, (uint8_t*)txBuffer, strlen(txBuffer), 10);
            TM1637_DisplayDecimal(speed, 0);                                        
            if(speed > 0)
            {
                uint16_t freq = (10000 / (2 * xFactor * speed )) - 1;
                //uint16_t freq = speed;
                TIM14->ARR = freq;
                TIM14->CCR1 = freq;
                HAL_TIM_OC_Start(&htim14, TIM_CHANNEL_1);
            }
            else {
                sprintf(txBuffer, "Speed: %d\n\r", speed);
                HAL_UART_Transmit(&huart1, (uint8_t*)txBuffer, strlen(txBuffer), 10);
            }
        }

        if(modeChange == TRUE)
        {
            sprintf(txBuffer, "Mode: %d\n\r", speed);
            HAL_UART_Transmit(&huart1, (uint8_t*)txBuffer, strlen(txBuffer), 10);
            rotChk = TRUE;
            modeChange = FALSE;
            Mode_Check();
        }
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
    UNUSED(GPIO_Pin);

    if((GPIO_Pin == _2X_SEL_Pin) || (GPIO_Pin == X_2_SEL_Pin))
    {
        modeChange = TRUE;
    }
void Mode_Check()
{
    if(HAL_GPIO_ReadPin(GPIOA, _2X_SEL_Pin) == GPIO_PIN_RESET)
    {
        currMode = FAST_2X;
        xFactor = 2;
        All_Led_Off();
        Red_Led_On();
    }
    else if(HAL_GPIO_ReadPin(GPIOA, X_2_SEL_Pin) == GPIO_PIN_RESET)
    {
        currMode = SLOW_X_2;
        xFactor = 0.5;
        All_Led_Off();
        Blue_Led_On();
    }
    else
    {
        currMode = MEDIUM_X;
        xFactor = 1;
        All_Led_Off();
        Green_Led_On();
    }
}

rotary encoder part void Rotary_Enc_Chk(void) { static uint32_t lastTick = 0; uint32_t currentTick = HAL_GetTick();

switch (encoderState) {
    case WAIT_FOR_A_LOW:
        if (HAL_GPIO_ReadPin(GPIOB, ROT_A_Pin) == GPIO_PIN_RESET) {
            if (HAL_GPIO_ReadPin(GPIOB, ROT_B_Pin) == GPIO_PIN_RESET) {
                encoderState = WAIT_FOR_B_LOW;
                lastTick = currentTick;
                
            } else {
                encoderState = WAIT_FOR_B_HIGH;
                lastTick = currentTick;
               
            }
        }
        break;

    case WAIT_FOR_B_LOW:
        if (HAL_GPIO_ReadPin(GPIOB, ROT_B_Pin) == GPIO_PIN_SET) {
            speed--;

           
            encoderState = WAIT_FOR_A_HIGH;
            rotChk = TRUE;
            lastTick = currentTick;
        } else if (currentTick - lastTick > 10) {
            
            encoderState = WAIT_FOR_A_LOW;
        }
        break;

    case WAIT_FOR_A_HIGH:
        if (HAL_GPIO_ReadPin(GPIOB, ROT_A_Pin) == GPIO_PIN_SET) {
            encoderState = WAIT_FOR_B_HIGH;
            lastTick = currentTick;
        } else if (currentTick - lastTick > 10) {
            
            encoderState = WAIT_FOR_A_LOW;

        }
        break;

    case WAIT_FOR_B_HIGH:
        if (HAL_GPIO_ReadPin(GPIOB, ROT_B_Pin) == GPIO_PIN_RESET) {
            speed++;
           
           
            encoderState = WAIT_FOR_A_LOW;
            lastTick = currentTick;
            // Set rotChk flag
            rotChk = TRUE;
        } else if (currentTick - lastTick > 10) {
            
            encoderState = WAIT_FOR_A_LOW;


        }
        break;
}

// Bound speed
if (speed < 0) speed = 0;

if (speed > 225) speed = 225;

}

void TM1637_DisplayDecimal(int v, int displaySeparator) { unsigned char digitArr[N_DISPLAYS]; for (int i = 0; i < N_DISPLAYS; ++i) { digitArr[i] = segmentMap[v % 10];

    if (i == 2 && displaySeparator)             //Code snippet for dot or display separator
    {
        digitArr[i] |= 1 << 7;
    }
    v /= 10;
}

if(digitArr[N_DISPLAYS - 1] == 0x3F)            //Code Snippet for MSB zeros to be off
{
    digitArr[N_DISPLAYS - 1] = 0x00;
    if(digitArr[N_DISPLAYS - 2] == 0x3F)
    {
        digitArr[N_DISPLAYS - 2] = 0x00;
    }
}
\$\endgroup\$
4
  • \$\begingroup\$ Can you provide the code for Rotary_Enc_Chk(), TM1637_DisplayDecimal(), All_Led_Off(), and the red/green/blue on functions? Also can you try commenting out all sprintf(), UART transmit, and LED on/off calls and see if that changes the behavior? \$\endgroup\$
    – pion
    Commented Aug 30 at 20:33
  • \$\begingroup\$ added. display function to display the encoder value and Led function to blink an rgb led based on mode. \$\endgroup\$
    – BNC
    Commented Aug 31 at 5:30
  • 2
    \$\begingroup\$ if (speed < 0) speed = 0; and uint16_t freq = (10000 / (2 * xFactor * speed )) - 1; is calling for troubles. Never divide by 0. \$\endgroup\$
    – Velvet
    Commented Aug 31 at 10:13
  • 3
    \$\begingroup\$ Sounds like a missed compare event while the timer runs to FFFF,, wraps around, and finally resumes normal operation. On some MCUs you must stop or reset the timer in some way before a new compare value is applied. However, I am not familiar with this MCU. \$\endgroup\$
    – Jens
    Commented Aug 31 at 21:39

1 Answer 1

2
\$\begingroup\$

That sound like symptoms of timer rollover. You probably face following problem: For example ARR = 100. Timer counts and CNT = 56. Then you write new value to ARR = 30. And situation is now CNT = 56, ARR = 30. Counter have to count until 0xFFFF (or 0xFFFFFFFF if 32bit), then rollover to 0 a then another 30 to make one period. There are mechanisms to solve this problem. It is Auto-reload preload in case ARR and Output Compare x preload in case of CCR.

If Preload is enabled, then value of ARR and CCR is writen only when timer uptadetes (counts from 0) - in safe time. Moreover, if you need to change ARR and CCR in the same time, then you should write new values long time before timer updates to be sure that both ARR and CCR are taken in next timer period. If you write these values in wrong time (for example ARR few moments before timer update, and CCR few moments after, then timer run one period with desired ARR and "old" CCR value). This can be protected using UDIS (Update disable) bit. You just simply disable update, then write all values (ARR, CCRs...) and then enable update back.

\$\endgroup\$
1
  • \$\begingroup\$ thank you for your input. it worked. \$\endgroup\$
    – BNC
    Commented Sep 2 at 10:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.