2026-03-22 12:08 AM - last edited on 2026-03-23 4:28 AM by mƎALLEm
Hi friends,
Again I have come with a problem. Please see my code. PWM works as usual. But it is made to stop by resetting PC3 bit, PWM does not stop rather, it behaves randomly. Sometimes the PWM channels are driven high, driven low or PWM pulse is there. Many modifications has turned futile. Hence I seek your support.
Regards
ankhola
Main,c
/* MAIN.C file
*
* Copyright (c) 2002-2005 STMicroelectronics
*/
#include "stm8s.h"
#include "ccp.h"
void inv_on(void);
void inv_off(void);
void sw_state(void);
unsigned int pwm_duty1 = 40;
unsigned int pwm_duty2 = 50;
unsigned int i=0;
unsigned int duty;
unsigned char toggle=0;
unsigned char off_state;
unsigned char on_state;
unsigned const int table []= {0, 16, 32, 48, 64, 80, 96,
112, 127, 143, 159, 174, 190, 205, 220, 235, 250, 264, 279,
293, 307, 321, 335, 349, 362, 375, 388, 400, 413, 425, 437,
448, 460, 471, 481, 492, 502, 511, 521, 530, 539, 547, 555,
563, 570, 577, 584, 590, 596, 602, 607, 612, 616, 620, 623,
627, 630, 632, 634, 636, 637, 638, 638, 638, 638, 637, 636,
634, 632, 630, 627, 623, 620, 616, 612, 607, 602, 596, 590,
584, 577, 570, 563, 555, 547, 539, 530, 521, 511, 502, 492,
481, 471, 460, 448, 437, 425, 413, 400, 388, 375, 362, 349,
335, 321, 307, 293, 279, 264, 250, 235, 220, 205, 190, 174,
159, 143, 127, 112, 96, 80, 64, 48, 32, 16};
@far @interrupt void TIM2_UPD_OVF_IRQHandler(void) {
// 1. Clear the Interrupt Pending Bit
if (TIM2->SR1 & TIM2_SR1_UIF)
{
if (toggle == 0) {
TIM1_SetCompare2 (0);
i++;
if(i == 125) {
i = 0;
toggle = ~ toggle;
}
TIM1_SetCompare1(table[i]);
}
else {
TIM1_SetCompare1 (0);
i++;
if(i == 125) {
i = 0;
toggle = ~ toggle;
}
TIM1_SetCompare2(table[i]);
}
TIM2->SR1 &= ~TIM2_SR1_UIF;
}
}
void sw_state(void) {
if (GPIO_ReadInputPin, (GPIOC, GPIO_PIN_3))
on_state = 1;
else
on_state = 0;
}
void inv_on() {
TIM1_ComplementaryPWM_Init();
TIM2_OC_Init();
//GPIO_Init(GPIOD,GPIO_PIN_7, GPIO_MODE_OUT_PP_HIGH_FAST);
enableInterrupts();
do {
sw_state();
} while (on_state == 1);
}
void inv_off () {
//TIM1 -> CCER1 &= 0xFA;
TIM1->CCER1 &= ~TIM1_CCER1_CC1E;
TIM1->CCER1 &= ~TIM1_CCER1_CC1NE;
TIM2 -> IER = TIM2_IER_UIE;
GPIOB -> DDR |= 0x03;
GPIOB -> ODR &= 0xFC;
GPIOC -> DDR |= 0x06;
GPIOC -> ODR &= 0xF9;
GPIOB -> CR1 |= 0x03;
GPIOC -> CR1 |= 0x06;
TIM1_CtrlPWMOutputs(DISABLE);
do {
sw_state();
} while (on_state == 0);
}
void main(void)
{
Clock_Config();
gpio_init();
do {
sw_state();
if (on_state == 1)
inv_on();
else
inv_off();
} while (1);
//while(1) {
// Main loop can be used for other tasks or dynamically changing PWM values
// }
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
while (1)
{
}
}
#endif
ccp.h
#include "stm8s.h"
void gpio_init(void) {
GPIO_Init(GPIOC, GPIO_PIN_3, GPIO_MODE_IN_PU_IT);
}
void Clock_Config(void) {
CLK->ICKR |= CLK_ICKR_HSIEN; // Enable HSI
while (!(CLK->ICKR & CLK_ICKR_HSIRDY)); // Wait for stabilization
CLK->CKDIVR = 0x00; // HSI 16MHz / 1 = 16MHz
CLK->SWR = 0xE1; // Use HSI as master clock
}
void TIM2_OC_Init(void)
{
CLK->PCKENR1 = 0xFF;
TIM2->PSCR = 0x00;
TIM2 -> ARRH = 1280 >> 8;
TIM2 -> ARRL = 1280 & 0xFF;
//TIM2->CCMR1 = 0x00;
//TIM2 -> EGR |= (1 << TIM2_EGR_UG);
TIM2 -> IER = 0x01;
TIM2->CR1 |= TIM2_CR1_CEN;
}
void TIM1_ComplementaryPWM_Init(void)
{
// 1. Enable TIM1 Clock
CLK_PeripheralClockConfig(CLK_PERIPHERAL_TIMER1, ENABLE);
// 2. Configure GPIOs for TIM1 Channels (PC6-CH1, PC7-CH1N, PC3-CH2, PC4-CH2N)
// Note: Depends on specific STM8 variant, check datasheet for pinout
GPIO_Init(GPIOC, GPIO_PIN_1, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(GPIOC, GPIO_PIN_2, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(GPIOB, GPIO_PIN_0, GPIO_MODE_OUT_PP_LOW_FAST);
GPIO_Init(GPIOB, GPIO_PIN_1, GPIO_MODE_OUT_PP_LOW_FAST);
// 3. Time Base Configuration: PWM Frequency = 16MHz / (15+1) / (999+1) = 1kHz
TIM1_TimeBaseInit(1, TIM1_COUNTERMODE_UP, 639, 0);
// 4. Channel 1 Configuration (PWM1 Mode)
TIM1_OC1Init(TIM1_OCMODE_PWM1,
TIM1_OUTPUTSTATE_ENABLE,
TIM1_OUTPUTNSTATE_ENABLE, // Enable complementary
500, // Duty Cycle (CCR1)
TIM1_OCPOLARITY_HIGH,
TIM1_OCNPOLARITY_HIGH,
TIM1_OCIDLESTATE_RESET,
TIM1_OCNIDLESTATE_RESET);
// 5. Channel 2 Configuration (PWM1 Mode)
TIM1_OC2Init(TIM1_OCMODE_PWM1,
TIM1_OUTPUTSTATE_ENABLE,
TIM1_OUTPUTNSTATE_ENABLE, // Enable complementary
250, // Duty Cycle (CCR2)
TIM1_OCPOLARITY_HIGH,
TIM1_OCNPOLARITY_HIGH,
TIM1_OCIDLESTATE_RESET,
TIM1_OCNIDLESTATE_RESET);
// 6. Dead Time Configuration (e.g., 10 ticks)
TIM1->DTR = 0x30;
// 7. Enable Main Output
TIM1_CtrlPWMOutputs(ENABLE);
// 8. Enable Timer
TIM1_Cmd(ENABLE);
}
Solved! Go to Solution.
2026-03-30 11:50 PM
@Peter BENSCH wrote:... and while we're at it: if you paste code only after formatting it with a code formatter (e.g. with formatter.org/cpp-formatter, coding style Google, indent 2, column 80), community members can read and understand your code more easily and quickly.
Regards
/Peter
Hi, Peter. It is humbly expressed that I could not get you as I am not so familiar with this community,
However, regarding my original problem, I undertook relentless tests for several days. Lastly, I detected the bug. During a test, where GPIOC Pin 3 was not used, but configured. Only the function inv_on() and inv_off() with delay set after each, within a do-while loop was done to see if those functions are working well. It was observed that those functions were working well. But, unwittingly, I pressed the switch connected to GPIOC Pin 3, and saw that the problem appears. Then I moved the polling pin to other port. Now there is no issue. No de-bounce effect. Code is working flawlessly.
Thank you Peter.
Regards,
ankhola
2026-03-23 3:15 AM
There are several issues in your code; the main ones explaining the random PWM behaviour are:
TIM2->IER = TIM2_IER_UIE; // This actually (re‑)enables the update interrupt instead of disabling it.
GPIOC->DDR |= 0x06; // sets PC1, PC2 as outputs (ok), but not PC3
GPIOC->ODR &= 0xF9; // clears PC1, PC2 bits (mask 0b11111001)Suggested minimal fix to stop PWM cleanly when PC3 goes low:
void inv_off(void)
{
// Stop using TIM2 to modulate the sine
TIM2->IER &= ~TIM2_IER_UIE; // disable update interrupt
TIM2->CR1 &= ~TIM2_CR1_CEN; // stop timer 2 (optional but cleaner)
TIM1_CtrlPWMOutputs(DISABLE); // Disable all PWM outputs from TIM1
TIM1->CR1 &= ~TIM1_CR1_CEN;
TIM1->CCER1 &= ~(TIM1_CCER1_CC1E | TIM1_CCER1_CC1NE |
TIM1_CCER1_CC2E | TIM1_CCER1_CC2NE);
// Force bridge pins to a defined low state
GPIOB->DDR |= 0x03;
GPIOB->CR1 |= 0x03;
GPIOB->ODR &= ~0x03;
GPIOC->DDR |= 0x06; // only PC1, PC2 as outputs
GPIOC->CR1 |= 0x06;
GPIOC->ODR &= ~0x06;
// Wait until PC3 becomes high again
do {
sw_state();
} while (on_state == 0);
}
And in TIM2_OC_Init() do not re‑enable the interrupt from elsewhere; only enable it when you really want PWM to run.
Hope this helps?
Regards
/Peter
2026-03-24 8:24 AM
Thanks Peter for spending your valuable time and patience to support a newbie. The mistake to disable TIM2 interrupt, clearing UIE bit was detected after uploading the post, but the error in configuring GPIOC in inv_off() function has been escaped. I have modified the code inserting your suggested snippet of code, but the problem still persists. It is not clear to me as to how TIM1 counter continues when it is stopped using TIM1->CR1 &= ~TIM1_CR1_CEN. It is noticed that the problem occurs while switching off PWM, For starting, there is no problem. It is also noticed that sometimes when switched off, continuous PWM pulse is seen with fixed duty, no variation in width of pulse. It shows that TIM2 interrupt is stopped, hence the last retrieved value of the sine table continues. If there is noise in switch on/off, effect will last for some cycles, then it should work as per position of switch. But it appears that control is stuck to a position as the pattern of pulse is not changed. I like to share a video of the pulse train captured in oscilloscope, but due to ignorance of uploading video, I failed.
Regards,
ankhola
2026-03-24 8:55 AM
From your description the timers themselves seem to behave correctly; what you are seeing is most likely caused by the switch handling / state machine rather than TIM1 ignoring CEN.
Please check:
do { sw_state(); } while (...)Maybe someone can make you a suggestion of a minimal start/stop sequence if you can share the updated code and which STM8 part and pins you use for CH1/CH1N/CH2/CH2N and the switch.
2026-03-24 9:00 AM
... and while we're at it: if you paste code only after formatting it with a code formatter (e.g. with formatter.org/cpp-formatter, coding style Google, indent 2, column 80), community members can read and understand your code more easily and quickly.
Regards
/Peter
2026-03-30 11:50 PM
@Peter BENSCH wrote:... and while we're at it: if you paste code only after formatting it with a code formatter (e.g. with formatter.org/cpp-formatter, coding style Google, indent 2, column 80), community members can read and understand your code more easily and quickly.
Regards
/Peter
Hi, Peter. It is humbly expressed that I could not get you as I am not so familiar with this community,
However, regarding my original problem, I undertook relentless tests for several days. Lastly, I detected the bug. During a test, where GPIOC Pin 3 was not used, but configured. Only the function inv_on() and inv_off() with delay set after each, within a do-while loop was done to see if those functions are working well. It was observed that those functions were working well. But, unwittingly, I pressed the switch connected to GPIOC Pin 3, and saw that the problem appears. Then I moved the polling pin to other port. Now there is no issue. No de-bounce effect. Code is working flawlessly.
Thank you Peter.
Regards,
ankhola