#include "stm32c0xx_hal.h"
#include <string.h>
// ─── HANDLES ──────────────────────────────────────────────────────────────────
I2C_HandleTypeDef hi2c1;
TIM_HandleTypeDef htim3;
// ─── DEFINES ──────────────────────────────────────────────────────────────────
#define LCD_ADDR (0x27 << 1)
#define LED_PIN GPIO_PIN_0 // PA0
#define BUTTON_PIN GPIO_PIN_1 // PA1
#define SERVO_PIN GPIO_PIN_4 // PA4 → TIM3_CH2 AF1
// ─── LCD (I2C, 4-bit, PCF8574) ───────────────────────────────────────────────
void LCD_SendNibble(uint8_t nibble, uint8_t rs) {
uint8_t base = (nibble << 4) | (rs ? 0x01 : 0x00) | 0x08;
uint8_t en_hi = base | 0x04;
uint8_t en_lo = base & ~0x04;
HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDR, &en_hi, 1, 100);
HAL_Delay(1);
HAL_I2C_Master_Transmit(&hi2c1, LCD_ADDR, &en_lo, 1, 100);
}
void LCD_SendByte(uint8_t byte, uint8_t rs) {
LCD_SendNibble(byte >> 4, rs);
LCD_SendNibble(byte & 0x0F, rs);
HAL_Delay(2);
}
void LCD_Cmd(uint8_t cmd) { LCD_SendByte(cmd, 0); }
void LCD_Char(uint8_t c) { LCD_SendByte(c, 1); }
void LCD_Init(void) {
HAL_Delay(50);
LCD_SendNibble(0x03, 0); HAL_Delay(5);
LCD_SendNibble(0x03, 0); HAL_Delay(1);
LCD_SendNibble(0x03, 0); HAL_Delay(1);
LCD_SendNibble(0x02, 0); HAL_Delay(1);
LCD_Cmd(0x28); // 2 lines, 5x8
LCD_Cmd(0x0C); // display ON, cursor OFF
LCD_Cmd(0x06); // auto-increment
LCD_Cmd(0x01); // clear
HAL_Delay(5);
}
void LCD_SetCursor(uint8_t row, uint8_t col) {
LCD_Cmd((row == 0 ? 0x80 : 0xC0) + col);
}
void LCD_Print(const char *s) {
while (*s) LCD_Char((uint8_t)*s++);
}
void LCD_Clear(void) { LCD_Cmd(0x01); HAL_Delay(5); }
// ─── SERVO (TIM3 CH2 @ PA4, 50 Hz) ──────────────────────────────────────────
// Prescaler=11 → 12 MHz / 12 = 1 MHz tick
// Period=19999 → 1 MHz / 20000 = 50 Hz
// Pulse 1000 → 1.0 ms = 0° (CERRADO)
// Pulse 1500 → 1.5 ms = 90° (ABIERTO)
void Servo_Set(uint8_t open) {
__HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_2, open ? 1500 : 1000);
}
// ─── GPIO INIT ───────────────────────────────────────────────────────────────
void GPIO_Init(void) {
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitTypeDef g = {0};
// PA0 - LED output
g.Pin = LED_PIN;
g.Mode = GPIO_MODE_OUTPUT_PP;
g.Pull = GPIO_NOPULL;
g.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &g);
// PA1 - Button input pull-up
g.Pin = BUTTON_PIN;
g.Mode = GPIO_MODE_INPUT;
g.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &g);
// PA4 - TIM3_CH2 AF1 (Servo PWM)
g.Pin = SERVO_PIN;
g.Mode = GPIO_MODE_AF_PP;
g.Pull = GPIO_NOPULL;
g.Speed = GPIO_SPEED_FREQ_LOW;
g.Alternate = GPIO_AF1_TIM3;
HAL_GPIO_Init(GPIOA, &g);
// PB8 - I2C1_SCL (D15)
// PB9 - I2C1_SDA (D14)
g.Pin = GPIO_PIN_8 | GPIO_PIN_9;
g.Mode = GPIO_MODE_AF_OD;
g.Pull = GPIO_NOPULL;
g.Speed = GPIO_SPEED_FREQ_HIGH;
g.Alternate = GPIO_AF6_I2C1;
HAL_GPIO_Init(GPIOB, &g);
}
// ─── I2C1 INIT (PB8=SCL, PB9=SDA) ───────────────────────────────────────────
void I2C_Init(void) {
__HAL_RCC_I2C1_CLK_ENABLE();
hi2c1.Instance = I2C1;
hi2c1.Init.Timing = 0x00303D5B;
hi2c1.Init.OwnAddress1 = 0;
hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
HAL_I2C_Init(&hi2c1);
}
// ─── TIM3 PWM INIT ───────────────────────────────────────────────────────────
void TIM3_Init(void) {
__HAL_RCC_TIM3_CLK_ENABLE();
htim3.Instance = TIM3;
htim3.Init.Prescaler = 11;
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 20000 - 1;
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
HAL_TIM_PWM_Init(&htim3);
TIM_OC_InitTypeDef oc = {0};
oc.OCMode = TIM_OCMODE_PWM1;
oc.Pulse = 1000; // 0° al inicio (CERRADO)
oc.OCPolarity = TIM_OCPOLARITY_HIGH;
oc.OCFastMode = TIM_OCFAST_DISABLE;
HAL_TIM_PWM_ConfigChannel(&htim3, &oc, TIM_CHANNEL_2);
HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_2);
}
// ─── PANTALLA ────────────────────────────────────────────────────────────────
void Show_Cerrado(void) {
LCD_Clear();
LCD_SetCursor(0, 3); LCD_Print("ESTADO:");
LCD_SetCursor(1, 3); LCD_Print("CERRADO");
}
void Show_Abierto(void) {
LCD_Clear();
LCD_SetCursor(0, 3); LCD_Print("ESTADO:");
LCD_SetCursor(1, 3); LCD_Print("ABIERTO");
}
// ─── MAIN ─────────────────────────────────────────────────────────────────────
int main(void) {
HAL_Init();
GPIO_Init();
I2C_Init();
TIM3_Init();
LCD_Init();
// Estado inicial → CERRADO (0°)
HAL_GPIO_WritePin(GPIOA, LED_PIN, GPIO_PIN_RESET);
Servo_Set(0);
Show_Cerrado();
uint8_t estado = 0; // 0=cerrado, 1=abierto
uint8_t btn_prev = 1; // pull-up: reposo HIGH
while (1) {
uint8_t btn_now = HAL_GPIO_ReadPin(GPIOA, BUTTON_PIN);
if (btn_prev == 1 && btn_now == 0) { // flanco de bajada
HAL_Delay(20); // debounce
estado ^= 1; // toggle
if (estado) {
// ── ABIERTO → LED ON, servo 90°, LCD "ABIERTO" ──
HAL_GPIO_WritePin(GPIOA, LED_PIN, GPIO_PIN_SET);
Servo_Set(1);
Show_Abierto();
} else {
// ── CERRADO → LED OFF, servo 0°, LCD "CERRADO" ──
HAL_GPIO_WritePin(GPIOA, LED_PIN, GPIO_PIN_RESET);
Servo_Set(0);
Show_Cerrado();
}
}
btn_prev = btn_now;
HAL_Delay(10);
}
}