A4M6-PWM-2timers and random port
My Christmas Ornament lighting.
My Christmas Ornament lighting.
/********************************************************
* Christmas Ornaments lighting
* File Name : main.c A4M6 16 pin, got tim1/tim2 working on A1/C1
* Author : Sherm
* Version : V1.0.0
* Date : 2025/12/02
* Description : uses tim1(PA1) and tim2(PC1) and random (PD4) for tree ornaments
*Master:USART1_Tx(PD5)\USART1_Rx(PD6)
as of 12/3/25 Standby kind of works. I got it to wake, but it affect PD4 and somtimes
PC1 (pin 1) stays on, something to do with pwm not being shut down properly.
as of 12/3/25 1914hrs - Fixed the PD4 issue, it no longer dime. It was in GPIOConfig()
that had the issue, It had PC1 PP and not PD4. It still sometimes sleep with PC1 light, again
probably a timer issue and not shut down proper.
as of 12/3/25 2016hrs - Still TIM2 on PC1 sometimes shuts down and sometimes does not
after trying everything AI said (ai sucks) no luck. But still this is more energy efficient now.
as of 12/3/25 2042hrs - finally solved from chatgpt's 3rd try:
see function void PWM2_Stop_PC1_Safe(void)
Why this works
PWM1 output logic:
HIGH when CNT < CH2CVR
LOW when CNT ≥ CH2CVR
If you stop TIM2 when the output is HIGH → hardware latch stays HIGH, even after switching to GPIO
Waiting for CNT ≥ CH2CVR guarantees the output has gone LOW internally
Disabling AF afterward ensures GPIO fully takes over
This produces 100% reliable LOW output, every time.
A4M6
____
PC1 - 1| |16 PC0
PC2 - 2| |15 VCC
PC3 - 3| |14 VSS
PC4 - 4| |13 PA2
PC1 - 5| |12 PA1
PC2 - 6| |11 PD7
PD1/SWIO- 7| |10 PD6
PD4 - 8| |9 PD5
----
*/
#include "debug.h"
#include "stdlib.h"
#define UP 1
#define DOWN 0
#define SLEEP_ENABLED
#define SLP_time 9 //9=4.3 sec (formula SLP_time * 480 = sleep time in milli sec)
#define LOOPS_till_sleep 3 //after TIM1 cycles up and down is one loop.
//#define MODE_Debug
void USARTx_CFG(void);
void init_Tim1PWM(uint16_t PRSC, uint16_t ARR, uint16_t CCR);
void init_Tim2PWM(uint16_t PRSC, uint16_t ARR, uint16_t CCR);
void PinInit(void);
void PinPullUp(void);
void ccr_set_tim1(uint16_t CCR);
void ccr_set_tim2(uint16_t CCR);
void killOUTpins(void);
void GPIOConfig(void);
void EXTI9_INT_INIT(void);
void standbyConfig(void);
void PWM2_Stop_PC1_Safe(void);
u16 prescale=6; //tim1 4 seems best with below settings (higher the faster)
u16 prescale2=20; //tim2
#define ARR_1 2300ul
#define ARR_2 1800ul
//u16 arr=ARR_1; //tim1 higher is slower 2300 seems good
//u16 arr2=ARR_2; //tim2 1800 seems good
u16 ccr=1; //mostly controled elseware
u8 upDown = UP;
u8 upDown2 = UP;
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
SystemCoreClockUpdate();
Delay_Init();
#ifdef MODE_Debug
SDI_Printf_Enable();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n",SystemCoreClock);
printf( "ChipID:%08x\r\n", DBGMCU_GetCHIPID() );
Delay_Ms(2000);
#endif
PinPullUp();
PinInit();
GPIO_WriteBit(GPIOD, GPIO_Pin_4, Bit_SET);
#ifdef SLEEP_ENABLED
GPIOConfig();
EXTI9_INT_INIT(); //Internal wake-up triggered by AWU //commented 11/19 still worked for button
standbyConfig(); //Auto wakeup (LSI), if disabled, EXTI9_INT_INIT() can be disabled, too.
Delay_Ms(4000); //in case something goes wrong with sleep
u8 countTillSleep = 0;
#endif
Delay_Ms(1000);
GPIO_WriteBit(GPIOD, GPIO_Pin_4, Bit_RESET);
u16 count_upDown = 0;
u16 countLoop = 0;
u16 count_upDown2 = 0;
u8 ranOnCount=0;
u16 randNum = 0;
while(1)
{
//tim1
if(upDown == UP){
count_upDown++;
if(count_upDown>=ARR_1){
upDown=DOWN;
}
}else{ //down
count_upDown--;
if(count_upDown<=0){
upDown=UP;
#ifdef SLEEP_ENABLED
countTillSleep++; //Put here because is the biggest loop
if(countTillSleep>=LOOPS_till_sleep){
countTillSleep=0;
killOUTpins();
//Delay_Ms(5000);
PWM2_Stop_PC1_Safe(); //must have or will light when pwm high then sleep
PWR_EnterSTANDBYMode(PWR_STANDBYEntry_WFE);
}
#endif
}
}
//tim2:
if(upDown2 == UP){
count_upDown2++;
if(count_upDown2>=ARR_2){
upDown2=DOWN;
}
}else{ //down
count_upDown2--;
if(count_upDown2<=0){
upDown2=UP;
}
}
//Random LED flash
if(countLoop>=randNum){
randNum = ((rand()%ARR_2)+6)/4;
countLoop=0;
if(ranOnCount>3){
//GPIO_WriteBit(GPIOD, GPIO_Pin_3, (i == 0) ? (i = Bit_SET) : (i = Bit_RESET));
GPIO_WriteBit(GPIOD, GPIO_Pin_4, Bit_RESET);
ranOnCount=0;
}else{
GPIO_WriteBit(GPIOD,GPIO_Pin_4,Bit_SET);
}
ranOnCount++;
}
init_Tim1PWM(prescale,ARR_1,count_upDown);
init_Tim2PWM(prescale2,ARR_2,count_upDown2);
countLoop++;
#ifdef MODE_Debug
if(count_upDown%100==0){
printf("count loop=%d updown=%d updown2=%d\r\n",countLoop,count_upDown,count_upDown2);
}
#endif
Delay_Us(400);
}
}
void killOUTpins(void){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef g={0};
g.GPIO_Pin=GPIO_Pin_1;
g.GPIO_Mode=GPIO_Mode_AF_PP;
g.GPIO_Speed=GPIO_Speed_2MHz; //changed from 30mhz to 2 and that dropped sleep to 8uA!!!
GPIO_Init(GPIOA,&g);
g.GPIO_Pin=GPIO_Pin_1;
g.GPIO_Mode=GPIO_Mode_AF_PP;
g.GPIO_Speed=GPIO_Speed_2MHz; //changed from 30mhz to 2 and that dropped sleep to 8uA!!!
GPIO_Init(GPIOC,&g);
GPIO_WriteBit(GPIOC, GPIO_Pin_1, Bit_RESET);
GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
GPIO_WriteBit(GPIOD, GPIO_Pin_4, Bit_RESET);
}
//pa1 appears to work!! Timer 1
void init_Tim1PWM(uint16_t PRSC, uint16_t ARR, uint16_t CCR){
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1|RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef g={0};
g.GPIO_Pin=GPIO_Pin_1;
g.GPIO_Mode=GPIO_Mode_AF_PP;
g.GPIO_Speed=GPIO_Speed_2MHz; //changed from 30mhz to 2 and that dropped sleep to 8uA!!!
GPIO_Init(GPIOA,&g);
TIM_TimeBaseInitTypeDef t={0};
TIM_Cmd( TIM1, DISABLE ); //added 12/4/25 1432
t.TIM_Period=ARR;
t.TIM_Prescaler=PRSC;
t.TIM_ClockDivision=TIM_CKD_DIV4;
t.TIM_CounterMode=TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1,&t);
TIM_OCInitTypeDef oc; //pwm on ch2
oc.TIM_OCMode=TIM_OCMode_PWM1;
oc.TIM_OutputState=TIM_OutputState_Enable;
oc.TIM_Pulse=CCR;
oc.TIM_OCPolarity=TIM_OCPolarity_High;
TIM_OC2Init(TIM1,&oc);
TIM_OC2PreloadConfig(TIM1, TIM_OCPreload_Enable);
TIM_CtrlPWMOutputs(TIM1,ENABLE);
TIM_Cmd(TIM1,ENABLE);
}
void init_Tim2PWM(uint16_t PRSC, uint16_t ARR, uint16_t CCR) //PC1
{
TIM_OCInitTypeDef TIM_OCInitStructure={0};
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure={0};
//This worked and enabled PC1 on tim2
// Disable the external oscillator function on PA1/PA2
GPIO_PinRemapConfig(AFIO_PCFR1_PA12_REMAP, DISABLE);
// 1. Enable clocks for relevant peripherals (AFIO, GPIO ports, Timer)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 2. Enable the full remap for TIM2 (example: moves output to a different pin)
GPIO_PinRemapConfig(GPIO_FullRemap_TIM2, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB1Periph_TIM2, ENABLE);
//no luck on these: (C1 worked)
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; //changed from 30
GPIO_Init(GPIOC, &GPIO_InitStructure );
//tim2: works on PC1 only
TIM_Cmd( TIM2, DISABLE );
TIM_TimeBaseInitStructure.TIM_Period = ARR;
TIM_TimeBaseInitStructure.TIM_Prescaler = PRSC;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //mabe reverse? not tested
TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure);
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = CCR;
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;//this does inverse of tim1 by using low
TIM_OC1Init( TIM2, &TIM_OCInitStructure );
TIM_CtrlPWMOutputs(TIM2, ENABLE );
TIM_OC1PreloadConfig( TIM2, TIM_OCPreload_Enable );
TIM_ARRPreloadConfig( TIM2, ENABLE );
TIM_Cmd( TIM2, ENABLE );
}
/*
Why this works
PWM1 output logic:
HIGH when CNT < CH2CVR
LOW when CNT ≥ CH2CVR
If you stop TIM2 when the output is HIGH → hardware latch stays HIGH, even after switching to GPIO
Waiting for CNT ≥ CH2CVR guarantees the output has gone LOW internally
Disabling AF afterward ensures GPIO fully takes over
This produces 100% reliable LOW output, every time.
*/
void PWM2_Stop_PC1_Safe(void)
{
/* 1. WAIT FOR PWM OUTPUT TO GO LOW
In PWM1 mode: output HIGH while CNT < CH2CVR.
*/
while (TIM2->CNT < TIM2->CH2CVR)
{
// wait until PWM output naturally reaches LOW
}
/* 2. STOP TIMER AND PWM OUTPUT */
TIM_CtrlPWMOutputs(TIM2, DISABLE);
TIM_Cmd(TIM2, DISABLE);
/* 3. DISABLE ALTERNATE FUNCTION ON PC1 (TIM2_CH2) */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
AFIO->PCFR1 &= ~(1 << 2); // disable AF for TIM2_CH2 on PC1
/* 4. SET PC1 AS NORMAL GPIO OUTPUT */
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitTypeDef gpio = {0};
gpio.GPIO_Pin = GPIO_Pin_1;
gpio.GPIO_Mode = GPIO_Mode_Out_PP;
gpio.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init(GPIOC, &gpio);
/* 5. FORCE PC1 LOW */
GPIO_ResetBits(GPIOC, GPIO_Pin_1);
}
void PinInit(void){
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD| RCC_APB2Periph_TIM1, ENABLE);
//D
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
void PinPullUp(void){
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA| RCC_APB2Periph_TIM1, ENABLE);
//D
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //Apply the settings on all pins under the selected port
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //Internal pull-up -> Contributes to power saving
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
void GPIOConfig()
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC | RCC_APB2Periph_GPIOD, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_All; //Apply the settings on all pins under the selected port
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //Internal pull-up -> Contributes to power saving
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_Init(GPIOD, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4; //PC1 - LED pin
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //Push-pull ("toggleable") configuration
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz; //Lowest speed
GPIO_Init(GPIOD, &GPIO_InitStructure);
}
void EXTI2_INT_INIT(void)
{
EXTI_InitTypeDef EXTI_InitStructure = {0};
NVIC_InitTypeDef NVIC_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE);
GPIO_EXTILineConfig(GPIO_PortSourceGPIOA, GPIO_PinSource2); //PA2
EXTI_InitStructure.EXTI_Line = EXTI_Line2; //PA2
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling; //Since the pin is configured as IPU, it should be pulled down by the button
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
NVIC_InitStructure.NVIC_IRQChannel = EXTI7_0_IRQn; //Combined interrupt handler for GPIO pins (interrupt lines) 0-7
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; //Highest interrupt priority (among competing interrupts)
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //Second highest sub-priority (among competing interrupts)
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void EXTI9_INT_INIT(void)
{
EXTI_InitTypeDef EXTI_InitStructure = {0};
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
EXTI_InitStructure.EXTI_Line = EXTI_Line9; //Internal line, see reference manual 2.3.4 (AWU)
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Event;
EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;
EXTI_InitStructure.EXTI_LineCmd = ENABLE;
EXTI_Init(&EXTI_InitStructure);
}
void standbyConfig()
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
RCC_LSICmd(ENABLE); //128 kHz internal oscillator
while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET); //Wait until the oscillator becomes ready
PWR_AWU_SetPrescaler(PWR_AWU_Prescaler_61440); //128 kHz -> 1/128kHz/61400 = 480 ms
PWR_AWU_SetWindowValue(SLP_time); //24+1 is added by the code later. 25 * 480 = 12 s -> expected sleep time so 9=4.3 seconds
PWR_AutoWakeUpCmd(ENABLE); //Enable the auto-wakeup feature
}