/**
******************************************************************************
* @file : main.c
* @author : Fernando Hermosillo Reynoso
* @brief : Main program body
******************************************************************************
*
* 2. Realice un programa para un cronómetro digital.
* a. El sistema cuenta con un botón que al
* presionarlo se iniciará el cronometro y
* si se vuelve a presionar el botón se detendrá.
* b. La base de tiempo del cronómetro será de 1ms y
* debe ser implementado por medio de un timer e
* interrupción.
* c. El valor del tiempo del cronometro se mostrará en
* una LCD16x2 y se actualizará cada 10ms con el
* formato mm:ss:millis
* d. El cronómetro se reinicia cada vez que se inicia
* la cuenta.
*
* La LCD esta conectada a
* D0-D7: PA1 - PA8
* RS: PA9
* RW: PA10
* E: PA11
*/
#include <stdint.h>
#include <stdio.h> // Para sprintf
#include "stm32f103_hal.h"
#include "lcd.h"
/* Variables Globales (Volatile porque cambian en interrupción) */
volatile uint16_t m_milis = 0;
volatile uint8_t m_seg = 0;
volatile uint8_t m_min = 0;
volatile uint8_t cronometro_activo = 0;
volatile uint8_t flag_update_lcd = 0;
/* Funciones de tiempo necesarias para lcd.c */
void delay_ms(uint16_t ms) {
for (uint16_t i = 0; i < ms; i++) {
for (volatile unsigned long t = 0; t < 800; t++);
}
}
void delay_us(uint16_t us) {
for (volatile unsigned int cycles = 0; cycles < us; cycles++);
}
/* Interrupción del Timer 3 (Cada 1ms) */
void TIM3_IRQHandler(void) {
if (TIM3->SR & TIM_SR_UIF) {
TIM3->SR &= ~TIM_SR_UIF; // Bajar bandera de interrupción
// CONMUTAR PB1 para el osciloscopio
gpio_bitwise_toggle(GPIOB, (1 << 1));
if (cronometro_activo) {
m_milis++;
if (m_milis >= 1000) {
m_milis = 0;
m_seg++;
if (m_seg >= 60) {
m_seg = 0;
m_min++;
}
}
}
// Bandera para actualizar LCD cada 10ms (inciso c)
static uint8_t div_lcd = 0;
if (++div_lcd >= 10) {
div_lcd = 0;
flag_update_lcd = 1;
}
}
}
int main(void) {
char buffer[16];
// 1. Relojes
rcc_clock_enable(RCC_GPIOA);
rcc_clock_enable(RCC_GPIOB); // Para el botón
rcc_clock_enable(RCC_TIM3);
// 2. Configurar Botón en PB0 (Entrada con Pull-Up)
gpio_set_input(GPIOB, 0, GPIO_INPUT_PU);
// 2.1 Configurar PB1 para el osciloscopio
gpio_set_output(GPIOB, 1, GPIO_OUTPUT_PP, GPIO_SPEED_50MHZ);
//2.2 Para poder ver la interrupción en el osiloscopio
gpio_set_output(GPIOB, 10, GPIO_OUTPUT_PP, GPIO_SPEED_50MHZ);
// 3. Inicializar LCD (Usa PA1-PA11 según tu lcd.h)
LCD_Init();
LCD_Print("CRONOMETRO");
// 4. Configurar Timer 3 para 1ms
// Frecuencia 8MHz -> PSC=7 (divide entre 8), ARR=999 (cuenta 1000)
timer_init(TIM3, TIMER_MODE_UP, 999, 7);
timer_set_interrupt(TIM3, TIMER_IRQ_UPDATE, IRQ_ENABLE);
nvic_enable_irq(TIM3_IRQn);
nvic_enable();
timer_start(TIM3, TIMER_CONTINUOUS);
uint8_t boton_presionado = 0;
while (1) {
// 1. Espejo del botón: Si IDR bit 0 es 1 (suelto), ponemos PB10 en alto.
if (GPIOB->IDR & (1 << 0)) {
gpio_bitwise_set(GPIOB, (1 << 10));
} else {
gpio_bitwise_clear(GPIOB, (1 << 10));
}
// Lógica del botón (Toggle con antirrebotebásico)
// PB0 es 0 cuando se presiona (Pull-up interno)
if (!(GPIOB->IDR & (1 << 0))) {
if (!boton_presionado) {
cronometro_activo = !cronometro_activo;
// Si arranca, reiniciar variables (inciso d)
if (cronometro_activo) {
m_milis = 0; m_seg = 0; m_min = 0;
LCD_SetCursor(0, 0);
LCD_Print("CONTANDO... ");
} else {
LCD_SetCursor(0, 0);
LCD_Print("PAUSADO ");
}
boton_presionado = 1;
delay_ms(200); // Simple debounce
}
} else {
boton_presionado = 0;
}
// Actualizar pantalla cada 10ms (inciso c)
if (flag_update_lcd) {
flag_update_lcd = 0;
// Formato mm:ss:millis con ceros a la izquierda
sprintf(buffer, "%02d:%02d:%03d", m_min, m_seg, m_milis);
LCD_SetCursor(0, 1);
LCD_Print(buffer);
}
}
}