1. Architecture générale du système
1.1 Spécifications fonctionnelles
- Affichage temporel : heures, minutes, secondes (format 24h)
- Affichage de la date : année, mois, jour, jour de la semaine
- Enterface utilisateur : 4 boutons indépendants pour le réglage et la navigation
- Fonction réveil : programmation d'une alarme
- Affcihage visuel : écran LCD1602, LCD12864 ou OLED
1.2 Diagramme bloc fonctionnel
┌─────────────────────────────────────────────────────────────┐
│ Système Horloge Numérique STM32 │
├─────────────────────────────────────────────────────────────┤
│ Contrôleur STM32 │ Module │ Module │ Module │
│ (Unité principale)│ d'entrée │ d'affichage │ d'horloge │
│ │ │ │ │
│ • Logique │ • 4 Boutons │ • LCD1602 │ • DS1302 │
│ temporelle │ • Anti-rebond│ • LCD12864 │ • DS3231 │
│ • Gestion alarme │ • Appui long│ • OLED │ • RTC │
│ • Rafraîchissement│ / court │ │ │
│ affichage │ │ │ │
└─────────────────────────────────────────────────────────────┘
2. Conception du matériel
2.1 Schéma de connexion matériel
Liste des composants principaux
| Composant |
Modèle |
Quantité |
Fonction |
| Microcontrôleur |
STM32F103C8T6 |
1 |
Contrôle central |
| Écran d'affichage |
LCD1602 |
1 |
Affichage temps/date |
| Horloge temps réel |
DS1302 |
1 |
Horloge précise |
| Boutons poussoirs |
Interrupteurs tactiles |
4 |
Entrées utilisateur |
| Buzzer |
Buzzer actif |
1 |
Signalisation alarme |
| Quartz |
32.768 kHz |
1 |
Référence temporelle |
Table de connexion des broches
/******************** Définitions des connexions matérielles ********************/
// Broches GPIO pour les boutons
#define BTN_PORT GPIOA
#define BTN_MODE_PIN GPIO_Pin_0 // Bouton 1 : changement de mode
#define BTN_INC_PIN GPIO_Pin_1 // Bouton 2 : incrément
#define BTN_DEC_PIN GPIO_Pin_2 // Bouton 3 : décrément
#define BTN_OK_PIN GPIO_Pin_3 // Bouton 4 : validation/annulation
// Broches pour le LCD1602
#define LCD_REG_SELECT GPIO_Pin_8
#define LCD_READ_WRITE GPIO_Pin_9
#define LCD_ENABLE GPIO_Pin_10
#define LCD_DATA_BIT4 GPIO_Pin_11
#define LCD_DATA_BIT5 GPIO_Pin_12
#define LCD_DATA_BIT6 GPIO_Pin_13
#define LCD_DATA_BIT7 GPIO_Pin_14
// Broches pour le DS1302
#define RTC_CHIP_SELECT GPIO_Pin_4
#define RTC_DATA_LINE GPIO_Pin_5
#define RTC_CLOCK_LINE GPIO_Pin_6
// Broche du buzzer
#define ALARM_BUZZER GPIO_Pin_7
3. Conception logicielle
3.1 Définition des fonctionnalités des boutons
/******************** Énumérations pour les boutons et les modes ********************/
typedef enum {
BTN_FUNCTION_MODE = 0, // Fonction : changement de mode
BTN_FUNCTION_INC, // Fonction : incrémentation
BTN_FUNCTION_DEC, // Fonction : décrémentation
BTN_FUNCTION_OK // Fonction : validation
} ButtonFunction;
// Modes d'opération du système
typedef enum {
SYS_MODE_VIEW = 0, // Mode visualisation
SYS_MODE_SET_TIME, // Mode réglage de l'heure
SYS_MODE_SET_DATE, // Mode réglage de la date
SYS_MODE_SET_ALARM, // Mode réglage de l'alarme
SYS_MODE_ALARM_ACTIVE // Mode alarme active
} SystemMode;
// États pendant le réglage
typedef enum {
ADJUST_HOUR = 0, // Ajustement de l'heure
ADJUST_MINUTE, // Ajustement des minutes
ADJUST_SECOND, // Ajustement des secondes
ADJUST_YEAR, // Ajustement de l'année
ADJUST_MONTH, // Ajustement du mois
ADJUST_DAY, // Ajustement du jour
ADJUST_ALARM_HOUR, // Ajustement de l'heure de l'alarme
ADJUST_ALARM_MINUTE // Ajustement des minutes de l'alarme
} AdjustmentState;
3.2 Structures de données principales
/******************** Structure pour le temps ********************/
typedef struct {
uint8_t current_hour; // Heure (0-23)
uint8_t current_minute; // Minute (0-59)
uint8_t current_second; // Seconde (0-59)
uint8_t current_year; // Année (00-99)
uint8_t current_month; // Mois (1-12)
uint8_t current_day; // Jour (1-31)
uint8_t current_weekday; // Jour de la semaine (1-7)
} TimeData;
typedef struct {
uint8_t alarm_hour; // Heure de l'alarme
uint8_t alarm_minute; // Minute de l'alarme
uint8_t alarm_enabled; // Activation de l'alarme
} AlarmData;
// Variables globales du système
TimeData g_current_time;
AlarmData g_alarm_config;
SystemMode g_system_state = SYS_MODE_VIEW;
AdjustmentState g_adjust_target = ADJUST_HOUR;
uint8_t g_blink_state = 0; // État du clignotement
uint8_t g_alarm_triggered = 0; // Drapeau d'alarme déclenchée
3.3 Gestion des boutons avec anti-rebond
/**
* @file button_handler.c
* @brief Gestion des boutons avec traitement anti-rebond
*/
#include "stm32f10x.h"
#include "button_handler.h"
#define DEBOUNCE_DELAY_MS 10 // Délai anti-rebond
#define LONG_PRESS_THRESHOLD 1000 // Seuil pour appui long
static uint8_t button_states[4] = {1, 1, 1, 1};
static uint16_t press_durations[4] = {0};
/**
* @brief Initialisation des broches des boutons
*/
void Buttons_Init(void) {
GPIO_InitTypeDef gpio_config;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
gpio_config.GPIO_Pin = BTN_MODE_PIN | BTN_INC_PIN | BTN_DEC_PIN | BTN_OK_PIN;
gpio_config.GPIO_Mode = GPIO_Mode_IPU;
gpio_config.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(BTN_PORT, &gpio_config);
}
/**
* @brief Détection d'appui simple
* @return Code du bouton pressé, 0 si aucun
*/
uint8_t Detect_Button_Press(void) {
uint8_t pressed_button = 0;
// Vérification des différents boutons
if (GPIO_ReadInputDataBit(BTN_PORT, BTN_MODE_PIN) == RESET) {
Delay_Ms(DEBOUNCE_DELAY_MS);
if (GPIO_ReadInputDataBit(BTN_PORT, BTN_MODE_PIN) == RESET) {
while (GPIO_ReadInputDataBit(BTN_PORT, BTN_MODE_PIN) == RESET);
pressed_button = BTN_FUNCTION_MODE;
}
}
else if (GPIO_ReadInputDataBit(BTN_PORT, BTN_INC_PIN) == RESET) {
Delay_Ms(DEBOUNCE_DELAY_MS);
if (GPIO_ReadInputDataBit(BTN_PORT, BTN_INC_PIN) == RESET) {
while (GPIO_ReadInputDataBit(BTN_PORT, BTN_INC_PIN) == RESET);
pressed_button = BTN_FUNCTION_INC;
}
}
else if (GPIO_ReadInputDataBit(BTN_PORT, BTN_DEC_PIN) == RESET) {
Delay_Ms(DEBOUNCE_DELAY_MS);
if (GPIO_ReadInputDataBit(BTN_PORT, BTN_DEC_PIN) == RESET) {
while (GPIO_ReadInputDataBit(BTN_PORT, BTN_DEC_PIN) == RESET);
pressed_button = BTN_FUNCTION_DEC;
}
}
else if (GPIO_ReadInputDataBit(BTN_PORT, BTN_OK_PIN) == RESET) {
Delay_Ms(DEBOUNCE_DELAY_MS);
if (GPIO_ReadInputDataBit(BTN_PORT, BTN_OK_PIN) == RESET) {
while (GPIO_ReadInputDataBit(BTN_PORT, BTN_OK_PIN) == RESET);
pressed_button = BTN_FUNCTION_OK;
}
}
return pressed_button;
}
/**
* @brief Détection d'appui avec gestion court/long
* @param press_type Pointeur pour stocker le type d'appui (1: court, 2: long)
* @return Code du bouton détecté
*/
uint8_t Scan_Button_Press(uint8_t *press_type) {
uint8_t detected_btn = 0;
uint8_t btn_index;
static uint8_t previous_states[4] = {1, 1, 1, 1};
uint8_t current_states[4];
uint8_t btn_pins[4] = {BTN_MODE_PIN, BTN_INC_PIN, BTN_DEC_PIN, BTN_OK_PIN};
*press_type = 0;
for (btn_index = 0; btn_index < 4; btn_index++) {
current_states[btn_index] = GPIO_ReadInputDataBit(BTN_PORT, btn_pins[btn_index]);
// Début d'appui détecté
if (previous_states[btn_index] && !current_states[btn_index]) {
Delay_Ms(DEBOUNCE_DELAY_MS);
press_durations[btn_index] = 0;
}
// Appui maintenu
else if (!previous_states[btn_index] && !current_states[btn_index]) {
press_durations[btn_index]++;
if (press_durations[btn_index] >= (LONG_PRESS_THRESHOLD / DEBOUNCE_DELAY_MS)) {
*press_type = 2;
detected_btn = btn_index + 1;
press_durations[btn_index] = 0;
break;
}
}
// Fin d'appui
else if (!previous_states[btn_index] && current_states[btn_index]) {
if (press_durations[btn_index] < (LONG_PRESS_THRESHOLD / DEBOUNCE_DELAY_MS)) {
*press_type = 1;
detected_btn = btn_index + 1;
}
press_durations[btn_index] = 0;
}
previous_states[btn_index] = current_states[btn_index];
}
return detected_btn;
}
3.4 Logique principale de l'horloge
/**
* @file clock_logic.c
* @brief Logique centrale de l'horloge numérique
*/
#include "clock_logic.h"
#include "rtc_driver.h"
#include "display_driver.h"
const char *weekdays_str[] = {"Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"};
/**
* @brief Mise à jour de l'affichage en fonction du mode
*/
void Update_Display_Content(void) {
char buffer[16];
switch (g_system_state) {
case SYS_MODE_VIEW:
// Affichage normal de l'heure et de la date
sprintf(buffer, "%02d:%02d:%02d",
g_current_time.current_hour,
g_current_time.current_minute,
g_current_time.current_second);
Display_String_At(0, 0, buffer);
sprintf(buffer, "%02d-%02d-%02d %s",
g_current_time.current_year,
g_current_time.current_month,
g_current_time.current_day,
weekdays_str[g_current_time.current_weekday - 1]);
Display_String_At(0, 1, buffer);
break;
case SYS_MODE_SET_TIME:
// Mode réglage de l'heure avec clignotement
Display_String_At(0, 0, "Regler Heure:");
if (g_blink_state) {
switch (g_adjust_target) {
case ADJUST_HOUR:
sprintf(buffer, " :%02d:%02d",
g_current_time.current_minute,
g_current_time.current_second);
break;
case ADJUST_MINUTE:
sprintf(buffer, "%02d: :%02d",
g_current_time.current_hour,
g_current_time.current_second);
break;
case ADJUST_SECOND:
sprintf(buffer, "%02d:%02d: ",
g_current_time.current_hour,
g_current_time.current_minute);
break;
}
} else {
sprintf(buffer, "%02d:%02d:%02d",
g_current_time.current_hour,
g_current_time.current_minute,
g_current_time.current_second);
}
Display_String_At(0, 1, buffer);
break;
case SYS_MODE_SET_DATE:
// Mode réglage de la date
Display_String_At(0, 0, "Regler Date:");
if (g_blink_state) {
switch (g_adjust_target) {
case ADJUST_YEAR:
sprintf(buffer, " -%02d-%02d",
g_current_time.current_month,
g_current_time.current_day);
break;
case ADJUST_MONTH:
sprintf(buffer, "%02d- -%02d",
g_current_time.current_year,
g_current_time.current_day);
break;
case ADJUST_DAY:
sprintf(buffer, "%02d-%02d- ",
g_current_time.current_year,
g_current_time.current_month);
break;
}
} else {
sprintf(buffer, "%02d-%02d-%02d",
g_current_time.current_year,
g_current_time.current_month,
g_current_time.current_day);
}
Display_String_At(0, 1, buffer);
break;
case SYS_MODE_SET_ALARM:
// Mode réglage de l'alarme
Display_String_At(0, 0, "Regler Alarme:");
if (g_blink_state) {
switch (g_adjust_target) {
case ADJUST_ALARM_HOUR:
sprintf(buffer, " :%02d", g_alarm_config.alarm_minute);
break;
case ADJUST_ALARM_MINUTE:
sprintf(buffer, "%02d: ", g_alarm_config.alarm_hour);
break;
}
} else {
sprintf(buffer, "%02d:%02d",
g_alarm_config.alarm_hour,
g_alarm_config.alarm_minute);
}
Display_String_At(0, 1, buffer);
break;
}
}
/**
* @brief Traitement des événements des boutons
* @param btn_code Code du bouton
* @param btn_type Type d'appui (1: court, 2: long)
*/
void Process_Button_Event(uint8_t btn_code, uint8_t btn_type) {
switch (g_system_state) {
case SYS_MODE_VIEW:
if (btn_code == BTN_FUNCTION_MODE) {
if (btn_type == 1) {
g_system_state = SYS_MODE_SET_TIME;
g_adjust_target = ADJUST_HOUR;
Clear_Display();
} else if (btn_type == 2) {
// Bascule l'état de l'alarme
g_alarm_config.alarm_enabled = !g_alarm_config.alarm_enabled;
Display_String_At(0, 0, "Alarme:");
Display_String_At(0, 1, g_alarm_config.alarm_enabled ? "Active " : "Desact.");
Delay_Ms(1000);
}
}
break;
case SYS_MODE_SET_TIME:
switch (btn_code) {
case BTN_FUNCTION_MODE:
// Passage au champ suivant
g_adjust_target = (g_adjust_target + 1) % 3;
break;
case BTN_FUNCTION_INC:
// Incrémentation de la valeur
switch (g_adjust_target) {
case ADJUST_HOUR:
g_current_time.current_hour = (g_current_time.current_hour + 1) % 24;
break;
case ADJUST_MINUTE:
g_current_time.current_minute = (g_current_time.current_minute + 1) % 60;
break;
case ADJUST_SECOND:
g_current_time.current_second = (g_current_time.current_second + 1) % 60;
break;
}
break;
case BTN_FUNCTION_DEC:
// Décrémentation de la valeur
switch (g_adjust_target) {
case ADJUST_HOUR:
g_current_time.current_hour = (g_current_time.current_hour + 23) % 24;
break;
case ADJUST_MINUTE:
g_current_time.current_minute = (g_current_time.current_minute + 59) % 60;
break;
case ADJUST_SECOND:
g_current_time.current_second = (g_current_time.current_second + 59) % 60;
break;
}
break;
case BTN_FUNCTION_OK:
// Validation et retour au mode visualisation
RTC_Write_Time(&g_current_time);
g_system_state = SYS_MODE_VIEW;
Clear_Display();
break;
}
break;
case SYS_MODE_SET_DATE:
switch (btn_code) {
case BTN_FUNCTION_MODE:
g_adjust_target = (g_adjust_target + 1) % 3;
break;
case BTN_FUNCTION_INC:
switch (g_adjust_target) {
case ADJUST_YEAR:
g_current_time.current_year = (g_current_time.current_year + 1) % 100;
break;
case ADJUST_MONTH:
g_current_time.current_month = (g_current_time.current_month % 12) + 1;
break;
case ADJUST_DAY:
g_current_time.current_day = (g_current_time.current_day % 31) + 1;
break;
}
break;
case BTN_FUNCTION_DEC:
switch (g_adjust_target) {
case ADJUST_YEAR:
g_current_time.current_year = (g_current_time.current_year + 99) % 100;
break;
case ADJUST_MONTH:
g_current_time.current_month = ((g_current_time.current_month + 10) % 12) + 1;
break;
case ADJUST_DAY:
g_current_time.current_day = ((g_current_time.current_day + 30) % 31) + 1;
break;
}
break;
case BTN_FUNCTION_OK:
RTC_Write_Date(&g_current_time);
g_system_state = SYS_MODE_VIEW;
Clear_Display();
break;
}
break;
}
}
/**
* @brief Vérification du déclenchement de l'alarme
*/
void Check_Alarm_Condition(void) {
if (g_alarm_config.alarm_enabled &&
g_current_time.current_hour == g_alarm_config.alarm_hour &&
g_current_time.current_minute == g_alarm_config.alarm_minute &&
g_current_time.current_second == 0) {
g_alarm_triggered = 1;
g_system_state = SYS_MODE_ALARM_ACTIVE;
Clear_Display();
Display_String_At(0, 0, "REVEIL!");
Display_String_At(0, 1, "Appuyez sur touche");
}
}
/**
* @brief Gestion du signal sonore de l'alarme
*/
void Handle_Alarm_Signal(void) {
static uint16_t signal_counter = 0;
if (g_alarm_triggered) {
// Génération du signal sonore intermittent
if (signal_counter < 100) {
GPIO_SetBits(GPIOB, ALARM_BUZZER);
} else if (signal_counter < 200) {
GPIO_ResetBits(GPIOB, ALARM_BUZZER);
} else {
signal_counter = 0;
}
signal_counter++;
// Arrêt de l'alarme sur pression d'une touche
if (Detect_Button_Press() != 0) {
g_alarm_triggered = 0;
g_system_state = SYS_MODE_VIEW;
GPIO_ResetBits(GPIOB, ALARM_BUZZER);
Clear_Display();
}
}
}
3.5 Programme principal
/**
* @file main.c
* @brief Point d'entrée principal de l'application horloge
*/
#include "stm32f10x.h"
#include "clock_logic.h"
#include "button_handler.h"
#include "rtc_driver.h"
#include "display_driver.h"
#include "timing_utils.h"
volatile uint32_t system_timer_tick = 0;
/**
* @brief Initialisation globale du système
*/
void Initialize_System(void) {
System_Clock_Config();
Delay_Timer_Init();
Buttons_Init();
Display_Init();
RTC_Module_Init();
// Chargement de l'heure depuis la puce RTC
RTC_Read_Time(&g_current_time);
RTC_Read_Date(&g_current_time);
// Configuration initiale de l'alarme
g_alarm_config.alarm_hour = 7;
g_alarm_config.alarm_minute = 30;
g_alarm_config.alarm_enabled = 1;
// Configuration du buzzer
GPIO_InitTypeDef buzzer_gpio;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
buzzer_gpio.GPIO_Pin = ALARM_BUZZER;
buzzer_gpio.GPIO_Mode = GPIO_Mode_Out_PP;
buzzer_gpio.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &buzzer_gpio);
GPIO_ResetBits(GPIOB, ALARM_BUZZER);
}
/**
* @brief Routine d'interruption du timer système
*/
void SysTick_IRQHandler(void) {
system_timer_tick++;
// Clignotement toutes les 500ms
if (system_timer_tick % 500 == 0) {
g_blink_state = !g_blink_state;
}
}
/**
* @brief Boucle principale de l'application
*/
int main(void) {
uint8_t detected_button;
uint8_t press_type;
uint32_t last_rtc_read = 0;
Initialize_System();
while (1) {
// Détection et traitement des boutons
detected_button = Scan_Button_Press(&press_type);
if (detected_button != 0) {
Process_Button_Event(detected_button, press_type);
}
// Actualisation périodique de l'heure depuis la RTC
if (g_system_state == SYS_MODE_VIEW) {
if (system_timer_tick - last_rtc_read >= 1000) {
RTC_Read_Time(&g_current_time);
RTC_Read_Date(&g_current_time);
last_rtc_read = system_timer_tick;
}
}
// Rafraîchissement de l'affichage
Update_Display_Content();
// Surveillance de l'alarme
Check_Alarm_Condition();
// Gestion du signal d'alarme
Handle_Alarm_Signal();
Delay_Ms(10);
}
}
3.6 Pilote pour le circuit DS1302
/**
* @file rtc_driver.c
* @brief Pilote pour le circuit d'horloge temps réel DS1302
*/
#include "rtc_driver.h"
// Adresses des registres du DS1302
#define REG_SECONDS 0x80
#define REG_MINUTES 0x82
#define REG_HOURS 0x84
#define REG_DATE 0x86
#define REG_MONTHS 0x88
#define REG_WEEKDAYS 0x8A
#define REG_YEARS 0x8C
#define REG_CONTROL 0x8E
/**
* @brief Initialisation des broches et configuration de la puce
*/
void RTC_Module_Init(void) {
GPIO_InitTypeDef rtc_pins;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
// Broche de sélection de puce
rtc_pins.GPIO_Pin = RTC_CHIP_SELECT;
rtc_pins.GPIO_Mode = GPIO_Mode_Out_PP;
rtc_pins.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &rtc_pins);
// Broche de données
rtc_pins.GPIO_Pin = RTC_DATA_LINE;
GPIO_Init(GPIOA, &rtc_pins);
// Broche d'horloge
rtc_pins.GPIO_Pin = RTC_CLOCK_LINE;
GPIO_Init(GPIOA, &rtc_pins);
// Désactivation de la protection en écriture
RTC_Write_Byte(REG_CONTROL, 0x00);
}
/**
* @brief Écriture d'un octet dans un registre
*/
void RTC_Write_Byte(uint8_t register_addr, uint8_t data_byte) {
uint8_t bit_index;
GPIO_SetBits(GPIOA, RTC_CHIP_SELECT);
// Transmission de l'adresse
for (bit_index = 0; bit_index < 8; bit_index++) {
GPIO_WriteBit(GPIOA, RTC_DATA_LINE,
(BitAction)((register_addr >> bit_index) & 0x01));
GPIO_SetBits(GPIOA, RTC_CLOCK_LINE);
GPIO_ResetBits(GPIOA, RTC_CLOCK_LINE);
}
// Transmission des données
for (bit_index = 0; bit_index < 8; bit_index++) {
GPIO_WriteBit(GPIOA, RTC_DATA_LINE,
(BitAction)((data_byte >> bit_index) & 0x01));
GPIO_SetBits(GPIOA, RTC_CLOCK_LINE);
GPIO_ResetBits(GPIOA, RTC_CLOCK_LINE);
}
GPIO_ResetBits(GPIOA, RTC_CHIP_SELECT);
}
/**
* @brief Lecture d'un octet depuis un registre
*/
uint8_t RTC_Read_Byte(uint8_t register_addr) {
uint8_t bit_index, read_value = 0;
GPIO_SetBits(GPIOA, RTC_CHIP_SELECT);
// Transmission de l'adresse en lecture
for (bit_index = 0; bit_index < 8; bit_index++) {
GPIO_WriteBit(GPIOA, RTC_DATA_LINE,
(BitAction)((register_addr >> bit_index) & 0x01));
GPIO_SetBits(GPIOA, RTC_CLOCK_LINE);
GPIO_ResetBits(GPIOA, RTC_CLOCK_LINE);
}
// Lecture des données
for (bit_index = 0; bit_index < 8; bit_index++) {
GPIO_SetBits(GPIOA, RTC_CLOCK_LINE);
read_value >>= 1;
if (GPIO_ReadInputDataBit(GPIOA, RTC_DATA_LINE)) {
read_value |= 0x80;
}
GPIO_ResetBits(GPIOA, RTC_CLOCK_LINE);
}
GPIO_ResetBits(GPIOA, RTC_CHIP_SELECT);
return read_value;
}
/**
* @brief Conversion BCD vers décimal
*/
uint8_t BCD_To_Decimal(uint8_t bcd_value) {
return ((bcd_value >> 4) * 10) + (bcd_value & 0x0F);
}
/**
* @brief Conversion décimal vers BCD
*/
uint8_t Decimal_To_BCD(uint8_t decimal_value) {
return ((decimal_value / 10) << 4) | (decimal_value % 10);
}
/**
* @brief Écriture de l'heure dans la puce RTC
*/
void RTC_Write_Time(TimeData *time_data) {
RTC_Write_Byte(REG_SECONDS, Decimal_To_BCD(time_data->current_second));
RTC_Write_Byte(REG_MINUTES, Decimal_To_BCD(time_data->current_minute));
RTC_Write_Byte(REG_HOURS, Decimal_To_BCD(time_data->current_hour));
}
/**
* @brief Lecture de l'heure depuis la puce RTC
*/
void RTC_Read_Time(TimeData *time_data) {
time_data->current_second = BCD_To_Decimal(RTC_Read_Byte(REG_SECONDS));
time_data->current_minute = BCD_To_Decimal(RTC_Read_Byte(REG_MINUTES));
time_data->current_hour = BCD_To_Decimal(RTC_Read_Byte(REG_HOURS));
}
3.7 Pilote d'affichage LCD1602
/**
* @file display_driver.c
* @brief Pilote pour l'écran LCD 16x2 en mode 4 bits
*/
#include "display_driver.h"
// Fonctions d'attente temporisées
static void Wait_Microseconds(uint16_t us_count) {
while (us_count--) {
__NOP(); __NOP(); __NOP(); __NOP();
}
}
static void Wait_Milliseconds(uint16_t ms_count) {
uint16_t i, j;
for (i = 0; i < ms_count; i++) {
for (j = 0; j < 1000; j++) {
__NOP();
}
}
}
/**
* @brief Envoi d'une commande à l'écran
*/
void Send_Command(uint8_t command) {
GPIO_ResetBits(GPIOB, LCD_REG_SELECT);
GPIO_ResetBits(GPIOB, LCD_READ_WRITE);
GPIO_SetBits(GPIOB, LCD_ENABLE);
GPIO_WriteBit(GPIOB, LCD_DATA_BIT4, (BitAction)((command >> 4) & 0x01));
GPIO_WriteBit(GPIOB, LCD_DATA_BIT5, (BitAction)((command >> 5) & 0x01));
GPIO_WriteBit(GPIOB, LCD_DATA_BIT6, (BitAction)((command >> 6) & 0x01));
GPIO_WriteBit(GPIOB, LCD_DATA_BIT7, (BitAction)((command >> 7) & 0x01));
Wait_Microseconds(1);
GPIO_ResetBits(GPIOB, LCD_ENABLE);
Wait_Milliseconds(2);
}
/**
* @brief Envoi d'une donnée à l'écran
*/
void Send_Data(uint8_t data) {
GPIO_SetBits(GPIOB, LCD_REG_SELECT);
GPIO_ResetBits(GPIOB, LCD_READ_WRITE);
GPIO_SetBits(GPIOB, LCD_ENABLE);
GPIO_WriteBit(GPIOB, LCD_DATA_BIT4, (BitAction)((data >> 4) & 0x01));
GPIO_WriteBit(GPIOB, LCD_DATA_BIT5, (BitAction)((data >> 5) & 0x01));
GPIO_WriteBit(GPIOB, LCD_DATA_BIT6, (BitAction)((data >> 6) & 0x01));
GPIO_WriteBit(GPIOB, LCD_DATA_BIT7, (BitAction)((data >> 7) & 0x01));
Wait_Microseconds(1);
GPIO_ResetBits(GPIOB, LCD_ENABLE);
Wait_Milliseconds(2);
}
/**
* @brief Initialisation de l'écran LCD
*/
void Display_Init(void) {
GPIO_InitTypeDef lcd_pins;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
lcd_pins.GPIO_Pin = LCD_REG_SELECT | LCD_READ_WRITE | LCD_ENABLE |
LCD_DATA_BIT4 | LCD_DATA_BIT5 | LCD_DATA_BIT6 | LCD_DATA_BIT7;
lcd_pins.GPIO_Mode = GPIO_Mode_Out_PP;
lcd_pins.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &lcd_pins);
Wait_Milliseconds(15);
// Séquence d'initialisation en mode 4 bits
Send_Command(0x33); // Initialisation mode 8 bits
Send_Command(0x32); // Basculement en mode 4 bits
Send_Command(0x28); // Configuration : 4 bits, 2 lignes, fonte 5x7
Send_Command(0x0C); // Affichage activé, curseur désactivé
Send_Command(0x06); // Mode incrément, pas de décalage
Send_Command(0x01); // Effacement de l'écran
Wait_Milliseconds(2);
}
/**
* @brief Affichage d'une chaîne à une position donnée
*/
void Display_String_At(uint8_t col, uint8_t row, char *text) {
uint8_t address;
if (row == 0) {
address = 0x80 + col;
} else {
address = 0xC0 + col;
}
Send_Command(address);
while (*text) {
Send_Data(*text++);
}
}
/**
* @brief Effacement complet de l'écran
*/
void Clear_Display(void) {
Send_Command(0x01);
Wait_Milliseconds(2);
}
4. Pistes d'amélioration et extensions
4.1 Fonctionnalités supplémentaires envisageables
- Mesure de température : intégration d'un capteur DS18B20
- Calendrier lunaire : ajout d'un algorithme de conversion
- Fuseaux horaires : support du temps universel et des décalages
- Minuteur : fonction de compte à rebours programmable
- Chronomètre : mesure de durées avec précision
- Rétroéclairage adaptatif : modulation PWM de la luminosité
- Mémoire non-volatile : sauvegarde des paramètres en EEPROM
4.2 Évolutions matérielles possibles
| Composant |
Solution actuelle |
Solution améliorée |
Avantages |
| Écran |
LCD1602 |
OLED 128x64 |
Meilleur contraste, faible consommation |
| Horloge RTC |
DS1302 |
DS3231 |
Précision supérieure, compensation thermique |
| Interface tactile |
Boutons mécaniques |
Boutons capacitifs |
Design plus élégant, durabilité accrue |
| Alimentation |
Alimentation directe |
Batterie Li-ion avec gestion |
Autonomie et portabilité améliorées |
4.3 Optimisations logicielles
- Gestion de l'énergie : mode veille pendant les périodes d'inactivité
- Luminosité automatique : adaptation à l'éclairage ambiant
- Synthèse vocale : annonce sonore de l'heure à la demande
- Connectivité sans fil : synchronisation via Bluetooth ou WiFi
- Synchronisation réseau : récupération du temps via NTP