/**
******************************************************************************
* @file : main.c
* @author : Auto-generated by STM32CubeIDE
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2026 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
#include <stdint.h>
#include "stm32f103xb.h"
/* H-bridge: IN1=PA1, IN2=PA2, ENA=PA0 */
#define IN1_PIN 1
#define IN2_PIN 2
#define EN_PIN 0
/* Buttons: DIR=PB0, RUN=PB1 */
#define BTN_DIR_PIN 0
#define BTN_RUN_PIN 1
static void delay_ms(uint16_t t){
for(uint16_t i=0;i<t;i++){
for(volatile uint32_t j=0;j<8000;j++){ __NOP(); }
}
}
/* ===== 7-seg on PC0..PC6 (a..g), common cathode ===== */
static void seg_clear(void){ GPIOC->BRR = 0x7F; }
static void display_S(void){ GPIOC->BSRR = (1<<0)|(1<<2)|(1<<3)|(1<<5)|(1<<6); } // a,c,d,f,g
static void display_L(void){ GPIOC->BSRR = (1<<3)|(1<<4)|(1<<5); } // d,e,f
static void display_R(void){ GPIOC->BSRR = (1<<2)|(1<<4)|(1<<6); } // c,e,g (aprox)
/* ===== Motor states ===== */
typedef enum { STOP=0, LEFT, RIGHT } state_t;
static state_t current = STOP;
static state_t selected = RIGHT;
static uint8_t running = 0;
static void motor_stop(void){
GPIOA->BRR = (1<<EN_PIN)|(1<<IN1_PIN)|(1<<IN2_PIN);
current = STOP;
}
static void motor_left(void){
GPIOA->BRR = (1<<IN1_PIN);
GPIOA->BSRR = (1<<IN2_PIN)|(1<<EN_PIN);
current = LEFT;
}
static void motor_right(void){
GPIOA->BRR = (1<<IN2_PIN);
GPIOA->BSRR = (1<<IN1_PIN)|(1<<EN_PIN);
current = RIGHT;
}
/* Dead time: STOP -> 10ms -> new direction */
static void change_state(state_t next){
if(next == current) return;
motor_stop();
delay_ms(10);
if(next == LEFT) motor_left();
else if(next == RIGHT) motor_right();
else motor_stop();
}
/* ===== Debounce edge detect ===== */
typedef struct { uint8_t stable; uint8_t last; } db_t;
static uint8_t read_pin(GPIO_TypeDef* port, uint8_t pin){
return (port->IDR & (1<<pin)) ? 1 : 0;
}
/* returns 1 only on rising edge (0->1) */
static uint8_t pressed_edge(db_t *db, GPIO_TypeDef* port, uint8_t pin){
uint8_t raw = read_pin(port, pin);
if(raw != db->last){
db->last = raw;
delay_ms(20); // debounce time
raw = read_pin(port, pin);
db->last = raw;
}
if(db->stable == 0 && raw == 1){
db->stable = 1;
return 1;
}
if(raw == 0) db->stable = 0;
return 0;
}
static void gpio_init(void){
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN;
/* PA0,PA1,PA2 output push-pull 2MHz */
GPIOA->CRL &= ~((0xF<<(EN_PIN*4)) | (0xF<<(IN1_PIN*4)) | (0xF<<(IN2_PIN*4)));
GPIOA->CRL |= ((0x2<<(EN_PIN*4)) | (0x2<<(IN1_PIN*4)) | (0x2<<(IN2_PIN*4)));
/* PB0,PB1 input pull-down */
GPIOB->CRL &= ~((0xF<<(BTN_DIR_PIN*4)) | (0xF<<(BTN_RUN_PIN*4)));
GPIOB->CRL |= ((0x8<<(BTN_DIR_PIN*4)) | (0x8<<(BTN_RUN_PIN*4)));
GPIOB->ODR &= ~((1<<BTN_DIR_PIN) | (1<<BTN_RUN_PIN));
/* PC0..PC6 output push-pull 2MHz */
for(int i=0;i<7;i++){
GPIOC->CRL &= ~(0xF<<(i*4));
GPIOC->CRL |= (0x2<<(i*4));
}
motor_stop();
seg_clear();
display_S();
}
int main(void){
gpio_init();
db_t dbDir = {0,0};
db_t dbRun = {0,0};
while(1){
if(pressed_edge(&dbDir, GPIOB, BTN_DIR_PIN)){
selected = (selected == RIGHT) ? LEFT : RIGHT;
if(running) change_state(selected);
}
if(pressed_edge(&dbRun, GPIOB, BTN_RUN_PIN)){
running = !running;
if(!running) change_state(STOP);
else change_state(selected);
}
seg_clear();
if(current == STOP) display_S();
else if(current == RIGHT) display_R();
else display_L();
delay_ms(5);
}
}