I'm struggling to get a correct behaviour from PA1 and PA2. Likely it has to do with my code but can't recognize the problem.
Through debugging i notice that PA2 always reads a zero, while it should be a pull-up input. PA1 on the other hand works correctly. So delay and other functions do work correctly.
PC1 does show a PWM and work correctly
PC2 does drive a led and works correctly
PC4 does drive a led and after the debounce time goes active, which is expected when PA2 reads always zero.
I actively disable PA1 and PA2 to be mapped as oscillator pins, but maybe i'm doing it wrong? PA2 seem to be stuck to zero.
I'm not using any board, just a bare chip, directly programmed by link-e. I'm also 100% sure there is no short between PA2 and GND.
I've tried on 2 samples and both show the same behaviour. So very likely my code is the reason and in some way PA2 always reads a zero.
Any help in solving this is appreciated.
Here is my code:
/***************************************************************
* File Name : main.c
* for ch32v003j4g6, soic8 package
* Description : create 50Hz pwm on pc1, with 2 buttons on pa1 and pa2, increment or decrement the pulse time of the pwm
* also control 2 leds, connected to pc2 and pc4
*
* programming pin connections
* pin 2 = gnd
* pin 4 = 3v3
* pin 8 = swdio
**************************************************************/
#include "ch32v00x.h"
#include "delay.h"
#include <stdint.h>
#define PWM_TIMER TIM2
#define PWM_FREQ 50 // 50Hz for servo
#define ZERO_ANGLE_PULSE 1500 // 1.5ms pulse width for 0 degrees
#define FULL_ANGLE_PULSE 2500 // 2.5ms pulse width for 180 degrees
#define MOVE_DURATION_MS 5000 // 5 seconds for full 90-degree motion
// Button pins
#define UP_PIN GPIO_Pin_1 // PA1
#define DOWN_PIN GPIO_Pin_2 // PA2
//PWM pin PC1 (mapping TIM2 CH4)
#define PWM_PIN GPIO_Pin_1 // PC1
// LED pins
#define GREEN_LED_PIN GPIO_Pin_2 // PC2
#define RED_LED_PIN GPIO_Pin_4 // PC4
/* Global Variable */
volatile uint8_t button1_state = 0xff; //0xff is the "not pushed" state
volatile uint8_t button2_state = 0xff; //0xff is the "not pushed" state
/*
// Initialize PWM on PA1 (default mapping is Timer 1 Channel 2 to PA1)
void pwm_init(void) {
// Enable clocks for TIM1 and GPIOA
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1 | RCC_APB2Periph_GPIOA, ENABLE);
// Enable AF clock to get to tim1 + undo any possible pin mapping for PA1 and PA2 back to default mapping
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_PA1_2, DISABLE);
// Configure PA1 as alternate function push-pull
GPIO_InitTypeDef GPIO_InitStructure = {0};
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; // PA1
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// Configure TIM1 for PWM
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = {0};
TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock / (PWM_FREQ * 20000) - 1;
TIM_TimeBaseStructure.TIM_Period = 20000;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //no division
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
TIM_OCInitTypeDef TIM_OCInitStructure = {0};
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = ZERO_ANGLE_PULSE;
TIM_OC2Init(TIM1, &TIM_OCInitStructure); // Use Channel 2
// Enable TIM1 outputs and start the timer
TIM_CtrlPWMOutputs(TIM1, ENABLE);
TIM_Cmd(TIM1, ENABLE);
}
// Initialize GPIO for buttons and LEDs
void gpio_init(void) {
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure = {0};
// Buttons: PC1 (UP) and PA2 (DOWN) as inputs with pull-up
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_UP;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_PIN_DOWN;
GPIO_Init(GPIOA, &GPIO_InitStructure);
// LEDs: PC2 (Green) and PC4 (Red) as outputs
GPIO_InitStructure.GPIO_Pin = GREEN_LED_PIN | RED_LED_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure);
GPIO_ResetBits(GPIOC, GREEN_LED_PIN | RED_LED_PIN); // Turn off LEDs initially
}
*/
// Function to configure GPIO
void gpio_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
// activate GPIO clocks + Alternate Function clock
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOC, ENABLE);
// make sure PA1 and PA2 are not remapped to OSC
GPIO_PinRemapConfig(GPIO_Remap_PA1_2, DISABLE);
//LEDS on PA1 and PA2
GPIO_InitStructure.GPIO_Pin = UP_PIN | DOWN_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//LEDS on PC2 and PC4
GPIO_InitStructure.GPIO_Pin = RED_LED_PIN | GREEN_LED_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// Turn off LEDs initially
GPIO_ResetBits(GPIOC, GREEN_LED_PIN | RED_LED_PIN);
}
// Function to configure PWM
void pwm_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure = {0};
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure = {0};
TIM_OCInitTypeDef TIM_OCInitStructure = {0};
// Enable Timer2 clock and gpioc clock
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC, ENABLE );
// set PC1 as AF to TIM2 CH4 => Pin as Alternate Function Push-Pull
GPIO_InitStructure.GPIO_Pin = PWM_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_30MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// Timer2 base configuration for PWM
TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock / (PWM_FREQ * 20000) - 1;
TIM_TimeBaseStructure.TIM_Period = 20000;
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //no division
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(PWM_TIMER, &TIM_TimeBaseStructure);
// PWM Channel 1 configuration
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
TIM_OCInitStructure.TIM_Pulse = ZERO_ANGLE_PULSE + 500; // Initial duty cycle
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;
TIM_OC1Init(PWM_TIMER, &TIM_OCInitStructure); // Channel 1 for PWM
TIM_CtrlPWMOutputs(PWM_TIMER, ENABLE );
TIM_OC1PreloadConfig(PWM_TIMER, TIM_OCPreload_Enable); // Channel 1 for PWM
// Enable TIM2 Preload register on ARR
TIM_ARRPreloadConfig(PWM_TIMER, ENABLE);
// Enable PWM_TIMER
TIM_Cmd(PWM_TIMER, ENABLE);
//remap PC1 as TIM2 CH1
GPIO_PinRemapConfig(GPIO_PartialRemap2_TIM2, ENABLE);
}
int main(void)
{
SystemCoreClockUpdate(); // Initialize system clocks and peripherals at 48MHz
delay_init();
gpio_init();
pwm_init();
uint32_t lastmills_button = getmills();
uint32_t lastmills_servo = getmills();
uint16_t pulse_width = 500;
while(1) {
//debounce the buttons
if ((getmills() - lastmills_button) > 1){
button1_state = (button1_state<<1) | GPIO_ReadInputDataBit(GPIOA, UP_PIN);
button2_state = (button2_state<<1) | GPIO_ReadInputDataBit(GPIOA, DOWN_PIN);
lastmills_button = getmills();
}
if ((getmills() - lastmills_servo) > 50) {
//act on button state. 0x00 means the button is pressed. 0xff means the button is released. any other value is debouncing noise
if (button1_state == 0x00) { //button is pressed
GPIO_SetBits(GPIOC, GREEN_LED_PIN);
pulse_width++;
} else if (button1_state == 0xff) {
GPIO_ResetBits(GPIOC, GREEN_LED_PIN);
}
if (button2_state == 0x00) { //button is pressed
GPIO_SetBits(GPIOC, RED_LED_PIN);
pulse_width--;
} else if (button2_state == 0xff) {
GPIO_ResetBits(GPIOC, RED_LED_PIN);
}
if (pulse_width > 999) { pulse_width = 0; }
// TIM_SetCompare1(PWM_TIMER, ZERO_ANGLE_PULSE + pulse_width); // Channel 1
lastmills_servo = getmills();
}
}
}